merge: branch 'huangzhenbao-admin' into comclib-analytics, keeping local RPC integration versions

This commit is contained in:
comlibmb
2026-02-11 17:23:01 +08:00
92 changed files with 5500 additions and 1735 deletions

View File

@@ -0,0 +1,83 @@
<template>
<view class="admin-page">
<view class="filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">代理商查询:</text>
<input class="filter-input" placeholder="请输入姓名、手机号或UID" />
</view>
<view class="filter-btns">
<button class="btn primary" @click="onSearch">查询</button>
</view>
</view>
</view>
<view class="content-card">
<view class="action-bar">
<button class="btn primary small" @click="onAdd">添加代理商</button>
</view>
<view class="table-container">
<view class="table-header">
<view class="col col-uid"><text>用户UID</text></view>
<view class="col col-avatar"><text>头像</text></view>
<view class="col col-name"><text>名称</text></view>
<view class="col col-ratio"><text>分佣比例</text></view>
<view class="col col-count"><text>员工数量</text></view>
<view class="col col-time"><text>过期时间</text></view>
<view class="col col-status"><text>状态</text></view>
<view class="col col-ops"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in agentList" :key="item.uid" class="table-row">
<view class="col col-uid"><text>{{ item.uid }}</text></view>
<view class="col col-avatar">
<image class="avatar-img" src="/static/logo.png" mode="aspectFill" />
</view>
<view class="col col-name"><text>{{ item.name }}</text></view>
<view class="col col-ratio"><text>{{ item.ratio }}%</text></view>
<view class="col col-count"><text>{{ item.staffCount }}</text></view>
<view class="col col-time"><text>{{ item.endTime }}</text></view>
<view class="col col-status">
<switch :checked="item.status" color="#1890ff" scale="0.8" />
</view>
<view class="col col-ops">
<text class="op-link">编辑</text>
<text class="op-divider">|</text>
<text class="op-link">查看</text>
<text class="op-divider">|</text>
<text class="op-link">员工</text>
<text class="op-divider">|</text>
<text class="op-link">删除</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const agentList = ref([
{ uid: '60569', name: 'cs2020', ratio: 50, staffCount: 1, endTime: '2026-01-01', status: true },
])
function onSearch() { uni.showToast({ title: '查询中...', icon: 'none' }) }
function onAdd() { uni.showToast({ title: '添加中...', icon: 'none' }) }
</script>
<style scoped lang="scss">
.admin-page { padding: 0; }
.filter-card { background: #fff; padding: 24px; margin-bottom: 16px; border-radius: 4px; }
.filter-row { display: flex; flex-direction: row; align-items: center; gap: 24px; }
.label { font-size: 14px; color: #333; }
.filter-input { border: 1px solid #d9d9d9; height: 32px; width: 220px; padding: 0 12px; font-size: 14px; }
.btn { height: 32px; padding: 0 16px; font-size: 14px; border: 1px solid #d9d9d9; background: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.btn.primary { background: #1890ff; border-color: #1890ff; color: #fff; }
.content-card { background: #fff; border-radius: 4px; }
.action-bar { padding: 16px 24px; }
.table-container { padding: 0 24px 24px; }
.table-header { display: flex; flex-direction: row; background: #f8faff; border-bottom: 1px solid #f0f0f0; padding: 12px 0; }
.table-row { display: flex; flex-direction: row; border-bottom: 1px solid #f0f0f0; padding: 12px 0; align-items: center; &:hover { background: #fafafa; } }
.col { padding: 0 8px; font-size: 14px; color: #333; display: flex; align-items: center; }
.col-uid { width: 80px; } .col-avatar { width: 80px; justify-content: center; } .col-name { width: 120px; } .col-ratio { width: 100px; justify-content: center; } .col-count { width: 100px; justify-content: center; } .col-time { width: 120px; justify-content: center; } .col-status { width: 80px; justify-content: center; } .col-ops { flex: 1; justify-content: flex-end; }
.avatar-img { width: 32px; height: 32px; border-radius: 2px; }
.op-link { color: #1890ff; cursor: pointer; }
.op-divider { color: #e8e8e8; margin: 0 8px; }
</style>

View File

@@ -0,0 +1,89 @@
<template>
<view class="admin-page">
<view class="filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">搜索:</text>
<input class="filter-input" placeholder="请输入姓名、UID" />
</view>
<view class="filter-btns">
<button class="btn primary" @click="onSearch">查询</button>
</view>
</view>
</view>
<view class="content-card">
<view class="tabs-row">
<view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: activeTab === index }" @click="activeTab = index">
<text>{{ tab }}</text>
</view>
</view>
<view class="table-container">
<view class="table-header">
<view class="col col-uid"><text>用户UID</text></view>
<view class="col col-name"><text>代理商名称</text></view>
<view class="col col-phone"><text>代理商电话</text></view>
<view class="col col-dept"><text>事业部名称</text></view>
<view class="col col-img"><text>申请图片</text></view>
<view class="col col-time"><text>申请时间</text></view>
<view class="col col-status"><text>申请状态</text></view>
<view class="col col-code"><text>邀请码</text></view>
<view class="col col-ops"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in applyList" :key="item.uid" class="table-row">
<view class="col col-uid"><text>{{ item.uid }}</text></view>
<view class="col col-name"><text>{{ item.name }}</text></view>
<view class="col col-phone"><text>{{ item.phone }}</text></view>
<view class="col col-dept"><text>{{ item.deptName }}</text></view>
<view class="col col-img">
<image class="table-img" src="/static/logo.png" mode="aspectFill" />
</view>
<view class="col col-time"><text>{{ item.time }}</text></view>
<view class="col col-status">
<view class="status-tag"><text>{{ item.statusText }}</text></view>
</view>
<view class="col col-code"><view class="code-box"><text>{{ item.code }}</text></view></view>
<view class="col col-ops">
<text class="op-link">同意</text>
<text class="op-divider">|</text>
<text class="op-link">拒绝</text>
<text class="op-divider">|</text>
<text class="op-link">删除</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const activeTab = ref(0)
const tabs = ['全部', '申请中', '已同意', '已拒绝']
const applyList = ref([
{ uid: '81806', name: '测试测试', phone: '19910205954', deptName: '26991', time: '2026-01-08 15:30:39', statusText: '申请中', code: '70623142' },
])
function onSearch() { uni.showToast({ title: '查询中...', icon: 'none' }) }
</script>
<style scoped lang="scss">
.admin-page { padding: 0; }
.filter-card { background: #fff; padding: 24px; margin-bottom: 16px; border-radius: 4px; }
.filter-row { display: flex; flex-direction: row; align-items: center; gap: 24px; }
.label { font-size: 14px; color: #333; }
.filter-input { border: 1px solid #d9d9d9; height: 32px; width: 220px; padding: 0 12px; font-size: 14px; }
.btn { height: 32px; padding: 0 16px; font-size: 14px; border: 1px solid #d9d9d9; background: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.btn.primary { background: #1890ff; border-color: #1890ff; color: #fff; }
.content-card { background: #fff; border-radius: 4px; }
.tabs-row { display: flex; flex-direction: row; padding: 0 24px; border-bottom: 1px solid #f0f0f0; }
.tab-item { padding: 16px 20px; cursor: pointer; position: relative; text { font-size: 15px; color: #666; } &.active { text { color: #1890ff; font-weight: 500; } &::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background: #1890ff; } } }
.table-container { padding: 24px; }
.table-header { display: flex; flex-direction: row; background: #f8faff; border-bottom: 1px solid #f0f0f0; padding: 12px 0; }
.table-row { display: flex; flex-direction: row; border-bottom: 1px solid #f0f0f0; padding: 12px 0; align-items: center; &:hover { background: #fafafa; } }
.col { padding: 0 8px; font-size: 14px; color: #333; display: flex; align-items: center; }
.col-uid { width: 80px; } .col-name { width: 120px; } .col-phone { width: 120px; } .col-dept { width: 120px; } .col-img { width: 80px; justify-content: center; } .col-time { width: 160px; justify-content: center; } .col-status { width: 100px; justify-content: center; } .col-code { width: 100px; justify-content: center; } .col-ops { flex: 1; justify-content: flex-end; }
.table-img { width: 32px; height: 32px; border-radius: 2px; }
.status-tag { border: 1px solid #1890ff; color: #1890ff; padding: 2px 8px; border-radius: 4px; font-size: 12px; }
.code-box { border: 1px solid #d9d9d9; padding: 2px 8px; border-radius: 4px; font-size: 12px; background: #fafafa; }
.op-link { color: #1890ff; cursor: pointer; }
.op-divider { color: #e8e8e8; margin: 0 8px; }
</style>

View File

@@ -0,0 +1,83 @@
<template>
<view class="admin-page">
<view class="filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">搜索:</text>
<input class="filter-input" placeholder="请输入姓名、UID" />
</view>
<view class="filter-btns">
<button class="btn primary" @click="onSearch">查询</button>
</view>
</view>
</view>
<view class="content-card">
<view class="action-bar">
<button class="btn primary small" @click="onAdd">添加事业部</button>
</view>
<view class="table-container">
<view class="table-header">
<view class="col col-uid"><text>用户UID</text></view>
<view class="col col-avatar"><text>头像</text></view>
<view class="col col-name"><text>名称</text></view>
<view class="col col-code"><text>邀请码</text></view>
<view class="col col-ratio"><text>分销比例</text></view>
<view class="col col-count"><text>代理商数量</text></view>
<view class="col col-time"><text>截止时间</text></view>
<view class="col col-status"><text>状态</text></view>
<view class="col col-ops"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in divisionList" :key="item.uid" class="table-row">
<view class="col col-uid"><text>{{ item.uid }}</text></view>
<view class="col col-avatar">
<image class="avatar-img" src="/static/logo.png" mode="aspectFill" />
</view>
<view class="col col-name"><text>{{ item.name }}</text></view>
<view class="col col-code"><text>{{ item.code }}</text></view>
<view class="col col-ratio"><text>{{ item.ratio }}%</text></view>
<view class="col col-count"><text>{{ item.agentCount }}</text></view>
<view class="col col-time"><text>{{ item.endTime }}</text></view>
<view class="col col-status">
<switch :checked="item.status" color="#1890ff" scale="0.8" />
</view>
<view class="col col-ops">
<text class="op-link">查看代理商</text>
<text class="op-divider">|</text>
<text class="op-link">编辑</text>
<text class="op-divider">|</text>
<text class="op-link">删除</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const divisionList = ref([
{ uid: '26991', name: '26991', code: '70623142', ratio: 40, agentCount: 1, endTime: '2026-12-31', status: true },
])
function onSearch() { uni.showToast({ title: '查询中...', icon: 'none' }) }
function onAdd() { uni.showToast({ title: '添加中...', icon: 'none' }) }
</script>
<style scoped lang="scss">
.admin-page { padding: 0; }
.filter-card { background: #fff; padding: 24px; margin-bottom: 16px; border-radius: 4px; }
.filter-row { display: flex; flex-direction: row; align-items: center; gap: 24px; }
.label { font-size: 14px; color: #333; }
.filter-input { border: 1px solid #d9d9d9; height: 32px; width: 220px; padding: 0 12px; font-size: 14px; }
.btn { height: 32px; padding: 0 16px; font-size: 14px; border: 1px solid #d9d9d9; background: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.btn.primary { background: #1890ff; border-color: #1890ff; color: #fff; }
.content-card { background: #fff; border-radius: 4px; }
.action-bar { padding: 16px 24px; }
.table-container { padding: 0 24px 24px; }
.table-header { display: flex; flex-direction: row; background: #f8faff; border-bottom: 1px solid #f0f0f0; padding: 12px 0; }
.table-row { display: flex; flex-direction: row; border-bottom: 1px solid #f0f0f0; padding: 12px 0; align-items: center; &:hover { background: #fafafa; } }
.col { padding: 0 8px; font-size: 14px; color: #333; display: flex; align-items: center; }
.col-uid { width: 80px; } .col-avatar { width: 80px; justify-content: center; } .col-name { width: 120px; } .col-code { width: 100px; justify-content: center; } .col-ratio { width: 100px; justify-content: center; } .col-count { width: 100px; justify-content: center; } .col-time { width: 120px; justify-content: center; } .col-status { width: 80px; justify-content: center; } .col-ops { flex: 1; justify-content: flex-end; }
.avatar-img { width: 32px; height: 32px; border-radius: 2px; }
.op-link { color: #1890ff; cursor: pointer; }
.op-divider { color: #e8e8e8; margin: 0 8px; }
</style>

View File

@@ -0,0 +1,95 @@
<template>
<view class="admin-page">
<view class="filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">是否显示:</text>
<view class="select-mock"><text>全部</text><text class="arrow">▼</text></view>
</view>
<view class="filter-item">
<text class="label">等级名称:</text>
<input class="filter-input" placeholder="请输入等级名称" />
</view>
<view class="filter-btns">
<button class="btn primary" @click="onSearch">查询</button>
</view>
</view>
</view>
<view class="content-card">
<view class="action-bar">
<button class="btn primary small" @click="onAdd">添加等级</button>
</view>
<view class="table-container">
<view class="table-header">
<view class="col col-id"><text>ID</text></view>
<view class="col col-img"><text>商品图片</text></view>
<view class="col col-name"><text>名称</text></view>
<view class="col col-level"><text>等级</text></view>
<view class="col col-percent"><text>一级分佣比例</text></view>
<view class="col col-percent"><text>二级分佣比例</text></view>
<view class="col col-stat"><text>任务总数</text></view>
<view class="col col-stat"><text>需完成数量</text></view>
<view class="col col-status"><text>是否显示</text></view>
<view class="col col-ops"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in levelList" :key="item.id" class="table-row">
<view class="col col-id"><text>{{ item.id }}</text></view>
<view class="col col-img">
<image class="table-img" src="/static/logo.png" mode="aspectFill" />
</view>
<view class="col col-name"><text>{{ item.name }}</text></view>
<view class="col col-level"><text>{{ item.level }}</text></view>
<view class="col col-percent"><text>{{ item.percent1 }}%</text></view>
<view class="col col-percent"><text>{{ item.percent2 }}%</text></view>
<view class="col col-stat"><text>{{ item.taskTotal }}</text></view>
<view class="col col-stat"><text>{{ item.taskFinish }}</text></view>
<view class="col col-status">
<switch :checked="item.show" color="#1890ff" scale="0.8" />
</view>
<view class="col col-ops">
<text class="op-link">等级任务</text>
<text class="op-divider">|</text>
<text class="op-link">编辑</text>
<text class="op-divider">|</text>
<text class="op-link">删除</text>
</view>
</view>
</view>
</view>
<view class="pagination">
<text class="page-info">共 {{ levelList.length }} 条</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const levelList = ref([
{ id: '1', name: '一级分销员', level: 1, percent1: 0.00, percent2: 0.00, taskTotal: 0, taskFinish: 0, show: true },
])
function onSearch() { uni.showToast({ title: '查询中...', icon: 'none' }) }
function onAdd() { uni.showToast({ title: '添加中...', icon: 'none' }) }
</script>
<style scoped lang="scss">
.admin-page { padding: 0; }
.filter-card { background: #fff; padding: 24px; margin-bottom: 16px; border-radius: 4px; }
.filter-row { display: flex; flex-direction: row; align-items: center; gap: 24px; }
.label { font-size: 14px; color: #333; }
.select-mock { display: flex; flex-direction: row; align-items: center; justify-content: space-between; border: 1px solid #d9d9d9; border-radius: 2px; height: 32px; width: 160px; padding: 0 12px; background: #fff; text { font-size: 14px; color: #666; } .arrow { font-size: 10px; color: #bfbfbf; } }
.filter-input { border: 1px solid #d9d9d9; height: 32px; width: 220px; padding: 0 12px; font-size: 14px; }
.btn { height: 32px; padding: 0 16px; font-size: 14px; border-radius: 2px; border: 1px solid #d9d9d9; background: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.btn.primary { background: #1890ff; border-color: #1890ff; color: #fff; }
.content-card { background: #fff; border-radius: 4px; }
.action-bar { padding: 16px 24px; }
.table-container { padding: 0 24px 24px; }
.table-header { display: flex; flex-direction: row; background: #f8faff; border-bottom: 1px solid #f0f0f0; padding: 12px 0; }
.table-row { display: flex; flex-direction: row; border-bottom: 1px solid #f0f0f0; padding: 12px 0; align-items: center; &:hover { background: #fafafa; } }
.col { padding: 0 8px; display: flex; align-items: center; font-size: 14px; color: #333; }
.col-id { width: 50px; } .col-img { width: 80px; justify-content: center; } .col-name { width: 120px; } .col-level { width: 80px; justify-content: center; } .col-percent { width: 120px; justify-content: center; } .col-stat { width: 100px; justify-content: center; } .col-status { width: 100px; justify-content: center; } .col-ops { flex: 1; justify-content: flex-end; padding-right: 16px; }
.table-img { width: 32px; height: 32px; border-radius: 2px; }
.op-link { color: #1890ff; cursor: pointer; }
.op-divider { color: #e8e8e8; margin: 0 8px; }
.pagination { padding: 16px 24px; border-top: 1px solid #f0f0f0; }
.page-info { font-size: 14px; color: #999; }
</style>

View File

@@ -0,0 +1,114 @@
<template>
<view class="admin-page">
<view class="filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">时间选择:</text>
<view class="date-picker-mock">
<text class="placeholder">开始日期 - 结束日期</text>
<text class="icon-calendar">📅</text>
</view>
</view>
<view class="filter-item">
<text class="label">搜索:</text>
<input class="filter-input" placeholder="请输入姓名、电话、UID" />
</view>
<view class="filter-btns">
<button class="btn primary" @click="onSearch">查询</button>
</view>
</view>
</view>
<view class="content-card">
<view class="action-bar">
<button class="btn ghost small" @click="onExport">导出</button>
</view>
<view class="table-container">
<view class="table-header">
<view class="col col-id"><text>ID</text></view>
<view class="col col-img"><text>头像</text></view>
<view class="col col-info"><text>用户信息</text></view>
<view class="col col-level"><text>分销等级</text></view>
<view class="col col-stat"><text>推广用户数量</text></view>
<view class="col col-stat"><text>推广订单数量</text></view>
<view class="col col-stat"><text>推广订单金额</text></view>
<view class="col col-stat"><text>佣金总金额</text></view>
<view class="col col-stat"><text>已提现金额</text></view>
<view class="col col-stat"><text>提现次数</text></view>
<view class="col col-stat"><text>未提现金额</text></view>
<view class="col col-ops"><text>操作</text></view>
</view>
<view class="table-body">
<view v-for="item in promoterList" :key="item.id" class="table-row">
<view class="col col-id"><text>{{ item.id }}</text></view>
<view class="col col-img">
<image class="table-img" src="/static/logo.png" mode="aspectFill" />
</view>
<view class="col col-info">
<view class="user-info-box">
<text class="info-text">昵称:{{ item.nickname }}</text>
<text class="info-text">姓名:{{ item.name }}</text>
<text class="info-text">电话:{{ item.phone }}</text>
</view>
</view>
<view class="col col-level"><text>{{ item.level }}</text></view>
<view class="col col-stat"><text>{{ item.userCount }}</text></view>
<view class="col col-stat"><text>{{ item.orderCount }}</text></view>
<view class="col col-stat"><text>{{ item.orderAmount }}</text></view>
<view class="col col-stat"><text>{{ item.commissionTotal }}</text></view>
<view class="col col-stat"><text>{{ item.withdrawnAmount }}</text></view>
<view class="col col-stat"><text>{{ item.withdrawCount }}</text></view>
<view class="col col-stat"><text>{{ item.unwithdrawnAmount }}</text></view>
<view class="col col-ops">
<text class="op-link" @click="onPromoter(item)">推广人</text>
<text class="op-divider">|</text>
<text class="op-link" @click="onMore(item)">更多</text>
<text class="arrow-down">▼</text>
</view>
</view>
</view>
</view>
<view class="pagination">
<text class="page-info">共 {{ promoterList.length }} 条</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const promoterList = ref([
{ id: '82764', nickname: '183****5762', name: '-', phone: '183****5762', level: '--', userCount: 0, orderCount: 0, orderAmount: '0.00', commissionTotal: '0.00', withdrawnAmount: 0, withdrawCount: 0, unwithdrawnAmount: 0 },
])
function onSearch() { uni.showToast({ title: '查询中...', icon: 'none' }) }
function onExport() { uni.showToast({ title: '开始导出', icon: 'none' }) }
function onPromoter(item: any) { uni.showToast({ title: '推广人: ' + item.id, icon: 'none' }) }
function onMore(item: any) { uni.showToast({ title: '更多: ' + item.id, icon: 'none' }) }
</script>
<style scoped lang="scss">
.admin-page { padding: 0; }
.filter-card { background: #fff; padding: 24px; margin-bottom: 16px; border-radius: 4px; }
.filter-row { display: flex; flex-direction: row; align-items: center; gap: 24px; }
.label { font-size: 14px; color: #333; }
.date-picker-mock { display: flex; flex-direction: row; align-items: center; justify-content: space-between; border: 1px solid #d9d9d9; border-radius: 2px; height: 32px; width: 260px; padding: 0 12px; background: #fff; .placeholder { font-size: 14px; color: #bfbfbf; } .icon-calendar { font-size: 14px; color: #bfbfbf; } }
.filter-input { border: 1px solid #d9d9d9; height: 32px; width: 220px; padding: 0 12px; font-size: 14px; }
.btn { height: 32px; padding: 0 16px; font-size: 14px; border-radius: 2px; border: 1px solid #d9d9d9; background: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.btn.primary { background: #1890ff; border-color: #1890ff; color: #fff; }
.btn.ghost { color: #666; background: #fff; }
.btn.small { height: 28px; padding: 0 12px; font-size: 13px; }
.content-card { background: #fff; border-radius: 4px; }
.action-bar { padding: 16px 24px; }
.table-container { padding: 0 24px 24px; }
.table-header { display: flex; flex-direction: row; background: #f8faff; border-bottom: 1px solid #f0f0f0; padding: 12px 0; }
.table-row { display: flex; flex-direction: row; border-bottom: 1px solid #f0f0f0; padding: 12px 0; align-items: center; &:hover { background: #fafafa; } }
.col { padding: 0 8px; display: flex; align-items: center; font-size: 14px; color: #333; }
.col-id { width: 60px; } .col-img { width: 80px; justify-content: center; } .col-info { width: 180px; } .col-level { width: 100px; justify-content: center; } .col-stat { width: 110px; justify-content: center; } .col-ops { flex: 1; justify-content: flex-end; padding-right: 16px; }
.table-img { width: 40px; height: 40px; border-radius: 4px; }
.user-info-box { display: flex; flex-direction: column; }
.info-text { font-size: 12px; color: #666; margin-bottom: 2px; }
.op-link { color: #1890ff; cursor: pointer; }
.op-divider { color: #e8e8e8; margin: 0 8px; }
.arrow-down { font-size: 10px; color: #1890ff; margin-left: 4px; }
.pagination { padding: 16px 24px; border-top: 1px solid #f0f0f0; }
.page-info { font-size: 14px; color: #999; }
</style>

View File

@@ -0,0 +1,508 @@
<template>
<view class="admin-page">
<view class="content-card">
<view class="tabs-row">
<view
v-for="(tab, index) in tabs"
:key="index"
class="tab-item"
:class="{ active: activeTab === index }"
@click="activeTab = index"
>
<text>{{ tab }}</text>
</view>
</view>
<view class="form-container">
<!-- 分销模式 -->
<view v-if="activeTab === 0" class="form-content">
<view class="form-item">
<text class="label">分销启用:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.statue = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.statue === '1'" color="#1890ff" /> 开启
</label>
<label class="radio-label">
<radio value="0" :checked="form.statue === '0'" color="#1890ff" /> 关闭
</label>
</radio-group>
<text class="hint">商城分销功能开启关闭</text>
</view>
<view class="form-item">
<text class="label">分销模式:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.extract_type = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.extract_type === '1'" color="#1890ff" /> 指定分销
</label>
<label class="radio-label">
<radio value="2" :checked="form.extract_type === '2'" color="#1890ff" /> 人人分销
</label>
<label class="radio-label">
<radio value="3" :checked="form.extract_type === '3'" color="#1890ff" /> 满额分销
</label>
</radio-group>
<text class="hint">人人分销"默认每个人都可以分销,“指定分销”仅后台手动设置推广员,“满额分销”指用户购买商品满足消费金额后自动开启分销</text>
</view>
<view class="form-item">
<text class="label">分销关系绑定:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.bind_type = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.bind_type === '1'" color="#1890ff" /> 所有用户
</label>
<label class="radio-label">
<radio value="2" :checked="form.bind_type === '2'" color="#1890ff" /> 新用户
</label>
</radio-group>
<text class="hint">所有用户”指所有没有上级推广人的用户点击或扫码即绑定分销关系,“新用户”指新注册的用户或首次进入系统的用户才会绑定推广关系</text>
</view>
<view class="form-item">
<text class="label">绑定模式:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.store_brokerage_binding_status = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.store_brokerage_binding_status === '1'" color="#1890ff" /> 永久
</label>
<label class="radio-label">
<radio value="2" :checked="form.store_brokerage_binding_status === '2'" color="#1890ff" /> 有效期
</label>
<label class="radio-label">
<radio value="3" :checked="form.store_brokerage_binding_status === '3'" color="#1890ff" /> 临时
</label>
</radio-group>
<text class="hint">永久”一次绑定永久有效,“有效期”绑定后一段时间内有效,“临时”临时有效</text>
</view>
<view class="form-item">
<text class="label">分销海报图:</text>
<view class="poster-upload">
<image class="poster-preview" v-if="form.brokerage_poster_status" :src="form.brokerage_poster_status" mode="aspectFill"></image>
<view v-else class="upload-btn" @click="onUploadPoster">
<text class="plus">+</text>
</view>
</view>
<text class="hint">个人中心分销海报图片建议尺寸600x1000</text>
</view>
<view class="form-item">
<text class="label">分销层级:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.brokerage_level = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.brokerage_level === '1'" color="#1890ff" /> 一级分销
</label>
<label class="radio-label">
<radio value="2" :checked="form.brokerage_level === '2'" color="#1890ff" /> 二级分销
</label>
</radio-group>
<text class="hint">分销层级,一级是只返上一层的佣金,二级是返上级 and 上上级的佣金</text>
</view>
<view class="form-item">
<text class="label">事业部开关:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.is_area_manager = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.is_area_manager === '1'" color="#1890ff" /> 开启
</label>
<label class="radio-label">
<radio value="0" :checked="form.is_area_manager === '0'" color="#1890ff" /> 关闭
</label>
</radio-group>
<text class="hint">事业部开关,关闭后不计算事业部佣金</text>
</view>
<view class="form-item">
<text class="label">代理商申请开关:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.is_agent_apply = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.is_agent_apply === '1'" color="#1890ff" /> 开启
</label>
<label class="radio-label">
<radio value="0" :checked="form.is_agent_apply === '0'" color="#1890ff" /> 关闭
</label>
</radio-group>
<text class="hint">控制移动端我的推广页面的代理商申请按钮是否显示</text>
</view>
<view class="form-item">
<text class="label">佣金悬浮窗开关:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.is_commission_window = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.is_commission_window === '1'" color="#1890ff" /> 开启
</label>
<label class="radio-label">
<radio value="0" :checked="form.is_commission_window === '0'" color="#1890ff" /> 关闭
</label>
</radio-group>
<text class="hint">佣金悬浮窗开关,关闭之后,商品详情不显示佣金悬浮窗</text>
</view>
<button class="submit-btn" @click="onSubmit">提交</button>
</view>
<!-- 返佣设置 -->
<view v-if="activeTab === 1" class="form-content">
<view class="form-item">
<text class="label">自购返佣:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.is_self_brokerage = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.is_self_brokerage === '1'" color="#1890ff" /> 开启
</label>
<label class="radio-label">
<radio value="0" :checked="form.is_self_brokerage === '0'" color="#1890ff" /> 关闭
</label>
</radio-group>
<text class="hint">是否开启自购返佣(开启:分销员自己购买商品,享受一级返佣,上级享受二级返佣;关闭:分销员自己购买商品没有返佣)</text>
</view>
<view class="form-item">
<text class="label">购买付费会员返佣:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.is_member_brokerage = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.is_member_brokerage === '1'" color="#1890ff" /> 开启
</label>
<label class="radio-label">
<radio value="0" :checked="form.is_member_brokerage === '0'" color="#1890ff" /> 关闭
</label>
</radio-group>
<text class="hint">购买付费会员是否按照设置的佣金比例进行返佣</text>
</view>
<view class="form-item">
<text class="label">返佣类型:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.brokerage_type = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.brokerage_type === '1'" color="#1890ff" /> 按照商品价格返佣
</label>
<label class="radio-label">
<radio value="2" :checked="form.brokerage_type === '2'" color="#1890ff" /> 按照实际支付价格返佣
</label>
</radio-group>
<text class="hint">选择返佣类型,按照商品价格返佣(按照商品售价计算返佣金额)以及按照实际支付价格返佣(按照商品的实际支付价格计算返佣)</text>
</view>
<view class="form-item">
<text class="label">推广用户返佣:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.is_promoter_brokerage = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.is_promoter_brokerage === '1'" color="#1890ff" /> 开启
</label>
<label class="radio-label">
<radio value="0" :checked="form.is_promoter_brokerage === '0'" color="#1890ff" /> 关闭
</label>
</radio-group>
<text class="hint">分销推广用户获取佣金</text>
</view>
<view class="form-item">
<text class="label">推广佣金单价:</text>
<view class="input-with-unit">
<input class="form-input" :value="form.promoter_brokerage_price" @input="(e: UniInputEvent) => form.promoter_brokerage_price = e.detail.value" />
</view>
<text class="hint">分销推广佣金单价(每推广一个用户)</text>
</view>
<view class="form-item">
<text class="label">每日推广佣金上限:</text>
<view class="input-with-unit">
<input class="form-input" :value="form.promoter_brokerage_day_max" @input="(e: UniInputEvent) => form.promoter_brokerage_day_max = e.detail.value" />
</view>
<text class="hint">每日推广佣金上限0:不发佣金;-1:不限制;最好是推广佣金单价的整数倍)</text>
</view>
<view class="form-item">
<text class="label">一级返佣比例:</text>
<view class="input-with-unit">
<input class="form-input" :value="form.store_brokerage_ratio" @input="(e: UniInputEvent) => form.store_brokerage_ratio = e.detail.value" />
</view>
<text class="hint">订单交易成功后给上级返佣的比例0 - 100,例:5 = 反订单商品金额的5%</text>
</view>
<view class="form-item">
<text class="label">二级返佣比例:</text>
<view class="input-with-unit">
<input class="form-input" :value="form.store_brokerage_two_ratio" @input="(e: UniInputEvent) => form.store_brokerage_two_ratio = e.detail.value" />
</view>
<text class="hint">订单交易成功后给上上级返佣的比例0 - 100,例:5 = 反订单商品金额 of 5%</text>
</view>
<view class="form-item">
<text class="label">冻结时间:</text>
<view class="input-with-unit">
<input class="form-input" :value="form.extract_frozen_time" @input="(e: UniInputEvent) => form.extract_frozen_time = e.detail.value" />
</view>
<text class="hint">防止用户退款,佣金被提现了,所以需要设置佣金冻结时间(天)</text>
</view>
<button class="submit-btn" @click="onSubmit">提交</button>
</view>
<!-- 提现设置 -->
<view v-if="activeTab === 2" class="form-content">
<view class="form-item">
<text class="label">提现最低金额:</text>
<input class="form-input" :value="form.user_extract_min_price" @input="(e: UniInputEvent) => form.user_extract_min_price = e.detail.value" />
<text class="hint">用户提现最低金额限制</text>
</view>
<view class="form-item">
<text class="label">提现银行卡:</text>
<textarea class="form-textarea" :value="form.extract_bank_list" @input="(e: UniInputEvent) => form.extract_bank_list = e.detail.value" placeholder="配置提现银行卡类型,每个银行换行"></textarea>
<text class="hint">配置提现银行卡类型,每个银行换行</text>
</view>
<view class="form-item">
<text class="label">提现方式:</text>
<checkbox-group class="checkbox-group" @change="(e: UniCheckboxGroupChangeEvent) => form.extract_type_list = e.detail.value">
<label class="checkbox-label"><checkbox value="bank" :checked="form.extract_type_list.includes('bank')" color="#1890ff" /> 银行卡</label>
<label class="checkbox-label"><checkbox value="wechat" :checked="form.extract_type_list.includes('wechat')" color="#1890ff" /> 微信</label>
<label class="checkbox-label"><checkbox value="alipay" :checked="form.extract_type_list.includes('alipay')" color="#1890ff" /> 支付宝</label>
</checkbox-group>
<text class="hint">开启后用户可以选择该提现方式</text>
</view>
<view class="form-item">
<text class="label">微信提现:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.wechat_extract_type = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.wechat_extract_type === '1'" color="#1890ff" /> 手动线下转账
</label>
<label class="radio-label">
<radio value="2" :checked="form.wechat_extract_type === '2'" color="#1890ff" /> 自动转账到零钱
</label>
</radio-group>
<text class="hint">微信提现方式:手动线下转账,自动转账到零钱(需开通商家转账到零钱)</text>
</view>
<view class="form-item">
<text class="label">支付宝提现:</text>
<radio-group class="radio-group" @change="(e: UniRadioGroupChangeEvent) => form.alipay_extract_type = e.detail.value">
<label class="radio-label">
<radio value="1" :checked="form.alipay_extract_type === '1'" color="#1890ff" /> 手动线下转账
</label>
<label class="radio-label">
<radio value="2" :checked="form.alipay_extract_type === '2'" color="#1890ff" /> 自动转账到余额
</label>
</radio-group>
<text class="hint">支付宝提现方式:手动线下转账,自动转账到余额(需开通支付宝转账)</text>
</view>
<view class="form-item">
<text class="label">提现手续费:</text>
<input class="form-input" :value="form.user_extract_fee" @input="(e: UniInputEvent) => form.user_extract_fee = e.detail.value" />
<text class="hint">提现手续费百分比范围0-1000为无提现手续费设置10即收取10%手续费提现100元到账90元10元手续费</text>
</view>
<button class="submit-btn" @click="onSubmit">提交</button>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const activeTab = ref(0)
const tabs = ['分销模式', '返佣设置', '提现设置']
const form = ref({
// 分销模式
statue: '1',
extract_type: '2',
bind_type: '2',
store_brokerage_binding_status: '1',
brokerage_poster_status: '',
brokerage_level: '2',
is_area_manager: '1',
is_agent_apply: '1',
is_commission_window: '1',
// 返佣设置
is_self_brokerage: '1',
is_member_brokerage: '0',
brokerage_type: '1',
is_promoter_brokerage: '1',
promoter_brokerage_price: '2',
promoter_brokerage_day_max: '-1',
store_brokerage_ratio: '20',
store_brokerage_two_ratio: '2',
extract_frozen_time: '1',
// 提现设置
user_extract_min_price: '1',
extract_bank_list: '中国银行',
extract_type_list: ['bank', 'wechat', 'alipay'],
wechat_extract_type: '1',
alipay_extract_type: '1',
user_extract_fee: '0'
})
function onUploadPoster() {
uni.chooseImage({
count: 1,
success: (res) => {
form.value.brokerage_poster_status = res.tempFilePaths[0]
}
})
}
function onSubmit() {
uni.showToast({ title: '保存成功', icon: 'success' })
}
</script>
<style scoped lang="scss">
.admin-page {
display: flex;
flex-direction: column;
}
.content-card {
background: #fff;
border-radius: 4px;
}
.tabs-row {
display: flex;
flex-direction: row;
padding: 0 24px;
border-bottom: 1px solid #f0f0f0;
}
.tab-item {
padding: 16px 20px;
cursor: pointer;
position: relative;
text { font-size: 15px; color: #666; }
&.active {
text { color: #1890ff; font-weight: 500; }
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background: #1890ff;
}
}
}
.form-container {
padding: 32px 24px;
}
.form-item {
margin-bottom: 24px;
display: flex;
flex-direction: column;
}
.label {
font-size: 14px;
color: #333;
margin-bottom: 8px;
font-weight: 500;
}
.radio-group, .checkbox-group {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 16px;
}
.radio-label, .checkbox-label {
display: flex;
flex-direction: row;
align-items: center;
font-size: 14px;
color: #666;
}
.hint {
font-size: 12px;
color: #999;
margin-top: 8px;
line-height: 1.5;
max-width: 800px;
}
.form-input {
border: 1px solid #d9d9d9;
border-radius: 4px;
height: 36px;
padding: 0 12px;
font-size: 14px;
width: 100%;
max-width: 400px;
}
.form-textarea {
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 8px 12px;
font-size: 14px;
width: 100%;
max-width: 600px;
height: 120px;
}
.poster-upload {
width: 80px;
height: 80px;
border: 1px dashed #d9d9d9;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
background-color: #fafafa;
&:hover {
border-color: #1890ff;
}
}
.poster-preview {
width: 100%;
height: 100%;
border-radius: 4px;
}
.upload-btn {
display: flex;
align-items: center;
justify-content: center;
.plus {
font-size: 24px;
color: #999;
}
}
.input-with-unit {
display: flex;
flex-direction: row;
align-items: center;
}
.submit-btn {
margin-top: 24px;
width: 80px;
height: 36px;
background: #1890ff;
color: #fff;
border-radius: 4px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
&:active {
opacity: 0.8;
}
}
</style>

View File

@@ -0,0 +1,85 @@
# 管理后台响应式布局实现指南
本文档总结了商城管理后台从固定布局到响应式布局的改造过程及核心技术点。
## 1. 核心目标
- **多终端适配**:确保后台在桌面端(宽屏)、平板端(中等屏幕)和移动端(窄屏)均能良好展示。
- **自动适配状态管理**:系统能自动感知屏幕宽度并调整侧边栏显隐逻辑。
- **布局平滑过渡**:侧边栏的收起、拉出及内容区的重排应具有良好的动画效果。
## 2. 状态管理 (Store) 扩展
在 [adminNavStore.uts](layouts/admin/store/adminNavStore.uts) 中引入了响应式状态:
- `windowWidth`: 实时存储当前窗口宽度。
- `isMobile`: 计算属性,当宽度小于 768px 时判定为移动端。
- `isMobileMenuOpen`: 控制移动端模式下侧边栏的展开状态。
```typescript
export const windowWidth = ref<number>(1024);
export const isMobile = computed<boolean>(() => windowWidth.value < 768);
export const isMobileMenuOpen = ref<boolean>(false);
```
## 3. 布局架构调整 (AdminLayout)
[AdminLayout.uvue](layouts/admin/AdminLayout.uvue) 负责整体结构的响应式策略:
### 3.1 监听并初始化
`onMounted` 中初始化宽度并监听 `uni.onWindowResize`
```typescript
onMounted(() => {
windowWidth.value = uni.getWindowInfo().windowWidth;
uni.onWindowResize((res) => {
windowWidth.value = res.size.windowWidth;
});
});
```
### 3.2 侧边栏处理
- **桌面端**:侧边栏常规展示,通过 `marginLeft` 为内容区腾出空间。
- **移动端**:侧边栏通过 `position: absolute` 隐藏在屏幕外,通过 `translateX` 动画滑入。同时禁用二级侧边栏的常驻显示。
### 3.3 移动端遮罩与切换
- 引入 `mobile-mask` 遮罩层,点击遮罩自动关闭菜单。
- [AdminHeader](layouts/admin/components/AdminHeader.uvue) 在移动端会显示 “☰” 切换按钮。
## 4. 页面内容重排 (HomeIndex)
[HomeIndex.uvue](layouts/admin/pages/HomeIndex.uvue) 利用 CSS 媒体查询实现内容区域的自适应:
### 4.1 KPI 卡片流式布局
使用 `flex-wrap: wrap` 配合 `min-width`
- **布局策略**:设置 `min-width: 250px` 作为安全边界,确保在 1200px 分辨率下依然能并排展示 4 个卡片。
- **动态列数**
- **宽屏 (>1200px)**: 4个卡片并排。
- **中屏 (768px - 1200px)**: 强制 2 个并排。
- **窄屏 (<768px)**: 1个卡片占满整行。
- **高度控制**:统一固定为 `200px`
### 4.2 图表响应式
- **垂直排列**:底部两个并排的图表在小屏下自动切换为垂直排列(`flex-direction: column`)。
- **组件自适应**:图表组件内部利用父容器宽度自动伸缩。
- **头部压缩**:移动端下,图表的配置项(如日期切换标签)由横向改为纵向,避免溢出。
## 5. 组件化实践 (KpiMiniCard)
统一使用了 [KpiMiniCard](pages/mall/admin/homePage/components/KpiMiniCard.uvue) 组件,确保:
- 样式一致性。
- 代码复用性。
- 内部样式的可维护性。
## 6. 使用建议
- 后续开发新页面时,请优先使用 `stats-row``stat-card` 进行布局。
- 对于复杂的表格页面,建议在移动端隐藏非核心列,或使用横向滚动条展示。
- 所有的宽度判定建议遵循 768px 这一标准断点。

View File

@@ -81,7 +81,7 @@ const props = withDefaults(defineProps<{
const trendArrow = computed((): string => {
if (props.trend === 'up') return '▲'
if (props.trend === 'down') return '▼'
return ''
return ''
})
const trendClass = computed((): string => {
@@ -94,33 +94,43 @@ const trendClass = computed((): string => {
<style>
.kpi-card{
background-color:#ffffff;
border:1px solid #ebeef5;
border-radius:6px;
border-radius:4px;
padding:16px;
box-shadow:0 2px 12px rgba(0,0,0,0.04);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
transition: box-shadow 0.3s;
height: 200px; /* 固定高度 */
min-width: 0; /* 允许由父级 grid 容器决定宽度,防止在 4 列布局时撑爆容器 */
display: flex;
flex-direction: column;
overflow: hidden;
}
.kpi-card:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}
/* Header */
.kpi-header{
display:flex;
flex-direction: row;
align-items:center;
justify-content:space-between;
gap:12px;
margin-bottom: 8px;
flex-shrink: 0;
.kpi-title{
font-size:14px;
color:#303133;
font-weight:600;
color:#666666;
font-weight:400;
}
.kpi-tag{
padding:2px 8px;
border-radius:4px;
border:1px solid #e1f3d8;
background:#f0f9eb;
padding:1px 6px;
border-radius:2px;
background-color: #e8f4ff;
}
.kpi-tag-text{
font-size:12px;
color:#67c23a;
color:#1890ff;
}
}
@@ -128,60 +138,82 @@ const trendClass = computed((): string => {
/* Body */
.kpi-body{
margin-top:10px;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.kpi-main-value{
font-size:32px;
font-weight:600;
color:#303133;
line-height:40px;
font-size:30px;
font-weight:500;
color:#262626;
line-height:1.2;
margin-bottom: 4px;
}
/* “昨日 / 日环比” */
.kpi-meta{
margin-top:8px;
display:flex;
flex-direction: row;
align-items:center;
justify-content:flex-start;
gap:12px;
flex-wrap:wrap;
gap:8px;
padding-bottom:12px;
border-bottom:1px solid #f0f0f0;
margin-bottom: auto; /* 将 footer 顶到底部 */
flex-wrap: nowrap; /* 不允许换行,依靠父容器 min-width 保证空间 */
}
.kpi-meta-text{
font-size:12px;
color:#909399;
color:#8c8c8c;
flex-shrink: 0;
}
.kpi-meta-right{
display:flex;
flex-direction: row;
align-items:center;
gap:6px;
gap:4px;
flex-shrink: 0;
}
.kpi-trend-arrow{
font-size:12px;
font-weight: 500;
}
.kpi-trend-arrow.is-up{ color:#f56c6c; }
.kpi-trend-arrow.is-down{ color:#67c23a; }
.kpi-trend-arrow.is-flat{ color:#909399; }
.kpi-trend-arrow.is-up{ color:#ff4d4f; }
.kpi-trend-arrow.is-down{ color:#52c41a; }
.kpi-trend-arrow.is-flat{ color:#8c8c8c; }
.kpi-divider{
height:1px;
background:#ebeef5;
margin:12px 0;
display: none; /* 已整合到 meta 的 border-bottom */
}
/* Footer */
.kpi-footer{
display:flex;
flex-direction: row;
align-items:center;
justify-content:space-between;
gap:12px;
flex-shrink: 0;
}
.kpi-footer-left{
font-size:12px;
color:#909399;
color:#8c8c8c;
white-space: nowrap;
}
.kpi-footer-right{
font-size:12px;
color:#909399;
color:#262626;
font-weight:500;
white-space: nowrap;
}
}
/* 响应式微调 */
@media screen and (max-width: 480px) {
.kpi-main-value {
font-size: 26px !important;
}
.kpi-card {
padding: 12px !important;
}
}
</style>

View File

@@ -1,24 +1,43 @@
<template>
<!-- 管理后台入口:直接加载 AdminLayout使用 CRMEB 内部路由系统 -->
<AdminLayout />
<view class="admin-home-page">
<AdminLayout>
<view class="home-content">
<text class="welcome-text">管理后台首页</text>
</view>
</AdminLayout>
</view>
</template>
<script setup lang="uts">
/**
* 管理后台入口页面
*
* 架构说明:
* 1. 此页面是 pages.json 中配置的主入口
* 2. 直接加载 AdminLayout 组件作为容器
* 3. AdminLayout 内部使用 CRMEB 路由系统管理所有子页面
* 4. 不需要额外的业务逻辑,保持简洁
*
* 路由流程:
* pages.json → homePage/index.uvue → AdminLayout → 内部路由切换
*/
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout'
const title = ref<string>('管理后台首页')
onLoad((options: OnLoadOptions) => {
console.log('首页加载完成')
})
onShow(() => {
console.log('首页显示')
})
</script>
<style scoped>
/* 无需额外样式,完全由 AdminLayout 控制布局 */
<style scoped lang="scss">
.admin-home-page {
width: 100%;
min-height: 100vh;
}
.home-content {
padding: 20px;
background-color: #f5f7fa;
}
.welcome-text {
font-size: 20px;
font-weight: 600;
color: #1a1a1a;
}
</style>

View File

@@ -1,14 +1,16 @@
<template>
<AdminLayout currentPage="marketing">
<view class="Page">
<view class="Header">
<text class="Title">营销管理</text>
<text class="SubTitle">marketing-management</text>
</view>
<view class="admin-page">
<view class="admin-sections">
<view class="admin-card 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 class="admin-card Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</view>
</AdminLayout>
@@ -27,13 +29,7 @@ onLoad((options) => {
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
@@ -45,10 +41,6 @@ onLoad((options) => {
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;

View File

@@ -182,6 +182,10 @@ export default {
}
},
methods: {
getStatusLabel(val : number) : string {
const item = this.statusOptions.find((opt: any) => opt.value === val)
return item ? item.label : '全部'
},
getStatusText(status : number) : string {
switch(status) {
case 0: return '未发货';

View File

@@ -7,7 +7,7 @@
<text class="label">时间选择:</text>
<view class="date-range-mock">
<text class="date-text">全部</text>
<uni-icons type="calendar" size="16" color="#c0c4cc"></uni-icons>
<text class="iconfont icon-calendar" style="font-size: 16px; color: #c0c4cc;"></text>
</view>
</view>
<view class="filter-item">
@@ -15,7 +15,7 @@
<picker mode="selector" :range="typeOptions" range-key="label" @change="typeChange">
<view class="admin-input-picker">
<text>{{ typeLabel }}</text>
<uni-icons type="arrowdown" size="14" color="#c0c4cc"></uni-icons>
<text class="iconfont icon-arrow-down" style="font-size: 14px; color: #c0c4cc;"></text>
</view>
</picker>
</view>
@@ -60,7 +60,11 @@
<!-- 分页 -->
<view class="table-pagination">
<text class="total-text">共 {{ total }} 条</text>
<uni-pagination :total="total" :pageSize="10" :current="1"></uni-pagination>
<view class="page-ops">
<button class="page-btn" disabled>上一页</button>
<text class="current-page">1</text>
<button class="page-btn">下一页</button>
</view>
</view>
</view>
</view>
@@ -272,6 +276,27 @@ export default {
justify-content: flex-end;
}
.total-text { font-size: 14px; color: #515a6e; margin-right: 15px; }
.page-ops {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
}
.page-btn {
height: 32px;
padding: 0 12px;
font-size: 14px;
border-radius: 2px;
border: 1px solid #d9d9d9;
background: #fff;
margin: 0;
}
.current-page {
padding: 0 12px;
font-size: 14px;
color: #2d8cf0;
}
</style>

View File

@@ -1,14 +1,16 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="Page">
<view class="Header">
<text class="Title">订单</text>
<text class="SubTitle">order-management</text>
</view>
<view class="admin-page">
<view class="admin-sections">
<view class="admin-card Header">
<text class="Title">订单</text>
<text class="SubTitle">order-management</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
<view class="admin-card Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</view>
</AdminLayout>
@@ -33,13 +35,7 @@ onLoad((options: Record<string, string>) => {
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
@@ -51,10 +47,6 @@ onLoad((options: Record<string, string>) => {
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;

View File

@@ -97,12 +97,12 @@
</view>
<view class="modal-body">
<view class="qr-item">
<image class="qr-img" src="https://p.demo.crmeb.net/uploads/attach/2024/09/20240905/66d87e35b7e9b.jpg" mode="aspectFit"></image>
<image class="qr-img" src="/static/logo.png" mode="aspectFit"></image>
<text class="qr-label">公众号二维码</text>
</view>
<view class="qr-item">
<view class="qr-placeholder-mp">
<image class="mp-qr-mock" src="https://p.demo.crmeb.net/uploads/attach/2024/09/20240905/66d87e35b7e9b.jpg" mode="aspectFit"></image>
<image class="mp-qr-mock" src="/static/logo.png" mode="aspectFit"></image>
</view>
<text class="qr-label">小程序二维码</text>
</view>

View File

@@ -1,64 +0,0 @@
<template>
<AdminLayout currentPage="product-classification">
<view class="Page">
<view class="Header">
<text class="Title">商品分类</text>
<text class="SubTitle">product-classification</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</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

@@ -1,64 +0,0 @@
<template>
<AdminLayout currentPage="product-labels">
<view class="Page">
<view class="Header">
<text class="Title">商品标签</text>
<text class="SubTitle">product-labels</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</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

@@ -1,64 +0,0 @@
<template>
<AdminLayout currentPage="product-management">
<view class="Page">
<view class="Header">
<text class="Title">商品</text>
<text class="SubTitle">product-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

@@ -1,64 +0,0 @@
<template>
<AdminLayout currentPage="product-parameters">
<view class="Page">
<view class="Header">
<text class="Title">商品参数</text>
<text class="SubTitle">product-parameters</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</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

@@ -1,64 +0,0 @@
<template>
<AdminLayout currentPage="product-protection">
<view class="Page">
<view class="Header">
<text class="Title">商品保障</text>
<text class="SubTitle">product-protection</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</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

@@ -1,65 +0,0 @@
<template>
<AdminLayout currentPage="product-reviews">
<view class="Page">
<view class="Header">
<text class="Title">商品评论</text>
<text class="SubTitle">product-reviews</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</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

@@ -1,64 +0,0 @@
<template>
<AdminLayout currentPage="product-specifications">
<view class="Page">
<view class="Header">
<text class="Title">商品规格</text>
<text class="SubTitle">product-specifications</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</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

@@ -1,25 +0,0 @@
<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>('index')
const title = ref<string>('index')
</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,25 +0,0 @@
<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>('index')
const title = ref<string>('index')
</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,25 +0,0 @@
<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>('index')
const title = ref<string>('index')
</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,26 +0,0 @@
<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>('product-protection')
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

@@ -1,25 +0,0 @@
<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>('index')
const title = ref<string>('index')
</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,26 +0,0 @@
<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>('product-specifications')
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

@@ -1,7 +1,8 @@
<template>
<view class="product-statistic-page">
<!-- 商品概况头部 -->
<view class="page-header-row">
<view class="admin-page">
<view class="admin-sections">
<!-- 商品概况头部 -->
<view class="admin-card page-header-row">
<view class="title-wrap">
<text class="page-title">商品概况</text>
<view class="info-icon">?</view>
@@ -16,9 +17,9 @@
</view>
</view>
<!-- 统计指标网格 -->
<view class="stat-grid">
<view v-for="(item, index) in statItems" :key="index" class="stat-card">
<!-- 统计指标网格 (使用统一响应式网格) -->
<view class="kpi-grid">
<view v-for="(item, index) in statItems" :key="index" class="admin-card stat-card">
<view class="stat-main">
<view class="icon-box" :style="{ backgroundColor: item.bgColor }">
<text class="stat-emoji">{{ item.emoji }}</text>
@@ -41,7 +42,7 @@
</view>
<!-- 图表卡片 -->
<view class="chart-card">
<view class="admin-card chart-card">
<view class="chart-header">
<view class="legend-wrap">
<view class="legend-item"><view class="dot purple"></view><text>商品浏览量</text></view>
@@ -59,7 +60,7 @@
</view>
<!-- 商品排行 -->
<view class="ranking-card">
<view class="admin-card ranking-card">
<view class="ranking-header">
<text class="ranking-title">商品排行</text>
<view class="ranking-filters">
@@ -114,6 +115,7 @@
</view>
</view>
</view>
</view>
</view>
</template>
@@ -274,18 +276,11 @@ function initChart(data: any[]) {
</script>
<style scoped lang="scss">
.product-statistic-page {
padding: 16px;
background-color: #f0f2f5;
min-height: 100vh;
}
.page-header-row {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.title-wrap {
@@ -327,19 +322,9 @@ function initChart(data: any[]) {
.btn-query { background: #1890ff; color: #fff; font-size: 14px; height: 32px; padding: 0 15px; border-radius: 4px; border: none; }
.btn-export { background: #1890ff; color: #fff; font-size: 14px; height: 32px; padding: 0 15px; border-radius: 4px; border: none; }
.stat-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 16px;
margin-bottom: 16px;
}
/* stat-grid 已废弃,由全局 kpi-grid 接管 */
.stat-card {
width: calc(33.33% - 11px);
background: #fff;
border-radius: 8px;
padding: 20px;
min-width: 0;
}
.stat-main {
@@ -379,9 +364,6 @@ function initChart(data: any[]) {
.arrow { font-size: 10px; margin-left: 2px; }
.chart-card {
background: #fff;
border-radius: 8px;
padding: 24px;
}
.chart-header {

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">协议管理</text>
</view>
<view class="page-content">
<text class="placeholder-text">协议管理 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">管理员列表</text>
</view>
<view class="page-content">
<text class="placeholder-text">管理员列表 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">权限设置</text>
</view>
<view class="page-content">
<text class="placeholder-text">权限设置 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">角色管理</text>
</view>
<view class="page-content">
<text class="placeholder-text">角色管理 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">配送员管理</text>
</view>
<view class="page-content">
<text class="placeholder-text">配送员管理 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">提货点设置</text>
</view>
<view class="page-content">
<text class="placeholder-text">提货点设置 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">运费模板</text>
</view>
<view class="page-content">
<text class="placeholder-text">运费模板 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">商品采集配置</text>
</view>
<view class="page-content">
<text class="placeholder-text">商品采集配置 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">电子面单配置</text>
</view>
<view class="page-content">
<text class="placeholder-text">电子面单配置 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">物流查询配置</text>
</view>
<view class="page-content">
<text class="placeholder-text">物流查询配置 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">一号通配置</text>
</view>
<view class="page-content">
<text class="placeholder-text">一号通配置 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">一号通页面</text>
</view>
<view class="page-content">
<text class="placeholder-text">一号通页面 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">商城支付配置</text>
</view>
<view class="page-content">
<text class="placeholder-text">商城支付配置 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">短信接口配置</text>
</view>
<view class="page-content">
<text class="placeholder-text">短信接口配置 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">系统存储配置</text>
</view>
<view class="page-content">
<text class="placeholder-text">系统存储配置 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">消息管理</text>
</view>
<view class="page-content">
<text class="placeholder-text">消息管理 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">协议设置</text>
</view>
<view class="page-content">
<text class="placeholder-text">协议设置 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -1,81 +1,333 @@
<template>
<view class="page-container">
<view class="page-header">
<text class="page-title">系统配置</text>
<text class="page-subtitle">Component: SettingSystemConfig</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 class="admin-page">
<view class="admin-sections">
<view class="admin-card settings-card">
<!-- 顶部导航标签 (1:1 复刻 CRMEB: 横向排列) -->
<view class="tabs-container">
<scroll-view class="tabs-scroll" scroll-x="true" show-scrollbar="false" :enable-flex="true">
<view class="tabs-bar">
<view
v-for="(tab, index) in tabs"
:key="index"
class="tab-item"
:class="{ active: currentTab === index }"
@click="currentTab = index"
>
<text class="tab-text">{{ tab.name }}</text>
<view class="tab-line" v-if="currentTab === index"></view>
</view>
</view>
</scroll-view>
</view>
<!-- 表单区域 -->
<view class="form-container">
<!-- 1. 基础配置 -->
<view v-if="currentTab === 0" class="form-content">
<view class="form-item">
<view class="form-label">站点开启:</view>
<view class="form-right">
<radio-group class="radio-group" @change="formData.site_open = parseInt(($event.detail.value as string))">
<label class="radio-label"><radio value="1" :checked="formData.site_open == 1" color="#1890ff" />开启</label>
<label class="radio-label"><radio value="0" :checked="formData.site_open == 0" color="#1890ff" />关闭</label>
</radio-group>
<view class="form-tip">站点开启/关闭(用于升级等临时关闭),关闭后前端会弹窗显示站点升级中,请稍后访问</view>
</view>
</view>
<view class="form-item">
<view class="form-label">网站名称:</view>
<view class="form-right">
<input class="form-input" v-model="formData.site_name" placeholder="请输入网站名称" />
<view class="form-tip">网站名称很多地方会显示的,建议认真填写</view>
</view>
</view>
<view class="form-item">
<view class="form-label">网站地址:</view>
<view class="form-right">
<input class="form-input" v-model="formData.site_url" placeholder="请输入网站地址" />
<view class="form-tip">安装自动配置,不要轻易修改,更换后会影响网站访问、接口请求、本地文件储存、支付回调、微信授权、支付、小程序图片访问、部分二维码、官方授权等</view>
</view>
</view>
<view class="form-item">
<view class="form-label">消息队列:</view>
<view class="form-right">
<radio-group class="radio-group" @change="formData.msg_queue = parseInt(($event.detail.value as string))">
<label class="radio-label"><radio value="1" :checked="formData.msg_queue == 1" color="#1890ff" />开启</label>
<label class="radio-label"><radio value="0" :checked="formData.msg_queue == 0" color="#1890ff" />关闭</label>
</radio-group>
<view class="form-tip">是否启用消息队列启用后提升程序运行速度启用前必须配置Redis缓存文档地址https://doc.crmeb.com/single/crmeb_v4/7217</view>
</view>
</view>
<view class="form-item">
<view class="form-label">联系电话:</view>
<view class="form-right">
<input class="form-input" v-model="formData.contact_phone" placeholder="请输入联系电话" />
<view class="form-tip">联系电话</view>
</view>
</view>
<view class="form-item">
<view class="form-label">授权密钥:</view>
<view class="form-right">
<input class="form-input" v-model="formData.auth_key" placeholder="请输入授权密钥" />
</view>
</view>
</view>
<!-- 2. 分享配置 -->
<view v-else-if="currentTab === 1" class="form-content">
<view class="form-item">
<view class="form-label">分享图片:</view>
<view class="form-right">
<view class="upload-placeholder" @click="handleUpload('share_img')">上传图片</view>
<view class="form-tip">分享图片比例5:4建议小于50KB</view>
</view>
</view>
<view class="form-item">
<view class="form-label">分享标题:</view>
<view class="form-right">
<input class="form-input" v-model="formData.share_title" />
</view>
</view>
<view class="form-item">
<view class="form-label">分享简介:</view>
<view class="form-right">
<textarea class="form-textarea" v-model="formData.share_desc" />
</view>
</view>
</view>
<!-- 3. LOGO配置 -->
<view v-else-if="currentTab === 2" class="form-content">
<view class="form-item">
<view class="form-label">后台登录LOGO:</view>
<view class="form-right">
<view class="upload-placeholder" @click="handleUpload('login_logo')">上传截图</view>
<view class="form-tip">建议尺寸270*75</view>
</view>
</view>
<view class="form-item">
<view class="form-label">后台小LOGO:</view>
<view class="form-right">
<view class="upload-placeholder" @click="handleUpload('small_logo')">上传图片</view>
<view class="form-tip">建议尺寸180*180</view>
</view>
</view>
<view class="form-item">
<view class="form-label">后台大LOGO:</view>
<view class="form-right">
<view class="upload-placeholder" @click="handleUpload('big_logo')">上传图片</view>
<view class="form-tip">建议尺寸170*50</view>
</view>
</view>
</view>
<!-- 4. 自定义JS -->
<view v-else-if="currentTab === 3" class="form-content">
<view class="form-item">
<view class="form-label">移动端JS:</view>
<view class="form-right">
<textarea class="form-textarea code-bg" v-model="formData.mobile_js" />
</view>
</view>
<view class="form-item">
<view class="form-label">管理端JS:</view>
<view class="form-right">
<textarea class="form-textarea code-bg" v-model="formData.admin_js" />
</view>
</view>
<view class="form-item">
<view class="form-label">PC端JS:</view>
<view class="form-right">
<textarea class="form-textarea code-bg" v-model="formData.pc_js" />
</view>
</view>
</view>
<!-- 5. 地图配置 -->
<view v-else-if="currentTab === 4" class="form-content">
<view class="form-item">
<view class="form-label">腾讯地图KEY:</view>
<view class="form-right">
<input class="form-input" v-model="formData.tencent_map_key" />
<view class="form-tip">申请地址https://lbs.qq.com</view>
</view>
</view>
</view>
<!-- 6. 备案配置 -->
<view v-else-if="currentTab === 5" class="form-content">
<view class="form-item">
<view class="form-label">备案号:</view>
<view class="form-right">
<input class="form-input" v-model="formData.filing_no" />
</view>
</view>
<view class="form-item">
<view class="form-label">ICP链接:</view>
<view class="form-right">
<input class="form-input" v-model="formData.icp_link" />
</view>
</view>
</view>
<!-- 7. 模块配置 -->
<view v-else-if="currentTab === 6" class="form-content">
<view class="form-item">
<view class="form-label">功能开启:</view>
<view class="form-right">
<checkbox-group class="checkbox-group" @change="formData.module_config = ($event.detail.value as string[])">
<label class="checkbox-label"><checkbox value="秒杀" :checked="formData.module_config.includes('秒杀')" color="#1890ff" />秒杀</label>
<label class="checkbox-label"><checkbox value="砍价" :checked="formData.module_config.includes('砍价')" color="#1890ff" />砍价</label>
<label class="checkbox-label"><checkbox value="拼团" :checked="formData.module_config.includes('拼团')" color="#1890ff" />拼团</label>
</checkbox-group>
</view>
</view>
</view>
<!-- 8. 远程登录 -->
<view v-else-if="currentTab === 7" class="form-content">
<view class="form-item">
<view class="form-label">远程登录地址:</view>
<view class="form-right">
<input class="form-input" v-model="formData.remote_login_url" />
</view>
</view>
</view>
<!-- 9. WAF配置 -->
<view v-else-if="currentTab === 8" class="form-content">
<view class="form-item">
<view class="form-label">WAF类型:</view>
<view class="form-right">
<radio-group class="radio-group" @change="formData.waf_type = parseInt(($event.detail.value as string))">
<label class="radio-label"><radio value="0" :checked="formData.waf_type == 0" color="#1890ff" />关闭</label>
<label class="radio-label"><radio value="1" :checked="formData.waf_type == 1" color="#1890ff" />拦截</label>
<label class="radio-label"><radio value="2" :checked="formData.waf_type == 2" color="#1890ff" />过滤</label>
</radio-group>
<view class="form-tip">WAF类型关闭所有参数都能正常请求拦截匹配到WAF配置的参数阻断接口请求过滤匹配到WAF配置的参数过滤参数正常请求接口</view>
</view>
</view>
<view class="form-item">
<view class="form-label">WAF配置:</view>
<view class="form-right">
<textarea class="form-textarea code-bg waf-textarea" v-model="formData.waf_config" />
<view class="form-tip">WAF配置验证参数过滤掉不需要的参数或拦截请求多个参数用回车换行分隔</view>
</view>
</view>
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<view class="form-label"></view> <!-- 占位用于对齐 -->
<view class="form-right">
<button class="btn-submit" @click="handleSubmit">提交</button>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { ref } from "vue"
// TODO: 实现 系统配置 的具体功能
const loading = ref<boolean>(false)
const currentTab = ref(0)
const tabs = [
{ name: "基础配置" }, { name: "分享配置" }, { name: "LOGO配置" },
{ name: "自定义JS" }, { name: "地图配置" }, { name: "备案配置" },
{ name: "模块配置" }, { name: "远程登录配置" }, { name: "WAF配置" }
]
const formData = ref({
site_open: 1,
site_name: "CRMEB标准版",
site_url: "https://v5.crmeb.net",
msg_queue: 0,
contact_phone: "",
auth_key: "AO9azvBW9vEcOH7swklTM0RYRb6EB4RLWMSD88MnKTi8Vd6cjXVd",
share_img: "",
share_title: "CRMEB v5标准版",
share_desc: "完善的文档 全心而来!",
login_logo: "",
small_logo: "",
big_logo: "",
mobile_js: "",
admin_js: "",
pc_js: "",
tencent_map_key: "SMJBZ-WCHK4-ZPZUA-DSIXI-XDDVQ-XWFX7",
filing_no: "陕ICP备14011498号-3",
icp_link: "https://beian.miit.gov.cn/",
module_config: ["秒杀", "砍价", "拼团"] as string[],
remote_login_url: "",
waf_type: 2,
waf_config: "/\\.\\.\\//\n/\\<\\?/\n/\\bor\\b.*=\\s*\\*/i\n/(select[\\s\\S]*?)(from|limit)/i\n/(union[\\s\\S]*?select)/i\n/(having\\s+updatexml|extractvalue)/i"
})
const handleUpload = (field: string) => {
uni.showToast({ title: "选择文件: " + field, icon: "none" })
}
const handleSubmit = () => {
uni.showLoading({ title: "保存中..." })
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: "保存成功", icon: "success" })
}, 800)
}
</script>
<style scoped lang="scss">
.page-container {
padding: 20px;
min-height: 100vh;
background: #f5f5f5;
<style scoped>
.settings-card {
box-shadow: 0 1px 4px rgba(0,21,41,0.08);
}
.page-header {
margin-bottom: 20px;
}
/* 核心修复:确保 Tabs 横向排列并且可以滑动 */
.tabs-container { margin-bottom: 30px; border-bottom: 1px solid #e8eaec; width: 100%; overflow: hidden; }
.tabs-scroll { width: 100%; white-space: nowrap; }
.tabs-bar { display: inline-flex; flex-direction: row; min-width: 100%; }
.page-title {
display: block;
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
.tab-item {
display: inline-flex;
flex-direction: row;
align-items: center;
padding: 12px 24px;
font-size: 14px;
color: #515a6e;
position: relative;
cursor: pointer;
flex-shrink: 0;
}
.tab-item.active { color: #1890ff; font-weight: bold; }
.tab-line { position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background-color: #1890ff; }
.page-subtitle {
display: block;
font-size: 14px;
color: #999;
}
.form-container { padding-left: 20px; }
.page-content {
background: #fff;
border-radius: 4px;
padding: 24px;
}
/* 核心修复:确保表单项横向排列 (Label left, Input right) */
.form-item { display: flex; flex-direction: row; margin-bottom: 25px; align-items: flex-start; }
.placeholder-card {
text-align: center;
padding: 60px 20px;
}
.form-label { width: 140px; font-size: 14px; color: #303133; text-align: right; padding-right: 20px; padding-top: 8px; flex-shrink: 0; }
.form-right { flex: 1; display: flex; flex-direction: column; width: 100%; }
.placeholder-title {
display: block;
font-size: 18px;
font-weight: 600;
color: #666;
margin-bottom: 12px;
}
.form-input { border: 1px solid #dcdfe6; width: 450px; height: 32px; padding: 0 15px; border-radius: 4px; font-size: 14px; color: #606266; outline: none; transition: border-color .2s; }
.form-input:focus { border-color: #409eff; }
.placeholder-desc {
display: block;
font-size: 14px;
color: #999;
margin-bottom: 8px;
}
.form-textarea { border: 1px solid #dcdfe6; width: 550px; height: 120px; padding: 10px 15px; border-radius: 4px; font-size: 14px; color: #606266; line-height: 1.6; outline: none; }
.form-textarea:focus { border-color: #409eff; }
.placeholder-info {
display: block;
font-size: 12px;
color: #1890ff;
}
</style>
.waf-textarea { height: 200px; }
.code-bg { background-color: #f5f7fa; font-family: "Lucida Console", Monaco, monospace; }
.form-tip { font-size: 12px; color: #c0c4cc; margin-top: 8px; line-height: 1.5; width: 550px; }
.radio-group, .checkbox-group { display: flex; flex-direction: row; align-items: center; min-height: 32px; }
.radio-label, .checkbox-label { display: flex; flex-direction: row; align-items: center; margin-right: 30px; font-size: 14px; color: #606266; cursor: pointer; }
.upload-placeholder { width: 80px; height: 80px; border: 1px dashed #dcdfe6; border-radius: 4px; display: flex; align-items: center; justify-content: center; font-size: 12px; color: #909399; cursor: pointer; transition: all .2s; }
.upload-placeholder:hover { border-color: #409eff; color: #409eff; }
.submit-section { display: flex; flex-direction: row; margin-top: 20px; padding-top: 10px; }
.btn-submit { background-color: #1890ff; color: #fff; width: 65px; height: 32px; line-height: 32px; font-size: 14px; border-radius: 4px; margin: 0; border: none; cursor: pointer; text-align: center; }
.btn-submit:active { background-color: #096dd9; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">消息管理</text>
</view>
<view class="page-content">
<text class="placeholder-text">消息管理 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">小票配置</text>
</view>
<view class="page-content">
<text class="placeholder-text">小票配置 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -0,0 +1,23 @@
<template>
<view class="admin-page-container">
<view class="page-card">
<view class="page-header">
<text class="page-title">客服设置</text>
</view>
<view class="page-content">
<text class="placeholder-text">客服设置 页面开发中...</text>
</view>
</view>
</view>
</template>
<script setup lang="uts">
</script>
<style scoped>
.admin-page-container { padding: 20px; background-color: #f5f7f9; min-height: 100vh; }
.page-card { background-color: #fff; border-radius: 4px; padding: 20px; box-shadow: 0 1px 4px rgba(0,21,41,0.08); }
.page-header { margin-bottom: 20px; border-bottom: 1px solid #f0f0f0; padding-bottom: 15px; }
.page-title { font-size: 16px; font-weight: bold; color: #303133; }
.placeholder-text { font-size: 14px; color: #909399; }
</style>

View File

@@ -1,65 +0,0 @@
<template>
<AdminLayout currentPage="sys-basic">
<view class="Page">
<view class="Header">
<text class="Title">设置</text>
<text class="SubTitle">system-settings</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

@@ -1,70 +0,0 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="Page">
<view class="Header">
<text class="Title">用户</text>
<text class="SubTitle">user-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 currentPage = ref('user-list') // 默认
onLoad((opts: Record<string, string>) => {
const action = opts['action'] || ''
if (action == 'group') currentPage.value = 'user-group'
else if (action == 'tag') currentPage.value = 'user-tag'
else if (action == 'level') currentPage.value = 'user-level'
else if (action == 'config') currentPage.value = 'user-config'
else currentPage.value = 'user-list'
})
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -1,65 +0,0 @@
<template>
<AdminLayout currentPage="user">
<view class="Page">
<view class="Header">
<text class="Title">用户统计</text>
<text class="SubTitle">user-statistics</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

@@ -74,7 +74,7 @@
</view>
<view class="item-content">
<view class="avatar-upload">
<image class="avatar-preview" src="https://img.crmeb.com/crmeb_demo/75211.png" mode="aspectFill" />
<image class="avatar-preview" src="/static/logo.png" mode="aspectFill" />
<view class="upload-mask">
<text class="upload-icon">+</text>
</view>

View File

@@ -1,8 +1,9 @@
<template>
<view class="user-list-page">
<!-- 筛选面板 -->
<view class="filter-card">
<view class="filter-row">
<view class="admin-page">
<view class="admin-sections">
<!-- 筛选面板 -->
<view class="admin-card filter-card">
<view class="filter-row">
<view class="filter-item">
<text class="label">用户搜索:</text>
<view class="input-group">
@@ -39,7 +40,7 @@
</view>
<!-- 内容卡片 -->
<view class="content-card">
<view class="admin-card content-card">
<!-- 平台切换 Tabs -->
<view class="tabs-row">
<view
@@ -143,6 +144,7 @@
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
@@ -154,15 +156,15 @@ const isAllChecked = ref(false)
const activeDropdownId = ref<string | null>(null)
const userList = ref([
{ id: '77414', avatar: 'https://img.crmeb.com/crmeb_demo/77414.png', nickname: '199****0268', isMember: '否', level: '无', group: '无', spreadLevel: '', phone: '199****0268', userType: '公众号', balance: '88888.00', checked: false },
{ id: '75311', avatar: 'https://img.crmeb.com/crmeb_demo/75311.png', nickname: 'wljbhg', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100002.00', checked: false },
{ id: '75305', avatar: 'https://img.crmeb.com/crmeb_demo/75305.png', nickname: '相见欢', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75296', avatar: 'https://img.crmeb.com/crmeb_demo/75296.png', nickname: '..', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75293', avatar: 'https://img.crmeb.com/crmeb_demo/75293.png', nickname: '钟(钏)华', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75289', avatar: 'https://img.crmeb.com/crmeb_demo/75289.png', nickname: '小二上酒', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75257', avatar: 'https://img.crmeb.com/crmeb_demo/75257.png', nickname: '5+7', isMember: '是', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75226', avatar: 'https://img.crmeb.com/crmeb_demo/75226.png', nickname: '慢步前行', isMember: '是', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75211', avatar: 'https://img.crmeb.com/crmeb_demo/75211.png', nickname: '难得糊涂', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false }
{ id: '77414', avatar: '/static/logo.png', nickname: '199****0268', isMember: '否', level: '无', group: '无', spreadLevel: '', phone: '199****0268', userType: '公众号', balance: '88888.00', checked: false },
{ id: '75311', avatar: '/static/logo.png', nickname: 'wljbhg', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100002.00', checked: false },
{ id: '75305', avatar: '/static/logo.png', nickname: '相见欢', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75296', avatar: '/static/logo.png', nickname: '..', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75293', avatar: '/static/logo.png', nickname: '钟(钏)华', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75289', avatar: '/static/logo.png', nickname: '小二上酒', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75257', avatar: '/static/logo.png', nickname: '5+7', isMember: '是', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75226', avatar: '/static/logo.png', nickname: '慢步前行', isMember: '是', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75211', avatar: '/static/logo.png', nickname: '难得糊涂', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false }
])
function onSearch() {
@@ -183,18 +185,8 @@ function onDetail(user: any) {
</script>
<style scoped lang="scss">
.user-list-page {
padding: 16px;
background-color: #f0f2f5;
min-height: 100vh;
}
/* 筛选卡片 */
.filter-card {
background: #fff;
border-radius: 4px;
padding: 24px;
margin-bottom: 16px;
}
.filter-row {
@@ -307,8 +299,6 @@ function onDetail(user: any) {
/* 内容卡片 */
.content-card {
background: #fff;
border-radius: 4px;
padding: 0;
overflow: visible; /* 必须 visible 以显示下拉菜单 */
}

View File

@@ -1,25 +0,0 @@
<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>('user-config')
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

@@ -1,25 +0,0 @@
<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>('user-group')
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

@@ -1,25 +0,0 @@
<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>('user-tag')
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

@@ -1,25 +0,0 @@
<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>('user-level')
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

@@ -1,25 +0,0 @@
<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>('user-statistics')
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>