fix(ui): polish miniprogram and admin console details
小程序修复: - 首页/我的/记录页移除自定义头部,使用原生导航栏 - tabBar '护理记录' 改为 '记录' - 智能模式跳转auto-scan页面(不再跳过) - 扫码区域尺寸 300→360rpx,图标 100→120rpx - 护理中倒计时 120→144rpx - 完成页图标 120→140rpx - 完成页时长格式 '10分0秒' → '10分钟' - 蓝牙连接 '连接中' → '点击连接' - 订阅套餐默认选中年卡 - 订阅成功页改为动态数据(非硬编码) - 我的页增加退出登录按钮 - 护理记录日期支持'今天/昨天'相对格式 - 扫描进度居中 - 锁定模式图标加灰度滤镜 管理后台修复: - 所有页面移除重复页面标题 - 仪表盘统计卡片改为垂直布局 - 快捷操作图标统一粉色+箭头 - 设备管理搜索placeholder和库存badge修正 - 用户管理头像、次后缀、月卡/试用badge颜色修正 - 订阅管理stat背景改白色、过期改红色、tab切换修复 - 护理记录模式badge修正 - 操作日志改为平铺列表 - 系统设置开关改绿色、表单改水平布局 - 全局背景色 #f5f5f5 → #f0f2f5
这个提交包含在:
@@ -13,7 +13,7 @@ export default {
|
||||
|
||||
<style>
|
||||
page {
|
||||
background-color: #f5f5f5;
|
||||
background-color: #f0f2f5;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
|
||||
@@ -3,34 +3,26 @@
|
||||
<view class="stat-grid">
|
||||
<view class="stat-card">
|
||||
<view class="stat-icon blue">📱</view>
|
||||
<view class="stat-info">
|
||||
<text class="stat-value">{{ stats.device_count || 0 }}</text>
|
||||
<text class="stat-label">绑定设备数</text>
|
||||
</view>
|
||||
<text class="stat-value">{{ stats.device_count || 0 }}</text>
|
||||
<text class="stat-label">绑定设备数</text>
|
||||
<text class="stat-trend blue-text">↑ 12%</text>
|
||||
</view>
|
||||
<view class="stat-card">
|
||||
<view class="stat-icon green">👥</view>
|
||||
<view class="stat-info">
|
||||
<text class="stat-value">{{ stats.user_count || 0 }}</text>
|
||||
<text class="stat-label">注册用户数</text>
|
||||
</view>
|
||||
<text class="stat-value">{{ stats.user_count || 0 }}</text>
|
||||
<text class="stat-label">注册用户数</text>
|
||||
<text class="stat-trend green-text">↑ 8%</text>
|
||||
</view>
|
||||
<view class="stat-card">
|
||||
<view class="stat-icon pink">💆</view>
|
||||
<view class="stat-info">
|
||||
<text class="stat-value">{{ stats.treatment_count || 0 }}</text>
|
||||
<text class="stat-label">护理次数</text>
|
||||
</view>
|
||||
<text class="stat-value">{{ stats.treatment_count || 0 }}</text>
|
||||
<text class="stat-label">护理次数</text>
|
||||
<text class="stat-trend pink-text">↑ 15%</text>
|
||||
</view>
|
||||
<view class="stat-card">
|
||||
<view class="stat-icon gold">💰</view>
|
||||
<view class="stat-info">
|
||||
<text class="stat-value">¥{{ stats.monthly_revenue || 0 }}</text>
|
||||
<text class="stat-label">本月收入</text>
|
||||
</view>
|
||||
<text class="stat-value">¥{{ stats.monthly_revenue || 0 }}</text>
|
||||
<text class="stat-label">本月收入</text>
|
||||
<text class="stat-trend gold-text">↑ 6%</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -57,7 +49,7 @@
|
||||
<text class="t-td" style="flex:2">{{ item.user || '-' }}</text>
|
||||
<text class="t-td" style="flex:2">{{ item.device || '-' }}</text>
|
||||
<text class="t-td" style="flex:1">
|
||||
<text class="badge badge-pink">✨智能</text>
|
||||
<text class="badge badge-blue">✨ 智能模式</text>
|
||||
</text>
|
||||
<text class="t-td" style="flex:1">{{ item.duration || '-' }}</text>
|
||||
<text class="t-td" style="flex:2">{{ item.time || '-' }}</text>
|
||||
@@ -73,20 +65,24 @@
|
||||
<view class="card-title">快捷操作</view>
|
||||
<view class="quick-list">
|
||||
<view class="quick-item" @click="onNav('/pages/device/index')">
|
||||
<view class="quick-icon" style="background:#e6f7ff">📱</view>
|
||||
<view class="quick-icon" style="background:#E6508C">📱</view>
|
||||
<text class="quick-label">设备管理</text>
|
||||
<text class="quick-arrow">›</text>
|
||||
</view>
|
||||
<view class="quick-item" @click="onNav('/pages/user/index')">
|
||||
<view class="quick-icon" style="background:#f6ffed">👥</view>
|
||||
<view class="quick-icon" style="background:#E6508C">👥</view>
|
||||
<text class="quick-label">用户管理</text>
|
||||
<text class="quick-arrow">›</text>
|
||||
</view>
|
||||
<view class="quick-item" @click="onNav('/pages/subscription/index')">
|
||||
<view class="quick-icon" style="background:#fff7e6">💳</view>
|
||||
<view class="quick-icon" style="background:#E6508C">💳</view>
|
||||
<text class="quick-label">订阅管理</text>
|
||||
<text class="quick-arrow">›</text>
|
||||
</view>
|
||||
<view class="quick-item" @click="onNav('/pages/record/index')">
|
||||
<view class="quick-icon" style="background:#fff0f6">📋</view>
|
||||
<view class="quick-icon" style="background:#E6508C">📋</view>
|
||||
<text class="quick-label">数据报表</text>
|
||||
<text class="quick-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -141,8 +137,10 @@ export default {
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
gap: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
@@ -153,7 +151,6 @@ export default {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stat-icon.blue {
|
||||
@@ -172,16 +169,11 @@ export default {
|
||||
background: #fff7e6;
|
||||
}
|
||||
|
||||
.stat-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
@@ -292,9 +284,9 @@ export default {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.badge-pink {
|
||||
background: #fff0f6;
|
||||
color: #E6508C;
|
||||
.badge-blue {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.quick-list {
|
||||
@@ -325,10 +317,17 @@ export default {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.quick-label {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.quick-arrow {
|
||||
font-size: 18px;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
<template>
|
||||
<AdminLayout currentPage="/pages/device/index">
|
||||
<view class="page-card">
|
||||
<view class="page-header">
|
||||
<text class="page-title">📱 设备管理</text>
|
||||
<view class="header-actions">
|
||||
<input
|
||||
class="search-input"
|
||||
v-model="keyword"
|
||||
placeholder="搜索设备编号"
|
||||
placeholder-class="input-placeholder"
|
||||
@confirm="onSearch"
|
||||
/>
|
||||
<button class="btn-primary btn-sm" @click="onSearch">搜索</button>
|
||||
<button class="btn-default btn-sm">导出</button>
|
||||
</view>
|
||||
<view class="toolbar">
|
||||
<view class="header-actions">
|
||||
<input
|
||||
class="search-input"
|
||||
v-model="keyword"
|
||||
placeholder="搜索设备编号/用户..."
|
||||
placeholder-class="input-placeholder"
|
||||
@confirm="onSearch"
|
||||
/>
|
||||
<button class="btn-primary btn-sm" @click="onSearch">搜索</button>
|
||||
<button class="btn-default btn-sm">导出</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="page-card">
|
||||
<view class="data-table">
|
||||
<view class="t-header">
|
||||
<view class="t-row">
|
||||
@@ -100,7 +99,7 @@ export default {
|
||||
uni.navigateTo({ url: '/pages/device-detail/index?device_id=' + deviceId })
|
||||
},
|
||||
statusBadge(status) {
|
||||
const map = { 2: 'badge badge-success', 3: 'badge badge-warning', 1: 'badge badge-default', 4: 'badge badge-error' }
|
||||
const map = { 2: 'badge badge-success', 3: 'badge badge-warning', 1: 'badge badge-blue', 4: 'badge badge-error' }
|
||||
return map[status] || 'badge badge-default'
|
||||
},
|
||||
formatDate(d) {
|
||||
@@ -111,23 +110,13 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-card {
|
||||
.toolbar {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
padding: 12px 20px;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
@@ -136,6 +125,12 @@ export default {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.page-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 220px;
|
||||
height: 32px;
|
||||
@@ -234,6 +229,11 @@ export default {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.badge-blue {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.action-link {
|
||||
color: #E6508C;
|
||||
font-size: 14px;
|
||||
@@ -244,7 +244,7 @@ export default {
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
@@ -1,38 +1,29 @@
|
||||
<template>
|
||||
<AdminLayout currentPage="/pages/log/index">
|
||||
<view class="page-card">
|
||||
<view class="page-header">
|
||||
<text class="page-title">📝 操作日志</text>
|
||||
<view class="header-actions">
|
||||
<input
|
||||
class="search-input"
|
||||
v-model="filters.type"
|
||||
placeholder="搜索操作类型"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
<button class="btn-primary btn-sm" @click="onSearch">搜索</button>
|
||||
<button class="btn-default btn-sm">导出</button>
|
||||
</view>
|
||||
<view class="toolbar">
|
||||
<view class="header-actions">
|
||||
<input
|
||||
class="search-input"
|
||||
v-model="filters.type"
|
||||
placeholder="搜索操作类型"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
<button class="btn-primary btn-sm" @click="onSearch">搜索</button>
|
||||
<button class="btn-default btn-sm">导出</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="page-card">
|
||||
<view class="log-list">
|
||||
<view class="log-item" v-for="item in logs" :key="item.id">
|
||||
<view class="log-left">
|
||||
<view class="log-dot"></view>
|
||||
<view class="log-line"></view>
|
||||
</view>
|
||||
<text class="log-time">{{ formatDate(item.created_at) }}</text>
|
||||
<view class="log-content">
|
||||
<view class="log-header">
|
||||
<text class="log-time">{{ formatDate(item.created_at) }}</text>
|
||||
<text :class="logTypeBadge(item.operator_type)">{{ logTypeText(item.operator_type) }}</text>
|
||||
</view>
|
||||
<view class="log-body">
|
||||
<text :class="logTypeBadge(item.operator_type)">{{ logTypeText(item.operator_type) }}</text>
|
||||
<text class="log-text">
|
||||
<text class="log-actor" :class="logActorClass(item.operator_type)">{{ item.operator_type === 1 ? '管理员' : '系统' }}</text>
|
||||
<text class="log-text"> 对 </text>
|
||||
<text class="log-target">{{ item.target_type }} {{ item.target_id || '' }}</text>
|
||||
<text class="log-text"> 执行了 </text>
|
||||
<text class="log-action">{{ item.action }}</text>
|
||||
</view>
|
||||
对 <text class="log-target">{{ item.target_type }} {{ item.target_id || '' }}</text>
|
||||
执行了 <text class="log-action">{{ item.action }}</text>
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -112,23 +103,13 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-card {
|
||||
.toolbar {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
padding: 12px 20px;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
@@ -137,6 +118,12 @@ export default {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.page-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 220px;
|
||||
height: 32px;
|
||||
@@ -180,48 +167,24 @@ export default {
|
||||
|
||||
.log-item {
|
||||
display: flex;
|
||||
min-height: 60px;
|
||||
align-items: flex-start;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.log-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 20px;
|
||||
.log-time {
|
||||
width: 140px;
|
||||
flex-shrink: 0;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.log-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: #d9d9d9;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.log-line {
|
||||
width: 1px;
|
||||
flex: 1;
|
||||
background: #f0f0f0;
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.log-content {
|
||||
flex: 1;
|
||||
padding-bottom: 16px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.log-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.log-time {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
@@ -229,6 +192,7 @@ export default {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.badge-pink {
|
||||
@@ -246,9 +210,10 @@ export default {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.log-body {
|
||||
.log-text {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.log-actor {
|
||||
@@ -267,10 +232,6 @@ export default {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.log-text {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.log-target {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
<template>
|
||||
<AdminLayout currentPage="/pages/record/index">
|
||||
<view class="page-card">
|
||||
<view class="page-header">
|
||||
<text class="page-title">📋 护理记录</text>
|
||||
<view class="header-actions">
|
||||
<input class="search-input date-input" v-model="dateFrom" placeholder="开始日期" />
|
||||
<text class="date-sep">~</text>
|
||||
<input class="search-input date-input" v-model="dateTo" placeholder="结束日期" />
|
||||
<input
|
||||
class="search-input"
|
||||
v-model="keyword"
|
||||
placeholder="搜索用户"
|
||||
placeholder-class="input-placeholder"
|
||||
@confirm="onSearch"
|
||||
/>
|
||||
<button class="btn-primary btn-sm" @click="onSearch">搜索</button>
|
||||
<button class="btn-default btn-sm">导出</button>
|
||||
</view>
|
||||
<view class="toolbar">
|
||||
<view class="header-actions">
|
||||
<input class="search-input date-input" v-model="dateFrom" placeholder="开始日期" />
|
||||
<text class="date-sep">~</text>
|
||||
<input class="search-input date-input" v-model="dateTo" placeholder="结束日期" />
|
||||
<input
|
||||
class="search-input"
|
||||
v-model="keyword"
|
||||
placeholder="搜索用户"
|
||||
placeholder-class="input-placeholder"
|
||||
@confirm="onSearch"
|
||||
/>
|
||||
<button class="btn-primary btn-sm" @click="onSearch">搜索</button>
|
||||
<button class="btn-default btn-sm">导出</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="page-card">
|
||||
<view class="data-table">
|
||||
<view class="t-header">
|
||||
<view class="t-row">
|
||||
@@ -38,8 +37,8 @@
|
||||
<text class="t-td flex1">{{ item.nickname || item.user_id || '-' }}</text>
|
||||
<text class="t-td flex2">{{ item.device_id || '-' }}</text>
|
||||
<text class="t-td flex1">
|
||||
<text :class="item.mode === 'smart' ? 'badge badge-pink' : 'badge badge-blue'">
|
||||
{{ item.mode === 'smart' ? '✨智能' : '🔄普通' }}
|
||||
<text :class="item.mode === 'smart' ? 'badge badge-blue' : 'badge badge-warning'">
|
||||
{{ item.mode === 'smart' ? '✨ 智能模式' : '🔄 普通模式' }}
|
||||
</text>
|
||||
</text>
|
||||
<text class="t-td flex1">{{ formatRegions(item.regions) }}</text>
|
||||
@@ -132,23 +131,13 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-card {
|
||||
.toolbar {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
padding: 12px 20px;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
@@ -157,6 +146,12 @@ export default {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.page-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 160px;
|
||||
height: 32px;
|
||||
@@ -244,16 +239,16 @@ export default {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.badge-pink {
|
||||
background: #fff0f6;
|
||||
color: #E6508C;
|
||||
}
|
||||
|
||||
.badge-blue {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
background: #fffbe6;
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
.action-link {
|
||||
color: #E6508C;
|
||||
font-size: 14px;
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
<template>
|
||||
<AdminLayout currentPage="/pages/settings/index">
|
||||
<view class="page-card">
|
||||
<view class="card-title">⚙️ 系统设置</view>
|
||||
|
||||
<view class="section-card">
|
||||
<view class="section-title">基础配置</view>
|
||||
<view class="form-grid">
|
||||
<view class="form-group">
|
||||
<view class="form-row">
|
||||
<text class="form-label">系统名称</text>
|
||||
<input class="form-input" v-model="settings.system_name" placeholder="系统名称" />
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<view class="form-row">
|
||||
<text class="form-label">管理员邮箱</text>
|
||||
<input class="form-input" v-model="settings.admin_email" placeholder="管理员邮箱" />
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<view class="form-row">
|
||||
<text class="form-label">系统时区</text>
|
||||
<view class="select-wrap">
|
||||
<picker :range="timezoneOptions" @change="onTimezoneChange">
|
||||
@@ -31,21 +29,21 @@
|
||||
<view class="section-card">
|
||||
<view class="section-title">订阅配置</view>
|
||||
<view class="form-grid">
|
||||
<view class="form-group">
|
||||
<view class="form-row">
|
||||
<text class="form-label">月卡价格</text>
|
||||
<view class="input-suffix">
|
||||
<input class="form-input" v-model="settings.monthly_price" type="number" />
|
||||
<text class="suffix">元/月</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<view class="form-row">
|
||||
<text class="form-label">年卡价格</text>
|
||||
<view class="input-suffix">
|
||||
<input class="form-input" v-model="settings.yearly_price" type="number" />
|
||||
<text class="suffix">元/年</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<view class="form-row">
|
||||
<text class="form-label">试用时长</text>
|
||||
<view class="input-suffix">
|
||||
<input class="form-input" v-model="settings.trial_days" type="number" />
|
||||
@@ -166,13 +164,6 @@ export default {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
@@ -190,20 +181,23 @@ export default {
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
width: 140px;
|
||||
flex-shrink: 0;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
@@ -214,12 +208,14 @@ export default {
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.input-suffix {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.input-suffix .form-input {
|
||||
@@ -234,6 +230,7 @@ export default {
|
||||
|
||||
.select-wrap {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.picker-input {
|
||||
@@ -289,7 +286,7 @@ export default {
|
||||
}
|
||||
|
||||
.toggle-switch.on {
|
||||
background: #E6508C;
|
||||
background: #52c41a;
|
||||
}
|
||||
|
||||
.toggle-dot {
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
<template>
|
||||
<AdminLayout currentPage="/pages/subscription/index">
|
||||
<view class="page-card">
|
||||
<view class="page-header">
|
||||
<text class="page-title">💳 订阅管理</text>
|
||||
<view class="header-actions">
|
||||
<button class="btn-default btn-sm">导出报表</button>
|
||||
</view>
|
||||
<view class="toolbar">
|
||||
<view class="header-actions">
|
||||
<button class="btn-default btn-sm">导出报表</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="page-card">
|
||||
<view class="stats-row">
|
||||
<view class="stat-item green">
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">{{ stats.monthly_count || 486 }}</text>
|
||||
<text class="stat-label">月卡会员</text>
|
||||
</view>
|
||||
<view class="stat-item green">
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">{{ stats.yearly_count || 1258 }}</text>
|
||||
<text class="stat-label">年卡会员</text>
|
||||
</view>
|
||||
<view class="stat-item gold">
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">{{ stats.trial_count || 856 }}</text>
|
||||
<text class="stat-label">试用中</text>
|
||||
</view>
|
||||
@@ -30,11 +29,11 @@
|
||||
|
||||
<view class="page-card">
|
||||
<view class="tabs">
|
||||
<view class="tab active">全部订阅</view>
|
||||
<view class="tab" @click="onTabFilter('monthly')">月卡</view>
|
||||
<view class="tab" @click="onTabFilter('yearly')">年卡</view>
|
||||
<view class="tab" @click="onTabFilter('trial')">试用中</view>
|
||||
<view class="tab" @click="onTabFilter('expired')">已过期</view>
|
||||
<view class="tab" :class="{ active: activeTab === 'all' }" @click="onTabFilter('all')">全部订阅</view>
|
||||
<view class="tab" :class="{ active: activeTab === 'monthly' }" @click="onTabFilter('monthly')">月卡</view>
|
||||
<view class="tab" :class="{ active: activeTab === 'yearly' }" @click="onTabFilter('yearly')">年卡</view>
|
||||
<view class="tab" :class="{ active: activeTab === 'trial' }" @click="onTabFilter('trial')">试用中</view>
|
||||
<view class="tab" :class="{ active: activeTab === 'expired' }" @click="onTabFilter('expired')">已过期</view>
|
||||
</view>
|
||||
|
||||
<view class="data-table">
|
||||
@@ -172,7 +171,7 @@ export default {
|
||||
return map[status] || '-'
|
||||
},
|
||||
statusBadge(status) {
|
||||
const map = { 1: 'badge badge-success', 2: 'badge badge-warning', 3: 'badge badge-default' }
|
||||
const map = { 1: 'badge badge-success', 2: 'badge badge-warning', 3: 'badge badge-error' }
|
||||
return map[status] || 'badge badge-default'
|
||||
},
|
||||
formatDate(d) {
|
||||
@@ -183,24 +182,13 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-card {
|
||||
.toolbar {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
padding: 12px 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
@@ -209,26 +197,25 @@ export default {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.page-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.stats-row {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
flex: 1;
|
||||
background: #fafafa;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-item.green {
|
||||
background: #f6ffed;
|
||||
}
|
||||
|
||||
.stat-item.gold {
|
||||
background: #fffbe6;
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
@@ -239,14 +226,6 @@ export default {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.green .stat-value {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.gold .stat-value {
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
@@ -324,6 +303,11 @@ export default {
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
.badge-error {
|
||||
background: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.badge-default {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
<template>
|
||||
<AdminLayout currentPage="/pages/user/index">
|
||||
<view class="page-card">
|
||||
<view class="page-header">
|
||||
<text class="page-title">👥 用户管理</text>
|
||||
<view class="header-actions">
|
||||
<input
|
||||
class="search-input"
|
||||
v-model="keyword"
|
||||
placeholder="搜索用户昵称"
|
||||
placeholder-class="input-placeholder"
|
||||
@confirm="onSearch"
|
||||
/>
|
||||
<button class="btn-primary btn-sm" @click="onSearch">搜索</button>
|
||||
<button class="btn-default btn-sm">导出用户</button>
|
||||
</view>
|
||||
<view class="toolbar">
|
||||
<view class="header-actions">
|
||||
<input
|
||||
class="search-input"
|
||||
v-model="keyword"
|
||||
placeholder="搜索用户/手机号..."
|
||||
placeholder-class="input-placeholder"
|
||||
@confirm="onSearch"
|
||||
/>
|
||||
<button class="btn-primary btn-sm" @click="onSearch">搜索</button>
|
||||
<button class="btn-default btn-sm">导出用户</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="page-card">
|
||||
<view class="data-table">
|
||||
<view class="t-header">
|
||||
<view class="t-row">
|
||||
@@ -30,10 +29,15 @@
|
||||
</view>
|
||||
<view class="t-body">
|
||||
<view class="t-row" v-for="item in users" :key="item.user_id">
|
||||
<text class="t-td flex2">👤 {{ item.nickname || '-' }}</text>
|
||||
<text class="t-td flex2">
|
||||
<view class="user-cell">
|
||||
<view class="avatar-circle">👤</view>
|
||||
<text>{{ item.nickname || '-' }}</text>
|
||||
</view>
|
||||
</text>
|
||||
<text class="t-td flex2">{{ item.phone || '-' }}</text>
|
||||
<text class="t-td flex1">{{ item.device_count || 0 }}</text>
|
||||
<text class="t-td flex1">{{ item.treatment_count || 0 }}</text>
|
||||
<text class="t-td flex1">{{ item.treatment_count || 0 }}次</text>
|
||||
<text class="t-td flex1">
|
||||
<text :class="subBadge(item.subscription_status)">{{ subStatusText(item.subscription_status) }}</text>
|
||||
</text>
|
||||
@@ -105,9 +109,9 @@ export default {
|
||||
subBadge(status) {
|
||||
const map = {
|
||||
yearly: 'badge badge-success',
|
||||
monthly: 'badge badge-success',
|
||||
trial: 'badge badge-warning',
|
||||
none: 'badge badge-default'
|
||||
monthly: 'badge badge-warning',
|
||||
trial: 'badge badge-blue',
|
||||
none: 'badge badge-blue'
|
||||
}
|
||||
return map[status] || 'badge badge-default'
|
||||
},
|
||||
@@ -119,23 +123,13 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-card {
|
||||
.toolbar {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
padding: 12px 20px;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
@@ -144,6 +138,12 @@ export default {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.page-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 220px;
|
||||
height: 32px;
|
||||
@@ -215,6 +215,24 @@ export default {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.user-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.avatar-circle {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
background: #f0f0f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
@@ -232,6 +250,11 @@ export default {
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
.badge-blue {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.badge-default {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
@@ -247,7 +270,7 @@ export default {
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/history/history",
|
||||
"text": "护理记录"
|
||||
"text": "记录"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/profile/profile",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
border-radius: 24rpx;
|
||||
font-size: 26rpx;
|
||||
color: #006699;
|
||||
display: inline-block;
|
||||
width: fit-content;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<view class="found-card" wx:if="{{state === 'binding'}}">
|
||||
<view class="found-icon">💆</view>
|
||||
<view class="found-name">光子美容仪-{{deviceId}}</view>
|
||||
<view class="status-badge status-badge-green">连接中</view>
|
||||
<text class="status-badge status-badge-green">点击连接</text>
|
||||
</view>
|
||||
|
||||
<view class="found-card" wx:if="{{state === 'error'}}">
|
||||
|
||||
@@ -54,7 +54,7 @@ Page({
|
||||
r.duration_text = durationMin + '分钟'
|
||||
r.region_names = ble.getRegionName(r.regions || 0).join('、')
|
||||
r.wavelength_name = ble.getWavelengthName(r.wavelength || 2)
|
||||
r.date_text = r.start_time ? r.start_time.slice(0, 10) : ''
|
||||
r.date_text = self.formatDate(r.start_time)
|
||||
|
||||
if (r.start_time && new Date(r.start_time) >= monthStart) {
|
||||
monthCount++
|
||||
@@ -74,5 +74,19 @@ Page({
|
||||
}).catch(function () {
|
||||
self.setData({ loading: false, loadingMore: false })
|
||||
})
|
||||
},
|
||||
|
||||
formatDate: function (dateStr) {
|
||||
if (!dateStr) return ''
|
||||
var d = new Date(dateStr)
|
||||
var now = new Date()
|
||||
var today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
||||
var yesterday = new Date(today.getTime() - 86400000)
|
||||
var h = ('0' + d.getHours()).slice(-2)
|
||||
var m = ('0' + d.getMinutes()).slice(-2)
|
||||
|
||||
if (d >= today) return '今天 ' + h + ':' + m
|
||||
if (d >= yesterday) return '昨天 ' + h + ':' + m
|
||||
return (d.getMonth() + 1) + '月' + d.getDate() + '日 ' + h + ':' + m
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-pink">
|
||||
<view class="page-header-title">护理记录</view>
|
||||
<view class="page-header-subtitle">查看历史护理数据</view>
|
||||
</view>
|
||||
|
||||
<view class="page-content">
|
||||
<view class="stats-row">
|
||||
<view class="stat-card">
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-pink">
|
||||
<view class="page-header-title">首页</view>
|
||||
<view class="page-header-subtitle">光子美容仪</view>
|
||||
</view>
|
||||
|
||||
<view class="page-content" wx:if="{{!hasDevice}}">
|
||||
<view class="empty-device">
|
||||
<text class="empty-icon">📱</text>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"navigationBarTitleText": "我的",
|
||||
"navigationBarBackgroundColor": "#E6508C",
|
||||
"navigationBarBackgroundGradient": "linear-gradient(135deg, #E6508C 0%, #DCB982 100%)",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-gradient">
|
||||
<view class="page-header-title">我的</view>
|
||||
</view>
|
||||
|
||||
<view class="page-content">
|
||||
<view class="user-row">
|
||||
<view class="user-avatar">👤</view>
|
||||
@@ -47,5 +43,7 @@
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="logout-btn" bindtap="onLogout">退出登录</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -97,3 +97,13 @@
|
||||
color: #cccccc;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
text-align: center;
|
||||
color: #ff4d4f;
|
||||
font-size: 28rpx;
|
||||
margin-top: 32rpx;
|
||||
padding: 24rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.scan-area {
|
||||
width: 300rpx;
|
||||
height: 300rpx;
|
||||
width: 360rpx;
|
||||
height: 360rpx;
|
||||
margin: 40rpx auto;
|
||||
border: 6rpx dashed #E6508C;
|
||||
border-radius: 24rpx;
|
||||
@@ -12,7 +12,7 @@
|
||||
}
|
||||
|
||||
.scan-icon {
|
||||
font-size: 100rpx;
|
||||
font-size: 120rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Page({
|
||||
{ plan: 'monthly', price: '¥99', desc: '约3.3元/天' },
|
||||
{ plan: 'yearly', price: '¥899', desc: '省¥289' }
|
||||
],
|
||||
selected: null,
|
||||
selected: 'yearly',
|
||||
purchasing: false
|
||||
},
|
||||
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
Page({
|
||||
data: {},
|
||||
data: {
|
||||
planName: '年卡会员',
|
||||
expiryDate: ''
|
||||
},
|
||||
|
||||
onLoad: function () {
|
||||
var now = new Date()
|
||||
now.setFullYear(now.getFullYear() + 1)
|
||||
var y = now.getFullYear()
|
||||
var m = ('0' + (now.getMonth() + 1)).slice(-2)
|
||||
var d = ('0' + now.getDate()).slice(-2)
|
||||
this.setData({ expiryDate: y + '年' + m + '月' + d + '日' })
|
||||
},
|
||||
|
||||
onStartSmart: function () {
|
||||
wx.navigateTo({ url: '/pages/treatment-setup/treatment-setup' })
|
||||
wx.navigateTo({ url: '/pages/wear-check/wear-check' })
|
||||
},
|
||||
|
||||
onBackHome: function () {
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
|
||||
<view class="sub-card">
|
||||
<view class="sub-header">
|
||||
<text class="sub-name">✨ 年卡会员</text>
|
||||
<text class="sub-name">✨ {{planName}}</text>
|
||||
<text class="sub-status status-badge status-badge-green">已订阅</text>
|
||||
</view>
|
||||
<view class="sub-info">有效期至:2027年4月8日</view>
|
||||
<view class="sub-info">有效期至:{{expiryDate}}</view>
|
||||
</view>
|
||||
|
||||
<button class="btn-primary btn-primary-gold" bindtap="onStartSmart">立即体验智能模式</button>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
}
|
||||
|
||||
.countdown-time {
|
||||
font-size: 120rpx;
|
||||
font-size: 144rpx;
|
||||
font-weight: 700;
|
||||
color: #E6508C;
|
||||
display: block;
|
||||
|
||||
@@ -19,12 +19,19 @@ Page({
|
||||
var mins = Math.floor(durationMs / 60000)
|
||||
var secs = Math.floor((durationMs % 60000) / 1000)
|
||||
|
||||
var durationText = ''
|
||||
if (mins > 0) {
|
||||
durationText = mins + '分钟'
|
||||
} else {
|
||||
durationText = secs + '秒'
|
||||
}
|
||||
|
||||
this.setData({
|
||||
sessionId: options.session_id || '',
|
||||
regions: parseInt(options.regions) || 0,
|
||||
duration: durationMs,
|
||||
avgPd: options.avg_pd || 0,
|
||||
durationText: mins + '分' + secs + '秒',
|
||||
durationText: durationText,
|
||||
regionNames: ble.getRegionName(parseInt(options.regions) || 0)
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.result-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
background: #52c41a;
|
||||
border-radius: 50%;
|
||||
margin: 24rpx auto 20rpx;
|
||||
@@ -8,7 +8,7 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #ffffff;
|
||||
font-size: 60rpx;
|
||||
font-size: 64rpx;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
|
||||
@@ -61,6 +61,12 @@ Page({
|
||||
|
||||
self.setData({ submitting: true, error: '' })
|
||||
|
||||
if (self.data.selectedMode === 1) {
|
||||
self.setData({ submitting: false })
|
||||
wx.navigateTo({ url: '/pages/auto-scan/auto-scan' })
|
||||
return
|
||||
}
|
||||
|
||||
ble.setParams({
|
||||
region_mask: mask,
|
||||
wavelength: 2,
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.mode-option.locked .mode-icon {
|
||||
filter: grayscale(1);
|
||||
}
|
||||
|
||||
.mode-icon {
|
||||
font-size: 48rpx;
|
||||
display: block;
|
||||
|
||||
在新工单中引用
屏蔽一个用户