home1022
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<view class="admin-center">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-back" @click="navigateBack">
|
||||
<u-icon name="arrow-left" size="24" color="#606266"></u-icon>
|
||||
</view>
|
||||
<view class="nav-title">管理中心</view>
|
||||
<view class="nav-right"></view>
|
||||
</view>
|
||||
|
||||
<!-- 管理员信息卡片 -->
|
||||
<view class="admin-card">
|
||||
<image class="admin-avatar" src="@/static/common/admin.svg" mode="aspectFill"></image>
|
||||
<view class="admin-info">
|
||||
<view class="admin-name">{{ adminName }}</view>
|
||||
<view class="admin-role">系统管理员</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能模块列表 -->
|
||||
<view class="module-list">
|
||||
<u-cell-group>
|
||||
<u-cell
|
||||
title="用户管理"
|
||||
icon="people"
|
||||
is-link
|
||||
@click="navigateTo('pages/admin-center/user-management/user-management')"
|
||||
>
|
||||
</u-cell>
|
||||
<u-cell
|
||||
title="部门管理"
|
||||
icon="office-building"
|
||||
is-link
|
||||
@click="navigateTo('pages/admin-center/dept-management/dept-management')"
|
||||
>
|
||||
</u-cell>
|
||||
<u-cell
|
||||
title="系统设置"
|
||||
icon="settings"
|
||||
is-link
|
||||
@click="navigateTo('pages/admin-center/system-settings/system-settings')"
|
||||
>
|
||||
</u-cell>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
|
||||
<!-- 统计信息 -->
|
||||
<view class="stats-section">
|
||||
<view class="stats-title">系统统计</view>
|
||||
<view class="stats-grid">
|
||||
<view class="stat-item">
|
||||
<view class="stat-number">{{ totalUsers }}</view>
|
||||
<view class="stat-label">总用户数</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<view class="stat-number">{{ totalDepartments }}</view>
|
||||
<view class="stat-label">部门数</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<view class="stat-number">{{ totalImages }}</view>
|
||||
<view class="stat-label">图片总数</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<view class="stat-number">{{ totalTasks }}</view>
|
||||
<view class="stat-label">任务总数</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
adminName: '管理员',
|
||||
totalUsers: 125,
|
||||
totalDepartments: 15,
|
||||
totalImages: 2580,
|
||||
totalTasks: 120
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
navigateBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
navigateTo(url) {
|
||||
uni.navigateTo({
|
||||
url: '/' + url
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.admin-center {
|
||||
padding-bottom: 20rpx;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 88rpx;
|
||||
padding: 0 30rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
margin: 20rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.admin-avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.admin-info {
|
||||
flex: 1;
|
||||
|
||||
.admin-name {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.admin-role {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.module-list {
|
||||
margin: 20rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
margin: 20rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx;
|
||||
|
||||
.stats-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
padding: 20rpx;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.stat-number {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #007AFF;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<view class="dept-management">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-back" @click="navigateBack">
|
||||
<u-icon name="arrow-left" size="24" color="#606266"></u-icon>
|
||||
</view>
|
||||
<view class="nav-title">部门管理</view>
|
||||
<view class="nav-right">
|
||||
<u-button type="text" @click="addDepartment">添加</u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-box">
|
||||
<u-search placeholder="搜索部门名称" v-model="searchKeyword"></u-search>
|
||||
</view>
|
||||
|
||||
<!-- 部门列表 -->
|
||||
<view class="dept-list">
|
||||
<u-cell-group v-if="filteredDepartments.length > 0">
|
||||
<u-cell
|
||||
v-for="dept in filteredDepartments"
|
||||
:key="dept.id"
|
||||
:title="dept.name"
|
||||
:label="dept.description ? `描述:${dept.description}` : '暂无描述'"
|
||||
:right-text="dept.leader ? dept.leader : '未分配负责人'"
|
||||
:right-icon="'arrow-right'"
|
||||
is-link
|
||||
@click="editDepartment(dept)"
|
||||
>
|
||||
</u-cell>
|
||||
</u-cell-group>
|
||||
<u-empty v-else text="暂无部门数据"></u-empty>
|
||||
</view>
|
||||
|
||||
<!-- 添加/编辑部门弹窗 -->
|
||||
<u-popup v-model="showDeptModal" mode="bottom" :round="true" :closeable="true">
|
||||
<view class="modal-content">
|
||||
<view class="modal-title">{{ isEditing ? '编辑部门' : '添加部门' }}</view>
|
||||
<u-form :model="formData" ref="uForm">
|
||||
<u-form-item label="部门名称">
|
||||
<u-input v-model="formData.name" placeholder="请输入部门名称"></u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="部门描述">
|
||||
<u-input v-model="formData.description" type="textarea" placeholder="请输入部门描述"></u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="部门负责人">
|
||||
<u-picker
|
||||
mode="selector"
|
||||
:range="managers"
|
||||
:rangeKey="'name'"
|
||||
v-model="formData.leaderIndex"
|
||||
placeholder="请选择部门负责人"
|
||||
></u-picker>
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
<view class="modal-buttons">
|
||||
<u-button @click="showDeptModal = false" style="flex: 1; margin-right: 20rpx;">取消</u-button>
|
||||
<u-button type="primary" @click="submitForm" style="flex: 1;">确定</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
searchKeyword: '',
|
||||
showDeptModal: false,
|
||||
isEditing: false,
|
||||
editDeptId: '',
|
||||
formData: {
|
||||
name: '',
|
||||
description: '',
|
||||
leaderIndex: 0
|
||||
},
|
||||
// 模拟的部门负责人数据(部门负责人和管理员)
|
||||
managers: [
|
||||
{ id: 1, name: '张三' },
|
||||
{ id: 2, name: '李四' },
|
||||
{ id: 3, name: '王五' },
|
||||
{ id: 6, name: '孙八' }
|
||||
],
|
||||
departments: [
|
||||
{ id: 1, name: '技术部', description: '负责系统开发和维护', leader: '张三' },
|
||||
{ id: 2, name: '市场部', description: '负责市场推广和销售', leader: '李四' },
|
||||
{ id: 3, name: '财务部', description: '负责财务管理和核算', leader: '王五' },
|
||||
{ id: 4, name: '人力资源部', description: '负责人事招聘和管理', leader: '' },
|
||||
{ id: 5, name: '行政部', description: '负责公司日常行政事务', leader: '孙八' }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredDepartments() {
|
||||
if (!this.searchKeyword) {
|
||||
return this.departments
|
||||
}
|
||||
return this.departments.filter(dept =>
|
||||
dept.name.includes(this.searchKeyword) ||
|
||||
(dept.description && dept.description.includes(this.searchKeyword))
|
||||
)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
navigateBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
addDepartment() {
|
||||
this.isEditing = false
|
||||
this.formData = {
|
||||
name: '',
|
||||
description: '',
|
||||
leaderIndex: 0
|
||||
}
|
||||
this.showDeptModal = true
|
||||
},
|
||||
editDepartment(dept) {
|
||||
this.isEditing = true
|
||||
this.editDeptId = dept.id
|
||||
const leaderIndex = this.managers.findIndex(m => m.name === dept.leader)
|
||||
this.formData = {
|
||||
name: dept.name,
|
||||
description: dept.description || '',
|
||||
leaderIndex: leaderIndex !== -1 ? leaderIndex : 0
|
||||
}
|
||||
this.showDeptModal = true
|
||||
},
|
||||
submitForm() {
|
||||
if (!this.formData.name) {
|
||||
uni.showToast({ title: '请输入部门名称', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const deptData = {
|
||||
name: this.formData.name,
|
||||
description: this.formData.description,
|
||||
leader: this.managers[this.formData.leaderIndex]?.name || ''
|
||||
}
|
||||
|
||||
if (this.isEditing) {
|
||||
// 编辑部门
|
||||
const index = this.departments.findIndex(d => d.id === this.editDeptId)
|
||||
if (index !== -1) {
|
||||
this.departments[index] = { ...this.departments[index], ...deptData }
|
||||
uni.showToast({ title: '编辑成功', icon: 'success' })
|
||||
}
|
||||
} else {
|
||||
// 添加部门
|
||||
const newId = Math.max(...this.departments.map(d => d.id), 0) + 1
|
||||
this.departments.push({ id: newId, ...deptData })
|
||||
uni.showToast({ title: '添加成功', icon: 'success' })
|
||||
}
|
||||
|
||||
this.showDeptModal = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.dept-management {
|
||||
padding-bottom: 20rpx;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 88rpx;
|
||||
padding: 0 30rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.search-box {
|
||||
padding: 20rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.dept-list {
|
||||
margin-top: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 30rpx;
|
||||
|
||||
.modal-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-buttons {
|
||||
display: flex;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,546 @@
|
||||
<template>
|
||||
<view class="clear-cache">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-back" @click="navigateBack">
|
||||
<u-icon name="arrow-left" size="24" color="#606266"></u-icon>
|
||||
</view>
|
||||
<view class="nav-title">清理缓存</view>
|
||||
<view class="nav-right">
|
||||
<u-button type="primary" size="small" @click="clearAllCache" :disabled="isClearing">
|
||||
{{ isClearing ? '清理中...' : '清理全部' }}
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 缓存概览 -->
|
||||
<view class="cache-overview">
|
||||
<view class="cache-card">
|
||||
<view class="cache-title">
|
||||
<u-icon name="folder-open" size="32" color="#606266" style="margin-right: 12rpx;"></u-icon>
|
||||
<text class="title-text">系统缓存</text>
|
||||
</view>
|
||||
<view class="cache-size">
|
||||
<text class="size-value">{{ totalCacheSize }}</text>
|
||||
<text class="size-unit">MB</text>
|
||||
</view>
|
||||
<view class="cache-desc">上次清理时间: {{ lastClearTime }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 缓存详情列表 -->
|
||||
<view class="cache-details">
|
||||
<view class="section-header">
|
||||
<text class="section-title">缓存详情</text>
|
||||
<text class="section-hint">点击可清理对应缓存</text>
|
||||
</view>
|
||||
|
||||
<u-list>
|
||||
<u-list-item v-for="item in cacheItems" :key="item.type" :border="true">
|
||||
<view class="cache-item" @click="clearSpecificCache(item.type)">
|
||||
<view class="cache-item-left">
|
||||
<u-icon :name="item.icon" size="32" color="#606266" style="margin-right: 12rpx;"></u-icon>
|
||||
<view class="cache-item-info">
|
||||
<view class="cache-item-name">{{ item.name }}</view>
|
||||
<view class="cache-item-desc">{{ item.description }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="cache-item-right">
|
||||
<text class="cache-item-size">{{ item.size }} MB</text>
|
||||
<u-icon name="arrow-right" size="24" color="#c0c4cc"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</u-list-item>
|
||||
</u-list>
|
||||
</view>
|
||||
|
||||
<!-- 缓存清理提示 -->
|
||||
<view class="cache-tips">
|
||||
<u-icon name="information-circle" size="24" color="#909399" style="margin-right: 12rpx;"></u-icon>
|
||||
<text class="tips-text">定期清理缓存可以提高系统运行速度,建议每周清理一次。</text>
|
||||
</view>
|
||||
|
||||
<!-- 清理历史 -->
|
||||
<view class="clear-history">
|
||||
<view class="section-header">
|
||||
<text class="section-title">清理历史</text>
|
||||
<u-button type="text" @click="clearHistory">清空历史</u-button>
|
||||
</view>
|
||||
|
||||
<u-list v-if="clearHistoryList.length > 0">
|
||||
<u-list-item v-for="history in clearHistoryList" :key="history.id" :border="true">
|
||||
<view class="history-item">
|
||||
<view class="history-content">
|
||||
<text class="history-action">{{ history.action }}</text>
|
||||
<text class="history-size">清理了 {{ history.clearedSize }} MB</text>
|
||||
</view>
|
||||
<view class="history-time">{{ formatTime(history.timestamp) }}</view>
|
||||
</view>
|
||||
</u-list-item>
|
||||
</u-list>
|
||||
<u-empty v-else text="暂无清理记录"></u-empty>
|
||||
</view>
|
||||
|
||||
<!-- 清理进度弹窗 -->
|
||||
<u-popup v-model="showProgressModal" mode="center" :round="true" :closeable="false" :mask-close-able="false">
|
||||
<view class="progress-modal-content">
|
||||
<view class="progress-title">{{ clearingTitle }}</view>
|
||||
<u-progress :percentage="clearProgress" :show-text="true" bar-height="10" active></u-progress>
|
||||
<view class="progress-text">{{ clearProgressText }}</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- 清理确认弹窗 -->
|
||||
<u-popup v-model="showConfirmModal" mode="center" :round="true">
|
||||
<view class="confirm-modal-content">
|
||||
<view class="confirm-title">确认清理</view>
|
||||
<view class="confirm-content">{{ confirmContent }}</view>
|
||||
<view class="confirm-buttons">
|
||||
<u-button @click="showConfirmModal = false" style="flex: 1; margin-right: 20rpx;">取消</u-button>
|
||||
<u-button type="primary" @click="confirmClearCache" style="flex: 1;">确认清理</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isClearing: false,
|
||||
clearProgress: 0,
|
||||
clearProgressText: '',
|
||||
clearingTitle: '',
|
||||
showProgressModal: false,
|
||||
showConfirmModal: false,
|
||||
confirmContent: '',
|
||||
cacheTypeToClear: '',
|
||||
totalCacheSize: '246.8',
|
||||
lastClearTime: '2023-08-01 10:30:00',
|
||||
cacheItems: [
|
||||
{
|
||||
type: 'image',
|
||||
name: '图片缓存',
|
||||
description: '临时存储的图片文件',
|
||||
size: '156.2',
|
||||
icon: 'image',
|
||||
color: '#409eff'
|
||||
},
|
||||
{
|
||||
type: 'data',
|
||||
name: '数据缓存',
|
||||
description: 'API请求数据缓存',
|
||||
size: '45.3',
|
||||
icon: 'document-text',
|
||||
color: '#67c23a'
|
||||
},
|
||||
{
|
||||
type: 'temp',
|
||||
name: '临时文件',
|
||||
description: '系统临时生成的文件',
|
||||
size: '28.7',
|
||||
icon: 'file-temp',
|
||||
color: '#faad14'
|
||||
},
|
||||
{
|
||||
type: 'log',
|
||||
name: '日志文件',
|
||||
description: '系统运行日志',
|
||||
size: '16.6',
|
||||
icon: 'document-copy',
|
||||
color: '#909399'
|
||||
}
|
||||
],
|
||||
clearHistoryList: [
|
||||
{
|
||||
id: 1,
|
||||
action: '清理全部缓存',
|
||||
clearedSize: '128.5',
|
||||
timestamp: 1690843200000
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
action: '清理图片缓存',
|
||||
clearedSize: '89.2',
|
||||
timestamp: 1690756800000
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
action: '清理日志文件',
|
||||
clearedSize: '12.3',
|
||||
timestamp: 1690670400000
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
navigateBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
formatTime(timestamp) {
|
||||
const date = new Date(timestamp)
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const hour = date.getHours().toString().padStart(2, '0')
|
||||
const minute = date.getMinutes().toString().padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hour}:${minute}`
|
||||
},
|
||||
clearAllCache() {
|
||||
this.showConfirmModal = true
|
||||
this.confirmContent = '确定要清理全部缓存吗?这将释放所有缓存空间。'
|
||||
this.cacheTypeToClear = 'all'
|
||||
},
|
||||
clearSpecificCache(type) {
|
||||
this.showConfirmModal = true
|
||||
const cacheItem = this.cacheItems.find(item => item.type === type)
|
||||
this.confirmContent = `确定要清理${cacheItem.name}吗?这将释放 ${cacheItem.size} MB 的存储空间。`
|
||||
this.cacheTypeToClear = type
|
||||
},
|
||||
confirmClearCache() {
|
||||
this.showConfirmModal = false
|
||||
this.isClearing = true
|
||||
this.clearProgress = 0
|
||||
this.showProgressModal = true
|
||||
|
||||
if (this.cacheTypeToClear === 'all') {
|
||||
this.clearingTitle = '清理全部缓存'
|
||||
this.clearProgressText = '准备清理...'
|
||||
} else {
|
||||
const cacheItem = this.cacheItems.find(item => item.type === this.cacheTypeToClear)
|
||||
this.clearingTitle = `清理${cacheItem.name}`
|
||||
this.clearProgressText = '准备清理...'
|
||||
}
|
||||
|
||||
// 模拟清理进度
|
||||
let progress = 0
|
||||
const interval = setInterval(() => {
|
||||
progress += 5
|
||||
this.clearProgress = progress
|
||||
|
||||
if (progress <= 30) {
|
||||
this.clearProgressText = '正在扫描文件...'
|
||||
} else if (progress <= 60) {
|
||||
this.clearProgressText = '正在清理缓存...'
|
||||
} else if (progress <= 90) {
|
||||
this.clearProgressText = '正在更新系统...'
|
||||
} else {
|
||||
this.clearProgressText = '清理完成!'
|
||||
}
|
||||
|
||||
if (progress >= 100) {
|
||||
clearInterval(interval)
|
||||
setTimeout(() => {
|
||||
this.isClearing = false
|
||||
this.showProgressModal = false
|
||||
|
||||
// 更新缓存大小
|
||||
if (this.cacheTypeToClear === 'all') {
|
||||
this.totalCacheSize = '0.0'
|
||||
this.cacheItems.forEach(item => {
|
||||
item.size = '0.0'
|
||||
})
|
||||
} else {
|
||||
const cacheItem = this.cacheItems.find(item => item.type === this.cacheTypeToClear)
|
||||
const clearedSize = parseFloat(cacheItem.size)
|
||||
cacheItem.size = '0.0'
|
||||
this.totalCacheSize = (parseFloat(this.totalCacheSize) - clearedSize).toFixed(1)
|
||||
}
|
||||
|
||||
// 更新最后清理时间
|
||||
const now = new Date()
|
||||
const year = now.getFullYear()
|
||||
const month = (now.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = now.getDate().toString().padStart(2, '0')
|
||||
const hour = now.getHours().toString().padStart(2, '0')
|
||||
const minute = now.getMinutes().toString().padStart(2, '0')
|
||||
const second = now.getSeconds().toString().padStart(2, '0')
|
||||
this.lastClearTime = `${year}-${month}-${day} ${hour}:${minute}:${second}`
|
||||
|
||||
// 添加到清理历史
|
||||
this.addToClearHistory()
|
||||
|
||||
uni.showToast({
|
||||
title: '缓存清理成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}, 200)
|
||||
},
|
||||
addToClearHistory() {
|
||||
let action, clearedSize
|
||||
|
||||
if (this.cacheTypeToClear === 'all') {
|
||||
action = '清理全部缓存'
|
||||
clearedSize = this.totalCacheSize
|
||||
} else {
|
||||
const cacheItem = this.cacheItems.find(item => item.type === this.cacheTypeToClear)
|
||||
action = `清理${cacheItem.name}`
|
||||
clearedSize = cacheItem.size
|
||||
}
|
||||
|
||||
const newHistory = {
|
||||
id: this.clearHistoryList.length + 1,
|
||||
action: action,
|
||||
clearedSize: clearedSize,
|
||||
timestamp: Date.now()
|
||||
}
|
||||
|
||||
this.clearHistoryList.unshift(newHistory)
|
||||
},
|
||||
clearHistory() {
|
||||
uni.showModal({
|
||||
title: '确认清空',
|
||||
content: '确定要清空清理历史记录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.clearHistoryList = []
|
||||
uni.showToast({
|
||||
title: '历史记录已清空',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.clear-cache {
|
||||
padding-bottom: 20rpx;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 88rpx;
|
||||
padding: 0 30rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.cache-overview {
|
||||
margin-top: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.cache-card {
|
||||
background: linear-gradient(135deg, #409eff, #67c23a);
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
|
||||
.cache-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.title-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.cache-size {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
gap: 5rpx;
|
||||
margin-bottom: 15rpx;
|
||||
|
||||
.size-value {
|
||||
font-size: 60rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.size-unit {
|
||||
font-size: 32rpx;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.cache-desc {
|
||||
font-size: 24rpx;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.cache-details {
|
||||
margin-top: 20rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.section-hint {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.cache-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
|
||||
.cache-item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.cache-item-info {
|
||||
.cache-item-name {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
margin-bottom: 5rpx;
|
||||
}
|
||||
|
||||
.cache-item-desc {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.cache-item-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
|
||||
.cache-item-size {
|
||||
font-size: 30rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cache-tips {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
margin: 20rpx;
|
||||
padding: 20rpx;
|
||||
background-color: #fdf6ec;
|
||||
border-radius: 10rpx;
|
||||
border-left: 4rpx solid #faad14;
|
||||
|
||||
.tips-text {
|
||||
font-size: 28rpx;
|
||||
color: #fa8c16;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.clear-history {
|
||||
margin-top: 20rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.history-item {
|
||||
padding: 20rpx;
|
||||
|
||||
.history-content {
|
||||
.history-action {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
margin-bottom: 5rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.history-size {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.history-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
display: block;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.progress-modal-content {
|
||||
padding: 40rpx;
|
||||
text-align: center;
|
||||
|
||||
.progress-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
margin-top: 20rpx;
|
||||
font-size: 30rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-modal-content {
|
||||
padding: 40rpx;
|
||||
|
||||
.confirm-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 20rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.confirm-content {
|
||||
font-size: 30rpx;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.confirm-buttons {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,492 @@
|
||||
<template>
|
||||
<view class="data-backup">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-back" @click="navigateBack">
|
||||
<u-icon name="arrow-left" size="24" color="#606266"></u-icon>
|
||||
</view>
|
||||
<view class="nav-title">数据备份</view>
|
||||
<view class="nav-right">
|
||||
<u-button type="primary" size="small" @click="createBackup" :disabled="isBackingUp">
|
||||
{{ isBackingUp ? '备份中...' : '立即备份' }}
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 备份设置 -->
|
||||
<view class="backup-settings">
|
||||
<u-cell-group>
|
||||
<u-cell
|
||||
title="自动备份频率"
|
||||
:right-text="`${autoBackupFrequency}小时`"
|
||||
is-link
|
||||
@click="showFrequencyModal = true"
|
||||
></u-cell>
|
||||
<u-cell
|
||||
title="保留备份数量"
|
||||
:right-text="`${maxBackups}个`"
|
||||
is-link
|
||||
@click="showMaxBackupsModal = true"
|
||||
></u-cell>
|
||||
<u-cell
|
||||
title="备份存储位置"
|
||||
:right-text="backupStorageLocation"
|
||||
is-link
|
||||
@click="selectStorageLocation"
|
||||
></u-cell>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
|
||||
<!-- 备份列表 -->
|
||||
<view class="backup-list">
|
||||
<view class="section-header">
|
||||
<text class="section-title">历史备份</text>
|
||||
<u-button type="text" @click="cleanOldBackups">清理旧备份</u-button>
|
||||
</view>
|
||||
|
||||
<u-list v-if="backups.length > 0" :load-more="loadMore" @loadmore="onLoadMore">
|
||||
<u-list-item v-for="backup in paginatedBackups" :key="backup.id" :border="true">
|
||||
<view class="backup-item">
|
||||
<view class="backup-header">
|
||||
<view class="backup-name">{{ backup.name }}</view>
|
||||
<view class="backup-time">{{ formatTime(backup.timestamp) }}</view>
|
||||
</view>
|
||||
<view class="backup-info">
|
||||
<text class="backup-size">大小: {{ backup.size }}</text>
|
||||
<text class="backup-type" :class="backup.type">类型: {{ backup.type === 'auto' ? '自动' : '手动' }}</text>
|
||||
</view>
|
||||
<view class="backup-actions">
|
||||
<u-button size="mini" type="primary" @click="downloadBackup(backup.id)">下载</u-button>
|
||||
<u-button size="mini" type="success" @click="restoreBackup(backup.id)">恢复</u-button>
|
||||
<u-button size="mini" type="error" @click="deleteBackup(backup.id)">删除</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-list-item>
|
||||
</u-list>
|
||||
<u-empty v-else text="暂无备份记录"></u-empty>
|
||||
</view>
|
||||
|
||||
<!-- 自动备份频率选择弹窗 -->
|
||||
<u-popup v-model="showFrequencyModal" mode="bottom" :round="true" :closeable="true">
|
||||
<view class="modal-content">
|
||||
<view class="modal-title">选择自动备份频率</view>
|
||||
<view class="picker-options">
|
||||
<u-button
|
||||
v-for="option in frequencyOptions"
|
||||
:key="option.value"
|
||||
:type="autoBackupFrequency === option.value ? 'primary' : 'default'"
|
||||
style="margin-bottom: 20rpx;"
|
||||
@click="selectFrequency(option.value)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</u-button>
|
||||
</view>
|
||||
<view class="modal-buttons">
|
||||
<u-button @click="showFrequencyModal = false">取消</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- 保留备份数量选择弹窗 -->
|
||||
<u-popup v-model="showMaxBackupsModal" mode="bottom" :round="true" :closeable="true">
|
||||
<view class="modal-content">
|
||||
<view class="modal-title">选择保留备份数量</view>
|
||||
<view class="picker-options">
|
||||
<u-button
|
||||
v-for="option in maxBackupsOptions"
|
||||
:key="option.value"
|
||||
:type="maxBackups === option.value ? 'primary' : 'default'"
|
||||
style="margin-bottom: 20rpx;"
|
||||
@click="selectMaxBackups(option.value)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</u-button>
|
||||
</view>
|
||||
<view class="modal-buttons">
|
||||
<u-button @click="showMaxBackupsModal = false">取消</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- 备份进度弹窗 -->
|
||||
<u-popup v-model="showProgressModal" mode="center" :round="true" :closeable="false" :mask-close-able="false">
|
||||
<view class="progress-modal-content">
|
||||
<view class="progress-title">正在备份数据</view>
|
||||
<u-progress :percentage="backupProgress" :show-text="true" bar-height="10" active></u-progress>
|
||||
<view class="progress-text">{{ backupProgressText }}</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isBackingUp: false,
|
||||
backupProgress: 0,
|
||||
backupProgressText: '准备备份...',
|
||||
showProgressModal: false,
|
||||
showFrequencyModal: false,
|
||||
showMaxBackupsModal: false,
|
||||
autoBackupFrequency: 24, // 默认24小时
|
||||
maxBackups: 10, // 默认保留10个备份
|
||||
backupStorageLocation: '本地存储',
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
loadMore: {
|
||||
loading: false,
|
||||
finished: false,
|
||||
error: false
|
||||
},
|
||||
frequencyOptions: [
|
||||
{ label: '6小时', value: 6 },
|
||||
{ label: '12小时', value: 12 },
|
||||
{ label: '24小时', value: 24 },
|
||||
{ label: '48小时', value: 48 },
|
||||
{ label: '72小时', value: 72 },
|
||||
{ label: '关闭自动备份', value: 0 }
|
||||
],
|
||||
maxBackupsOptions: [
|
||||
{ label: '5个', value: 5 },
|
||||
{ label: '10个', value: 10 },
|
||||
{ label: '20个', value: 20 },
|
||||
{ label: '50个', value: 50 }
|
||||
],
|
||||
backups: [
|
||||
{ id: 1, name: 'backup_20230801_120000', timestamp: 1690843200000, size: '12.5 MB', type: 'auto' },
|
||||
{ id: 2, name: 'backup_20230802_120000', timestamp: 1690929600000, size: '13.2 MB', type: 'auto' },
|
||||
{ id: 3, name: 'backup_20230803_103000', timestamp: 1691005800000, size: '14.1 MB', type: 'manual' },
|
||||
{ id: 4, name: 'backup_20230804_120000', timestamp: 1691092200000, size: '14.3 MB', type: 'auto' },
|
||||
{ id: 5, name: 'backup_20230805_120000', timestamp: 1691178600000, size: '14.8 MB', type: 'auto' },
|
||||
{ id: 6, name: 'backup_20230806_120000', timestamp: 1691265000000, size: '15.2 MB', type: 'auto' },
|
||||
{ id: 7, name: 'backup_20230807_091500', timestamp: 1691336100000, size: '15.5 MB', type: 'manual' },
|
||||
{ id: 8, name: 'backup_20230808_120000', timestamp: 1691434200000, size: '16.1 MB', type: 'auto' },
|
||||
{ id: 9, name: 'backup_20230809_120000', timestamp: 1691520600000, size: '16.5 MB', type: 'auto' },
|
||||
{ id: 10, name: 'backup_20230810_120000', timestamp: 1691607000000, size: '17.0 MB', type: 'auto' },
|
||||
{ id: 11, name: 'backup_20230811_120000', timestamp: 1691693400000, size: '17.3 MB', type: 'auto' },
|
||||
{ id: 12, name: 'backup_20230812_153000', timestamp: 1691779800000, size: '17.8 MB', type: 'manual' }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
paginatedBackups() {
|
||||
const start = (this.currentPage - 1) * this.pageSize
|
||||
const end = start + this.pageSize
|
||||
return this.backups.sort((a, b) => b.timestamp - a.timestamp).slice(start, end)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
navigateBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
formatTime(timestamp) {
|
||||
const date = new Date(timestamp)
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const hour = date.getHours().toString().padStart(2, '0')
|
||||
const minute = date.getMinutes().toString().padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hour}:${minute}`
|
||||
},
|
||||
createBackup() {
|
||||
this.isBackingUp = true
|
||||
this.backupProgress = 0
|
||||
this.backupProgressText = '准备备份...'
|
||||
this.showProgressModal = true
|
||||
|
||||
// 模拟备份进度
|
||||
let progress = 0
|
||||
const interval = setInterval(() => {
|
||||
progress += 5
|
||||
this.backupProgress = progress
|
||||
|
||||
if (progress <= 30) {
|
||||
this.backupProgressText = '正在收集数据...'
|
||||
} else if (progress <= 60) {
|
||||
this.backupProgressText = '正在压缩数据...'
|
||||
} else if (progress <= 90) {
|
||||
this.backupProgressText = '正在保存备份...'
|
||||
} else {
|
||||
this.backupProgressText = '备份完成!'
|
||||
}
|
||||
|
||||
if (progress >= 100) {
|
||||
clearInterval(interval)
|
||||
setTimeout(() => {
|
||||
this.isBackingUp = false
|
||||
this.showProgressModal = false
|
||||
|
||||
// 添加新备份到列表
|
||||
const now = new Date()
|
||||
const newBackup = {
|
||||
id: this.backups.length + 1,
|
||||
name: `backup_${now.getFullYear()}${(now.getMonth() + 1).toString().padStart(2, '0')}${now.getDate().toString().padStart(2, '0')}_${now.getHours().toString().padStart(2, '0')}${now.getMinutes().toString().padStart(2, '0')}${now.getSeconds().toString().padStart(2, '0')}`,
|
||||
timestamp: now.getTime(),
|
||||
size: `${(Math.random() * 5 + 15).toFixed(1)} MB`,
|
||||
type: 'manual'
|
||||
}
|
||||
this.backups.unshift(newBackup)
|
||||
this.currentPage = 1
|
||||
this.updateLoadMoreStatus()
|
||||
|
||||
uni.showToast({ title: '备份成功', icon: 'success' })
|
||||
}, 1000)
|
||||
}
|
||||
}, 200)
|
||||
},
|
||||
downloadBackup(id) {
|
||||
uni.showLoading({ title: '正在下载...' })
|
||||
|
||||
// 模拟下载
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
const backup = this.backups.find(b => b.id === id)
|
||||
uni.showToast({
|
||||
title: `备份文件 ${backup.name} 已下载`,
|
||||
icon: 'success'
|
||||
})
|
||||
}, 1500)
|
||||
},
|
||||
restoreBackup(id) {
|
||||
uni.showModal({
|
||||
title: '确认恢复',
|
||||
content: '恢复数据将覆盖当前系统数据,是否继续?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({ title: '正在恢复...' })
|
||||
|
||||
// 模拟恢复
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '数据恢复成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
deleteBackup(id) {
|
||||
uni.showModal({
|
||||
title: '确认删除',
|
||||
content: '确定要删除此备份文件吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.backups = this.backups.filter(backup => backup.id !== id)
|
||||
uni.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
})
|
||||
this.updateLoadMoreStatus()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
cleanOldBackups() {
|
||||
uni.showModal({
|
||||
title: '清理旧备份',
|
||||
content: `系统将只保留最近 ${this.maxBackups} 个备份文件,是否继续?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 模拟清理
|
||||
setTimeout(() => {
|
||||
const sortedBackups = this.backups.sort((a, b) => b.timestamp - a.timestamp)
|
||||
this.backups = sortedBackups.slice(0, this.maxBackups)
|
||||
uni.showToast({
|
||||
title: '清理完成',
|
||||
icon: 'success'
|
||||
})
|
||||
this.currentPage = 1
|
||||
this.updateLoadMoreStatus()
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
selectFrequency(value) {
|
||||
this.autoBackupFrequency = value
|
||||
this.showFrequencyModal = false
|
||||
uni.showToast({
|
||||
title: `自动备份频率已设置为${value === 0 ? '关闭' : value + '小时'}`,
|
||||
icon: 'success'
|
||||
})
|
||||
},
|
||||
selectMaxBackups(value) {
|
||||
this.maxBackups = value
|
||||
this.showMaxBackupsModal = false
|
||||
uni.showToast({
|
||||
title: `保留备份数量已设置为${value}个`,
|
||||
icon: 'success'
|
||||
})
|
||||
},
|
||||
selectStorageLocation() {
|
||||
// 模拟选择存储位置
|
||||
uni.showActionSheet({
|
||||
itemList: ['本地存储', '云存储'],
|
||||
success: (res) => {
|
||||
this.backupStorageLocation = res.tapIndex === 0 ? '本地存储' : '云存储'
|
||||
uni.showToast({
|
||||
title: `存储位置已设置为${this.backupStorageLocation}`,
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
onLoadMore() {
|
||||
if (this.loadMore.loading) return
|
||||
|
||||
this.loadMore.loading = true
|
||||
|
||||
// 模拟加载更多数据
|
||||
setTimeout(() => {
|
||||
this.currentPage++
|
||||
this.loadMore.loading = false
|
||||
this.updateLoadMoreStatus()
|
||||
}, 1000)
|
||||
},
|
||||
updateLoadMoreStatus() {
|
||||
const hasMore = this.paginatedBackups.length < this.backups.length
|
||||
this.loadMore.finished = !hasMore
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.data-backup {
|
||||
padding-bottom: 20rpx;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 88rpx;
|
||||
padding: 0 30rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.backup-settings {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.backup-list {
|
||||
margin-top: 20rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.backup-item {
|
||||
padding: 20rpx;
|
||||
|
||||
.backup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.backup-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.backup-time {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.backup-info {
|
||||
display: flex;
|
||||
gap: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.backup-size {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.backup-type {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.backup-type.auto {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.backup-type.manual {
|
||||
color: #67c23a;
|
||||
}
|
||||
}
|
||||
|
||||
.backup-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 30rpx;
|
||||
|
||||
.modal-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.picker-options {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.modal-buttons {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-modal-content {
|
||||
padding: 40rpx;
|
||||
text-align: center;
|
||||
|
||||
.progress-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
margin-top: 20rpx;
|
||||
font-size: 30rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,308 @@
|
||||
<template>
|
||||
<view class="operation-logs">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-back" @click="navigateBack">
|
||||
<u-icon name="arrow-left" size="24" color="#606266"></u-icon>
|
||||
</view>
|
||||
<view class="nav-title">操作日志</view>
|
||||
<view class="nav-right">
|
||||
<u-button type="text" @click="filterLogs">筛选</u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-box">
|
||||
<u-search placeholder="搜索操作人/操作内容" v-model="searchKeyword"></u-search>
|
||||
</view>
|
||||
|
||||
<!-- 时间范围选择 -->
|
||||
<view class="time-range">
|
||||
<u-cell-group>
|
||||
<u-cell
|
||||
title="时间范围"
|
||||
:right-text="`${startDate} 至 ${endDate}`"
|
||||
is-link
|
||||
@click="showDateRangeModal = true"
|
||||
></u-cell>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
|
||||
<!-- 操作日志列表 -->
|
||||
<view class="logs-list">
|
||||
<u-list v-if="filteredLogs.length > 0" :load-more="loadMore" @loadmore="onLoadMore">
|
||||
<u-list-item v-for="log in paginatedLogs" :key="log.id" :border="true">
|
||||
<view class="log-item">
|
||||
<view class="log-header">
|
||||
<view class="log-user">{{ log.userName }}</view>
|
||||
<view class="log-time">{{ formatTime(log.timestamp) }}</view>
|
||||
</view>
|
||||
<view class="log-content">{{ log.content }}</view>
|
||||
<view class="log-result">
|
||||
<u-tag :type="log.success ? 'success' : 'error'">
|
||||
{{ log.success ? '成功' : '失败' }}
|
||||
</u-tag>
|
||||
</view>
|
||||
</view>
|
||||
</u-list-item>
|
||||
</u-list>
|
||||
<u-empty v-else text="暂无操作日志"></u-empty>
|
||||
</view>
|
||||
|
||||
<!-- 时间范围选择弹窗 -->
|
||||
<u-popup v-model="showDateRangeModal" mode="bottom" :round="true" :closeable="true">
|
||||
<view class="modal-content">
|
||||
<view class="modal-title">选择时间范围</view>
|
||||
<view class="date-picker">
|
||||
<u-datetime-picker
|
||||
v-model="startDateTime"
|
||||
mode="date"
|
||||
:formatter="formatter"
|
||||
@confirm="onStartDateConfirm"
|
||||
></u-datetime-picker>
|
||||
<view class="date-separator">至</view>
|
||||
<u-datetime-picker
|
||||
v-model="endDateTime"
|
||||
mode="date"
|
||||
:formatter="formatter"
|
||||
@confirm="onEndDateConfirm"
|
||||
></u-datetime-picker>
|
||||
</view>
|
||||
<view class="modal-buttons">
|
||||
<u-button @click="showDateRangeModal = false" style="flex: 1; margin-right: 20rpx;">取消</u-button>
|
||||
<u-button type="primary" @click="confirmDateRange" style="flex: 1;">确定</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
searchKeyword: '',
|
||||
startDate: '2023-01-01',
|
||||
endDate: '2023-12-31',
|
||||
startDateTime: [2023, 0, 1],
|
||||
endDateTime: [2023, 11, 31],
|
||||
showDateRangeModal: false,
|
||||
currentPage: 1,
|
||||
pageSize: 20,
|
||||
loadMore: {
|
||||
loading: false,
|
||||
finished: false,
|
||||
error: false
|
||||
},
|
||||
logs: [
|
||||
{ id: 1, userName: '张三', content: '添加新用户:李四', timestamp: 1691234567890, success: true },
|
||||
{ id: 2, userName: '李四', content: '创建新部门:市场部', timestamp: 1691234567891, success: true },
|
||||
{ id: 3, userName: '王五', content: '上传图片:项目截图.jpg', timestamp: 1691234567892, success: true },
|
||||
{ id: 4, userName: '张三', content: '审核图片:项目截图.jpg', timestamp: 1691234567893, success: true },
|
||||
{ id: 5, userName: '孙八', content: '修改用户角色:李四 - 部门负责人', timestamp: 1691234567894, success: true },
|
||||
{ id: 6, userName: '张三', content: '删除过期图片', timestamp: 1691234567895, success: false },
|
||||
{ id: 7, userName: '李四', content: '创建收集任务:季度报告图片', timestamp: 1691234567896, success: true },
|
||||
{ id: 8, userName: '王五', content: '登录系统', timestamp: 1691234567897, success: true },
|
||||
{ id: 9, userName: '张三', content: '修改系统设置:最大上传图片大小', timestamp: 1691234567898, success: true },
|
||||
{ id: 10, userName: '李四', content: '批量审核图片', timestamp: 1691234567899, success: true },
|
||||
{ id: 11, userName: '王五', content: '查看任务统计报表', timestamp: 1691234567900, success: true },
|
||||
{ id: 12, userName: '张三', content: '系统数据备份', timestamp: 1691234567901, success: true },
|
||||
{ id: 13, userName: '李四', content: '修改部门负责人:技术部', timestamp: 1691234567902, success: true },
|
||||
{ id: 14, userName: '王五', content: '上传图片:活动照片.png', timestamp: 1691234567903, success: true },
|
||||
{ id: 15, userName: '张三', content: '修改用户密码:王五', timestamp: 1691234567904, success: false },
|
||||
{ id: 16, userName: '李四', content: '查看用户列表', timestamp: 1691234567905, success: true },
|
||||
{ id: 17, userName: '王五', content: '修改任务截止时间', timestamp: 1691234567906, success: true },
|
||||
{ id: 18, userName: '张三', content: '清理系统缓存', timestamp: 1691234567907, success: true },
|
||||
{ id: 19, userName: '李四', content: '上传图片:产品展示.jpg', timestamp: 1691234567908, success: true },
|
||||
{ id: 20, userName: '王五', content: '退出系统', timestamp: 1691234567909, success: true },
|
||||
{ id: 21, userName: '张三', content: '修改用户信息:李四', timestamp: 1691234567910, success: true },
|
||||
{ id: 22, userName: '李四', content: '删除部门:测试部', timestamp: 1691234567911, success: true }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredLogs() {
|
||||
let result = this.logs
|
||||
|
||||
// 按关键词搜索
|
||||
if (this.searchKeyword) {
|
||||
result = result.filter(log =>
|
||||
log.userName.includes(this.searchKeyword) ||
|
||||
log.content.includes(this.searchKeyword)
|
||||
)
|
||||
}
|
||||
|
||||
// 按时间范围过滤
|
||||
const startTimestamp = new Date(this.startDate).getTime()
|
||||
const endTimestamp = new Date(this.endDate).setHours(23, 59, 59, 999)
|
||||
result = result.filter(log =>
|
||||
log.timestamp >= startTimestamp && log.timestamp <= endTimestamp
|
||||
)
|
||||
|
||||
// 按时间倒序排列
|
||||
return result.sort((a, b) => b.timestamp - a.timestamp)
|
||||
},
|
||||
paginatedLogs() {
|
||||
const start = (this.currentPage - 1) * this.pageSize
|
||||
const end = start + this.pageSize
|
||||
return this.filteredLogs.slice(start, end)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
navigateBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
formatTime(timestamp) {
|
||||
const date = new Date(timestamp)
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const hour = date.getHours().toString().padStart(2, '0')
|
||||
const minute = date.getMinutes().toString().padStart(2, '0')
|
||||
const second = date.getSeconds().toString().padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
|
||||
},
|
||||
formatter(type, value) {
|
||||
if (type === 'year') {
|
||||
return `${value}年`
|
||||
} else if (type === 'month') {
|
||||
return `${value}月`
|
||||
} else if (type === 'day') {
|
||||
return `${value}日`
|
||||
}
|
||||
return value
|
||||
},
|
||||
onStartDateConfirm(e) {
|
||||
const [year, month, day] = e
|
||||
this.startDate = `${year}-${(month + 1).toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`
|
||||
},
|
||||
onEndDateConfirm(e) {
|
||||
const [year, month, day] = e
|
||||
this.endDate = `${year}-${(month + 1).toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`
|
||||
},
|
||||
confirmDateRange() {
|
||||
this.showDateRangeModal = false
|
||||
this.currentPage = 1
|
||||
this.updateLoadMoreStatus()
|
||||
},
|
||||
filterLogs() {
|
||||
// 筛选功能可以进一步扩展
|
||||
uni.showToast({ title: '筛选功能开发中', icon: 'none' })
|
||||
},
|
||||
onLoadMore() {
|
||||
if (this.loadMore.loading) return
|
||||
|
||||
this.loadMore.loading = true
|
||||
|
||||
// 模拟加载更多数据
|
||||
setTimeout(() => {
|
||||
this.currentPage++
|
||||
this.loadMore.loading = false
|
||||
this.updateLoadMoreStatus()
|
||||
}, 1000)
|
||||
},
|
||||
updateLoadMoreStatus() {
|
||||
const hasMore = this.paginatedLogs.length < this.filteredLogs.length
|
||||
this.loadMore.finished = !hasMore
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.operation-logs {
|
||||
padding-bottom: 20rpx;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 88rpx;
|
||||
padding: 0 30rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.search-box {
|
||||
padding: 20rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.time-range {
|
||||
margin-top: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.logs-list {
|
||||
margin-top: 20rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.log-item {
|
||||
padding: 20rpx;
|
||||
|
||||
.log-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.log-user {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.log-time {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.log-content {
|
||||
font-size: 30rpx;
|
||||
color: #666;
|
||||
margin-bottom: 10rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.log-result {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 30rpx;
|
||||
|
||||
.modal-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.date-picker {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.date-separator {
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
font-size: 32rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-buttons {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<view class="system-settings">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-back" @click="navigateBack">
|
||||
<u-icon name="arrow-left" size="24" color="#606266"></u-icon>
|
||||
</view>
|
||||
<view class="nav-title">系统设置</view>
|
||||
<view class="nav-right"></view>
|
||||
</view>
|
||||
|
||||
<!-- 系统参数设置 -->
|
||||
<view class="settings-section">
|
||||
<view class="section-title">系统参数设置</view>
|
||||
<u-cell-group>
|
||||
<u-cell
|
||||
title="最大上传图片大小"
|
||||
:right-text="`${maxImageSize}MB`"
|
||||
is-link
|
||||
@click="showImageSizeModal = true"
|
||||
></u-cell>
|
||||
<u-cell
|
||||
title="图片审核时效"
|
||||
:right-text="`${reviewTimeLimit}小时`"
|
||||
is-link
|
||||
@click="showReviewTimeModal = true"
|
||||
></u-cell>
|
||||
<u-cell
|
||||
title="数据自动备份"
|
||||
:right-icon="'arrow-right'"
|
||||
>
|
||||
<template #right>
|
||||
<u-switch v-model="autoBackup"></u-switch>
|
||||
</template>
|
||||
</u-cell>
|
||||
<u-cell
|
||||
title="通知提醒"
|
||||
:right-icon="'arrow-right'"
|
||||
>
|
||||
<template #right>
|
||||
<u-switch v-model="enableNotifications"></u-switch>
|
||||
</template>
|
||||
</u-cell>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
|
||||
<!-- 数据管理 -->
|
||||
<view class="settings-section">
|
||||
<view class="section-title">数据管理</view>
|
||||
<u-cell-group>
|
||||
<u-cell
|
||||
title="手动备份数据"
|
||||
:right-icon="'arrow-right'"
|
||||
is-link
|
||||
@click="navigateTo('data-backup')"
|
||||
></u-cell>
|
||||
<u-cell
|
||||
title="清理缓存"
|
||||
:right-text="cacheSize"
|
||||
is-link
|
||||
@click="navigateTo('clear-cache')"
|
||||
></u-cell>
|
||||
<u-cell
|
||||
title="操作日志"
|
||||
:right-icon="'arrow-right'"
|
||||
is-link
|
||||
@click="navigateTo('operation-logs')"
|
||||
></u-cell>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
|
||||
<!-- 关于系统 -->
|
||||
<view class="settings-section">
|
||||
<view class="section-title">关于系统</view>
|
||||
<u-cell-group>
|
||||
<u-cell
|
||||
title="系统版本"
|
||||
:right-text="systemVersion"
|
||||
></u-cell>
|
||||
<u-cell
|
||||
title="开发团队"
|
||||
:right-text="'技术部'"
|
||||
></u-cell>
|
||||
<u-cell
|
||||
title="服务协议"
|
||||
is-link
|
||||
@click="viewAgreement"
|
||||
></u-cell>
|
||||
<u-cell
|
||||
title="隐私政策"
|
||||
is-link
|
||||
@click="viewPrivacy"
|
||||
></u-cell>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
|
||||
<!-- 退出登录 -->
|
||||
<view class="logout-section">
|
||||
<u-button type="error" @click="logout">退出登录</u-button>
|
||||
</view>
|
||||
|
||||
<!-- 图片大小设置弹窗 -->
|
||||
<u-popup v-model="showImageSizeModal" mode="center" :closeable="true">
|
||||
<view class="modal-content">
|
||||
<view class="modal-title">设置最大上传图片大小</view>
|
||||
<view class="slider-container">
|
||||
<u-slider v-model="maxImageSize" :min="1" :max="20" :step="1"></u-slider>
|
||||
<view class="slider-value">{{ maxImageSize }}MB</view>
|
||||
</view>
|
||||
<view class="modal-buttons">
|
||||
<u-button @click="showImageSizeModal = false" style="flex: 1; margin-right: 20rpx;">取消</u-button>
|
||||
<u-button type="primary" @click="confirmImageSize" style="flex: 1;">确定</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- 审核时效设置弹窗 -->
|
||||
<u-popup v-model="showReviewTimeModal" mode="center" :closeable="true">
|
||||
<view class="modal-content">
|
||||
<view class="modal-title">设置图片审核时效</view>
|
||||
<view class="slider-container">
|
||||
<u-slider v-model="reviewTimeLimit" :min="1" :max="48" :step="1"></u-slider>
|
||||
<view class="slider-value">{{ reviewTimeLimit }}小时</view>
|
||||
</view>
|
||||
<view class="modal-buttons">
|
||||
<u-button @click="showReviewTimeModal = false" style="flex: 1; margin-right: 20rpx;">取消</u-button>
|
||||
<u-button type="primary" @click="confirmReviewTime" style="flex: 1;">确定</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 系统参数
|
||||
maxImageSize: 10,
|
||||
reviewTimeLimit: 24,
|
||||
autoBackup: true,
|
||||
enableNotifications: true,
|
||||
cacheSize: '12.3MB',
|
||||
systemVersion: 'v1.0.0',
|
||||
|
||||
// 弹窗控制
|
||||
showImageSizeModal: false,
|
||||
showReviewTimeModal: false,
|
||||
tempImageSize: 10,
|
||||
tempReviewTime: 24
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
navigateBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
// 导航到子页面
|
||||
navigateTo(pageName) {
|
||||
uni.navigateTo({
|
||||
url: `./${pageName}`
|
||||
})
|
||||
},
|
||||
confirmImageSize() {
|
||||
this.tempImageSize = this.maxImageSize
|
||||
this.showImageSizeModal = false
|
||||
uni.showToast({ title: '设置成功', icon: 'success' })
|
||||
},
|
||||
confirmReviewTime() {
|
||||
this.tempReviewTime = this.reviewTimeLimit
|
||||
this.showReviewTimeModal = false
|
||||
uni.showToast({ title: '设置成功', icon: 'success' })
|
||||
},
|
||||
viewAgreement() {
|
||||
uni.showToast({ title: '查看服务协议', icon: 'none' })
|
||||
},
|
||||
viewPrivacy() {
|
||||
uni.showToast({ title: '查看隐私政策', icon: 'none' })
|
||||
},
|
||||
logout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要退出登录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.redirectTo({
|
||||
url: '/pages/login/index'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.system-settings {
|
||||
padding-bottom: 40rpx;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 88rpx;
|
||||
padding: 0 30rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
margin-top: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-bottom: 10rpx;
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.logout-section {
|
||||
margin-top: 40rpx;
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 30rpx;
|
||||
width: 80%;
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.modal-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.slider-container {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.slider-value {
|
||||
text-align: center;
|
||||
margin-top: 20rpx;
|
||||
font-size: 32rpx;
|
||||
color: #007AFF;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-buttons {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<view class="user-management">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-back" @click="navigateBack">
|
||||
<u-icon name="arrow-left" size="24" color="#606266"></u-icon>
|
||||
</view>
|
||||
<view class="nav-title">用户管理</view>
|
||||
<view class="nav-right">
|
||||
<u-button type="text" @click="addUser">添加</u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-box">
|
||||
<u-search placeholder="搜索用户名/手机号" v-model="searchKeyword"></u-search>
|
||||
</view>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
<view class="user-list">
|
||||
<u-cell-group v-if="filteredUsers.length > 0">
|
||||
<u-cell
|
||||
v-for="(user, index) in filteredUsers"
|
||||
:key="user.id"
|
||||
:title="user.name"
|
||||
:label="`手机号:${user.phone} | 角色:${getRoleName(user.role)}`"
|
||||
:right-icon="'arrow-right'"
|
||||
is-link
|
||||
@click="editUser(user)"
|
||||
>
|
||||
<template #right>
|
||||
<u-tag :type="getRoleType(user.role)">{{ getRoleName(user.role) }}</u-tag>
|
||||
</template>
|
||||
</u-cell>
|
||||
</u-cell-group>
|
||||
<u-empty v-else text="暂无用户数据"></u-empty>
|
||||
</view>
|
||||
|
||||
<!-- 分页 -->
|
||||
<u-pagination
|
||||
v-if="filteredUsers.length > 0"
|
||||
:current="currentPage"
|
||||
:pageSize="pageSize"
|
||||
:total="filteredUsers.length"
|
||||
@change="changePage"
|
||||
></u-pagination>
|
||||
|
||||
<!-- 添加/编辑用户弹窗 -->
|
||||
<u-popup v-model="showUserModal" mode="bottom" :round="true" :closeable="true">
|
||||
<view class="modal-content">
|
||||
<view class="modal-title">{{ isEditing ? '编辑用户' : '添加用户' }}</view>
|
||||
<u-form :model="formData" ref="uForm">
|
||||
<u-form-item label="姓名">
|
||||
<u-input v-model="formData.name" placeholder="请输入姓名"></u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="手机号">
|
||||
<u-input v-model="formData.phone" type="number" placeholder="请输入手机号"></u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="角色">
|
||||
<u-picker
|
||||
mode="selector"
|
||||
:range="roles"
|
||||
:rangeKey="'name'"
|
||||
v-model="formData.roleIndex"
|
||||
placeholder="请选择角色"
|
||||
></u-picker>
|
||||
</u-form-item>
|
||||
<u-form-item label="部门">
|
||||
<u-picker
|
||||
mode="selector"
|
||||
:range="departments"
|
||||
:rangeKey="'name'"
|
||||
v-model="formData.deptIndex"
|
||||
placeholder="请选择部门"
|
||||
></u-picker>
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
<view class="modal-buttons">
|
||||
<u-button @click="showUserModal = false" style="flex: 1; margin-right: 20rpx;">取消</u-button>
|
||||
<u-button type="primary" @click="submitForm" style="flex: 1;">确定</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
searchKeyword: '',
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
showUserModal: false,
|
||||
isEditing: false,
|
||||
editUserId: '',
|
||||
formData: {
|
||||
name: '',
|
||||
phone: '',
|
||||
roleIndex: 0,
|
||||
deptIndex: 0
|
||||
},
|
||||
roles: [
|
||||
{ id: 1, name: '管理员' },
|
||||
{ id: 2, name: '信息员' },
|
||||
{ id: 3, name: '部门负责人' },
|
||||
{ id: 4, name: '普通用户' }
|
||||
],
|
||||
departments: [
|
||||
{ id: 1, name: '技术部' },
|
||||
{ id: 2, name: '市场部' },
|
||||
{ id: 3, name: '财务部' },
|
||||
{ id: 4, name: '人力资源部' },
|
||||
{ id: 5, name: '行政部' }
|
||||
],
|
||||
users: [
|
||||
{ id: 1, name: '张三', phone: '13800138001', role: 1, deptId: 1 },
|
||||
{ id: 2, name: '李四', phone: '13800138002', role: 2, deptId: 2 },
|
||||
{ id: 3, name: '王五', phone: '13800138003', role: 3, deptId: 3 },
|
||||
{ id: 4, name: '赵六', phone: '13800138004', role: 4, deptId: 4 },
|
||||
{ id: 5, name: '钱七', phone: '13800138005', role: 4, deptId: 5 },
|
||||
{ id: 6, name: '孙八', phone: '13800138006', role: 3, deptId: 1 },
|
||||
{ id: 7, name: '周九', phone: '13800138007', role: 2, deptId: 3 },
|
||||
{ id: 8, name: '吴十', phone: '13800138008', role: 4, deptId: 2 }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredUsers() {
|
||||
if (!this.searchKeyword) {
|
||||
return this.users
|
||||
}
|
||||
return this.users.filter(user =>
|
||||
user.name.includes(this.searchKeyword) ||
|
||||
user.phone.includes(this.searchKeyword)
|
||||
)
|
||||
},
|
||||
paginatedUsers() {
|
||||
const start = (this.currentPage - 1) * this.pageSize
|
||||
const end = start + this.pageSize
|
||||
return this.filteredUsers.slice(start, end)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
navigateBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
getRoleName(roleId) {
|
||||
const role = this.roles.find(r => r.id === roleId)
|
||||
return role ? role.name : '未知'
|
||||
},
|
||||
getRoleType(roleId) {
|
||||
switch (roleId) {
|
||||
case 1: return 'error'
|
||||
case 2: return 'success'
|
||||
case 3: return 'warning'
|
||||
case 4: return 'info'
|
||||
default: return 'info'
|
||||
}
|
||||
},
|
||||
addUser() {
|
||||
this.isEditing = false
|
||||
this.formData = {
|
||||
name: '',
|
||||
phone: '',
|
||||
roleIndex: 0,
|
||||
deptIndex: 0
|
||||
}
|
||||
this.showUserModal = true
|
||||
},
|
||||
editUser(user) {
|
||||
this.isEditing = true
|
||||
this.editUserId = user.id
|
||||
const roleIndex = this.roles.findIndex(r => r.id === user.role)
|
||||
const deptIndex = this.departments.findIndex(d => d.id === user.deptId)
|
||||
this.formData = {
|
||||
name: user.name,
|
||||
phone: user.phone,
|
||||
roleIndex: roleIndex !== -1 ? roleIndex : 0,
|
||||
deptIndex: deptIndex !== -1 ? deptIndex : 0
|
||||
}
|
||||
this.showUserModal = true
|
||||
},
|
||||
submitForm() {
|
||||
if (!this.formData.name || !this.formData.phone) {
|
||||
uni.showToast({ title: '请填写完整信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const userData = {
|
||||
name: this.formData.name,
|
||||
phone: this.formData.phone,
|
||||
role: this.roles[this.formData.roleIndex].id,
|
||||
deptId: this.departments[this.formData.deptIndex].id
|
||||
}
|
||||
|
||||
if (this.isEditing) {
|
||||
// 编辑用户
|
||||
const index = this.users.findIndex(u => u.id === this.editUserId)
|
||||
if (index !== -1) {
|
||||
this.users[index] = { ...this.users[index], ...userData }
|
||||
uni.showToast({ title: '编辑成功', icon: 'success' })
|
||||
}
|
||||
} else {
|
||||
// 添加用户
|
||||
const newId = Math.max(...this.users.map(u => u.id), 0) + 1
|
||||
this.users.push({ id: newId, ...userData })
|
||||
uni.showToast({ title: '添加成功', icon: 'success' })
|
||||
}
|
||||
|
||||
this.showUserModal = false
|
||||
},
|
||||
changePage(page) {
|
||||
this.currentPage = page
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.user-management {
|
||||
padding-bottom: 20rpx;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 88rpx;
|
||||
padding: 0 30rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.search-box {
|
||||
padding: 20rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.user-list {
|
||||
margin-top: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding: 30rpx;
|
||||
|
||||
.modal-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-buttons {
|
||||
display: flex;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user