This commit is contained in:
bwstudio
2025-10-22 08:19:00 +08:00
parent 467b04e5b0
commit 97fbfe08d7
6040 changed files with 529099 additions and 9685 deletions
+197
View File
@@ -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>