chore: sync local changes before merging huangzhenbao-admin

This commit is contained in:
comlibmb
2026-02-03 08:56:35 +08:00
325 changed files with 55791 additions and 13448 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

@@ -0,0 +1,185 @@
/**
* 文章管理服务层
* 提供文章列表、详情、保存、删除等接口
*/
// 文章列表项数据结构
export interface ArticleItem {
id: number
title: string
category_id: number
category_name: string
image: string
description: string
status: number // 0: 未发布, 1: 已发布
views: number
created_at: string
updated_at: string
}
// 文章详情数据结构
export interface ArticleDetail {
id: number
title: string
category_id: number
image: string
description: string
content: string
status: number
created_at: string
updated_at: string
}
// 文章创建/编辑参数
export interface ArticlePayload {
title: string
category_id: number
image: string
description: string
content: string
status: number
}
/**
* 获取文章列表
* @param params 查询参数 { page, limit, keyword, status, category_id }
* @returns Promise<{ items: ArticleItem[], total: number }>
*/
export function getArticleList(params: any = {}): Promise<any> {
// TODO: 替换为实际 API 调用
// return uni.$http.get('/article/list', { params })
return new Promise((resolve) => {
setTimeout(() => {
resolve({
items: [
{
id: 1,
title: '如何选择合适的商品分类',
category_id: 1,
category_name: '运营指南',
image: '/static/article-1.png',
description: '商品分类是电商平台的重要组成部分...',
status: 1,
views: 128,
created_at: '2026-01-28 10:30:00',
updated_at: '2026-01-28 10:30:00'
},
{
id: 2,
title: '商城营销活动最佳实践',
category_id: 2,
category_name: '营销技巧',
image: '/static/article-2.png',
description: '分享最新的营销活动策略和案例...',
status: 1,
views: 256,
created_at: '2026-01-27 15:20:00',
updated_at: '2026-01-27 15:20:00'
},
{
id: 3,
title: '用户评价管理指南',
category_id: 1,
category_name: '运营指南',
image: '/static/article-3.png',
description: '如何有效管理用户的评价和反馈...',
status: 0,
views: 64,
created_at: '2026-01-26 09:15:00',
updated_at: '2026-01-26 09:15:00'
}
],
total: 3
})
}, 300)
})
}
/**
* 获取文章详情
* @param id 文章ID
* @returns Promise<ArticleDetail>
*/
export function getArticleDetail(id: number): Promise<ArticleDetail> {
// TODO: 替换为实际 API 调用
// return uni.$http.get(`/article/${id}`)
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id,
title: '如何选择合适的商品分类',
category_id: 1,
image: '/static/article-1.png',
description: '商品分类是电商平台的重要组成部分...',
content: '<h2>标题</h2><p>详细内容...</p>',
status: 1,
created_at: '2026-01-28 10:30:00',
updated_at: '2026-01-28 10:30:00'
})
}, 300)
})
}
/**
* 保存文章(新建或编辑)
* @param data 文章数据
* @param id 文章ID编辑时传入
* @returns Promise<{ success: boolean, message: string, id?: number }>
*/
export function saveArticle(data: ArticlePayload, id?: number): Promise<any> {
// TODO: 替换为实际 API 调用
// const method = id ? 'PUT' : 'POST'
// const url = id ? `/article/${id}` : '/article'
// return uni.$http[method === 'PUT' ? 'put' : 'post'](url, data)
return new Promise((resolve) => {
setTimeout(() => {
resolve({
success: true,
message: id ? '编辑成功' : '新建成功',
id: id || Math.floor(Math.random() * 10000)
})
}, 500)
})
}
/**
* 删除文章
* @param id 文章ID
* @returns Promise<{ success: boolean, message: string }>
*/
export function deleteArticle(id: number): Promise<any> {
// TODO: 替换为实际 API 调用
// return uni.$http.delete(`/article/${id}`)
return new Promise((resolve) => {
setTimeout(() => {
resolve({
success: true,
message: '删除成功'
})
}, 300)
})
}
/**
* 发布/取消发布文章
* @param id 文章ID
* @param status 状态 (0: 取消发布, 1: 发布)
* @returns Promise<{ success: boolean, message: string }>
*/
export function publishArticle(id: number, status: number): Promise<any> {
// TODO: 替换为实际 API 调用
// return uni.$http.put(`/article/${id}/publish`, { status })
return new Promise((resolve) => {
setTimeout(() => {
resolve({
success: true,
message: status === 1 ? '发布成功' : '取消发布成功'
})
}, 300)
})
}

View File

@@ -0,0 +1,178 @@
/**
* 文章分类管理服务层
* 提供分类列表、保存、删除等接口
*/
// 分类数据结构
export interface CategoryItem {
id: number
name: string
description: string
image: string
article_count: number
sort: number
status: number // 0: 禁用, 1: 启用
created_at: string
updated_at: string
}
// 分类创建/编辑参数
export interface CategoryPayload {
name: string
description: string
image: string
sort: number
status: number
}
/**
* 获取分类列表
* @param params 查询参数 { page, limit, keyword, status }
* @returns Promise<{ items: CategoryItem[], total: number }>
*/
export function getCategoryList(params: any = {}): Promise<any> {
// TODO: 替换为实际 API 调用
// return uni.$http.get('/article/category/list', { params })
return new Promise((resolve) => {
setTimeout(() => {
resolve({
items: [
{
id: 1,
name: '运营指南',
description: '关于商城运营的各类指南和教程',
image: '/static/category-1.png',
article_count: 12,
sort: 1,
status: 1,
created_at: '2026-01-15 10:00:00',
updated_at: '2026-01-20 15:30:00'
},
{
id: 2,
name: '营销技巧',
description: '营销活动策略和最佳实践',
image: '/static/category-2.png',
article_count: 8,
sort: 2,
status: 1,
created_at: '2026-01-15 11:00:00',
updated_at: '2026-01-19 14:20:00'
},
{
id: 3,
name: '常见问题',
description: '常见问题解答和故障排除',
image: '/static/category-3.png',
article_count: 5,
sort: 3,
status: 1,
created_at: '2026-01-15 12:00:00',
updated_at: '2026-01-18 09:45:00'
},
{
id: 4,
name: '产品更新',
description: '产品更新日志和新功能介绍',
image: '/static/category-4.png',
article_count: 3,
sort: 4,
status: 0,
created_at: '2026-01-16 10:00:00',
updated_at: '2026-01-17 16:00:00'
}
],
total: 4
})
}, 300)
})
}
/**
* 获取分类详情
* @param id 分类ID
* @returns Promise<CategoryItem>
*/
export function getCategoryDetail(id: number): Promise<CategoryItem> {
// TODO: 替换为实际 API 调用
// return uni.$http.get(`/article/category/${id}`)
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id,
name: '运营指南',
description: '关于商城运营的各类指南和教程',
image: '/static/category-1.png',
article_count: 12,
sort: 1,
status: 1,
created_at: '2026-01-15 10:00:00',
updated_at: '2026-01-20 15:30:00'
})
}, 300)
})
}
/**
* 保存分类(新建或编辑)
* @param data 分类数据
* @param id 分类ID编辑时传入
* @returns Promise<{ success: boolean, message: string, id?: number }>
*/
export function saveCategory(data: CategoryPayload, id?: number): Promise<any> {
// TODO: 替换为实际 API 调用
// const method = id ? 'PUT' : 'POST'
// const url = id ? `/article/category/${id}` : '/article/category'
// return uni.$http[method === 'PUT' ? 'put' : 'post'](url, data)
return new Promise((resolve) => {
setTimeout(() => {
resolve({
success: true,
message: id ? '编辑成功' : '新建成功',
id: id || Math.floor(Math.random() * 10000)
})
}, 500)
})
}
/**
* 删除分类
* @param id 分类ID
* @returns Promise<{ success: boolean, message: string }>
*/
export function deleteCategory(id: number): Promise<any> {
// TODO: 替换为实际 API 调用
// return uni.$http.delete(`/article/category/${id}`)
return new Promise((resolve) => {
setTimeout(() => {
resolve({
success: true,
message: '删除成功'
})
}, 300)
})
}
/**
* 启用/禁用分类
* @param id 分类ID
* @param status 状态 (0: 禁用, 1: 启用)
* @returns Promise<{ success: boolean, message: string }>
*/
export function toggleCategoryStatus(id: number, status: number): Promise<any> {
// TODO: 替换为实际 API 调用
// return uni.$http.put(`/article/category/${id}/status`, { status })
return new Promise((resolve) => {
setTimeout(() => {
resolve({
success: true,
message: status === 1 ? '启用成功' : '禁用成功'
})
}, 300)
})
}

View File

@@ -0,0 +1,25 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('category')
const title = ref<string>('category')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,25 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('create')
const title = ref<string>('create')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,25 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('edit')
const title = ref<string>('edit')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,25 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('article-index')
const title = ref<string>('文章管理')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page-container">
<view class="page-header">
<text class="page-title">文章管理</text>
<text class="page-subtitle">Component: CmsArticle</text>
</view>
<view class="page-content">
<view class="placeholder-card">
<text class="placeholder-title">页面占位</text>
<text class="placeholder-desc">该功能模块正在开发中</text>
<text class="placeholder-info">当前采用 CRMEB 路由体系 1:1 映射</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
// TODO: 实现 文章管理 的具体功能
const loading = ref<boolean>(false)
</script>
<style scoped lang="scss">
.page-container {
padding: 20px;
min-height: 100vh;
background: #f5f5f5;
}
.page-header {
margin-bottom: 20px;
}
.page-title {
display: block;
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.page-subtitle {
display: block;
font-size: 14px;
color: #999;
}
.page-content {
background: #fff;
border-radius: 4px;
padding: 24px;
}
.placeholder-card {
text-align: center;
padding: 60px 20px;
}
.placeholder-title {
display: block;
font-size: 18px;
font-weight: 600;
color: #666;
margin-bottom: 12px;
}
.placeholder-desc {
display: block;
font-size: 14px;
color: #999;
margin-bottom: 8px;
}
.placeholder-info {
display: block;
font-size: 12px;
color: #1890ff;
}
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page-container">
<view class="page-header">
<text class="page-title">文章分类</text>
<text class="page-subtitle">Component: CmsCategory</text>
</view>
<view class="page-content">
<view class="placeholder-card">
<text class="placeholder-title">页面占位</text>
<text class="placeholder-desc">该功能模块正在开发中</text>
<text class="placeholder-info">当前采用 CRMEB 路由体系 1:1 映射</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
// TODO: 实现 文章分类 的具体功能
const loading = ref<boolean>(false)
</script>
<style scoped lang="scss">
.page-container {
padding: 20px;
min-height: 100vh;
background: #f5f5f5;
}
.page-header {
margin-bottom: 20px;
}
.page-title {
display: block;
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.page-subtitle {
display: block;
font-size: 14px;
color: #999;
}
.page-content {
background: #fff;
border-radius: 4px;
padding: 24px;
}
.placeholder-card {
text-align: center;
padding: 60px 20px;
}
.placeholder-title {
display: block;
font-size: 18px;
font-weight: 600;
color: #666;
margin-bottom: 12px;
}
.placeholder-desc {
display: block;
font-size: 14px;
color: #999;
margin-bottom: 8px;
}
.placeholder-info {
display: block;
font-size: 12px;
color: #1890ff;
}
</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,65 @@
<template>
<AdminLayout currentPage="content-list">
<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>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
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,98 @@
<template>
<AdminLayout currentPage="cs-auto-reply">
<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>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
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,98 @@
<template>
<AdminLayout currentPage="cs-config">
<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>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
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,97 @@
<template>
<AdminLayout current-page='list'>
<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>
</AdminLayout>
</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,98 @@
<template>
<AdminLayout currentPage="cs-message">
<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>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
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,98 @@
<template>
<AdminLayout currentPage="cs-script">
<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>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
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,179 @@
# 页面装修管理模块 - README
> 基于CRMEB项目标准实现完整的页面装修和DIY功能
## 📋 文件结构
```
pages/mall/admin/design/
├── index.uvue # 装修管理主界面898行
├── design.uts # 业务逻辑和数据管理350+行)
├── editor.uvue # 装修编辑器(待实现)
├── preview.uvue # 装修预览页面(待实现)
└── README.md # 本文件
```
## 🎯 核心功能
### 1. 首页装修 (Homepage)
- 自定义首页布局和显示内容
- 支持轮播图、商品展示、文本等组件
- 实时预览效果
- 版本管理和发布
### 2. 分类页装修 (Category)
- 为不同商品分类创建装修页面
- 支持多套分类装修方案
- 按分类自动应用装修效果
- 快速切换和对比
### 3. 商品页装修 (Product)
- 自定义商品详情页布局
- 支持商品图、信息、评价等模块
- 提升商品转化率
- A/B测试支持
### 4. 自定义页面 (Custom)
- 创建和管理自定义营销页面
- 灵活的页面路径设置
- 独立的装修配置
- 活动和推广专用
### 5. 页面模板库 (Templates)
- 预设4套电商风格模板
- 一键应用模板快速建站
- 模板库不断扩充
- 自定义模板保存
### 6. 组件库 (Components)
- 8种预设装修组件
- 图片组件 (Image)
- 文本组件 (Text)
- 商品组件 (Product)
- 轮播组件 (Carousel)
- 分割线 (Divider)
- 间距组件 (Spacer)
- 按钮组件 (Button)
- 表单组件 (Form)
## 🔧 API 函数列表
| 函数 | 参数 | 返回值 | 说明 |
| -------------------------- | ---------- | -------------------------- | ---------------- |
| `getDesignList(params?)` | 查询参数 | Promise<DesignItem[]> | 获取装修列表 |
| `getHomePageDesign()` | 无 | Promise<DesignItem> | 获取首页装修 |
| `getProductPageDesign()` | 无 | Promise<DesignItem> | 获取商品页装修 |
| `getCategoryDesigns()` | 无 | Promise<DesignItem[]> | 获取分类装修列表 |
| `getCustomPages()` | 无 | Promise<DesignItem[]> | 获取自定义页面 |
| `getTemplateLibrary()` | 无 | Promise<DesignTemplate[]> | 获取模板库 |
| `getAvailableComponents()` | 无 | Promise<DesignComponent[]> | 获取可用组件 |
| `saveDesign(design)` | DesignItem | Promise<{id, message}> | 保存装修 |
| `publishDesign(id)` | 装修ID | Promise<{message}> | 发布装修 |
| `deleteDesign(id)` | 装修ID | Promise<{message}> | 删除装修 |
## 📊 数据结构
### DesignItem 装修页面
```typescript
interface DesignItem {
id: string | number; // 装修ID
name: string; // 装修名称
type: "homepage" | "category" | "product" | "custom";
status: 0 | 1; // 0=草稿, 1=已发布
content: DesignComponent[]; // 组件内容
categoryId?: string | number; // 分类ID
categoryName?: string; // 分类名称
path?: string; // 页面路径
version?: string; // 版本号
created_at?: string; // 创建时间
updated_at?: string; // 更新时间
}
```
### DesignComponent 组件配置
```typescript
interface DesignComponent {
id: string; // 组件ID
type:
| "image"
| "text"
| "product"
| "carousel"
| "divider"
| "spacer"
| "button"
| "form";
name: string; // 组件名称
icon: string; // 组件图标
description?: string; // 组件描述
config?: Record<string, any>; // 配置参数
children?: DesignComponent[]; // 子组件
}
```
## 💻 使用示例
```typescript
// 导入服务
import {
getDesignList,
saveDesign,
publishDesign,
getAvailableComponents,
} from "./design.uts";
// 获取列表
const designs = await getDesignList();
// 保存装修
await saveDesign({
id: 1,
name: "首页",
type: "homepage",
status: 0,
content: [],
});
// 发布装修
await publishDesign(1);
// 获取组件库
const components = await getAvailableComponents();
```
## 📱 菜单配置
```json
{
"id": "design",
"title": "设计",
"children": [
{
"id": "design-home",
"title": "页面装修",
"path": "/pages/mall/admin/design/index"
}
]
}
```
## 🚀 后续开发
- [ ] editor.uvue - 装修编辑器
- [ ] preview.uvue - 装修预览
- [ ] 拖拽排序功能
- [ ] 版本管理
- [ ] 模板库管理
---
**最后更新**: 2026-01-31
**版本**: 1.0.0

View File

@@ -0,0 +1,25 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('design-category')
const title = ref<string>('分类页装修')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,25 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('category')
const title = ref<string>('category')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,439 @@
/**
* 设计模块页面路由配置与数据
* 将 design.uts 的函数输出转换为页面路由/配置格式
*/
import type { DesignItem, DesignComponent, DesignTemplate } from './design.uts'
/**
* 装修页面列表路由配置
*/
export const designListPageConfig = {
id: 'design-list',
path: '/pages/mall/admin/design/list',
name: '装修列表',
title: '装修管理',
data: [
{
id: 1,
name: '首页装修',
type: 'homepage' as const,
status: 1,
path: '/pages/mall/admin/design/index?tab=homepage',
content: [],
updated_at: '2026-01-30 14:30:00'
},
{
id: 2,
name: '年货节活动页',
type: 'custom' as const,
status: 1,
path: '/pages/mall/admin/design/index?tab=custom',
content: [],
updated_at: '2026-01-28 10:15:00'
}
] as DesignItem[]
}
/**
* 首页装修页面路由配置
*/
export const designHomepagePageConfig = {
id: 'design-homepage',
path: '/pages/mall/admin/design/index?tab=homepage',
name: '首页装修',
title: '首页装修 - 打造吸引人的商城首页',
data: {
id: 'homepage',
name: '首页装修',
type: 'homepage' as const,
status: 1,
content: [
{
id: 'carousel-1',
type: 'carousel' as const,
name: '轮播图',
icon: 'C',
description: '首页顶部轮播图展示',
config: {
autoplay: true,
duration: 5000,
height: 300
}
},
{
id: 'product-1',
type: 'product' as const,
name: '商品展示',
icon: 'P',
description: '热销商品列表',
config: {
count: 8,
columns: 2,
layout: 'grid'
}
}
],
version: '1.0.0',
updated_at: '2026-01-30 14:30:00'
} as DesignItem
}
/**
* 分类页装修页面路由配置
*/
export const designCategoryPageConfig = {
id: 'design-category',
path: '/pages/mall/admin/design/index?tab=category',
name: '分类页装修',
title: '分类页装修 - 为不同分类创建独特展示',
data: [
{
id: 1,
name: '默认分类装修',
type: 'category' as const,
status: 1,
categoryId: 0,
categoryName: '全部分类',
path: '/pages/mall/admin/design/index?tab=category&id=1',
content: [],
updated_at: '2026-01-30 14:30:00'
},
{
id: 2,
name: '热销商品分类',
type: 'category' as const,
status: 0,
categoryId: 1,
categoryName: '推荐分类',
path: '/pages/mall/admin/design/index?tab=category&id=2',
content: [],
updated_at: '2026-01-29 10:15:00'
}
] as DesignItem[]
}
/**
* 商品页装修页面路由配置
*/
export const designProductPageConfig = {
id: 'design-product',
path: '/pages/mall/admin/design/index?tab=product',
name: '商品页装修',
title: '商品页装修 - 自定义商品详情页展示',
data: {
id: 'product',
name: '商品页装修',
type: 'product' as const,
status: 1,
content: [
{
id: 'image-1',
type: 'image' as const,
name: '商品图',
icon: 'I',
description: '商品主图展示'
},
{
id: 'product-info',
type: 'text' as const,
name: '商品信息',
icon: 'T',
description: '商品名称和价格'
}
],
version: '1.0.0',
updated_at: '2026-01-30 14:30:00'
} as DesignItem
}
/**
* 自定义页面路由配置
*/
export const designCustomPageConfig = {
id: 'design-custom',
path: '/pages/mall/admin/design/index?tab=custom',
name: '自定义页面',
title: '自定义页面 - 创建特殊内容页面',
data: [
{
id: 1,
name: '新年促销页',
type: 'custom' as const,
status: 1,
path: '/pages/mall/admin/design/index?tab=custom&id=1',
content: [],
updated_at: '2026-01-28 09:00:00'
}
] as DesignItem[]
}
/**
* 模板库页面路由配置
*/
export const designTemplatePageConfig = {
id: 'design-templates',
path: '/pages/mall/admin/design/index?tab=templates',
name: '模板库',
title: '模板库 - 选择预设装修模板',
data: [
{
id: 1,
name: '电商风格A',
description: '简洁现代的电商布局',
type: 'homepage',
preview: '/static/images/template-a.png',
content: []
},
{
id: 2,
name: '电商风格B',
description: '豪华展示的电商布局',
type: 'homepage',
preview: '/static/images/template-b.png',
content: []
},
{
id: 3,
name: '精品风格',
description: '精品商品展示布局',
type: 'homepage',
preview: '/static/images/template-c.png',
content: []
},
{
id: 4,
name: '商城风格',
description: '完整商城功能布局',
type: 'homepage',
preview: '/static/images/template-d.png',
content: []
}
] as DesignTemplate[]
}
/**
* 组件库页面路由配置
*/
export const designComponentPageConfig = {
id: 'design-components',
path: '/pages/mall/admin/design/index?tab=components',
name: '组件库',
title: '组件库 - 丰富的页面组件',
data: [
{
id: 'image',
type: 'image' as const,
name: '图片组件',
icon: 'I',
description: '展示图片和图片轮播',
componentName: 'ImageComponent',
config: {
defaultWidth: '100%',
defaultHeight: 'auto'
}
},
{
id: 'text',
type: 'text' as const,
name: '文本组件',
icon: 'T',
description: '展示文本内容和段落',
componentName: 'TextComponent'
},
{
id: 'product',
type: 'product' as const,
name: '商品组件',
icon: 'P',
description: '展示商品列表和推荐',
componentName: 'ProductComponent',
config: {
defaultCount: 6,
defaultColumns: 2
}
},
{
id: 'carousel',
type: 'carousel' as const,
name: '轮播组件',
icon: 'C',
description: '图片和内容轮播',
componentName: 'CarouselComponent',
config: {
autoplay: true,
duration: 5000
}
},
{
id: 'divider',
type: 'divider' as const,
name: '分割线',
icon: 'D',
description: '分割不同内容区域',
componentName: 'DividerComponent'
},
{
id: 'spacer',
type: 'spacer' as const,
name: '间距组件',
icon: 'S',
description: '调整元素间距',
componentName: 'SpacerComponent',
config: {
defaultHeight: 16
}
},
{
id: 'button',
type: 'button' as const,
name: '按钮组件',
icon: 'B',
description: '创建点击按钮',
componentName: 'ButtonComponent'
},
{
id: 'form',
type: 'form' as const,
name: '表单组件',
icon: 'F',
description: '收集用户输入数据',
componentName: 'FormComponent'
}
] as DesignComponent[]
}
/**
* 编辑页面路由配置
*/
export const designEditorPageConfig = {
id: 'design-editor',
path: '/pages/mall/admin/design/editor',
name: '装修编辑器',
title: '装修编辑器 - 可视化编辑装修页面',
components: [
{
id: 'canvas',
name: '编辑画布',
description: '拖拽编辑区域'
},
{
id: 'sidebar',
name: '组件侧栏',
description: '可用组件列表'
},
{
id: 'properties',
name: '属性面板',
description: '组件属性编辑'
},
{
id: 'preview',
name: '预览窗口',
description: '实时效果预览'
}
]
}
/**
* 预览页面路由配置
*/
export const designPreviewPageConfig = {
id: 'design-preview',
path: '/pages/mall/design/preview/:id',
name: '装修预览',
title: '装修效果预览',
features: [
'全屏预览',
'响应式展示',
'交互测试',
'性能分析'
]
}
/**
* 所有设计页面路由配置
*/
export const allDesignPageConfigs = [
designListPageConfig,
designHomepagePageConfig,
designCategoryPageConfig,
designProductPageConfig,
designCustomPageConfig,
designTemplatePageConfig,
designComponentPageConfig,
designEditorPageConfig,
designPreviewPageConfig
]
/**
* 根据 tab 获取对应的页面配置
*/
export function getDesignPageConfig(tab: string) {
const configMap: Record<string, any> = {
'homepage': designHomepagePageConfig,
'category': designCategoryPageConfig,
'product': designProductPageConfig,
'custom': designCustomPageConfig,
'templates': designTemplatePageConfig,
'components': designComponentPageConfig,
'editor': designEditorPageConfig,
'preview': designPreviewPageConfig,
'list': designListPageConfig
}
return configMap[tab] || designListPageConfig
}
/**
* 装修页面导航菜单结构
*/
export const designMenuStructure = {
id: 'design',
title: '设计',
icon: '/static/design.svg',
path: '/pages/mall/admin/design/index',
children: [
{
id: 'page-decoration',
title: '页面装修',
children: [
{
id: 'design-homepage',
title: '首页装修',
path: '/pages/mall/admin/design/index?tab=homepage'
},
{
id: 'design-category',
title: '分类页装修',
path: '/pages/mall/admin/design/index?tab=category'
},
{
id: 'design-product',
title: '商品页装修',
path: '/pages/mall/admin/design/index?tab=product'
},
{
id: 'design-custom',
title: '自定义页面',
path: '/pages/mall/admin/design/index?tab=custom'
}
]
},
{
id: 'design-library',
title: '设计库',
children: [
{
id: 'design-templates',
title: '模板库',
path: '/pages/mall/admin/design/index?tab=templates'
},
{
id: 'design-components',
title: '组件库',
path: '/pages/mall/admin/design/index?tab=components'
}
]
}
]
}

View File

@@ -0,0 +1,25 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('custom')
const title = ref<string>('custom')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,38 @@
<template>
<AdminLayout current-page="design-data">
<view class="admin-main">
<view class="header">
<text class="title">数据配置</text>
</view>
<view class="content">
<text>商城数据配置(建设中)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.admin-main {
padding: 20px;
}
.header {
margin-bottom: 20px;
}
.title {
font-size: 20px;
font-weight: bold;
}
.content {
background-color: #fff;
padding: 20px;
border-radius: 4px;
min-height: 400px;
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -0,0 +1,549 @@
/**
* 页面装修业务逻辑模块
* 参考CRMEB项目提供完整的装修管理功能
*/
/**
* 装修页面数据接口
*/
export interface DesignItem {
id: string | number
name: string
type: 'homepage' | 'category' | 'product' | 'custom'
status: 0 | 1 // 0: 草稿, 1: 已发布
categoryId?: string | number
categoryName?: string
path?: string
preview_url?: string
content: DesignComponent[]
version?: string
created_at?: string
updated_at?: string
}
/**
* 装修组件接口
*/
export interface DesignComponent {
id: string
type: 'image' | 'text' | 'product' | 'carousel' | 'divider' | 'spacer' | 'button' | 'form'
name: string
icon: string
description?: string
componentName?: string
config?: Record<string, any>
children?: DesignComponent[]
}
/**
* 装修模板接口
*/
export interface DesignTemplate {
id: string | number
name: string
description: string
type: string
preview: string
content: DesignComponent[]
created_at?: string
}
/**
* 获取装修页面列表
* @param params 查询参数
* @returns 装修页面列表
*/
export function getDesignList(params?: Record<string, any>): Promise<DesignItem[]> {
return new Promise((resolve, reject) => {
// TODO: 实际应调用后端API
const designList: DesignItem[] = [
{
id: 1,
name: '首页装修',
type: 'homepage',
status: 1,
content: [],
updated_at: '2026-01-30 14:30:00'
},
{
id: 2,
name: '年货节活动页',
type: 'custom',
status: 1,
content: [],
updated_at: '2026-01-28 10:15:00'
}
]
setTimeout(() => resolve(designList), 300)
})
}
/**
* 获取首页装修详情
* @returns 首页装修数据
*/
export function getHomePageDesign(): Promise<DesignItem> {
return new Promise((resolve, reject) => {
const homepage: DesignItem = {
id: 'homepage',
name: '首页装修',
type: 'homepage',
status: 1,
content: [
{
id: 'carousel-1',
type: 'carousel',
name: '轮播图',
icon: 'C',
description: '首页顶部轮播图展示',
config: {
autoplay: true,
duration: 5000,
height: 300
}
},
{
id: 'product-1',
type: 'product',
name: '商品展示',
icon: 'P',
description: '热销商品列表',
config: {
count: 8,
columns: 2,
layout: 'grid'
}
}
],
version: '1.0.0',
updated_at: '2026-01-30 14:30:00'
}
setTimeout(() => resolve(homepage), 300)
})
}
/**
* 获取商品页装修详情
* @returns 商品页装修数据
*/
export function getProductPageDesign(): Promise<DesignItem> {
return new Promise((resolve, reject) => {
const productPage: DesignItem = {
id: 'product',
name: '商品页装修',
type: 'product',
status: 1,
content: [
{
id: 'image-1',
type: 'image',
name: '商品图',
icon: 'I',
description: '商品主图展示'
},
{
id: 'product-info',
type: 'text',
name: '商品信息',
icon: 'T',
description: '商品名称和价格'
}
],
version: '1.0.0',
updated_at: '2026-01-30 14:30:00'
}
setTimeout(() => resolve(productPage), 300)
})
}
/**
* 获取分类装修列表
* @returns 分类装修列表
*/
export function getCategoryDesigns(): Promise<DesignItem[]> {
return new Promise((resolve, reject) => {
const categories: DesignItem[] = [
{
id: 1,
name: '默认分类装修',
type: 'category',
status: 1,
categoryId: 0,
categoryName: '全部分类',
content: [],
updated_at: '2026-01-30 14:30:00'
},
{
id: 2,
name: '热销商品分类',
type: 'category',
status: 0,
categoryId: 1,
categoryName: '推荐分类',
content: [],
updated_at: '2026-01-29 10:15:00'
}
]
setTimeout(() => resolve(categories), 300)
})
}
/**
* 获取自定义页面列表
* @returns 自定义页面列表
*/
export function getCustomPages(): Promise<DesignItem[]> {
return new Promise((resolve, reject) => {
const customPages: DesignItem[] = [
{
id: 1,
name: '新年促销页',
type: 'custom',
status: 1,
path: '/pages/promotion/newyear',
content: [],
updated_at: '2026-01-28 09:00:00'
}
]
setTimeout(() => resolve(customPages), 300)
})
}
/**
* 获取页面模板库
* @returns 模板列表
*/
export function getTemplateLibrary(): Promise<DesignTemplate[]> {
return new Promise((resolve, reject) => {
const templates: DesignTemplate[] = [
{
id: 1,
name: '电商风格A',
description: '简洁现代的电商布局',
type: 'homepage',
preview: '@/static/images/template-a.png',
content: []
},
{
id: 2,
name: '电商风格B',
description: '豪华展示的电商布局',
type: 'homepage',
preview: '@/static/images/template-b.png',
content: []
},
{
id: 3,
name: '精品风格',
description: '精品商品展示布局',
type: 'homepage',
preview: '@/static/images/template-c.png',
content: []
},
{
id: 4,
name: '商城风格',
description: '完整商城功能布局',
type: 'homepage',
preview: '@/static/images/template-d.png',
content: []
}
]
setTimeout(() => resolve(templates), 300)
})
}
/**
* 获取可用组件库
* @returns 组件列表
*/
export function getAvailableComponents(): Promise<DesignComponent[]> {
return new Promise((resolve, reject) => {
const components: DesignComponent[] = [
{
id: 'image',
type: 'image',
name: '图片组件',
icon: 'I',
description: '展示图片和图片轮播',
componentName: 'ImageComponent',
config: {
defaultWidth: '100%',
defaultHeight: 'auto'
}
},
{
id: 'text',
type: 'text',
name: '文本组件',
icon: 'T',
description: '展示文本内容和段落',
componentName: 'TextComponent'
},
{
id: 'product',
type: 'product',
name: '商品组件',
icon: 'P',
description: '展示商品列表和推荐',
componentName: 'ProductComponent',
config: {
defaultCount: 6,
defaultColumns: 2
}
},
{
id: 'carousel',
type: 'carousel',
name: '轮播组件',
icon: 'C',
description: '图片和内容轮播',
componentName: 'CarouselComponent',
config: {
autoplay: true,
duration: 5000
}
},
{
id: 'divider',
type: 'divider',
name: '分割线',
icon: 'D',
description: '分割不同内容区域',
componentName: 'DividerComponent'
},
{
id: 'spacer',
type: 'spacer',
name: '间距组件',
icon: 'S',
description: '调整元素间距',
componentName: 'SpacerComponent',
config: {
defaultHeight: 16
}
},
{
id: 'button',
type: 'button',
name: '按钮组件',
icon: 'B',
description: '创建点击按钮',
componentName: 'ButtonComponent'
},
{
id: 'form',
type: 'form',
name: '表单组件',
icon: 'F',
description: '收集用户输入数据',
componentName: 'FormComponent'
}
]
setTimeout(() => resolve(components), 300)
})
}
/**
* 保存装修页面
* @param design 装修数据
* @returns 保存结果
*/
export function saveDesign(design: DesignItem): Promise<{ id: string | number; message: string }> {
return new Promise((resolve, reject) => {
if (!design.name || design.name.trim() === '') {
reject(new Error('装修名称不能为空'))
return
}
if (!design.type) {
reject(new Error('装修类型不能为空'))
return
}
// TODO: 实际应调用后端API保存
const result = {
id: design.id || Math.random().toString(36).substr(2, 9),
message: '保存成功'
}
setTimeout(() => resolve(result), 500)
})
}
/**
* 发布装修页面
* @param designId 装修页面ID
* @returns 发布结果
*/
export function publishDesign(designId: string | number): Promise<{ message: string }> {
return new Promise((resolve, reject) => {
if (!designId) {
reject(new Error('装修ID不能为空'))
return
}
// TODO: 实际应调用后端API发布
setTimeout(() => {
resolve({ message: '发布成功' })
}, 500)
})
}
/**
* 删除装修页面
* @param designId 装修页面ID
* @returns 删除结果
*/
export function deleteDesign(designId: string | number): Promise<{ message: string }> {
return new Promise((resolve, reject) => {
if (!designId) {
reject(new Error('装修ID不能为空'))
return
}
// TODO: 实际应调用后端API删除
setTimeout(() => {
resolve({ message: '删除成功' })
}, 500)
})
}
/**
* 获取装修预览URL
* @param designId 装修ID
* @returns 预览URL
*/
export function getDesignPreviewUrl(designId: string | number): string {
return `/pages/mall/design/preview/${designId}`
}
/**
* 获取装修编辑URL
* @param designId 装修ID
* @returns 编辑URL
*/
export function getDesignEditorUrl(designId: string | number): string {
return `/pages/mall/admin/design/editor?id=${designId}`
}
/**
* 格式化日期时间
* @param dateStr 日期字符串
* @returns 格式化后的日期
*/
export function formatDateTime(dateStr?: string): string {
if (!dateStr) return '--'
try {
const date = new Date(dateStr)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}`
} catch {
return dateStr
}
}
/**
* 验证组件配置
* @param component 组件配置
* @returns 验证结果
*/
export function validateComponent(component: DesignComponent): { valid: boolean; errors: string[] } {
const errors: string[] = []
if (!component.id) {
errors.push('组件ID不能为空')
}
if (!component.type) {
errors.push('组件类型不能为空')
}
if (!component.name) {
errors.push('组件名称不能为空')
}
return {
valid: errors.length === 0,
errors
}
}
/**
* 生成组件ID
* @param type 组件类型
* @returns 生成的组件ID
*/
export function generateComponentId(type: string): string {
const timestamp = Date.now().toString(36)
const random = Math.random().toString(36).substr(2, 5)
return `${type}-${timestamp}-${random}`
}
/**
* 获取装修约束条件
* @returns 约束条件对象
*/
export function getDesignConstraints(): DesignConstraints {
return {
maxComponents: 50,
allowedComponentTypes: ['image', 'text', 'product', 'carousel', 'divider', 'spacer', 'button', 'form'],
maxImageSize: 5242880, // 5MB
supportedImageFormats: ['jpg', 'jpeg', 'png', 'gif', 'webp']
}
}
/**
* 深度克隆装修数据
* @param design 装修数据
* @returns 克隆后的数据
*/
export function cloneDesign(design: DesignItem): DesignItem {
return JSON.parse(JSON.stringify(design))
}
/**
* 验证装修数据完整性
* @param design 装修数据
* @returns 验证结果
*/
export function validateDesign(design: DesignItem): { valid: boolean; message: string } {
if (!design.name || design.name.trim() === '') {
return { valid: false, message: '装修名称不能为空' }
}
if (!design.type) {
return { valid: false, message: '装修类型不能为空' }
}
if (!Array.isArray(design.content)) {
return { valid: false, message: '装修内容格式错误' }
}
if (design.content.length > getDesignConstraints().maxComponents) {
return { valid: false, message: `组件数量超过限制(最多${getDesignConstraints().maxComponents}个)` }
}
return { valid: true, message: '验证通过' }
}
/**
* 导出装修为JSON
* @param design 装修数据
* @returns JSON字符串
*/
export function exportDesignJSON(design: DesignItem): string {
return JSON.stringify(design, null, 2)
}
/**
* 从JSON导入装修
* @param jsonStr JSON字符串
* @returns 装修数据
*/
export function importDesignJSON(jsonStr: string): DesignItem {
try {
return JSON.parse(jsonStr) as DesignItem
} catch (error) {
throw new Error('JSON格式错误无法导入')
}
}

View File

@@ -0,0 +1,25 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('design-homepage')
const title = ref<string>('首页装修')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,65 @@
<template>
<AdminLayout currentPage="design-home">
<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>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
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,38 @@
<template>
<AdminLayout current-page="design-link">
<view class="admin-main">
<view class="header">
<text class="title">链接管理</text>
</view>
<view class="content">
<text>商城链接管理(建设中)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.admin-main {
padding: 20px;
}
.header {
margin-bottom: 20px;
}
.title {
font-size: 20px;
font-weight: bold;
}
.content {
background-color: #fff;
padding: 20px;
border-radius: 4px;
min-height: 400px;
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -0,0 +1,38 @@
<template>
<AdminLayout current-page="design-material">
<view class="admin-main">
<view class="header">
<text class="title">素材管理</text>
</view>
<view class="content">
<text>商城素材管理(建设中)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.admin-main {
padding: 20px;
}
.header {
margin-bottom: 20px;
}
.title {
font-size: 20px;
font-weight: bold;
}
.content {
background-color: #fff;
padding: 20px;
border-radius: 4px;
min-height: 400px;
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -0,0 +1,25 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('product')
const title = ref<string>('product')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,162 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="design-container">
<view class="module-header">
<text class="module-title">模板库</text>
<text class="module-desc">从丰富的模板中快速创建页面</text>
</view>
<view class="templates-grid">
<view v-for="template in templateLibrary" :key="template.id" class="template-card">
<view class="template-header">
<text class="template-name">{{ template.name }}</text>
</view>
<view class="template-body">
<text class="template-desc">{{ template.description }}</text>
<button class="btn-use" @click="handleUseTemplate(template.id)">使用模板</button>
</view>
</view>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('design-templates')
const templateLibrary = ref<any[]>([
{
id: 1,
name: '首页模板A',
description: '经典电商首页布局'
},
{
id: 2,
name: '首页模板B',
description: '简约风格的商城页面'
},
{
id: 3,
name: '活动模板',
description: '活动促销页面布局'
},
{
id: 4,
name: '商品模板',
description: '商品展示页面布局'
}
])
const handleUseTemplate = (templateId: number) => {
console.log('使用模板', templateId)
uni.showToast({
title: '使用模板成功',
icon: 'none'
})
}
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.design-container {
min-height: 100vh;
background: $background-secondary;
padding: $space-lg;
}
.module-header {
margin-bottom: $space-xl;
}
.module-title {
font-size: $font-size-lg;
font-weight: bold;
color: $text-primary;
display: block;
margin-bottom: $space-sm;
}
.module-desc {
font-size: $font-size-sm;
color: $text-secondary;
}
.templates-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: $space-lg;
margin-bottom: $space-lg;
}
.template-card {
background: white;
border-radius: $radius-lg;
box-shadow: $shadow-sm;
overflow: hidden;
transition: all 0.3s ease;
&:hover {
box-shadow: $shadow-md;
transform: translateY(-2px);
}
}
.template-header {
padding: $space-lg;
background: $background-tertiary;
text-align: center;
}
.template-body {
padding: $space-lg;
}
.template-name {
font-size: $font-size-md;
font-weight: bold;
color: $text-primary;
display: block;
margin-bottom: $space-sm;
}
.template-desc {
color: $text-secondary;
font-size: $font-size-sm;
line-height: 1.5;
display: block;
margin-bottom: $space-lg;
}
.btn-use {
background: $primary-color;
color: white;
padding: $space-sm $space-lg;
border-radius: $radius-sm;
border: none;
margin-top: $space-lg;
font-size: $font-size-sm;
cursor: pointer;
width: 100%;
}
@media (max-width: 768px) {
.design-container {
padding: $space-md;
}
.templates-grid {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
}
@media (max-width: 480px) {
.templates-grid {
grid-template-columns: 1fr;
}
}
</style>

View File

@@ -0,0 +1,38 @@
<template>
<AdminLayout current-page="design-theme">
<view class="admin-main">
<view class="header">
<text class="title">主题风格</text>
</view>
<view class="content">
<text>商城主题风格设置(建设中)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.admin-main {
padding: 20px;
}
.header {
margin-bottom: 20px;
}
.title {
font-size: 20px;
font-weight: bold;
}
.content {
background-color: #fff;
padding: 20px;
border-radius: 4px;
min-height: 400px;
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -0,0 +1,38 @@
<template>
<AdminLayout current-page="design-user">
<view class="admin-main">
<view class="header">
<text class="title">个人中心装修</text>
</view>
<view class="content">
<text>个人中心页面装修(建设中)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.admin-main {
padding: 20px;
}
.header {
margin-bottom: 20px;
}
.title {
font-size: 20px;
font-weight: bold;
}
.content {
background-color: #fff;
padding: 20px;
border-radius: 4px;
min-height: 400px;
display: flex;
justify-content: center;
align-items: center;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
<template>
<view class="page-container">
<view class="page-header">
<text class="page-title">财务记录</text>
<text class="page-subtitle">Component: FinanceRecord</text>
</view>
<view class="page-content">
<view class="placeholder-card">
<text class="placeholder-title">页面占位</text>
<text class="placeholder-desc">该功能模块正在开发中</text>
<text class="placeholder-info">当前采用 CRMEB 路由体系 1:1 映射</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
// TODO: 实现 财务记录 的具体功能
const loading = ref<boolean>(false)
</script>
<style scoped lang="scss">
.page-container {
padding: 20px;
min-height: 100vh;
background: #f5f5f5;
}
.page-header {
margin-bottom: 20px;
}
.page-title {
display: block;
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.page-subtitle {
display: block;
font-size: 14px;
color: #999;
}
.page-content {
background: #fff;
border-radius: 4px;
padding: 24px;
}
.placeholder-card {
text-align: center;
padding: 60px 20px;
}
.placeholder-title {
display: block;
font-size: 18px;
font-weight: 600;
color: #666;
margin-bottom: 12px;
}
.placeholder-desc {
display: block;
font-size: 14px;
color: #999;
margin-bottom: 8px;
}
.placeholder-info {
display: block;
font-size: 12px;
color: #1890ff;
}
</style>

View File

@@ -1,494 +1,24 @@
<template>
<AdminLayout current-page="dashboard">
<view class="dashboard-page">
<!-- 第一行4 个 KPI 卡片 -->
<view class="kpi-cards-row">
<KpiMiniCard
title="销售额"
tagText="今日"
valuePrefix="¥"
:valueText="String(formatNumber(kpiData.sales.today))"
:metaLeft="`昨日 ${formatNumber(kpiData.sales.yesterday)}`"
:metaRight="`日环比 ${Math.abs(kpiData.sales.change)}%`"
:trend="kpiData.sales.change > 0 ? 'up' : (kpiData.sales.change < 0 ? 'down' : 'flat')"
:footerLeftText="'本月销售额'"
:footerRightText="`¥${formatNumber(kpiData.sales.monthTotal)}`"
/>
<KpiMiniCard
title="用户访问量"
tagText="今日"
:valueText="String(formatNumber(kpiData.visits.today))"
:metaLeft="`昨日 ${formatNumber(kpiData.visits.yesterday)}`"
:metaRight="`日环比 ${Math.abs(kpiData.visits.change)}%`"
:trend="kpiData.visits.change > 0 ? 'up' : (kpiData.visits.change < 0 ? 'down' : 'flat')"
footerLeftText="本月访问量"
:footerRightText="`${formatNumber(kpiData.visits.monthTotal)}Pv`"
/>
<KpiMiniCard
title="订单量"
tagText="今日"
:valueText="String(formatNumber(kpiData.orders.today))"
:metaLeft="`昨日 ${formatNumber(kpiData.orders.yesterday)}`"
:metaRight="`日环比 ${Math.abs(kpiData.orders.change)}%`"
:trend="kpiData.orders.change > 0 ? 'up' : (kpiData.orders.change < 0 ? 'down' : 'flat')"
footerLeftText="本月订单量"
:footerRightText="`${formatNumber(kpiData.orders.monthTotal)}单`"
/>
<KpiMiniCard
title="新增用户"
tagText="今日"
:valueText="String(formatNumber(kpiData.users.today))"
:metaLeft="`昨日 ${formatNumber(kpiData.users.yesterday)}`"
:metaRight="`日环比 ${Math.abs(kpiData.users.change)}%`"
:trend="kpiData.users.change > 0 ? 'up' : (kpiData.users.change < 0 ? 'down' : 'flat')"
footerLeftText="本月新增用户"
:footerRightText="`${formatNumber(kpiData.users.monthTotal)}人`"
/>
</view>
<!-- 第二行:订单统计图表 -->
<view class="chart-section">
<view class="admin-card">
<view class="admin-card-header">
<text class="admin-card-title">订单</text>
<view class="chart-controls">
<button
v-for="period in chartPeriods"
:key="period.value"
class="period-btn"
:class="{ 'active': selectedPeriod === period.value }"
@click="changePeriod(period.value)"
>
{{ period.label }}
</button>
</view>
</view>
<view class="admin-card-body">
<!-- ECharts 组合图容器 -->
<view class="echarts-container">
<text class="chart-placeholder">📊 ECharts 组合图:柱状图(订单金额) + 折线图(订单数量)</text>
<text class="chart-desc">时间粒度:{{ selectedPeriodLabel }}</text>
</view>
</view>
</view>
</view>
<!-- 第三行:用户统计图表 -->
<view class="charts-row">
<!-- 用户趋势折线图 -->
<view class="chart-col">
<view class="admin-card">
<view class="admin-card-header">
<text class="admin-card-title">用户趋势</text>
</view>
<view class="admin-card-body">
<view class="echarts-container">
<text class="chart-placeholder">📈 ECharts 折线图:用户增长趋势</text>
</view>
</view>
</view>
</view>
<!-- 用户构成饼图 -->
<view class="chart-col">
<view class="admin-card">
<view class="admin-card-header">
<text class="admin-card-title">用户构成</text>
</view>
<view class="admin-card-body">
<view class="echarts-container">
<text class="chart-placeholder">🥧 ECharts 饼图:用户来源分布</text>
</view>
</view>
</view>
</view>
</view>
</view>
</AdminLayout>
<!-- 管理后台入口:直接加载 AdminLayout使用 CRMEB 内部路由系统 -->
<AdminLayout />
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/index.uvue'
import KpiMiniCard from './components/KpiMiniCard.uvue'
// KPI 数据
const kpiData = ref({
sales: {
today: 125680.50,
yesterday: 118920.30,
monthTotal: 2857808.90,
change: 5.7
},
visits: {
today: 15420,
yesterday: 14890,
monthTotal: 342680,
change: 3.4
},
orders: {
today: 342,
yesterday: 318,
monthTotal: 8956,
change: 7.5
},
users: {
today: 156,
yesterday: 142,
monthTotal: 3245,
change: 9.9
}
})
// 图表配置
const selectedPeriod = ref('30days')
const selectedPeriodLabel = ref('30天')
const chartPeriods = [
{ label: '30天', value: '30days' },
{ label: '周', value: 'week' },
{ label: '月', value: 'month' },
{ label: '年', value: 'year' }
]
// 方法
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()
}
const changePeriod = (period: string) => {
selectedPeriod.value = period
const periodMap: Record<string, string> = {
'30days': '30天',
'week': '周',
'month': '月',
'year': '年'
}
selectedPeriodLabel.value = periodMap[period] || '30天'
// TODO: 重新加载图表数据
console.log('切换时间粒度:', period)
}
/**
* 管理后台入口页面
*
* 架构说明:
* 1. 此页面是 pages.json 中配置的主入口
* 2. 直接加载 AdminLayout 组件作为容器
* 3. AdminLayout 内部使用 CRMEB 路由系统管理所有子页面
* 4. 不需要额外的业务逻辑,保持简洁
*
* 路由流程:
* pages.json → homePage/index.uvue → AdminLayout → 内部路由切换
*/
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style>
/* ===== Dashboard 页面样式 ===== */
.dashboard-page {
width: 100%;
}
/* ===== KPI 卡片行 ===== */
/* 第一行4 个 KPI 卡片一行 */
.kpi-cards-row{
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr)); /* 一行 4 列等分 */
gap: 16px;
align-items: stretch;
}
/* 卡片本体:不要写死宽高 */
.kpi-card{
background-color: #ffffff;
border: 1px solid #e8e8e8;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
position: relative;
overflow: hidden;
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 200px; /* 你可以改成 140/160别写死 200px */
box-sizing: border-box;
margin-bottom: 20rpx;
min-width: 200rpx;
}
/* 响应式:宽度不够时变 2 列 / 1 列(可选) */
@media (max-width: 1200px){
.kpi-cards-row{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 768px){
.kpi-cards-row{ grid-template-columns: 1fr; }
}
.kpi-card-content {
flex: 1;
}
.kpi-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.kpi-card-title {
position: absolute;
top: 10rpx;
left: 5rpx;
font-size: 16px;
color: #666666;
margin-right: 12px;
}
.kpi-card-tag {
background-color: #1890ff;
padding: 2px 8px;
border-radius: 12px;
}
.kpi-tag-text {
font-size: 12px;
color: #ffffff;
}
.kpi-card-value {
display: flex;
align-items: center;
margin-bottom: 16px;
}
.kpi-value-number {
font-size: 32px;
font-weight: 600;
color: #262626;
margin-right: 16px;
}
.kpi-value-trend {
display: flex;
align-items: center;
font-size: 14px;
border-radius: 12px;
padding: 4px 8px;
}
.kpi-value-trend.up {
background-color: #f6ffed;
color: #52c41a;
}
.kpi-value-trend.down {
background-color: #fff2f0;
color: #ff4d4f;
}
.kpi-trend-text {
margin-left: 4px;
font-weight: 500;
}
.kpi-card-footer {
display: flex;
justify-content: space-between;
}
.kpi-footer-text {
font-size: 14px;
color: #999999;
}
.kpi-card-icon {
width: 64px;
height: 64px;
background: linear-gradient(135deg, #1890ff 0%, #36cfc9 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 32px;
flex-shrink: 0;
}
/* ===== 图表区域 ===== */
.chart-section {
margin-bottom: 24px;
}
.charts-row {
display: flex;
gap: 24px;
}
.chart-col {
flex: 1;
}
/* ===== Admin Card 组件样式 ===== */
.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;
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-controls {
display: flex;
gap: 12px;
}
.period-btn {
padding: 6px 16px;
border: 1px solid #d9d9d9;
background-color: #ffffff;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
}
.period-btn:hover {
border-color: #1890ff;
color: #1890ff;
}
.period-btn.active {
background-color: #1890ff;
color: #ffffff;
border-color: #1890ff;
}
/* ===== ECharts 容器 ===== */
.echarts-container {
height: 350px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #fafafa;
border: 1px solid #e8e8e8;
border-radius: 6px;
}
.chart-placeholder {
font-size: 16px;
color: #666666;
text-align: center;
margin-bottom: 8px;
}
.chart-desc {
font-size: 14px;
color: #999999;
text-align: center;
}
/* ===== 响应式设计 ===== */
@media (max-width: 1200px) {
.kpi-cards-row {
flex-wrap: wrap;
}
.kpi-card {
min-width: 45%;
flex: 0 0 auto;
}
}
@media (max-width: 768px) {
.kpi-cards-row {
flex-direction: column;
}
.kpi-card {
min-width: auto;
width: 100%;
}
.charts-row {
flex-direction: column;
}
.dashboard-page {
padding: 16px;
}
.kpi-cards-row,
.chart-section,
.charts-row {
margin-bottom: 16px;
}
.kpi-card {
padding: 16px;
}
.admin-card-header,
.admin-card-body {
padding-left: 16px;
padding-right: 16px;
}
}
/* ===== 图标字体 ===== */
.iconfont {
font-family: 'iconfont';
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-up:before {
content: '↑';
}
.icon-down:before {
content: '↓';
}
.icon-sales:before {
content: '💰';
}
.icon-visits:before {
content: '👁️';
}
.icon-orders:before {
content: '📦';
}
.icon-users:before {
content: '👥';
}
</style>
<style scoped>
/* 无需额外样式,完全由 AdminLayout 控制布局 */
</style>

View File

@@ -0,0 +1,483 @@
<template>
<!-- 直接加载 AdminLayout使用 CRMEB 内部路由系统 -->
<AdminLayout />
<!-- 第二行:订单统计图表 -->
<view class="chart-section">
<view class="admin-card">
<view class="admin-card-header">
<view class="header-left">
<view class="title-icon">
<!-- 不用 emoji纯样式画一个“图表感”的小方块 -->
<view class="title-icon-mark"></view>
</view>
<text class="admin-card-title">订单</text>
</view>
<view class="chart-controls">
<view
v-for="p in chartPeriods"
:key="p.value"
class="seg-btn"
:class="{ active: selectedPeriod === p.value }"
@click="changePeriod(p.value)"
>
<text class="seg-btn-text">{{ p.label }}</text>
</view>
</view>
</view>
<view class="admin-card-body">
<!-- 图表容器:你后面接 ECharts / uCharts 都挂这里 -->
<view class="echarts-container">
<!-- 先空着也行;不要放 emoji 占位符 -->
111
</view>
</view>
</view>
</view>
<!-- 第三行:用户统计图表 -->
<view class="charts-row">
<!-- 用户趋势折线图 -->
<view class="chart-col">
<view class="admin-card">
<view class="admin-card-header">
<text class="admin-card-title">用户趋势</text>
</view>
<view class="admin-card-body">
<view class="echarts-container">
<text class="chart-placeholder">📈 ECharts 折线图:用户增长趋势</text>
</view>
</view>
</view>
</view>
<!-- 用户构成饼图 -->
<view class="chart-col">
<view class="admin-card">
<view class="admin-card-header">
<text class="admin-card-title">用户构成</text>
</view>
<view class="admin-card-body">
<view class="echarts-container">
<text class="chart-placeholder">🥧 ECharts 饼图:用户来源分布</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import KpiMiniCard from './components/KpiMiniCard.uvue'
// KPI 数据
const kpiData = ref({
sales: {
today: 125680.50,
yesterday: 118920.30,
monthTotal: 2857808.90,
change: 5.7
},
visits: {
today: 15420,
yesterday: 14890,
monthTotal: 342680,
change: 3.4
},
orders: {
today: 342,
yesterday: 318,
monthTotal: 8956,
change: 7.5
},
users: {
today: 156,
yesterday: 142,
monthTotal: 3245,
change: 9.9
}
})
// 图表配置
const selectedPeriod = ref('30days')
const selectedPeriodLabel = computed((): string => {
const hit = chartPeriods.value.find((x) => x.value === selectedPeriod.value)
return hit ? hit.label : ""
})
const chartPeriods = [
{ label: '30天', value: '30days' },
{ label: '周', value: 'week' },
{ label: '月', value: 'month' },
{ label: '年', value: 'year' }
]
type PeriodItem = {
label: string
value: string
}
// 方法
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()
}
const changePeriod = (period: string) => {
selectedPeriod.value = period
const periodMap: Record<string, string> = {
'30days': '30天',
'week': '周',
'month': '月',
'year': '年'
}
selectedPeriodLabel.value = periodMap[period] || '30天'
// TODO: 重新加载图表数据
console.log('切换时间粒度:', period)
}
</script>
<style>
/* ===== Dashboard 页面样式 ===== */
.dashboard-page {
width: 100%;
}
/* ===== KPI 卡片行 ===== */
/* 第一行4 个 KPI 卡片一行 */
.kpi-cards-row{
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr)); /* 一行 4 列等分 */
gap: 16px;
align-items: stretch;
}
/* 卡片本体:不要写死宽高 */
.kpi-card{
background-color: #ffffff;
border: 1px solid #e8e8e8;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
position: relative;
overflow: hidden;
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 200px; /* 你可以改成 140/160别写死 200px */
box-sizing: border-box;
margin-bottom: 20rpx;
min-width: 200rpx;
}
/* 响应式:宽度不够时变 2 列 / 1 列(可选) */
@media (max-width: 1200px){
.kpi-cards-row{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 768px){
.kpi-cards-row{ grid-template-columns: 1fr; }
}
.kpi-card-content {
flex: 1;
}
.kpi-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.kpi-card-title {
position: absolute;
top: 10rpx;
left: 5rpx;
font-size: 16px;
color: #666666;
margin-right: 12px;
}
.kpi-card-tag {
background-color: #1890ff;
padding: 2px 8px;
border-radius: 12px;
}
.kpi-tag-text {
font-size: 12px;
color: #ffffff;
}
.kpi-card-value {
display: flex;
align-items: center;
margin-bottom: 16px;
}
.kpi-value-number {
font-size: 32px;
font-weight: 600;
color: #262626;
margin-right: 16px;
}
.kpi-value-trend {
display: flex;
align-items: center;
font-size: 14px;
border-radius: 12px;
padding: 4px 8px;
}
.kpi-value-trend.up {
background-color: #f6ffed;
color: #52c41a;
}
.kpi-value-trend.down {
background-color: #fff2f0;
color: #ff4d4f;
}
.kpi-trend-text {
margin-left: 4px;
font-weight: 500;
}
.kpi-card-footer {
display: flex;
justify-content: space-between;
}
.kpi-footer-text {
font-size: 14px;
color: #999999;
}
.kpi-card-icon {
width: 64px;
height: 64px;
background: linear-gradient(135deg, #1890ff 0%, #36cfc9 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 32px;
flex-shrink: 0;
}
/* ===== 图表区域 ===== */
/* 卡片外观 */
.admin-card {
background: #ffffff;
border-radius: 8px;
border: 1px solid #f0f0f0;
}
/* 头部:左标题 + 右分段按钮(不换行) */
.admin-card-header {
padding: 16px 24px 12px 24px;
display: flex;
flex-direction: row;;
justify-content: space-between;
align-items: center;
flex-wrap: nowrap; /* 防止被挤下去 */
}
.header-left {
display: flex;
flex-direction: row;;
align-items: center;
gap: 10px;
min-width: 0;
}
.title-icon {
width: 28px;
height: 28px;
border-radius: 14px;
background: #e6f4ff;
display: flex;
align-items: center;
justify-content: center;
}
.title-icon-mark {
width: 14px;
height: 14px;
border-radius: 4px;
background: #1677ff;
}
.admin-card-title {
font-size: 18px;
font-weight: 600;
color: #262626;
white-space: nowrap;
}
/* 分段控件:一整条外框 + 内部分段(完全贴近你第二张图右上角) */
.chart-controls {
display: flex;
flex-direction: row;;
align-items: center;
justify-content: center;;
border: 1px solid #d9d9d9;
border-radius: 4px;
overflow: hidden;
background: #ffffff;
flex-shrink: 0; /* 防止被压缩换行 */
}
.seg-btn {
height: 32px;
min-width: 44px;
padding: 0 14px;
display: flex;
align-items: center;
justify-content: center;
border-left: 1px solid #d9d9d9;
background: #ffffff;
}
.seg-btn:first-child {
border-left: 0;
}
.seg-btn-text {
font-size: 14px;
color: #262626;
line-height: 1;
}
.seg-btn.active {
background: #1677ff;
}
.seg-btn.active .seg-btn-text {
color: #ffffff;
}
/* ✅ 注意body 是 header 的兄弟,不要写进 header 嵌套里 */
.admin-card-body {
padding: 0 24px 16px 24px;
}
.echarts-container {
width: 100%;
height: 300px; /* 贴近截图比例 */
}
.charts-row{
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
margin-top: 16px;
}
/* 每个图表列容器 */
.chart-col{
min-width: 0; /* 防止 ECharts/SVG 内容把列撑爆 */
}
/* ===== 响应式设计 ===== */
@media (max-width: 1200px) {
.kpi-card {
min-width: 45%;
flex: 0 0 auto;
}
.charts-row{
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.kpi-cards-row {
flex-direction: column;
}
.kpi-card {
min-width: auto;
width: 100%;
}
.dashboard-page {
padding: 16px;
}
.kpi-cards-row,
.chart-section,
.charts-row {
margin-bottom: 16px;
}
.kpi-card {
padding: 16px;
}
.admin-card-header,
.admin-card-body {
padding-left: 16px;
padding-right: 16px;
}
}
/* ===== 图标字体 ===== */
.iconfont {
font-family: 'iconfont';
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-up:before {
content: '↑';
}
.icon-down:before {
content: '↓';
}
.icon-sales:before {
content: '💰';
}
.icon-visits:before {
content: '👁️';
}
.icon-orders:before {
content: '📦';
}
.icon-users:before {
content: '👥';
}
</style>

View File

@@ -0,0 +1,13 @@
<template>
<AdminLayout />
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
// AdminLayout 现在采用内部路由模式
// 所有页面内容在 AdminLayout 内部切换,不再使用 uni.navigateTo
</script>
<style>
</style>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,13 @@
<template>
<AdminLayout currentPage="dev-tools-codegen">
<view class="page">
<view class="header">
<text class="title">代码生成</text>
</view>
<view class="content">
<text class="tip">TODO: 代码生成</text>
</view>
</view>
</AdminLayout>
</template>

View File

@@ -0,0 +1,12 @@
<template>
<AdminLayout currentPage="dev-tools-dict">
<view class="page">
<view class="header">
<text class="title">数据字典</text>
</view>
<view class="content">
<text class="tip">TODO: 数据字典</text>
</view>
</view>
</AdminLayout>
</template>

View File

@@ -0,0 +1,12 @@
<template>
<AdminLayout currentPage="dev-tools-db">
<view class="page">
<view class="header">
<text class="title">数据库管理</text>
</view>
<view class="content">
<text class="tip">TODO: 数据库管理</text>
</view>
</view>
</AdminLayout>
</template>

View File

@@ -0,0 +1,12 @@
<template>
<AdminLayout currentPage="dev-tools-file">
<view class="page">
<view class="header">
<text class="title">文件管理</text>
</view>
<view class="content">
<text class="tip">TODO: 文件管理</text>
</view>
</view>
</AdminLayout>
</template>

View File

@@ -0,0 +1,13 @@
<template>
<AdminLayout currentPage="external-account">
<view class="page">
<view class="header">
<text class="title">账号管理</text>
</view>
<view class="content">
<text class="tip">TODO: 账号管理</text>
</view>
</view>
</AdminLayout>
</template>

View File

@@ -0,0 +1,12 @@
<template>
<AdminLayout currentPage="i18n-language-detail">
<view class="page">
<view class="header">
<text class="title">语言详情</text>
</view>
<view class="content">
<text class="tip">TODO: 语言详情</text>
</view>
</view>
</AdminLayout>
</template>

View File

@@ -0,0 +1,12 @@
<template>
<AdminLayout currentPage="i18n-language-list">
<view class="page">
<view class="header">
<text class="title">语言列表</text>
</view>
<view class="content">
<text class="tip">TODO: 语言列表</text>
</view>
</view>
</AdminLayout>
</template>

View File

@@ -0,0 +1,12 @@
<template>
<AdminLayout currentPage="i18n-region-list">
<view class="page">
<view class="header">
<text class="title">地区列表</text>
</view>
<view class="content">
<text class="tip">TODO: 地区列表</text>
</view>
</view>
</AdminLayout>
</template>

View File

@@ -0,0 +1,12 @@
<template>
<AdminLayout currentPage="i18n-translate-config">
<view class="page">
<view class="header">
<text class="title">翻译配置</text>
</view>
<view class="content">
<text class="tip">TODO: 翻译配置</text>
</view>
</view>
</AdminLayout>
</template>

View File

@@ -0,0 +1,12 @@
<template>
<AdminLayout currentPage="security-online-upgrade">
<view class="page">
<view class="header">
<text class="title">在线升级</text>
</view>
<view class="content">
<text class="tip">TODO: 在线升级</text>
</view>
</view>
</AdminLayout>
</template>

View File

@@ -0,0 +1,12 @@
<template>
<AdminLayout currentPage="security-refresh-cache">
<view class="page">
<view class="header">
<text class="title">刷新缓存</text>
</view>
<view class="content">
<text class="tip">TODO: 刷新缓存</text>
</view>
</view>
</AdminLayout>
</template>

View File

@@ -0,0 +1,12 @@
<template>
<AdminLayout currentPage="security-system-log">
<view class="page">
<view class="header">
<text class="title">系统日志</text>
</view>
<view class="content">
<text class="tip">TODO: 系统日志</text>
</view>
</view>
</AdminLayout>
</template>

View File

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

View File

@@ -0,0 +1,64 @@
<template>
<AdminLayout currentPage="marketing">
<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>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
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,81 @@
<template>
<view class="page-container">
<view class="page-header">
<text class="page-title">砍价活动</text>
<text class="page-subtitle">Component: MarketingBargain</text>
</view>
<view class="page-content">
<view class="placeholder-card">
<text class="placeholder-title">页面占位</text>
<text class="placeholder-desc">该功能模块正在开发中</text>
<text class="placeholder-info">当前采用 CRMEB 路由体系 1:1 映射</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
// TODO: 实现 砍价活动 的具体功能
const loading = ref<boolean>(false)
</script>
<style scoped lang="scss">
.page-container {
padding: 20px;
min-height: 100vh;
background: #f5f5f5;
}
.page-header {
margin-bottom: 20px;
}
.page-title {
display: block;
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.page-subtitle {
display: block;
font-size: 14px;
color: #999;
}
.page-content {
background: #fff;
border-radius: 4px;
padding: 24px;
}
.placeholder-card {
text-align: center;
padding: 60px 20px;
}
.placeholder-title {
display: block;
font-size: 18px;
font-weight: 600;
color: #666;
margin-bottom: 12px;
}
.placeholder-desc {
display: block;
font-size: 14px;
color: #999;
margin-bottom: 8px;
}
.placeholder-info {
display: block;
font-size: 12px;
color: #1890ff;
}
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page-container">
<view class="page-header">
<text class="page-title">拼团活动</text>
<text class="page-subtitle">Component: MarketingCombination</text>
</view>
<view class="page-content">
<view class="placeholder-card">
<text class="placeholder-title">页面占位</text>
<text class="placeholder-desc">该功能模块正在开发中</text>
<text class="placeholder-info">当前采用 CRMEB 路由体系 1:1 映射</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
// TODO: 实现 拼团活动 的具体功能
const loading = ref<boolean>(false)
</script>
<style scoped lang="scss">
.page-container {
padding: 20px;
min-height: 100vh;
background: #f5f5f5;
}
.page-header {
margin-bottom: 20px;
}
.page-title {
display: block;
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.page-subtitle {
display: block;
font-size: 14px;
color: #999;
}
.page-content {
background: #fff;
border-radius: 4px;
padding: 24px;
}
.placeholder-card {
text-align: center;
padding: 60px 20px;
}
.placeholder-title {
display: block;
font-size: 18px;
font-weight: 600;
color: #666;
margin-bottom: 12px;
}
.placeholder-desc {
display: block;
font-size: 14px;
color: #999;
margin-bottom: 8px;
}
.placeholder-info {
display: block;
font-size: 12px;
color: #1890ff;
}
</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,65 @@
<template>
<view class="container">
<text class="title">优惠券列表</text>
</view>
</template>
<AdminLayout currentPage="coupon-list">
<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>
</AdminLayout>
</template>
<script setup lang="uts">
// Minimal script
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
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,65 @@
<template>
<view class="container">
<text class="title">用户领取记录</text>
</view>
</template>
<AdminLayout currentPage="coupon-receive">
<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>
</AdminLayout>
</template>
<script setup lang="uts">
// Minimal script
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
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

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('groupbuy-goods')
const title = ref<string>('goods')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('groupbuy-list')
const title = ref<string>('list')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,26 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('marketing')
const title = ref<string>('营销看板')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,81 @@
<template>
<view class="page-container">
<view class="page-header">
<text class="page-title">积分管理</text>
<text class="page-subtitle">Component: MarketingIntegral</text>
</view>
<view class="page-content">
<view class="placeholder-card">
<text class="placeholder-title">页面占位</text>
<text class="placeholder-desc">该功能模块正在开发中</text>
<text class="placeholder-info">当前采用 CRMEB 路由体系 1:1 映射</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
// TODO: 实现 积分管理 的具体功能
const loading = ref<boolean>(false)
</script>
<style scoped lang="scss">
.page-container {
padding: 20px;
min-height: 100vh;
background: #f5f5f5;
}
.page-header {
margin-bottom: 20px;
}
.page-title {
display: block;
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.page-subtitle {
display: block;
font-size: 14px;
color: #999;
}
.page-content {
background: #fff;
border-radius: 4px;
padding: 24px;
}
.placeholder-card {
text-align: center;
padding: 60px 20px;
}
.placeholder-title {
display: block;
font-size: 18px;
font-weight: 600;
color: #666;
margin-bottom: 12px;
}
.placeholder-desc {
display: block;
font-size: 14px;
color: #999;
margin-bottom: 8px;
}
.placeholder-info {
display: block;
font-size: 12px;
color: #1890ff;
}
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('live-anchor')
const title = ref<string>('anchor')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('live-goods')
const title = ref<string>('goods')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('live-room')
const title = ref<string>('room')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('lottery-config')
const title = ref<string>('config')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('lottery-list')
const title = ref<string>('list')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('member-card')
const title = ref<string>('card')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('member-config')
const title = ref<string>('config')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('member-record')
const title = ref<string>('record')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('member-rights')
const title = ref<string>('rights')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('member-type')
const title = ref<string>('type')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('newcomer')
const title = ref<string>('newcomer')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('points-config')
const title = ref<string>('config')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,25 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('goods')
const title = ref<string>('goods')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -1,28 +1,95 @@
<template>
<view class="container">
<text class="title">积分管理</text>
<AdminLayout :currentPage="currentPage">
<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>
</AdminLayout>
</template>
<script setup lang="uts">
// Minimal script
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const params = ref('')
const tab = ref('stats')
onLoad((options) => {
params.value = JSON.stringify(options ?? {})
tab.value = options?.tab || 'stats'
})
const currentPage = computed(() => {
switch (tab.value) {
case 'goods': return 'points-goods'
case 'order': return 'points-order'
case 'record': return 'points-record'
case 'config': return 'points-config'
case 'lottery-list': return 'lottery-list'
case 'lottery-config': return 'lottery-config'
case 'groupbuy-goods': return 'groupbuy-goods'
case 'groupbuy-list': return 'groupbuy-list'
case 'seckill-goods': return 'seckill-goods'
case 'seckill-list': return 'seckill-list'
case 'seckill-config': return 'seckill-config'
case 'member-type': return 'member-type'
case 'member-rights': return 'member-rights'
case 'member-card': return 'member-card'
case 'member-record': return 'member-record'
case 'member-config': return 'member-config'
case 'live-room': return 'live-room'
case 'live-goods': return 'live-goods'
case 'live-anchor': return 'live-anchor'
case 'recharge-amount': return 'recharge-amount'
case 'recharge-config': return 'recharge-config'
case 'recharge-record': return 'recharge-record'
case 'newcomer': return 'newcomer'
default: return 'points-stats'
}
})
</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

@@ -0,0 +1,25 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('order')
const title = ref<string>('order')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('points-record')
const title = ref<string>('record')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">积分统计数据,包含积分发放和消费情况?</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('points-stats')
const title = ref<string>('积分统计')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('recharge-amount')
const title = ref<string>('amount')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('recharge-config')
const title = ref<string>('config')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('recharge-record')
const title = ref<string>('record')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('seckill-config')
const title = ref<string>('config')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,27 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面占位 (自动生成)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('seckill-goods')
const title = ref<string>('goods')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -0,0 +1,87 @@
<template>
<view class="page-container">
<view class="page-header">
<text class="page-title">秒杀列表</text>
<text class="page-subtitle">Component: MarketingSeckill</text>
</view>
<view class="page-content">
<view class="placeholder-card">
<text class="placeholder-title">页面占位</text>
<text class="placeholder-desc">该功能模块正在开发中</text>
<text class="placeholder-info">当前采用 CRMEB 路由体系 1:1 映射</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const pageTitle = ref<string>('秒杀列表')
</script>
<style scoped>
.page-container {
padding: 24px;
background-color: #f0f2f5;
min-height: 100vh;
}
.page-header {
background-color: #ffffff;
padding: 24px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.page-title {
display: block;
font-size: 20px;
font-weight: 600;
color: #262626;
margin-bottom: 8px;
}
.page-subtitle {
display: block;
font-size: 14px;
color: #8c8c8c;
}
.page-content {
background-color: #ffffff;
padding: 24px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.placeholder-card {
text-align: center;
padding: 48px 24px;
}
.placeholder-title {
display: block;
font-size: 18px;
font-weight: 600;
color: #262626;
margin-bottom: 12px;
}
.placeholder-desc {
display: block;
font-size: 14px;
color: #8c8c8c;
margin-bottom: 8px;
}
.placeholder-info {
display: block;
font-size: 12px;
color: #bfbfbf;
}
</style>

View File

@@ -1,28 +1,65 @@
<template>
<view class="container">
<text class="title">签到记录</text>
</view>
</template>
<AdminLayout currentPage="signin-record">
<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>
</AdminLayout>
</template>
<script setup lang="uts">
// Minimal script
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
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,65 @@
<template>
<view class="container">
<text class="title">签到规则</text>
</view>
</template>
<AdminLayout currentPage="signin-rule">
<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>
</AdminLayout>
</template>
<script setup lang="uts">
// Minimal script
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
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,23 @@
<template>
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">售后订单管理与申请处理</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const title = ref<string>('售后订单')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

Some files were not shown because too many files have changed in this diff Show More