Files
FileCollect/原型/后台管理页面/image-management.html
T
2025-10-20 07:11:14 +08:00

1232 lines
40 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片管理 - 图片收集系统</title>
<!-- 引入Vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script>
<!-- 引入Element UI CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-ui@2.15.8/lib/theme-chalk/index.css">
<!-- 引入Element UI JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/element-ui@2.15.8/lib/index.js"></script>
<!-- 引入Font Awesome -->
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<!-- 引入ECharts -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
height: 100vh;
background-color: #f0f2f5;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
.container {
height: 100vh;
display: flex;
}
.sidebar {
width: 220px;
background-color: #001529;
color: white;
display: flex;
flex-direction: column;
}
.sidebar-header {
padding: 20px;
text-align: center;
border-bottom: 1px solid #1f2937;
}
.logo {
font-size: 20px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
}
.logo-icon {
margin-right: 8px;
font-size: 22px;
}
.menu-item {
padding: 15px 20px;
cursor: pointer;
transition: background-color 0.3s;
display: flex;
align-items: center;
}
.menu-item:hover {
background-color: #1f2937;
}
.menu-item.active {
background-color: #1989fa;
}
.menu-icon {
margin-right: 10px;
font-size: 16px;
}
.main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.header {
background-color: white;
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.header-title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
.header-actions {
display: flex;
align-items: center;
gap: 15px;
}
.user-info {
display: flex;
align-items: center;
}
.user-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background-color: #1989fa;
color: white;
display: flex;
align-items: center;
justify-content: center;
margin-right: 8px;
font-size: 14px;
}
.user-name {
color: #606266;
}
.content-wrapper {
flex: 1;
padding: 20px;
overflow-y: auto;
}
.card {
background-color: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
margin-bottom: 20px;
}
.search-section {
display: flex;
gap: 15px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.search-input {
width: 250px;
}
.select-wrapper {
width: 200px;
}
.table-wrapper {
overflow-x: auto;
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.table-title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
.btn-primary {
background-color: #1989fa;
border-color: #1989fa;
}
.btn-primary:hover {
background-color: #409eff;
border-color: #409eff;
}
.operation-buttons {
display: flex;
gap: 8px;
}
.el-button {
padding: 6px 12px;
font-size: 14px;
}
.dialog-form-item {
margin-bottom: 20px;
}
.dialog-form-label {
font-weight: 500;
color: #303133;
margin-bottom: 8px;
font-size: 14px;
}
.status-badge {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
}
.status-pending {
background-color: #fdf6ec;
color: #e6a23c;
}
.status-approved {
background-color: #f0f9eb;
color: #67c23a;
}
.status-rejected {
background-color: #fef0f0;
color: #f56c6c;
}
.statistics-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.stat-card {
background-color: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
display: flex;
align-items: center;
}
.stat-icon {
width: 48px;
height: 48px;
border-radius: 50%;
background-color: #ecf5ff;
color: #1989fa;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
margin-right: 15px;
}
.stat-content {
flex: 1;
}
.stat-value {
font-size: 24px;
font-weight: 600;
color: #303133;
margin-bottom: 5px;
}
.stat-label {
font-size: 14px;
color: #909399;
}
.logout-btn {
cursor: pointer;
color: #606266;
transition: color 0.3s;
}
.logout-btn:hover {
color: #f56c6c;
}
.chart-container {
height: 300px;
width: 100%;
}
.chart-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 20px;
}
.image-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 15px;
}
.image-item {
background-color: #f5f7fa;
border-radius: 8px;
overflow: hidden;
transition: transform 0.3s, box-shadow 0.3s;
cursor: pointer;
position: relative;
}
.image-item:hover {
transform: translateY(-5px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.image-preview {
width: 100%;
height: 120px;
object-fit: cover;
}
.image-info {
padding: 10px;
}
.image-title {
font-size: 13px;
color: #303133;
margin-bottom: 5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.image-meta {
font-size: 12px;
color: #909399;
display: flex;
justify-content: space-between;
}
.image-status-badge {
position: absolute;
top: 8px;
right: 8px;
padding: 2px 8px;
border-radius: 10px;
font-size: 11px;
}
.category-tag {
margin-right: 5px;
margin-bottom: 5px;
}
.image-detail-dialog .el-dialog__body {
max-height: 70vh;
overflow-y: auto;
}
.image-detail {}
.image-detail-preview {
width: 100%;
max-height: 400px;
object-fit: contain;
margin-bottom: 20px;
}
.detail-section {
margin-bottom: 20px;
}
.detail-label {
font-weight: 600;
color: #303133;
margin-bottom: 8px;
}
.detail-value {
color: #606266;
line-height: 1.6;
}
.detail-row {
display: flex;
margin-bottom: 10px;
}
.detail-col {
flex: 1;
}
.tab-content {
padding: 20px 0;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<!-- 侧边栏 -->
<div class="sidebar">
<div class="sidebar-header">
<div class="logo">
<i class="fa fa-tachometer logo-icon"></i>
<span>管理平台</span>
</div>
</div>
<div class="menu-item" @click="navigateTo('department')">
<i class="fa fa-building-o menu-icon"></i>
<span>部门管理</span>
</div>
<div class="menu-item" @click="navigateTo('user')">
<i class="fa fa-users menu-icon"></i>
<span>用户管理</span>
</div>
<div class="menu-item" @click="navigateTo('task')">
<i class="fa fa-tasks menu-icon"></i>
<span>任务管理</span>
</div>
<div class="menu-item active" @click="navigateTo('image')">
<i class="fa fa-picture-o menu-icon"></i>
<span>图片管理</span>
</div>
</div>
<!-- 主内容区 -->
<div class="main-content">
<!-- 顶部导航栏 -->
<div class="header">
<div class="header-title">图片管理</div>
<div class="header-actions">
<div class="user-info">
<div class="user-avatar"></div>
<span class="user-name">系统管理员</span>
</div>
<div class="logout-btn" @click="handleLogout">
<i class="fa fa-sign-out"></i> 退出
</div>
</div>
</div>
<!-- 内容区域 -->
<div class="content-wrapper">
<!-- 统计卡片 -->
<div class="statistics-cards">
<div class="stat-card">
<div class="stat-icon">
<i class="fa fa-image"></i>
</div>
<div class="stat-content">
<div class="stat-value">{{ imageStats.total }}</div>
<div class="stat-label">总图片数</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon" style="background-color: #f0f9eb; color: #67c23a;">
<i class="fa fa-check-circle"></i>
</div>
<div class="stat-content">
<div class="stat-value">{{ imageStats.approved }}</div>
<div class="stat-label">已审核通过</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon" style="background-color: #fdf6ec; color: #e6a23c;">
<i class="fa fa-clock-o"></i>
</div>
<div class="stat-content">
<div class="stat-value">{{ imageStats.pending }}</div>
<div class="stat-label">待审核</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon" style="background-color: #fef0f0; color: #f56c6c;">
<i class="fa fa-times-circle"></i>
</div>
<div class="stat-content">
<div class="stat-value">{{ imageStats.rejected }}</div>
<div class="stat-label">已拒绝</div>
</div>
</div>
</div>
<!-- 图表区域 -->
<div class="chart-row">
<div class="card">
<h3 style="margin-bottom: 20px; color: #303133;">图片状态分布</h3>
<div id="imageStatusChart" class="chart-container"></div>
</div>
<div class="card">
<h3 style="margin-bottom: 20px; color: #303133;">图片分类统计</h3>
<div id="imageCategoryChart" class="chart-container"></div>
</div>
</div>
<!-- 搜索和操作区域 -->
<div class="card">
<el-tabs v-model="activeTab" @tab-click="handleTabChange">
<el-tab-pane label="列表视图" name="list">
<div class="search-section">
<el-input
v-model="searchQuery"
placeholder="搜索图片标题或描述"
prefix-icon="el-icon-search"
class="search-input"
></el-input>
<el-select
v-model="statusFilter"
placeholder="选择状态"
class="select-wrapper"
clearable
>
<el-option label="待审核" value="pending"></el-option>
<el-option label="已通过" value="approved"></el-option>
<el-option label="已拒绝" value="rejected"></el-option>
</el-select>
<el-select
v-model="categoryFilter"
placeholder="选择分类"
class="select-wrapper"
clearable
>
<el-option
v-for="category in categories"
:key="category.id"
:label="category.name"
:value="category.id"
></el-option>
</el-select>
<el-select
v-model="departmentFilter"
placeholder="选择部门"
class="select-wrapper"
clearable
>
<el-option
v-for="dept in departments"
:key="dept.id"
:label="dept.name"
:value="dept.id"
></el-option>
</el-select>
<el-button
type="primary"
class="btn-primary"
@click="batchApprove"
:disabled="selectedImages.length === 0"
>
<i class="fa fa-check"></i> 批量审核通过
</el-button>
</div>
</el-tab-pane>
<el-tab-pane label="网格视图" name="grid">
<div class="search-section">
<el-input
v-model="searchQuery"
placeholder="搜索图片标题或描述"
prefix-icon="el-icon-search"
class="search-input"
></el-input>
<el-select
v-model="statusFilter"
placeholder="选择状态"
class="select-wrapper"
clearable
>
<el-option label="待审核" value="pending"></el-option>
<el-option label="已通过" value="approved"></el-option>
<el-option label="已拒绝" value="rejected"></el-option>
</el-select>
<el-select
v-model="categoryFilter"
placeholder="选择分类"
class="select-wrapper"
clearable
>
<el-option
v-for="category in categories"
:key="category.id"
:label="category.name"
:value="category.id"
></el-option>
</el-select>
</div>
</el-tab-pane>
</el-tabs>
</div>
<!-- 图片列表 -->
<div class="card">
<div class="tab-content" v-if="activeTab === 'list'">
<div class="table-wrapper">
<el-table
:data="filteredImages"
stripe
style="width: 100%"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="id" label="图片ID" width="80"></el-table-column>
<el-table-column label="预览" width="100">
<template slot-scope="scope">
<img
:src="scope.row.url"
:alt="scope.row.title"
style="width: 80px; height: 60px; object-fit: cover; border-radius: 4px; cursor: pointer;"
@click="viewImageDetail(scope.row)"
>
</template>
</el-table-column>
<el-table-column prop="title" label="标题" min-width="180">
<template slot-scope="scope">
<div style="font-weight: 500; cursor: pointer;" @click="viewImageDetail(scope.row)">{{ scope.row.title }}</div>
<div style="color: #909399; font-size: 12px; margin-top: 2px; line-height: 1.4;">{{ scope.row.description }}</div>
</template>
</el-table-column>
<el-table-column prop="categoryName" label="分类" width="100"></el-table-column>
<el-table-column prop="departmentName" label="所属部门" width="120"></el-table-column>
<el-table-column prop="uploaderName" label="上传人" width="100"></el-table-column>
<el-table-column prop="taskName" label="关联任务" min-width="150"></el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template slot-scope="scope">
<span
class="status-badge"
:class="{
'status-pending': scope.row.status === 'pending',
'status-approved': scope.row.status === 'approved',
'status-rejected': scope.row.status === 'rejected'
}"
>
{{ getStatusText(scope.row.status) }}
</span>
</template>
</el-table-column>
<el-table-column prop="uploadTime" label="上传时间" min-width="150">
<template slot-scope="scope">
{{ formatDateTime(scope.row.uploadTime) }}
</template>
</el-table-column>
<el-table-column prop="size" label="大小" width="80">
<template slot-scope="scope">
{{ formatFileSize(scope.row.size) }}
</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right">
<template slot-scope="scope">
<div class="operation-buttons">
<el-button
type="primary"
size="small"
@click="viewImageDetail(scope.row)"
>
查看
</el-button>
<template v-if="scope.row.status === 'pending'">
<el-button
type="success"
size="small"
@click="approveImage(scope.row.id)"
>
通过
</el-button>
<el-button
type="danger"
size="small"
@click="rejectImage(scope.row.id)"
>
拒绝
</el-button>
</template>
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- 图片网格 -->
<div class="tab-content" v-else-if="activeTab === 'grid'">
<div class="image-grid">
<div
v-for="image in filteredImages"
:key="image.id"
class="image-item"
@click="viewImageDetail(image)"
>
<img
:src="image.url"
:alt="image.title"
class="image-preview"
>
<span
class="image-status-badge"
:class="{
'status-pending': image.status === 'pending',
'status-approved': image.status === 'approved',
'status-rejected': image.status === 'rejected'
}"
>
{{ getStatusText(image.status) }}
</span>
<div class="image-info">
<div class="image-title">{{ image.title }}</div>
<div class="image-meta">
<span>{{ image.categoryName }}</span>
<span>{{ formatDate(image.uploadTime) }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 图片详情对话框 -->
<el-dialog
:title="'图片详情 - ' + imageDetail.title"
:visible.sync="showImageDetail"
width="800px"
class="image-detail-dialog"
>
<div class="image-detail" v-if="imageDetail.id">
<img
:src="imageDetail.url"
:alt="imageDetail.title"
class="image-detail-preview"
>
<div class="detail-section">
<div class="detail-label">基本信息</div>
<div class="detail-value">
<div class="detail-row">
<div class="detail-col">
<strong>标题:</strong>{{ imageDetail.title || '-' }}
</div>
<div class="detail-col">
<strong>ID</strong>{{ imageDetail.id || '-' }}
</div>
</div>
<div class="detail-row">
<div class="detail-col">
<strong>描述:</strong>{{ imageDetail.description || '-' }}
</div>
<div class="detail-col">
<strong>分类:</strong>{{ imageDetail.categoryName || '-' }}
</div>
</div>
<div class="detail-row">
<div class="detail-col">
<strong>上传人:</strong>{{ imageDetail.uploaderName || '-' }}
</div>
<div class="detail-col">
<strong>所属部门:</strong>{{ imageDetail.departmentName || '-' }}
</div>
</div>
<div class="detail-row">
<div class="detail-col">
<strong>关联任务:</strong>{{ imageDetail.taskName || '-' }}
</div>
<div class="detail-col">
<strong>状态:</strong>
<span
class="status-badge"
:class="{
'status-pending': imageDetail.status === 'pending',
'status-approved': imageDetail.status === 'approved',
'status-rejected': imageDetail.status === 'rejected'
}"
>
{{ getStatusText(imageDetail.status) }}
</span>
</div>
</div>
<div class="detail-row">
<div class="detail-col">
<strong>上传时间:</strong>{{ formatDateTime(imageDetail.uploadTime) || '-' }}
</div>
<div class="detail-col">
<strong>文件大小:</strong>{{ formatFileSize(imageDetail.size) || '-' }}
</div>
</div>
</div>
</div>
<div class="detail-section" v-if="imageDetail.tags && imageDetail.tags.length > 0">
<div class="detail-label">标签</div>
<div class="tag-list">
<el-tag
v-for="tag in imageDetail.tags"
:key="tag"
class="category-tag"
size="small"
>
{{ tag }}
</el-tag>
</div>
</div>
<div class="detail-section" v-if="imageDetail.status === 'pending'">
<div class="detail-label">审核操作</div>
<div class="operation-buttons">
<el-button
type="success"
size="medium"
@click="approveImage(imageDetail.id, true)"
>
<i class="fa fa-check"></i> 审核通过
</el-button>
<el-button
type="danger"
size="medium"
@click="rejectImage(imageDetail.id, true)"
>
<i class="fa fa-times"></i> 审核拒绝
</el-button>
</div>
</div>
</div>
</el-dialog>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
searchQuery: '',
statusFilter: '',
categoryFilter: '',
departmentFilter: '',
activeTab: 'list',
showImageDetail: false,
selectedImages: [],
departments: [
{ id: 1, name: '技术部' },
{ id: 2, name: '市场部' },
{ id: 3, name: '人力资源部' },
{ id: 4, name: '财务部' },
{ id: 5, name: '行政部' }
],
categories: [
{ id: 1, name: '产品包装' },
{ id: 2, name: '办公环境' },
{ id: 3, name: '活动现场' },
{ id: 4, name: '员工证件' },
{ id: 5, name: '财务凭证' },
{ id: 6, name: 'IT设备' }
],
images: [
{
id: 1,
title: '产品包装正面图',
description: '新款产品包装的正面展示图',
url: 'https://picsum.photos/id/26/800/600',
categoryId: 1,
categoryName: '产品包装',
departmentId: 2,
departmentName: '市场部',
uploaderId: 3,
uploaderName: '李明',
taskId: 1,
taskName: '产品包装图片采集',
status: 'approved',
uploadTime: '2023-12-01 14:30:00',
size: 2048000,
tags: ['产品', '包装', '正面']
},
{
id: 2,
title: '办公区域全景',
description: '新办公区的全景照片',
url: 'https://picsum.photos/id/28/800/600',
categoryId: 2,
categoryName: '办公环境',
departmentId: 5,
departmentName: '行政部',
uploaderId: 5,
uploaderName: '赵明',
taskId: 2,
taskName: '办公环境照片拍摄',
status: 'approved',
uploadTime: '2023-12-02 10:15:00',
size: 3500000,
tags: ['办公环境', '全景']
},
{
id: 3,
title: '市场活动现场',
description: '新产品发布会现场照片',
url: 'https://picsum.photos/id/29/800/600',
categoryId: 3,
categoryName: '活动现场',
departmentId: 2,
departmentName: '市场部',
uploaderId: 3,
uploaderName: '李明',
taskId: 3,
taskName: '活动现场照片收集',
status: 'approved',
uploadTime: '2023-12-03 16:45:00',
size: 4200000,
tags: ['活动', '现场', '发布会']
},
{
id: 4,
title: '员工证件照',
description: '新入职员工的证件照片',
url: 'https://picsum.photos/id/33/800/600',
categoryId: 4,
categoryName: '员工证件',
departmentId: 3,
departmentName: '人力资源部',
uploaderId: 4,
uploaderName: '王红',
taskId: 4,
taskName: '员工证件照更新',
status: 'pending',
uploadTime: '2023-12-04 09:20:00',
size: 1500000,
tags: ['员工', '证件照']
},
{
id: 5,
title: '财务发票扫描件',
description: '11月份的办公费用发票',
url: 'https://picsum.photos/id/36/800/600',
categoryId: 5,
categoryName: '财务凭证',
departmentId: 4,
departmentName: '财务部',
uploaderId: 5,
uploaderName: '赵明',
taskId: 5,
taskName: '财务凭证扫描',
status: 'pending',
uploadTime: '2023-12-05 13:10:00',
size: 800000,
tags: ['财务', '发票', '凭证']
},
{
id: 6,
title: '服务器设备照片',
description: '机房服务器设备的照片',
url: 'https://picsum.photos/id/42/800/600',
categoryId: 6,
categoryName: 'IT设备',
departmentId: 1,
departmentName: '技术部',
uploaderId: 2,
uploaderName: '张经理',
taskId: 6,
taskName: '设备库存盘点',
status: 'rejected',
uploadTime: '2023-12-06 11:30:00',
size: 3200000,
tags: ['IT设备', '服务器', '机房']
},
{
id: 7,
title: '产品包装侧面图',
description: '新款产品包装的侧面展示图',
url: 'https://picsum.photos/id/44/800/600',
categoryId: 1,
categoryName: '产品包装',
departmentId: 2,
departmentName: '市场部',
uploaderId: 3,
uploaderName: '李明',
taskId: 1,
taskName: '产品包装图片采集',
status: 'approved',
uploadTime: '2023-12-07 15:20:00',
size: 2800000,
tags: ['产品', '包装', '侧面']
},
{
id: 8,
title: '会议室照片',
description: '新装修的会议室照片',
url: 'https://picsum.photos/id/45/800/600',
categoryId: 2,
categoryName: '办公环境',
departmentId: 5,
departmentName: '行政部',
uploaderId: 5,
uploaderName: '赵明',
taskId: 2,
taskName: '办公环境照片拍摄',
status: 'pending',
uploadTime: '2023-12-08 14:45:00',
size: 3800000,
tags: ['办公环境', '会议室']
}
],
imageDetail: {},
imageStats: {
total: 8,
approved: 4,
pending: 3,
rejected: 1
}
}
},
computed: {
filteredImages() {
return this.images.filter(image => {
// 搜索图片标题或描述
const matchesSearch = !this.searchQuery ||
image.title.toLowerCase().includes(this.searchQuery.toLowerCase()) ||
image.description.toLowerCase().includes(this.searchQuery.toLowerCase());
// 状态筛选
const matchesStatus = !this.statusFilter ||
image.status === this.statusFilter;
// 分类筛选
const matchesCategory = !this.categoryFilter ||
image.categoryId === this.categoryFilter;
// 部门筛选
const matchesDepartment = !this.departmentFilter ||
image.departmentId === this.departmentFilter;
return matchesSearch && matchesStatus && matchesCategory && matchesDepartment;
});
}
},
mounted() {
this.initCharts();
},
methods: {
// 初始化图表
initCharts() {
// 图片状态分布图表
const statusChart = echarts.init(document.getElementById('imageStatusChart'));
const statusOption = {
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '图片状态',
type: 'pie',
radius: '50%',
data: [
{ value: this.imageStats.approved, name: '已通过', itemStyle: { color: '#67c23a' } },
{ value: this.imageStats.pending, name: '待审核', itemStyle: { color: '#e6a23c' } },
{ value: this.imageStats.rejected, name: '已拒绝', itemStyle: { color: '#f56c6c' } }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
statusChart.setOption(statusOption);
// 图片分类统计图表
const categoryChart = echarts.init(document.getElementById('imageCategoryChart'));
const categoryData = [];
const categoryColors = ['#1989fa', '#67c23a', '#e6a23c', '#f56c6c', '#909399', '#409eff'];
this.categories.forEach((category, index) => {
const count = this.images.filter(img => img.categoryId === category.id).length;
if (count > 0) {
categoryData.push({
name: category.name,
value: count,
itemStyle: { color: categoryColors[index % categoryColors.length] }
});
}
});
const categoryOption = {
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '图片分类',
type: 'pie',
radius: '50%',
data: categoryData,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
categoryChart.setOption(categoryOption);
// 监听窗口大小变化
window.addEventListener('resize', () => {
statusChart.resize();
categoryChart.resize();
});
},
// 处理标签切换
handleTabChange() {
// 标签切换时清空选中项
this.selectedImages = [];
},
// 处理表格选择
handleSelectionChange(selection) {
this.selectedImages = selection;
},
// 导航到其他页面
navigateTo(page) {
let url = '';
switch (page) {
case 'department':
url = 'department-management.html';
break;
case 'user':
url = 'user-management.html';
break;
case 'task':
url = 'task-management.html';
break;
case 'image':
url = 'image-management.html';
break;
}
if (url && url !== window.location.pathname) {
window.location.href = url;
}
},
// 格式化日期
formatDate(dateStr) {
if (!dateStr) return '';
return dateStr.split(' ')[0];
},
// 格式化日期时间
formatDateTime(dateStr) {
return dateStr || '';
},
// 格式化文件大小
formatFileSize(bytes) {
if (!bytes) return '';
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
},
// 获取状态文本
getStatusText(status) {
const statusMap = {
'pending': '待审核',
'approved': '已通过',
'rejected': '已拒绝'
};
return statusMap[status] || '待审核';
},
// 查看图片详情
viewImageDetail(image) {
this.imageDetail = { ...image };
this.showImageDetail = true;
},
// 审核通过图片
approveImage(imageId, fromDetail = false) {
this.$confirm(`确定要审核通过该图片吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'success'
}).then(() => {
const index = this.images.findIndex(img => img.id === imageId);
if (index > -1) {
if (this.images[index].status === 'pending') {
this.images[index].status = 'approved';
this.imageStats.pending--;
this.imageStats.approved++;
}
this.$message.success('图片已审核通过');
// 如果从详情页操作,关闭详情页
if (fromDetail) {
this.showImageDetail = false;
}
this.initCharts(); // 重新初始化图表
}
}).catch(() => {
// 取消操作
});
},
// 审核拒绝图片
rejectImage(imageId, fromDetail = false) {
this.$confirm(`确定要拒绝该图片吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const index = this.images.findIndex(img => img.id === imageId);
if (index > -1) {
if (this.images[index].status === 'pending') {
this.images[index].status = 'rejected';
this.imageStats.pending--;
this.imageStats.rejected++;
}
this.$message.success('图片已拒绝');
// 如果从详情页操作,关闭详情页
if (fromDetail) {
this.showImageDetail = false;
}
this.initCharts(); // 重新初始化图表
}
}).catch(() => {
// 取消操作
});
},
// 批量审核通过
batchApprove() {
if (this.selectedImages.length === 0) {
this.$message.warning('请先选择要审核的图片');
return;
}
const pendingImages = this.selectedImages.filter(img => img.status === 'pending');
if (pendingImages.length === 0) {
this.$message.warning('选中的图片中没有待审核的图片');
return;
}
this.$confirm(`确定要批量审核通过这 ${pendingImages.length} 张图片吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'success'
}).then(() => {
pendingImages.forEach(img => {
const index = this.images.findIndex(i => i.id === img.id);
if (index > -1) {
this.images[index].status = 'approved';
this.imageStats.pending--;
this.imageStats.approved++;
}
});
this.$message.success(`已成功审核通过 ${pendingImages.length} 张图片`);
this.selectedImages = []; // 清空选中项
this.initCharts(); // 重新初始化图表
}).catch(() => {
// 取消操作
});
},
// 处理退出登录
handleLogout() {
this.$confirm('确定要退出登录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$message.success('退出成功');
setTimeout(() => {
window.location.href = 'admin-login.html';
}, 1000);
}).catch(() => {
// 取消退出
});
}
}
})
</script>
</body>
</html>