补充页面内容

This commit is contained in:
2026-02-11 19:51:32 +08:00
parent 50ddf01e7c
commit 53820a4654
30 changed files with 5915 additions and 259 deletions

View File

@@ -125,9 +125,9 @@ export const componentMap: Map<string, any> = new Map([
// 设置模块
['SettingSystemConfig', defineAsyncComponent(() => import('@/pages/mall/admin/setting/system/config.uvue'))],
['SettingMessage', defineAsyncComponent(() => import('@/pages/mall/admin/setting/message.uvue'))],
['SettingAgreement', defineAsyncComponent(() => import('@/pages/mall/admin/setting/agreement.uvue'))],
['SettingTicket', defineAsyncComponent(() => import('@/pages/mall/admin/setting/ticket.uvue'))],
['SettingMessageIndex', defineAsyncComponent(() => import('@/pages/mall/admin/setting/message.uvue'))],
['SettingProtocolIndex', defineAsyncComponent(() => import('@/pages/mall/admin/setting/agreement.uvue'))],
['SettingTicketIndex', defineAsyncComponent(() => import('@/pages/mall/admin/setting/ticket.uvue'))],
['SettingAuthRole', defineAsyncComponent(() => import('@/pages/mall/admin/setting/auth/role.uvue'))],
['SettingAuthAdmin', defineAsyncComponent(() => import('@/pages/mall/admin/setting/auth/admin.uvue'))],
['SettingAuthPermission', defineAsyncComponent(() => import('@/pages/mall/admin/setting/auth/permission.uvue'))],
@@ -173,28 +173,28 @@ export const componentMap: Map<string, any> = new Map([
['AppList', PlaceholderPage],
// 维护模块
['MaintainDevConfig', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev/config.uvue'))],
['MaintainDevData', PlaceholderPage],
['MaintainDevTask', PlaceholderPage],
['MaintainDevAuth', PlaceholderPage],
['MaintainDevModule', PlaceholderPage],
['MaintainDevEvent', PlaceholderPage],
['MaintainSecurityCache', PlaceholderPage],
['MaintainSecurityLog', PlaceholderPage],
['MaintainSecurityUpgrade', PlaceholderPage],
['MaintainDataLogistics', PlaceholderPage],
['MaintainDataCity', PlaceholderPage],
['MaintainDataClear', PlaceholderPage],
['MaintainApiAccount', PlaceholderPage],
['MaintainLangList', PlaceholderPage],
['MaintainLangDetail', PlaceholderPage],
['MaintainLangRegion', PlaceholderPage],
['MaintainLangConfig', PlaceholderPage],
['MaintainToolDb', PlaceholderPage],
['MaintainToolFile', PlaceholderPage],
['MaintainToolApi', PlaceholderPage],
['MaintainToolDic', PlaceholderPage],
['MaintainSysInfo', PlaceholderPage]
['MaintainDevConfig', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-config/category.uvue'))],
['MaintainDevData', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-config/combination-data.uvue'))],
['MaintainDevTask', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-config/cron-job.uvue'))],
['MaintainDevAuth', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-config/permission.uvue'))],
['MaintainDevModule', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-config/module-config.uvue'))],
['MaintainDevEvent', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-config/custom-event.uvue'))],
['MaintainSecurityCache', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/security/refresh-cache.uvue'))],
['MaintainSecurityLog', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/security/system-log.uvue'))],
['MaintainSecurityUpgrade', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/security/online-upgrade.uvue'))],
['MaintainDataLogistics', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/data/logistics.uvue'))],
['MaintainDataCity', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/data/city.uvue'))],
['MaintainDataClear', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/data/clear.uvue'))],
['MaintainApiAccount', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/api/account.uvue'))],
['MaintainLangList', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/lang/list.uvue'))],
['MaintainLangDetail', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/lang/detail.uvue'))],
['MaintainLangRegion', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/lang/region.uvue'))],
['MaintainLangConfig', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/lang/config.uvue'))],
['MaintainToolDb', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-tools/database.uvue'))],
['MaintainToolFile', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-tools/file.uvue'))],
['MaintainToolApi', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-tools/api.uvue'))],
['MaintainToolDic', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/dev-tools/data-dict.uvue'))],
['MaintainSysInfo', defineAsyncComponent(() => import('@/pages/mall/admin/maintain/sys/info.uvue'))]
])
/**

View File

@@ -197,7 +197,7 @@ export const topMenus: TopMenu[] = [
id: 'maintain',
title: '维护',
icon: 'tool',
path: '/pages/mall/admin/maintain/dev/config',
path: '/pages/mall/admin/maintain/dev-config/category',
order: 13,
groups: [
{ id: 'maintain-dev', title: '开发配置', order: 1 },
@@ -1294,17 +1294,17 @@ export const routes: RouteRecord[] = [
// ========== 维护模块 ==========
// 开发配置
{ id: 'maintain_dev_config', title: '配置分类', path: '/pages/mall/admin/maintain/dev/config', componentKey: 'MaintainDevConfig', parentId: 'maintain', groupId: 'maintain-dev', order: 1 },
{ id: 'maintain_dev_data', title: '组合数据', path: '/pages/mall/admin/maintain/dev/data', componentKey: 'MaintainDevData', parentId: 'maintain', groupId: 'maintain-dev', order: 2 },
{ id: 'maintain_dev_task', title: '定时任务', path: '/pages/mall/admin/maintain/dev/task', componentKey: 'MaintainDevTask', parentId: 'maintain', groupId: 'maintain-dev', order: 3 },
{ id: 'maintain_dev_auth', title: '权限维护', path: '/pages/mall/admin/maintain/dev/auth', componentKey: 'MaintainDevAuth', parentId: 'maintain', groupId: 'maintain-dev', order: 4 },
{ id: 'maintain_dev_module', title: '模块配置', path: '/pages/mall/admin/maintain/dev/module', componentKey: 'MaintainDevModule', parentId: 'maintain', groupId: 'maintain-dev', order: 5 },
{ id: 'maintain_dev_event', title: '自定事件', path: '/pages/mall/admin/maintain/dev/event', componentKey: 'MaintainDevEvent', parentId: 'maintain', groupId: 'maintain-dev', order: 6 },
{ id: 'maintain_dev_config', title: '配置分类', path: '/pages/mall/admin/maintain/dev-config/category', componentKey: 'MaintainDevConfig', parentId: 'maintain', groupId: 'maintain-dev', order: 1 },
{ id: 'maintain_dev_data', title: '组合数据', path: '/pages/mall/admin/maintain/dev-config/combination-data', componentKey: 'MaintainDevData', parentId: 'maintain', groupId: 'maintain-dev', order: 2 },
{ id: 'maintain_dev_task', title: '定时任务', path: '/pages/mall/admin/maintain/dev-config/cron-job', componentKey: 'MaintainDevTask', parentId: 'maintain', groupId: 'maintain-dev', order: 3 },
{ id: 'maintain_dev_auth', title: '权限维护', path: '/pages/mall/admin/maintain/dev-config/permission', componentKey: 'MaintainDevAuth', parentId: 'maintain', groupId: 'maintain-dev', order: 4 },
{ id: 'maintain_dev_module', title: '模块配置', path: '/pages/mall/admin/maintain/dev-config/module-config', componentKey: 'MaintainDevModule', parentId: 'maintain', groupId: 'maintain-dev', order: 5 },
{ id: 'maintain_dev_event', title: '自定事件', path: '/pages/mall/admin/maintain/dev-config/custom-event', componentKey: 'MaintainDevEvent', parentId: 'maintain', groupId: 'maintain-dev', order: 6 },
// 安全维护
{ id: 'maintain_security_cache', title: '刷新缓存', path: '/pages/mall/admin/maintain/security/cache', componentKey: 'MaintainSecurityCache', parentId: 'maintain', groupId: 'maintain-security', order: 1 },
{ id: 'maintain_security_log', title: '系统日志', path: '/pages/mall/admin/maintain/security/log', componentKey: 'MaintainSecurityLog', parentId: 'maintain', groupId: 'maintain-security', order: 2 },
{ id: 'maintain_security_upgrade', title: '在线升级', path: '/pages/mall/admin/maintain/security/upgrade', componentKey: 'MaintainSecurityUpgrade', parentId: 'maintain', groupId: 'maintain-security', order: 3 },
{ id: 'maintain_security_cache', title: '刷新缓存', path: '/pages/mall/admin/maintain/security/refresh-cache', componentKey: 'MaintainSecurityCache', parentId: 'maintain', groupId: 'maintain-security', order: 1 },
{ id: 'maintain_security_log', title: '系统日志', path: '/pages/mall/admin/maintain/security/system-log', componentKey: 'MaintainSecurityLog', parentId: 'maintain', groupId: 'maintain-security', order: 2 },
{ id: 'maintain_security_upgrade', title: '在线升级', path: '/pages/mall/admin/maintain/security/online-upgrade', componentKey: 'MaintainSecurityUpgrade', parentId: 'maintain', groupId: 'maintain-security', order: 3 },
// 数据维护
{ id: 'maintain_data_logistics', title: '物流公司', path: '/pages/mall/admin/maintain/data/logistics', componentKey: 'MaintainDataLogistics', parentId: 'maintain', groupId: 'maintain-data', order: 1 },
@@ -1321,10 +1321,10 @@ export const routes: RouteRecord[] = [
{ id: 'maintain_lang_config', title: '翻译配置', path: '/pages/mall/admin/maintain/lang/config', componentKey: 'MaintainLangConfig', parentId: 'maintain', groupId: 'maintain-lang', order: 4 },
// 开发工具
{ id: 'maintain_tool_db', title: '数据库管理', path: '/pages/mall/admin/maintain/tool/db', componentKey: 'MaintainToolDb', parentId: 'maintain', groupId: 'maintain-tool', order: 1 },
{ id: 'maintain_tool_file', title: '文件管理', path: '/pages/mall/admin/maintain/tool/file', componentKey: 'MaintainToolFile', parentId: 'maintain', groupId: 'maintain-tool', order: 2 },
{ id: 'maintain_tool_api', title: '接口管理', path: '/pages/mall/admin/maintain/tool/api', componentKey: 'MaintainToolApi', parentId: 'maintain', groupId: 'maintain-tool', order: 3 },
{ id: 'maintain_tool_dic', title: '数据字典', path: '/pages/mall/admin/maintain/tool/dic', componentKey: 'MaintainToolDic', parentId: 'maintain', groupId: 'maintain-tool', order: 4 },
{ id: 'maintain_tool_db', title: '数据库管理', path: '/pages/mall/admin/maintain/dev-tools/database', componentKey: 'MaintainToolDb', parentId: 'maintain', groupId: 'maintain-tool', order: 1 },
{ id: 'maintain_tool_file', title: '文件管理', path: '/pages/mall/admin/maintain/dev-tools/file', componentKey: 'MaintainToolFile', parentId: 'maintain', groupId: 'maintain-tool', order: 2 },
{ id: 'maintain_tool_api', title: '接口管理', path: '/pages/mall/admin/maintain/dev-tools/api', componentKey: 'MaintainToolApi', parentId: 'maintain', groupId: 'maintain-tool', order: 3 },
{ id: 'maintain_tool_dic', title: '数据字典', path: '/pages/mall/admin/maintain/dev-tools/data-dict', componentKey: 'MaintainToolDic', parentId: 'maintain', groupId: 'maintain-tool', order: 4 },
// 系统信息
{ id: 'maintain_sys_info', title: '系统信息', path: '/pages/mall/admin/maintain/sys/info', componentKey: 'MaintainSysInfo', parentId: 'maintain', groupId: 'maintain-sys', order: 1 }

View File

@@ -605,6 +605,55 @@
"style": {
"navigationBarTitleText": "签到记录"
}
},
{
"path": "maintain/dev-tools/database",
"style": {
"navigationBarTitleText": "数据库管理",
"navigationStyle": "custom"
}
},
{
"path": "maintain/dev-tools/file",
"style": {
"navigationBarTitleText": "文件管理",
"navigationStyle": "custom"
}
},
{
"path": "maintain/dev-tools/api",
"style": {
"navigationBarTitleText": "接口管理",
"navigationStyle": "custom"
}
},
{
"path": "maintain/dev-tools/data-dict",
"style": {
"navigationBarTitleText": "数据字典",
"navigationStyle": "custom"
}
},
{
"path": "setting/message",
"style": {
"navigationBarTitleText": "消息管理",
"navigationStyle": "custom"
}
},
{
"path": "setting/agreement",
"style": {
"navigationBarTitleText": "协议设置",
"navigationStyle": "custom"
}
},
{
"path": "setting/ticket",
"style": {
"navigationBarTitleText": "小票配置",
"navigationStyle": "custom"
}
}
]
},

View File

@@ -0,0 +1,278 @@
<template>
<view class="admin-page">
<view class="admin-sections">
<!-- 提示栏 -->
<view class="alert-warning">
<text class="alert-content">获取访问 Token 的接口:\n请求 URL/outapi/access_token 请求方式POST 请求参数appid和appsecret 返回数据access_token访问令牌 exp_time令牌过期时间 auth_info授权信息\n使用获取到的 Token 访问对外接口:\n在 HTTP 请求头中添加 Authorization 字段 字段值为 Bearer access_token(注意 Bearer 后有一个空格)</text>
</view>
<!-- 操作栏 -->
<view class="action-bar header-actions">
<button class="btn primary" @click="addAccount">添加账号</button>
</view>
<!-- 表格内容 -->
<view class="admin-card content-card">
<view class="table-container list-table">
<view class="table-header">
<view class="col col-id"><text>编号</text></view>
<view class="col col-account"><text>账号</text></view>
<view class="col col-desc"><text>描述</text></view>
<view class="col col-add-time"><text>添加时间</text></view>
<view class="col col-last-time"><text>最后登录时间</text></view>
<view class="col col-last-ip"><text>最后登录ip</text></view>
<view class="col col-status"><text>状态</text></view>
<view class="col col-action"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in dataList" :key="item.id" class="table-row">
<view class="col col-id"><text>{{ item.id }}</text></view>
<view class="col col-account"><text>{{ item.account }}</text></view>
<view class="col col-desc"><text>{{ item.desc }}</text></view>
<view class="col col-add-time"><text>{{ item.addTime }}</text></view>
<view class="col col-last-time"><text>{{ item.lastTime }}</text></view>
<view class="col col-last-ip"><text>{{ item.lastIp }}</text></view>
<view class="col col-status">
<view :class="['status-tag', item.status ? 'active' : 'inactive']" @click="toggleStatus(item)">
<text class="tag-text">{{ item.status ? '开启' : '关闭' }}</text>
<view class="tag-dot"></view>
</view>
</view>
<view class="col col-action">
<text class="action-btn" @click="setConfig(item)">设置</text>
<text class="action-btn divider">|</text>
<text class="action-btn" @click="editItem(item)">编辑</text>
<text class="action-btn divider">|</text>
<text class="action-btn danger" @click="deleteItem(item)">删除</text>
</view>
</view>
</view>
</view>
<!-- 分页 (模拟) -->
<view class="pagination">
<text class="page-info">共 2 条 20条/页</text>
<view class="page-btns">
<text class="p-btn disabled">˂</text>
<text class="p-btn active">1</text>
<text class="p-btn disabled">˃</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const dataList = ref([
{ id: 9, account: 'ceshi', desc: 'ceshi', addTime: '2024-09-05 11:16:07', lastTime: '暂无', lastIp: '', status: true },
{ id: 10, account: 'tuoluojiang', desc: '', addTime: '2024-09-09 10:12:54', lastTime: '2024-09-10 19:15:01', lastIp: '124.221.49.234', status: true }
])
function addAccount() {
uni.showToast({ title: '添加账号', icon: 'none' })
}
function toggleStatus(item: any) {
item.status = !item.status
}
function setConfig(item: any) {
uni.showToast({ title: '参数设置: ' + item.account, icon: 'none' })
}
function editItem(item: any) {
uni.showToast({ title: '编辑: ' + item.account, icon: 'none' })
}
function deleteItem(item: any) {
uni.showModal({
title: '提示',
content: '确定要删除此账号吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '删除成功' })
}
}
})
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 24px;
background-color: #f5f7f9;
min-height: 100vh;
}
.alert-warning {
background-color: #fff7e6;
border: 1px solid #ffe7ba;
padding: 16px 20px;
border-radius: 4px;
margin-bottom: 24px;
}
.alert-content {
color: #fa8c16;
font-size: 14px;
line-height: 1.8;
white-space: pre-wrap;
}
.action-bar {
margin-bottom: 20px;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 0;
overflow: hidden;
}
.btn {
height: 32px;
padding: 0 16px;
border-radius: 4px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
}
.btn.primary {
background-color: #1890ff;
color: #fff;
}
.table-container {
width: 100%;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-id { width: 80px; }
.col-account { width: 120px; }
.col-desc { flex: 1; }
.col-add-time { width: 180px; }
.col-last-time { width: 180px; }
.col-last-ip { width: 150px; }
.col-status { width: 100px; justify-content: center; }
.col-action { width: 200px; justify-content: flex-end; }
.status-tag {
width: 60px;
height: 24px;
border-radius: 12px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0 8px;
cursor: pointer;
}
.status-tag.active {
background-color: #1890ff;
color: #fff;
}
.status-tag.inactive {
background-color: #d9d9d9;
color: #fff;
}
.tag-text {
font-size: 12px;
}
.tag-dot {
width: 8px;
height: 8px;
background-color: #fff;
border-radius: 50%;
}
.action-btn {
color: #1890ff;
cursor: pointer;
}
.action-btn.danger {
color: #ff4d4f;
}
.action-btn.divider {
margin: 0 8px;
color: #e8e8e8;
}
.pagination {
padding: 16px 24px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
gap: 16px;
}
.page-info {
font-size: 14px;
color: #666;
}
.page-btns {
display: flex;
flex-direction: row;
gap: 8px;
}
.p-btn {
width: 32px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #666;
cursor: pointer;
}
.p-btn.active {
background-color: #fff;
border-color: #1890ff;
color: #1890ff;
}
.p-btn.disabled {
color: #bfbfbf;
cursor: not-allowed;
}
</style>

View File

@@ -0,0 +1,200 @@
<template>
<view class="admin-page">
<view class="admin-sections">
<!-- 操作栏 -->
<view class="action-bar header-actions">
<view class="left-btns">
<button class="btn primary" @click="addProvince">添加省份</button>
<button class="btn ghost ml-12" @click="clearCache">清除缓存</button>
</view>
</view>
<!-- 表格内容 -->
<view class="admin-card content-card">
<view class="table-container list-table tree-table">
<view class="table-header">
<view class="col col-id"><text>编号</text></view>
<view class="col col-name"><text>地区名称</text></view>
<view class="col col-parent"><text>上级名称</text></view>
<view class="col col-action"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in dataList" :key="item.id" class="table-row">
<view class="col col-id"><text>{{ item.id }}</text></view>
<view class="col col-name">
<text class="expand-icon">˃</text>
<text>{{ item.name }}</text>
</view>
<view class="col col-parent"><text>{{ item.parentName }}</text></view>
<view class="col col-action">
<text class="action-btn" @click="addItem(item)">添加</text>
<text class="action-btn divider">|</text>
<text class="action-btn" @click="editItem(item)">编辑</text>
<text class="action-btn divider">|</text>
<text class="action-btn danger" @click="deleteItem(item)">删除</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const dataList = ref([
{ id: 1, name: '北京市', parentName: '中国' },
{ id: 2, name: '天津市', parentName: '中国' },
{ id: 3, name: '河北省', parentName: '中国' },
{ id: 4, name: '山西省', parentName: '中国' },
{ id: 5, name: '内蒙古自治区', parentName: '中国' },
{ id: 6, name: '辽宁省', parentName: '中国' },
{ id: 7, name: '吉林省', parentName: '中国' },
{ id: 8, name: '黑龙江省', parentName: '中国' },
{ id: 9, name: '上海市', parentName: '中国' },
{ id: 10, name: '江苏省', parentName: '中国' },
{ id: 11, name: '浙江省', parentName: '中国' },
{ id: 12, name: '安徽省', parentName: '中国' },
{ id: 13, name: '福建省', parentName: '中国' },
{ id: 14, name: '江西省', parentName: '中国' }
])
function addProvince() {
uni.showToast({ title: '添加省份', icon: 'none' })
}
function clearCache() {
uni.showLoading({ title: '清理中...' })
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: '清理成功' })
}, 1000)
}
function addItem(item: any) {
uni.showToast({ title: '添加下级: ' + item.name, icon: 'none' })
}
function editItem(item: any) {
uni.showToast({ title: '编辑: ' + item.name, icon: 'none' })
}
function deleteItem(item: any) {
uni.showModal({
title: '提示',
content: '确定要删除该区域吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '删除成功' })
}
}
})
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 20px;
background-color: #f5f7f9;
min-height: 100vh;
}
.header-actions {
margin-bottom: 20px;
}
.left-btns {
display: flex;
flex-direction: row;
}
.ml-12 {
margin-left: 12px;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 0; /* 表格一般不带卡片内边距 */
margin-bottom: 20px;
overflow: hidden;
}
.btn {
height: 32px;
padding: 0 15px;
border-radius: 4px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.btn.primary {
background-color: #1890ff;
color: #fff;
border: none;
}
.btn.ghost {
background-color: #fff;
border: 1px solid #d9d9d9;
color: #666;
}
.table-container {
width: 100%;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 12px 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-id { width: 100px; }
.col-name { flex: 2; }
.col-parent { flex: 1; }
.col-action { width: 200px; justify-content: flex-end; }
.expand-icon {
margin-right: 8px;
color: #999;
font-size: 12px;
}
.action-btn {
font-size: 14px;
color: #1890ff;
cursor: pointer;
}
.action-btn.danger {
color: #ff4d4f;
}
.action-btn.divider {
margin: 0 8px;
color: #e8e8e8;
cursor: default;
}
</style>

View File

@@ -0,0 +1,177 @@
<template>
<view class="admin-page">
<view class="admin-sections">
<!-- 页面标题与提示 -->
<view class="page-header">
<text class="title">清除数据</text>
<view class="warning-tip">
<text class="warning-icon">⚠</text>
<text class="warning-text">清除数据请谨慎,清除就无法恢复哦!</text>
</view>
</view>
<!-- 卡片网格 -->
<view class="card-grid">
<view v-for="card in cards" :key="card.id" class="clear-card">
<view class="card-content">
<text class="card-title">{{ card.title }}</text>
<text class="card-desc">{{ card.desc }}</text>
</view>
<view class="card-footer">
<button :class="['btn', card.primary ? 'primary' : 'ghost']" @click="handleAction(card)">
{{ card.btnText }}
</button>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const cards = ref([
{ id: 1, title: '更换域名', desc: '替换所有本地上传的图片域名', btnText: '立即更换', primary: true },
{ id: 2, title: '清除用户生成的临时附件', desc: '清除用户生成的临时附件,不会影响商品图', btnText: '立即清理', primary: false },
{ id: 3, title: '清除回收站商品', desc: '清除回收站商品,谨慎操作', btnText: '立即清理', primary: false },
{ id: 4, title: '清除用户数据', desc: '用户相关的所有表都将被清除,谨慎操作', btnText: '立即清理', primary: false },
{ id: 5, title: '清除商城数据', desc: '清除所有商城数据,谨慎操作', btnText: '立即清理', primary: false },
{ id: 6, title: '清除商品分类', desc: '会清除所有商品分类,谨慎操作', btnText: '立即清理', primary: false },
{ id: 7, title: '清除订单数据', desc: '清除用户所有订单数据,谨慎操作', btnText: '立即清理', primary: false },
{ id: 8, title: '清除客服数据', desc: '清除添加的客服数据,谨慎操作', btnText: '立即清理', primary: false },
{ id: 9, title: '清除微信数据', desc: '清除微信菜单保存数据,微信关键字无效回复', btnText: '立即清理', primary: false },
{ id: 10, title: '清除内容分类', desc: '清除添加的文章和文章分类,谨慎操作', btnText: '立即清理', primary: false },
{ id: 11, title: '清除所有附件', desc: '清除所有附件用户生成和后台上传,谨慎操作', btnText: '立即清理', primary: false },
{ id: 12, title: '清除系统记录', desc: '清除系统记录,谨慎操作', btnText: '立即清理', primary: false }
])
function handleAction(card: any) {
uni.showModal({
title: '极其危险的操作',
content: `您确定要对「${card.title}」执行此操作吗?数据清除后将无法恢复!`,
confirmColor: '#ff4d4f',
success: (res) => {
if (res.confirm) {
uni.showLoading({ title: '正在处理...' })
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: '操作完成' })
}, 2000)
}
}
})
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 24px;
background-color: #f5f7f9;
min-height: 100vh;
}
.page-header {
background-color: #fff;
padding: 16px 24px;
border-radius: 4px;
margin-bottom: 24px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.title {
font-size: 18px;
font-weight: 500;
color: #333;
}
.warning-tip {
display: flex;
flex-direction: row;
align-items: center;
}
.warning-icon {
color: #ff4d4f;
margin-right: 8px;
}
.warning-text {
color: #ff4d4f;
font-size: 14px;
}
.card-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 20px;
}
.clear-card {
width: calc(25% - 15px); /* 4列布局 */
background-color: #fff;
border-radius: 4px;
padding: 30px 20px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
border: 1px solid #f0f0f0;
}
.card-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 12px;
}
.card-desc {
font-size: 14px;
color: #999;
line-height: 1.6;
margin-bottom: 24px;
height: 44px; /* 保持描述文字高度一致 */
display: flex;
align-items: center;
justify-content: center;
}
.btn {
height: 32px;
padding: 0 20px;
border-radius: 4px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.btn.primary {
background-color: #1890ff;
color: #fff;
border: none;
}
.btn.ghost {
background-color: #fff;
border: 1px solid #d9d9d9;
color: #666;
}
@media screen and (max-width: 1400px) {
.clear-card {
width: calc(33.33% - 14px);
}
}
@media screen and (max-width: 1000px) {
.clear-card {
width: calc(50% - 10px);
}
}
</style>

View File

@@ -0,0 +1,227 @@
<template>
<view class="admin-page">
<view class="admin-sections">
<!-- 搜索栏 -->
<view class="admin-card filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">是否显示:</text>
<view class="filter-select">
<text class="select-text">全部</text>
<text class="arrow">▼</text>
</view>
</view>
<view class="filter-item">
<text class="label">搜索:</text>
<input class="filter-input input-lg" placeholder="请输入物流公司名称或者编码" />
</view>
<button class="btn primary" @click="onSearch">查询</button>
</view>
</view>
<!-- 操作栏 -->
<view class="action-bar">
<button class="btn primary" @click="syncLogistics">同步物流公司</button>
</view>
<!-- 表格内容 -->
<view class="admin-card content-card">
<view class="table-container list-table">
<view class="table-header">
<view class="col col-id"><text>ID</text></view>
<view class="col col-name"><text>物流公司名称</text></view>
<view class="col col-code"><text>编码</text></view>
<view class="col col-sort"><text>排序</text></view>
<view class="col col-status"><text>是否显示</text></view>
<view class="col col-action"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in dataList" :key="item.id" class="table-row">
<view class="col col-id"><text>{{ item.id }}</text></view>
<view class="col col-name"><text>{{ item.name }}</text></view>
<view class="col col-code"><text>{{ item.code }}</text></view>
<view class="col col-sort"><text>{{ item.sort }}</text></view>
<view class="col col-status">
<switch :checked="item.show" color="#1890ff" @change="onStatusChange(item)" />
</view>
<view class="col col-action">
<text class="action-btn" @click="editItem(item)">编辑</text>
</view>
</view>
</view>
</view>
<!-- 分页 (模拟) -->
<view class="pagination">
<!-- 这里可以放分页组件 -->
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const dataList = ref([
{ id: 2, name: '顺丰速运', code: 'shunfeng', sort: 0, show: true },
{ id: 3, name: '圆通速递', code: 'yuantong', sort: 0, show: true },
{ id: 4, name: '中通快递', code: 'zhongtong', sort: 0, show: true },
{ id: 5, name: '申通快递', code: 'shentong', sort: 0, show: true },
{ id: 6, name: '百世快递', code: 'huitongkuaidi', sort: 0, show: true },
{ id: 7, name: '京东物流', code: 'jd', sort: 0, show: true },
{ id: 8, name: '极兔速递', code: 'jtexpress', sort: 0, show: true },
{ id: 9, name: '邮政快递包裹', code: 'youzhengguonei', sort: 0, show: true },
{ id: 10, name: '天天快递', code: 'tiantian', sort: 0, show: true },
{ id: 11, name: 'EMS', code: 'ems', sort: 0, show: true },
{ id: 12, name: '德邦物流', code: 'debangwuliu', sort: 0, show: true },
{ id: 13, name: '德邦快递', code: 'debangkuaidi', sort: 0, show: true },
{ id: 14, name: '众邮快递', code: 'zhongyouex', sort: 0, show: true }
])
function onSearch() {
uni.showToast({ title: '查询中...', icon: 'none' })
}
function syncLogistics() {
uni.showLoading({ title: '同步中...' })
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: '同步成功' })
}, 1000)
}
function onStatusChange(item: any) {
item.show = !item.show
uni.showToast({ title: '状态已更新', icon: 'none' })
}
function editItem(item: any) {
uni.showToast({ title: '编辑: ' + item.name, icon: 'none' })
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 20px;
background-color: #f5f7f9;
min-height: 100vh;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
}
.filter-row {
display: flex;
flex-direction: row;
align-items: center;
gap: 20px;
}
.filter-item {
display: flex;
flex-direction: row;
align-items: center;
}
.label {
font-size: 14px;
color: #333;
margin-right: 8px;
white-space: nowrap;
}
.filter-select {
width: 200px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0 10px;
}
.filter-input {
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 0 10px;
font-size: 14px;
}
.input-lg {
width: 300px;
}
.btn {
height: 32px;
padding: 0 15px;
border-radius: 4px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
}
.btn.primary {
background-color: #1890ff;
color: #fff;
}
.action-bar {
margin-bottom: 16px;
}
.table-container {
width: 100%;
border: 1px solid #f0f0f0;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 12px 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-id { width: 80px; }
.col-name { flex: 2; }
.col-code { flex: 2; }
.col-sort { width: 100px; }
.col-status { width: 120px; justify-content: center; }
.col-action { width: 100px; justify-content: center; }
.action-btn {
color: #1890ff;
cursor: pointer;
}
.pagination {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
</style>

View File

@@ -1,22 +1,268 @@
<template>
<AdminLayout currentPage="dev-config-category">
<view class="page">
<view class="header">
<text class="title">配置分类</text>
<view class="admin-page">
<view class="admin-sections">
<!-- 搜索栏 -->
<view class="admin-card filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">是否显示:</text>
<view class="filter-select">
<text class="select-placeholder">请选择</text>
<text class="arrow">▼</text>
</view>
</view>
<view class="filter-item">
<text class="label">分类名称:</text>
<input class="filter-input" placeholder="请输入分类名称" />
</view>
<button class="btn primary" @click="onSearch">查询分类</button>
</view>
<view class="filter-row mt-12">
<view class="filter-item">
<text class="label">配置名称:</text>
<input class="filter-input" placeholder="请输入配置名称" />
</view>
<button class="btn primary" @click="onSearchConfig">查询配置</button>
</view>
</view>
<view class="content">
<text class="tip">TODO: 配置分类</text>
<!-- 内容区 -->
<view class="admin-card content-card">
<!-- 操作按钮行 -->
<view class="action-bar">
<button class="btn primary small" @click="onAdd">添加配置分类</button>
</view>
<!-- 表格 -->
<view class="table-container list-table">
<view class="table-header">
<view class="col col-id"><text>ID</text></view>
<view class="col col-name"><text>分类名称</text></view>
<view class="col col-field"><text>分类字段</text></view>
<view class="col col-status"><text>状态</text></view>
<view class="col col-ops"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in dataList" :key="item.id" class="table-row">
<view class="col col-id"><text>{{ item.id }}</text></view>
<view class="col col-name">
<text class="expand-icon" v-if="item.hasChildren">▶</text>
<text>{{ item.name }}</text>
</view>
<view class="col col-field"><text>{{ item.field }}</text></view>
<view class="col col-status">
<switch :checked="item.status" color="#1890ff" scale="0.8" />
</view>
<view class="col col-ops">
<text class="op-link" @click="onConfig(item)">配置列表</text>
<view class="op-divider">|</view>
<text class="op-link" @click="onEdit(item)">编辑</text>
<view class="op-divider">|</view>
<text class="op-link op-danger" @click="onDelete(item)">删除</text>
</view>
</view>
</view>
</view>
</view>
</view>
</AdminLayout>
</view>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.page { padding: 16px; }
.title { font-size: 18px; font-weight: 600; }
.tip { color: #999; margin-top: 8px; display: block; }
<script setup lang="uts">
import { ref } from 'vue'
const dataList = ref([
{ id: 9, name: '分销配置', field: 'fenxiao', status: true, hasChildren: true },
{ id: 65, name: '接口设置', field: 'system_serve', status: true, hasChildren: true },
{ id: 69, name: '客服配置', field: 'kefu_config', status: true, hasChildren: true },
{ id: 78, name: '应用配置', field: 'sys_app', status: true, hasChildren: true },
{ id: 100, name: '用户配置', field: 'system_user_config', status: true, hasChildren: true },
{ id: 113, name: '订单配置', field: 'order_config', status: true, hasChildren: true },
{ id: 129, name: '系统配置', field: 'system_config', status: true, hasChildren: true },
{ id: 136, name: '商品配置', field: 'product_config', status: true, hasChildren: true }
])
function onSearch() {
uni.showToast({ title: '查询分类', icon: 'none' })
}
function onSearchConfig() {
uni.showToast({ title: '查询配置', icon: 'none' })
}
function onAdd() {
uni.showToast({ title: '添加配置分类', icon: 'none' })
}
function onConfig(item: any) {
uni.showToast({ title: '配置列表: ' + item.name, icon: 'none' })
}
function onEdit(item: any) {
uni.showToast({ title: '编辑: ' + item.name, icon: 'none' })
}
function onDelete(item: any) {
uni.showModal({
title: '提示',
content: '确定要删除该分类吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '已删除', icon: 'none' })
}
}
})
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 20px;
background-color: #f5f7f9;
min-height: 100vh;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
}
.filter-row {
display: flex;
flex-direction: row;
align-items: center;
}
.mt-12 { margin-top: 12px; }
.filter-item {
display: flex;
flex-direction: row;
align-items: center;
margin-right: 24px;
}
.label {
font-size: 14px;
color: #333;
width: 80px;
}
.filter-select {
width: 200px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 2px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0 10px;
}
.select-placeholder {
color: #bfbfbf;
font-size: 14px;
}
.arrow {
font-size: 10px;
color: #bfbfbf;
}
.filter-input {
width: 200px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 2px;
padding: 0 10px;
font-size: 14px;
}
.btn {
height: 32px;
padding: 0 20px;
border-radius: 2px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 1px solid #d9d9d9;
background-color: #fff;
}
.btn.primary {
background-color: #1890ff;
border-color: #1890ff;
color: #fff;
}
.btn.small {
height: 28px;
padding: 0 10px;
font-size: 12px;
}
.action-bar {
margin-bottom: 16px;
}
.table-container {
width: 100%;
border: 1px solid #f0f0f0;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 12px 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-id { width: 80px; }
.col-name { flex: 2; }
.col-field { flex: 2; }
.col-status { width: 100px; justify-content: center; }
.col-ops { flex: 2; justify-content: flex-end; }
.expand-icon {
margin-right: 8px;
font-size: 10px;
color: #999;
}
.op-link {
color: #1890ff;
cursor: pointer;
margin: 0 4px;
}
.op-danger {
color: #ff4d4f;
}
.op-divider {
color: #f0f0f0;
margin: 0 4px;
}
</style>

View File

@@ -1,23 +1,223 @@
<template>
<AdminLayout currentPage="dev-config-combo">
<view class="page">
<view class="header">
<text class="title">组合数据</text>
<view class="admin-page">
<view class="admin-sections">
<!-- 搜索栏 -->
<view class="admin-card filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">数据搜索:</text>
<input class="filter-input input-large" placeholder="请输入ID,KEY,数据组名称,简介" />
</view>
<button class="btn primary" @click="onSearch">查询</button>
</view>
</view>
<!-- 内容区 -->
<view class="admin-card content-card">
<!-- 操作按钮行 -->
<view class="action-bar">
<button class="btn primary small" @click="onAdd">添加数据组</button>
</view>
<!-- 表格 -->
<view class="table-container">
<view class="table-header">
<view class="col col-id"><text>ID</text></view>
<view class="col col-key"><text>KEY</text></view>
<view class="col col-name"><text>数据组名称</text></view>
<view class="col col-desc"><text>简介</text></view>
<view class="col col-ops"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in dataList" :key="item.id" class="table-row">
<view class="col col-id"><text>{{ item.id }}</text></view>
<view class="col col-key"><text>{{ item.key }}</text></view>
<view class="col col-name"><text>{{ item.name }}</text></view>
<view class="col col-desc"><text>{{ item.desc }}</text></view>
<view class="col col-ops">
<text class="op-link" @click="onDataList(item)">数据列表</text>
<view class="op-divider">|</view>
<text class="op-link" @click="onEdit(item)">编辑</text>
<view class="op-divider">|</view>
<text class="op-link op-danger" @click="onDelete(item)">删除</text>
</view>
</view>
</view>
</view>
</view>
<view class="content">
<text class="tip">TODO: 组合数据</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
<script setup lang="uts">
import { ref } from 'vue'
const dataList = ref([
{ id: 49, key: 'routine_seckill_time', name: '秒杀时间段', desc: '秒杀时间段' },
{ id: 52, key: 'routine_home_bast_banner', name: '首页精品推荐图片', desc: '首页精品推荐图片' },
{ id: 53, key: 'order_details_images', name: '订单详情状态图', desc: '订单详情状态图' },
{ id: 54, key: 'routine_my_menus', name: '个人中心菜单', desc: '个人中心菜单' },
{ id: 55, key: 'sign_day_num', name: '签到天数配置', desc: '签到天数配置' },
{ id: 57, key: 'routine_home_hot_banner', name: '热门榜单推荐图片', desc: '热门榜单推荐图片' },
{ id: 58, key: 'routine_home_new_banner', name: '首发新品推荐图片', desc: '首发新品推荐图片' },
{ id: 59, key: 'routine_home_benefit_banner', name: '促销单品推荐图片', desc: '促销单品推荐图片' },
{ id: 62, key: 'user_recharge_quota', name: '充值金额设置', desc: '设置充值金额额度选择' },
{ id: 65, key: 'admin_login_slide', name: '后台登录页面幻灯片', desc: '后台登录页面幻灯片' },
{ id: 66, key: 'uni_app_link', name: '前端页面链接', desc: '前端页面链接' },
{ id: 67, key: 'combination_banner', name: '拼团列表轮播图', desc: '拼团列表轮播图' },
{ id: 68, key: 'integral_shop_banner', name: '积分商城轮播图', desc: '积分商城轮播图' }
])
function onSearch() {
uni.showToast({ title: '查询中...', icon: 'none' })
}
function onAdd() {
uni.showToast({ title: '添加数据组', icon: 'none' })
}
function onDataList(item: any) {
uni.showToast({ title: '数据列表: ' + item.name, icon: 'none' })
}
function onEdit(item: any) {
uni.showToast({ title: '编辑: ' + item.name, icon: 'none' })
}
function onDelete(item: any) {
uni.showModal({
title: '提示',
content: '确定要删除该数据组吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '已删除', icon: 'none' })
}
}
})
}
</script>
<style scoped>
.page { padding: 16px; }
.title { font-size: 18px; font-weight: 600; }
.tip { color: #999; margin-top: 8px; display: block; }
</style>
<style scoped lang="scss">
.admin-page {
padding: 20px;
background-color: #f5f7f9;
min-height: 100vh;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
}
.filter-row {
display: flex;
flex-direction: row;
align-items: center;
}
.filter-item {
display: flex;
flex-direction: row;
align-items: center;
margin-right: 24px;
}
.label {
font-size: 14px;
color: #333;
}
.filter-input {
width: 200px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 2px;
padding: 0 10px;
font-size: 14px;
}
.input-large {
width: 300px;
}
.btn {
height: 32px;
padding: 0 20px;
border-radius: 2px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 1px solid #d9d9d9;
background-color: #fff;
}
.btn.primary {
background-color: #1890ff;
border-color: #1890ff;
color: #fff;
}
.btn.small {
height: 28px;
padding: 0 10px;
font-size: 12px;
}
.action-bar {
margin-bottom: 16px;
}
.table-container {
width: 100%;
border: 1px solid #f0f0f0;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 12px 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-id { width: 80px; }
.col-key { flex: 2; }
.col-name { flex: 2; }
.col-desc { flex: 2; }
.col-ops { flex: 2; justify-content: flex-end; }
.op-link {
color: #1890ff;
cursor: pointer;
margin: 0 4px;
}
.op-danger {
color: #ff4d4f;
}
.op-divider {
color: #f0f0f0;
margin: 0 4px;
}
</style>

View File

@@ -1,22 +1,192 @@
<template>
<AdminLayout currentPage="dev-config-cron">
<view class="page">
<view class="header">
<text class="title">定时任务</text>
</view>
<view class="content">
<text class="tip">TODO: 定时任务</text>
<view class="admin-page">
<view class="admin-sections">
<!-- 顶部通知 -->
<view class="admin-card alert-card">
<view class="alert-content">
<text class="alert-title">启动定时任务两种方式:</text>
<text class="alert-desc">1、使用命令行启动php think timer start --d; 如果更改了执行周期、编辑是否开启、删除定时任务需要重新启动下定时任务确保生效;</text>
<text class="alert-desc">2、使用接口触发定时任务建议每分钟调用一次接口地址 https://v5.crmeb.net/api/crontab/run</text>
</view>
</view>
<!-- 操作卡片 -->
<view class="admin-card content-card">
<view class="tabs-row">
<view class="tab-item active"><text>系统任务</text></view>
<view class="tab-item"><text>自定义任务</text></view>
</view>
<!-- 表格 -->
<view class="table-container list-table mt-16">
<view class="table-header">
<view class="col col-title"><text>标题</text></view>
<view class="col col-desc"><text>任务说明</text></view>
<view class="col col-cycle"><text>执行周期</text></view>
<view class="col col-status"><text>是否开启</text></view>
<view class="col col-ops"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in dataList" :key="item.id" class="table-row">
<view class="col col-title"><text>{{ item.title }}</text></view>
<view class="col col-desc"><text>{{ item.desc }}</text></view>
<view class="col col-cycle"><text>{{ item.cycle }}</text></view>
<view class="col col-status">
<switch :checked="item.status" color="#1890ff" scale="0.8" />
</view>
<view class="col col-ops">
<text class="op-link" @click="onEdit(item)">编辑</text>
</view>
</view>
</view>
</view>
<!-- 分页 -->
<view class="pagination">
<text class="page-info">共 10 条 15条/页</text>
</view>
</view>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.page { padding: 16px; }
.title { font-size: 18px; font-weight: 600; }
.tip { color: #999; margin-top: 8px; display: block; }
</style>
<script setup lang="uts">
import { ref } from 'vue'
const dataList = ref([
{ id: 1, title: '自动开具/冲红电子发票', desc: '每隔10分钟执行自动开具/冲红电子发票', cycle: '每隔10分钟执行一次', status: true },
{ id: 2, title: '清除昨日海报', desc: '每天0时30分0秒执行一次清除昨日海报', cycle: '每天0时30分0秒执行一次', status: true },
{ id: 3, title: '订单商品自动好评', desc: '每隔5分钟执行订单到期商品好评', cycle: '每隔5分钟执行一次', status: true },
{ id: 4, title: '预售商品到期自动下架', desc: '每隔5分钟执行预售商品到期下架', cycle: '每隔5分钟执行一次', status: true },
{ id: 5, title: '订单自动收货', desc: '每隔5分钟执行订单到期自动收货', cycle: '每隔5分钟执行一次', status: true },
{ id: 6, title: '自动更新直播间状态', desc: '每隔3分钟执行更新直播间状态', cycle: '每隔3分钟执行一次', status: true },
{ id: 7, title: '自动更新直播商品状态', desc: '每隔3分钟执行更新直播商品状态', cycle: '每隔3分钟执行一次', status: true },
{ id: 8, title: '到期自动解绑上级', desc: '每隔1分钟执行到期的绑定关系的解除', cycle: '每隔1分钟执行一次', status: true },
{ id: 9, title: '拼团到期订单处理', desc: '每隔1分钟拼团到期后的操作', cycle: '每隔1分钟执行一次', status: true },
{ id: 10, title: '未支付自动取消订单', desc: '每隔30秒执行自动取消到期未支付的订单', cycle: '每隔30秒执行一次', status: true }
])
function onEdit(item: any) {
uni.showToast({ title: '编辑: ' + item.title, icon: 'none' })
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 20px;
background-color: #f5f7f9;
min-height: 100vh;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
}
.alert-card {
background-color: #fffbe6;
border: 1px solid #ffe58f;
}
.alert-content {
padding: 8px;
}
.alert-title {
font-size: 14px;
color: #faad14;
margin-bottom: 8px;
font-weight: bold;
}
.alert-desc {
font-size: 13px;
color: #faad14;
line-height: 20px;
margin-bottom: 4px;
}
.tabs-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
margin-bottom: 24px;
}
.tab-item {
padding: 12px 20px;
font-size: 14px;
color: #333;
cursor: pointer;
position: relative;
}
.tab-item.active {
color: #1890ff;
}
.tab-item.active::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
right: 0;
height: 2px;
background-color: #1890ff;
}
.table-container {
width: 100%;
border: 1px solid #f0f0f0;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 12px 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-title { flex: 2; }
.col-desc { flex: 3; }
.col-cycle { flex: 2; }
.col-status { width: 100px; justify-content: center; }
.col-ops { width: 80px; justify-content: center; }
.op-link {
color: #1890ff;
cursor: pointer;
}
.pagination {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
.page-info {
font-size: 14px;
color: #999;
}
.mt-16 { margin-top: 16px; }
</style>

View File

@@ -1,22 +1,155 @@
<template>
<AdminLayout currentPage="dev-config-event">
<view class="page">
<view class="header">
<text class="title">自定事件</text>
</view>
<view class="content">
<text class="tip">TODO: 自定事件</text>
<view class="admin-page">
<view class="admin-sections">
<!-- 提示语 -->
<view class="admin-card alert-card">
<text class="alert-title">自定义事件说明:</text>
<text class="alert-desc">1、新增的事件会在对应的事件类型相关的流程中触发选择用户登录则在用户登录时执行代码。</text>
<text class="alert-desc">2、可以使用对应事件类型中的参数$data['nickname'], $data['phone']等。</text>
<text class="alert-desc">3、调用类的时请写入完整路径\think\facade\Db、\app\services\other\CacheServices::class等。</text>
</view>
<!-- 内容区 -->
<view class="admin-card content-card">
<!-- 操作按钮行 -->
<view class="action-bar">
<button class="btn primary small" @click="onAdd">新增系统事件</button>
</view>
<!-- 表格(暂无数据) -->
<view class="table-container list-table">
<view class="table-header">
<view class="col col-id"><text>编号</text></view>
<view class="col col-name"><text>事件名</text></view>
<view class="col col-type"><text>事件类型</text></view>
<view class="col col-status"><text>是否开启</text></view>
<view class="col col-time"><text>创建时间</text></view>
<view class="col col-ops"><text>操作</text></view>
</view>
<view class="table-body">
<view class="empty-row">
<text class="empty-text">暂无数据</text>
</view>
</view>
</view>
</view>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.page { padding: 16px; }
.title { font-size: 18px; font-weight: 600; }
.tip { color: #999; margin-top: 8px; display: block; }
<script setup lang="uts">
function onAdd() {
uni.showToast({ title: '新增系统事件', icon: 'none' })
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 20px;
background-color: #f5f7f9;
min-height: 100vh;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
}
.alert-card {
background-color: #fff7e6;
border: 1px solid #ffd591;
}
.alert-title {
font-size: 14px;
color: #fa8c16;
margin-bottom: 8px;
font-weight: bold;
}
.alert-desc {
font-size: 13px;
color: #fa8c16;
line-height: 20px;
margin-bottom: 4px;
}
.action-bar {
margin-bottom: 16px;
}
.table-container {
width: 100%;
border: 1px solid #f0f0f0;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 12px 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-id { width: 80px; }
.col-name { flex: 2; }
.col-type { flex: 2; }
.col-status { width: 100px; justify-content: center; }
.col-time { flex: 2; }
.col-ops { width: 100px; justify-content: center; }
.empty-row {
height: 200px;
display: flex;
align-items: center;
justify-content: center;
}
.empty-text {
font-size: 14px;
color: #bfbfbf;
}
.btn {
height: 32px;
padding: 0 20px;
border-radius: 2px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 1px solid #d9d9d9;
background-color: #fff;
}
.btn.primary {
background-color: #1890ff;
border-color: #1890ff;
color: #fff;
}
.btn.small {
height: 28px;
padding: 0 10px;
font-size: 12px;
}
</style>

View File

@@ -1,22 +1,122 @@
<template>
<AdminLayout currentPage="dev-config-module">
<view class="page">
<view class="header">
<text class="title">模块配置</text>
</view>
<view class="content">
<text class="tip">TODO: 模块配置</text>
<view class="admin-page">
<view class="admin-sections">
<view class="admin-card content-card">
<view class="form-container">
<view class="form-item">
<view class="form-label">模块配置:</view>
<view class="form-content">
<label class="checkbox-item">
<checkbox color="#1890ff" checked />
<text class="checkbox-label">秒杀</text>
</label>
<label class="checkbox-item">
<checkbox color="#1890ff" checked />
<text class="checkbox-label">砍价</text>
</label>
<label class="checkbox-item">
<checkbox color="#1890ff" checked />
<text class="checkbox-label">拼团</text>
</label>
</view>
</view>
<view class="form-tip">
模块配置,选中展示对应的模块,取消选中则前后端不展示模块相关内容
</view>
<view class="form-actions">
<button class="btn primary" @click="onSubmit">提交</button>
</view>
</view>
</view>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
function onSubmit() {
uni.showToast({ title: '已提交', icon: 'none' })
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 20px;
background-color: #f5f7f9;
min-height: 100vh;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 40px;
margin-bottom: 20px;
}
.form-container {
max-width: 600px;
}
.form-item {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 12px;
}
.form-label {
font-size: 14px;
color: #333;
width: 100px;
}
.form-content {
display: flex;
flex-direction: row;
align-items: center;
}
.checkbox-item {
display: flex;
flex-direction: row;
align-items: center;
margin-right: 20px;
}
.checkbox-label {
margin-left: 8px;
font-size: 14px;
color: #333;
}
.form-tip {
font-size: 12px;
color: #bfbfbf;
margin-left: 100px;
margin-bottom: 24px;
}
.form-actions {
margin-left: 100px;
}
.btn {
height: 32px;
padding: 0 20px;
border-radius: 2px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 1px solid #d9d9d9;
background-color: #fff;
}
.btn.primary {
background-color: #1890ff;
border-color: #1890ff;
color: #fff;
}
</style>
<style scoped>
.page { padding: 16px; }
.title { font-size: 18px; font-weight: 600; }
.tip { color: #999; margin-top: 8px; display: block; }
</style>

View File

@@ -1,22 +1,263 @@
<template>
<AdminLayout currentPage="dev-config-permission">
<view class="page">
<view class="header">
<text class="title">权限维护</text>
</view>
<view class="content">
<text class="tip">TODO: 权限维护</text>
<view class="admin-page">
<view class="admin-sections">
<!-- 搜索栏 -->
<view class="admin-card filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">规则状态:</text>
<view class="filter-select">
<text class="select-placeholder">请选择</text>
<text class="arrow">▼</text>
</view>
</view>
<view class="filter-item">
<text class="label">按钮名称:</text>
<input class="filter-input" placeholder="请输入按钮名称" />
</view>
<button class="btn primary" @click="onSearch">查询</button>
</view>
</view>
<!-- 内容区 -->
<view class="admin-card content-card">
<!-- 操作按钮行 -->
<view class="action-bar">
<button class="btn primary small" @click="onAdd">添加规则</button>
</view>
<!-- 表格 -->
<view class="table-container list-table">
<view class="table-header">
<view class="col col-name"><text>按钮名称</text></view>
<view class="col col-auth"><text>前项权限</text></view>
<view class="col col-route"><text>路由</text></view>
<view class="col col-status"><text>规则状态</text></view>
<view class="col col-memo"><text>备注</text></view>
<view class="col col-ops"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in dataList" :key="item.id" class="table-row">
<view class="col col-name">
<text class="expand-icon">▶</text>
<text>{{ item.name }}</text>
</view>
<view class="col col-auth"><text>{{ item.auth }}</text></view>
<view class="col col-route"><text>{{ item.route }}</text></view>
<view class="col col-status">
<switch :checked="item.status" color="#1890ff" scale="0.8" />
</view>
<view class="col col-memo"><text>{{ item.memo }}</text></view>
<view class="col col-ops">
<text class="op-link" @click="onSelectAuth(item)">选择权限</text>
<view class="op-divider">|</view>
<text class="op-link" @click="onAddSub(item)">添加下级</text>
<view class="op-divider">|</view>
<text class="op-link" @click="onEdit(item)">编辑</text>
<view class="op-divider">|</view>
<text class="op-link op-danger" @click="onDelete(item)">删除</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.page { padding: 16px; }
.title { font-size: 18px; font-weight: 600; }
.tip { color: #999; margin-top: 8px; display: block; }
<script setup lang="uts">
import { ref } from 'vue'
const dataList = ref([
{ id: 1, name: '主页', auth: 'admin-home', route: '菜单: /admin/index', status: true, memo: '主页' },
{ id: 2, name: '用户', auth: 'admin-user', route: '菜单: /admin/user', status: true, memo: '用户' },
{ id: 3, name: '订单', auth: 'admin-order', route: '菜单: /admin/order', status: true, memo: '订单' },
{ id: 4, name: '商品', auth: 'admin-product', route: '菜单: /admin/product', status: true, memo: '商品' },
{ id: 5, name: '营销', auth: 'admin-marketing', route: '菜单: /admin/marketing', status: true, memo: '营销' },
{ id: 6, name: '分销', auth: 'admin-agent', route: '菜单: /admin/agent', status: true, memo: '分销' },
{ id: 7, name: '客服', auth: 'setting-store-service', route: '菜单: /admin/kefu', status: true, memo: '客服' },
{ id: 8, name: '财务', auth: 'admin-finance', route: '菜单: /admin/finance', status: true, memo: '财务' },
{ id: 9, name: '内容', auth: 'admin-cms', route: '菜单: /admin/cms', status: true, memo: '内容' },
{ id: 10, name: '装修', auth: 'admin-setting-pages', route: '菜单: /admin/setting/pages', status: true, memo: '装修' },
{ id: 11, name: '应用', auth: 'admin-app', route: '菜单: /admin/app', status: true, memo: '应用' },
{ id: 12, name: '设置', auth: 'admin-setting', route: '菜单: /admin/setting', status: true, memo: '设置' },
{ id: 13, name: '维护', auth: 'admin-system', route: '菜单: /admin/system', status: true, memo: '维护' }
])
function onSearch() {
uni.showToast({ title: '查询中...', icon: 'none' })
}
function onAdd() {
uni.showToast({ title: '添加规则', icon: 'none' })
}
function onSelectAuth(item: any) {
uni.showToast({ title: '选择权限: ' + item.name, icon: 'none' })
}
function onAddSub(item: any) {
uni.showToast({ title: '添加下级: ' + item.name, icon: 'none' })
}
function onEdit(item: any) {
uni.showToast({ title: '编辑: ' + item.name, icon: 'none' })
}
function onDelete(item: any) {
uni.showModal({
title: '提示',
content: '确定要删除该规则吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '已删除', icon: 'none' })
}
}
})
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 20px;
background-color: #f5f7f9;
min-height: 100vh;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
}
.filter-row {
display: flex;
flex-direction: row;
align-items: center;
}
.filter-item {
display: flex;
flex-direction: row;
align-items: center;
margin-right: 24px;
}
.label {
font-size: 14px;
color: #333;
}
.filter-select {
width: 150px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 2px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0 10px;
}
.select-placeholder {
color: #bfbfbf;
font-size: 14px;
}
.filter-input {
width: 200px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 2px;
padding: 0 10px;
font-size: 14px;
}
.btn {
height: 32px;
padding: 0 20px;
border-radius: 2px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 1px solid #d9d9d9;
background-color: #fff;
}
.btn.primary {
background-color: #1890ff;
border-color: #1890ff;
color: #fff;
}
.btn.small {
height: 28px;
padding: 0 10px;
font-size: 12px;
}
.action-bar {
margin-bottom: 16px;
}
.table-container {
width: 100%;
border: 1px solid #f0f0f0;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 12px 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-name { flex: 1.5; }
.col-auth { flex: 2; }
.col-route { flex: 3; }
.col-status { width: 100px; justify-content: center; }
.col-memo { flex: 1; }
.col-ops { flex: 3; justify-content: flex-end; }
.expand-icon {
margin-right: 8px;
font-size: 10px;
color: #999;
}
.op-link {
color: #1890ff;
cursor: pointer;
margin: 0 4px;
}
.op-danger {
color: #ff4d4f;
}
.op-divider {
color: #f0f0f0;
margin: 0 4px;
}
</style>

View File

@@ -1,21 +1,330 @@
<template>
<AdminLayout currentPage="dev-tools-api">
<view class="page">
<view class="header">
<text class="title">接口管理</text>
</view>
<view class="content">
<text class="tip">TODO: 接口管理</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.page { padding: 16px; }
.title { font-size: 18px; font-weight: 600; }
.tip { color: #999; margin-top: 8px; display: block; }
<view class="admin-page api-management">
<!-- 左侧分类树 -->
<view class="sidebar-tree">
<view class="sidebar-header">
<button class="btn-primary-sm">新增分类</button>
<button class="btn-success-sm ml-10">同步</button>
</view>
<scroll-view class="tree-scroll" scroll-y="true">
<view class="tree-node" v-for="(group, gIndex) in apiGroups" :key="gIndex">
<view class="node-label">
<text class="arrow">▼</text>
<text class="folder-icon">📁</text>
<text class="label-text">{{ group.name }}</text>
</view>
<view class="node-children">
<view
class="child-item"
v-for="(api, aIndex) in group.children"
:key="aIndex"
:class="{ active: selectedApiId === api.id }"
@click="selectedApiId = api.id"
>
<text class="method-tag" :class="api.method.toLowerCase()">{{ api.method }}</text>
<text class="api-name">{{ api.name }}</text>
<text class="more-icon">...</text>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 右侧接口详情 -->
<view class="api-content">
<view class="content-header">
<text class="current-api-title">分销员列表</text>
<view class="header-actions">
<button class="btn-secondary-sm">调试</button>
<button class="btn-primary-sm ml-10">编辑</button>
</view>
</view>
<view class="info-section">
<view class="info-title">接口信息</view>
<view class="info-list">
<view class="info-item">
<text class="info-label">接口名称:</text>
<text class="info-value">分销员列表</text>
</view>
<view class="info-item">
<text class="info-label">请求类型:</text>
<text class="method-badge get">GET</text>
</view>
<view class="info-item">
<text class="info-label">功能描述:</text>
<text class="info-value">--</text>
</view>
<view class="info-item">
<text class="info-label">是否公共:</text>
<text class="info-value">否</text>
</view>
</view>
</view>
<view class="info-section">
<view class="info-title">调用方式</view>
<view class="info-list">
<view class="info-item">
<text class="info-label">路由地址:</text>
<text class="info-value">agent/index</text>
</view>
<view class="info-item">
<text class="info-label">文件地址:</text>
<text class="info-value">app/adminapi/controller/v1/agent/AgentManage.php</text>
</view>
<view class="info-item">
<text class="info-label">方法名:</text>
<text class="info-value">index</text>
</view>
</view>
</view>
<!-- 参数表格 -->
<view class="params-section">
<view class="info-title">header参数</view>
<view class="params-table">
<view class="table-head">
<view class="th p-attr">属性</view>
<view class="th p-type">类型</view>
<view class="th p-required">必须</view>
<view class="th p-desc">说明</view>
</view>
<view class="table-row">
<view class="td p-attr">Authori-zation</view>
<view class="td p-type">string</view>
<view class="td p-required">否</view>
<view class="td p-desc">--</view>
</view>
</view>
</view>
<view class="params-section mt-20">
<view class="info-title">query参数</view>
<view class="params-table">
<view class="table-head">
<view class="th p-attr">属性</view>
<view class="th p-type">类型</view>
<view class="th p-required">必须</view>
<view class="th p-desc">说明</view>
</view>
<view class="table-row" v-for="(p, i) in queryParams" :key="i">
<view class="td p-attr">{{ p.attr }}</view>
<view class="td p-type">{{ p.type }}</view>
<view class="td p-required">{{ p.required }}</view>
<view class="td p-desc">{{ p.desc }}</view>
</view>
</view>
</view>
</view>
</view></template>
<script setup lang="uts">
import { ref } from 'vue'
const selectedApiId = ref('1-1')
const apiGroups = ref([
{
name: '分销员管理',
children: [
{ id: '1-1', name: '分销员列表', method: 'GET' },
{ id: '1-2', name: '修改上级推广人', method: 'PUT' },
{ id: '1-3', name: '分销员列表头部统计', method: 'GET' },
{ id: '1-4', name: '推广人列表', method: 'GET' },
{ id: '1-5', name: '推广订单列表', method: 'GET' }
]
},
{
name: '分销设置',
children: []
},
{
name: '分销等级',
children: []
}
])
const queryParams = ref([
{ attr: 'page', type: 'string', required: '是', desc: '页码' },
{ attr: 'limit', type: 'string', required: '是', desc: '每页条数' },
{ attr: 'nickname', type: 'string', required: '是', desc: '用户昵称/用户id/手机号码' }
])
</script>
<style scoped>
.api-management {
display: flex;
flex-direction: row;
height: calc(100vh - 64px);
background-color: #fff;
}
.sidebar-tree {
width: 260px;
border-right: 1px solid #f0f0f0;
display: flex;
flex-direction: column;
}
.sidebar-header {
padding: 15px;
border-bottom: 1px solid #f0f0f0;
display: flex;
flex-direction: row;
}
.tree-scroll {
flex: 1;
}
.tree-node {
padding: 5px 0;
}
.node-label {
padding: 8px 15px;
display: flex;
flex-direction: row;
align-items: center;
cursor: pointer;
}
.label-text {
font-size: 14px;
font-weight: 500;
margin-left: 5px;
}
.node-children {
padding-left: 20px;
}
.child-item {
padding: 8px 15px;
display: flex;
flex-direction: row;
align-items: center;
cursor: pointer;
position: relative;
}
.child-item.active {
background-color: #e6f7ff;
color: #1890ff;
}
.method-tag {
font-size: 10px;
padding: 0 4px;
border-radius: 2px;
margin-right: 8px;
min-width: 30px;
text-align: center;
}
.method-tag.get { color: #52c41a; border: 1px solid #b7eb8f; background: #f6ffed; }
.method-tag.put { color: #faad14; border: 1px solid #ffe58f; background: #fffbe6; }
.api-name {
font-size: 13px;
flex: 1;
}
.api-content {
flex: 1;
padding: 20px;
overflow-y: auto;
}
.content-header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
}
.current-api-title {
font-size: 20px;
font-weight: 500;
}
.info-section {
margin-bottom: 30px;
}
.info-title {
font-size: 16px;
font-weight: 500;
margin-bottom: 15px;
}
.info-list {
display: flex;
flex-direction: column;
gap: 12px;
padding-left: 15px;
}
.info-item {
display: flex;
flex-direction: row;
align-items: center;
}
.info-label {
font-size: 14px;
color: #666;
width: 80px;
}
.info-value {
font-size: 14px;
color: #333;
}
.method-badge {
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
}
.method-badge.get { background-color: #e6f7ff; color: #1890ff; }
.params-table {
border: 1px solid #f0f0f0;
border-radius: 4px;
}
.table-head {
background-color: #f8f8f9;
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.th, .td {
padding: 12px;
font-size: 13px;
}
.p-attr { flex: 2; }
.p-type { flex: 1; }
.p-required { flex: 1; }
.p-desc { flex: 3; }
.btn-primary-sm { background-color: #1890ff; color: #fff; font-size: 12px; padding: 5px 10px; border-radius: 4px; border: none; }
.btn-success-sm { background-color: #52c41a; color: #fff; font-size: 12px; padding: 5px 10px; border-radius: 4px; border: none; }
.btn-secondary-sm { background-color: #fff; color: #666; border: 1px solid #d9d9d9; font-size: 12px; padding: 5px 10px; border-radius: 4px; }
.ml-10 { margin-left: 10px; }
.mt-20 { margin-top: 20px; }
</style>

View File

@@ -1,7 +1,6 @@
<template>
<AdminLayout currentPage="dev-tools-codegen">
<view class="admin-page">
<view class="page">
<view class="header">
<text class="title">代码生成</text>
</view>
@@ -9,5 +8,14 @@
<text class="tip">TODO: 代码生成</text>
</view>
</view>
</AdminLayout>
</template>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page {
padding: 20px;
}
</style>

View File

@@ -1,12 +1,200 @@
<template>
<AdminLayout currentPage="dev-tools-dict">
<view class="page">
<view class="header">
<text class="title">数据字典</text>
</view>
<view class="content">
<text class="tip">TODO: 数据字典</text>
</view>
</view>
</AdminLayout>
</template>
<view class="admin-page data-dictionary">
<view class="admin-sections">
<view class="admin-card">
<!-- 搜索和添加 -->
<view class="header-tools">
<view class="search-form">
<text class="label">字典名称:</text>
<input class="search-input" v-model="searchQuery" placeholder="请输入字典名称" />
<button class="btn-search" @click="handleSearch">查询</button>
</view>
<button class="btn-primary" @click="handleAdd">添加数据字典</button>
</view>
<!-- 数据表格 -->
<view class="table-container">
<view class="table-header">
<view class="table-cell" style="flex: 0 0 60px;">ID</view>
<view class="table-cell" style="flex: 2;">字典名称</view>
<view class="table-cell" style="flex: 2;">数据标识</view>
<view class="table-cell" style="flex: 1;">类型</view>
<view class="table-cell" style="flex: 2;">添加时间</view>
<view class="table-cell action-cell" style="flex: 2;">操作</view>
</view>
<view class="table-body">
<view class="table-row" v-for="(item, index) in dictionaryList" :key="index">
<view class="table-cell" style="flex: 0 0 60px;">{{ item.id }}</view>
<view class="table-cell" style="flex: 2;">{{ item.name }}</view>
<view class="table-cell" style="flex: 2;">{{ item.tag }}</view>
<view class="table-cell" style="flex: 1;">{{ item.type }}</view>
<view class="table-cell" style="flex: 2;">{{ item.add_time }}</view>
<view class="table-cell action-cell" style="flex: 2;">
<text class="action-link" @click="handleEdit(item)">编辑</text>
<text class="action-link ml-10" @click="handleManage(item)">数据管理</text>
<text class="action-link link-danger ml-10" @click="handleDelete(item)">删除</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view></template>
<script setup lang="uts">
import { ref } from 'vue'
const searchQuery = ref('')
const dictionaryList = ref([
{ id: 8, name: '21', tag: '25425', type: '一级', add_time: '2024-08-23 17:37:08' },
{ id: 10, name: '相册', tag: 'xc', type: '多级', add_time: '2024-09-09 15:49:58' },
{ id: 11, name: '品类', tag: 'category', type: '多级', add_time: '2024-09-24 17:47:16' },
{ id: 12, name: '123', tag: '1', type: '一级', add_time: '2024-09-27 15:57:03' },
{ id: 13, name: '企业类型', tag: 'company_type', type: '多级', add_time: '2024-09-27 16:16:21' },
{ id: 14, name: '测试', tag: '343322222222234555343434', type: '多级', add_time: '2024-09-27 16:17:23' },
{ id: 16, name: '用户管理模块', tag: '123', type: '多级', add_time: '2024-10-22 09:30:46' },
{ id: 18, name: '类型', tag: '1', type: '多级', add_time: '2024-12-11 13:48:01' },
{ id: 19, name: '店铺', tag: 'shop', type: '多级', add_time: '2024-12-11 22:23:07' },
{ id: 20, name: 'ce', tag: 'zzz', type: '一级', add_time: '2024-12-14 12:19:17' }
])
function handleSearch() {
uni.showToast({ title: '搜索: ' + searchQuery.value, icon: 'none' })
}
function handleAdd() {
uni.showToast({ title: '添加数据字典', icon: 'none' })
}
function handleEdit(item: any) {
uni.showToast({ title: '编辑: ' + item.name, icon: 'none' })
}
function handleManage(item: any) {
uni.showToast({ title: '数据管理: ' + item.name, icon: 'none' })
}
function handleDelete(item: any) {
uni.showModal({
title: '提示',
content: '确定删除该数据字典吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '已删除', icon: 'success' })
}
}
})
}
</script>
<style scoped>
.admin-page {
padding: 20px;
background-color: #f5f7f9;
min-height: 100vh;
}
.admin-card {
background-color: #fff;
padding: 24px;
border-radius: 8px;
}
.header-tools {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.search-form {
display: flex;
flex-direction: row;
align-items: center;
}
.label {
font-size: 14px;
color: #333;
margin-right: 10px;
}
.search-input {
width: 200px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 0 12px;
font-size: 14px;
margin-right: 10px;
}
.btn-search {
background-color: #1890ff;
color: #fff;
font-size: 14px;
height: 32px;
line-height: 32px;
padding: 0 15px;
border-radius: 4px;
border: none;
}
.btn-primary {
background-color: #1890ff;
color: #fff;
font-size: 14px;
height: 32px;
line-height: 32px;
padding: 0 20px;
border-radius: 4px;
border: none;
}
.table-container {
border: 1px solid #f0f0f0;
border-radius: 4px;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #f8f8f9;
border-bottom: 1px solid #f0f0f0;
padding: 12px 10px;
}
.table-body {
display: flex;
flex-direction: column;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
padding: 12px 10px;
align-items: center;
}
.table-cell {
font-size: 14px;
color: #666;
padding: 0 10px;
}
.action-link {
color: #1890ff;
font-size: 13px;
cursor: pointer;
}
.link-danger {
color: #ff4d4f;
}
.ml-10 {
margin-left: 10px;
}
</style>

View File

@@ -1,12 +1,146 @@
<template>
<AdminLayout currentPage="dev-tools-db">
<view class="page">
<view class="header">
<text class="title">数据库管理</text>
</view>
<view class="content">
<text class="tip">TODO: 数据库管理</text>
</view>
</view>
</AdminLayout>
<view class="admin-page">
<view class="admin-sections">
<view class="admin-card">
<view class="page-header">
<text class="page-title">数据库管理</text>
<view class="header-btns">
<button class="btn btn-primary btn-sm" @click="handleExport">备份数据库</button>
</view>
</view>
<!-- 数据表格 -->
<view class="admin-table">
<view class="thead">
<view class="th col-name">表名</view>
<view class="th col-engine">引擎</view>
<view class="th col-rows">行数</view>
<view class="th col-size">数据大小</view>
<view class="th col-index">索引大小</view>
<view class="th col-comment">备注</view>
<view class="th col-op">操作</view>
</view>
<view class="tbody">
<view v-for="(item, index) in tableData" :key="index" class="tr">
<view class="td col-name">{{ item.name }}</view>
<view class="td col-engine">{{ item.engine }}</view>
<view class="td col-rows">{{ item.rows }}</view>
<view class="td col-size">{{ item.dataSize }}</view>
<view class="td col-index">{{ item.indexSize }}</view>
<view class="td col-comment">{{ item.comment }}</view>
<view class="td col-op">
<text class="op-link" @click="handleOptimize(item.name)">优化</text>
<text class="op-link" @click="handleRepair(item.name)">修复</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const tableData = ref([
{ name: 'eb_system_admin', engine: 'InnoDB', rows: 5, dataSize: '16.00 KB', indexSize: '16.00 KB', comment: '后台管理员表' },
{ name: 'eb_user', engine: 'InnoDB', rows: 1250, dataSize: '320.00 KB', indexSize: '156.00 KB', comment: '用户表' },
{ name: 'eb_store_product', engine: 'InnoDB', rows: 86, dataSize: '1.20 MB', indexSize: '64.00 KB', comment: '商品表' },
{ name: 'eb_store_order', engine: 'InnoDB', rows: 4521, dataSize: '2.50 MB', indexSize: '512.00 KB', comment: '订单表' },
{ name: 'eb_system_config', engine: 'InnoDB', rows: 156, dataSize: '48.00 KB', indexSize: '16.00 KB', comment: '系统配置表' }
])
function handleExport() {
uni.showLoading({ title: '正在备份...' })
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: '备份成功' })
}, 1500)
}
function handleOptimize(name: string) {
uni.showToast({ title: '优化表: ' + name, icon: 'none' })
}
function handleRepair(name: string) {
uni.showToast({ title: '修复表: ' + name, icon: 'none' })
}
</script>
<style scoped>
.admin-page {
padding: 20px;
}
.admin-card {
background-color: #fff;
padding: 20px;
border-radius: 4px;
}
.page-header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.page-title {
font-size: 16px;
font-weight: bold;
}
.btn-sm {
height: 32px;
line-height: 32px;
font-size: 14px;
padding: 0 15px;
}
.btn-primary {
background-color: #1890ff;
color: #fff;
}
/* 表格样式 */
.admin-table {
border: 1px solid #e8eaec;
}
.thead {
display: flex;
flex-direction: row;
background-color: #f8f8f9;
border-bottom: 1px solid #e8eaec;
}
.tbody {
display: flex;
flex-direction: column;
}
.tr {
display: flex;
flex-direction: row;
border-bottom: 1px solid #e8eaec;
}
.tr:last-child {
border-bottom: none;
}
.th, .td {
padding: 12px 10px;
font-size: 14px;
color: #515a6e;
display: flex;
align-items: center;
}
.th {
font-weight: bold;
}
.col-name { flex: 2; }
.col-engine { flex: 1; }
.col-rows { flex: 1; }
.col-size { flex: 1; }
.col-index { flex: 1; }
.col-comment { flex: 2; }
.col-op { width: 120px; justify-content: space-around; }
.op-link {
color: #1890ff;
cursor: pointer;
}
</style>

View File

@@ -1,12 +1,144 @@
<template>
<AdminLayout currentPage="dev-tools-file">
<view class="page">
<view class="header">
<text class="title">文件管理</text>
</view>
<view class="content">
<text class="tip">TODO: 文件管理</text>
</view>
</view>
</AdminLayout>
<view class="admin-page file-login-container">
<view class="login-card">
<view class="login-header">
<text class="login-title">文件管理登录</text>
</view>
<view class="login-form">
<view class="form-item">
<input
class="login-input"
type="password"
v-model="password"
placeholder="请输入密码"
/>
</view>
<view class="form-tip">
提示:密码配置在 /config/filesystem.php 文件中修改 'password' => '密码'
</view>
<button class="login-btn" @click="handleLogin">登录</button>
</view>
</view>
<!-- 底部备案/版本信息 (1:1 复刻 CRMEB) -->
<view class="login-footer">
<view class="footer-links">
<text class="footer-link">官网</text>
<text class="footer-link">社区</text>
<text class="footer-link">文档</text>
</view>
<view class="copyright">
Copyright © 2014-2025 CRMEB-BZ v5.6.4
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const password = ref('')
const handleLogin = () => {
if (!password.value) {
uni.showToast({ title: '请输入密码', icon: 'none' })
return
}
uni.showLoading({ title: '正在登录...' })
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: '登录成功(演示)' })
}, 1000)
}
</script>
<style scoped>
.admin-page {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: calc(100vh - 120px);
background-color: #f5f7f9;
}
.login-card {
width: 400px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
padding: 40px;
}
.login-header {
text-align: center;
margin-bottom: 30px;
}
.login-title {
font-size: 28px;
color: #1890ff;
font-weight: 500;
}
.login-form {
display: flex;
flex-direction: column;
}
.form-item {
margin-bottom: 20px;
}
.login-input {
width: 100%;
height: 40px;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 0 15px;
font-size: 14px;
box-sizing: border-box;
}
.form-tip {
font-size: 12px;
color: #909399;
margin-bottom: 20px;
text-align: center;
}
.login-btn {
width: 120px;
height: 40px;
line-height: 40px;
background-color: #1890ff;
color: #fff;
font-size: 14px;
align-self: center;
border-radius: 4px;
border: none;
}
.login-footer {
margin-top: 50px;
text-align: center;
}
.footer-links {
display: flex;
flex-direction: row;
justify-content: center;
gap: 20px;
margin-bottom: 10px;
}
.footer-link {
font-size: 14px;
color: #606266;
}
.copyright {
font-size: 14px;
color: #909399;
}
</style>

View File

@@ -0,0 +1,146 @@
<template>
<view class="admin-page">
<view class="admin-sections">
<!-- 页面标题 -->
<view class="page-header admin-card">
<text class="title">翻译配置</text>
</view>
<!-- 配置表单 -->
<view class="admin-card content-card form-container">
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
<text>AccessKey:</text>
</view>
<view class="form-content">
<input class="form-input" v-model="config.accessKey" placeholder="请输入 AccessKey" />
<text class="form-tip">机器翻译仅支持火山翻译,注册地址 https://console.volcengine.com在访问控制里面新建 api 密钥</text>
</view>
</view>
<view class="form-item mt-24">
<view class="form-label">
<text class="required">*</text>
<text>SecretKey:</text>
</view>
<view class="form-content">
<input class="form-input" v-model="config.secretKey" placeholder="请输入 SecretKey" type="password" />
<text class="form-tip">机器翻译仅支持火山翻译,注册地址 https://console.volcengine.com在访问控制里面新建 api 密钥</text>
</view>
</view>
<view class="form-actions mt-32">
<button class="btn primary submit-btn" @click="onSubmit">提交</button>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const config = ref({
accessKey: 'AKLTMzkzZTEzNjg3OTg2NDVlMzlwnmFIYzhmNzkzMmE4MmI4YmI',
secretKey: 'TVRneU16STFOVFV4WVRWVE5EERTJaV0pqWm1aa1U1UagINVFpPWld'
})
function onSubmit() {
uni.showLoading({ title: '提交中...' })
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: '保存成功' })
}, 1000)
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 24px;
background-color: #f5f7f9;
min-height: 100vh;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 20px 24px;
margin-bottom: 24px;
}
.title {
font-size: 18px;
font-weight: 500;
color: #333;
}
.form-item {
display: flex;
flex-direction: row;
align-items: flex-start;
}
.form-label {
width: 120px;
padding-top: 8px;
text-align: right;
margin-right: 16px;
font-size: 14px;
color: #333;
}
.required {
color: #ff4d4f;
margin-right: 4px;
}
.form-content {
flex: 1;
max-width: 600px;
}
.form-input {
width: 100%;
height: 38px;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 0 12px;
font-size: 14px;
}
.form-tip {
display: block;
margin-top: 8px;
font-size: 12px;
color: #999;
}
.mt-24 { margin-top: 24px; }
.mt-32 { margin-top: 32px; }
.form-actions {
padding-left: 136px;
}
.btn {
height: 38px;
padding: 0 24px;
border-radius: 4px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
}
.btn.primary {
background-color: #1890ff;
color: #fff;
}
.submit-btn {
width: 80px;
}
</style>

View File

@@ -0,0 +1,274 @@
<template>
<view class="admin-page">
<view class="admin-sections">
<!-- 搜索栏 -->
<view class="admin-card filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">语言分类:</text>
<view class="filter-select">
<text class="select-text">页面语言</text>
<text class="arrow">▼</text>
</view>
</view>
<view class="filter-item">
<text class="label">语言类型:</text>
<view class="filter-select select-md">
<text class="select-text">中文(zh-CN)</text>
<text class="arrow">▼</text>
</view>
</view>
<view class="filter-item">
<text class="label">搜索:</text>
<input class="filter-input" placeholder="请输入语言备注" />
</view>
<button class="btn primary" @click="onSearch">搜索</button>
</view>
</view>
<!-- 提示栏 -->
<view class="alert-info">
<view class="info-block">
<text class="info-title">页面语言</text>
<text class="info-content">添加页面语言,添加完成之后状态码为中文文字,移动端面使用 $t('xxxx')js文件中使用 this.$t('xxxx') 或者使用 that.$t('xxxx') 实现语言的切换</text>
</view>
<view class="info-block mt-12">
<text class="info-title">接口语言</text>
<text class="info-content">添加接口语言添加完成之后状态码为6位数字接口返回提示信息时直接返回对应的错误码即可实现语言的切换</text>
</view>
</view>
<!-- 操作栏 -->
<view class="action-bar header-actions">
<button class="btn primary" @click="addWord">添加语句</button>
</view>
<!-- 表格内容 -->
<view class="admin-card content-card">
<view class="table-container list-table">
<view class="table-header">
<view class="col col-id"><text>编号</text></view>
<view class="col col-origin"><text>原语句</text></view>
<view class="col col-translation"><text>对应语言翻译</text></view>
<view class="col col-code"><text>状态码/文字(接口/页面调用参考)</text></view>
<view class="col col-type"><text>语言类型</text></view>
<view class="col col-action"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in dataList" :key="item.id" class="table-row">
<view class="col col-id"><text>{{ item.id }}</text></view>
<view class="col col-origin"><text>{{ item.origin }}</text></view>
<view class="col col-translation"><text>{{ item.translation }}</text></view>
<view class="col col-code"><text>{{ item.code }}</text></view>
<view class="col col-type"><text>{{ item.type }}</text></view>
<view class="col col-action">
<text class="action-btn" @click="editWord(item)">编辑</text>
<text class="action-btn divider">|</text>
<text class="action-btn danger" @click="deleteWord(item)">删除</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const dataList = ref([
{ id: 55493, origin: '秒杀活动', translation: '秒杀活动', code: '秒杀活动', type: '中文(zh-CN)' },
{ id: 55483, origin: '哥哥我', translation: '', code: '哥哥我', type: '中文(zh-CN)' },
{ id: 25221, origin: '提交订单', translation: '提交订单', code: '提交订单', type: '中文(zh-CN)' },
{ id: 25211, origin: '用户等级优惠', translation: '用户等级优惠', code: '用户等级优惠', type: '中文(zh-CN)' },
{ id: 25201, origin: '暂不支付', translation: '暂不支付', code: '暂不支付', type: '中文(zh-CN)' },
{ id: 25191, origin: '确认支付', translation: '确认支付', code: '确认支付', type: '中文(zh-CN)' },
{ id: 25181, origin: '支付剩余时间', translation: '支付剩余时间', code: '支付剩余时间', type: '中文(zh-CN)' },
{ id: 24711, origin: '全部已读', translation: '全部已读', code: '全部已读', type: '中文(zh-CN)' },
{ id: 24701, origin: '获得拼团团长佣金', translation: '获得拼团团长佣金', code: '获得拼团团长佣金', type: '中文(zh-CN)' },
{ id: 24691, origin: '获得事业部推广订单佣金', translation: '获得事业部推广订单佣金', code: '获得事业部推广订单佣金', type: '中文(zh-CN)' }
])
function onSearch() {
uni.showToast({ title: '搜索中...', icon: 'none' })
}
function addWord() {
uni.showToast({ title: '添加语句', icon: 'none' })
}
function editWord(item: any) {
uni.showToast({ title: '编辑: ' + item.origin, icon: 'none' })
}
function deleteWord(item: any) {
uni.showModal({
title: '提示',
content: '确定要删除此翻译语句吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '删除成功' })
}
}
})
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 24px;
background-color: #f5f7f9;
min-height: 100vh;
}
.filter-row {
display: flex;
flex-direction: row;
align-items: center;
gap: 20px;
}
.filter-item {
display: flex;
flex-direction: row;
align-items: center;
}
.label {
font-size: 14px;
color: #333;
margin-right: 8px;
white-space: nowrap;
}
.filter-select {
width: 150px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0 10px;
}
.select-md {
width: 200px;
}
.filter-input {
width: 200px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 0 10px;
font-size: 14px;
}
.alert-info {
background-color: #fff7e6;
border: 1px solid #ffe7ba;
padding: 20px;
border-radius: 4px;
margin: 24px 0;
}
.info-block {
display: flex;
flex-direction: column;
}
.mt-12 {
margin-top: 12px;
}
.info-title {
color: #fa8c16;
font-size: 14px;
font-weight: bold;
margin-bottom: 4px;
}
.info-content {
color: #fa8c16;
font-size: 14px;
line-height: 1.6;
}
.action-bar {
margin-bottom: 20px;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 0;
overflow: hidden;
}
.btn {
height: 32px;
padding: 0 16px;
border-radius: 4px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
}
.btn.primary {
background-color: #1890ff;
color: #fff;
}
.table-container {
width: 100%;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-id { width: 80px; }
.col-origin { flex: 2; }
.col-translation { flex: 2; }
.col-code { flex: 2; }
.col-type { width: 150px; }
.col-action { width: 150px; justify-content: flex-end; }
.action-btn {
color: #1890ff;
cursor: pointer;
}
.action-btn.danger {
color: #ff4d4f;
}
.action-btn.divider {
margin: 0 8px;
color: #e8e8e8;
}
</style>

View File

@@ -0,0 +1,226 @@
<template>
<view class="admin-page">
<view class="admin-sections">
<!-- 操作栏 -->
<view class="action-bar header-actions">
<button class="btn primary" @click="addLanguage">添加语言</button>
</view>
<!-- 表格内容 -->
<view class="admin-card content-card">
<view class="table-container list-table">
<view class="table-header">
<view class="col col-id"><text>ID</text></view>
<view class="col col-name"><text>语言名称</text></view>
<view class="col col-code"><text>浏览器语言识别码</text></view>
<view class="col col-status"><text>状态</text></view>
<view class="col col-action"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in dataList" :key="item.id" class="table-row">
<view class="col col-id"><text>{{ item.id }}</text></view>
<view class="col col-name">
<text>{{ item.name }}</text>
<text v-if="item.isDefault" class="default-badge">默认</text>
</view>
<view class="col col-code"><text>{{ item.code }}</text></view>
<view class="col col-status">
<view :class="['status-tag', item.status ? 'active' : 'inactive']" @click="toggleStatus(item)">
<text class="tag-text">{{ item.status ? '开启' : '关闭' }}</text>
<view class="tag-dot"></view>
</view>
</view>
<view class="col col-action">
<text class="action-btn" @click="editItem(item)">编辑</text>
<text class="action-btn divider">|</text>
<text class="action-btn danger" @click="deleteItem(item)">删除</text>
</view>
</view>
</view>
</view>
<!-- 分页 -->
<view class="pagination">
<text class="page-info">共 10 条 15条/页</text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const dataList = ref([
{ id: 1, name: '中文', code: 'zh-CN', status: true, isDefault: true },
{ id: 2, name: 'English', code: 'en-US', status: true, isDefault: false },
{ id: 3, name: '繁體中文', code: 'zh-Hant', status: true, isDefault: false },
{ id: 4, name: 'Français', code: 'fr-FR', status: true, isDefault: false },
{ id: 5, name: 'Italiano', code: 'it-IT', status: true, isDefault: false },
{ id: 6, name: '日本語', code: 'ja-JP', status: true, isDefault: false },
{ id: 7, name: '한국어', code: 'ko-KR', status: true, isDefault: false },
{ id: 8, name: 'Монгол', code: 'mn-MN', status: true, isDefault: false },
{ id: 9, name: 'ภาษาไทย', code: 'th-TH', status: true, isDefault: false },
{ id: 10, name: 'ViệtName', code: 'vi-VN', status: true, isDefault: false }
])
function addLanguage() {
uni.showToast({ title: '添加语言', icon: 'none' })
}
function toggleStatus(item: any) {
item.status = !item.status
}
function editItem(item: any) {
uni.showToast({ title: '编辑: ' + item.name, icon: 'none' })
}
function deleteItem(item: any) {
uni.showModal({
title: '提示',
content: '确定要删除此语言吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '删除成功' })
}
}
})
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 24px;
background-color: #f5f7f9;
min-height: 100vh;
}
.action-bar {
margin-bottom: 20px;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 0;
overflow: hidden;
}
.btn {
height: 32px;
padding: 0 16px;
border-radius: 4px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
}
.btn.primary {
background-color: #1890ff;
color: #fff;
}
.table-container {
width: 100%;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-id { width: 80px; }
.col-name { flex: 1; position: relative; }
.col-code { flex: 1; }
.col-status { width: 120px; justify-content: center; }
.col-action { width: 150px; justify-content: flex-end; }
.default-badge {
background-color: #e6f7ff;
color: #1890ff;
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
margin-left: 8px;
border: 1px solid #91d5ff;
}
.status-tag {
width: 60px;
height: 24px;
border-radius: 12px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0 8px;
cursor: pointer;
}
.status-tag.active {
background-color: #1890ff;
color: #fff;
}
.status-tag.inactive {
background-color: #d9d9d9;
color: #fff;
}
.tag-text {
font-size: 12px;
}
.tag-dot {
width: 8px;
height: 8px;
background-color: #fff;
border-radius: 50%;
}
.action-btn {
color: #1890ff;
cursor: pointer;
}
.action-btn.danger {
color: #ff4d4f;
}
.action-btn.divider {
margin: 0 8px;
color: #e8e8e8;
}
.pagination {
padding: 16px 24px;
display: flex;
justify-content: flex-end;
}
.page-info {
font-size: 14px;
color: #999;
}
</style>

View File

@@ -0,0 +1,191 @@
<template>
<view class="admin-page">
<view class="admin-sections">
<!-- 搜索栏 -->
<view class="admin-card filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">搜索:</text>
<input class="filter-input" placeholder="请输入语言Code" />
</view>
<button class="btn primary" @click="onSearch">搜索</button>
</view>
</view>
<!-- 操作栏 -->
<view class="action-bar header-actions">
<button class="btn primary" @click="addRegion">添加语言地区</button>
</view>
<!-- 表格内容 -->
<view class="admin-card content-card">
<view class="table-container list-table">
<view class="table-header">
<view class="col col-id"><text>编号</text></view>
<view class="col col-code"><text>浏览器语言识别码</text></view>
<view class="col col-desc"><text>语言说明</text></view>
<view class="col col-lang"><text>关联语言</text></view>
<view class="col col-action"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in dataList" :key="item.id" class="table-row">
<view class="col col-id"><text>{{ item.id }}</text></view>
<view class="col col-code"><text>{{ item.code }}</text></view>
<view class="col col-desc"><text>{{ item.desc }}</text></view>
<view class="col col-lang"><text>{{ item.lang || '暂无' }}</text></view>
<view class="col col-action">
<text class="action-btn" @click="editItem(item)">编辑</text>
<text class="action-btn divider">|</text>
<text class="action-btn danger" @click="deleteItem(item)">删除</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const dataList = ref([
{ id: 346, code: 'zh-HK', desc: '中文(繁体, 香港特别行政区)', lang: '' },
{ id: 344, code: 'zh-MO', desc: '中文(繁体, 澳门特别行政区)', lang: '' },
{ id: 343, code: 'zh-Hant', desc: '中文(繁体)', lang: '繁体中文(zh-Hant)' },
{ id: 342, code: 'zh', desc: '中文', lang: '' },
{ id: 340, code: 'vi', desc: '越南语', lang: '' },
{ id: 339, code: 'yo-NG', desc: '约鲁巴语(尼日利亚)', lang: '' },
{ id: 337, code: 'en-GB', desc: '英语(英国)', lang: '' },
{ id: 336, code: 'en-IN', desc: '英语(印度)', lang: '' },
{ id: 335, code: 'en-JM', desc: '英语(牙买加)', lang: '' },
{ id: 334, code: 'en-NZ', desc: '英语(新西兰)', lang: '' }
])
function onSearch() {
uni.showToast({ title: '搜索中...', icon: 'none' })
}
function addRegion() {
uni.showToast({ title: '添加语言地区', icon: 'none' })
}
function editItem(item: any) {
uni.showToast({ title: '编辑: ' + item.code, icon: 'none' })
}
function deleteItem(item: any) {
uni.showModal({
title: '提示',
content: '确定要删除此地区配置吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '删除成功' })
}
}
})
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 24px;
background-color: #f5f7f9;
min-height: 100vh;
}
.filter-row {
display: flex;
flex-direction: row;
align-items: center;
gap: 16px;
}
.label {
font-size: 14px;
color: #333;
}
.filter-input {
width: 200px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 0 10px;
font-size: 14px;
}
.btn {
height: 32px;
padding: 0 16px;
border-radius: 4px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
}
.btn.primary {
background-color: #1890ff;
color: #fff;
}
.action-bar {
margin-bottom: 20px;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 0;
overflow: hidden;
}
.table-container {
width: 100%;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-id { width: 100px; }
.col-code { flex: 1; }
.col-desc { flex: 2; }
.col-lang { flex: 1; }
.col-action { width: 150px; justify-content: flex-end; }
.action-btn {
color: #1890ff;
cursor: pointer;
}
.action-btn.danger {
color: #ff4d4f;
}
.action-btn.divider {
margin: 0 8px;
color: #e8e8e8;
}
</style>

View File

@@ -1,12 +1,236 @@
<template>
<AdminLayout currentPage="security-online-upgrade">
<view class="page">
<view class="header">
<text class="title">在线升级</text>
</view>
<view class="content">
<text class="tip">TODO: 在线升级</text>
</view>
</view>
</AdminLayout>
<view class="admin-page">
<view class="admin-sections">
<!-- 提示栏 -->
<view class="alert-info">
<text class="alert-text">温馨提示:检查更新需要授权码,请先授权后再检查更新!</text>
</view>
<!-- 选项卡 -->
<view class="admin-tabs">
<view
v-for="tab in tabs"
:key="tab.value"
:class="['tab-item', activeTab === tab.value ? 'active' : '']"
@click="activeTab = tab.value"
>
<text class="tab-label">{{ tab.label }}</text>
</view>
</view>
<!-- 升级内容 -->
<view v-if="activeTab === 'upgrade'" class="admin-card">
<view class="upgrade-status">
<view class="status-item">
<text class="label">当前版本:</text>
<text class="value version-tag">v3.1.2</text>
</view>
<view class="status-item">
<text class="label">授权状态:</text>
<text class="value unauthorized">未授权</text>
</view>
<view class="status-item">
<button class="btn primary" @click="checkUpdates">检查更新</button>
</view>
</view>
</view>
<!-- 升级日志内容 -->
<view v-if="activeTab === 'log'" class="admin-card">
<view class="table-container list-table">
<view class="table-header">
<view class="col col-id"><text>ID</text></view>
<view class="col col-version"><text>版本号</text></view>
<view class="col col-content"><text>更新内容</text></view>
<view class="col col-time"><text>升级时间</text></view>
</view>
<view class="table-body">
<view v-for="item in logList" :key="item.id" class="table-row">
<view class="col col-id"><text>{{ item.id }}</text></view>
<view class="col col-version"><text>{{ item.version }}</text></view>
<view class="col col-content"><text>{{ item.content }}</text></view>
<view class="col col-time"><text>{{ item.time }}</text></view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const activeTab = ref('upgrade')
const tabs = [
{ label: '系统升级', value: 'upgrade' },
{ label: '升级日志', value: 'log' }
]
const logList = ref([
{ id: 1, version: 'v3.1.2', content: '优化核心库性能修复已知Bug', time: '2026-02-10 10:00' },
{ id: 2, version: 'v3.1.1', content: '修复支付接口异常,增加权限校验', time: '2026-01-25 15:30' },
{ id: 3, version: 'v3.1.0', content: '大版本发布优化UI界面支持多级路由', time: '2026-01-10 09:20' }
])
function checkUpdates() {
uni.showLoading({ title: '检查中...' })
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: '您已是最新版本', icon: 'success' })
}, 1500)
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 20px;
background-color: #f5f7f9;
min-height: 100vh;
}
.alert-info {
background-color: #fffbe6;
border: 1px solid #ffe58f;
padding: 12px 16px;
border-radius: 4px;
margin-bottom: 20px;
}
.alert-text {
color: #856404;
font-size: 14px;
}
.admin-tabs {
display: flex;
flex-direction: row;
border-bottom: 1px solid #e8e8e8;
margin-bottom: 20px;
}
.tab-item {
padding: 12px 24px;
cursor: pointer;
position: relative;
}
.tab-item.active {
background-color: #fff;
}
.tab-item.active::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
right: 0;
height: 2px;
background-color: #1890ff;
}
.tab-label {
font-size: 14px;
color: #666;
}
.tab-item.active .tab-label {
color: #1890ff;
font-weight: bold;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 30px;
}
.upgrade-status {
display: flex;
flex-direction: column;
gap: 20px;
}
.status-item {
display: flex;
flex-direction: row;
align-items: center;
}
.label {
font-size: 16px;
color: #666;
width: 100px;
}
.value {
font-size: 16px;
color: #333;
}
.version-tag {
background-color: #e6f7ff;
color: #1890ff;
padding: 2px 8px;
border-radius: 4px;
font-weight: bold;
}
.unauthorized {
color: #ff4d4f;
font-weight: bold;
}
.btn {
height: 38px;
padding: 0 30px;
border-radius: 4px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 1px solid #d9d9d9;
background-color: #fff;
}
.btn.primary {
background-color: #1890ff;
border-color: #1890ff;
color: #fff;
}
.table-container {
width: 100%;
border: 1px solid #f0f0f0;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 12px 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-id { width: 80px; }
.col-version { width: 150px; }
.col-content { flex: 1; }
.col-time { width: 200px; }
</style>

View File

@@ -1,12 +1,116 @@
<template>
<AdminLayout currentPage="security-refresh-cache">
<view class="page">
<view class="header">
<text class="title">刷新缓存</text>
</view>
<view class="content">
<text class="tip">TODO: 刷新缓存</text>
</view>
</view>
</AdminLayout>
<view class="admin-page">
<view class="admin-sections">
<view class="admin-grid-2">
<!-- 清除缓存 -->
<view class="admin-card cache-card">
<view class="cache-header">
<text class="cache-title">清除缓存</text>
<text class="cache-desc">清除系统的所有缓存</text>
</view>
<button class="btn primary full" @click="onClearCache">立即清除</button>
</view>
<!-- 清除日志 -->
<view class="admin-card cache-card">
<view class="cache-header">
<text class="cache-title">清除日志</text>
<text class="cache-desc">清除系统的所有日志文件</text>
</view>
<button class="btn primary full" @click="onClearLog">立即清除</button>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
function onClearCache() {
uni.showModal({
title: '提示',
content: '确定要清除所有缓存吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '缓存已清除', icon: 'none' })
}
}
})
}
function onClearLog() {
uni.showModal({
title: '提示',
content: '确定要清除所有日志吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '日志已清除', icon: 'none' })
}
}
})
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 20px;
background-color: #f5f7f9;
min-height: 100vh;
}
.admin-grid-2 {
display: flex;
flex-direction: row;
gap: 20px;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 40px;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.cache-header {
margin-bottom: 24px;
}
.cache-title {
font-size: 18px;
color: #333;
font-weight: bold;
display: block;
margin-bottom: 12px;
}
.cache-desc {
font-size: 14px;
color: #999;
}
.btn {
height: 32px;
padding: 0 20px;
border-radius: 2px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.btn.primary {
background-color: #1890ff;
color: #fff;
border: none;
}
.btn.full {
width: 120px;
}
</style>

View File

@@ -1,12 +1,242 @@
<template>
<AdminLayout currentPage="security-system-log">
<view class="page">
<view class="header">
<text class="title">系统日志</text>
</view>
<view class="content">
<text class="tip">TODO: 系统日志</text>
</view>
</view>
</AdminLayout>
<view class="admin-page">
<view class="admin-sections">
<!-- 搜索栏 -->
<view class="admin-card filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">选择时间:</text>
<view class="filter-date-range">
<input class="filter-input date-input" placeholder="开始日期" />
<text class="date-sep">-</text>
<input class="filter-input date-input" placeholder="结束日期" />
</view>
</view>
<view class="filter-item">
<text class="label">名称:</text>
<view class="filter-select">
<text class="select-placeholder">请选择</text>
<text class="arrow">▼</text>
</view>
</view>
<view class="filter-item">
<text class="label">链接:</text>
<input class="filter-input" placeholder="请输入链接" />
</view>
<view class="filter-item">
<text class="label">IP</text>
<input class="filter-input" placeholder="请输入IP" />
</view>
<button class="btn primary" @click="onSearch">搜索</button>
</view>
</view>
<!-- 内容区 -->
<view class="admin-card content-card">
<!-- 表格 -->
<view class="table-container list-table">
<view class="table-header">
<view class="col col-id"><text>ID</text></view>
<view class="col col-user"><text>ID/名称</text></view>
<view class="col col-action"><text>操作</text></view>
<view class="col col-link"><text>链接</text></view>
<view class="col col-ip"><text>操作IP</text></view>
<view class="col col-type"><text>类型</text></view>
<view class="col col-time"><text>操作时间</text></view>
</view>
<view class="table-body">
<view v-for="item in dataList" :key="item.id" class="table-row">
<view class="col col-id"><text>{{ item.id }}</text></view>
<view class="col col-user"><text>{{ item.user }}</text></view>
<view class="col col-action"><text>{{ item.action }}</text></view>
<view class="col col-link"><text>{{ item.link }}</text></view>
<view class="col col-ip"><text>{{ item.ip }}</text></view>
<view class="col col-type"><text>{{ item.type }}</text></view>
<view class="col col-time"><text>{{ item.time }}</text></view>
</view>
</view>
</view>
<!-- 分页 -->
<view class="pagination">
<text class="page-info">共 583387 条 15条/页</text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const dataList = ref([
{ id: 585387, user: '5 / demo', action: '系统日志', link: 'system/log', ip: '223.104.72.77', type: 'system', time: '2026-02-11 18:49' },
{ id: 585386, user: '5 / demo', action: '系统日志管理员搜索条件', link: 'system/log/search_admin', ip: '223.104.72.77', type: 'system', time: '2026-02-11 18:49' },
{ id: 585385, user: '5 / demo', action: '自定义事件类型', link: 'system/event/mark', ip: '223.104.72.77', type: 'system', time: '2026-02-11 18:49' },
{ id: 585384, user: '5 / demo', action: '自定义事件列表', link: 'system/event/list', ip: '223.104.72.77', type: 'system', time: '2026-02-11 18:49' },
{ id: 585383, user: '5 / demo', action: '积分配置编辑表单', link: 'marketing/integral_config/edit_basics', ip: '223.104.72.77', type: 'system', time: '2026-02-11 18:48' },
{ id: 585382, user: '5 / demo', action: '基本配置编辑头部数据', link: 'setting/config/header_basics', ip: '223.104.72.77', type: 'system', time: '2026-02-11 18:48' },
{ id: 585381, user: '5 / demo', action: '保存权限菜单', link: 'setting/menus', ip: '223.104.72.77', type: 'system', time: '2026-02-11 18:48' },
{ id: 585380, user: '5 / demo', action: '定时任务类型', link: 'system/crontab/mark', ip: '223.104.72.77', type: 'system', time: '2026-02-11 18:47' },
{ id: 585379, user: '5 / demo', action: '定时任务列表', link: 'system/crontab/list', ip: '223.104.72.77', type: 'system', time: '2026-02-11 18:47' },
{ id: 585378, user: '5 / demo', action: '保存组合数据', link: 'setting/group', ip: '223.104.72.77', type: 'system', time: '2026-02-11 18:47' },
{ id: 585377, user: '5 / demo', action: '保存系统配置分类', link: 'setting/config_class', ip: '223.104.72.77', type: 'system', time: '2026-02-11 18:47' }
])
function onSearch() {
uni.showToast({ title: '搜索中...', icon: 'none' })
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 20px;
background-color: #f5f7f9;
min-height: 100vh;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
}
.filter-row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
gap: 16px;
}
.filter-item {
display: flex;
flex-direction: row;
align-items: center;
}
.label {
font-size: 14px;
color: #333;
white-space: nowrap;
}
.filter-date-range {
display: flex;
flex-direction: row;
align-items: center;
border: 1px solid #d9d9d9;
border-radius: 2px;
padding: 0 10px;
height: 32px;
}
.date-input {
width: 100px;
border: none;
height: 100%;
}
.date-sep {
margin: 0 8px;
color: #bfbfbf;
}
.filter-select {
width: 120px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 2px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0 10px;
}
.select-placeholder {
color: #bfbfbf;
font-size: 14px;
}
.arrow {
font-size: 10px;
color: #bfbfbf;
}
.filter-input {
width: 150px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 2px;
padding: 0 10px;
font-size: 14px;
}
.btn {
height: 32px;
padding: 0 20px;
border-radius: 2px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 1px solid #d9d9d9;
background-color: #fff;
}
.btn.primary {
background-color: #1890ff;
border-color: #1890ff;
color: #fff;
}
.table-container {
width: 100%;
border: 1px solid #f0f0f0;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 12px 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
.col-id { width: 80px; }
.col-user { width: 120px; }
.col-action { flex: 2; }
.col-link { flex: 2; }
.col-ip { width: 150px; }
.col-type { width: 100px; }
.col-time { width: 180px; }
.pagination {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
.page-info {
font-size: 14px;
color: #999;
}
</style>

View File

@@ -0,0 +1,178 @@
<template>
<view class="admin-page">
<view class="admin-sections">
<!-- 商业授权 -->
<view class="admin-card info-section">
<view class="section-header">
<text class="section-title">商业授权</text>
</view>
<view class="table-container header-table">
<view class="table-header">
<view class="col col-title"><text>产品证书编号</text></view>
<view class="col col-action"><text>操作</text></view>
</view>
<view class="table-body">
<view class="table-row">
<view class="col col-title"><text>ZC2884891</text></view>
<view class="col col-action">
<text class="action-btn" @click="gotoOfficial">进入官网</text>
</view>
</view>
</view>
</view>
</view>
<!-- 自定义版权信息 -->
<view class="admin-card info-section mt-24">
<view class="section-header">
<text class="section-title">自定义版权信息</text>
</view>
<view class="table-container header-table">
<view class="table-header">
<view class="col col-text"><text>文字版权信息</text></view>
<view class="col col-image"><text>底部版权图片</text></view>
<view class="col col-action"><text>操作</text></view>
</view>
<view class="table-body">
<view class="table-row">
<view class="col col-text"><text></text></view>
<view class="col col-image"><text></text></view>
<view class="col col-action">
<text class="action-btn" @click="editCopyright">编辑</text>
</view>
</view>
</view>
</view>
</view>
<!-- 服务器信息 -->
<view class="admin-card info-section mt-24">
<view class="section-header">
<text class="section-title">服务器信息</text>
</view>
<view class="table-container header-table">
<view class="table-header">
<view class="col col-env"><text>环境</text></view>
<view class="col col-req"><text>要求</text></view>
<view class="col col-status"><text>状态</text></view>
</view>
<view class="table-body">
<view class="table-row">
<view class="col col-env"><text>服务器系统</text></view>
<view class="col col-req"><text>类UNIX</text></view>
<view class="col col-status"><text>Linux</text></view>
</view>
<view class="table-row">
<view class="col col-env"><text>WEB环境</text></view>
<view class="col col-req"><text>Apache/Nginx/IIS</text></view>
<view class="col col-status"><text>nginx/1.24.0</text></view>
</view>
</view>
</view>
</view>
<!-- 系统环境要求 -->
<view class="admin-card info-section mt-24">
<view class="section-header">
<text class="section-title">系统环境要求</text>
</view>
<view class="table-container header-table">
<view class="table-header">
<view class="col col-env"><text>环境</text></view>
<view class="col col-req"><text>要求</text></view>
<view class="col col-status"><text>状态</text></view>
</view>
<view class="table-body">
<view class="table-row">
<view class="col col-env"><text>PHP版本</text></view>
<view class="col col-req"><text>7.1-7.4</text></view>
<view class="col col-status"><text>7.4.33</text></view>
</view>
<view class="table-row">
<view class="col col-env"><text>MySQL版本</text></view>
<view class="col col-req"><text>5.6-8.0</text></view>
<view class="col col-status"><text>8.0.35</text></view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
function gotoOfficial() {
uni.showToast({ title: '正在打开官网...', icon: 'none' })
}
function editCopyright() {
uni.showToast({ title: '编辑版权信息', icon: 'none' })
}
</script>
<style scoped lang="scss">
.admin-page {
padding: 24px;
background-color: #f5f7f9;
min-height: 100vh;
}
.admin-card {
background-color: #fff;
border-radius: 4px;
padding: 24px;
}
.section-header {
margin-bottom: 16px;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
}
.table-container {
width: 100%;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #e6f7ff;
border-bottom: 1px solid #f0f0f0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 16px;
display: flex;
align-items: center;
font-size: 14px;
color: #333;
}
/* 各表格占位列宽 */
.col-title { flex: 1; }
.col-text { flex: 1; }
.col-image { flex: 1; }
.col-env { flex: 1; }
.col-req { flex: 1; }
.col-status { flex: 1; }
.col-action { width: 150px; justify-content: flex-end; }
.action-btn {
color: #1890ff;
cursor: pointer;
}
.mt-24 {
margin-top: 24px;
}
</style>

View File

@@ -1,23 +1,225 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">协议管理</text>
</view>
<view class="page-content">
<text class="placeholder-text">协议管理 页面开发中...</text>
</view>
</view>
</view>
</template>
<view class="admin-page agreement-settings">
<view class="admin-sections">
<view class="admin-card">
<!-- 协议类型标签页 -->
<scroll-view class="tabs-scroll" scroll-x="true" show-scrollbar="false">
<view class="tabs-bar">
<view
v-for="(tab, index) in agreementTabs"
:key="index"
class="tab-item"
:class="{ active: currentTab === index }"
@click="currentTab = index"
>
<text class="tab-text">{{ tab }}</text>
<view class="tab-line" v-if="currentTab === index"></view>
</view>
</view>
</scroll-view>
<view class="editor-layout">
<!-- 左侧编辑器区域 -->
<view class="editor-main">
<view class="editor-toolbar">
<!-- 模拟富文本工具栏 -->
<view class="toolbar-icon">H</view>
<view class="toolbar-icon">B</view>
<view class="toolbar-icon">I</view>
<view class="toolbar-icon">U</view>
<view class="toolbar-icon">S</view>
<view class="toolbar-icon">🔗</view>
<view class="toolbar-icon">🖼️</view>
</view>
<textarea
class="rich-textarea"
v-model="agreementContent"
placeholder="请输入协议内容..."
></textarea>
<view class="editor-footer">
<button class="btn btn-primary" @click="handleSave">保存</button>
</view>
</view>
<!-- 右侧手机预览 -->
<view class="preview-area">
<view class="phone-frame">
<view class="phone-header">
<text class="phone-title">{{ agreementTabs[currentTab] }}</text>
</view>
<scroll-view class="phone-content" scroll-y="true">
<rich-text :nodes="agreementContent"></rich-text>
</scroll-view>
</view>
<text class="preview-tip">协议预览</text>
</view>
</view>
</view>
</view>
</view></template>
<script setup lang="uts">
import { ref, watch } from 'vue'
const currentTab = ref(0)
const agreementTabs = [
'付费会员协议', '代理商协议', '隐私协议', '用户协议', '注销协议', '积分协议', '分销协议'
]
const agreementContent = ref(`
<h2 style="text-align: center;">第1条 相关定义</h2>
<p>1.1 SVIP会员SVIP会员是为商城客户打造的高级会员服务通过提供高品质的客户服务让网购变的更加方便省钱和放心。</p>
<p>1.5 实名信息用户开通SVIP11会员需保证商城用户信息真实同一自然人如有多个商城账号可同时开通对应的多个会员。</p>
<h2 style="text-align: center;">第2条 本站服务条款的确认和接纳</h2>
<p>2.1 本站所提供的SVIP会员试用期及正式期活动的所有权和运作权归本公司所有。</p>
<p>2.2 用户支付会员成功即视为用户确认自己同意接受SVIP会员相关服务的条款且同意按本协议内容履行如产生用户相关责任的同意承担相应法律责任。</p>
`)
const handleSave = () => {
uni.showToast({ title: '保存成功', icon: 'success' })
}
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
.agreement-settings {
padding: 20px;
background-color: #f5f7f9;
}
.admin-card {
background-color: #fff;
padding: 24px;
border-radius: 8px;
}
.tabs-scroll {
border-bottom: 1px solid #f0f0f0;
margin-bottom: 25px;
}
.tabs-bar {
display: flex;
flex-direction: row;
white-space: nowrap;
}
.tab-item {
padding: 12px 25px;
position: relative;
cursor: pointer;
}
.tab-item.active {
color: #1890ff;
}
.tab-text {
font-size: 14px;
}
.tab-line {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background-color: #1890ff;
}
.editor-layout {
display: flex;
flex-direction: row;
gap: 40px;
}
.editor-main {
flex: 1;
display: flex;
flex-direction: column;
border: 1px solid #dcdfe6;
border-radius: 4px;
}
.editor-toolbar {
display: flex;
flex-direction: row;
padding: 10px;
background-color: #f8f8f9;
border-bottom: 1px solid #dcdfe6;
gap: 15px;
}
.toolbar-icon {
font-size: 16px;
color: #666;
cursor: pointer;
}
.rich-textarea {
width: 100%;
height: 500px;
padding: 20px;
font-size: 14px;
line-height: 1.6;
box-sizing: border-box;
}
.editor-footer {
padding: 20px;
border-top: 1px solid #f0f0f0;
text-align: center;
}
.preview-area {
width: 320px;
display: flex;
flex-direction: column;
align-items: center;
}
.phone-frame {
width: 300px;
height: 600px;
border: 12px solid #333;
border-radius: 36px;
overflow: hidden;
background-color: #fff;
display: flex;
flex-direction: column;
}
.phone-header {
height: 44px;
background-color: #f8f8f8;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 1px solid #eeeeee;
}
.phone-title {
font-size: 16px;
font-weight: 500;
}
.phone-content {
flex: 1;
padding: 15px;
}
.preview-tip {
margin-top: 15px;
font-size: 14px;
color: #999;
}
.btn-primary {
background-color: #1890ff;
color: #fff;
border: none;
padding: 0 30px;
height: 40px;
line-height: 40px;
border-radius: 4px;
}
</style>

View File

@@ -1,23 +1,238 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">消息管理</text>
</view>
<view class="page-content">
<text class="placeholder-text">消息管理 页面开发中...</text>
</view>
</view>
</view>
</template>
<view class="admin-page message-management">
<view class="admin-sections">
<view class="admin-card">
<!-- 标签页 -->
<view class="tabs-container">
<view class="tabs-bar">
<view
class="tab-item"
:class="{ active: currentTab === 0 }"
@click="currentTab = 0"
>
<text class="tab-text">会员通知</text>
<view class="tab-line" v-if="currentTab === 0"></view>
</view>
<view
class="tab-item"
:class="{ active: currentTab === 1 }"
@click="currentTab = 1"
>
<text class="tab-text">平台通知</text>
<view class="tab-line" v-if="currentTab === 1"></view>
</view>
<view
class="tab-item"
:class="{ active: currentTab === 2 }"
@click="currentTab = 2"
>
<text class="tab-text">自定义通知</text>
<view class="tab-line" v-if="currentTab === 2"></view>
</view>
</view>
</view>
<!-- 顶部操作 -->
<view class="top-actions">
<button class="btn btn-primary" @click="syncMiniProgram">同步小程序订阅消息</button>
<button class="btn btn-primary ml-10" @click="syncWechat">同步微信模板消息</button>
</view>
<!-- 提示信息 -->
<view class="alert-info">
<text class="alert-title">小程序订阅消息</text>
<text class="alert-desc">登录微信小程序后台,基本设置,服务类目增加《生活服务 > 百货/超市/便利店》(否则同步小程序订阅消息会报错)</text>
<text class="alert-desc">同步小程序订阅消息,是在小程序后台未添加订阅消息模板的前提下使用的,会新增一个模板消息并把信息同步过来,并非本项目数据库。</text>
</view>
<!-- 数据表格 -->
<view class="table-container">
<view class="table-header">
<view class="table-cell" style="flex: 0 0 60px;">ID</view>
<view class="table-cell" style="flex: 2;">通知类型</view>
<view class="table-cell" style="flex: 3;">通知场景说明</view>
<view class="table-cell" style="flex: 1;">站内信</view>
<view class="table-cell" style="flex: 1.5;">公众号模板</view>
<view class="table-cell" style="flex: 1;">发送短信</view>
<view class="table-cell" style="flex: 1.5;">小程序订阅</view>
<view class="table-cell action-cell" style="flex: 1;">操作</view>
</view>
<view class="table-body">
<view class="table-row" v-for="(item, index) in messageList" :key="index">
<view class="table-cell" style="flex: 0 0 60px;">{{ item.id }}</view>
<view class="table-cell" style="flex: 2;">{{ item.type }}</view>
<view class="table-cell" style="flex: 3;">{{ item.desc }}</view>
<view class="table-cell" style="flex: 1;">
<switch color="#1890ff" :checked="item.is_msg" style="transform: scale(0.7);" />
</view>
<view class="table-cell" style="flex: 1.5;">
<switch color="#1890ff" :checked="item.is_wechat" style="transform: scale(0.7);" />
</view>
<view class="table-cell" style="flex: 1;">
<switch color="#1890ff" :checked="item.is_sms" style="transform: scale(0.7);" />
</view>
<view class="table-cell" style="flex: 1.5;">
<switch color="#1890ff" :checked="item.is_mini" style="transform: scale(0.7);" />
</view>
<view class="table-cell action-cell" style="flex: 1;">
<text class="action-link" @click="handleSet(item)">设置</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view></template>
<script setup lang="uts">
import { ref } from 'vue'
const currentTab = ref(0)
const messageList = ref([
{ id: 1, type: '短信验证码', desc: '短信验证码', is_msg: false, is_wechat: false, is_sms: true, is_mini: false },
{ id: 2, type: '绑定推广关系消息', desc: '绑定推广关系消息', is_msg: true, is_wechat: false, is_sms: false, is_mini: false },
{ id: 3, type: '支付成功提醒消息', desc: '支付成功提醒消息', is_msg: true, is_wechat: true, is_sms: true, is_mini: true },
{ id: 4, type: '发货提醒消息', desc: '发货提醒消息', is_msg: true, is_wechat: true, is_sms: true, is_mini: true },
{ id: 5, type: '送货提醒消息', desc: '送货提醒消息', is_msg: true, is_wechat: true, is_sms: false, is_mini: true },
{ id: 6, type: '确认收货提醒消息', desc: '确认收货提醒消息', is_msg: true, is_wechat: true, is_sms: false, is_mini: true },
{ id: 7, type: '退款成功提醒消息', desc: '退款成功提醒消息', is_msg: true, is_wechat: true, is_sms: false, is_mini: true },
{ id: 8, type: '退款失败提醒消息', desc: '退款失败提醒消息', is_msg: true, is_wechat: true, is_sms: false, is_mini: true },
{ id: 9, type: '改价提醒消息', desc: '改价提醒消息', is_msg: true, is_wechat: true, is_sms: false, is_mini: true },
{ id: 10, type: '未付款提醒消息', desc: '未付款提醒消息', is_msg: true, is_wechat: true, is_sms: false, is_mini: true }
])
function syncMiniProgram() {
uni.showToast({ title: '同步小程序订阅消息', icon: 'none' })
}
function syncWechat() {
uni.showToast({ title: '同步微信模板消息', icon: 'none' })
}
function handleSet(item: any) {
uni.showToast({ title: '设置: ' + item.type, icon: 'none' })
}
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
.message-management {
padding: 20px;
background-color: #f5f7f9;
}
.admin-card {
background-color: #fff;
padding: 24px;
border-radius: 8px;
}
.tabs-container {
border-bottom: 1px solid #f0f0f0;
margin-bottom: 20px;
}
.tabs-bar {
display: flex;
flex-direction: row;
}
.tab-item {
padding: 12px 20px;
position: relative;
cursor: pointer;
}
.tab-item.active {
color: #1890ff;
}
.tab-text {
font-size: 14px;
}
.tab-line {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background-color: #1890ff;
}
.top-actions {
margin-bottom: 20px;
}
.btn {
height: 32px;
line-height: 32px;
font-size: 14px;
padding: 0 15px;
border-radius: 4px;
border: none;
}
.btn-primary {
background-color: #1890ff;
color: #fff;
}
.alert-info {
background-color: #fff7e6;
border: 1px solid #ffd591;
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
display: flex;
flex-direction: column;
}
.alert-title {
color: #fa8c16;
font-weight: bold;
font-size: 14px;
margin-bottom: 8px;
}
.alert-desc {
color: #666;
font-size: 13px;
line-height: 1.6;
}
.table-container {
border: 1px solid #f0f0f0;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #f8f8f9;
border-bottom: 1px solid #f0f0f0;
padding: 12px 0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
padding: 12px 0;
align-items: center;
}
.table-cell {
font-size: 13px;
color: #666;
padding: 0 10px;
text-align: center;
}
.action-link {
color: #1890ff;
cursor: pointer;
}
.ml-10 {
margin-left: 10px;
}
</style>

View File

@@ -1,23 +1,256 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">客服设置</text>
</view>
<view class="page-content">
<text class="placeholder-text">客服设置 页面开发中...</text>
</view>
</view>
</view>
</template>
<view class="admin-page ticket-config">
<view class="admin-sections">
<view class="admin-card">
<!-- 搜索和添加 -->
<view class="header-tools">
<view class="search-form">
<view class="form-item">
<text class="label">打印机名称:</text>
<input class="search-input" v-model="searchQuery.name" placeholder="请输入打印机名称" />
</view>
<view class="form-item ml-20">
<text class="label">平台选择:</text>
<picker :range="platforms" @change="onPlatformChange">
<view class="picker-view">{{ platforms[selectedPlatform] }}</view>
</picker>
</view>
<button class="btn-search ml-20" @click="handleSearch">查询</button>
</view>
<button class="btn-primary" @click="handleAdd">添加打印机</button>
</view>
<!-- 数据表格 -->
<view class="table-container">
<view class="table-header">
<view class="table-cell" style="flex: 0 0 60px;">ID</view>
<view class="table-cell" style="flex: 2;">打印机名称</view>
<view class="table-cell" style="flex: 1.5;">平台</view>
<view class="table-cell" style="flex: 1.5;">应用账号</view>
<view class="table-cell" style="flex: 1;">打印联数</view>
<view class="table-cell" style="flex: 2;">创建时间</view>
<view class="table-cell" style="flex: 1;">打印开关</view>
<view class="table-cell action-cell" style="flex: 2;">操作</view>
</view>
<view class="table-body">
<view class="table-row" v-for="(item, index) in printerList" :key="index">
<view class="table-cell" style="flex: 0 0 60px;">{{ item.id }}</view>
<view class="table-cell" style="flex: 2;">{{ item.name }}</view>
<view class="table-cell" style="flex: 1.5;">{{ item.platform }}</view>
<view class="table-cell" style="flex: 1.5;">{{ item.account }}</view>
<view class="table-cell" style="flex: 1;">{{ item.copies }}</view>
<view class="table-cell" style="flex: 2;">{{ item.add_time }}</view>
<view class="table-cell" style="flex: 1;">
<switch color="#1890ff" :checked="item.status" style="transform: scale(0.7);" />
</view>
<view class="table-cell action-cell" style="flex: 2;">
<text class="action-link" @click="handleDesign(item)">设计</text>
<text class="action-link ml-10" @click="handleEdit(item)">编辑</text>
<text class="action-link link-danger ml-10" @click="handleDelete(item)">删除</text>
</view>
</view>
</view>
</view>
<!-- 分页 (模拟) -->
<view class="pagination">
<text class="total">共 1 条</text>
<picker :range="['15条/页', '30条/页']">
<view class="page-size">15条/页</view>
</picker>
</view>
</view>
</view>
</view></template>
<script setup lang="uts">
import { ref } from 'vue'
const searchQuery = ref({ name: '' })
const platforms = ['全部', '易联云', '飞鹅打印机']
const selectedPlatform = ref(0)
const printerList = ref([
{ id: 1, name: '一号打印', platform: '易联云', account: '222', copies: 1, add_time: '2024-12-11 15:12:58', status: true }
])
function onPlatformChange(e: any) {
selectedPlatform.value = e.detail.value
}
function handleSearch() {
uni.showToast({ title: '查询中...', icon: 'none' })
}
function handleAdd() {
uni.showToast({ title: '添加打印机', icon: 'none' })
}
function handleDesign(item: any) {
uni.showToast({ title: '打印设计: ' + item.name, icon: 'none' })
}
function handleEdit(item: any) {
uni.showToast({ title: '编辑打印机: ' + item.name, icon: 'none' })
}
function handleDelete(item: any) {
uni.showModal({
title: '提示',
content: '确定删除该打印机吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '已删除', icon: 'success' })
}
}
})
}
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
.ticket-config {
padding: 20px;
background-color: #f5f7f9;
}
.admin-card {
background-color: #fff;
padding: 24px;
border-radius: 8px;
}
.header-tools {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.search-form {
display: flex;
flex-direction: row;
align-items: center;
}
.form-item {
display: flex;
flex-direction: row;
align-items: center;
}
.label {
font-size: 14px;
color: #333;
}
.search-input {
width: 180px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 0 12px;
font-size: 14px;
}
.picker-view {
width: 120px;
height: 32px;
line-height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 0 12px;
font-size: 14px;
background-color: #fff;
}
.btn-search {
background-color: #1890ff;
color: #fff;
height: 32px;
line-height: 32px;
padding: 0 15px;
font-size: 14px;
border: none;
border-radius: 4px;
}
.btn-primary {
background-color: #1890ff;
color: #fff;
height: 32px;
line-height: 32px;
padding: 0 15px;
font-size: 14px;
border: none;
border-radius: 4px;
}
.table-container {
border: 1px solid #f0f0f0;
border-radius: 4px;
}
.table-header {
display: flex;
flex-direction: row;
background-color: #f8f8f9;
border-bottom: 1px solid #f0f0f0;
padding: 12px 0;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
padding: 12px 0;
align-items: center;
}
.table-cell {
font-size: 13px;
color: #666;
padding: 0 10px;
text-align: center;
}
.action-link {
color: #1890ff;
cursor: pointer;
font-size: 13px;
}
.link-danger {
color: #ff4d4f;
}
.pagination {
margin-top: 20px;
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
gap: 15px;
}
.total {
font-size: 13px;
color: #666;
}
.page-size {
font-size: 13px;
color: #666;
border: 1px solid #d9d9d9;
padding: 2px 10px;
border-radius: 4px;
}
.ml-20 {
margin-left: 20px;
}
.ml-10 {
margin-left: 10px;
}
</style>

View File

@@ -0,0 +1,641 @@
adminComponentMap.uts:194 GET http://localhost:5173/pages/mall/admin/maintain/dev-tools/file.uvue?t=1770810007330&import net::ERR_ABORTED 500 (Internal Server Error)
(anonymous) @ adminComponentMap.uts:194
load @ vue.runtime.esm.js:3681
setup @ vue.runtime.esm.js:3760
callWithErrorHandling @ vue.runtime.esm.js:1381
setupStatefulComponent @ vue.runtime.esm.js:8985
setupComponent @ vue.runtime.esm.js:8946
mountComponent @ vue.runtime.esm.js:7262
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
patchKeyedChildren @ vue.runtime.esm.js:7783
patchChildren @ vue.runtime.esm.js:7564
processFragment @ vue.runtime.esm.js:7202
patch @ vue.runtime.esm.js:6668
patchKeyedChildren @ vue.runtime.esm.js:7650
patchChildren @ vue.runtime.esm.js:7564
patchElement @ vue.runtime.esm.js:6989
processElement @ vue.runtime.esm.js:6825
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7453
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
(anonymous) @ vue.runtime.esm.js:7491
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
(anonymous) @ AdminLayout.uvue:263
setTimeout
watch.immediate @ AdminLayout.uvue:262
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
job @ vue.runtime.esm.js:3157
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
scheduler @ vue.runtime.esm.js:3179
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
openRoute @ adminNavStore.uts:87
onSubClick @ AdminLayout.uvue:318
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
emit @ vue.runtime.esm.js:1907
(anonymous) @ vue.runtime.esm.js:9176
handleNodeClick @ AdminSubSider.uvue:116
onClick @ AdminSubSider.uvue?import:112
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
invoker @ vue.runtime.esm.js:10253
AdminLayout.uvue:263 [Vue warn]: Unhandled error during execution of async component loader
at <AsyncComponentWrapper>
at <View>
at <View>
at <View>
at <View>
at <AdminLayout>
at <View>
at <Index>
at <AsyncComponentWrapper>
at <PageBody>
at <Page>
at <Anonymous>
at <KeepAlive>
at <RouterView>
at <Layout>
at <App>
warnHandler @ uni-h5.es.js:19975
callWithErrorHandling @ vue.runtime.esm.js:1381
warn$1 @ vue.runtime.esm.js:1207
logError @ vue.runtime.esm.js:1438
errorHandler @ uni-h5.es.js:19600
callWithErrorHandling @ vue.runtime.esm.js:1381
handleError @ vue.runtime.esm.js:1421
onError @ vue.runtime.esm.js:3724
(anonymous) @ vue.runtime.esm.js:3767
Promise.catch
setup @ vue.runtime.esm.js:3766
callWithErrorHandling @ vue.runtime.esm.js:1381
setupStatefulComponent @ vue.runtime.esm.js:8985
setupComponent @ vue.runtime.esm.js:8946
mountComponent @ vue.runtime.esm.js:7262
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
patchKeyedChildren @ vue.runtime.esm.js:7783
patchChildren @ vue.runtime.esm.js:7564
processFragment @ vue.runtime.esm.js:7202
patch @ vue.runtime.esm.js:6668
patchKeyedChildren @ vue.runtime.esm.js:7650
patchChildren @ vue.runtime.esm.js:7564
patchElement @ vue.runtime.esm.js:6989
processElement @ vue.runtime.esm.js:6825
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7453
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
(anonymous) @ vue.runtime.esm.js:7491
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
(anonymous) @ AdminLayout.uvue:263
setTimeout
watch.immediate @ AdminLayout.uvue:262
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
job @ vue.runtime.esm.js:3157
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
scheduler @ vue.runtime.esm.js:3179
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
openRoute @ adminNavStore.uts:87
onSubClick @ AdminLayout.uvue:318
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
emit @ vue.runtime.esm.js:1907
(anonymous) @ vue.runtime.esm.js:9176
handleNodeClick @ AdminSubSider.uvue:116
onClick @ AdminSubSider.uvue?import:112
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
invoker @ vue.runtime.esm.js:10253
AdminLayout.uvue:263 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/maintain/dev-tools/file.uvue?t=1770810007330&import
logError @ vue.runtime.esm.js:1443
errorHandler @ uni-h5.es.js:19600
callWithErrorHandling @ vue.runtime.esm.js:1381
handleError @ vue.runtime.esm.js:1421
onError @ vue.runtime.esm.js:3724
(anonymous) @ vue.runtime.esm.js:3767
Promise.catch
setup @ vue.runtime.esm.js:3766
callWithErrorHandling @ vue.runtime.esm.js:1381
setupStatefulComponent @ vue.runtime.esm.js:8985
setupComponent @ vue.runtime.esm.js:8946
mountComponent @ vue.runtime.esm.js:7262
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
patchKeyedChildren @ vue.runtime.esm.js:7783
patchChildren @ vue.runtime.esm.js:7564
processFragment @ vue.runtime.esm.js:7202
patch @ vue.runtime.esm.js:6668
patchKeyedChildren @ vue.runtime.esm.js:7650
patchChildren @ vue.runtime.esm.js:7564
patchElement @ vue.runtime.esm.js:6989
processElement @ vue.runtime.esm.js:6825
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7453
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
(anonymous) @ vue.runtime.esm.js:7491
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
(anonymous) @ AdminLayout.uvue:263
setTimeout
watch.immediate @ AdminLayout.uvue:262
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
job @ vue.runtime.esm.js:3157
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
scheduler @ vue.runtime.esm.js:3179
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
openRoute @ adminNavStore.uts:87
onSubClick @ AdminLayout.uvue:318
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
emit @ vue.runtime.esm.js:1907
(anonymous) @ vue.runtime.esm.js:9176
handleNodeClick @ AdminSubSider.uvue:116
onClick @ AdminSubSider.uvue?import:112
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
invoker @ vue.runtime.esm.js:10253
adminComponentMap.uts:195 GET http://localhost:5173/pages/mall/admin/maintain/dev-tools/api.uvue?t=1770809996535&import net::ERR_ABORTED 500 (Internal Server Error)
(anonymous) @ adminComponentMap.uts:195
load @ vue.runtime.esm.js:3681
setup @ vue.runtime.esm.js:3760
callWithErrorHandling @ vue.runtime.esm.js:1381
setupStatefulComponent @ vue.runtime.esm.js:8985
setupComponent @ vue.runtime.esm.js:8946
mountComponent @ vue.runtime.esm.js:7262
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
patchKeyedChildren @ vue.runtime.esm.js:7783
patchChildren @ vue.runtime.esm.js:7564
processFragment @ vue.runtime.esm.js:7202
patch @ vue.runtime.esm.js:6668
patchKeyedChildren @ vue.runtime.esm.js:7650
patchChildren @ vue.runtime.esm.js:7564
patchElement @ vue.runtime.esm.js:6989
processElement @ vue.runtime.esm.js:6825
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7453
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
(anonymous) @ vue.runtime.esm.js:7491
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
(anonymous) @ AdminLayout.uvue:263
setTimeout
watch.immediate @ AdminLayout.uvue:262
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
job @ vue.runtime.esm.js:3157
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
scheduler @ vue.runtime.esm.js:3179
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
openRoute @ adminNavStore.uts:87
onSubClick @ AdminLayout.uvue:318
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
emit @ vue.runtime.esm.js:1907
(anonymous) @ vue.runtime.esm.js:9176
handleNodeClick @ AdminSubSider.uvue:116
onClick @ AdminSubSider.uvue?import:112
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
invoker @ vue.runtime.esm.js:10253
AdminLayout.uvue:263 [Vue warn]: Unhandled error during execution of async component loader
at <AsyncComponentWrapper>
at <View>
at <View>
at <View>
at <View>
at <AdminLayout>
at <View>
at <Index>
at <AsyncComponentWrapper>
at <PageBody>
at <Page>
at <Anonymous>
at <KeepAlive>
at <RouterView>
at <Layout>
at <App>
warnHandler @ uni-h5.es.js:19975
callWithErrorHandling @ vue.runtime.esm.js:1381
warn$1 @ vue.runtime.esm.js:1207
logError @ vue.runtime.esm.js:1438
errorHandler @ uni-h5.es.js:19600
callWithErrorHandling @ vue.runtime.esm.js:1381
handleError @ vue.runtime.esm.js:1421
onError @ vue.runtime.esm.js:3724
(anonymous) @ vue.runtime.esm.js:3767
Promise.catch
setup @ vue.runtime.esm.js:3766
callWithErrorHandling @ vue.runtime.esm.js:1381
setupStatefulComponent @ vue.runtime.esm.js:8985
setupComponent @ vue.runtime.esm.js:8946
mountComponent @ vue.runtime.esm.js:7262
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
patchKeyedChildren @ vue.runtime.esm.js:7783
patchChildren @ vue.runtime.esm.js:7564
processFragment @ vue.runtime.esm.js:7202
patch @ vue.runtime.esm.js:6668
patchKeyedChildren @ vue.runtime.esm.js:7650
patchChildren @ vue.runtime.esm.js:7564
patchElement @ vue.runtime.esm.js:6989
processElement @ vue.runtime.esm.js:6825
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7453
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
(anonymous) @ vue.runtime.esm.js:7491
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
(anonymous) @ AdminLayout.uvue:263
setTimeout
watch.immediate @ AdminLayout.uvue:262
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
job @ vue.runtime.esm.js:3157
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
scheduler @ vue.runtime.esm.js:3179
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
openRoute @ adminNavStore.uts:87
onSubClick @ AdminLayout.uvue:318
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
emit @ vue.runtime.esm.js:1907
(anonymous) @ vue.runtime.esm.js:9176
handleNodeClick @ AdminSubSider.uvue:116
onClick @ AdminSubSider.uvue?import:112
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
invoker @ vue.runtime.esm.js:10253
AdminLayout.uvue:263 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/maintain/dev-tools/api.uvue?t=1770809996535&import
logError @ vue.runtime.esm.js:1443
errorHandler @ uni-h5.es.js:19600
callWithErrorHandling @ vue.runtime.esm.js:1381
handleError @ vue.runtime.esm.js:1421
onError @ vue.runtime.esm.js:3724
(anonymous) @ vue.runtime.esm.js:3767
Promise.catch
setup @ vue.runtime.esm.js:3766
callWithErrorHandling @ vue.runtime.esm.js:1381
setupStatefulComponent @ vue.runtime.esm.js:8985
setupComponent @ vue.runtime.esm.js:8946
mountComponent @ vue.runtime.esm.js:7262
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
patchKeyedChildren @ vue.runtime.esm.js:7783
patchChildren @ vue.runtime.esm.js:7564
processFragment @ vue.runtime.esm.js:7202
patch @ vue.runtime.esm.js:6668
patchKeyedChildren @ vue.runtime.esm.js:7650
patchChildren @ vue.runtime.esm.js:7564
patchElement @ vue.runtime.esm.js:6989
processElement @ vue.runtime.esm.js:6825
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7453
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
(anonymous) @ vue.runtime.esm.js:7491
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
(anonymous) @ AdminLayout.uvue:263
setTimeout
watch.immediate @ AdminLayout.uvue:262
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
job @ vue.runtime.esm.js:3157
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
scheduler @ vue.runtime.esm.js:3179
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
openRoute @ adminNavStore.uts:87
onSubClick @ AdminLayout.uvue:318
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
emit @ vue.runtime.esm.js:1907
(anonymous) @ vue.runtime.esm.js:9176
handleNodeClick @ AdminSubSider.uvue:116
onClick @ AdminSubSider.uvue?import:112
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
invoker @ vue.runtime.esm.js:10253
adminComponentMap.uts:196 GET http://localhost:5173/pages/mall/admin/maintain/dev-tools/data-dict.uvue?t=1770810020653&import net::ERR_ABORTED 500 (Internal Server Error)
(anonymous) @ adminComponentMap.uts:196
load @ vue.runtime.esm.js:3681
setup @ vue.runtime.esm.js:3760
callWithErrorHandling @ vue.runtime.esm.js:1381
setupStatefulComponent @ vue.runtime.esm.js:8985
setupComponent @ vue.runtime.esm.js:8946
mountComponent @ vue.runtime.esm.js:7262
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
patchKeyedChildren @ vue.runtime.esm.js:7783
patchChildren @ vue.runtime.esm.js:7564
processFragment @ vue.runtime.esm.js:7202
patch @ vue.runtime.esm.js:6668
patchKeyedChildren @ vue.runtime.esm.js:7650
patchChildren @ vue.runtime.esm.js:7564
patchElement @ vue.runtime.esm.js:6989
processElement @ vue.runtime.esm.js:6825
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7453
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
(anonymous) @ vue.runtime.esm.js:7491
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
(anonymous) @ AdminLayout.uvue:263
setTimeout
watch.immediate @ AdminLayout.uvue:262
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
job @ vue.runtime.esm.js:3157
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
scheduler @ vue.runtime.esm.js:3179
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
openRoute @ adminNavStore.uts:87
onSubClick @ AdminLayout.uvue:318
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
emit @ vue.runtime.esm.js:1907
(anonymous) @ vue.runtime.esm.js:9176
handleNodeClick @ AdminSubSider.uvue:116
onClick @ AdminSubSider.uvue?import:112
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
invoker @ vue.runtime.esm.js:10253
AdminLayout.uvue:263 [Vue warn]: Unhandled error during execution of async component loader
at <AsyncComponentWrapper>
at <View>
at <View>
at <View>
at <View>
at <AdminLayout>
at <View>
at <Index>
at <AsyncComponentWrapper>
at <PageBody>
at <Page>
at <Anonymous>
at <KeepAlive>
at <RouterView>
at <Layout>
at <App>
warnHandler @ uni-h5.es.js:19975
callWithErrorHandling @ vue.runtime.esm.js:1381
warn$1 @ vue.runtime.esm.js:1207
logError @ vue.runtime.esm.js:1438
errorHandler @ uni-h5.es.js:19600
callWithErrorHandling @ vue.runtime.esm.js:1381
handleError @ vue.runtime.esm.js:1421
onError @ vue.runtime.esm.js:3724
(anonymous) @ vue.runtime.esm.js:3767
Promise.catch
setup @ vue.runtime.esm.js:3766
callWithErrorHandling @ vue.runtime.esm.js:1381
setupStatefulComponent @ vue.runtime.esm.js:8985
setupComponent @ vue.runtime.esm.js:8946
mountComponent @ vue.runtime.esm.js:7262
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
patchKeyedChildren @ vue.runtime.esm.js:7783
patchChildren @ vue.runtime.esm.js:7564
processFragment @ vue.runtime.esm.js:7202
patch @ vue.runtime.esm.js:6668
patchKeyedChildren @ vue.runtime.esm.js:7650
patchChildren @ vue.runtime.esm.js:7564
patchElement @ vue.runtime.esm.js:6989
processElement @ vue.runtime.esm.js:6825
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7453
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
(anonymous) @ vue.runtime.esm.js:7491
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
(anonymous) @ AdminLayout.uvue:263
setTimeout
watch.immediate @ AdminLayout.uvue:262
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
job @ vue.runtime.esm.js:3157
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
scheduler @ vue.runtime.esm.js:3179
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
openRoute @ adminNavStore.uts:87
onSubClick @ AdminLayout.uvue:318
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
emit @ vue.runtime.esm.js:1907
(anonymous) @ vue.runtime.esm.js:9176
handleNodeClick @ AdminSubSider.uvue:116
onClick @ AdminSubSider.uvue?import:112
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
invoker @ vue.runtime.esm.js:10253
AdminLayout.uvue:263 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/maintain/dev-tools/data-dict.uvue?t=1770810020653&import
logError @ vue.runtime.esm.js:1443
errorHandler @ uni-h5.es.js:19600
callWithErrorHandling @ vue.runtime.esm.js:1381
handleError @ vue.runtime.esm.js:1421
onError @ vue.runtime.esm.js:3724
(anonymous) @ vue.runtime.esm.js:3767
Promise.catch
setup @ vue.runtime.esm.js:3766
callWithErrorHandling @ vue.runtime.esm.js:1381
setupStatefulComponent @ vue.runtime.esm.js:8985
setupComponent @ vue.runtime.esm.js:8946
mountComponent @ vue.runtime.esm.js:7262
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
patchKeyedChildren @ vue.runtime.esm.js:7783
patchChildren @ vue.runtime.esm.js:7564
processFragment @ vue.runtime.esm.js:7202
patch @ vue.runtime.esm.js:6668
patchKeyedChildren @ vue.runtime.esm.js:7650
patchChildren @ vue.runtime.esm.js:7564
patchElement @ vue.runtime.esm.js:6989
processElement @ vue.runtime.esm.js:6825
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7453
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
(anonymous) @ vue.runtime.esm.js:7491
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
(anonymous) @ AdminLayout.uvue:263
setTimeout
watch.immediate @ AdminLayout.uvue:262
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
job @ vue.runtime.esm.js:3157
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
scheduler @ vue.runtime.esm.js:3179
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
openRoute @ adminNavStore.uts:87
onSubClick @ AdminLayout.uvue:318
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
emit @ vue.runtime.esm.js:1907
(anonymous) @ vue.runtime.esm.js:9176
handleNodeClick @ AdminSubSider.uvue:116
onClick @ AdminSubSider.uvue?import:112
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
invoker @ vue.runtime.esm.js:10253
pages-json-js?t=1770810049956:278 GET http://localhost:5173/pages/mall/admin/setting/message.uvue?t=1770810030507&import net::ERR_ABORTED 500 (Internal Server Error)
PagesMallAdminSettingMessageLoader @ pages-json-js?t=1770810049956:278
(anonymous) @ uni-h5.es.js:24748
(anonymous) @ uni-h5.es.js:4125
invokeApi @ uni-h5.es.js:3971
(anonymous) @ uni-h5.es.js:3989
(anonymous) @ uni-h5.es.js:24765
(anonymous) @ uni-h5.es.js:24764
Promise.then
(anonymous) @ uni-h5.es.js:24763
(anonymous) @ uni-h5.es.js:24762
pages-json-js?t=1770810049956:280 GET http://localhost:5173/pages/mall/admin/setting/agreement.uvue?t=1770810040122&import net::ERR_ABORTED 500 (Internal Server Error)
PagesMallAdminSettingAgreementLoader @ pages-json-js?t=1770810049956:280
(anonymous) @ uni-h5.es.js:24748
(anonymous) @ uni-h5.es.js:4125
invokeApi @ uni-h5.es.js:3971
(anonymous) @ uni-h5.es.js:3989
(anonymous) @ uni-h5.es.js:24765
(anonymous) @ uni-h5.es.js:24764
Promise.then
(anonymous) @ uni-h5.es.js:24763
(anonymous) @ uni-h5.es.js:24762
pages-json-js?t=1770810049956:282 GET http://localhost:5173/pages/mall/admin/setting/ticket.uvue?t=1770810049956&import net::ERR_ABORTED 500 (Internal Server Error).