490 lines
11 KiB
Vue
490 lines
11 KiB
Vue
|
|
<template>
|
|||
|
|
<view class="log-manage-container">
|
|||
|
|
<!-- 页面标题 -->
|
|||
|
|
<view class="page-header">
|
|||
|
|
<view class="header-left" @click="goBack">
|
|||
|
|
<view class="back-icon">←</view>
|
|||
|
|
</view>
|
|||
|
|
<view class="header-title">操作日志查看</view>
|
|||
|
|
<view class="header-right" @click="exportLog">
|
|||
|
|
<view class="export-btn">导出</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 筛选条件 -->
|
|||
|
|
<view class="filter-section">
|
|||
|
|
<view class="filter-row">
|
|||
|
|
<view class="filter-item">
|
|||
|
|
<view class="filter-label">操作类型</view>
|
|||
|
|
<view class="filter-control">
|
|||
|
|
<picker
|
|||
|
|
:range="operationOptions"
|
|||
|
|
:value="operationIndex"
|
|||
|
|
@change="onOperationChange"
|
|||
|
|
class="picker"
|
|||
|
|
>
|
|||
|
|
<view class="picker-text">{{ operationOptions[operationIndex] }}</view>
|
|||
|
|
</picker>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
<view class="filter-item">
|
|||
|
|
<view class="filter-label">时间范围</view>
|
|||
|
|
<view class="filter-control">
|
|||
|
|
<picker
|
|||
|
|
mode="date"
|
|||
|
|
start="2026-01-01"
|
|||
|
|
end="2026-12-31"
|
|||
|
|
:value="dateRange"
|
|||
|
|
@change="onDateChange"
|
|||
|
|
class="picker"
|
|||
|
|
>
|
|||
|
|
<view class="picker-text">{{ dateRangeDisplay }}</view>
|
|||
|
|
</picker>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
<view class="filter-item">
|
|||
|
|
<view class="filter-label">搜索</view>
|
|||
|
|
<view class="filter-control">
|
|||
|
|
<input
|
|||
|
|
v-model="searchKeyword"
|
|||
|
|
class="search-input"
|
|||
|
|
placeholder="请输入操作人或账号"
|
|||
|
|
@input="onSearch"
|
|||
|
|
/>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 日志列表 -->
|
|||
|
|
<view class="log-list">
|
|||
|
|
<view
|
|||
|
|
v-for="(log, index) in logList"
|
|||
|
|
:key="index"
|
|||
|
|
class="log-item"
|
|||
|
|
>
|
|||
|
|
<view class="log-header">
|
|||
|
|
<view class="log-time">{{ log.time }}</view>
|
|||
|
|
<view class="log-status" :class="log.statusClass">{{ log.status }}</view>
|
|||
|
|
</view>
|
|||
|
|
<view class="log-content">
|
|||
|
|
<view class="log-operation">{{ log.operation }}</view>
|
|||
|
|
<view class="log-detail">{{ log.detail }}</view>
|
|||
|
|
</view>
|
|||
|
|
<view class="log-meta">
|
|||
|
|
<view class="log-operator">{{ log.operator }}</view>
|
|||
|
|
<view class="log-ip">{{ log.ip }}</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 空状态 -->
|
|||
|
|
<view v-if="logList.length === 0" class="empty-state">
|
|||
|
|
<view class="empty-icon">📋</view>
|
|||
|
|
<view class="empty-text">暂无操作日志</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 分页 -->
|
|||
|
|
<view v-if="logList.length > 0" class="pagination">
|
|||
|
|
<view class="page-info">
|
|||
|
|
共 {{ totalLogs }} 条,当前第 {{ currentPage }} 页
|
|||
|
|
</view>
|
|||
|
|
<view class="page-controls">
|
|||
|
|
<view
|
|||
|
|
class="page-btn"
|
|||
|
|
:class="{ disabled: currentPage === 1 }"
|
|||
|
|
@click="goToPage(currentPage - 1)"
|
|||
|
|
>
|
|||
|
|
上一页
|
|||
|
|
</view>
|
|||
|
|
<view
|
|||
|
|
class="page-btn"
|
|||
|
|
:class="{ disabled: currentPage === totalPages }"
|
|||
|
|
@click="goToPage(currentPage + 1)"
|
|||
|
|
>
|
|||
|
|
下一页
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
import { ref, computed, onMounted } from "vue"
|
|||
|
|
|
|||
|
|
// 操作类型选项
|
|||
|
|
const operationOptions = ['全部', '账号创建', '账号冻结', '账号启用', '密码修改', '权限变更']
|
|||
|
|
const operationIndex = ref(0)
|
|||
|
|
|
|||
|
|
// 日期范围
|
|||
|
|
const dateRange = ref('2026-01-29')
|
|||
|
|
const dateRangeDisplay = ref('2026-01-29')
|
|||
|
|
|
|||
|
|
// 搜索关键词
|
|||
|
|
const searchKeyword = ref('')
|
|||
|
|
|
|||
|
|
// 日志列表
|
|||
|
|
const logList = ref([
|
|||
|
|
{
|
|||
|
|
id: 1,
|
|||
|
|
time: '2026-01-29 15:30:45',
|
|||
|
|
operation: '账号创建',
|
|||
|
|
detail: '创建了新的分销员账号:张教练',
|
|||
|
|
operator: '系统管理员',
|
|||
|
|
ip: '192.168.1.100',
|
|||
|
|
status: '成功',
|
|||
|
|
statusClass: 'status-success'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 2,
|
|||
|
|
time: '2026-01-29 14:20:30',
|
|||
|
|
operation: '账号冻结',
|
|||
|
|
detail: '冻结了分销员账号:王教练',
|
|||
|
|
operator: '系统管理员',
|
|||
|
|
ip: '192.168.1.100',
|
|||
|
|
status: '成功',
|
|||
|
|
statusClass: 'status-success'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 3,
|
|||
|
|
time: '2026-01-29 10:15:20',
|
|||
|
|
operation: '密码修改',
|
|||
|
|
detail: '修改了账号密码:李教练',
|
|||
|
|
operator: '李教练',
|
|||
|
|
ip: '192.168.1.101',
|
|||
|
|
status: '成功',
|
|||
|
|
statusClass: 'status-success'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 4,
|
|||
|
|
time: '2026-01-28 16:45:10',
|
|||
|
|
operation: '权限变更',
|
|||
|
|
detail: '修改了分销员权限:张教练',
|
|||
|
|
operator: '系统管理员',
|
|||
|
|
ip: '192.168.1.100',
|
|||
|
|
status: '成功',
|
|||
|
|
statusClass: 'status-success'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 5,
|
|||
|
|
time: '2026-01-28 09:30:00',
|
|||
|
|
operation: '账号创建',
|
|||
|
|
detail: '创建了新的分销员账号:刘教练',
|
|||
|
|
operator: '系统管理员',
|
|||
|
|
ip: '192.168.1.100',
|
|||
|
|
status: '成功',
|
|||
|
|
statusClass: 'status-success'
|
|||
|
|
}
|
|||
|
|
])
|
|||
|
|
|
|||
|
|
// 分页信息
|
|||
|
|
const currentPage = ref(1)
|
|||
|
|
const totalLogs = ref(100)
|
|||
|
|
const pageSize = ref(10)
|
|||
|
|
|
|||
|
|
// 计算总页数
|
|||
|
|
const totalPages = computed(() => {
|
|||
|
|
return Math.ceil(totalLogs.value / pageSize.value)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
onMounted(() => {
|
|||
|
|
// 实际项目中应从接口获取操作日志
|
|||
|
|
// loadLogList()
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 返回上一页
|
|||
|
|
function goBack() {
|
|||
|
|
uni.navigateBack({ delta: 1 })
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 导出日志
|
|||
|
|
function exportLog() {
|
|||
|
|
uni.showModal({
|
|||
|
|
title: '导出日志',
|
|||
|
|
content: '确定要导出当前筛选条件的操作日志吗?',
|
|||
|
|
success: function(res) {
|
|||
|
|
if (res.confirm) {
|
|||
|
|
uni.showLoading({ title: '导出中...' })
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
uni.hideLoading()
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '导出成功',
|
|||
|
|
icon: 'success'
|
|||
|
|
})
|
|||
|
|
// 实际项目中应调用接口导出日志
|
|||
|
|
// exportLogData()
|
|||
|
|
}, 1500)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 操作类型变更
|
|||
|
|
function onOperationChange(e) {
|
|||
|
|
const value = e.detail.value
|
|||
|
|
operationIndex.value = value
|
|||
|
|
// 实际项目中应根据操作类型筛选日志
|
|||
|
|
// filterLogList()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 日期变更
|
|||
|
|
function onDateChange(e) {
|
|||
|
|
dateRange.value = e.detail.value
|
|||
|
|
dateRangeDisplay.value = e.detail.value
|
|||
|
|
// 实际项目中应根据日期筛选日志
|
|||
|
|
// filterLogList()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 搜索
|
|||
|
|
function onSearch() {
|
|||
|
|
// 实际项目中应根据关键词搜索日志
|
|||
|
|
// searchLogList()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳转到指定页码
|
|||
|
|
function goToPage(page) {
|
|||
|
|
if (page < 1 || page > totalPages.value) {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
currentPage.value = page
|
|||
|
|
// 实际项目中应加载指定页码的日志
|
|||
|
|
// loadLogList(page)
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
/* #ifndef APP-NVUE */
|
|||
|
|
page {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
background-color: #f5f7fa;
|
|||
|
|
min-height: 100%;
|
|||
|
|
height: auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
view {
|
|||
|
|
font-size: 14px;
|
|||
|
|
line-height: inherit;
|
|||
|
|
}
|
|||
|
|
/* #endif */
|
|||
|
|
|
|||
|
|
.log-manage-container {
|
|||
|
|
flex: 1;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 页面头部 */
|
|||
|
|
.page-header {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
height: 120rpx;
|
|||
|
|
background-color: #fff;
|
|||
|
|
padding: 0 32rpx;
|
|||
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.header-left {
|
|||
|
|
width: 60rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.back-icon {
|
|||
|
|
font-size: 40rpx;
|
|||
|
|
color: #303133;
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.header-title {
|
|||
|
|
font-size: 32rpx;
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: #303133;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.header-right {
|
|||
|
|
width: 60rpx;
|
|||
|
|
text-align: right;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.export-btn {
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #409eff;
|
|||
|
|
font-weight: 600;
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 筛选条件 */
|
|||
|
|
.filter-section {
|
|||
|
|
padding: 24rpx 32rpx;
|
|||
|
|
background-color: #fff;
|
|||
|
|
margin: 16rpx;
|
|||
|
|
border-radius: 16rpx;
|
|||
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.filter-row {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 16rpx;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.filter-item {
|
|||
|
|
flex: 1;
|
|||
|
|
min-width: 200rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.filter-label {
|
|||
|
|
font-size: 24rpx;
|
|||
|
|
color: #606266;
|
|||
|
|
margin-bottom: 8rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.filter-control {
|
|||
|
|
background-color: #f9f9f9;
|
|||
|
|
border: 1rpx solid #dcdfe6;
|
|||
|
|
border-radius: 8rpx;
|
|||
|
|
padding: 16rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.picker {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.picker-text {
|
|||
|
|
font-size: 24rpx;
|
|||
|
|
color: #303133;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.search-input {
|
|||
|
|
font-size: 24rpx;
|
|||
|
|
color: #303133;
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 日志列表 */
|
|||
|
|
.log-list {
|
|||
|
|
flex: 1;
|
|||
|
|
padding: 0 16rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.log-item {
|
|||
|
|
background-color: #fff;
|
|||
|
|
border-radius: 16rpx;
|
|||
|
|
padding: 24rpx;
|
|||
|
|
margin-bottom: 16rpx;
|
|||
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.log-header {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
margin-bottom: 16rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.log-time {
|
|||
|
|
font-size: 20rpx;
|
|||
|
|
color: #606266;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.log-status {
|
|||
|
|
font-size: 20rpx;
|
|||
|
|
font-weight: 600;
|
|||
|
|
padding: 4rpx 12rpx;
|
|||
|
|
border-radius: 12rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.status-success {
|
|||
|
|
color: #67c23a;
|
|||
|
|
background-color: rgba(103, 194, 58, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.status-fail {
|
|||
|
|
color: #f56c6c;
|
|||
|
|
background-color: rgba(245, 108, 108, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.log-content {
|
|||
|
|
margin-bottom: 16rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.log-operation {
|
|||
|
|
font-size: 24rpx;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #303133;
|
|||
|
|
margin-bottom: 8rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.log-detail {
|
|||
|
|
font-size: 20rpx;
|
|||
|
|
color: #606266;
|
|||
|
|
line-height: 1.4;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.log-meta {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
font-size: 20rpx;
|
|||
|
|
color: #909399;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 空状态 */
|
|||
|
|
.empty-state {
|
|||
|
|
flex: 1;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
padding: 100rpx 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-icon {
|
|||
|
|
font-size: 80rpx;
|
|||
|
|
margin-bottom: 24rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-text {
|
|||
|
|
font-size: 24rpx;
|
|||
|
|
color: #909399;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 分页 */
|
|||
|
|
.pagination {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 24rpx 32rpx;
|
|||
|
|
background-color: #fff;
|
|||
|
|
margin: 16rpx;
|
|||
|
|
border-radius: 16rpx;
|
|||
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.page-info {
|
|||
|
|
font-size: 20rpx;
|
|||
|
|
color: #606266;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.page-controls {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 16rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.page-btn {
|
|||
|
|
padding: 8rpx 16rpx;
|
|||
|
|
border-radius: 8rpx;
|
|||
|
|
font-size: 20rpx;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #409eff;
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.page-btn.disabled {
|
|||
|
|
color: #909399;
|
|||
|
|
cursor: not-allowed;
|
|||
|
|
}
|
|||
|
|
</style>
|