Files
cpzs-frontend-new/src/views/admin/OperationHistory.vue

423 lines
10 KiB
Vue

<template>
<div class="operation-history">
<!-- 页面标题 -->
<div class="page-header">
<div class="header-content">
<h1>操作历史管理</h1>
<p>查看和管理系统操作历史记录</p>
</div>
</div>
<!-- 筛选器 -->
<el-card class="filter-card">
<template #header>
<div class="card-header">
<el-icon><Filter /></el-icon>
<span>筛选选项</span>
</div>
</template>
<el-form :inline="true" :model="filterForm" class="filter-form">
<el-form-item label="关键词">
<el-input
v-model="filterForm.keyword"
placeholder="搜索详细信息"
clearable
@input="handleFilter"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item label="操作模块">
<div class="custom-select-wrapper">
<select
v-model="filterForm.operationModule"
class="custom-select"
@change="handleFilter"
>
<option value="">全部模块</option>
<option value="0">会员码管理</option>
<option value="1">Excel导入</option>
</select>
</div>
</el-form-item>
<el-form-item label="操作结果">
<div class="custom-select-wrapper">
<select
v-model="filterForm.operationResult"
class="custom-select"
@change="handleFilter"
>
<option value="">全部结果</option>
<option value="成功">成功</option>
<option value="失败">失败</option>
</select>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleFilter">搜索</el-button>
<el-button @click="resetFilter">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 操作历史列表 -->
<el-card class="history-card">
<template #header>
<div class="card-header">
<el-icon><Clock /></el-icon>
<span>操作历史列表</span>
<div class="header-actions">
<el-button @click="refreshHistory">
<el-icon><Refresh /></el-icon>
刷新
</el-button>
</div>
</div>
</template>
<el-table
:data="historyList"
v-loading="loading"
stripe
style="width: 100%"
>
<el-table-column prop="operationTime" label="操作时间" width="180">
<template #default="{ row }">
{{ formatDate(row.operationTime) }}
</template>
</el-table-column>
<el-table-column prop="operationModule" label="操作模块" width="120">
<template #default="{ row }">
<el-tag>{{ getModuleText(row.operationModule) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="operationType" label="操作类型" width="150">
<template #default="{ row }">
<el-tag :type="getOperationType(row.operationType)">
{{ row.operationType }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="userName" label="操作人" width="120">
<template #default="{ row }">
{{ row.userName || '-' }}
</template>
</el-table-column>
<el-table-column prop="operationResult" label="操作结果" width="100">
<template #default="{ row }">
<el-tag :type="row.operationResult === '成功' ? 'success' : 'danger'">
{{ row.operationResult }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="operationDetail" label="详细信息" min-width="200">
<template #default="{ row }">
{{ row.operationDetail || row.resultMessage || '-' }}
</template>
</el-table-column>
</el-table>
<div class="pagination-wrapper">
<el-pagination
v-model:current-page="pagination.current"
v-model:page-size="pagination.size"
:page-sizes="[10, 20, 50]"
:total="pagination.total"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
</div>
</template>
<script>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { lotteryApi } from '../../api/index.js'
import { userStore } from '../../store/user.js'
export default {
name: 'OperationHistory',
setup() {
// 筛选表单
const filterForm = reactive({
operationModule: '',
operationResult: '',
keyword: ''
})
// 操作历史
const historyList = ref([])
const loading = ref(false)
const pagination = reactive({
current: 1,
size: 10,
total: 0
})
// 初始化
onMounted(() => {
loadOperationHistory()
})
// 加载操作历史
const loadOperationHistory = async () => {
try {
loading.value = true
// 构建查询参数
const params = {
operationModule: filterForm.operationModule,
operationResult: filterForm.operationResult,
keyword: filterForm.keyword
}
// 调用统一接口获取操作历史
const response = await lotteryApi.getOperationHistoryList(params)
console.log('操作历史接口响应:', response)
if (response && response.success) {
// 处理响应数据
const data = response.data || []
// 简单的前端分页
const startIndex = (pagination.current - 1) * pagination.size
const endIndex = startIndex + pagination.size
historyList.value = data.slice(startIndex, endIndex)
pagination.total = data.length
} else {
ElMessage.error('获取操作历史失败: ' + (response?.message || '未知错误'))
historyList.value = []
pagination.total = 0
}
} catch (error) {
console.error('加载操作历史失败:', error)
ElMessage.error('加载操作历史失败: ' + (error?.message || '未知错误'))
historyList.value = []
pagination.total = 0
} finally {
loading.value = false
}
}
// 刷新历史
const refreshHistory = () => {
pagination.current = 1
loadOperationHistory()
}
// 筛选处理
const handleFilter = () => {
pagination.current = 1
loadOperationHistory()
}
// 重置筛选
const resetFilter = () => {
filterForm.operationModule = ''
filterForm.operationResult = ''
filterForm.keyword = ''
pagination.current = 1
loadOperationHistory()
}
// 分页处理
const handleSizeChange = (size) => {
pagination.size = size
pagination.current = 1
loadOperationHistory()
}
const handleCurrentChange = (current) => {
pagination.current = current
loadOperationHistory()
}
// 获取操作模块文本
const getModuleText = (module) => {
const modules = {
0: '会员码管理',
1: 'Excel导入',
2: '用户管理'
}
return modules[module] || '未知模块'
}
// 获取操作类型标签样式
const getOperationType = (type) => {
const types = {
'完整数据导入': 'primary',
'开奖数据覆盖导入': 'warning',
'开奖数据追加': 'success',
'生成会员码': 'info',
'删除会员码': 'danger'
}
return types[type] || 'info'
}
// 格式化日期
const formatDate = (dateStr) => {
if (!dateStr) return '-'
const date = new Date(dateStr)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
return {
filterForm,
historyList,
loading,
pagination,
handleFilter,
resetFilter,
refreshHistory,
handleSizeChange,
handleCurrentChange,
getModuleText,
getOperationType,
formatDate
}
}
}
</script>
<style scoped>
.operation-history {
padding: 20px;
}
/* 页面标题 */
.page-header {
margin-bottom: 24px;
background: linear-gradient(135deg, #3a7bd5, #00d2ff);
padding: 30px;
border-radius: 8px;
color: white;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.header-content h1 {
font-size: 28px;
font-weight: 600;
color: white;
margin-bottom: 8px;
text-align: center;
}
.header-content p {
color: rgba(255, 255, 255, 0.9);
font-size: 16px;
margin: 0;
text-align: center;
}
/* 卡片样式 */
.filter-card, .history-card {
margin-bottom: 24px;
border: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
color: #333;
}
.card-header .el-icon {
font-size: 18px;
color: #409EFF;
}
.header-actions {
margin-left: auto;
}
/* 筛选表单 */
.filter-form {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
/* 自定义下拉框样式 */
.custom-select-wrapper {
position: relative;
width: 100%;
min-width: 160px;
}
.custom-select {
width: 100%;
padding: 8px 12px;
border: 1px solid #dcdfe6;
border-radius: 4px;
background-color: white;
font-size: 14px;
color: #606266;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23606266'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 16px;
cursor: pointer;
transition: all 0.3s;
}
.custom-select:hover {
border-color: #c0c4cc;
}
.custom-select:focus {
outline: none;
border-color: #409eff;
}
/* 分页 */
.pagination-wrapper {
margin-top: 20px;
display: flex;
justify-content: center;
}
/* 响应式设计 */
@media (max-width: 768px) {
.operation-history {
padding: 15px;
}
.filter-form {
flex-direction: column;
}
.filter-form .el-form-item {
margin-right: 0;
width: 100%;
}
.header-actions {
margin-top: 8px;
margin-left: 0;
}
}
</style>