fix: improve UX and fix admin console display issues
Miniprogram: - Add back buttons to all custom-nav pages - Add debug/mock buttons (BLE connect, wear check, treatment) controlled by __DEV__ flag (hidden in prod) Admin console: - Fix log page using nonexistent fields (operator_type, target_type) now correctly reads admin_id/user_id/action/detail from API - Fix subscription date fields (started_at→start_time, expired_at→expire_time) - Wire up subscription detail/extend/renew action buttons - Wire up device detail "查看完整日志" button - Add "创建订阅" button to subscription toolbar - Fix subscription status mapping (2=expired, 3=cancelled) Docs: - Add detailed architecture docs for server, miniprogram, admin console
这个提交包含在:
@@ -1,4 +1,5 @@
|
||||
var ble = require('../../services/ble')
|
||||
var config = require('../../config/env')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -8,14 +9,24 @@ Page({
|
||||
regions: 0x7F,
|
||||
regionData: [],
|
||||
timeout: false,
|
||||
error: ''
|
||||
error: '',
|
||||
devMode: false
|
||||
},
|
||||
|
||||
onBack: function () {
|
||||
wx.navigateBack({
|
||||
fail: function () {
|
||||
wx.reLaunch({ url: '/pages/index/index' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onLoad: function (options) {
|
||||
var app = getApp()
|
||||
this.setData({
|
||||
statusBarHeight: app.globalData.statusBarHeight,
|
||||
regions: parseInt(options.regions) || 0x7F
|
||||
regions: parseInt(options.regions) || 0x7F,
|
||||
devMode: config.__DEV__ || false
|
||||
})
|
||||
this.startScan()
|
||||
},
|
||||
@@ -80,5 +91,17 @@ Page({
|
||||
}).catch(function (err) {
|
||||
wx.showToast({ title: err.error_msg || '启动失败', icon: 'none' })
|
||||
})
|
||||
},
|
||||
|
||||
onMockScanDone: function () {
|
||||
var self = this
|
||||
if (self._progressTimer) clearInterval(self._progressTimer)
|
||||
var mask = parseInt(self.options.regions) || 0x7F
|
||||
wx.redirectTo({
|
||||
url: '/pages/treating/treating?regions=' + mask +
|
||||
'&wavelength=2' +
|
||||
'&duration=600000' +
|
||||
'&mode=1'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
||||
<view class="page-header-title">自动扫描</view>
|
||||
<view class="page-header-subtitle">正在识别面部...</view>
|
||||
</view>
|
||||
@@ -15,6 +16,11 @@
|
||||
|
||||
<view class="scan-hint">请将面罩对准面部</view>
|
||||
|
||||
<view class="debug-actions" wx:if="{{devMode}}">
|
||||
<view class="debug-label">🔧 调试工具</view>
|
||||
<button class="btn-secondary btn-sm" bindtap="onMockScanDone">模拟扫描完成</button>
|
||||
</view>
|
||||
|
||||
<view class="scan-progress" wx:if="{{scanning}}">
|
||||
正在扫描... <text class="highlight">{{regionData.length || 3}}</text> 个区域
|
||||
</view>
|
||||
|
||||
@@ -5,6 +5,14 @@ Page({
|
||||
trialDays: 7
|
||||
},
|
||||
|
||||
onBack: function () {
|
||||
wx.navigateBack({
|
||||
fail: function () {
|
||||
wx.reLaunch({ url: '/pages/index/index' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onLoad: function (options) {
|
||||
var app = getApp()
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-green" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
||||
<view class="page-header-title">绑定成功</view>
|
||||
<view class="page-header-subtitle">设备已成功绑定</view>
|
||||
</view>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
var ble = require('../../services/ble')
|
||||
var http = require('../../utils/request')
|
||||
var config = require('../../config/env')
|
||||
var app = getApp()
|
||||
var SCAN_TIMEOUT = 15000
|
||||
|
||||
@@ -9,14 +10,24 @@ Page({
|
||||
deviceId: '',
|
||||
bindToken: '',
|
||||
state: 'scanning',
|
||||
error: ''
|
||||
error: '',
|
||||
devMode: false
|
||||
},
|
||||
|
||||
onBack: function () {
|
||||
wx.navigateBack({
|
||||
fail: function () {
|
||||
wx.reLaunch({ url: '/pages/index/index' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onLoad: function (options) {
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||
this.setData({
|
||||
deviceId: options.device_id || '',
|
||||
bindToken: options.bind_token || ''
|
||||
bindToken: options.bind_token || '',
|
||||
devMode: config.__DEV__ || false
|
||||
})
|
||||
this.startBleConnect()
|
||||
},
|
||||
@@ -83,5 +94,40 @@ Page({
|
||||
onRetry: function () {
|
||||
ble.disconnect()
|
||||
this.startBleConnect()
|
||||
},
|
||||
|
||||
onMockConnect: function () {
|
||||
var self = this
|
||||
clearTimeout(this._scanTimer)
|
||||
ble.stopScan()
|
||||
self.setData({ state: 'binding' })
|
||||
// Skip BLE bind, confirm directly with server
|
||||
http.post('/api/v1/device/bind/confirm', {
|
||||
device_id: self.data.deviceId,
|
||||
bind_token: self.data.bindToken
|
||||
}).then(function () {
|
||||
self.setData({ state: 'done' })
|
||||
wx.redirectTo({
|
||||
url: '/pages/bind-success/bind-success?device_id=' + self.data.deviceId
|
||||
})
|
||||
}).catch(function (err) {
|
||||
self.setData({ state: 'error', error: err.message || '模拟确认失败' })
|
||||
})
|
||||
},
|
||||
|
||||
onMockBindSuccess: function () {
|
||||
var self = this
|
||||
clearTimeout(this._scanTimer)
|
||||
ble.stopScan()
|
||||
http.post('/api/v1/device/bind/confirm', {
|
||||
device_id: self.data.deviceId,
|
||||
bind_token: self.data.bindToken
|
||||
}).then(function () {
|
||||
wx.redirectTo({
|
||||
url: '/pages/bind-success/bind-success?device_id=' + self.data.deviceId
|
||||
})
|
||||
}).catch(function (err) {
|
||||
self.setData({ state: 'error', error: err.message || '模拟确认失败' })
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
||||
<view class="page-header-title">蓝牙连接</view>
|
||||
<view class="page-header-subtitle">{{state === 'scanning' ? '正在搜索设备...' : state === 'connecting' ? '正在连接设备...' : state === 'binding' ? '正在绑定...' : '连接失败'}}</view>
|
||||
</view>
|
||||
@@ -26,5 +27,11 @@
|
||||
<view class="found-name">{{error || '连接失败'}}</view>
|
||||
<button class="retry-btn" bindtap="onRetry">重试</button>
|
||||
</view>
|
||||
|
||||
<view class="debug-actions" wx:if="{{devMode}}">
|
||||
<view class="debug-label">🔧 调试工具</view>
|
||||
<button class="btn-secondary btn-sm" bindtap="onMockConnect">模拟连接成功</button>
|
||||
<button class="btn-secondary btn-sm" bindtap="onMockBindSuccess">模拟绑定成功</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -8,6 +8,14 @@ Page({
|
||||
error: ''
|
||||
},
|
||||
|
||||
onBack: function () {
|
||||
wx.navigateBack({
|
||||
fail: function () {
|
||||
wx.reLaunch({ url: '/pages/index/index' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onLoad: function () {
|
||||
var app = getApp()
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
||||
<view class="page-header-title">扫码绑定</view>
|
||||
<view class="page-header-subtitle">扫描设备底部二维码</view>
|
||||
</view>
|
||||
|
||||
@@ -11,6 +11,14 @@ Page({
|
||||
purchasing: false
|
||||
},
|
||||
|
||||
onBack: function () {
|
||||
wx.navigateBack({
|
||||
fail: function () {
|
||||
wx.reLaunch({ url: '/pages/index/index' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onLoad: function () {
|
||||
var app = getApp()
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-gold" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
||||
<view class="page-header-title">订阅服务</view>
|
||||
<view class="page-header-subtitle">解锁智能模式</view>
|
||||
</view>
|
||||
|
||||
@@ -13,6 +13,10 @@ Page({
|
||||
},
|
||||
|
||||
onBack: function () {
|
||||
wx.switchTab({ url: '/pages/index/index' })
|
||||
wx.navigateBack({
|
||||
fail: function () {
|
||||
wx.reLaunch({ url: '/pages/index/index' })
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
||||
<view class="page-header-title">护理设置</view>
|
||||
<view class="page-header-subtitle">试用期已结束</view>
|
||||
</view>
|
||||
|
||||
@@ -5,6 +5,14 @@ Page({
|
||||
expiryDate: ''
|
||||
},
|
||||
|
||||
onBack: function () {
|
||||
wx.navigateBack({
|
||||
fail: function () {
|
||||
wx.reLaunch({ url: '/pages/index/index' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onLoad: function (options) {
|
||||
var app = getApp()
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-green" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
||||
<view class="page-header-title">订阅成功</view>
|
||||
<view class="page-header-subtitle">欢迎使用智能模式</view>
|
||||
</view>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
var ble = require('../../services/ble')
|
||||
var commandSync = require('../../services/command-sync')
|
||||
var config = require('../../config/env')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -16,11 +17,12 @@ Page({
|
||||
paused: false,
|
||||
completed: false,
|
||||
startedAt: 0,
|
||||
devMode: false,
|
||||
},
|
||||
|
||||
onLoad: function (options) {
|
||||
var app = getApp()
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight, devMode: config.__DEV__ || false })
|
||||
this.setData({
|
||||
regions: parseInt(options.regions) || 0x7F,
|
||||
wavelength: parseInt(options.wavelength) || 2,
|
||||
@@ -187,5 +189,9 @@ Page({
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onMockComplete: function () {
|
||||
this.finishAsComplete()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -26,6 +26,11 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="debug-actions" wx:if="{{devMode}}">
|
||||
<view class="debug-label">🔧 调试工具</view>
|
||||
<button class="btn-secondary btn-sm" bindtap="onMockComplete">模拟护理完成</button>
|
||||
</view>
|
||||
|
||||
<button class="btn-primary btn-primary-red" bindtap="onStop">停止护理</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -16,6 +16,10 @@ Page({
|
||||
synced: false
|
||||
},
|
||||
|
||||
onBack: function () {
|
||||
wx.reLaunch({ url: '/pages/index/index' })
|
||||
},
|
||||
|
||||
onLoad: function (options) {
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||
var durationMs = parseInt(options.duration) || 0
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-green" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
||||
<view class="page-header-title">护理完成</view>
|
||||
<view class="page-header-subtitle">本次护理已结束</view>
|
||||
</view>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var ble = require('../../services/ble')
|
||||
var config = require('../../config/env')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -15,12 +16,21 @@ Page({
|
||||
subDays: 6,
|
||||
fixedDurationMs: 600000,
|
||||
submitting: false,
|
||||
error: ''
|
||||
error: '',
|
||||
devMode: false
|
||||
},
|
||||
|
||||
onBack: function () {
|
||||
wx.navigateBack({
|
||||
fail: function () {
|
||||
wx.reLaunch({ url: '/pages/index/index' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onLoad: function () {
|
||||
var app = getApp()
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight, devMode: config.__DEV__ || false })
|
||||
this.checkSubscription()
|
||||
},
|
||||
|
||||
@@ -97,5 +107,19 @@ Page({
|
||||
}).catch(function (err) {
|
||||
self.setData({ submitting: false, error: err.error_msg || '启动失败' })
|
||||
})
|
||||
},
|
||||
|
||||
onMockStart: function () {
|
||||
var mask = 0
|
||||
this.data.regions.forEach(function (r) {
|
||||
if (r.checked) mask |= r.mask
|
||||
})
|
||||
if (mask === 0) mask = 0x7F
|
||||
wx.redirectTo({
|
||||
url: '/pages/treating/treating?regions=' + mask +
|
||||
'&wavelength=2' +
|
||||
'&duration=' + this.data.fixedDurationMs +
|
||||
'&mode=' + this.data.selectedMode
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
||||
<view class="page-header-title">护理设置</view>
|
||||
<view class="page-header-subtitle">选择模式和区域</view>
|
||||
</view>
|
||||
@@ -37,5 +38,10 @@
|
||||
</button>
|
||||
|
||||
<view class="error-text" wx:if="{{error}}">{{error}}</view>
|
||||
|
||||
<view class="debug-actions" wx:if="{{devMode}}">
|
||||
<view class="debug-label">🔧 调试工具</view>
|
||||
<button class="btn-secondary btn-sm" bindtap="onMockStart">模拟开始护理(跳过BLE)</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
var ble = require('../../services/ble')
|
||||
var config = require('../../config/env')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
checking: false,
|
||||
result: null,
|
||||
error: ''
|
||||
error: '',
|
||||
devMode: false
|
||||
},
|
||||
|
||||
onBack: function () {
|
||||
wx.navigateBack({
|
||||
fail: function () {
|
||||
wx.reLaunch({ url: '/pages/index/index' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onLoad: function () {
|
||||
var app = getApp()
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight, devMode: config.__DEV__ || false })
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
@@ -47,5 +57,9 @@ Page({
|
||||
|
||||
onRetry: function () {
|
||||
this.checkWearing()
|
||||
},
|
||||
|
||||
onMockWearOk: function () {
|
||||
this.setData({ checking: false, result: 'ok' })
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<view class="page">
|
||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
||||
<view class="page-header-title">确认佩戴</view>
|
||||
<view class="page-header-subtitle">请确保面罩贴合面部</view>
|
||||
</view>
|
||||
@@ -39,5 +40,10 @@
|
||||
</view>
|
||||
|
||||
<button wx:if="{{result === 'ok'}}" class="btn-primary mt-30" bindtap="onNext">设置护理参数</button>
|
||||
|
||||
<view class="debug-actions" wx:if="{{devMode}}">
|
||||
<view class="debug-label">🔧 调试工具</view>
|
||||
<button class="btn-secondary btn-sm" bindtap="onMockWearOk">模拟佩戴确认</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
在新工单中引用
屏蔽一个用户