|
|
|
@ -1,19 +1,30 @@
|
|
|
|
|
<template>
|
|
|
|
|
<el-main>
|
|
|
|
|
<!-- 预览遮罩层 -->
|
|
|
|
|
<div v-if="isPreviewing" class="preview-mask" @click.self="closePreview">
|
|
|
|
|
<div class="preview-close" @click="closePreview">×</div>
|
|
|
|
|
<div class="preview-image-container">
|
|
|
|
|
<img :src="previewImage" class="preview-image" />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="preview-title">{{ previewTitle }}</div>
|
|
|
|
|
<div class="preview-hint">点击任意位置或按ESC键关闭预览</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 表格 -->
|
|
|
|
|
<el-table :height="tableHeight" :data="tableList" border stripe>
|
|
|
|
|
<el-table :height="tableHeight" :data="tableList" border stripe :class="{'preview-mode': isPreviewing}">
|
|
|
|
|
<el-table-column label="名称" prop="goodsName"></el-table-column>
|
|
|
|
|
<el-table-column label="商品图片" prop="goodsImage">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-image :src="getGoodsImage(scope.row.goodsImage)"
|
|
|
|
|
style="height: 60px; width: 60px; border-radius: 8px"
|
|
|
|
|
:preview-src-list="[getGoodsImage(scope.row.goodsImage)]"
|
|
|
|
|
fit="cover"
|
|
|
|
|
hide-on-click-modal
|
|
|
|
|
@error="handleImageError">
|
|
|
|
|
<el-image :src="getGoodsImage(scope.row.goodsImage)"
|
|
|
|
|
style="height: 60px; width: 60px; border-radius: 8px; cursor: pointer;"
|
|
|
|
|
fit="cover"
|
|
|
|
|
@error="handleImageError"
|
|
|
|
|
@click="openPreview(getGoodsImage(scope.row.goodsImage), scope.row.goodsName)">
|
|
|
|
|
<template #error>
|
|
|
|
|
<div class="image-error">
|
|
|
|
|
<el-icon><Picture /></el-icon>
|
|
|
|
|
<el-icon>
|
|
|
|
|
<Picture />
|
|
|
|
|
</el-icon>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-image>
|
|
|
|
@ -21,16 +32,17 @@
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="昵称" prop="nickName"></el-table-column>
|
|
|
|
|
<el-table-column label="头像" prop="avatarUrl">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-image :src="getAvatarUrl(scope.row.avatarUrl)"
|
|
|
|
|
style="height: 60px; width: 60px; border-radius: 50%"
|
|
|
|
|
:preview-src-list="[getAvatarUrl(scope.row.avatarUrl)]"
|
|
|
|
|
fit="cover"
|
|
|
|
|
hide-on-click-modal
|
|
|
|
|
@error="handleImageError">
|
|
|
|
|
<template #default="scoped">
|
|
|
|
|
<el-image :src="getAvatarUrl(scoped.row.avatarUrl)"
|
|
|
|
|
style="height: 60px; width: 60px; border-radius: 50%; cursor: pointer;"
|
|
|
|
|
fit="cover"
|
|
|
|
|
@error="handleImageError"
|
|
|
|
|
@click="openPreview(getAvatarUrl(scoped.row.avatarUrl), scoped.row.nickName)">
|
|
|
|
|
<template #error>
|
|
|
|
|
<div class="image-error">
|
|
|
|
|
<el-icon><User /></el-icon>
|
|
|
|
|
<el-icon>
|
|
|
|
|
<User />
|
|
|
|
|
</el-icon>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-image>
|
|
|
|
@ -46,20 +58,30 @@
|
|
|
|
|
<el-table-column label="时间" prop="createTime" width="160"></el-table-column>
|
|
|
|
|
<el-table-column label="操作" width="120" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-button :icon="Delete" @click="deleteBtn(scope.row.commentId)" type="danger" size="default">删除</el-button>
|
|
|
|
|
<el-button :icon="Delete"
|
|
|
|
|
@click="deleteBtn(scope.row.commentId)"
|
|
|
|
|
type="danger"
|
|
|
|
|
size="default"
|
|
|
|
|
:disabled="isPreviewing">删除</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
<!-- 分页 -->
|
|
|
|
|
<el-pagination @size-change="sizeChange" @current-change="currentChange" :current-page.sync="listParm.currentPage"
|
|
|
|
|
:page-sizes="[10, 20, 40, 80, 100]" :page-size="listParm.pageSize"
|
|
|
|
|
layout="total, sizes, prev, pager, next, jumper" :total="listParm.total" background>
|
|
|
|
|
<el-pagination @size-change="sizeChange"
|
|
|
|
|
@current-change="currentChange"
|
|
|
|
|
:current-page.sync="listParm.currentPage"
|
|
|
|
|
:page-sizes="[10, 20, 40, 80, 100]"
|
|
|
|
|
:page-size="listParm.pageSize"
|
|
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
|
|
:total="listParm.total"
|
|
|
|
|
background
|
|
|
|
|
:disabled="isPreviewing">
|
|
|
|
|
</el-pagination>
|
|
|
|
|
</el-main>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, onMounted } from 'vue'
|
|
|
|
|
import { ref, onMounted, onUnmounted } from 'vue'
|
|
|
|
|
import { Delete, Picture, User } from "@element-plus/icons-vue";
|
|
|
|
|
import useCommentTable from "../../compositions/comment/useCommentTable";
|
|
|
|
|
|
|
|
|
@ -68,19 +90,100 @@ const imgUrl = ref('http://localhost:8089/')
|
|
|
|
|
// 表格业务
|
|
|
|
|
const { listParm, deleteBtn, tableList, sizeChange, currentChange, tableHeight } = useCommentTable();
|
|
|
|
|
|
|
|
|
|
// 获取头像URL
|
|
|
|
|
const getAvatarUrl = (url: string) => {
|
|
|
|
|
if (!url) return '';
|
|
|
|
|
// 微信头像URL已经是完整URL,直接返回
|
|
|
|
|
if (url.indexOf('http') === 0 || url.indexOf('https') === 0) return url;
|
|
|
|
|
// 其他情况添加基础路径
|
|
|
|
|
return imgUrl.value + url;
|
|
|
|
|
// 预览相关状态
|
|
|
|
|
const isPreviewing = ref(false);
|
|
|
|
|
const previewImage = ref('');
|
|
|
|
|
const previewTitle = ref('');
|
|
|
|
|
|
|
|
|
|
// 打开预览
|
|
|
|
|
const openPreview = (imageUrl: string, title: string) => {
|
|
|
|
|
if (!imageUrl) return;
|
|
|
|
|
|
|
|
|
|
previewImage.value = imageUrl;
|
|
|
|
|
previewTitle.value = title || '图片预览';
|
|
|
|
|
isPreviewing.value = true;
|
|
|
|
|
|
|
|
|
|
// 禁用滚动
|
|
|
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 关闭预览
|
|
|
|
|
const closePreview = () => {
|
|
|
|
|
isPreviewing.value = false;
|
|
|
|
|
previewImage.value = '';
|
|
|
|
|
previewTitle.value = '';
|
|
|
|
|
|
|
|
|
|
// 恢复滚动
|
|
|
|
|
document.body.style.overflow = '';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ESC键监听
|
|
|
|
|
const handleKeydown = (e: KeyboardEvent) => {
|
|
|
|
|
if (e.key === 'Escape' && isPreviewing.value) {
|
|
|
|
|
closePreview();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
window.addEventListener('keydown', handleKeydown);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
window.removeEventListener('keydown', handleKeydown);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 定义调试信息接口
|
|
|
|
|
interface DebugInfo {
|
|
|
|
|
message: string;
|
|
|
|
|
status: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用明确的类型定义
|
|
|
|
|
const urlDebugInfo = ref<DebugInfo[]>([]);
|
|
|
|
|
|
|
|
|
|
// 添加调试信息的方法
|
|
|
|
|
const addDebugInfo = (message: string, status: string) => {
|
|
|
|
|
urlDebugInfo.value.push({
|
|
|
|
|
message,
|
|
|
|
|
status
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
// 获取头像URL - 修复URL处理逻辑
|
|
|
|
|
const getAvatarUrl = (url: string) => {
|
|
|
|
|
if (!url) {
|
|
|
|
|
// 替代原来的直接操作数组的方式
|
|
|
|
|
addDebugInfo('头像URL为空,使用默认占位符', 'error');
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果已经是完整URL,直接返回
|
|
|
|
|
if (url.indexOf('http') === 0 || url.indexOf('https') === 0) {
|
|
|
|
|
urlDebugInfo.value.push({
|
|
|
|
|
message: `头像URL完整: ${url}`,
|
|
|
|
|
status: 'success'
|
|
|
|
|
});
|
|
|
|
|
return url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理相对路径(去除可能的多余字符)
|
|
|
|
|
let cleanUrl = url;
|
|
|
|
|
if (cleanUrl.indexOf('/') === 0) {
|
|
|
|
|
cleanUrl = cleanUrl.substring(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加基础路径
|
|
|
|
|
const fullUrl = imgUrl.value + cleanUrl;
|
|
|
|
|
urlDebugInfo.value.push({
|
|
|
|
|
message: `头像URL不完整,使用完整路径: ${fullUrl}`,
|
|
|
|
|
status: 'success'
|
|
|
|
|
});
|
|
|
|
|
return fullUrl;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取商品图片URL
|
|
|
|
|
const getGoodsImage = (images: string) => {
|
|
|
|
|
if (!images) return '';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const imageArray = images.split(',');
|
|
|
|
|
if (imageArray.length > 0) {
|
|
|
|
@ -94,7 +197,7 @@ const getGoodsImage = (images: string) => {
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('解析商品图片失败:', e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -126,4 +229,67 @@ onMounted(() => {
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
max-width: 300px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preview-mask {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
background: rgba(0, 0, 0, 0.85);
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preview-image-container {
|
|
|
|
|
max-width: 80%;
|
|
|
|
|
max-height: 80%;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preview-image {
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
max-height: 100%;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preview-close {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 20px;
|
|
|
|
|
right: 20px;
|
|
|
|
|
color: white;
|
|
|
|
|
font-size: 30px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
|
|
width: 50px;
|
|
|
|
|
height: 50px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preview-title {
|
|
|
|
|
color: white;
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preview-hint {
|
|
|
|
|
color: rgba(255, 255, 255, 0.7);
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preview-mode {
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
</style>
|