939 lines
21 KiB
Vue
939 lines
21 KiB
Vue
<template>
|
||
<div class="admin-content-review">
|
||
<!-- 统计卡片 -->
|
||
<div class="review-stats">
|
||
<!-- <div class="stat-card">
|
||
<div class="stat-icon total">
|
||
<el-icon><Document /></el-icon>
|
||
</div>
|
||
<div class="stat-info">
|
||
<div class="stat-number">{{ reviewStats.total }}</div>
|
||
<div class="stat-label">{{ t('admin.review.stats.total') }}</div>
|
||
</div>
|
||
</div> -->
|
||
|
||
<div class="stat-card">
|
||
<div class="stat-icon pending">
|
||
<el-icon><Clock /></el-icon>
|
||
</div>
|
||
<div class="stat-info">
|
||
<div class="stat-number">{{ reviewStats.pending }}</div>
|
||
<div class="stat-label">{{ t('admin.review.stats.pending') }}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="stat-card">
|
||
<div class="stat-icon approved">
|
||
<el-icon><Check /></el-icon>
|
||
</div>
|
||
<div class="stat-info">
|
||
<div class="stat-number">{{ reviewStats.approved }}</div>
|
||
<div class="stat-label">{{ t('admin.review.stats.approved') }}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="stat-card">
|
||
<div class="stat-icon rejected">
|
||
<el-icon><Close /></el-icon>
|
||
</div>
|
||
<div class="stat-info">
|
||
<div class="stat-number">{{ reviewStats.rejected }}</div>
|
||
<div class="stat-label">{{ t('admin.review.stats.rejected') }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 筛选和搜索 -->
|
||
<div class="review-filters">
|
||
<div class="search-group">
|
||
<el-input
|
||
v-model="searchQuery"
|
||
:placeholder="t('admin.review.search')"
|
||
:prefix-icon="Search"
|
||
clearable
|
||
/>
|
||
</div>
|
||
|
||
<div class="filter-actions">
|
||
<el-button :icon="Refresh" @click="refresh">
|
||
{{ t('admin.common.refresh') }}
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 审核列表 -->
|
||
<div class="review-table" :style="{ height: tableHeight + 'px' }">
|
||
<!-- stripe -->
|
||
<el-table
|
||
:data="filteredReviewList"
|
||
style="width: 100%"
|
||
v-loading="loading"
|
||
:max-height="tableHeight"
|
||
:height="tableHeight"
|
||
>
|
||
<el-table-column prop="id" label="ID" width="80" />
|
||
<el-table-column prop="creatorName" :label="t('admin.review.creator')" min-width="140" />
|
||
<el-table-column :label="t('admin.review.thumbnail')" min-width="80">
|
||
<template #default="{ row }">
|
||
<div
|
||
class="thumbnail-container"
|
||
@click="previewThumbnail(row.thumbnail)"
|
||
>
|
||
<el-image
|
||
:src="row.thumbnail"
|
||
:lazy="true"
|
||
style="width: 50px; height: 50px; cursor: pointer;"
|
||
fit="cover"
|
||
>
|
||
<template #error>
|
||
<div class="image-error">
|
||
<el-icon><Picture /></el-icon>
|
||
</div>
|
||
</template>
|
||
</el-image>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="orderPrice" :label="t('admin.review.orderPrice')" min-width="120">
|
||
<template #default="{ row }">
|
||
¥{{ row.orderPrice.toFixed(2) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="status" :label="t('admin.review.status')" min-width="120">
|
||
<template #default="{ row }">
|
||
<el-tag :type="getStatusTagType(row).status">
|
||
{{ t(`${getStatusTagType(row).label}`) }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="createTime" :label="t('admin.review.createTime')" min-width="160">
|
||
<template #default="{ row }">
|
||
{{ formatDate(row.createTime) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column :label="$t('admin.review.actions')" min-width="300" align="center" fixed="right">
|
||
<template #default="{ row }">
|
||
<div class="actions-container">
|
||
<el-button size="small" @click="previewModel(row)">
|
||
{{ t('admin.review.preview3D') }}
|
||
</el-button>
|
||
<el-button
|
||
size="small"
|
||
type="primary"
|
||
@click="approveReview(row)"
|
||
>
|
||
{{ t('admin.review.approve') }}
|
||
</el-button>
|
||
<el-button
|
||
size="small"
|
||
type="danger"
|
||
@click="rejectReview(row)"
|
||
>
|
||
{{ t('admin.review.reject') }}
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
|
||
<!-- 分页 -->
|
||
<div class="pagination">
|
||
<el-pagination
|
||
v-model:current-page="currentPage"
|
||
v-model:page-size="pageSize"
|
||
:page-sizes="[10, 20, 50, 100]"
|
||
:total="totalReviews"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handleCurrentChange"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 预览图片对话框 -->
|
||
<el-dialog
|
||
:title="t('admin.review.previewImage')"
|
||
v-model="previewImageVisible"
|
||
width="80%"
|
||
top="8vh"
|
||
:z-index="1000"
|
||
:append-to-body="true"
|
||
class="image-preview-dialog"
|
||
>
|
||
<div class="image-preview-container">
|
||
<div class="image-preview-content">
|
||
<img
|
||
:src="currentImage"
|
||
class="preview-image"
|
||
:style="{ transform: `scale(${imageScale})` }"
|
||
/>
|
||
</div>
|
||
<div class="image-actions">
|
||
<el-button
|
||
size="small"
|
||
@click="zoomIn"
|
||
>
|
||
<el-icon><ZoomIn /></el-icon>
|
||
{{ t('admin.review.zoomIn') }}
|
||
</el-button>
|
||
<el-button
|
||
size="small"
|
||
@click="zoomOut"
|
||
>
|
||
<el-icon><ZoomOut /></el-icon>
|
||
{{ t('admin.review.zoomOut') }}
|
||
</el-button>
|
||
<el-button
|
||
size="small"
|
||
@click="resetZoom"
|
||
>
|
||
<el-icon><Refresh /></el-icon>
|
||
{{ t('admin.review.resetZoom') }}
|
||
</el-button>
|
||
<el-button
|
||
size="small"
|
||
type="primary"
|
||
@click="openInNewTab"
|
||
>
|
||
<el-icon><Link /></el-icon>
|
||
{{ t('admin.review.openInNewTab') }}
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 3D模型预览对话框 -->
|
||
<el-dialog
|
||
:title="t('admin.review.preview3DModel')"
|
||
v-model="preview3DVisible"
|
||
width="85%"
|
||
top="5vh"
|
||
:z-index="2000"
|
||
:append-to-body="true"
|
||
class="model-preview-dialog"
|
||
>
|
||
<div class="model-preview-content">
|
||
<ModelViewer
|
||
ref="modelViewerRef"
|
||
:model-url="currentModelUrl"
|
||
:show-controls="true"
|
||
:loading-text="t('modelViewer.loadingModel')"
|
||
style="height: 70vh; width: 100%;"
|
||
/>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 拒绝审核对话框 -->
|
||
<el-dialog
|
||
:title="t('admin.review.rejectReview')"
|
||
v-model="rejectDialogVisible"
|
||
width="500px"
|
||
>
|
||
<el-form :model="rejectForm" :rules="rejectRules" ref="rejectFormRef" label-width="100px">
|
||
<el-form-item :label="t('admin.review.rejectionReason')" prop="reason">
|
||
<el-input
|
||
v-model="rejectForm.reason"
|
||
type="textarea"
|
||
:rows="4"
|
||
:placeholder="t('admin.review.pleaseInputReason')"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<span class="dialog-footer">
|
||
<el-button @click="rejectDialogVisible = false">
|
||
{{ t('common.cancel') }}
|
||
</el-button>
|
||
<el-button type="primary" @click="confirmRejectReview">
|
||
{{ t('common.confirm') }}
|
||
</el-button>
|
||
</span>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { orderStatus } from '@deotaland/utils'
|
||
import {AdminOrders} from './AdminOrders/AdminOrders'
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { useI18n } from 'vue-i18n'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import {
|
||
Refresh,
|
||
Search,
|
||
Document,
|
||
Clock,
|
||
Check,
|
||
Close,
|
||
Picture,
|
||
ZoomIn,
|
||
ZoomOut,
|
||
Link
|
||
} from '@element-plus/icons-vue'
|
||
const adminOrders = new AdminOrders();
|
||
// 引入 useRouter
|
||
import { useRouter } from 'vue-router'
|
||
import ModelViewer from '@/components/common/ModelViewer.vue'
|
||
|
||
const { t } = useI18n()
|
||
const router = useRouter()
|
||
|
||
// 响应式数据
|
||
const loading = ref(false)
|
||
const currentPage = ref(1)
|
||
const pageSize = ref(20)
|
||
const totalReviews = ref(0)
|
||
const searchQuery = ref('')
|
||
const tableHeight = ref(600)
|
||
|
||
// 对话框状态
|
||
const previewImageVisible = ref(false)
|
||
const preview3DVisible = ref(false)
|
||
const rejectDialogVisible = ref(false)
|
||
const currentImage = ref('')
|
||
const currentModelUrl = ref('') // 当前显示的模型URL
|
||
|
||
// 图片预览相关
|
||
const imageScale = ref(1)
|
||
|
||
// 拒绝审核表单
|
||
const rejectFormRef = ref(null)
|
||
const rejectForm = ref({
|
||
reason: ''
|
||
})
|
||
|
||
const rejectRules = {
|
||
reason: [
|
||
{ required: true, message: t('admin.review.pleaseInputReason'), trigger: 'blur' }
|
||
]
|
||
}
|
||
|
||
// 选中行数据
|
||
const selectedReview = ref(null)
|
||
|
||
|
||
|
||
|
||
// 计算属性
|
||
const filteredReviewList = computed(() => {
|
||
let list = ordersList.value.map((item) =>{
|
||
return {
|
||
...item,
|
||
creatorName: (item.order_info?.shipping?.firstName || '')+(item.order_info?.shipping?.lastName || '') ,
|
||
thumbnail: item.order_info?.modelData?.imageUrl || '',
|
||
orderPrice: item.actual_amount,
|
||
createTime: item.created_at,
|
||
modelUrl: item.order_info?.modelData?.modelUrl || '',
|
||
}
|
||
});
|
||
return list
|
||
|
||
})
|
||
// 统计数据
|
||
const reviewStats = ref({
|
||
total: 0,
|
||
pending: 0,
|
||
approved: 0,
|
||
rejected: 0
|
||
})
|
||
const getOrderStatistics = async () => {
|
||
adminOrders.getOrderStatistics().then(res=>{
|
||
if(res.code==0){
|
||
let data = res.data.approve_maps
|
||
reviewStats.value = {
|
||
total: data.total_approve_count,
|
||
pending: data.pending_count,
|
||
approved: data.pass_count,
|
||
rejected: data.failure_count,
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 方法
|
||
const refresh = () => {
|
||
loading.value = true
|
||
init();
|
||
}
|
||
|
||
const handleSizeChange = (size) => {
|
||
pageSize.value = size
|
||
currentPage.value = 1
|
||
}
|
||
|
||
const handleCurrentChange = (page) => {
|
||
currentPage.value = page
|
||
init();
|
||
}
|
||
|
||
const formatDate = (dateString) => {
|
||
const date = new Date(dateString)
|
||
return date.toLocaleString(
|
||
t('admin.review.dateFormat') === 'en-US' ? 'en-US' : 'zh-CN',
|
||
{
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
}
|
||
)
|
||
}
|
||
|
||
const getStatusTagType = (status) => {
|
||
return orderStatus.getOrderStatusOptions(status)
|
||
}
|
||
|
||
const previewThumbnail = (imageUrl) => {
|
||
if (!imageUrl) {
|
||
ElMessage.warning(t('admin.review.noThumbnail'))
|
||
return
|
||
}
|
||
currentImage.value = imageUrl
|
||
imageScale.value = 1
|
||
previewImageVisible.value = true
|
||
}
|
||
|
||
// 图片预览相关方法
|
||
const zoomIn = () => {
|
||
if (imageScale.value < 3) {
|
||
imageScale.value = Math.round((imageScale.value + 0.2) * 10) / 10
|
||
}
|
||
}
|
||
|
||
const zoomOut = () => {
|
||
if (imageScale.value > 0.3) {
|
||
imageScale.value = Math.round((imageScale.value - 0.2) * 10) / 10
|
||
}
|
||
}
|
||
|
||
const resetZoom = () => {
|
||
imageScale.value = 1
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
const openInNewTab = () => {
|
||
window.open(currentImage.value, '_blank')
|
||
}
|
||
|
||
const previewModel = (review) => {
|
||
// 设置3D模型URL,如果review中有modelUrl则使用,否则使用默认模型
|
||
if (review.modelUrl) {
|
||
currentModelUrl.value = review.modelUrl
|
||
preview3DVisible.value = true
|
||
}
|
||
}
|
||
// 审批同意审核
|
||
const approveReview = async (review) => {
|
||
try {
|
||
await ElMessageBox.confirm(
|
||
t('admin.review.confirmApprove'),
|
||
t('admin.review.approve'),
|
||
{
|
||
confirmButtonText: t('common.confirm'),
|
||
cancelButtonText: t('common.cancel'),
|
||
type: 'success'
|
||
}
|
||
)
|
||
adminOrders.updateOrderStatus(1,'',{id:review.id}).then(res=>{
|
||
if(res.code==0){
|
||
ElMessage.success(t('admin.review.approveSuccess'))
|
||
// 跳转到拆件订单页面
|
||
router.push(`/admin/disassembly-orders?order_no=${review.order_no}`)
|
||
}
|
||
})
|
||
} catch {
|
||
// 用户取消
|
||
}
|
||
}
|
||
|
||
const rejectReview = (review) => {
|
||
selectedReview.value = review
|
||
rejectForm.value.reason = ''
|
||
rejectDialogVisible.value = true
|
||
}
|
||
|
||
const confirmRejectReview = async () => {
|
||
if (!rejectFormRef.value) return
|
||
try {
|
||
await rejectFormRef.value.validate()
|
||
adminOrders.updateOrderStatus(0,rejectForm.value.reason,{id:selectedReview.value.id}).then(res=>{
|
||
if(res.code==0){
|
||
ElMessage.success(t('admin.review.rejectSuccess'))
|
||
// 跳转到拆件订单页面
|
||
init();
|
||
rejectDialogVisible.value = false;
|
||
}
|
||
})
|
||
} catch {
|
||
// 验证失败
|
||
}
|
||
}
|
||
const ortherjson = ref({
|
||
order_status:[0],
|
||
refund_status:[0],
|
||
payment_status:[1]
|
||
});
|
||
const ordersList = ref([]);
|
||
const getList = ()=>{
|
||
loading.value = true
|
||
let params = {
|
||
...ortherjson.value,
|
||
page_size: pageSize.value,
|
||
page: currentPage.value,
|
||
order_no:searchQuery.value
|
||
}
|
||
adminOrders.getOrderList(params).then(res=>{
|
||
let data = res.data || [];
|
||
ordersList.value = data.items;
|
||
loading.value = false
|
||
totalReviews.value = data.total;
|
||
// ordersList.value = [...data.items,...data.items];
|
||
// orderStats.value.pending = data.total || 0;
|
||
})
|
||
}
|
||
const init = ()=>{
|
||
getOrderStatistics();
|
||
getList()
|
||
}
|
||
|
||
// 根据窗口大小调整表格高度
|
||
const updateTableHeight = () => {
|
||
const windowHeight = window.innerHeight
|
||
const headerHeight = 200 // 估算头部区域高度
|
||
const paginationHeight = 80 // 估算分页区域高度
|
||
const padding = 40 // 页面padding
|
||
|
||
if (window.innerWidth <= 768) {
|
||
// 移动端使用较小的高度
|
||
tableHeight.value = Math.max(400, windowHeight - headerHeight - paginationHeight - padding)
|
||
} else {
|
||
// 桌面端使用标准高度
|
||
tableHeight.value = Math.min(600, windowHeight - headerHeight - paginationHeight - padding-160)
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
// 检查路由参数中的order_no
|
||
const routeQuery = router.currentRoute.value.query
|
||
if (routeQuery.order_no) {
|
||
searchQuery.value = routeQuery.order_no
|
||
}
|
||
|
||
// 页面加载时的初始化操作
|
||
init();
|
||
|
||
// 设置初始表格高度
|
||
updateTableHeight()
|
||
|
||
// 监听窗口大小变化
|
||
window.addEventListener('resize', updateTableHeight)
|
||
})
|
||
|
||
// 组件卸载时移除监听器
|
||
import { onUnmounted } from 'vue'
|
||
onUnmounted(() => {
|
||
window.removeEventListener('resize', updateTableHeight)
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.admin-content-review {
|
||
padding: 20px;
|
||
}
|
||
|
||
.dashboard-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
margin-bottom: 24px;
|
||
padding-bottom: 16px;
|
||
border-bottom: 1px solid #e5e7eb;
|
||
}
|
||
|
||
.header-left .title {
|
||
font-size: 28px;
|
||
font-weight: 600;
|
||
color: #1f2937;
|
||
margin: 0 0 8px 0;
|
||
}
|
||
|
||
.header-left .subtitle {
|
||
color: #6b7280;
|
||
margin: 0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.header-actions {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.review-stats {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
gap: 20px;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.stat-card {
|
||
background: white;
|
||
padding: 24px;
|
||
border-radius: 12px;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||
border: 1px solid #e5e7eb;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
}
|
||
|
||
.stat-icon {
|
||
width: 48px;
|
||
height: 48px;
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 24px;
|
||
color: white;
|
||
}
|
||
|
||
.stat-icon.total { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
|
||
.stat-icon.pending { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
|
||
.stat-icon.approved { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); }
|
||
.stat-icon.rejected { background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); }
|
||
|
||
.stat-number {
|
||
font-size: 32px;
|
||
font-weight: 700;
|
||
color: #1f2937;
|
||
line-height: 1;
|
||
}
|
||
|
||
.stat-label {
|
||
color: #6b7280;
|
||
font-size: 14px;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.review-filters {
|
||
padding: 20px;
|
||
border-radius: 12px;
|
||
margin-bottom: 24px;
|
||
display: flex;
|
||
gap: 12px;
|
||
align-items: flex-end;
|
||
justify-content: flex-end;
|
||
overflow-x: auto;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.filter-group {
|
||
display: flex;
|
||
gap: 8px;
|
||
align-items: flex-end;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.search-group {
|
||
flex: 1;
|
||
min-width: 200px;
|
||
max-width: 300px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.filter-actions {
|
||
display: flex;
|
||
align-items: flex-end;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.review-table {
|
||
background: white;
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||
border: 1px solid #e5e7eb;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
/* 表格样式优化 */
|
||
.review-table :deep(.el-table) {
|
||
width: 100% !important;
|
||
table-layout: auto;
|
||
height: 100% !important;
|
||
}
|
||
|
||
.review-table :deep(.el-table__body-wrapper) {
|
||
overflow-y: auto;
|
||
height: calc(100% - 60px);
|
||
}
|
||
|
||
/* 优化滚动条样式 */
|
||
.review-table :deep(.el-table__body-wrapper::-webkit-scrollbar) {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
|
||
.review-table :deep(.el-table__body-wrapper::-webkit-scrollbar-track) {
|
||
background: #f1f1f1;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.review-table :deep(.el-table__body-wrapper::-webkit-scrollbar-thumb) {
|
||
background: #c1c1c1;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.review-table :deep(.el-table__body-wrapper::-webkit-scrollbar-thumb:hover) {
|
||
background: #a8a8a8;
|
||
}
|
||
|
||
.review-table :deep(.el-table__header) {
|
||
width: 100% !important;
|
||
}
|
||
|
||
.review-table :deep(.el-table__body) {
|
||
width: 100% !important;
|
||
}
|
||
|
||
.review-table :deep(.el-table__cell) {
|
||
padding: 8px 12px;
|
||
white-space: nowrap;
|
||
vertical-align: middle;
|
||
text-align: center;
|
||
}
|
||
|
||
.review-table :deep(.el-table__row) {
|
||
height: 66px;
|
||
}
|
||
|
||
.review-table :deep(.el-table__header-wrapper) {
|
||
background-color: #f8fafc;
|
||
}
|
||
|
||
.review-table :deep(.el-table__header th) {
|
||
background-color: #f8fafc;
|
||
color: #374151;
|
||
font-weight: 600;
|
||
border-bottom: 2px solid #e5e7eb;
|
||
white-space: nowrap !important;
|
||
text-align: center;
|
||
text-overflow: ellipsis;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.review-table :deep(.el-table__header th .cell) {
|
||
text-align: center;
|
||
white-space: nowrap !important;
|
||
word-break: keep-all;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
/* 操作按钮容器 */
|
||
.actions-container {
|
||
display: flex;
|
||
gap: 6px;
|
||
flex-wrap: nowrap;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 100%;
|
||
overflow-x: auto;
|
||
padding-bottom: 4px;
|
||
}
|
||
|
||
.actions-container .el-button {
|
||
flex-shrink: 0;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* 缩略图容器样式 */
|
||
.thumbnail-container {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 8px 0;
|
||
}
|
||
|
||
.thumbnail-container .el-image {
|
||
border-radius: 6px;
|
||
width: 50px !important;
|
||
height: 50px !important;
|
||
min-width: 50px !important;
|
||
max-width: 50px !important;
|
||
min-height: 50px !important;
|
||
max-height: 50px !important;
|
||
overflow: hidden !important;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.thumbnail-container .el-image img {
|
||
width: 100% !important;
|
||
height: 100% !important;
|
||
object-fit: cover !important;
|
||
border-radius: 6px !important;
|
||
}
|
||
|
||
/* 图片错误样式优化 */
|
||
.image-error {
|
||
width: 50px;
|
||
height: 50px;
|
||
background: #f3f4f6;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #9ca3af;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.rejection-text {
|
||
color: #ef4444;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.pagination {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
padding: 16px 0;
|
||
}
|
||
|
||
/* 图片预览对话框样式 */
|
||
.image-preview-dialog :deep(.el-dialog__body) {
|
||
padding: 0;
|
||
}
|
||
|
||
.image-preview-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 70vh;
|
||
}
|
||
|
||
.image-preview-content {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #f8fafc;
|
||
border-radius: 8px;
|
||
margin-bottom: 16px;
|
||
min-height: 400px;
|
||
}
|
||
|
||
.preview-image {
|
||
max-width: 100%;
|
||
max-height: 100%;
|
||
object-fit: contain;
|
||
transition: transform 0.3s ease;
|
||
cursor: grab;
|
||
}
|
||
|
||
.preview-image:active {
|
||
cursor: grabbing;
|
||
}
|
||
|
||
.image-actions {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 12px;
|
||
padding: 16px 0;
|
||
border-top: 1px solid #e5e7eb;
|
||
}
|
||
|
||
.image-actions .el-button {
|
||
min-width: 80px;
|
||
}
|
||
|
||
/* 原有样式保留 */
|
||
.image-preview {
|
||
text-align: center;
|
||
}
|
||
|
||
.model-preview {
|
||
height: 60vh;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #f8fafc;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.model-placeholder {
|
||
text-align: center;
|
||
color: #6b7280;
|
||
}
|
||
|
||
.model-placeholder p {
|
||
margin: 16px 0;
|
||
}
|
||
|
||
.model-info {
|
||
color: #374151;
|
||
font-size: 14px;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.admin-content-review {
|
||
padding: 12px;
|
||
}
|
||
|
||
.dashboard-header {
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.review-stats {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.review-filters {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
white-space: normal;
|
||
overflow-x: visible;
|
||
}
|
||
|
||
.filter-group {
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.search-group {
|
||
max-width: none;
|
||
}
|
||
|
||
/* 移动端表格优化 */
|
||
.review-table {
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.review-table :deep(.el-table) {
|
||
width: auto !important;
|
||
min-width: 800px;
|
||
height: 100% !important;
|
||
}
|
||
|
||
.review-table :deep(.el-table__body-wrapper) {
|
||
height: calc(100% - 60px);
|
||
}
|
||
|
||
.review-table :deep(.el-table__cell) {
|
||
padding: 6px 8px;
|
||
font-size: 12px;
|
||
text-align: center;
|
||
}
|
||
|
||
.actions-container {
|
||
gap: 4px;
|
||
}
|
||
|
||
.actions-container .el-button {
|
||
padding: 4px 8px;
|
||
font-size: 12px;
|
||
}
|
||
}
|
||
</style> |