完善页面

This commit is contained in:
2026-01-29 17:54:35 +08:00
parent 9f3c2803e3
commit fcc976680d
95 changed files with 4595 additions and 10542 deletions

View File

@@ -1,63 +0,0 @@
<template>
<view class="activity-log">
<view class="page-header">
<text class="page-title">活动日志</text>
<text class="page-subtitle">查看系统活动和操作日志</text>
</view>
<view class="log-content">
<text class="coming-soon">活动日志功能正在开发中...</text>
</view>
</view>
</template>
<script setup lang="uts">
// 统一的导航方法
const go = (url: string) => {
// 1) 目标页面必须是非 tabBar 页面
// 2) 必须在 pages.json / subPackages 注册
uni.navigateTo({ url })
}
</script>
<style lang="scss">
.activity-log {
padding: 30rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.page-header {
background-color: #fff;
padding: 40rpx;
border-radius: 16rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #212529;
display: block;
margin-bottom: 10rpx;
}
.page-subtitle {
font-size: 26rpx;
color: #6c757d;
}
}
.log-content {
background-color: #fff;
padding: 60rpx 40rpx;
border-radius: 16rpx;
text-align: center;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.coming-soon {
font-size: 28rpx;
color: #6c757d;
}
}
</style>

View File

@@ -1,63 +0,0 @@
<template>
<view class="complaints">
<view class="page-header">
<text class="page-title">投诉处理</text>
<text class="page-subtitle">处理用户投诉和反馈</text>
</view>
<view class="complaints-content">
<text class="coming-soon">投诉处理功能正在开发中...</text>
</view>
</view>
</template>
<script setup lang="uts">
// 统一的导航方法
const go = (url: string) => {
// 1) 目标页面必须是非 tabBar 页面
// 2) 必须在 pages.json / subPackages 注册
uni.navigateTo({ url })
}
</script>
<style lang="scss">
.complaints {
padding: 30rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.page-header {
background-color: #fff;
padding: 40rpx;
border-radius: 16rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #212529;
display: block;
margin-bottom: 10rpx;
}
.page-subtitle {
font-size: 26rpx;
color: #6c757d;
}
}
.complaints-content {
background-color: #fff;
padding: 60rpx 40rpx;
border-radius: 16rpx;
text-align: center;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.coming-soon {
font-size: 28rpx;
color: #6c757d;
}
}
</style>

View File

@@ -0,0 +1,62 @@
<template>
<view class="Page">
<view class="Header">
<text class="Title">文章管理</text>
<text class="SubTitle">content/index</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -0,0 +1,95 @@
<template>
<view class="page">
<view class="topbar">
<view class="topbar-left">
<text class="title">自动回复</text>
<text class="subtitle">customer-service/auto-reply</text>
</view>
<view class="topbar-right">
<view class="btn" @click="onBack"><text class="btn-text">返回</text></view>
</view>
</view>
<view class="container">
<view class="card">
<text class="h1">自动回复</text>
<text class="p">这是页面骨架(可跑)。你可以在这里接入你们项目的 TopBar / Container 组件与业务逻辑。</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
const onBack = () => {
// H5/小程序均可用
uni.navigateBack()
}
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f6f7fb;
}
.topbar {
position: sticky;
top: 0;
z-index: 10;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
background: #ffffff;
border-bottom: 1px solid #eef0f6;
}
.title {
font-size: 18px;
font-weight: 700;
color: #111827;
}
.subtitle {
margin-top: 2px;
font-size: 12px;
color: #6b7280;
}
.topbar-left {
display: flex;
flex-direction: column;
}
.topbar-right {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
}
.btn {
padding: 8px 10px;
border-radius: 10px;
background: #111827;
}
.btn-text {
font-size: 12px;
color: #ffffff;
}
.container {
padding: 16px;
}
.card {
padding: 16px;
border-radius: 14px;
background: #ffffff;
border: 1px solid #eef0f6;
}
.h1 {
font-size: 16px;
font-weight: 700;
color: #111827;
margin-bottom: 8px;
}
.p {
font-size: 13px;
color: #374151;
line-height: 20px;
}
</style>

View File

@@ -0,0 +1,95 @@
<template>
<view class="page">
<view class="topbar">
<view class="topbar-left">
<text class="title">客服设置</text>
<text class="subtitle">customer-service/config</text>
</view>
<view class="topbar-right">
<view class="btn" @click="onBack"><text class="btn-text">返回</text></view>
</view>
</view>
<view class="container">
<view class="card">
<text class="h1">客服设置</text>
<text class="p">这是页面骨架(可跑)。你可以在这里接入你们项目的 TopBar / Container 组件与业务逻辑。</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
const onBack = () => {
// H5/小程序均可用
uni.navigateBack()
}
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f6f7fb;
}
.topbar {
position: sticky;
top: 0;
z-index: 10;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
background: #ffffff;
border-bottom: 1px solid #eef0f6;
}
.title {
font-size: 18px;
font-weight: 700;
color: #111827;
}
.subtitle {
margin-top: 2px;
font-size: 12px;
color: #6b7280;
}
.topbar-left {
display: flex;
flex-direction: column;
}
.topbar-right {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
}
.btn {
padding: 8px 10px;
border-radius: 10px;
background: #111827;
}
.btn-text {
font-size: 12px;
color: #ffffff;
}
.container {
padding: 16px;
}
.card {
padding: 16px;
border-radius: 14px;
background: #ffffff;
border: 1px solid #eef0f6;
}
.h1 {
font-size: 16px;
font-weight: 700;
color: #111827;
margin-bottom: 8px;
}
.p {
font-size: 13px;
color: #374151;
line-height: 20px;
}
</style>

View File

@@ -0,0 +1,95 @@
<template>
<view class="page">
<view class="topbar">
<view class="topbar-left">
<text class="title">客服列表</text>
<text class="subtitle">customer-service/list</text>
</view>
<view class="topbar-right">
<view class="btn" @click="onBack"><text class="btn-text">返回</text></view>
</view>
</view>
<view class="container">
<view class="card">
<text class="h1">客服列表</text>
<text class="p">这是页面骨架(可跑)。你可以在这里接入你们项目的 TopBar / Container 组件与业务逻辑。</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
const onBack = () => {
// H5/小程序均可用
uni.navigateBack()
}
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f6f7fb;
}
.topbar {
position: sticky;
top: 0;
z-index: 10;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
background: #ffffff;
border-bottom: 1px solid #eef0f6;
}
.title {
font-size: 18px;
font-weight: 700;
color: #111827;
}
.subtitle {
margin-top: 2px;
font-size: 12px;
color: #6b7280;
}
.topbar-left {
display: flex;
flex-direction: column;
}
.topbar-right {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
}
.btn {
padding: 8px 10px;
border-radius: 10px;
background: #111827;
}
.btn-text {
font-size: 12px;
color: #ffffff;
}
.container {
padding: 16px;
}
.card {
padding: 16px;
border-radius: 14px;
background: #ffffff;
border: 1px solid #eef0f6;
}
.h1 {
font-size: 16px;
font-weight: 700;
color: #111827;
margin-bottom: 8px;
}
.p {
font-size: 13px;
color: #374151;
line-height: 20px;
}
</style>

View File

@@ -0,0 +1,95 @@
<template>
<view class="page">
<view class="topbar">
<view class="topbar-left">
<text class="title">客服消息</text>
<text class="subtitle">customer-service/messages</text>
</view>
<view class="topbar-right">
<view class="btn" @click="onBack"><text class="btn-text">返回</text></view>
</view>
</view>
<view class="container">
<view class="card">
<text class="h1">客服消息</text>
<text class="p">这是页面骨架(可跑)。你可以在这里接入你们项目的 TopBar / Container 组件与业务逻辑。</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
const onBack = () => {
// H5/小程序均可用
uni.navigateBack()
}
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f6f7fb;
}
.topbar {
position: sticky;
top: 0;
z-index: 10;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
background: #ffffff;
border-bottom: 1px solid #eef0f6;
}
.title {
font-size: 18px;
font-weight: 700;
color: #111827;
}
.subtitle {
margin-top: 2px;
font-size: 12px;
color: #6b7280;
}
.topbar-left {
display: flex;
flex-direction: column;
}
.topbar-right {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
}
.btn {
padding: 8px 10px;
border-radius: 10px;
background: #111827;
}
.btn-text {
font-size: 12px;
color: #ffffff;
}
.container {
padding: 16px;
}
.card {
padding: 16px;
border-radius: 14px;
background: #ffffff;
border: 1px solid #eef0f6;
}
.h1 {
font-size: 16px;
font-weight: 700;
color: #111827;
margin-bottom: 8px;
}
.p {
font-size: 13px;
color: #374151;
line-height: 20px;
}
</style>

View File

@@ -0,0 +1,95 @@
<template>
<view class="page">
<view class="topbar">
<view class="topbar-left">
<text class="title">快捷回复话术</text>
<text class="subtitle">customer-service/script</text>
</view>
<view class="topbar-right">
<view class="btn" @click="onBack"><text class="btn-text">返回</text></view>
</view>
</view>
<view class="container">
<view class="card">
<text class="h1">快捷回复话术</text>
<text class="p">这是页面骨架(可跑)。你可以在这里接入你们项目的 TopBar / Container 组件与业务逻辑。</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
const onBack = () => {
// H5/小程序均可用
uni.navigateBack()
}
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f6f7fb;
}
.topbar {
position: sticky;
top: 0;
z-index: 10;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
background: #ffffff;
border-bottom: 1px solid #eef0f6;
}
.title {
font-size: 18px;
font-weight: 700;
color: #111827;
}
.subtitle {
margin-top: 2px;
font-size: 12px;
color: #6b7280;
}
.topbar-left {
display: flex;
flex-direction: column;
}
.topbar-right {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
}
.btn {
padding: 8px 10px;
border-radius: 10px;
background: #111827;
}
.btn-text {
font-size: 12px;
color: #ffffff;
}
.container {
padding: 16px;
}
.card {
padding: 16px;
border-radius: 14px;
background: #ffffff;
border: 1px solid #eef0f6;
}
.h1 {
font-size: 16px;
font-weight: 700;
color: #111827;
margin-bottom: 8px;
}
.p {
font-size: 13px;
color: #374151;
line-height: 20px;
}
</style>

View File

@@ -1,11 +0,0 @@
<template>
<view class="page">
<text>配送管理 - 占位页</text>
</view>
</template>
<script lang="uts">
export default {}
</script>
<style>
.page { padding: 30rpx; }
</style>

View File

@@ -0,0 +1,62 @@
<template>
<view class="Page">
<view class="Header">
<text class="Title">页面装修</text>
<text class="SubTitle">design/index</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">城市数据</text>
</view>
<view class="content">
<text class="tip">TODO: 城市数据</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">清除数据</text>
</view>
<view class="content">
<text class="tip">TODO: 清除数据</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">物流公司</text>
</view>
<view class="content">
<text class="tip">TODO: 物流公司</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">配置分类</text>
</view>
<view class="content">
<text class="tip">TODO: 配置分类</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">组合数据</text>
</view>
<view class="content">
<text class="tip">TODO: 组合数据</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">定时任务</text>
</view>
<view class="content">
<text class="tip">TODO: 定时任务</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">自定事件</text>
</view>
<view class="content">
<text class="tip">TODO: 自定事件</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">模块配置</text>
</view>
<view class="content">
<text class="tip">TODO: 模块配置</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">权限维护</text>
</view>
<view class="content">
<text class="tip">TODO: 权限维护</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">接口管理</text>
</view>
<view class="content">
<text class="tip">TODO: 接口管理</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">代码生成</text>
</view>
<view class="content">
<text class="tip">TODO: 代码生成</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">数据字典</text>
</view>
<view class="content">
<text class="tip">TODO: 数据字典</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">数据库管理</text>
</view>
<view class="content">
<text class="tip">TODO: 数据库管理</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">文件管理</text>
</view>
<view class="content">
<text class="tip">TODO: 文件管理</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">账号管理</text>
</view>
<view class="content">
<text class="tip">TODO: 账号管理</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">语言详情</text>
</view>
<view class="content">
<text class="tip">TODO: 语言详情</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">语言列表</text>
</view>
<view class="content">
<text class="tip">TODO: 语言列表</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">地区列表</text>
</view>
<view class="content">
<text class="tip">TODO: 地区列表</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">翻译配置</text>
</view>
<view class="content">
<text class="tip">TODO: 翻译配置</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">在线升级</text>
</view>
<view class="content">
<text class="tip">TODO: 在线升级</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">刷新缓存</text>
</view>
<view class="content">
<text class="tip">TODO: 刷新缓存</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">系统日志</text>
</view>
<view class="content">
<text class="tip">TODO: 系统日志</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,13 @@
<template>
<view class="page">
<view class="header">
<text class="title">系统信息</text>
</view>
<view class="content">
<text class="tip">TODO: 系统信息</text>
</view>
</view>
</template>
<script setup lang="uts"> </script> <style scoped> .page { padding: 16px; } .title { font-size: 18px; font-weight: 600; } .tip { color: #999; margin-top: 8px; display: block; } </style>

View File

@@ -0,0 +1,62 @@
<template>
<view class="Page">
<view class="Header">
<text class="Title">营销管理</text>
<text class="SubTitle">marketing-management</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -1,11 +0,0 @@
<template>
<view class="page">
<text>优惠券管理 - 占位页</text>
</view>
</template>
<script lang="uts">
export default {}
</script>
<style>
.page { padding: 30rpx; }
</style>

View File

@@ -1,28 +1,62 @@
<template>
<view class="container">
<text class="title">优惠券列表</text>
<view class="Page">
<view class="Header">
<text class="Title">优惠券列表</text>
<text class="SubTitle">marketing/coupon/list</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
// Minimal script
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.container {
padding: 40rpx;
text-align: center;
.Page {
padding: 24rpx;
}
.title {
font-size: 48rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.content {
font-size: 28rpx;
color: #666;
.Title {
font-size: 36rpx;
font-weight: 700;
}
</style>
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -1,28 +1,62 @@
<template>
<view class="container">
<text class="title">用户领取记录</text>
<view class="Page">
<view class="Header">
<text class="Title">领取情况</text>
<text class="SubTitle">marketing/coupon/receive</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
// Minimal script
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.container {
padding: 40rpx;
text-align: center;
.Page {
padding: 24rpx;
}
.title {
font-size: 48rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.content {
font-size: 28rpx;
color: #666;
.Title {
font-size: 36rpx;
font-weight: 700;
}
</style>
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -1,28 +1,62 @@
<template>
<view class="container">
<text class="title">积分管理</text>
<view class="Page">
<view class="Header">
<text class="Title">积分统计</text>
<text class="SubTitle">marketing/points/index</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
// Minimal script
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.container {
padding: 40rpx;
text-align: center;
.Page {
padding: 24rpx;
}
.title {
font-size: 48rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.content {
font-size: 28rpx;
color: #666;
.Title {
font-size: 36rpx;
font-weight: 700;
}
</style>
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -1,28 +1,62 @@
<template>
<view class="container">
<text class="title">签到记录</text>
<view class="Page">
<view class="Header">
<text class="Title">签到奖励</text>
<text class="SubTitle">marketing/signin/record</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
// Minimal script
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.container {
padding: 40rpx;
text-align: center;
.Page {
padding: 24rpx;
}
.title {
font-size: 48rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.content {
font-size: 28rpx;
color: #666;
.Title {
font-size: 36rpx;
font-weight: 700;
}
</style>
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -1,28 +1,62 @@
<template>
<view class="container">
<text class="title">签到规则</text>
<view class="Page">
<view class="Header">
<text class="Title">签到配置</text>
<text class="SubTitle">marketing/signin/rule</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
// Minimal script
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.container {
padding: 40rpx;
text-align: center;
.Page {
padding: 24rpx;
}
.title {
font-size: 48rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.content {
font-size: 28rpx;
color: #666;
.Title {
font-size: 36rpx;
font-weight: 700;
}
</style>
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -1,13 +0,0 @@
<template>
<view class="page">
<text>商家管理 - 占位页</text>
</view>
</template>
<script lang="uts">
export default {}
</script>
<style>
.page { padding: 30rpx; }
</style>

View File

@@ -1,63 +0,0 @@
<template>
<view class="merchant-review">
<view class="page-header">
<text class="page-title">商家入驻审核</text>
<text class="page-subtitle">审核商家入驻申请</text>
</view>
<view class="review-content">
<text class="coming-soon">商家审核功能正在开发中...</text>
</view>
</view>
</template>
<script setup lang="uts">
// 统一的导航方法
const go = (url: string) => {
// 1) 目标页面必须是非 tabBar 页面
// 2) 必须在 pages.json / subPackages 注册
uni.navigateTo({ url })
}
</script>
<style lang="scss">
.merchant-review {
padding: 30rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.page-header {
background-color: #fff;
padding: 40rpx;
border-radius: 16rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #212529;
display: block;
margin-bottom: 10rpx;
}
.page-subtitle {
font-size: 26rpx;
color: #6c757d;
}
}
.review-content {
background-color: #fff;
padding: 60rpx 40rpx;
border-radius: 16rpx;
text-align: center;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.coming-soon {
font-size: 28rpx;
color: #6c757d;
}
}
</style>

View File

@@ -1,11 +0,0 @@
<template>
<view class="page">
<text>通知中心 - 占位页</text>
</view>
</template>
<script lang="uts">
export default {}
</script>
<style>
.page { padding: 30rpx; }
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
<template>
<view class="Page">
<view class="Header">
<text class="Title">商品分类</text>
<text class="SubTitle">product-classification</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -0,0 +1,62 @@
<template>
<view class="Page">
<view class="Header">
<text class="Title">商品标签</text>
<text class="SubTitle">product-labels</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
<template>
<view class="Page">
<view class="Header">
<text class="Title">商品参数</text>
<text class="SubTitle">product-parameters</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -0,0 +1,62 @@
<template>
<view class="Page">
<view class="Header">
<text class="Title">商品保障</text>
<text class="SubTitle">product-protection</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -1,63 +0,0 @@
<template>
<view class="product-review">
<view class="page-header">
<text class="page-title">商品审核</text>
<text class="page-subtitle">审核商品上架申请</text>
</view>
<view class="review-content">
<text class="coming-soon">商品审核功能正在开发中...</text>
</view>
</view>
</template>
<script setup lang="uts">
// 统一的导航方法
const go = (url: string) => {
// 1) 目标页面必须是非 tabBar 页面
// 2) 必须在 pages.json / subPackages 注册
uni.navigateTo({ url })
}
</script>
<style lang="scss">
.product-review {
padding: 30rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.page-header {
background-color: #fff;
padding: 40rpx;
border-radius: 16rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #212529;
display: block;
margin-bottom: 10rpx;
}
.page-subtitle {
font-size: 26rpx;
color: #6c757d;
}
}
.review-content {
background-color: #fff;
padding: 60rpx 40rpx;
border-radius: 16rpx;
text-align: center;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.coming-soon {
font-size: 28rpx;
color: #6c757d;
}
}
</style>

View File

@@ -0,0 +1,62 @@
<template>
<view class="Page">
<view class="Header">
<text class="Title">商品评论</text>
<text class="SubTitle">product-reviews</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -0,0 +1,62 @@
<template>
<view class="Page">
<view class="Header">
<text class="Title">商品规格</text>
<text class="SubTitle">product-specifications</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -0,0 +1,62 @@
<template>
<view class="Page">
<view class="Header">
<text class="Title">商品统计</text>
<text class="SubTitle">product-statistics</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -1,872 +0,0 @@
<template>
<view class="admin-profile">
<!-- 管理员信息头部 -->
<view class="profile-header">
<image :src="adminInfo.avatar_url || '/static/default-avatar.png'" class="admin-avatar" @click="editProfile" />
<view class="admin-info">
<text class="admin-name">{{ adminInfo.nickname || adminInfo.phone }}</text>
<text class="admin-role">{{ getAdminRole() }}</text>
<view class="admin-stats">
<text class="stat-item">在线时长: {{ onlineHours }}h</text>
<text class="stat-item">权限等级: {{ adminLevel }}</text>
</view>
</view>
<view class="settings-icon" @click="goToSettings">⚙️</view>
</view>
<!-- 系统概览 -->
<view class="system-overview">
<view class="section-title">系统概览</view>
<view class="overview-grid">
<view class="overview-card" @click="goToUsers">
<text class="card-icon">👥</text>
<text class="card-value">{{ systemStats.users }}</text>
<text class="card-label">用户总数</text>
<text class="card-change" :class="{ positive: systemStats.userGrowth > 0 }">
{{ systemStats.userGrowth > 0 ? '+' : '' }}{{ systemStats.userGrowth }}%
</text>
</view>
<view class="overview-card" @click="goToOrders">
<text class="card-icon">📋</text>
<text class="card-value">{{ systemStats.orders }}</text>
<text class="card-label">订单总数</text>
<text class="card-change" :class="{ positive: systemStats.orderGrowth > 0 }">
{{ systemStats.orderGrowth > 0 ? '+' : '' }}{{ systemStats.orderGrowth }}%
</text>
</view>
<view class="overview-card" @click="goToMerchants">
<text class="card-icon">🏪</text>
<text class="card-value">{{ systemStats.merchants }}</text>
<text class="card-label">商家总数</text>
<text class="card-change" :class="{ positive: systemStats.merchantGrowth > 0 }">
{{ systemStats.merchantGrowth > 0 ? '+' : '' }}{{ systemStats.merchantGrowth }}%
</text>
</view>
<view class="overview-card" @click="goToRevenue">
<text class="card-icon">💰</text>
<text class="card-value">¥{{ systemStats.revenue }}</text>
<text class="card-label">总营收</text>
<text class="card-change" :class="{ positive: systemStats.revenueGrowth > 0 }">
{{ systemStats.revenueGrowth > 0 ? '+' : '' }}{{ systemStats.revenueGrowth }}%
</text>
</view>
</view>
</view>
<!-- 待处理事项 -->
<view class="pending-tasks">
<view class="section-title">待处理事项</view>
<view class="task-list">
<view class="task-item urgent" @click="goToAudit('merchant')">
<text class="task-icon">🏪</text>
<view class="task-info">
<text class="task-title">商家审核</text>
<text class="task-desc">{{ pendingTasks.merchantAudit }}个商家待审核</text>
</view>
<text class="task-badge urgent">{{ pendingTasks.merchantAudit }}</text>
</view>
<view class="task-item" @click="goToComplaints">
<text class="task-icon">📢</text>
<view class="task-info">
<text class="task-title">投诉处理</text>
<text class="task-desc">{{ pendingTasks.complaints }}个投诉待处理</text>
</view>
<text class="task-badge">{{ pendingTasks.complaints }}</text>
</view>
<view class="task-item" @click="goToRefunds">
<text class="task-icon">↩️</text>
<view class="task-info">
<text class="task-title">退款审核</text>
<text class="task-desc">{{ pendingTasks.refunds }}个退款待审核</text>
</view>
<text class="task-badge">{{ pendingTasks.refunds }}</text>
</view>
<view class="task-item" @click="goToReports">
<text class="task-icon">⚠️</text>
<view class="task-info">
<text class="task-title">举报处理</text>
<text class="task-desc">{{ pendingTasks.reports }}个举报待处理</text>
</view>
<text class="task-badge">{{ pendingTasks.reports }}</text>
</view>
</view>
</view>
<!-- 今日数据 -->
<view class="today-data">
<view class="section-title">今日数据</view>
<view class="data-grid">
<view class="data-card">
<text class="data-value">{{ todayData.newUsers }}</text>
<text class="data-label">新增用户</text>
</view>
<view class="data-card">
<text class="data-value">{{ todayData.newOrders }}</text>
<text class="data-label">新增订单</text>
</view>
<view class="data-card">
<text class="data-value">¥{{ todayData.revenue }}</text>
<text class="data-label">平台收入</text>
</view>
<view class="data-card">
<text class="data-value">{{ todayData.activeUsers }}</text>
<text class="data-label">活跃用户</text>
</view>
</view>
</view>
<!-- 系统健康状态 -->
<view class="system-health">
<view class="section-header">
<text class="section-title">系统健康状态</text>
<text class="view-more" @click="goToMonitor">详细监控 ></text>
</view>
<view class="health-grid">
<view class="health-item">
<text class="health-label">服务器状态</text>
<view class="health-status">
<view class="status-dot" :class="{ online: systemHealth.server }"></view>
<text class="status-text">{{ systemHealth.server ? '正常' : '异常' }}</text>
</view>
</view>
<view class="health-item">
<text class="health-label">数据库状态</text>
<view class="health-status">
<view class="status-dot" :class="{ online: systemHealth.database }"></view>
<text class="status-text">{{ systemHealth.database ? '正常' : '异常' }}</text>
</view>
</view>
<view class="health-item">
<text class="health-label">缓存状态</text>
<view class="health-status">
<view class="status-dot" :class="{ online: systemHealth.cache }"></view>
<text class="status-text">{{ systemHealth.cache ? '正常' : '异常' }}</text>
</view>
</view>
<view class="health-item">
<text class="health-label">支付服务</text>
<view class="health-status">
<view class="status-dot" :class="{ online: systemHealth.payment }"></view>
<text class="status-text">{{ systemHealth.payment ? '正常' : '异常' }}</text>
</view>
</view>
</view>
</view>
<!-- 最近操作记录 -->
<view class="recent-operations">
<view class="section-header">
<text class="section-title">最近操作</text>
<text class="view-all" @click="goToOperationLogs">查看全部 ></text>
</view>
<view v-if="recentOperations.length > 0" class="operation-list">
<view v-for="operation in recentOperations" :key="operation.id" class="operation-item">
<view class="operation-info">
<text class="operation-title">{{ operation.title }}</text>
<text class="operation-desc">{{ operation.description }}</text>
</view>
<text class="operation-time">{{ formatTime(operation.created_at) }}</text>
</view>
</view>
<view v-else class="no-data">
<text class="no-data-text">暂无操作记录</text>
</view>
</view>
<!-- 快捷功能 -->
<view class="quick-functions">
<view class="section-title">快捷功能</view>
<view class="function-grid">
<view class="function-item" @click="goToUserManagement">
<text class="function-icon">👥</text>
<text class="function-label">用户管理</text>
</view>
<view class="function-item" @click="goToMerchantManagement">
<text class="function-icon">🏪</text>
<text class="function-label">商家管理</text>
</view>
<view class="function-item" @click="goToProductManagement">
<text class="function-icon">📦</text>
<text class="function-label">商品管理</text>
</view>
<view class="function-item" @click="goToOrderManagement">
<text class="function-icon">📋</text>
<text class="function-label">订单管理</text>
</view>
<view class="function-item" @click="goToFinanceManagement">
<text class="function-icon">💰</text>
<text class="function-label">财务管理</text>
</view>
<view class="function-item" @click="goToSystemSettings">
<text class="function-icon">⚙️</text>
<text class="function-label">系统设置</text>
</view>
</view>
</view>
<!-- 功能菜单 -->
<view class="function-menu">
<view class="menu-group">
<view class="menu-item" @click="goToReports">
<text class="menu-icon">📊</text>
<text class="menu-label">数据报表</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="goToAnnouncements">
<text class="menu-icon">📢</text>
<text class="menu-label">公告管理</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="goToPermissions">
<text class="menu-icon">🔐</text>
<text class="menu-label">权限管理</text>
<text class="menu-arrow">></text>
</view>
</view>
<view class="menu-group">
<view class="menu-item" @click="goToHelp">
<text class="menu-icon">❓</text>
<text class="menu-label">帮助中心</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="goToFeedback">
<text class="menu-icon">💬</text>
<text class="menu-label">意见反馈</text>
<text class="menu-arrow">></text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref, onMounted } from 'vue'
import type { UserType, ApiResponseType } from '@/types/mall-types'
// 响应式数据
const adminInfo = ref({
id: '',
phone: '',
nickname: '系统管理员',
avatar_url: ''
} as UserType)
const onlineHours = ref(8.5)
const adminLevel = ref('超级管理员')
const systemStats = ref({
users: '12,568',
userGrowth: 12.5,
orders: '8,932',
orderGrowth: 8.3,
merchants: '1,234',
merchantGrowth: 5.2,
revenue: '1,256,789',
revenueGrowth: 15.8
})
const pendingTasks = ref({
merchantAudit: 8,
complaints: 15,
refunds: 12,
reports: 6
})
const todayData = ref({
newUsers: 156,
newOrders: 289,
revenue: '25,680',
activeUsers: 3456
})
const systemHealth = ref({
server: true,
database: true,
cache: true,
payment: true
})
const recentOperations = ref([
{
id: 'op001',
title: '商家审核通过',
description: '审核通过商家"时尚服饰专营店"',
created_at: '2024-12-01 14:30:00'
},
{
id: 'op002',
title: '处理用户投诉',
description: '处理订单#ORD20241201001投诉',
created_at: '2024-12-01 13:45:00'
},
{
id: 'op003',
title: '系统配置更新',
description: '更新了支付配置参数',
created_at: '2024-12-01 12:20:00'
}
])
// 生命周期
onMounted(() => {
loadAdminInfo()
loadSystemStats()
loadPendingTasks()
})
// 方法
function loadAdminInfo() {
// 模拟加载管理员信息
adminInfo.value = {
id: 'admin001',
phone: '13900000000',
email: 'admin@mall.com',
nickname: '超级管理员',
avatar_url: '/static/admin-avatar.png',
gender: 0,
user_type: 99,
status: 1,
created_at: '2024-01-01'
}
}
function loadSystemStats() {
// 模拟加载系统统计
// 实际应用中从API获取
}
function loadPendingTasks() {
// 模拟加载待处理任务
// 实际应用中从API获取
}
function getAdminRole(): string {
const roleMap = {
99: '超级管理员',
98: '系统管理员',
97: '运营管理员'
}
return roleMap[adminInfo.value.user_type] || '管理员'
}
function formatTime(dateStr: string): string {
const date = new Date(dateStr)
const now = new Date()
const diff = now.getTime() - date.getTime()
const hours = Math.floor(diff / (1000 * 60 * 60))
if (hours < 1) {
return '刚刚'
} else if (hours < 24) {
return `${hours}小时前`
} else {
return `${Math.floor(hours / 24)}天前`
}
}
// 导航方法
function editProfile() {
uni.navigateTo({
url: '/pages/mall/admin/profile-edit'
})
}
function goToSettings() {
uni.navigateTo({
url: '/pages/mall/admin/settings'
})
}
function goToUsers() {
uni.navigateTo({
url: '/pages/mall/admin/users'
})
}
function goToOrders() {
uni.navigateTo({
url: '/pages/mall/admin/orders'
})
}
function goToMerchants() {
uni.navigateTo({
url: '/pages/mall/admin/merchants'
})
}
function goToRevenue() {
uni.navigateTo({
url: '/pages/mall/admin/revenue'
})
}
function goToAudit(type: string) {
uni.navigateTo({
url: `/pages/mall/admin/audit?type=${type}`
})
}
function goToComplaints() {
uni.navigateTo({
url: '/pages/mall/admin/complaints'
})
}
function goToRefunds() {
uni.navigateTo({
url: '/pages/mall/admin/refunds'
})
}
function goToReports() {
uni.navigateTo({
url: '/pages/mall/admin/reports'
})
}
function goToMonitor() {
uni.navigateTo({
url: '/pages/mall/admin/monitor'
})
}
function goToOperationLogs() {
uni.navigateTo({
url: '/pages/mall/admin/operation-logs'
})
}
function goToUserManagement() {
uni.navigateTo({
url: '/pages/mall/admin/user-management'
})
}
function goToMerchantManagement() {
uni.navigateTo({
url: '/pages/mall/admin/merchant-management'
})
}
function goToProductManagement() {
uni.navigateTo({
url: '/pages/mall/admin/product-management'
})
}
function goToOrderManagement() {
uni.navigateTo({
url: '/pages/mall/admin/order-management'
})
}
function goToFinanceManagement() {
uni.navigateTo({
url: '/pages/mall/admin/finance-management'
})
}
function goToSystemSettings() {
uni.navigateTo({
url: '/pages/mall/admin/system-settings'
})
}
function goToAnnouncements() {
uni.navigateTo({
url: '/pages/mall/admin/announcements'
})
}
function goToPermissions() {
uni.navigateTo({
url: '/pages/mall/admin/permissions'
})
}
function goToHelp() {
uni.navigateTo({
url: '/pages/mall/common/help'
})
}
function goToFeedback() {
uni.navigateTo({
url: '/pages/mall/common/feedback'
})
}
</script>
<style scoped>
.admin-profile {
padding: 0 0 120rpx 0;
background-color: #f5f5f5;
min-height: 100vh;
}
.profile-header {
display: flex;
align-items: center;
padding: 40rpx 30rpx;
background: linear-gradient(135deg, #a29bfe 0%, #6c5ce7 100%);
position: relative;
}
.admin-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
margin-right: 30rpx;
border: 4rpx solid rgba(255, 255, 255, 0.3);
}
.admin-info {
flex: 1;
}
.admin-name {
font-size: 36rpx;
font-weight: bold;
color: white;
margin-bottom: 10rpx;
}
.admin-role {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 15rpx;
}
.admin-stats {
display: flex;
gap: 30rpx;
}
.stat-item {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.9);
}
.settings-icon {
font-size: 36rpx;
color: white;
padding: 10rpx;
}
.system-overview, .pending-tasks, .today-data, .system-health, .recent-operations, .quick-functions, .function-menu {
margin: 20rpx 30rpx;
background: white;
border-radius: 20rpx;
padding: 30rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 30rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.view-all, .view-more {
font-size: 24rpx;
color: #a29bfe;
}
.overview-grid {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 20rpx;
}
.overview-card {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
min-width: 150rpx;
padding: 25rpx 15rpx;
background: linear-gradient(135deg, #f8f9ff 0%, #e8ecff 100%);
border-radius: 20rpx;
position: relative;
}
.card-icon {
font-size: 48rpx;
margin-bottom: 10rpx;
}
.card-value {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 5rpx;
}
.card-label {
font-size: 22rpx;
color: #666;
margin-bottom: 10rpx;
}
.card-change {
font-size: 20rpx;
color: #ff6b6b;
}
.card-change.positive {
color: #00b894;
}
.task-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.task-item {
display: flex;
align-items: center;
padding: 20rpx;
background: #f8f9ff;
border-radius: 15rpx;
position: relative;
}
.task-item.urgent {
border-left: 6rpx solid #ff6b6b;
}
.task-icon {
font-size: 36rpx;
margin-right: 20rpx;
}
.task-info {
flex: 1;
}
.task-title {
font-size: 28rpx;
color: #333;
font-weight: 500;
margin-bottom: 5rpx;
}
.task-desc {
font-size: 22rpx;
color: #666;
}
.task-badge {
background: #a29bfe;
color: white;
font-size: 20rpx;
padding: 6rpx 12rpx;
border-radius: 15rpx;
min-width: 30rpx;
text-align: center;
}
.task-badge.urgent {
background: #ff6b6b;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.data-grid {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 20rpx;
}
.data-card {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
min-width: 150rpx;
padding: 20rpx;
background: #f8f9ff;
border-radius: 15rpx;
}
.data-value {
font-size: 36rpx;
font-weight: bold;
color: #a29bfe;
margin-bottom: 5rpx;
}
.data-label {
font-size: 24rpx;
color: #666;
}
.health-grid {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.health-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx;
background: #f8f9ff;
border-radius: 15rpx;
}
.health-label {
font-size: 26rpx;
color: #333;
}
.health-status {
display: flex;
align-items: center;
gap: 10rpx;
}
.status-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background: #ff6b6b;
}
.status-dot.online {
background: #00b894;
}
.status-text {
font-size: 24rpx;
color: #666;
}
.operation-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.operation-item {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 20rpx;
background: #f8f9ff;
border-radius: 15rpx;
}
.operation-info {
flex: 1;
}
.operation-title {
font-size: 26rpx;
color: #333;
font-weight: 500;
margin-bottom: 5rpx;
}
.operation-desc {
font-size: 22rpx;
color: #666;
}
.operation-time {
font-size: 20rpx;
color: #999;
}
.function-grid {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 20rpx;
}
.function-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
min-width: 140rpx;
padding: 25rpx 15rpx;
background: #f8f9ff;
border-radius: 15rpx;
}
.function-icon {
font-size: 48rpx;
margin-bottom: 15rpx;
}
.function-label {
font-size: 24rpx;
color: #333;
}
.menu-group {
margin-bottom: 30rpx;
}
.menu-group:last-child {
margin-bottom: 0;
}
.menu-item {
display: flex;
align-items: center;
padding: 25rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.menu-item:last-child {
border-bottom: none;
}
.menu-icon {
font-size: 36rpx;
width: 60rpx;
margin-right: 25rpx;
}
.menu-label {
flex: 1;
font-size: 28rpx;
color: #333;
}
.menu-arrow {
font-size: 24rpx;
color: #ccc;
}
.no-data {
text-align: center;
padding: 60rpx 0;
}
.no-data-text {
font-size: 24rpx;
color: #999;
}
</style>

View File

@@ -1,63 +0,0 @@
<template>
<view class="refund-review">
<view class="page-header">
<text class="page-title">退款审核</text>
<text class="page-subtitle">审核用户退款申请</text>
</view>
<view class="review-content">
<text class="coming-soon">退款审核功能正在开发中...</text>
</view>
</view>
</template>
<script setup lang="uts">
// 统一的导航方法
const go = (url: string) => {
// 1) 目标页面必须是非 tabBar 页面
// 2) 必须在 pages.json / subPackages 注册
uni.navigateTo({ url })
}
</script>
<style lang="scss">
.refund-review {
padding: 30rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.page-header {
background-color: #fff;
padding: 40rpx;
border-radius: 16rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #212529;
display: block;
margin-bottom: 10rpx;
}
.page-subtitle {
font-size: 26rpx;
color: #6c757d;
}
}
.review-content {
background-color: #fff;
padding: 60rpx 40rpx;
border-radius: 16rpx;
text-align: center;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.coming-soon {
font-size: 28rpx;
color: #6c757d;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">协议设置</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">协议设置</text>
<text class="p">TODO在这里实现 协议设置 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">商品采集配置</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">商品采集配置</text>
<text class="p">TODO在这里实现 商品采集配置 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">物流查询配置</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">物流查询配置</text>
<text class="p">TODO在这里实现 物流查询配置 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">商城支付配置</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">商城支付配置</text>
<text class="p">TODO在这里实现 商城支付配置 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">短信接口配置</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">短信接口配置</text>
<text class="p">TODO在这里实现 短信接口配置 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">系统存储配置</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">系统存储配置</text>
<text class="p">TODO在这里实现 系统存储配置 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">电子面单配置</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">电子面单配置</text>
<text class="p">TODO在这里实现 电子面单配置 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">一号通配置</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">一号通配置</text>
<text class="p">TODO在这里实现 一号通配置 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">一号通页面</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">一号通页面</text>
<text class="p">TODO在这里实现 一号通页面 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">消息管理</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">消息管理</text>
<text class="p">TODO在这里实现 消息管理 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">管理员列表</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">管理员列表</text>
<text class="p">TODO在这里实现 管理员列表 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">权限设置</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">权限设置</text>
<text class="p">TODO在这里实现 权限设置 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">角色管理</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">角色管理</text>
<text class="p">TODO在这里实现 角色管理 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">小票配置</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">小票配置</text>
<text class="p">TODO在这里实现 小票配置 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">配送员管理</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">配送员管理</text>
<text class="p">TODO在这里实现 配送员管理 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">运费模板</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">运费模板</text>
<text class="p">TODO在这里实现 运费模板 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">提货点</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">提货点</text>
<text class="p">TODO在这里实现 提货点 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page">
<!-- TopBar (navigationStyle: custom 时需要自己做顶部栏) -->
<view class="topbar">
<view class="status-bar" />
<view class="nav">
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
<text class="nav-text">返回</text>
</view>
<text class="nav-title">核销员</text>
<view class="nav-right">
<text class="nav-action" @click="onPrimaryAction">保存</text>
</view>
</view>
</view>
<!-- Main -->
<scroll-view class="body" scroll-y="true">
<view class="container">
<view class="card">
<text class="h2">核销员</text>
<text class="p">TODO在这里实现 核销员 的页面内容。</text>
<view class="divider" />
<text class="p muted">提示当前为可跑的占位模板TopBar + Container + Card。</text>
</view>
<view class="card">
<text class="h3">建议你下一步先补齐</text>
<view class="list">
<text class="li">1接口拉取/保存数据API 协议对齐后端)</text>
<text class="li">2列表/表单:搜索、分页、增删改</text>
<text class="li">3权限按钮/路由级权限控制</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
const goBack = () => {
uni.navigateBack({ delta: 1 })
}
const onPrimaryAction = () => {
uni.showToast({ title: 'TODO保存逻辑', icon: 'none' })
}
</script>
<style scoped>
.page { width: 100%; height: 100%; background-color: #f6f7fb; }
/* 顶部栏(适配沉浸式状态栏) */
.topbar { position: sticky; top: 0; z-index: 10; background-color: #fff; }
.status-bar { height: var(--status-bar-height); }
.nav { height: 44px; display: flex; align-items: center; padding: 0 12px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #eef0f5; }
.nav-left { width: 88px; display: flex; align-items: center; }
.nav-icon { font-size: 22px; margin-right: 4px; color: #111; }
.nav-text { font-size: 14px; color: #111; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #111; }
.nav-right { width: 88px; display: flex; justify-content: flex-end; }
.nav-action { font-size: 14px; color: #1677ff; }
.body { height: calc(100vh - 44px - var(--status-bar-height)); }
.container { padding: 12px; }
.card { background-color: #fff; border-radius: 12px; padding: 14px; margin-bottom: 12px; box-shadow: 0 6px 18px rgba(17, 24, 39, 0.06); }
.h2 { font-size: 18px; font-weight: 700; color: #111; }
.h3 { font-size: 16px; font-weight: 600; color: #111; margin-bottom: 8px; }
.p { font-size: 13px; color: #333; margin-top: 8px; line-height: 18px; }
.muted { color: #8892a6; }
.divider { height: 1px; background-color: #eef0f5; margin: 12px 0; }
.list { margin-top: 8px; }
.li { font-size: 13px; color: #333; line-height: 20px; }
</style>

View File

@@ -1,907 +0,0 @@
<template>
<view class="user-detail-page">
<!-- 用户基本信息 -->
<view class="user-profile">
<view class="profile-header">
<image :src="user.avatar_url || '/static/default-avatar.png'" class="user-avatar" />
<view class="user-basic">
<text class="user-name">{{ user.nickname || user.phone }}</text>
<text class="user-id">ID: {{ user.id }}</text>
<view class="user-status">
<text class="status-badge" :class="{ active: user.status === 1, inactive: user.status === 0 }">
{{ user.status === 1 ? '正常' : '冻结' }}
</text>
<text class="user-type">{{ getUserTypeText() }}</text>
</view>
</view>
<view class="action-menu" @click="showActionMenu">⋮</view>
</view>
<view class="profile-details">
<view class="detail-item">
<text class="detail-label">手机号码</text>
<text class="detail-value">{{ user.phone }}</text>
</view>
<view class="detail-item">
<text class="detail-label">邮箱地址</text>
<text class="detail-value">{{ user.email || '未设置' }}</text>
</view>
<view class="detail-item">
<text class="detail-label">性别</text>
<text class="detail-value">{{ getGenderText() }}</text>
</view>
<view class="detail-item">
<text class="detail-label">注册时间</text>
<text class="detail-value">{{ formatTime(user.created_at) }}</text>
</view>
</view>
</view>
<!-- 用户统计 -->
<view class="user-stats">
<view class="section-title">用户统计</view>
<view class="stats-grid">
<view class="stat-item">
<text class="stat-value">{{ userStats.total_orders }}</text>
<text class="stat-label">总订单数</text>
</view>
<view class="stat-item">
<text class="stat-value">¥{{ userStats.total_amount }}</text>
<text class="stat-label">消费总额</text>
</view>
<view class="stat-item">
<text class="stat-value">{{ userStats.total_reviews }}</text>
<text class="stat-label">评价数量</text>
</view>
<view class="stat-item">
<text class="stat-value">{{ userStats.avg_rating.toFixed(1) }}</text>
<text class="stat-label">平均评分</text>
</view>
</view>
</view>
<!-- 最近订单 -->
<view class="recent-orders">
<view class="section-header">
<text class="section-title">最近订单</text>
<text class="view-all" @click="viewAllOrders">查看全部</text>
</view>
<view v-if="recentOrders.length === 0" class="empty-orders">
<text class="empty-text">暂无订单记录</text>
</view>
<view v-for="order in recentOrders" :key="order.id" class="order-item" @click="viewOrderDetail(order)">
<view class="order-header">
<text class="order-no">{{ order.order_no }}</text>
<text class="order-status" :class="getOrderStatusClass(order.status)">{{ getOrderStatusText(order.status) }}</text>
</view>
<view class="order-info">
<text class="order-amount">¥{{ order.actual_amount }}</text>
<text class="order-time">{{ formatTime(order.created_at) }}</text>
</view>
</view>
</view>
<!-- 用户行为记录 -->
<view class="user-activities">
<view class="section-header">
<text class="section-title">行为记录</text>
<text class="view-all" @click="viewAllActivities">查看全部</text>
</view>
<view v-for="activity in userActivities" :key="activity.id" class="activity-item">
<view class="activity-icon" :class="activity.type">{{ getActivityIcon(activity.type) }}</view>
<view class="activity-content">
<text class="activity-desc">{{ activity.description }}</text>
<text class="activity-time">{{ formatTime(activity.created_at) }}</text>
</view>
</view>
</view>
<!-- 风险评估 -->
<view class="risk-assessment">
<view class="section-title">风险评估</view>
<view class="risk-score">
<view class="score-circle" :class="getRiskLevel()">
<text class="score-value">{{ riskData.score }}</text>
<text class="score-max">/100</text>
</view>
<view class="risk-info">
<text class="risk-level">{{ getRiskLevelText() }}</text>
<text class="risk-desc">{{ getRiskDescription() }}</text>
</view>
</view>
<view class="risk-factors">
<view v-for="factor in riskData.factors" :key="factor.type" class="factor-item">
<text class="factor-label">{{ factor.label }}</text>
<view class="factor-bar">
<view class="factor-fill" :style="{ width: factor.value + '%' }" :class="factor.level"></view>
</view>
<text class="factor-value">{{ factor.value }}%</text>
</view>
</view>
</view>
<!-- 操作记录 -->
<view class="admin-logs">
<view class="section-header">
<text class="section-title">操作记录</text>
<text class="add-log" @click="addAdminLog">添加记录</text>
</view>
<view v-for="log in adminLogs" :key="log.id" class="log-item">
<view class="log-content">
<text class="log-action">{{ log.action }}</text>
<text class="log-reason">{{ log.reason }}</text>
</view>
<view class="log-meta">
<text class="log-admin">{{ log.admin_name }}</text>
<text class="log-time">{{ formatTime(log.created_at) }}</text>
</view>
</view>
</view>
<!-- 操作菜单弹窗 -->
<view v-if="showMenu" class="action-modal" @click="hideActionMenu">
<view class="action-list" @click.stop>
<view class="action-item" @click="toggleUserStatus">
{{ user.status === 1 ? '冻结用户' : '解冻用户' }}
</view>
<view class="action-item" @click="resetPassword">重置密码</view>
<view class="action-item" @click="sendMessage">发送消息</view>
<view class="action-item danger" @click="deleteUser">删除用户</view>
</view>
</view>
</view>
</template>
<script>
import { UserType, OrderType } from '@/types/mall-types.uts'
type UserStatsType = {
total_orders: number
total_amount: number
total_reviews: number
avg_rating: number
}
type UserActivityType = {
id: string
type: string
description: string
created_at: string
}
type RiskFactorType = {
type: string
label: string
value: number
level: string
}
type RiskDataType = {
score: number
level: string
factors: Array<RiskFactorType>
}
type AdminLogType = {
id: string
action: string
reason: string
admin_name: string
created_at: string
}
export default {
data() {
return {
user: {
id: '',
phone: '',
email: '',
nickname: '',
avatar_url: '',
gender: 0,
user_type: 0,
status: 0,
created_at: ''
} as UserType,
userStats: {
total_orders: 0,
total_amount: 0,
total_reviews: 0,
avg_rating: 0
} as UserStatsType,
recentOrders: [] as Array<OrderType>,
userActivities: [] as Array<UserActivityType>,
riskData: {
score: 0,
level: '',
factors: []
} as RiskDataType,
adminLogs: [] as Array<AdminLogType>,
showMenu: false
}
},
onLoad(options: any) {
const userId = options.userId as string
if (userId) {
this.loadUserDetail(userId)
}
},
methods: {
loadUserDetail(userId: string) {
// 模拟加载用户详情数据
this.user = {
id: userId,
phone: '13800138000',
email: 'user@example.com',
nickname: '张三',
avatar_url: '/static/avatar1.jpg',
gender: 1,
user_type: 1,
status: 1,
created_at: '2023-06-15T10:30:00'
}
this.userStats = {
total_orders: 23,
total_amount: 5680.50,
total_reviews: 18,
avg_rating: 4.3
}
this.recentOrders = [
{
id: 'order_001',
order_no: 'ORD202401150001',
user_id: userId,
merchant_id: 'merchant_001',
status: 4,
total_amount: 299.98,
discount_amount: 30.00,
delivery_fee: 8.00,
actual_amount: 277.98,
payment_method: 1,
payment_status: 1,
delivery_address: {},
created_at: '2024-01-15T14:30:00'
}
]
this.userActivities = [
{
id: 'activity_001',
type: 'login',
description: '用户登录系统',
created_at: '2024-01-15T09:30:00'
},
{
id: 'activity_002',
type: 'order',
description: '创建订单 ORD202401150001',
created_at: '2024-01-15T14:30:00'
},
{
id: 'activity_003',
type: 'review',
description: '对商品进行评价',
created_at: '2024-01-14T16:20:00'
}
]
this.riskData = {
score: 25,
level: 'low',
factors: [
{ type: 'refund', label: '退款率', value: 15, level: 'low' },
{ type: 'complaint', label: '投诉率', value: 5, level: 'low' },
{ type: 'chargeback', label: '拒付率', value: 0, level: 'low' },
{ type: 'fraud', label: '欺诈风险', value: 10, level: 'low' }
]
}
this.adminLogs = [
{
id: 'log_001',
action: '账户验证',
reason: '用户实名认证通过',
admin_name: '管理员小李',
created_at: '2024-01-10T11:00:00'
}
]
},
getUserTypeText(): string {
const types = ['普通用户', '普通用户', '商家用户', '配送员', '管理员']
return types[this.user.user_type] || '未知'
},
getGenderText(): string {
const genders = ['未知', '男', '女']
return genders[this.user.gender] || '未知'
},
getOrderStatusText(status: number): string {
const statusTexts = ['异常', '待支付', '待发货', '待收货', '已完成', '已取消']
return statusTexts[status] || '未知'
},
getOrderStatusClass(status: number): string {
const statusClasses = ['error', 'pending', 'processing', 'shipping', 'completed', 'cancelled']
return statusClasses[status] || 'error'
},
getActivityIcon(type: string): string {
const icons: Record<string, string> = {
login: '🔐',
order: '🛍️',
review: '⭐',
refund: '💰',
complaint: '⚠️'
}
return icons[type] || '📝'
},
getRiskLevel(): string {
if (this.riskData.score < 30) return 'low'
if (this.riskData.score < 70) return 'medium'
return 'high'
},
getRiskLevelText(): string {
const level = this.getRiskLevel()
const levelTexts: Record<string, string> = {
low: '低风险',
medium: '中风险',
high: '高风险'
}
return levelTexts[level] || '未知'
},
getRiskDescription(): string {
const level = this.getRiskLevel()
const descriptions: Record<string, string> = {
low: '用户行为正常,风险较低',
medium: '存在一定风险,需要关注',
high: '高风险用户,建议重点监控'
}
return descriptions[level] || ''
},
formatTime(timeStr: string): string {
return timeStr.replace('T', ' ').split('.')[0]
},
showActionMenu() {
this.showMenu = true
},
hideActionMenu() {
this.showMenu = false
},
toggleUserStatus() {
const action = this.user.status === 1 ? '冻结' : '解冻'
uni.showModal({
title: `${action}用户`,
content: `确定要${action}用户 ${this.user.nickname} 吗?`,
success: (res) => {
if (res.confirm) {
this.user.status = this.user.status === 1 ? 0 : 1
this.hideActionMenu()
uni.showToast({
title: `${action}成功`,
icon: 'success'
})
}
}
})
},
resetPassword() {
uni.showModal({
title: '重置密码',
content: '确定要重置用户密码吗?新密码将发送到用户手机。',
success: (res) => {
if (res.confirm) {
this.hideActionMenu()
uni.showToast({
title: '密码重置成功',
icon: 'success'
})
}
}
})
},
sendMessage() {
this.hideActionMenu()
uni.navigateTo({
url: `/pages/mall/admin/send-message?userId=${this.user.id}`
})
},
deleteUser() {
uni.showModal({
title: '删除用户',
content: '删除用户将无法恢复,确定要删除吗?',
success: (res) => {
if (res.confirm) {
this.hideActionMenu()
uni.showToast({
title: '用户已删除',
icon: 'success'
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
}
})
},
viewAllOrders() {
uni.navigateTo({
url: `/pages/mall/admin/user-orders?userId=${this.user.id}`
})
},
viewOrderDetail(order: OrderType) {
uni.navigateTo({
url: `/pages/mall/admin/order-detail?orderId=${order.id}`
})
},
viewAllActivities() {
uni.navigateTo({
url: `/pages/mall/admin/user-activities?userId=${this.user.id}`
})
},
addAdminLog() {
uni.navigateTo({
url: `/pages/mall/admin/add-log?userId=${this.user.id}`
})
}
}
}
</script>
<style>
.user-detail-page {
background-color: #f5f5f5;
min-height: 100vh;
}
.user-profile, .user-stats, .recent-orders, .user-activities, .risk-assessment, .admin-logs {
background-color: #fff;
margin-bottom: 20rpx;
padding: 30rpx;
}
.profile-header {
display: flex;
align-items: center;
margin-bottom: 30rpx;
}
.user-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
margin-right: 25rpx;
}
.user-basic {
flex: 1;
}
.user-name {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.user-id {
font-size: 24rpx;
color: #666;
margin-bottom: 10rpx;
}
.user-status {
display: flex;
align-items: center;
gap: 15rpx;
}
.status-badge {
padding: 8rpx 16rpx;
border-radius: 12rpx;
font-size: 22rpx;
color: #fff;
}
.status-badge.active {
background-color: #4caf50;
}
.status-badge.inactive {
background-color: #ff4444;
}
.user-type {
font-size: 22rpx;
color: #666;
background-color: #f0f0f0;
padding: 8rpx 16rpx;
border-radius: 12rpx;
}
.action-menu {
font-size: 32rpx;
color: #666;
padding: 10rpx;
}
.profile-details {
border-top: 1rpx solid #f5f5f5;
padding-top: 25rpx;
}
.detail-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15rpx 0;
}
.detail-label {
font-size: 26rpx;
color: #666;
width: 150rpx;
}
.detail-value {
flex: 1;
font-size: 26rpx;
color: #333;
text-align: right;
}
.section-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
margin-bottom: 25rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25rpx;
}
.view-all, .add-log {
font-size: 24rpx;
color: #007aff;
}
.stats-grid {
display: flex;
gap: 20rpx;
}
.stat-item {
flex: 1;
text-align: center;
padding: 30rpx 0;
background-color: #f8f9fa;
border-radius: 10rpx;
}
.stat-value {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.stat-label {
font-size: 22rpx;
color: #666;
}
.empty-orders {
text-align: center;
padding: 60rpx 0;
}
.empty-text {
font-size: 24rpx;
color: #999;
}
.order-item, .log-item {
padding: 25rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.order-item:last-child, .log-item:last-child {
border-bottom: none;
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10rpx;
}
.order-no {
font-size: 26rpx;
color: #333;
font-weight: bold;
}
.order-status {
font-size: 22rpx;
padding: 6rpx 12rpx;
border-radius: 10rpx;
color: #fff;
}
.order-status.pending {
background-color: #ffa726;
}
.order-status.processing {
background-color: #2196f3;
}
.order-status.shipping {
background-color: #9c27b0;
}
.order-status.completed {
background-color: #4caf50;
}
.order-status.cancelled {
background-color: #999;
}
.order-info {
display: flex;
justify-content: space-between;
align-items: center;
}
.order-amount {
font-size: 24rpx;
color: #ff4444;
font-weight: bold;
}
.order-time {
font-size: 22rpx;
color: #999;
}
.activity-item {
display: flex;
align-items: flex-start;
padding: 20rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.activity-item:last-child {
border-bottom: none;
}
.activity-icon {
font-size: 28rpx;
margin-right: 15rpx;
margin-top: 5rpx;
}
.activity-content {
flex: 1;
}
.activity-desc {
font-size: 26rpx;
color: #333;
margin-bottom: 5rpx;
}
.activity-time {
font-size: 22rpx;
color: #999;
}
.risk-score {
display: flex;
align-items: center;
margin-bottom: 30rpx;
}
.score-circle {
width: 140rpx;
height: 140rpx;
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-right: 30rpx;
border: 6rpx solid;
}
.score-circle.low {
border-color: #4caf50;
background-color: #e8f5e8;
}
.score-circle.medium {
border-color: #ffa726;
background-color: #fff8e1;
}
.score-circle.high {
border-color: #ff4444;
background-color: #ffebee;
}
.score-value {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
.score-max {
font-size: 20rpx;
color: #666;
margin-top: -5rpx;
}
.risk-info {
flex: 1;
}
.risk-level {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.risk-desc {
font-size: 24rpx;
color: #666;
line-height: 1.4;
}
.risk-factors {
margin-top: 30rpx;
}
.factor-item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.factor-label {
font-size: 24rpx;
color: #666;
width: 120rpx;
}
.factor-bar {
flex: 1;
height: 12rpx;
background-color: #f0f0f0;
border-radius: 6rpx;
margin: 0 20rpx;
overflow: hidden;
}
.factor-fill {
height: 100%;
border-radius: 6rpx;
}
.factor-fill.low {
background-color: #4caf50;
}
.factor-fill.medium {
background-color: #ffa726;
}
.factor-fill.high {
background-color: #ff4444;
}
.factor-value {
font-size: 22rpx;
color: #333;
width: 60rpx;
text-align: right;
}
.log-content {
margin-bottom: 10rpx;
}
.log-action {
font-size: 26rpx;
color: #333;
font-weight: bold;
margin-bottom: 5rpx;
}
.log-reason {
font-size: 24rpx;
color: #666;
}
.log-meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.log-admin {
font-size: 22rpx;
color: #007aff;
}
.log-time {
font-size: 22rpx;
color: #999;
}
.action-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.action-list {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx 0;
margin: 0 60rpx;
max-width: 500rpx;
}
.action-item {
padding: 25rpx 40rpx;
font-size: 28rpx;
color: #333;
text-align: center;
border-bottom: 1rpx solid #f5f5f5;
}
.action-item:last-child {
border-bottom: none;
}
.action-item.danger {
color: #ff4444;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -1,772 +1,62 @@
<template>
<AdminLayout current-page="statistics">
<view class="user-statistics-page">
<!-- 筛选条件栏 -->
<view class="filter-section">
<view class="filter-row">
<view class="filter-left">
<view class="filter-item">
<text class="filter-label">用户渠道:</text>
<picker
mode="selector"
:range="channelOptions"
:value="selectedChannel"
@change="handleChannelChange"
>
<view class="filter-select">
<text>{{ channelOptions[selectedChannel] }}</text>
<text class="iconfont icon-down"></text>
</view>
</picker>
</view>
<view class="filter-item">
<text class="filter-label">日期范围:</text>
<view class="date-range">
<picker
mode="date"
:value="startDate"
:start="minDate"
:end="maxDate"
@change="handleStartDateChange"
>
<view class="date-input">
<text>{{ startDate || '开始日期' }}</text>
</view>
</picker>
<text class="date-separator">-</text>
<picker
mode="date"
:value="endDate"
:start="minDate"
:end="maxDate"
@change="handleEndDateChange"
>
<view class="date-input">
<text>{{ endDate || '结束日期' }}</text>
</view>
</picker>
</view>
</view>
</view>
<view class="filter-right">
<button class="btn-secondary" @click="handleSearch">
<text class="iconfont icon-search"></text>
查询
</button>
<button class="btn-primary" @click="handleExport">
<text class="iconfont icon-export"></text>
导出
</button>
</view>
</view>
</view>
<!-- 指标概览 -->
<view class="metrics-section">
<view class="metrics-row">
<view class="metric-card">
<view class="metric-icon">
<text class="iconfont icon-users"></text>
</view>
<view class="metric-content">
<text class="metric-title">累计用户</text>
<text class="metric-value">{{ formatNumber(totalUsers) }}</text>
<view class="metric-change up">
<text class="iconfont icon-up"></text>
<text class="change-text">{{ userGrowth }}%</text>
<text class="change-desc">较上月</text>
</view>
</view>
</view>
<view class="metric-card">
<view class="metric-icon">
<text class="iconfont icon-eye"></text>
</view>
<view class="metric-content">
<text class="metric-title">访客数</text>
<text class="metric-value">{{ formatNumber(totalVisitors) }}</text>
<view class="metric-change up">
<text class="iconfont icon-up"></text>
<text class="change-text">{{ visitorGrowth }}%</text>
<text class="change-desc">较上月</text>
</view>
</view>
</view>
<view class="metric-card">
<view class="metric-icon">
<text class="iconfont icon-view"></text>
</view>
<view class="metric-content">
<text class="metric-title">浏览量</text>
<text class="metric-value">{{ formatNumber(totalPageViews) }}</text>
<view class="metric-change down">
<text class="iconfont icon-down"></text>
<text class="change-text">{{ pageViewDecline }}%</text>
<text class="change-desc">较上月</text>
</view>
</view>
</view>
<view class="metric-card">
<view class="metric-icon">
<text class="iconfont icon-user-add"></text>
</view>
<view class="metric-content">
<text class="metric-title">新增用户</text>
<text class="metric-value">{{ formatNumber(newUsers) }}</text>
<view class="metric-change up">
<text class="iconfont icon-up"></text>
<text class="change-text">{{ newUserGrowth }}%</text>
<text class="change-desc">较上月</text>
</view>
</view>
</view>
<view class="metric-card">
<view class="metric-icon">
<text class="iconfont icon-shopping"></text>
</view>
<view class="metric-content">
<text class="metric-title">成交用户</text>
<text class="metric-value">{{ formatNumber(convertedUsers) }}</text>
<view class="metric-change up">
<text class="iconfont icon-up"></text>
<text class="change-text">{{ conversionGrowth }}%</text>
<text class="change-desc">较上月</text>
</view>
</view>
</view>
<view class="metric-card">
<view class="metric-icon">
<text class="iconfont icon-vip"></text>
</view>
<view class="metric-content">
<text class="metric-title">付费会员</text>
<text class="metric-value">{{ formatNumber(vipUsers) }}</text>
<view class="metric-change up">
<text class="iconfont icon-up"></text>
<text class="change-text">{{ vipGrowth }}%</text>
<text class="change-desc">较上月</text>
</view>
</view>
</view>
</view>
</view>
<!-- 用户趋势图表 -->
<view class="chart-section">
<view class="admin-card">
<view class="admin-card-header">
<text class="admin-card-title">用户数据趋势分析</text>
</view>
<view class="admin-card-body">
<!-- 图表图例 -->
<view class="chart-legend">
<view class="legend-item" v-for="item in trendLegend" :key="item.key">
<view class="legend-color" :style="{ backgroundColor: item.color }"></view>
<text class="legend-text">{{ item.name }}</text>
</view>
</view>
<!-- 多折线图表容器 -->
<view class="multi-line-chart">
<!-- 图表区域 -->
<view class="chart-area">
<!-- 模拟多折线图 -->
<view class="line-container" v-for="(line, index) in trendLines" :key="line.key">
<view class="line-points">
<view
v-for="(point, pIndex) in line.data"
:key="pIndex"
class="line-point"
:style="{
left: (pIndex * 100 / (line.data.length - 1)) + '%',
bottom: point.height + '%',
backgroundColor: line.color
}"
></view>
</view>
</view>
<!-- X轴标签 -->
<view class="x-axis-labels">
<text class="axis-label" v-for="date in chartDates" :key="date">{{ date }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="Page">
<view class="Header">
<text class="Title">用户统计</text>
<text class="SubTitle">user-statistics</text>
</view>
</AdminLayout>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/index.uvue'
import { onLoad } from '@dcloudio/uni-app'
// 筛选条件
const selectedChannel = ref(0)
const channelOptions = ['全部渠道', '自然流量', '搜索引擎', '社交媒体', '广告投放', '其他']
const params = ref('')
const startDate = ref('')
const endDate = ref('')
const minDate = '2020-01-01'
const maxDate = new Date().toISOString().split('T')[0]
// 指标数据
const totalUsers = ref(32456)
const userGrowth = ref(12.5)
const totalVisitors = ref(156789)
const visitorGrowth = ref(8.3)
const totalPageViews = ref(456123)
const pageViewDecline = ref(3.2)
const newUsers = ref(1234)
const newUserGrowth = ref(15.7)
const convertedUsers = ref(5678)
const conversionGrowth = ref(9.4)
const vipUsers = ref(1234)
const vipGrowth = ref(22.1)
// 图表数据
const chartDates = ['01-01', '01-08', '01-15', '01-22', '01-29', '02-05', '02-12']
const trendLegend = [
{ key: 'newUsers', name: '新增用户', color: '#1890ff' },
{ key: 'visitors', name: '访客数', color: '#52c41a' },
{ key: 'pageViews', name: '浏览量', color: '#faad14' },
{ key: 'conversions', name: '成交用户', color: '#f5222d' },
{ key: 'vipUsers', name: '付费会员', color: '#722ed1' }
]
// 趋势线数据
const trendLines = ref([
{
key: 'newUsers',
color: '#1890ff',
data: [
{ value: 120, height: 12 },
{ value: 180, height: 18 },
{ value: 250, height: 25 },
{ value: 320, height: 32 },
{ value: 280, height: 28 },
{ value: 350, height: 35 },
{ value: 420, height: 42 }
]
},
{
key: 'visitors',
color: '#52c41a',
data: [
{ value: 450, height: 45 },
{ value: 520, height: 52 },
{ value: 580, height: 58 },
{ value: 620, height: 62 },
{ value: 550, height: 55 },
{ value: 680, height: 68 },
{ value: 750, height: 75 }
]
},
{
key: 'pageViews',
color: '#faad14',
data: [
{ value: 680, height: 68 },
{ value: 720, height: 72 },
{ value: 850, height: 85 },
{ value: 920, height: 92 },
{ value: 780, height: 78 },
{ value: 950, height: 95 },
{ value: 1000, height: 100 }
]
},
{
key: 'conversions',
color: '#f5222d',
data: [
{ value: 45, height: 4.5 },
{ value: 52, height: 5.2 },
{ value: 68, height: 6.8 },
{ value: 75, height: 7.5 },
{ value: 62, height: 6.2 },
{ value: 85, height: 8.5 },
{ value: 95, height: 9.5 }
]
},
{
key: 'vipUsers',
color: '#722ed1',
data: [
{ value: 12, height: 1.2 },
{ value: 15, height: 1.5 },
{ value: 22, height: 2.2 },
{ value: 28, height: 2.8 },
{ value: 25, height: 2.5 },
{ value: 35, height: 3.5 },
{ value: 42, height: 4.2 }
]
}
])
// 方法
const handleChannelChange = (e: any) => {
selectedChannel.value = e.detail.value
}
const handleStartDateChange = (e: any) => {
startDate.value = e.detail.value
}
const handleEndDateChange = (e: any) => {
endDate.value = e.detail.value
}
const handleSearch = () => {
uni.showToast({
title: '数据已更新',
icon: 'success'
})
}
const handleExport = () => {
uni.showToast({
title: '导出功能开发中',
icon: 'none'
})
}
const formatNumber = (num: number) => {
if (num >= 10000) {
return (num / 10000).toFixed(1) + '万'
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + 'k'
}
return num.toString()
}
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
/* ===== 用户统计页面样式 ===== */
.user-statistics-page {
width: 100%;
.Page {
padding: 24rpx;
}
/* ===== 筛选条件栏 ===== */
.filter-section {
background-color: #ffffff;
border: 1px solid #e8e8e8;
border-radius: 8px;
padding: 24px;
margin-bottom: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.filter-row {
display: flex;
flex-direction:row;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 20px;
.Title {
font-size: 36rpx;
font-weight: 700;
}
.filter-left {
display: flex;
flex-direction:row;
gap: 32px;
flex-wrap: wrap;
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.filter-right {
display: flex;
flex-direction:row;
gap: 16px;
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.filter-item {
display: flex;
flex-direction:row;
align-items: center;
gap: 12px;
}
.filter-label {
font-size: 14px;
color: #666666;
white-space: nowrap;
}
.filter-select {
display: flex;
flex-direction:row;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
border: 1px solid #d9d9d9;
border-radius: 6px;
background-color: #ffffff;
cursor: pointer;
min-width: 120px;
font-size: 14px;
}
.date-range {
display: flex;
flex-direction:row;
align-items: center;
gap: 8px;
}
.date-input {
padding: 8px 12px;
border: 1px solid #d9d9d9;
border-radius: 6px;
background-color: #ffffff;
text-align: center;
cursor: pointer;
font-size: 14px;
min-width: 120px;
}
.date-separator {
color: #666666;
font-size: 14px;
}
.btn-primary {
background-color: #1890ff;
color: #ffffff;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
}
.btn-secondary {
background-color: #ffffff;
color: #666666;
border: 1px solid #d9d9d9;
border-radius: 6px;
padding: 8px 16px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
}
/* ===== 指标概览 ===== */
.metrics-row {
display: flex;
flex-direction:row;
flex-wrap: wrap;
}
.metric-card {
flex: 1;
min-width: 280px;
background-color: #ffffff;
border: 1px solid #e8e8e8;
border-radius: 8px;
padding: 24px;
display: flex;
align-items: center;
gap: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.metric-icon {
width: 56px;
height: 56px;
background: linear-gradient(135deg, #1890ff 0%, #36cfc9 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 24px;
flex-shrink: 0;
}
.metric-content {
flex: 1;
}
.metric-title {
display: block;
font-size: 14px;
color: #666666;
margin-bottom: 8px;
}
.metric-value {
display: block;
font-size: 24px;
.Label {
font-size: 26rpx;
font-weight: 600;
color: #262626;
margin-bottom: 8px;
margin-bottom: 12rpx;
}
.metric-change {
display: flex;
align-items: center;
font-size: 12px;
border-radius: 12px;
padding: 4px 8px;
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
.metric-change.up {
background-color: #f6ffed;
color: #52c41a;
}
.metric-change.down {
background-color: #fff2f0;
color: #ff4d4f;
}
.change-text {
margin: 0 4px;
font-weight: 500;
}
.change-desc {
color: #999999;
}
/* ===== 图表区域 ===== */
.chart-section {
margin-bottom: 24px;
}
.admin-card {
background-color: #ffffff;
border: 1px solid #e8e8e8;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.admin-card-header {
padding: 24px 24px 0 24px;
display: flex;
flex-direction:row;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.admin-card-title {
font-size: 18px;
font-weight: 600;
color: #262626;
}
.admin-card-body {
padding: 0 24px 24px 24px;
}
/* ===== 图表图例 ===== */
.chart-legend {
display: flex;
flex-direction:row;
justify-content: center;
gap: 32px;
margin-bottom: 24px;
flex-wrap: wrap;
}
.legend-item {
display: flex;
flex-direction:row;
align-items: center;
gap: 8px;
}
.legend-color {
width: 16px;
height: 16px;
border-radius: 2px;
}
.legend-text {
font-size: 14px;
color: #666666;
}
/* ===== 多折线图表 ===== */
.multi-line-chart {
height: 400px;
position: relative;
background-color: #ffffff;
border: 1px solid #e8e8e8;
border-radius: 6px;
}
.chart-area {
position: absolute;
top: 40px;
left: 60px;
right: 40px;
bottom: 60px;
}
.line-container {
position: absolute;
width: 100%;
height: 100%;
}
.line-points {
position: relative;
width: 100%;
height: 100%;
}
.line-point {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
border: 2px solid #ffffff;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
transform: translate(-50%, -50%);
}
.x-axis-labels {
position: absolute;
bottom: -40px;
left: 0;
right: 0;
display: flex;
justify-content: space-between;
padding: 0 30px;
}
.axis-label {
font-size: 12px;
color: #999999;
text-align: center;
}
/* ===== 响应式设计 ===== */
@media (max-width: 1200px) {
.metrics-row {
flex-wrap: wrap;
}
.metric-card {
min-width: 45%;
flex: 0 0 auto;
}
}
@media (max-width: 768px) {
.filter-row {
flex-direction: column;
align-items: stretch;
}
.filter-left {
flex-direction: column;
gap: 16px;
}
.filter-right {
justify-content: center;
}
.metrics-row {
flex-direction: column;
}
.metric-card {
min-width: auto;
width: 100%;
}
.user-statistics-page {
padding: 16px;
}
.filter-section,
.chart-section {
margin-bottom: 16px;
}
.admin-card-header,
.admin-card-body {
padding-left: 16px;
padding-right: 16px;
}
.chart-legend {
gap: 16px;
}
}
/* ===== 图标字体 ===== */
.iconfont {
font-family: 'iconfont';
font-size: 14px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-up:before {
content: '↑';
}
.icon-down:before {
content: '↓';
}
.icon-users:before {
content: '👥';
}
.icon-eye:before {
content: '👁️';
}
.icon-view:before {
content: '📊';
}
.icon-user-add:before {
content: '👤';
}
.icon-shopping:before {
content: '🛒';
}
.icon-vip:before {
content: '👑';
}
.icon-search:before {
content: '🔍';
}
.icon-export:before {
content: '📤';
}
.icon-down:before {
content: '▼';
}
</style>
</style>

View File

@@ -466,12 +466,6 @@
"navigationBarTitleText": "营销管理"
}
},
{
"path": "activity-log",
"style": {
"navigationBarTitleText": "活动日志"
}
},
{
"path": "merchant-review",
"style": {
@@ -479,7 +473,7 @@
}
},
{
"path": "product-review",
"path": "product-reviews",
"style": {
"navigationBarTitleText": "商品审核"
}