From 1f55e430c8c336619339b698d5d0c594e4492c2f Mon Sep 17 00:00:00 2001 From: Guoguo Date: Tue, 28 Apr 2026 19:42:23 -0700 Subject: [PATCH] feat: add password change, trial subscription, batch import, UX improvements Server: - POST /api/v1/admin/password: admin password change with bcrypt migration - POST /api/v1/subscription/trial: user trial activation, one per user - POST /api/v1/admin/devices/batch: bulk device import (up to 500) - Add trial plan (7 days, free) to PLANS constant Admin console: - Settings page: add password change form with validation - Device page: add batch import modal with textarea input Miniprogram: - Treating page: add back button with stop-treatment confirmation - Index page: add mock device bind button (dev mode only) --- admin-console/src/pages/device/index.vue | 101 ++++++++++++++++++++- admin-console/src/pages/settings/index.vue | 56 +++++++++++- miniprogram/app.wxss | 4 + miniprogram/pages/index/index.js | 29 +++++- miniprogram/pages/index/index.wxml | 4 + miniprogram/pages/treating/treating.js | 4 + miniprogram/pages/treating/treating.wxml | 1 + server/src/routes/admin.js | 49 ++++++++++ server/src/routes/subscription.js | 13 +++ 9 files changed, 258 insertions(+), 3 deletions(-) diff --git a/admin-console/src/pages/device/index.vue b/admin-console/src/pages/device/index.vue index d02c1ab..24d71be 100644 --- a/admin-console/src/pages/device/index.vue +++ b/admin-console/src/pages/device/index.vue @@ -11,6 +11,7 @@ /> + @@ -55,6 +56,21 @@ 共 {{ totalPages }} 页 + + + + 批量导入设备 + + 设备编号(每行一个) + + + 最多 500 个设备 + + + + + + @@ -73,7 +89,10 @@ export default { page: 1, pageSize: 20, keyword: '', - statusMap: { 1: '未激活', 2: '在线', 3: '离线', 4: '故障' } + statusMap: { 1: '未激活', 2: '在线', 3: '离线', 4: '故障' }, + showBatchImport: false, + batchDeviceIds: '', + batchImporting: false } }, computed: { @@ -146,6 +165,29 @@ export default { return map[status] || 'badge badge-default' }, formatDate: formatDateShort, + async onBatchImport() { + const ids = this.batchDeviceIds.split('\n').map(s => s.trim()).filter(Boolean) + if (ids.length === 0) { + uni.showToast({ title: '请输入设备编号', icon: 'none' }) + return + } + if (ids.length > 500) { + uni.showToast({ title: '最多 500 个', icon: 'none' }) + return + } + this.batchImporting = true + try { + const result = await post('/api/v1/admin/devices/batch', { device_ids: ids }) + uni.showToast({ title: '导入 ' + result.created + ' 个设备', icon: 'success' }) + this.showBatchImport = false + this.batchDeviceIds = '' + this.loadDevices() + } catch (e) { + uni.showToast({ title: '导入失败', icon: 'none' }) + } finally { + this.batchImporting = false + } + }, async onExport() { try { const data = await get('/api/v1/admin/devices', { page: 1, page_size: 9999, keyword: this.keyword }) @@ -171,4 +213,61 @@ export default { .pagination { justify-content: flex-end; } + +.modal-mask { + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.45); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.modal-content { + width: 480px; + background: #fff; + border-radius: 8px; + padding: 24px; +} + +.modal-title { + font-size: 18px; + font-weight: 600; + margin-bottom: 20px; +} + +.form-group { + margin-bottom: 16px; +} + +.form-label { + display: block; + font-size: 14px; + color: #333; + margin-bottom: 6px; +} + +.form-textarea { + width: 100%; + height: 160px; + border: 1px solid #d9d9d9; + border-radius: 6px; + padding: 12px; + font-size: 14px; + box-sizing: border-box; + resize: vertical; +} + +.batch-hint { + font-size: 12px; + color: #999; + margin-bottom: 16px; +} + +.modal-actions { + display: flex; + justify-content: flex-end; + gap: 8px; +} diff --git a/admin-console/src/pages/settings/index.vue b/admin-console/src/pages/settings/index.vue index 826862b..1991a8f 100644 --- a/admin-console/src/pages/settings/index.vue +++ b/admin-console/src/pages/settings/index.vue @@ -1,6 +1,28 @@