feat(i18n): 剩余15页全量接入中英(登录/注册/扫码/绑定/佩戴/扫描/治疗/订阅/帮助等)
- 3 个并行 agent 各管 5 页,互不重叠命名空间 - 全 App 18 页均 i18n.bind,22 命名空间/400 键中英对齐 - 动态文案(时长/天数/价格/区域)JS 内 i18n.t 计算;区域标签复用 common.regions
这个提交包含在:
@@ -1,5 +1,40 @@
|
|||||||
// Strings for the autoScan page. Fill zh/en with identical key structure.
|
// Strings for the auto-scan (smart scan) page. Keep zh/en with identical key structure.
|
||||||
|
// Pre/Post pairs are concatenated around a frame count in WXML (WXML cannot do {param}).
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '智能检测',
|
||||||
|
subReady: '准备就绪',
|
||||||
|
subWaiting: '等待佩戴贴合...',
|
||||||
|
subCollectingPre: '采集数据中 (',
|
||||||
|
subCollectingPost: '帧)...',
|
||||||
|
subDone: '检测完成,正在启动...',
|
||||||
|
readyHint: '设备已佩戴,点击下方按钮开始智能检测',
|
||||||
|
startBtn: '开始检测',
|
||||||
|
waitingStatus: '正在检测面膜贴合状态,请确保设备正确佩戴...',
|
||||||
|
collectingStatusPre: '已贴合,正在采集光谱数据... 已收到 ',
|
||||||
|
collectingStatusPost: ' 帧',
|
||||||
|
doneStatusPre: '检测完成(共 ',
|
||||||
|
doneStatusPost: ' 帧),正在启动治疗...',
|
||||||
|
notWorn: '面膜未佩戴好,请重新佩戴后再试',
|
||||||
|
timeout: '检测超时,未收到数据,请重试',
|
||||||
|
cmdFailed: '检测命令发送失败'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Smart Scan',
|
||||||
|
subReady: 'Ready',
|
||||||
|
subWaiting: 'Waiting for fit...',
|
||||||
|
subCollectingPre: 'Collecting data (',
|
||||||
|
subCollectingPost: ' frames)...',
|
||||||
|
subDone: 'Scan complete, starting...',
|
||||||
|
readyHint: 'Device is on. Tap the button below to start the smart scan',
|
||||||
|
startBtn: 'Start Scan',
|
||||||
|
waitingStatus: 'Checking mask fit, please make sure the device is worn correctly...',
|
||||||
|
collectingStatusPre: 'Fitted. Collecting spectral data... received ',
|
||||||
|
collectingStatusPost: ' frames',
|
||||||
|
doneStatusPre: 'Scan complete (',
|
||||||
|
doneStatusPost: ' frames), starting treatment...',
|
||||||
|
notWorn: 'The mask is not worn properly. Please re-fit and try again',
|
||||||
|
timeout: 'Scan timed out with no data. Please try again',
|
||||||
|
cmdFailed: 'Failed to send scan command'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,23 @@
|
|||||||
// Strings for the bindSuccess page. Fill zh/en with identical key structure.
|
// Strings for the bindSuccess page. Fill zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '绑定成功',
|
||||||
|
subtitle: '设备已成功绑定',
|
||||||
|
successTitle: '绑定成功!',
|
||||||
|
freeModeOpen: '智能模式已开放',
|
||||||
|
trialGranted: '恭喜获得{days}天智能模式试用',
|
||||||
|
trialSub: '体验结束后可订阅继续使用',
|
||||||
|
startUsing: '开始使用',
|
||||||
|
goHome: '返回首页'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Bound Successfully',
|
||||||
|
subtitle: 'Device bound successfully',
|
||||||
|
successTitle: 'Bound Successfully!',
|
||||||
|
freeModeOpen: 'Smart mode unlocked',
|
||||||
|
trialGranted: 'You got a {days}-day Smart mode trial',
|
||||||
|
trialSub: 'Subscribe to keep using it after the trial ends',
|
||||||
|
startUsing: 'Get Started',
|
||||||
|
goHome: 'Back to Home'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,33 @@
|
|||||||
// Strings for the bleConnect page. Fill zh/en with identical key structure.
|
// Strings for the bleConnect page. Fill zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '蓝牙连接',
|
||||||
|
searchingSub: '正在搜索设备...',
|
||||||
|
connectingSub: '正在连接设备...',
|
||||||
|
bindingSub: '正在绑定...',
|
||||||
|
failedSub: '连接失败',
|
||||||
|
bluetoothTip: '请确保手机蓝牙已开启',
|
||||||
|
searching: '搜索中...',
|
||||||
|
pairing: '正在配对...',
|
||||||
|
failed: '连接失败',
|
||||||
|
scanTimeout: '搜索超时,请确认设备已开机并在附近',
|
||||||
|
confirmFailed: '后台确认绑定失败',
|
||||||
|
bindFailed: '设备绑定失败',
|
||||||
|
bindCmdFailed: '绑定命令失败'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Bluetooth Connection',
|
||||||
|
searchingSub: 'Searching for device...',
|
||||||
|
connectingSub: 'Connecting to device...',
|
||||||
|
bindingSub: 'Binding...',
|
||||||
|
failedSub: 'Connection failed',
|
||||||
|
bluetoothTip: 'Please make sure Bluetooth is enabled on your phone',
|
||||||
|
searching: 'Searching...',
|
||||||
|
pairing: 'Pairing...',
|
||||||
|
failed: 'Connection failed',
|
||||||
|
scanTimeout: 'Search timed out. Please make sure the device is powered on and nearby',
|
||||||
|
confirmFailed: 'Failed to confirm binding on the server',
|
||||||
|
bindFailed: 'Device binding failed',
|
||||||
|
bindCmdFailed: 'Binding command failed'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
// Strings for the contact page. Fill zh/en with identical key structure.
|
// Strings for the contact page. Fill zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '联系我们',
|
||||||
|
placeholderText: '内容建设中',
|
||||||
|
placeholderSub: '联系方式正在整理,敬请期待'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Contact Us',
|
||||||
|
placeholderText: 'Coming soon',
|
||||||
|
placeholderSub: 'Contact details are being prepared. Stay tuned.'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
// Strings for the help page. Fill zh/en with identical key structure.
|
// Strings for the help page. Fill zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '使用帮助',
|
||||||
|
placeholderText: '内容建设中',
|
||||||
|
placeholderSub: '帮助文档正在编写,敬请期待'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Help',
|
||||||
|
placeholderText: 'Coming soon',
|
||||||
|
placeholderSub: 'The help guide is being written. Stay tuned.'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
// Strings for the login page. Fill zh/en with identical key structure.
|
// Strings for the login page. Fill zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
wechatLogin: '微信登录',
|
||||||
|
welcome: '欢迎使用LumiFlow',
|
||||||
|
desc: '登录后即可使用全部功能',
|
||||||
|
agreement: '登录即表示同意《用户协议》和《隐私政策》',
|
||||||
|
loginFailed: '登录失败',
|
||||||
|
agreementTitle: '提示',
|
||||||
|
agreementContent: '用户协议和隐私政策内容建设中'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
wechatLogin: 'WeChat Login',
|
||||||
|
welcome: 'Welcome to LumiFlow',
|
||||||
|
desc: 'Log in to unlock all features',
|
||||||
|
agreement: 'By logging in, you agree to the Terms of Service and Privacy Policy',
|
||||||
|
loginFailed: 'Login failed',
|
||||||
|
agreementTitle: 'Notice',
|
||||||
|
agreementContent: 'Terms of Service and Privacy Policy are coming soon'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,45 @@
|
|||||||
// Strings for the register page. Fill zh/en with identical key structure.
|
// Strings for the register page. Fill zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
subtitle: '完善个人资料',
|
||||||
|
avatarSection: '设置头像',
|
||||||
|
changeAvatar: '点击更换',
|
||||||
|
nicknameSection: '设置昵称',
|
||||||
|
nicknamePlaceholder: '请输入昵称',
|
||||||
|
authorizePhone: '授权手机号',
|
||||||
|
phoneHint: '手机号用于接收服务通知,必须授权',
|
||||||
|
submit: '完成注册',
|
||||||
|
agreement: '注册即表示同意《用户协议》和《隐私政策》',
|
||||||
|
nicknamePrefix: '用户',
|
||||||
|
phoneRequired: '需要授权手机号才能完成注册',
|
||||||
|
phoneAuthSuccess: '手机号授权成功',
|
||||||
|
phoneAuthFailed: '手机号授权失败',
|
||||||
|
authorized: '已授权',
|
||||||
|
uploadFailed: '上传失败',
|
||||||
|
phoneFirst: '请先授权手机号',
|
||||||
|
nicknameRequired: '请输入昵称',
|
||||||
|
agreementTitle: '提示',
|
||||||
|
agreementContent: '用户协议和隐私政策内容建设中'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
subtitle: 'Complete Your Profile',
|
||||||
|
avatarSection: 'Set Avatar',
|
||||||
|
changeAvatar: 'Tap to change',
|
||||||
|
nicknameSection: 'Set Nickname',
|
||||||
|
nicknamePlaceholder: 'Enter a nickname',
|
||||||
|
authorizePhone: 'Authorize Phone Number',
|
||||||
|
phoneHint: 'Your phone number is used to receive service notifications and is required',
|
||||||
|
submit: 'Complete Registration',
|
||||||
|
agreement: 'By registering, you agree to the Terms of Service and Privacy Policy',
|
||||||
|
nicknamePrefix: 'User',
|
||||||
|
phoneRequired: 'Phone number authorization is required to complete registration',
|
||||||
|
phoneAuthSuccess: 'Phone number authorized',
|
||||||
|
phoneAuthFailed: 'Phone authorization failed',
|
||||||
|
authorized: 'Authorized',
|
||||||
|
uploadFailed: 'Upload failed',
|
||||||
|
phoneFirst: 'Please authorize your phone number first',
|
||||||
|
nicknameRequired: 'Please enter a nickname',
|
||||||
|
agreementTitle: 'Notice',
|
||||||
|
agreementContent: 'Terms of Service and Privacy Policy are coming soon'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,29 @@
|
|||||||
// Strings for the scan page. Fill zh/en with identical key structure.
|
// Strings for the scan page. Fill zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '扫码绑定',
|
||||||
|
subtitle: '扫描设备底部二维码',
|
||||||
|
scanText: '扫描设备二维码',
|
||||||
|
scan: '扫码',
|
||||||
|
scanning: '扫描中...',
|
||||||
|
manualInput: '手动输入设备号',
|
||||||
|
manualPlaceholder: '请输入设备ID,如 672B6D5A 4DCD861',
|
||||||
|
invalidQr: '无效的设备二维码',
|
||||||
|
alreadyBound: '已绑定设备',
|
||||||
|
bindFailed: '绑定失败',
|
||||||
|
invalidFormat: '设备号格式不正确'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Scan to Bind',
|
||||||
|
subtitle: 'Scan the QR code on the bottom of the device',
|
||||||
|
scanText: 'Scan device QR code',
|
||||||
|
scan: 'Scan',
|
||||||
|
scanning: 'Scanning...',
|
||||||
|
manualInput: 'Enter Device ID Manually',
|
||||||
|
manualPlaceholder: 'Enter device ID, e.g. 672B6D5A 4DCD861',
|
||||||
|
invalidQr: 'Invalid device QR code',
|
||||||
|
alreadyBound: 'Device already bound',
|
||||||
|
bindFailed: 'Binding failed',
|
||||||
|
invalidFormat: 'Invalid device ID format'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,33 @@
|
|||||||
// Strings for the setup page. Fill zh/en with identical key structure.
|
// Strings for the treatment-setup page. Keep zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '使用设置',
|
||||||
|
subtitle: '选择模式和区域',
|
||||||
|
selectMode: '选择模式',
|
||||||
|
normalMode: '普通模式',
|
||||||
|
normalModeSub: '6色循环',
|
||||||
|
smartMode: '智能模式',
|
||||||
|
remainDays: '剩余{days}天',
|
||||||
|
subscribeUnlock: '订阅解锁',
|
||||||
|
selectRegion: '选择区域',
|
||||||
|
normalTip: '普通模式固定 10 分钟,使用区域默认全区域,不可调整',
|
||||||
|
startBtn: '开始使用',
|
||||||
|
noRegion: '请至少选择一个区域',
|
||||||
|
startFailed: '启动失败'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Session Setup',
|
||||||
|
subtitle: 'Choose mode and regions',
|
||||||
|
selectMode: 'Select Mode',
|
||||||
|
normalMode: 'Normal Mode',
|
||||||
|
normalModeSub: '6-color cycle',
|
||||||
|
smartMode: 'Smart Mode',
|
||||||
|
remainDays: '{days} days left',
|
||||||
|
subscribeUnlock: 'Subscribe to unlock',
|
||||||
|
selectRegion: 'Select Regions',
|
||||||
|
normalTip: 'Normal mode runs a fixed 10 minutes over all regions and cannot be adjusted',
|
||||||
|
startBtn: 'Start',
|
||||||
|
noRegion: 'Please select at least one region',
|
||||||
|
startFailed: 'Failed to start'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,87 @@
|
|||||||
// Strings for the subPlans page. Fill zh/en with identical key structure.
|
// Strings for the subPlans page. Fill zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '订阅服务',
|
||||||
|
subtitle: '解锁智能模式',
|
||||||
|
currentPlanLabel: '当前套餐',
|
||||||
|
remainDaysLabel: '剩余天数',
|
||||||
|
daysValue: '{days}天',
|
||||||
|
choosePlan: '选择订阅套餐',
|
||||||
|
loadingPlans: '加载套餐中...',
|
||||||
|
smartMode: '智能模式',
|
||||||
|
smartModeDesc: '智能自动调节',
|
||||||
|
renew: '续费',
|
||||||
|
confirmPay: '确认支付',
|
||||||
|
agreement: '支付即表示同意《订阅协议》',
|
||||||
|
// plan names (also used as fallback plan labels)
|
||||||
|
planTrial: '试用',
|
||||||
|
planMonthly: '月卡',
|
||||||
|
planYearly: '年卡',
|
||||||
|
// computed plan descriptions
|
||||||
|
free: '免费',
|
||||||
|
descTrial: '{days}天免费体验',
|
||||||
|
descSave: '省¥{amount}',
|
||||||
|
descDaily: '约{price}元/天',
|
||||||
|
tagRecommend: '推荐',
|
||||||
|
tagUsed: '已使用',
|
||||||
|
// modals / toasts
|
||||||
|
tipTitle: '提示',
|
||||||
|
agreementBuilding: '订阅协议内容建设中',
|
||||||
|
activateTrialTitle: '激活试用',
|
||||||
|
activateTrialConfirm: '免费试用 {desc}?',
|
||||||
|
confirmRenewTitle: '确认续费',
|
||||||
|
confirmPayTitle: '确认支付',
|
||||||
|
renewContent: '在现有订阅基础上延长{days}天,支付 ¥{price}',
|
||||||
|
payContent: '支付 ¥{price}',
|
||||||
|
trialActivated: '试用已激活',
|
||||||
|
activateFailed: '激活失败',
|
||||||
|
payUnavailable: '支付服务暂不可用',
|
||||||
|
payCancelled: '已取消支付',
|
||||||
|
payFailed: '支付失败',
|
||||||
|
createOrderFailed: '创建订单失败',
|
||||||
|
paySuccess: '支付成功',
|
||||||
|
payProcessing: '支付处理中,请稍后在订阅页查看'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Subscription',
|
||||||
|
subtitle: 'Unlock Smart Mode',
|
||||||
|
currentPlanLabel: 'Current plan',
|
||||||
|
remainDaysLabel: 'Days remaining',
|
||||||
|
daysValue: '{days} days',
|
||||||
|
choosePlan: 'Choose a plan',
|
||||||
|
loadingPlans: 'Loading plans...',
|
||||||
|
smartMode: 'Smart Mode',
|
||||||
|
smartModeDesc: 'Automatic smart adjustment',
|
||||||
|
renew: 'Renew',
|
||||||
|
confirmPay: 'Pay Now',
|
||||||
|
agreement: 'By paying you agree to the Subscription Agreement',
|
||||||
|
// plan names (also used as fallback plan labels)
|
||||||
|
planTrial: 'Trial',
|
||||||
|
planMonthly: 'Monthly',
|
||||||
|
planYearly: 'Yearly',
|
||||||
|
// computed plan descriptions
|
||||||
|
free: 'Free',
|
||||||
|
descTrial: '{days}-day free trial',
|
||||||
|
descSave: 'Save ¥{amount}',
|
||||||
|
descDaily: 'About ¥{price}/day',
|
||||||
|
tagRecommend: 'Best value',
|
||||||
|
tagUsed: 'Used',
|
||||||
|
// modals / toasts
|
||||||
|
tipTitle: 'Notice',
|
||||||
|
agreementBuilding: 'The subscription agreement is being prepared',
|
||||||
|
activateTrialTitle: 'Activate trial',
|
||||||
|
activateTrialConfirm: 'Start free trial: {desc}?',
|
||||||
|
confirmRenewTitle: 'Confirm renewal',
|
||||||
|
confirmPayTitle: 'Confirm payment',
|
||||||
|
renewContent: 'Extend your current subscription by {days} days for ¥{price}',
|
||||||
|
payContent: 'Pay ¥{price}',
|
||||||
|
trialActivated: 'Trial activated',
|
||||||
|
activateFailed: 'Activation failed',
|
||||||
|
payUnavailable: 'Payment service is unavailable',
|
||||||
|
payCancelled: 'Payment cancelled',
|
||||||
|
payFailed: 'Payment failed',
|
||||||
|
createOrderFailed: 'Failed to create order',
|
||||||
|
paySuccess: 'Payment successful',
|
||||||
|
payProcessing: 'Payment is processing. Please check the subscription page later.'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,29 @@
|
|||||||
// Strings for the subPrompt page. Fill zh/en with identical key structure.
|
// Strings for the subPrompt page. Fill zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '使用设置',
|
||||||
|
subtitle: '试用期已结束',
|
||||||
|
selectMode: '选择模式',
|
||||||
|
normalMode: '普通模式',
|
||||||
|
normalModeSub: '6色循环',
|
||||||
|
smartMode: '智能模式',
|
||||||
|
smartModeSub: '订阅解锁',
|
||||||
|
trialEndedTitle: '试用已结束',
|
||||||
|
trialEndedSub: '订阅后继续使用智能模式',
|
||||||
|
subscribeNow: '立即订阅',
|
||||||
|
useNormalFirst: '先使用普通模式'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Settings',
|
||||||
|
subtitle: 'Your free trial has ended',
|
||||||
|
selectMode: 'Choose a mode',
|
||||||
|
normalMode: 'Standard Mode',
|
||||||
|
normalModeSub: '6-color cycle',
|
||||||
|
smartMode: 'Smart Mode',
|
||||||
|
smartModeSub: 'Unlock with subscription',
|
||||||
|
trialEndedTitle: 'Trial ended',
|
||||||
|
trialEndedSub: 'Subscribe to keep using Smart Mode',
|
||||||
|
subscribeNow: 'Subscribe Now',
|
||||||
|
useNormalFirst: 'Use Standard Mode for now'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,29 @@
|
|||||||
// Strings for the subSuccess page. Fill zh/en with identical key structure.
|
// Strings for the subSuccess page. Fill zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '订阅成功',
|
||||||
|
subtitle: '欢迎使用智能模式',
|
||||||
|
successTitle: '订阅成功!',
|
||||||
|
subscribed: '已订阅',
|
||||||
|
validUntil: '有效期至:',
|
||||||
|
startSmart: '立即体验智能模式',
|
||||||
|
backHome: '返回首页',
|
||||||
|
planTrial: '试用会员',
|
||||||
|
planMonthly: '月卡会员',
|
||||||
|
planYearly: '年卡会员',
|
||||||
|
planDefault: '会员'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Subscription Successful',
|
||||||
|
subtitle: 'Welcome to Smart Mode',
|
||||||
|
successTitle: 'Subscription successful!',
|
||||||
|
subscribed: 'Subscribed',
|
||||||
|
validUntil: 'Valid until: ',
|
||||||
|
startSmart: 'Try Smart Mode now',
|
||||||
|
backHome: 'Back to Home',
|
||||||
|
planTrial: 'Trial Member',
|
||||||
|
planMonthly: 'Monthly Member',
|
||||||
|
planYearly: 'Yearly Member',
|
||||||
|
planDefault: 'Member'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,59 @@
|
|||||||
// Strings for the treating page. Fill zh/en with identical key structure.
|
// Strings for the treating (live treatment) page. Keep zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '使用中',
|
||||||
|
subtitle: '正在使用...',
|
||||||
|
remainingLabel: '剩余时间',
|
||||||
|
smartMode: '✨ 智能模式',
|
||||||
|
normalMode: '🔄 普通模式',
|
||||||
|
allRegions: '全区域',
|
||||||
|
regionTopLeft: '左上区',
|
||||||
|
regionTopRight: '右上区',
|
||||||
|
relax1: '请放松心情',
|
||||||
|
relax2: '享受美好时光',
|
||||||
|
stopBtn: '停止使用',
|
||||||
|
fittingLost: '检测到离肤,请重新佩戴设备',
|
||||||
|
fittingRestored: '已重新贴合,继续使用',
|
||||||
|
faultTitle: '设备故障',
|
||||||
|
faultContent: '设备上报故障,已自动停止。',
|
||||||
|
lowBatteryTitle: '电量过低',
|
||||||
|
lowBatteryContent: '设备电量不足5%,已自动停止。请及时充电。',
|
||||||
|
lowBatteryWarn: '电量低,请及时充电',
|
||||||
|
exceptionTitle: '设备异常',
|
||||||
|
exceptionContent: '使用过程中出现异常',
|
||||||
|
disconnected: '设备连接断开,正在重连...',
|
||||||
|
reconnected: '设备已重新连接',
|
||||||
|
reconnectFailedTitle: '连接丢失',
|
||||||
|
reconnectFailedContent: '设备蓝牙连接已断开,无法恢复。本次使用数据将保存。',
|
||||||
|
stopTitle: '结束使用',
|
||||||
|
stopContent: '确定要提前结束本次使用吗?'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'In Progress',
|
||||||
|
subtitle: 'Treatment in progress...',
|
||||||
|
remainingLabel: 'Time Left',
|
||||||
|
smartMode: '✨ Smart Mode',
|
||||||
|
normalMode: '🔄 Normal Mode',
|
||||||
|
allRegions: 'All regions',
|
||||||
|
regionTopLeft: 'Top-left',
|
||||||
|
regionTopRight: 'Top-right',
|
||||||
|
relax1: 'Relax and unwind',
|
||||||
|
relax2: 'Enjoy your moment',
|
||||||
|
stopBtn: 'Stop',
|
||||||
|
fittingLost: 'Device lifted off skin. Please put it back on',
|
||||||
|
fittingRestored: 'Re-fitted, continuing',
|
||||||
|
faultTitle: 'Device Fault',
|
||||||
|
faultContent: 'The device reported a fault and stopped automatically.',
|
||||||
|
lowBatteryTitle: 'Battery Too Low',
|
||||||
|
lowBatteryContent: 'Battery is below 5%. Stopped automatically. Please charge soon.',
|
||||||
|
lowBatteryWarn: 'Low battery, please charge soon',
|
||||||
|
exceptionTitle: 'Device Error',
|
||||||
|
exceptionContent: 'Something went wrong during the session',
|
||||||
|
disconnected: 'Device disconnected, reconnecting...',
|
||||||
|
reconnected: 'Device reconnected',
|
||||||
|
reconnectFailedTitle: 'Connection Lost',
|
||||||
|
reconnectFailedContent: 'The Bluetooth connection was lost and cannot be restored. This session will be saved.',
|
||||||
|
stopTitle: 'End Session',
|
||||||
|
stopContent: 'Are you sure you want to end this session early?'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,25 @@
|
|||||||
// Strings for the treatmentDone page. Fill zh/en with identical key structure.
|
// Strings for the treatment-done page. Keep zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '使用完成',
|
||||||
|
headerSub: '本次使用已结束',
|
||||||
|
resultTitle: '使用完成!',
|
||||||
|
durationLabel: '使用时长',
|
||||||
|
regionLabel: '使用区域',
|
||||||
|
recorded: '本次使用已记录',
|
||||||
|
backHome: '返回首页',
|
||||||
|
durationMin: '{min}分钟',
|
||||||
|
durationSec: '{sec}秒'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Complete',
|
||||||
|
headerSub: 'This session has ended',
|
||||||
|
resultTitle: 'Session Complete!',
|
||||||
|
durationLabel: 'Duration',
|
||||||
|
regionLabel: 'Regions',
|
||||||
|
recorded: 'This session has been recorded',
|
||||||
|
backHome: 'Back to Home',
|
||||||
|
durationMin: '{min} min',
|
||||||
|
durationSec: '{sec} s'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,39 @@
|
|||||||
// Strings for the wearCheck page. Fill zh/en with identical key structure.
|
// Strings for the wear-check page. Keep zh/en with identical key structure.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
zh: {},
|
zh: {
|
||||||
en: {}
|
title: '确认佩戴',
|
||||||
|
subtitle: '请确保设备正确佩戴',
|
||||||
|
connected: '已连接',
|
||||||
|
disconnected: '未连接',
|
||||||
|
battery: '电量',
|
||||||
|
guideTitle: '佩戴指引',
|
||||||
|
guideImg: '设备佩戴示意图',
|
||||||
|
step1: '1. 取出设备,展开弹性绑带',
|
||||||
|
step2: '2. 将设备正确佩戴',
|
||||||
|
step3: '3. 调节绑带至舒适位置',
|
||||||
|
confirmBtn: '确认已佩戴,开始检测',
|
||||||
|
reconnect: '重新连接',
|
||||||
|
connecting: '正在连接...',
|
||||||
|
connectSuccess: '连接成功',
|
||||||
|
connectFailed: '连接失败',
|
||||||
|
searchTimeout: '搜索超时'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Confirm Fit',
|
||||||
|
subtitle: 'Make sure the device is worn correctly',
|
||||||
|
connected: 'Connected',
|
||||||
|
disconnected: 'Not connected',
|
||||||
|
battery: 'Battery',
|
||||||
|
guideTitle: 'How to Wear',
|
||||||
|
guideImg: 'Wearing diagram',
|
||||||
|
step1: '1. Take out the device and unfold the strap',
|
||||||
|
step2: '2. Put the device on correctly',
|
||||||
|
step3: '3. Adjust the strap to a comfortable fit',
|
||||||
|
confirmBtn: 'Confirm Fit & Continue',
|
||||||
|
reconnect: 'Reconnect',
|
||||||
|
connecting: 'Connecting...',
|
||||||
|
connectSuccess: 'Connected',
|
||||||
|
connectFailed: 'Connection failed',
|
||||||
|
searchTimeout: 'Search timed out'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
var ble = require('../../services/ble')
|
var ble = require('../../services/ble')
|
||||||
var api = require('../../utils/api')
|
var api = require('../../utils/api')
|
||||||
var diagnosis = require('../../services/diagnosis')
|
var diagnosis = require('../../services/diagnosis')
|
||||||
|
var i18n = require('../../i18n/index')
|
||||||
var SCAN_TIMEOUT = 180000
|
var SCAN_TIMEOUT = 180000
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
@@ -31,6 +32,10 @@ Page({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
_cleanup: function () {
|
_cleanup: function () {
|
||||||
if (this._timeoutTimer) { clearTimeout(this._timeoutTimer); this._timeoutTimer = null }
|
if (this._timeoutTimer) { clearTimeout(this._timeoutTimer); this._timeoutTimer = null }
|
||||||
if (this._onAdc) { ble.off('adc', this._onAdc); this._onAdc = null }
|
if (this._onAdc) { ble.off('adc', this._onAdc); this._onAdc = null }
|
||||||
@@ -42,7 +47,7 @@ Page({
|
|||||||
var self = this
|
var self = this
|
||||||
|
|
||||||
if (!ble.isConnected()) {
|
if (!ble.isConnected()) {
|
||||||
self.setData({ error: '设备未连接' })
|
self.setData({ error: i18n.t('common.deviceNotConnected') })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +95,7 @@ Page({
|
|||||||
console.log('[SCAN] 未贴合,检测失败')
|
console.log('[SCAN] 未贴合,检测失败')
|
||||||
self._completed = true
|
self._completed = true
|
||||||
self._cleanup()
|
self._cleanup()
|
||||||
self.setData({ phase: 'ready', error: '面膜未佩戴好,请重新佩戴后再试' })
|
self.setData({ phase: 'ready', error: i18n.t('autoScan.notWorn') })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +134,7 @@ Page({
|
|||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
console.error('[SCAN] 检测命令发送失败:', err)
|
console.error('[SCAN] 检测命令发送失败:', err)
|
||||||
self._cleanup()
|
self._cleanup()
|
||||||
self.setData({ phase: 'ready', error: '检测命令发送失败' })
|
self.setData({ phase: 'ready', error: i18n.t('autoScan.cmdFailed') })
|
||||||
})
|
})
|
||||||
|
|
||||||
self._timeoutTimer = setTimeout(function () {
|
self._timeoutTimer = setTimeout(function () {
|
||||||
@@ -140,7 +145,7 @@ Page({
|
|||||||
} else {
|
} else {
|
||||||
self._completed = true
|
self._completed = true
|
||||||
self._cleanup()
|
self._cleanup()
|
||||||
self.setData({ phase: 'ready', error: '检测超时,未收到数据,请重试' })
|
self.setData({ phase: 'ready', error: i18n.t('autoScan.timeout') })
|
||||||
}
|
}
|
||||||
}, SCAN_TIMEOUT)
|
}, SCAN_TIMEOUT)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">智能检测</view>
|
<view class="page-header-title">{{i18n.autoScan.title}}</view>
|
||||||
<view class="page-header-subtitle">{{phase === 'ready' ? '准备就绪' : phase === 'waiting_fit' ? '等待佩戴贴合...' : phase === 'collecting' ? '采集数据中 (' + frameCount + '帧)...' : '检测完成,正在启动...'}}</view>
|
<view class="page-header-subtitle">{{phase === 'ready' ? i18n.autoScan.subReady : phase === 'waiting_fit' ? i18n.autoScan.subWaiting : phase === 'collecting' ? i18n.autoScan.subCollectingPre + frameCount + i18n.autoScan.subCollectingPost : i18n.autoScan.subDone}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<!-- 待开始 -->
|
<!-- 待开始 -->
|
||||||
<view wx:if="{{phase === 'ready'}}">
|
<view wx:if="{{phase === 'ready'}}">
|
||||||
<view class="scan-hint">设备已佩戴,点击下方按钮开始智能检测</view>
|
<view class="scan-hint">{{i18n.autoScan.readyHint}}</view>
|
||||||
<button class="btn-primary mt-30" bindtap="onStartScan">开始检测</button>
|
<button class="btn-primary mt-30" bindtap="onStartScan">{{i18n.autoScan.startBtn}}</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 等待贴合 -->
|
<!-- 等待贴合 -->
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<view class="scan-line"></view>
|
<view class="scan-line"></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="scan-status">正在检测面膜贴合状态,请确保设备正确佩戴...</view>
|
<view class="scan-status">{{i18n.autoScan.waitingStatus}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 采集中 -->
|
<!-- 采集中 -->
|
||||||
@@ -29,12 +29,12 @@
|
|||||||
<view class="scan-line"></view>
|
<view class="scan-line"></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="scan-status">已贴合,正在采集光谱数据... 已收到 {{frameCount}} 帧</view>
|
<view class="scan-status">{{i18n.autoScan.collectingStatusPre}}{{frameCount}}{{i18n.autoScan.collectingStatusPost}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 完成,自动跳转 -->
|
<!-- 完成,自动跳转 -->
|
||||||
<view wx:if="{{phase === 'done'}}">
|
<view wx:if="{{phase === 'done'}}">
|
||||||
<view class="scan-status">检测完成(共 {{frameCount}} 帧),正在启动治疗...</view>
|
<view class="scan-status">{{i18n.autoScan.doneStatusPre}}{{frameCount}}{{i18n.autoScan.doneStatusPost}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="scan-error" wx:if="{{error}}">{{error}}</view>
|
<view class="scan-error" wx:if="{{error}}">{{error}}</view>
|
||||||
|
|||||||
@@ -1,9 +1,24 @@
|
|||||||
|
var i18n = require('../../i18n/index')
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
statusBarHeight: 44,
|
statusBarHeight: 44,
|
||||||
deviceId: '',
|
deviceId: '',
|
||||||
trialDays: 7,
|
trialDays: 7,
|
||||||
isFreeMode: false
|
isFreeMode: false,
|
||||||
|
tipTitle: ''
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
|
updateTip: function () {
|
||||||
|
this.setData({
|
||||||
|
tipTitle: this.data.isFreeMode
|
||||||
|
? i18n.t('bindSuccess.freeModeOpen')
|
||||||
|
: i18n.t('bindSuccess.trialGranted', { days: this.data.trialDays })
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
onBack: function () {
|
onBack: function () {
|
||||||
@@ -18,10 +33,14 @@ Page({
|
|||||||
var app = getApp()
|
var app = getApp()
|
||||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||||
this.setData({ deviceId: options.device_id || '' })
|
this.setData({ deviceId: options.device_id || '' })
|
||||||
|
this.updateTip()
|
||||||
var self = this
|
var self = this
|
||||||
var http = require('../../utils/request')
|
var http = require('../../utils/request')
|
||||||
http.get('/api/v1/subscription').then(function (sub) {
|
http.get('/api/v1/subscription').then(function (sub) {
|
||||||
if (sub && sub.plan === 'free') self.setData({ isFreeMode: true })
|
if (sub && sub.plan === 'free') {
|
||||||
|
self.setData({ isFreeMode: true })
|
||||||
|
self.updateTip()
|
||||||
|
}
|
||||||
}).catch(function () {})
|
}).catch(function () {})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-green" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-green" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">绑定成功</view>
|
<view class="page-header-title">{{i18n.bindSuccess.title}}</view>
|
||||||
<view class="page-header-subtitle">设备已成功绑定</view>
|
<view class="page-header-subtitle">{{i18n.bindSuccess.subtitle}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<view class="success-area">
|
<view class="success-area">
|
||||||
<view class="success-icon">✓</view>
|
<view class="success-icon">✓</view>
|
||||||
<view class="success-title">绑定成功!</view>
|
<view class="success-title">{{i18n.bindSuccess.successTitle}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="tip-card">
|
<view class="tip-card">
|
||||||
<text class="tip-card-icon">🎁</text>
|
<text class="tip-card-icon">🎁</text>
|
||||||
<view class="tip-card-body">
|
<view class="tip-card-body">
|
||||||
<view class="tip-card-title">{{isFreeMode ? '智能模式已开放' : '恭喜获得7天智能模式试用'}}</view>
|
<view class="tip-card-title">{{tipTitle}}</view>
|
||||||
<view class="tip-card-sub" wx:if="{{!isFreeMode}}">体验结束后可订阅继续使用</view>
|
<view class="tip-card-sub" wx:if="{{!isFreeMode}}">{{i18n.bindSuccess.trialSub}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<button class="btn-primary" bindtap="onStart">开始使用</button>
|
<button class="btn-primary" bindtap="onStart">{{i18n.bindSuccess.startUsing}}</button>
|
||||||
<button class="btn-secondary" bindtap="onGoHome">返回首页</button>
|
<button class="btn-secondary" bindtap="onGoHome">{{i18n.bindSuccess.goHome}}</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
var ble = require('../../services/ble')
|
var ble = require('../../services/ble')
|
||||||
var http = require('../../utils/request')
|
var http = require('../../utils/request')
|
||||||
|
var i18n = require('../../i18n/index')
|
||||||
var app = getApp()
|
var app = getApp()
|
||||||
var SCAN_TIMEOUT = 15000
|
var SCAN_TIMEOUT = 15000
|
||||||
|
|
||||||
@@ -12,6 +13,10 @@ Page({
|
|||||||
error: '',
|
error: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
onBack: function () {
|
onBack: function () {
|
||||||
wx.navigateBack({
|
wx.navigateBack({
|
||||||
fail: function () {
|
fail: function () {
|
||||||
@@ -48,7 +53,7 @@ Page({
|
|||||||
|
|
||||||
self._scanTimer = setTimeout(function () {
|
self._scanTimer = setTimeout(function () {
|
||||||
ble.stopScan()
|
ble.stopScan()
|
||||||
self.setData({ state: 'error', error: '搜索超时,请确认设备已开机并在附近' })
|
self.setData({ state: 'error', error: i18n.t('bleConnect.scanTimeout') })
|
||||||
}, SCAN_TIMEOUT)
|
}, SCAN_TIMEOUT)
|
||||||
|
|
||||||
ble.startScan({
|
ble.startScan({
|
||||||
@@ -64,7 +69,7 @@ Page({
|
|||||||
},
|
},
|
||||||
onError: function (err) {
|
onError: function (err) {
|
||||||
clearTimeout(self._scanTimer)
|
clearTimeout(self._scanTimer)
|
||||||
self.setData({ state: 'error', error: err.msg || '连接失败' })
|
self.setData({ state: 'error', error: err.msg || i18n.t('bleConnect.failed') })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -83,16 +88,16 @@ Page({
|
|||||||
url: '/pages/bind-success/bind-success?device_id=' + self.data.deviceId
|
url: '/pages/bind-success/bind-success?device_id=' + self.data.deviceId
|
||||||
})
|
})
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
self.setData({ state: 'error', error: err.message || '后台确认绑定失败' })
|
self.setData({ state: 'error', error: err.message || i18n.t('bleConnect.confirmFailed') })
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
self.setData({ state: 'error', error: '设备绑定失败' })
|
self.setData({ state: 'error', error: i18n.t('bleConnect.bindFailed') })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ble.on('bind_result', this._onBindResult)
|
ble.on('bind_result', this._onBindResult)
|
||||||
|
|
||||||
ble.bindDevice(userId, self.data.bindToken).catch(function (err) {
|
ble.bindDevice(userId, self.data.bindToken).catch(function (err) {
|
||||||
self.setData({ state: 'error', error: err.error_msg || '绑定命令失败' })
|
self.setData({ state: 'error', error: err.error_msg || i18n.t('bleConnect.bindCmdFailed') })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">蓝牙连接</view>
|
<view class="page-header-title">{{i18n.bleConnect.title}}</view>
|
||||||
<view class="page-header-subtitle">{{state === 'scanning' ? '正在搜索设备...' : state === 'connecting' ? '正在连接设备...' : state === 'binding' ? '正在绑定...' : '连接失败'}}</view>
|
<view class="page-header-subtitle">{{state === 'scanning' ? i18n.bleConnect.searchingSub : state === 'connecting' ? i18n.bleConnect.connectingSub : state === 'binding' ? i18n.bleConnect.bindingSub : i18n.bleConnect.failedSub}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<view class="info-tip">
|
<view class="info-tip">
|
||||||
<text>📡</text>
|
<text>📡</text>
|
||||||
<text>请确保手机蓝牙已开启</text>
|
<text>{{i18n.bleConnect.bluetoothTip}}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="search-card" wx:if="{{state === 'scanning' || state === 'connecting'}}">
|
<view class="search-card" wx:if="{{state === 'scanning' || state === 'connecting'}}">
|
||||||
<view class="search-icon pulsing">⏳</view>
|
<view class="search-icon pulsing">⏳</view>
|
||||||
<view class="search-text">搜索中...</view>
|
<view class="search-text">{{i18n.bleConnect.searching}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="found-card" wx:if="{{state === 'binding'}}">
|
<view class="found-card" wx:if="{{state === 'binding'}}">
|
||||||
<view class="found-icon">💆</view>
|
<view class="found-icon">💆</view>
|
||||||
<view class="found-name">LumiFlow-{{deviceId}}</view>
|
<view class="found-name">LumiFlow-{{deviceId}}</view>
|
||||||
<text class="status-badge status-badge-green">正在配对...</text>
|
<text class="status-badge status-badge-green">{{i18n.bleConnect.pairing}}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="found-card" wx:if="{{state === 'error'}}">
|
<view class="found-card" wx:if="{{state === 'error'}}">
|
||||||
<view class="search-icon">⚠️</view>
|
<view class="search-icon">⚠️</view>
|
||||||
<view class="found-name">{{error || '连接失败'}}</view>
|
<view class="found-name">{{error || i18n.bleConnect.failed}}</view>
|
||||||
<button class="retry-btn" bindtap="onRetry">重试</button>
|
<button class="retry-btn" bindtap="onRetry">{{i18n.common.retry}}</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
|
var i18n = require('../../i18n/index')
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
statusBarHeight: 44
|
statusBarHeight: 44
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
onLoad: function () {
|
onLoad: function () {
|
||||||
var app = getApp()
|
var app = getApp()
|
||||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">联系我们</view>
|
<view class="page-header-title">{{i18n.contact.title}}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<view class="placeholder-container">
|
<view class="placeholder-container">
|
||||||
<view class="placeholder-icon">📬</view>
|
<view class="placeholder-icon">📬</view>
|
||||||
<view class="placeholder-text">内容建设中</view>
|
<view class="placeholder-text">{{i18n.contact.placeholderText}}</view>
|
||||||
<view class="placeholder-sub">联系方式正在整理,敬请期待</view>
|
<view class="placeholder-sub">{{i18n.contact.placeholderSub}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
|
var i18n = require('../../i18n/index')
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
statusBarHeight: 44
|
statusBarHeight: 44
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
onLoad: function () {
|
onLoad: function () {
|
||||||
var app = getApp()
|
var app = getApp()
|
||||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">使用帮助</view>
|
<view class="page-header-title">{{i18n.help.title}}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<view class="placeholder-container">
|
<view class="placeholder-container">
|
||||||
<view class="placeholder-icon">📖</view>
|
<view class="placeholder-icon">📖</view>
|
||||||
<view class="placeholder-text">内容建设中</view>
|
<view class="placeholder-text">{{i18n.help.placeholderText}}</view>
|
||||||
<view class="placeholder-sub">帮助文档正在编写,敬请期待</view>
|
<view class="placeholder-sub">{{i18n.help.placeholderSub}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
var app = getApp()
|
var app = getApp()
|
||||||
|
var i18n = require('../../i18n/index')
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
@@ -6,6 +7,10 @@ Page({
|
|||||||
loading: false
|
loading: false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
onLoad: function () {
|
onLoad: function () {
|
||||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||||
},
|
},
|
||||||
@@ -23,11 +28,11 @@ Page({
|
|||||||
}
|
}
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
self.setData({ loading: false })
|
self.setData({ loading: false })
|
||||||
wx.showToast({ title: err.message || '登录失败', icon: 'none' })
|
wx.showToast({ title: err.message || i18n.t('login.loginFailed'), icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
onAgreement: function () {
|
onAgreement: function () {
|
||||||
wx.showModal({ title: '提示', content: '用户协议和隐私政策内容建设中', showCancel: false })
|
wx.showModal({ title: i18n.t('login.agreementTitle'), content: i18n.t('login.agreementContent'), showCancel: false })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="page-header-title">LumiFlow</view>
|
<view class="page-header-title">LumiFlow</view>
|
||||||
<view class="page-header-subtitle">微信登录</view>
|
<view class="page-header-subtitle">{{i18n.login.wechatLogin}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<view class="welcome-text">欢迎使用LumiFlow</view>
|
<view class="welcome-text">{{i18n.login.welcome}}</view>
|
||||||
|
|
||||||
<view class="flower-area">
|
<view class="flower-area">
|
||||||
<text class="flower">💎</text>
|
<text class="flower">💎</text>
|
||||||
<view class="desc">登录后即可使用全部功能</view>
|
<view class="desc">{{i18n.login.desc}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<button class="login-btn" loading="{{loading}}" bindtap="onLogin" disabled="{{loading}}">
|
<button class="login-btn" loading="{{loading}}" bindtap="onLogin" disabled="{{loading}}">
|
||||||
微信登录
|
{{i18n.login.wechatLogin}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<view class="agreement" bindtap="onAgreement">登录即表示同意《用户协议》和《隐私政策》</view>
|
<view class="agreement" bindtap="onAgreement">{{i18n.login.agreement}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
var app = getApp()
|
var app = getApp()
|
||||||
var http = require('../../utils/request')
|
var http = require('../../utils/request')
|
||||||
var config = require('../../config/env')
|
var config = require('../../config/env')
|
||||||
|
var i18n = require('../../i18n/index')
|
||||||
|
|
||||||
var DEFAULT_AVATAR = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI9t2NHfLvvMiaiaCJQn3KP2P0vAHp1QBP0piavhGfhKCARlGd6z1EbR58ibJXKCp5WVlNnojwA/640'
|
var DEFAULT_AVATAR = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI9t2NHfLvvMiaiaCJQn3KP2P0vAHp1QBP0piavhGfhKCARlGd6z1EbR58ibJXKCp5WVlNnojwA/640'
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ function randomNickname() {
|
|||||||
for (var i = 0; i < 4; i++) {
|
for (var i = 0; i < 4; i++) {
|
||||||
digits += Math.floor(Math.random() * 10)
|
digits += Math.floor(Math.random() * 10)
|
||||||
}
|
}
|
||||||
return '用户' + digits
|
return i18n.t('register.nicknamePrefix') + digits
|
||||||
}
|
}
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
@@ -21,6 +22,10 @@ Page({
|
|||||||
phone: ''
|
phone: ''
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
onLoad: function () {
|
onLoad: function () {
|
||||||
this.setData({
|
this.setData({
|
||||||
statusBarHeight: app.globalData.statusBarHeight,
|
statusBarHeight: app.globalData.statusBarHeight,
|
||||||
@@ -58,19 +63,19 @@ Page({
|
|||||||
onGetPhoneNumber: function (e) {
|
onGetPhoneNumber: function (e) {
|
||||||
var self = this
|
var self = this
|
||||||
if (!e.detail || !e.detail.code) {
|
if (!e.detail || !e.detail.code) {
|
||||||
wx.showToast({ title: '需要授权手机号才能完成注册', icon: 'none' })
|
wx.showToast({ title: i18n.t('register.phoneRequired'), icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.setData({ loading: true })
|
self.setData({ loading: true })
|
||||||
http.post('/api/v1/user/phone', { code: e.detail.code }).then(function (data) {
|
http.post('/api/v1/user/phone', { code: e.detail.code }).then(function (data) {
|
||||||
self.setData({
|
self.setData({
|
||||||
loading: false,
|
loading: false,
|
||||||
phone: '已授权'
|
phone: i18n.t('register.authorized')
|
||||||
})
|
})
|
||||||
wx.showToast({ title: '手机号授权成功', icon: 'success' })
|
wx.showToast({ title: i18n.t('register.phoneAuthSuccess'), icon: 'success' })
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
self.setData({ loading: false })
|
self.setData({ loading: false })
|
||||||
wx.showToast({ title: err.message || '手机号授权失败', icon: 'none' })
|
wx.showToast({ title: err.message || i18n.t('register.phoneAuthFailed'), icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -88,13 +93,13 @@ Page({
|
|||||||
if (data.code === 0 && data.data && data.data.avatar) {
|
if (data.code === 0 && data.data && data.data.avatar) {
|
||||||
resolve(data.data.avatar)
|
resolve(data.data.avatar)
|
||||||
} else {
|
} else {
|
||||||
reject(new Error(data.message || '上传失败'))
|
reject(new Error(data.message || i18n.t('register.uploadFailed')))
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(new Error('上传失败'))
|
reject(new Error(i18n.t('register.uploadFailed')))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: function () { reject(new Error('上传失败')) }
|
fail: function () { reject(new Error(i18n.t('register.uploadFailed'))) }
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -105,12 +110,12 @@ Page({
|
|||||||
var avatarUrl = self.data.avatarUrl
|
var avatarUrl = self.data.avatarUrl
|
||||||
|
|
||||||
if (!self.data.phone) {
|
if (!self.data.phone) {
|
||||||
wx.showToast({ title: '请先授权手机号', icon: 'none' })
|
wx.showToast({ title: i18n.t('register.phoneFirst'), icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nickname) {
|
if (!nickname) {
|
||||||
wx.showToast({ title: '请输入昵称', icon: 'none' })
|
wx.showToast({ title: i18n.t('register.nicknameRequired'), icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,11 +137,11 @@ Page({
|
|||||||
wx.switchTab({ url: '/pages/index/index' })
|
wx.switchTab({ url: '/pages/index/index' })
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
self.setData({ loading: false })
|
self.setData({ loading: false })
|
||||||
wx.showToast({ title: err.message || '保存失败', icon: 'none' })
|
wx.showToast({ title: err.message || i18n.t('common.saveFailed'), icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
onAgreement: function () {
|
onAgreement: function () {
|
||||||
wx.showModal({ title: '提示', content: '用户协议和隐私政策内容建设中', showCancel: false })
|
wx.showModal({ title: i18n.t('register.agreementTitle'), content: i18n.t('register.agreementContent'), showCancel: false })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="page-header-title">LumiFlow</view>
|
<view class="page-header-title">LumiFlow</view>
|
||||||
<view class="page-header-subtitle">完善个人资料</view>
|
<view class="page-header-subtitle">{{i18n.register.subtitle}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<view class="section-title">设置头像</view>
|
<view class="section-title">{{i18n.register.avatarSection}}</view>
|
||||||
<view class="avatar-area">
|
<view class="avatar-area">
|
||||||
<view class="avatar-wrap" bindtap="onPickAvatar">
|
<view class="avatar-wrap" bindtap="onPickAvatar">
|
||||||
<image class="avatar-img" src="{{avatarUrl}}" mode="aspectFill"></image>
|
<image class="avatar-img" src="{{avatarUrl}}" mode="aspectFill"></image>
|
||||||
<view class="avatar-edit-hint">点击更换</view>
|
<view class="avatar-edit-hint">{{i18n.register.changeAvatar}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="section-title">设置昵称</view>
|
<view class="section-title">{{i18n.register.nicknameSection}}</view>
|
||||||
<view class="input-wrap">
|
<view class="input-wrap">
|
||||||
<input type="nickname" class="weui-input nickname-input" value="{{nickname}}" bindinput="onNicknameInput" bindblur="onNicknameBlur" placeholder="请输入昵称" />
|
<input type="nickname" class="weui-input nickname-input" value="{{nickname}}" bindinput="onNicknameInput" bindblur="onNicknameBlur" placeholder="{{i18n.register.nicknamePlaceholder}}" />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="section-title">授权手机号</view>
|
<view class="section-title">{{i18n.register.authorizePhone}}</view>
|
||||||
<view class="phone-area">
|
<view class="phone-area">
|
||||||
<block wx:if="{{phone}}">
|
<block wx:if="{{phone}}">
|
||||||
<view class="phone-done">
|
<view class="phone-done">
|
||||||
@@ -28,16 +28,16 @@
|
|||||||
</block>
|
</block>
|
||||||
<block wx:else>
|
<block wx:else>
|
||||||
<button class="phone-btn" open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber" disabled="{{loading}}">
|
<button class="phone-btn" open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber" disabled="{{loading}}">
|
||||||
授权手机号
|
{{i18n.register.authorizePhone}}
|
||||||
</button>
|
</button>
|
||||||
<view class="phone-hint">手机号用于接收服务通知,必须授权</view>
|
<view class="phone-hint">{{i18n.register.phoneHint}}</view>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<button class="submit-btn" bindtap="onSubmit" loading="{{loading}}" disabled="{{loading}}">
|
<button class="submit-btn" bindtap="onSubmit" loading="{{loading}}" disabled="{{loading}}">
|
||||||
完成注册
|
{{i18n.register.submit}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<view class="agreement" bindtap="onAgreement">注册即表示同意《用户协议》和《隐私政策》</view>
|
<view class="agreement" bindtap="onAgreement">{{i18n.register.agreement}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
var http = require('../../utils/request')
|
var http = require('../../utils/request')
|
||||||
|
var i18n = require('../../i18n/index')
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
@@ -8,6 +9,10 @@ Page({
|
|||||||
error: ''
|
error: ''
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
onBack: function () {
|
onBack: function () {
|
||||||
wx.navigateBack({
|
wx.navigateBack({
|
||||||
fail: function () {
|
fail: function () {
|
||||||
@@ -31,7 +36,7 @@ Page({
|
|||||||
success: function (res) {
|
success: function (res) {
|
||||||
var deviceId = self.parseDeviceId(res.result)
|
var deviceId = self.parseDeviceId(res.result)
|
||||||
if (!deviceId) {
|
if (!deviceId) {
|
||||||
self.setData({ scanning: false, error: '无效的设备二维码' })
|
self.setData({ scanning: false, error: i18n.t('scan.invalidQr') })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.bindDevice(deviceId)
|
self.bindDevice(deviceId)
|
||||||
@@ -66,12 +71,12 @@ Page({
|
|||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
self.setData({ scanning: false })
|
self.setData({ scanning: false })
|
||||||
if (err && err.code === 2001) {
|
if (err && err.code === 2001) {
|
||||||
wx.showToast({ title: '已绑定设备', icon: 'none' })
|
wx.showToast({ title: i18n.t('scan.alreadyBound'), icon: 'none' })
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
wx.navigateBack()
|
wx.navigateBack()
|
||||||
}, 1500)
|
}, 1500)
|
||||||
} else {
|
} else {
|
||||||
self.setData({ error: err.message || '绑定失败' })
|
self.setData({ error: err.message || i18n.t('scan.bindFailed') })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -79,16 +84,16 @@ Page({
|
|||||||
onManualInput: function () {
|
onManualInput: function () {
|
||||||
var self = this
|
var self = this
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '手动输入设备号',
|
title: i18n.t('scan.manualInput'),
|
||||||
editable: true,
|
editable: true,
|
||||||
placeholderText: '请输入设备ID,如 672B6D5A 4DCD861',
|
placeholderText: i18n.t('scan.manualPlaceholder'),
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
if (res.confirm && res.content) {
|
if (res.confirm && res.content) {
|
||||||
var deviceId = self.parseDeviceId(res.content)
|
var deviceId = self.parseDeviceId(res.content)
|
||||||
if (deviceId) {
|
if (deviceId) {
|
||||||
self.bindDevice(deviceId)
|
self.bindDevice(deviceId)
|
||||||
} else {
|
} else {
|
||||||
wx.showToast({ title: '设备号格式不正确', icon: 'none' })
|
wx.showToast({ title: i18n.t('scan.invalidFormat'), icon: 'none' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">扫码绑定</view>
|
<view class="page-header-title">{{i18n.scan.title}}</view>
|
||||||
<view class="page-header-subtitle">扫描设备底部二维码</view>
|
<view class="page-header-subtitle">{{i18n.scan.subtitle}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<view class="scan-area">
|
<view class="scan-area">
|
||||||
<text class="scan-icon">📷</text>
|
<text class="scan-icon">📷</text>
|
||||||
<text class="scan-text">扫描设备二维码</text>
|
<text class="scan-text">{{i18n.scan.scanText}}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<button class="btn-primary" bindtap="onScan" disabled="{{scanning}}">
|
<button class="btn-primary" bindtap="onScan" disabled="{{scanning}}">
|
||||||
{{scanning ? '扫描中...' : '扫码'}}
|
{{scanning ? i18n.scan.scanning : i18n.scan.scan}}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn-secondary" bindtap="onManualInput">手动输入设备号</button>
|
<button class="btn-secondary" bindtap="onManualInput">{{i18n.scan.manualInput}}</button>
|
||||||
|
|
||||||
<view class="error-text" wx:if="{{error}}">{{error}}</view>
|
<view class="error-text" wx:if="{{error}}">{{error}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
var api = require('../../utils/api')
|
var api = require('../../utils/api')
|
||||||
|
var i18n = require('../../i18n/index')
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
@@ -8,6 +9,7 @@ Page({
|
|||||||
purchasing: false,
|
purchasing: false,
|
||||||
subscription: null,
|
subscription: null,
|
||||||
subRemaining: 0,
|
subRemaining: 0,
|
||||||
|
subRemainingText: '',
|
||||||
loadingPlans: true
|
loadingPlans: true
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -25,10 +27,18 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onShow: function () {
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
this.loadPlans()
|
this.loadPlans()
|
||||||
this.loadSubscription()
|
this.loadSubscription()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
planName: function (key) {
|
||||||
|
if (key === 'trial') return i18n.t('subPlans.planTrial')
|
||||||
|
if (key === 'monthly') return i18n.t('subPlans.planMonthly')
|
||||||
|
if (key === 'yearly') return i18n.t('subPlans.planYearly')
|
||||||
|
return key
|
||||||
|
},
|
||||||
|
|
||||||
loadPlans: function () {
|
loadPlans: function () {
|
||||||
var self = this
|
var self = this
|
||||||
self.setData({ loadingPlans: true })
|
self.setData({ loadingPlans: true })
|
||||||
@@ -41,16 +51,16 @@ Page({
|
|||||||
var plans = serverPlans.map(function (p) {
|
var plans = serverPlans.map(function (p) {
|
||||||
var daily = p.days > 0 ? (p.price / p.days).toFixed(1) : 0
|
var daily = p.days > 0 ? (p.price / p.days).toFixed(1) : 0
|
||||||
var desc = ''
|
var desc = ''
|
||||||
if (p.key === 'trial') desc = p.days + '天免费体验'
|
if (p.key === 'trial') desc = i18n.t('subPlans.descTrial', { days: p.days })
|
||||||
else if (p.key === 'yearly') desc = '省¥' + Math.round(monthlyPrice * 12 - p.price)
|
else if (p.key === 'yearly') desc = i18n.t('subPlans.descSave', { amount: Math.round(monthlyPrice * 12 - p.price) })
|
||||||
else desc = '约' + daily + '元/天'
|
else desc = i18n.t('subPlans.descDaily', { price: daily })
|
||||||
return {
|
return {
|
||||||
key: p.key,
|
key: p.key,
|
||||||
name: p.name,
|
name: self.planName(p.key),
|
||||||
price: p.price,
|
price: p.price,
|
||||||
priceLabel: p.key === 'trial' ? '免费' : '¥' + p.price,
|
priceLabel: p.key === 'trial' ? i18n.t('subPlans.free') : '¥' + p.price,
|
||||||
desc: desc,
|
desc: desc,
|
||||||
tag: p.key === 'yearly' ? '推荐' : '',
|
tag: p.key === 'yearly' ? i18n.t('subPlans.tagRecommend') : '',
|
||||||
disabled: false
|
disabled: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -61,9 +71,9 @@ Page({
|
|||||||
self.setData({
|
self.setData({
|
||||||
loadingPlans: false,
|
loadingPlans: false,
|
||||||
plans: [
|
plans: [
|
||||||
{ key: 'trial', name: '试用', price: 0, priceLabel: '免费', desc: '7天免费体验', disabled: false },
|
{ key: 'trial', name: self.planName('trial'), price: 0, priceLabel: i18n.t('subPlans.free'), desc: i18n.t('subPlans.descTrial', { days: 7 }), disabled: false },
|
||||||
{ key: 'monthly', name: '月卡', price: 99, priceLabel: '¥99', desc: '约3.3元/天' },
|
{ key: 'monthly', name: self.planName('monthly'), price: 99, priceLabel: '¥99', desc: i18n.t('subPlans.descDaily', { price: '3.3' }) },
|
||||||
{ key: 'yearly', name: '年卡', price: 899, priceLabel: '¥899', desc: '省¥289', tag: '推荐' }
|
{ key: 'yearly', name: self.planName('yearly'), price: 899, priceLabel: '¥899', desc: i18n.t('subPlans.descSave', { amount: 289 }), tag: i18n.t('subPlans.tagRecommend') }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
self.checkTrialUsed()
|
self.checkTrialUsed()
|
||||||
@@ -75,7 +85,7 @@ Page({
|
|||||||
var sub = self.data.subscription
|
var sub = self.data.subscription
|
||||||
if (sub && sub.trial_used) {
|
if (sub && sub.trial_used) {
|
||||||
var plans = self.data.plans.map(function (p) {
|
var plans = self.data.plans.map(function (p) {
|
||||||
if (p.key === 'trial') return Object.assign({}, p, { disabled: true, tag: '已使用' })
|
if (p.key === 'trial') return Object.assign({}, p, { disabled: true, tag: i18n.t('subPlans.tagUsed') })
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
self.setData({ plans: plans })
|
self.setData({ plans: plans })
|
||||||
@@ -86,16 +96,18 @@ Page({
|
|||||||
loadSubscription: function () {
|
loadSubscription: function () {
|
||||||
var self = this
|
var self = this
|
||||||
api.getSubscription().then(function (sub) {
|
api.getSubscription().then(function (sub) {
|
||||||
|
var remaining = sub.remaining_days || 0
|
||||||
self.setData({
|
self.setData({
|
||||||
subscription: sub,
|
subscription: sub,
|
||||||
subRemaining: sub.remaining_days || 0
|
subRemaining: remaining,
|
||||||
|
subRemainingText: i18n.t('subPlans.daysValue', { days: remaining })
|
||||||
})
|
})
|
||||||
self.checkTrialUsed()
|
self.checkTrialUsed()
|
||||||
}).catch(function () {})
|
}).catch(function () {})
|
||||||
},
|
},
|
||||||
|
|
||||||
onAgreement: function () {
|
onAgreement: function () {
|
||||||
wx.showModal({ title: '提示', content: '订阅协议内容建设中', showCancel: false })
|
wx.showModal({ title: i18n.t('subPlans.tipTitle'), content: i18n.t('subPlans.agreementBuilding'), showCancel: false })
|
||||||
},
|
},
|
||||||
|
|
||||||
onSelect: function (e) {
|
onSelect: function (e) {
|
||||||
@@ -116,8 +128,8 @@ Page({
|
|||||||
|
|
||||||
if (plan.key === 'trial') {
|
if (plan.key === 'trial') {
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '激活试用',
|
title: i18n.t('subPlans.activateTrialTitle'),
|
||||||
content: '免费试用 ' + plan.desc + '?',
|
content: i18n.t('subPlans.activateTrialConfirm', { desc: plan.desc }),
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
if (res.confirm) self.doActivateTrial()
|
if (res.confirm) self.doActivateTrial()
|
||||||
}
|
}
|
||||||
@@ -127,10 +139,10 @@ Page({
|
|||||||
|
|
||||||
var isRenew = self.data.subscription && self.data.subscription.status === 'active' && self.data.subRemaining > 0
|
var isRenew = self.data.subscription && self.data.subscription.status === 'active' && self.data.subRemaining > 0
|
||||||
var planDays = plan.key === 'yearly' ? 365 : 30
|
var planDays = plan.key === 'yearly' ? 365 : 30
|
||||||
var title = isRenew ? '确认续费' : '确认支付'
|
var title = isRenew ? i18n.t('subPlans.confirmRenewTitle') : i18n.t('subPlans.confirmPayTitle')
|
||||||
var content = isRenew
|
var content = isRenew
|
||||||
? '在现有订阅基础上延长' + planDays + '天,支付 ¥' + plan.price
|
? i18n.t('subPlans.renewContent', { days: planDays, price: plan.price })
|
||||||
: '支付 ¥' + plan.price
|
: i18n.t('subPlans.payContent', { price: plan.price })
|
||||||
|
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: title,
|
title: title,
|
||||||
@@ -148,13 +160,13 @@ Page({
|
|||||||
self.setData({ purchasing: true })
|
self.setData({ purchasing: true })
|
||||||
api.activateTrial().then(function () {
|
api.activateTrial().then(function () {
|
||||||
self.setData({ purchasing: false })
|
self.setData({ purchasing: false })
|
||||||
wx.showToast({ title: '试用已激活', icon: 'success' })
|
wx.showToast({ title: i18n.t('subPlans.trialActivated'), icon: 'success' })
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
wx.redirectTo({ url: '/pages/subscribe-success/subscribe-success?plan=trial' })
|
wx.redirectTo({ url: '/pages/subscribe-success/subscribe-success?plan=trial' })
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
self.setData({ purchasing: false })
|
self.setData({ purchasing: false })
|
||||||
wx.showToast({ title: err.message || '激活失败', icon: 'none' })
|
wx.showToast({ title: err.message || i18n.t('subPlans.activateFailed'), icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -164,7 +176,7 @@ Page({
|
|||||||
api.purchase(plan.key).then(function (data) {
|
api.purchase(plan.key).then(function (data) {
|
||||||
if (!data.payment_params) {
|
if (!data.payment_params) {
|
||||||
self.setData({ purchasing: false })
|
self.setData({ purchasing: false })
|
||||||
wx.showToast({ title: '支付服务暂不可用', icon: 'none' })
|
wx.showToast({ title: i18n.t('subPlans.payUnavailable'), icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,13 +194,13 @@ Page({
|
|||||||
},
|
},
|
||||||
fail: function (err) {
|
fail: function (err) {
|
||||||
self.setData({ purchasing: false })
|
self.setData({ purchasing: false })
|
||||||
var msg = (err.errMsg || '').indexOf('cancel') > -1 ? '已取消支付' : '支付失败'
|
var msg = (err.errMsg || '').indexOf('cancel') > -1 ? i18n.t('subPlans.payCancelled') : i18n.t('subPlans.payFailed')
|
||||||
wx.showToast({ title: msg, icon: 'none' })
|
wx.showToast({ title: msg, icon: 'none' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
self.setData({ purchasing: false })
|
self.setData({ purchasing: false })
|
||||||
wx.showToast({ title: err.message || '创建订单失败', icon: 'none' })
|
wx.showToast({ title: err.message || i18n.t('subPlans.createOrderFailed'), icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -201,7 +213,7 @@ Page({
|
|||||||
api.syncPaymentOrder(orderId).then(function (result) {
|
api.syncPaymentOrder(orderId).then(function (result) {
|
||||||
if (result.status === 'paid') {
|
if (result.status === 'paid') {
|
||||||
self.setData({ purchasing: false })
|
self.setData({ purchasing: false })
|
||||||
wx.showToast({ title: '支付成功', icon: 'success' })
|
wx.showToast({ title: i18n.t('subPlans.paySuccess'), icon: 'success' })
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
wx.redirectTo({ url: '/pages/subscribe-success/subscribe-success?plan=' + planKey })
|
wx.redirectTo({ url: '/pages/subscribe-success/subscribe-success?plan=' + planKey })
|
||||||
}, 1000)
|
}, 1000)
|
||||||
@@ -210,7 +222,7 @@ Page({
|
|||||||
setTimeout(pollSync, 2000)
|
setTimeout(pollSync, 2000)
|
||||||
} else {
|
} else {
|
||||||
self.setData({ purchasing: false })
|
self.setData({ purchasing: false })
|
||||||
wx.showToast({ title: '支付处理中,请稍后在订阅页查看', icon: 'none' })
|
wx.showToast({ title: i18n.t('subPlans.payProcessing'), icon: 'none' })
|
||||||
}
|
}
|
||||||
}).catch(function () {
|
}).catch(function () {
|
||||||
if (retryCount < maxRetries) {
|
if (retryCount < maxRetries) {
|
||||||
@@ -218,7 +230,7 @@ Page({
|
|||||||
setTimeout(pollSync, 2000)
|
setTimeout(pollSync, 2000)
|
||||||
} else {
|
} else {
|
||||||
self.setData({ purchasing: false })
|
self.setData({ purchasing: false })
|
||||||
wx.showToast({ title: '支付处理中,请稍后在订阅页查看', icon: 'none' })
|
wx.showToast({ title: i18n.t('subPlans.payProcessing'), icon: 'none' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-gold" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-gold" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">订阅服务</view>
|
<view class="page-header-title">{{i18n.subPlans.title}}</view>
|
||||||
<view class="page-header-subtitle">解锁智能模式</view>
|
<view class="page-header-subtitle">{{i18n.subPlans.subtitle}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<!-- Current subscription status -->
|
<!-- Current subscription status -->
|
||||||
<view class="current-sub" wx:if="{{subscription && subscription.status === 'active' && subRemaining > 0}}">
|
<view class="current-sub" wx:if="{{subscription && subscription.status === 'active' && subRemaining > 0}}">
|
||||||
<view class="current-sub-row">
|
<view class="current-sub-row">
|
||||||
<text class="current-sub-label">当前套餐</text>
|
<text class="current-sub-label">{{i18n.subPlans.currentPlanLabel}}</text>
|
||||||
<text class="current-sub-value">{{subscription.plan === 'trial' ? '试用' : subscription.plan === 'monthly' ? '月卡' : subscription.plan === 'yearly' ? '年卡' : subscription.plan}}</text>
|
<text class="current-sub-value">{{subscription.plan === 'trial' ? i18n.subPlans.planTrial : subscription.plan === 'monthly' ? i18n.subPlans.planMonthly : subscription.plan === 'yearly' ? i18n.subPlans.planYearly : subscription.plan}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="current-sub-row">
|
<view class="current-sub-row">
|
||||||
<text class="current-sub-label">剩余天数</text>
|
<text class="current-sub-label">{{i18n.subPlans.remainDaysLabel}}</text>
|
||||||
<text class="current-sub-value">{{subRemaining}}天</text>
|
<text class="current-sub-value">{{subRemainingText}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-title">选择订阅套餐</view>
|
<view class="page-title">{{i18n.subPlans.choosePlan}}</view>
|
||||||
|
|
||||||
<view class="loading-plans" wx:if="{{loadingPlans}}">
|
<view class="loading-plans" wx:if="{{loadingPlans}}">
|
||||||
<view class="loading-spinner"></view>
|
<view class="loading-spinner"></view>
|
||||||
<text class="loading-text">加载套餐中...</text>
|
<text class="loading-text">{{i18n.subPlans.loadingPlans}}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="service-card" wx:if="{{!loadingPlans}}">
|
<view class="service-card" wx:if="{{!loadingPlans}}">
|
||||||
<view class="service-icon">✨</view>
|
<view class="service-icon">✨</view>
|
||||||
<view class="service-info">
|
<view class="service-info">
|
||||||
<view class="service-name">智能模式</view>
|
<view class="service-name">{{i18n.subPlans.smartMode}}</view>
|
||||||
<view class="service-desc">智能自动调节</view>
|
<view class="service-desc">{{i18n.subPlans.smartModeDesc}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -44,9 +44,9 @@
|
|||||||
|
|
||||||
<block wx:if="{{!loadingPlans}}">
|
<block wx:if="{{!loadingPlans}}">
|
||||||
<button class="btn-primary btn-primary-gold" bindtap="onPurchase" disabled="{{purchasing}}" loading="{{purchasing}}">
|
<button class="btn-primary btn-primary-gold" bindtap="onPurchase" disabled="{{purchasing}}" loading="{{purchasing}}">
|
||||||
{{subscription && subscription.status === 'active' && subRemaining > 0 ? '续费' : '确认支付'}}
|
{{subscription && subscription.status === 'active' && subRemaining > 0 ? i18n.subPlans.renew : i18n.subPlans.confirmPay}}
|
||||||
</button>
|
</button>
|
||||||
<view class="agreement" bindtap="onAgreement">支付即表示同意《订阅协议》</view>
|
<view class="agreement" bindtap="onAgreement">{{i18n.subPlans.agreement}}</view>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
|
var i18n = require('../../i18n/index')
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
statusBarHeight: 44
|
statusBarHeight: 44
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
onLoad: function () {
|
onLoad: function () {
|
||||||
var app = getApp()
|
var app = getApp()
|
||||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">使用设置</view>
|
<view class="page-header-title">{{i18n.subPrompt.title}}</view>
|
||||||
<view class="page-header-subtitle">试用期已结束</view>
|
<view class="page-header-subtitle">{{i18n.subPrompt.subtitle}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<view class="page-title">选择模式</view>
|
<view class="page-title">{{i18n.subPrompt.selectMode}}</view>
|
||||||
|
|
||||||
<view class="mode-selector">
|
<view class="mode-selector">
|
||||||
<view class="mode-option active">
|
<view class="mode-option active">
|
||||||
<text class="mode-icon">🔄</text>
|
<text class="mode-icon">🔄</text>
|
||||||
<view class="mode-label">普通模式</view>
|
<view class="mode-label">{{i18n.subPrompt.normalMode}}</view>
|
||||||
<view class="mode-sub">6色循环</view>
|
<view class="mode-sub">{{i18n.subPrompt.normalModeSub}}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="mode-option locked">
|
<view class="mode-option locked">
|
||||||
<text class="mode-icon">🔒</text>
|
<text class="mode-icon">🔒</text>
|
||||||
<view class="mode-label">智能模式</view>
|
<view class="mode-label">{{i18n.subPrompt.smartMode}}</view>
|
||||||
<view class="mode-sub">订阅解锁</view>
|
<view class="mode-sub">{{i18n.subPrompt.smartModeSub}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="tip-card">
|
<view class="tip-card">
|
||||||
<text class="tip-card-icon">⏰</text>
|
<text class="tip-card-icon">⏰</text>
|
||||||
<view class="tip-card-body">
|
<view class="tip-card-body">
|
||||||
<view class="tip-card-title">试用已结束</view>
|
<view class="tip-card-title">{{i18n.subPrompt.trialEndedTitle}}</view>
|
||||||
<view class="tip-card-sub">订阅后继续使用智能模式</view>
|
<view class="tip-card-sub">{{i18n.subPrompt.trialEndedSub}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<button class="btn-primary btn-primary-gold" bindtap="onViewPlans">立即订阅</button>
|
<button class="btn-primary btn-primary-gold" bindtap="onViewPlans">{{i18n.subPrompt.subscribeNow}}</button>
|
||||||
<button class="btn-secondary" bindtap="onUseFreeMode">先使用普通模式</button>
|
<button class="btn-secondary" bindtap="onUseFreeMode">{{i18n.subPrompt.useNormalFirst}}</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
var api = require('../../utils/api')
|
var api = require('../../utils/api')
|
||||||
|
var i18n = require('../../i18n/index')
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
statusBarHeight: 44,
|
statusBarHeight: 44,
|
||||||
planName: '年卡会员',
|
planName: '',
|
||||||
expiryDate: ''
|
expiryDate: ''
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -15,33 +16,56 @@ Page({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
|
this.render()
|
||||||
|
},
|
||||||
|
|
||||||
|
// Recompute locale-dependent display strings from stored raw values.
|
||||||
|
render: function () {
|
||||||
|
this.setData({
|
||||||
|
planName: this.planNameFor(this._plan),
|
||||||
|
expiryDate: this._expireTime ? this.formatDate(this._expireTime) : ''
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
planNameFor: function (plan) {
|
||||||
|
if (plan === 'trial') return i18n.t('subSuccess.planTrial')
|
||||||
|
if (plan === 'monthly') return i18n.t('subSuccess.planMonthly')
|
||||||
|
if (plan === 'yearly') return i18n.t('subSuccess.planYearly')
|
||||||
|
return i18n.t('subSuccess.planDefault')
|
||||||
|
},
|
||||||
|
|
||||||
|
formatDate: function (raw) {
|
||||||
|
var date = new Date(raw)
|
||||||
|
var y = date.getFullYear()
|
||||||
|
var m = ('0' + (date.getMonth() + 1)).slice(-2)
|
||||||
|
var d = ('0' + date.getDate()).slice(-2)
|
||||||
|
if (i18n.getLocale() === 'en') return y + '-' + m + '-' + d
|
||||||
|
return y + '年' + m + '月' + d + '日'
|
||||||
|
},
|
||||||
|
|
||||||
onLoad: function (options) {
|
onLoad: function (options) {
|
||||||
var self = this
|
var self = this
|
||||||
var app = getApp()
|
var app = getApp()
|
||||||
self.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
self.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||||
|
|
||||||
var planMap = { yearly: '年卡会员', monthly: '月卡会员', trial: '试用会员' }
|
|
||||||
var plan = options.plan || 'yearly'
|
var plan = options.plan || 'yearly'
|
||||||
self.setData({ planName: planMap[plan] || '会员' })
|
self._plan = plan
|
||||||
|
|
||||||
// Fetch actual subscription expiry from server instead of computing client-side
|
// Fetch actual subscription expiry from server instead of computing client-side
|
||||||
api.getSubscription().then(function (res) {
|
api.getSubscription().then(function (res) {
|
||||||
if (res && res.expire_time) {
|
if (res && res.expire_time) {
|
||||||
var date = new Date(res.expire_time)
|
self._expireTime = res.expire_time
|
||||||
var y = date.getFullYear()
|
|
||||||
var m = ('0' + (date.getMonth() + 1)).slice(-2)
|
|
||||||
var d = ('0' + date.getDate()).slice(-2)
|
|
||||||
self.setData({ expiryDate: y + '年' + m + '月' + d + '日' })
|
|
||||||
}
|
}
|
||||||
|
self.render()
|
||||||
}).catch(function () {
|
}).catch(function () {
|
||||||
// Fallback: compute from current date if server call fails
|
// Fallback: compute from current date if server call fails
|
||||||
var durationMap = { yearly: 365, monthly: 30, trial: 7 }
|
var durationMap = { yearly: 365, monthly: 30, trial: 7 }
|
||||||
var now = new Date()
|
var now = new Date()
|
||||||
now.setDate(now.getDate() + (durationMap[plan] || 365))
|
now.setDate(now.getDate() + (durationMap[plan] || 365))
|
||||||
var y = now.getFullYear()
|
self._expireTime = now.getTime()
|
||||||
var m = ('0' + (now.getMonth() + 1)).slice(-2)
|
self.render()
|
||||||
var d = ('0' + now.getDate()).slice(-2)
|
|
||||||
self.setData({ expiryDate: y + '年' + m + '月' + d + '日' })
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-green" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-green" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">订阅成功</view>
|
<view class="page-header-title">{{i18n.subSuccess.title}}</view>
|
||||||
<view class="page-header-subtitle">欢迎使用智能模式</view>
|
<view class="page-header-subtitle">{{i18n.subSuccess.subtitle}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<view class="success-area">
|
<view class="success-area">
|
||||||
<view class="success-icon">✓</view>
|
<view class="success-icon">✓</view>
|
||||||
<view class="success-title">订阅成功!</view>
|
<view class="success-title">{{i18n.subSuccess.successTitle}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="sub-card">
|
<view class="sub-card">
|
||||||
<view class="sub-header">
|
<view class="sub-header">
|
||||||
<text class="sub-name">✨ {{planName}}</text>
|
<text class="sub-name">✨ {{planName}}</text>
|
||||||
<text class="sub-status status-badge status-badge-green">已订阅</text>
|
<text class="sub-status status-badge status-badge-green">{{i18n.subSuccess.subscribed}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="sub-info">有效期至:{{expiryDate}}</view>
|
<view class="sub-info">{{i18n.subSuccess.validUntil}}{{expiryDate}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<button class="btn-primary btn-primary-gold" bindtap="onStartSmart">立即体验智能模式</button>
|
<button class="btn-primary btn-primary-gold" bindtap="onStartSmart">{{i18n.subSuccess.startSmart}}</button>
|
||||||
<button class="btn-secondary btn-secondary-gold" bindtap="onBackHome">返回首页</button>
|
<button class="btn-secondary btn-secondary-gold" bindtap="onBackHome">{{i18n.subSuccess.backHome}}</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
var ble = require('../../services/ble')
|
var ble = require('../../services/ble')
|
||||||
var api = require('../../utils/api')
|
var api = require('../../utils/api')
|
||||||
var commandSync = require('../../services/command-sync')
|
var commandSync = require('../../services/command-sync')
|
||||||
|
var i18n = require('../../i18n/index')
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
statusBarHeight: 44,
|
statusBarHeight: 44,
|
||||||
@@ -16,19 +17,31 @@ Page({
|
|||||||
paused: false,
|
paused: false,
|
||||||
completed: false,
|
completed: false,
|
||||||
startedAt: 0,
|
startedAt: 0,
|
||||||
regionText: '全区域',
|
regionText: '',
|
||||||
fittingLost: false,
|
fittingLost: false,
|
||||||
deviceFault: false
|
deviceFault: false
|
||||||
},
|
},
|
||||||
|
|
||||||
_getRegionText: function (regionMask) {
|
_getRegionText: function (regionMask) {
|
||||||
var REGION_MAP = {1:'右区', 2:'左区', 4:'上区', 8:'中区', 16:'下区', 32:'左上区', 64:'右上区'}
|
var REGION_MAP = {
|
||||||
if (regionMask === 0x7F) return '全区域'
|
1: 'common.regions.right',
|
||||||
|
2: 'common.regions.left',
|
||||||
|
4: 'common.regions.top',
|
||||||
|
8: 'common.regions.middle',
|
||||||
|
16: 'common.regions.bottom',
|
||||||
|
32: 'treating.regionTopLeft',
|
||||||
|
64: 'treating.regionTopRight'
|
||||||
|
}
|
||||||
|
if (regionMask === 0x7F) return i18n.t('treating.allRegions')
|
||||||
var names = []
|
var names = []
|
||||||
for (var bit in REGION_MAP) {
|
for (var bit in REGION_MAP) {
|
||||||
if (regionMask & parseInt(bit)) names.push(REGION_MAP[bit])
|
if (regionMask & parseInt(bit)) names.push(i18n.t(REGION_MAP[bit]))
|
||||||
}
|
}
|
||||||
return names.length > 0 ? names.join('+') : '全区域'
|
return names.length > 0 ? names.join('+') : i18n.t('treating.allRegions')
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad: function (options) {
|
onLoad: function (options) {
|
||||||
@@ -91,10 +104,10 @@ Page({
|
|||||||
if (this.data.completed) return
|
if (this.data.completed) return
|
||||||
if (data.fitting === 0 && !this.data.fittingLost) {
|
if (data.fitting === 0 && !this.data.fittingLost) {
|
||||||
this.setData({ fittingLost: true, paused: true })
|
this.setData({ fittingLost: true, paused: true })
|
||||||
wx.showToast({ title: '检测到离肤,请重新佩戴设备', icon: 'none', duration: 3000 })
|
wx.showToast({ title: i18n.t('treating.fittingLost'), icon: 'none', duration: 3000 })
|
||||||
} else if (data.fitting === 1 && this.data.fittingLost) {
|
} else if (data.fitting === 1 && this.data.fittingLost) {
|
||||||
this.setData({ fittingLost: false, paused: false })
|
this.setData({ fittingLost: false, paused: false })
|
||||||
wx.showToast({ title: '已重新贴合,继续使用', icon: 'success', duration: 2000 })
|
wx.showToast({ title: i18n.t('treating.fittingRestored'), icon: 'success', duration: 2000 })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -112,8 +125,8 @@ Page({
|
|||||||
this.setData({ deviceFault: true })
|
this.setData({ deviceFault: true })
|
||||||
var self = this
|
var self = this
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '设备故障',
|
title: i18n.t('treating.faultTitle'),
|
||||||
content: '设备上报故障,已自动停止。',
|
content: i18n.t('treating.faultContent'),
|
||||||
showCancel: false,
|
showCancel: false,
|
||||||
complete: function () { self.finishAsComplete() }
|
complete: function () { self.finishAsComplete() }
|
||||||
})
|
})
|
||||||
@@ -136,8 +149,8 @@ Page({
|
|||||||
if (self.data.battery > 0 && self.data.battery <= 5) {
|
if (self.data.battery > 0 && self.data.battery <= 5) {
|
||||||
clearInterval(timer)
|
clearInterval(timer)
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '电量过低',
|
title: i18n.t('treating.lowBatteryTitle'),
|
||||||
content: '设备电量不足5%,已自动停止。请及时充电。',
|
content: i18n.t('treating.lowBatteryContent'),
|
||||||
showCancel: false,
|
showCancel: false,
|
||||||
complete: function () { self.finishAsComplete() }
|
complete: function () { self.finishAsComplete() }
|
||||||
})
|
})
|
||||||
@@ -146,7 +159,7 @@ Page({
|
|||||||
|
|
||||||
if (self.data.battery > 0 && self.data.battery <= 10 && !self._lowBatteryWarned) {
|
if (self.data.battery > 0 && self.data.battery <= 10 && !self._lowBatteryWarned) {
|
||||||
self._lowBatteryWarned = true
|
self._lowBatteryWarned = true
|
||||||
wx.showToast({ title: '电量低,请及时充电', icon: 'none', duration: 3000 })
|
wx.showToast({ title: i18n.t('treating.lowBatteryWarn'), icon: 'none', duration: 3000 })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remaining <= 0) {
|
if (remaining <= 0) {
|
||||||
@@ -262,8 +275,8 @@ Page({
|
|||||||
|
|
||||||
onException: function (err) {
|
onException: function (err) {
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '设备异常',
|
title: i18n.t('treating.exceptionTitle'),
|
||||||
content: err.error_msg || '使用过程中出现异常',
|
content: err.error_msg || i18n.t('treating.exceptionContent'),
|
||||||
showCancel: false,
|
showCancel: false,
|
||||||
complete: function () {
|
complete: function () {
|
||||||
wx.navigateBack()
|
wx.navigateBack()
|
||||||
@@ -274,7 +287,7 @@ Page({
|
|||||||
onDisconnect: function () {
|
onDisconnect: function () {
|
||||||
this.setData({ paused: true })
|
this.setData({ paused: true })
|
||||||
wx.showToast({
|
wx.showToast({
|
||||||
title: '设备连接断开,正在重连...',
|
title: i18n.t('treating.disconnected'),
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
duration: 3000
|
duration: 3000
|
||||||
})
|
})
|
||||||
@@ -283,7 +296,7 @@ Page({
|
|||||||
onReconnect: function () {
|
onReconnect: function () {
|
||||||
this.setData({ paused: false })
|
this.setData({ paused: false })
|
||||||
wx.showToast({
|
wx.showToast({
|
||||||
title: '设备已重新连接',
|
title: i18n.t('treating.reconnected'),
|
||||||
icon: 'success',
|
icon: 'success',
|
||||||
duration: 2000
|
duration: 2000
|
||||||
})
|
})
|
||||||
@@ -296,8 +309,8 @@ Page({
|
|||||||
this._localTimer = null
|
this._localTimer = null
|
||||||
}
|
}
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '连接丢失',
|
title: i18n.t('treating.reconnectFailedTitle'),
|
||||||
content: '设备蓝牙连接已断开,无法恢复。本次使用数据将保存。',
|
content: i18n.t('treating.reconnectFailedContent'),
|
||||||
showCancel: false,
|
showCancel: false,
|
||||||
complete: function () {
|
complete: function () {
|
||||||
self.finishAsComplete()
|
self.finishAsComplete()
|
||||||
@@ -312,8 +325,8 @@ Page({
|
|||||||
onStop: function () {
|
onStop: function () {
|
||||||
var self = this
|
var self = this
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '结束使用',
|
title: i18n.t('treating.stopTitle'),
|
||||||
content: '确定要提前结束本次使用吗?',
|
content: i18n.t('treating.stopContent'),
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
ble.stopTreatment().then(function (res) {
|
ble.stopTreatment().then(function (res) {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-blue" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-blue" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back nav-back-light" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back nav-back-light" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">使用中</view>
|
<view class="page-header-title">{{i18n.treating.title}}</view>
|
||||||
<view class="page-header-subtitle">正在使用...</view>
|
<view class="page-header-subtitle">{{i18n.treating.subtitle}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<view class="countdown-area">
|
<view class="countdown-area">
|
||||||
<text class="countdown-time">{{remainingText}}</text>
|
<text class="countdown-time">{{remainingText}}</text>
|
||||||
<view class="countdown-label">剩余时间</view>
|
<view class="countdown-label">{{i18n.treating.remainingLabel}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="progress-bar">
|
<view class="progress-bar">
|
||||||
@@ -16,17 +16,17 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="mode-tag-area">
|
<view class="mode-tag-area">
|
||||||
<text class="mode-tag">{{mode === 1 ? '✨ 智能模式' : '🔄 普通模式'}} · {{regionText}}</text>
|
<text class="mode-tag">{{mode === 1 ? i18n.treating.smartMode : i18n.treating.normalMode}} · {{regionText}}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="relax-card">
|
<view class="relax-card">
|
||||||
<text class="relax-icon">💆</text>
|
<text class="relax-icon">💆</text>
|
||||||
<view class="relax-text">
|
<view class="relax-text">
|
||||||
<text>请放松心情</text>
|
<text>{{i18n.treating.relax1}}</text>
|
||||||
<text>享受美好时光</text>
|
<text>{{i18n.treating.relax2}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<button class="btn-primary btn-primary-red" bindtap="onStop">停止使用</button>
|
<button class="btn-primary btn-primary-red" bindtap="onStop">{{i18n.treating.stopBtn}}</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
var api = require('../../utils/api')
|
var api = require('../../utils/api')
|
||||||
var ble = require('../../services/ble')
|
var ble = require('../../services/ble')
|
||||||
|
var i18n = require('../../i18n/index')
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
@@ -15,6 +16,10 @@ Page({
|
|||||||
synced: false
|
synced: false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
onBack: function () {
|
onBack: function () {
|
||||||
wx.reLaunch({ url: '/pages/index/index' })
|
wx.reLaunch({ url: '/pages/index/index' })
|
||||||
},
|
},
|
||||||
@@ -33,7 +38,7 @@ Page({
|
|||||||
regions: parseInt(options.regions) || 0,
|
regions: parseInt(options.regions) || 0,
|
||||||
duration: durationMs,
|
duration: durationMs,
|
||||||
avgPd: options.avg_pd || 0,
|
avgPd: options.avg_pd || 0,
|
||||||
durationText: mins > 0 ? mins + '分钟' : secs + '秒',
|
durationText: mins > 0 ? i18n.t('treatmentDone.durationMin', { min: mins }) : i18n.t('treatmentDone.durationSec', { sec: secs }),
|
||||||
mode: parseInt(options.mode) || 0,
|
mode: parseInt(options.mode) || 0,
|
||||||
regionNames: ble.getRegionName(parseInt(options.regions) || 0)
|
regionNames: ble.getRegionName(parseInt(options.regions) || 0)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-green" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-green" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">使用完成</view>
|
<view class="page-header-title">{{i18n.treatmentDone.title}}</view>
|
||||||
<view class="page-header-subtitle">本次使用已结束</view>
|
<view class="page-header-subtitle">{{i18n.treatmentDone.headerSub}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<view class="result-icon">✓</view>
|
<view class="result-icon">✓</view>
|
||||||
<view class="result-title">使用完成!</view>
|
<view class="result-title">{{i18n.treatmentDone.resultTitle}}</view>
|
||||||
|
|
||||||
<view class="result-data">
|
<view class="result-data">
|
||||||
<view class="result-item">
|
<view class="result-item">
|
||||||
<view class="result-value">{{durationText}}</view>
|
<view class="result-value">{{durationText}}</view>
|
||||||
<view class="result-label">使用时长</view>
|
<view class="result-label">{{i18n.treatmentDone.durationLabel}}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="result-item">
|
<view class="result-item">
|
||||||
<view class="result-value">{{regionNames.length}}</view>
|
<view class="result-value">{{regionNames.length}}</view>
|
||||||
<view class="result-label">使用区域</view>
|
<view class="result-label">{{i18n.treatmentDone.regionLabel}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="result-tip">
|
<view class="result-tip">
|
||||||
<text class="result-tip-icon">✨</text>
|
<text class="result-tip-icon">✨</text>
|
||||||
<text class="result-tip-text">本次使用已记录</text>
|
<text class="result-tip-text">{{i18n.treatmentDone.recorded}}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<button class="btn-primary" bindtap="onBackHome">返回首页</button>
|
<button class="btn-primary" bindtap="onBackHome">{{i18n.treatmentDone.backHome}}</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ Page({
|
|||||||
data: {
|
data: {
|
||||||
statusBarHeight: 44,
|
statusBarHeight: 44,
|
||||||
regions: [
|
regions: [
|
||||||
{ name: '右区', mask: 0x01, checked: true },
|
{ name: '', mask: 0x01, checked: true },
|
||||||
{ name: '左区', mask: 0x02, checked: true },
|
{ name: '', mask: 0x02, checked: true },
|
||||||
{ name: '上区', mask: 0x04, checked: true },
|
{ name: '', mask: 0x04, checked: true },
|
||||||
{ name: '中区', mask: 0x08, checked: true },
|
{ name: '', mask: 0x08, checked: true },
|
||||||
{ name: '下区', mask: 0x10, checked: true }
|
{ name: '', mask: 0x10, checked: true }
|
||||||
],
|
],
|
||||||
selectedMode: 0,
|
selectedMode: 0,
|
||||||
subExpired: false,
|
subExpired: false,
|
||||||
@@ -34,6 +34,15 @@ Page({
|
|||||||
var app = getApp()
|
var app = getApp()
|
||||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||||
i18n.bind(this)
|
i18n.bind(this)
|
||||||
|
var regionKeys = ['right', 'left', 'top', 'middle', 'bottom']
|
||||||
|
var regions = this.data.regions
|
||||||
|
regions.forEach(function (r, i) {
|
||||||
|
r.name = i18n.t('common.regions.' + regionKeys[i])
|
||||||
|
})
|
||||||
|
this.setData({
|
||||||
|
regions: regions,
|
||||||
|
subDaysText: i18n.t('setup.remainDays', { days: this.data.subDays })
|
||||||
|
})
|
||||||
this.checkSubscription()
|
this.checkSubscription()
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -43,7 +52,8 @@ Page({
|
|||||||
http.get('/api/v1/subscription').then(function (sub) {
|
http.get('/api/v1/subscription').then(function (sub) {
|
||||||
self.setData({
|
self.setData({
|
||||||
subExpired: sub.status !== 'active',
|
subExpired: sub.status !== 'active',
|
||||||
subDays: sub.remaining_days || 0
|
subDays: sub.remaining_days || 0,
|
||||||
|
subDaysText: i18n.t('setup.remainDays', { days: sub.remaining_days || 0 })
|
||||||
})
|
})
|
||||||
}).catch(function () {})
|
}).catch(function () {})
|
||||||
},
|
},
|
||||||
@@ -79,7 +89,7 @@ Page({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (mask === 0) {
|
if (mask === 0) {
|
||||||
wx.showToast({ title: '请至少选择一个区域', icon: 'none' })
|
wx.showToast({ title: i18n.t('setup.noRegion'), icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +119,7 @@ Page({
|
|||||||
'&mode=' + self.data.selectedMode
|
'&mode=' + self.data.selectedMode
|
||||||
})
|
})
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
self.setData({ submitting: false, error: err.error_msg || '启动失败' })
|
self.setData({ submitting: false, error: err.error_msg || i18n.t('setup.startFailed') })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +1,40 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">使用设置</view>
|
<view class="page-header-title">{{i18n.setup.title}}</view>
|
||||||
<view class="page-header-subtitle">选择模式和区域</view>
|
<view class="page-header-subtitle">{{i18n.setup.subtitle}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
<view class="page-title">选择模式</view>
|
<view class="page-title">{{i18n.setup.selectMode}}</view>
|
||||||
|
|
||||||
<view class="mode-selector">
|
<view class="mode-selector">
|
||||||
<view class="mode-option {{selectedMode === 0 ? 'active' : ''}}" bindtap="onSelectMode" data-value="0">
|
<view class="mode-option {{selectedMode === 0 ? 'active' : ''}}" bindtap="onSelectMode" data-value="0">
|
||||||
<text class="mode-icon">🔄</text>
|
<text class="mode-icon">🔄</text>
|
||||||
<view class="mode-label">普通模式</view>
|
<view class="mode-label">{{i18n.setup.normalMode}}</view>
|
||||||
<view class="mode-sub">6色循环</view>
|
<view class="mode-sub">{{i18n.setup.normalModeSub}}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="mode-option {{selectedMode === 1 ? 'active' : ''}} {{subExpired ? 'locked' : ''}}" bindtap="onSelectMode" data-value="1">
|
<view class="mode-option {{selectedMode === 1 ? 'active' : ''}} {{subExpired ? 'locked' : ''}}" bindtap="onSelectMode" data-value="1">
|
||||||
<text class="mode-icon">✨</text>
|
<text class="mode-icon">✨</text>
|
||||||
<view class="mode-label">智能模式</view>
|
<view class="mode-label">{{i18n.setup.smartMode}}</view>
|
||||||
<view class="mode-sub" wx:if="{{!subExpired}}">剩余{{subDays}}天</view>
|
<view class="mode-sub" wx:if="{{!subExpired}}">{{subDaysText}}</view>
|
||||||
<view class="mode-sub" wx:else>订阅解锁</view>
|
<view class="mode-sub" wx:else>{{i18n.setup.subscribeUnlock}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-title" style="font-size:28rpx;">选择区域</view>
|
<view class="page-title" style="font-size:28rpx;">{{i18n.setup.selectRegion}}</view>
|
||||||
<view class="mode-tip" wx:if="{{selectedMode === 0}}">普通模式固定 10 分钟,使用区域默认全区域,不可调整</view>
|
<view class="mode-tip" wx:if="{{selectedMode === 0}}">{{i18n.setup.normalTip}}</view>
|
||||||
|
|
||||||
<view class="face-map">
|
<view class="face-map">
|
||||||
<view class="face-region forehead {{regions[2].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="2">上区</view>
|
<view class="face-region forehead {{regions[2].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="2">{{i18n.common.regions.top}}</view>
|
||||||
<view class="face-region left-cheek {{regions[1].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="1">左区</view>
|
<view class="face-region left-cheek {{regions[1].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="1">{{i18n.common.regions.left}}</view>
|
||||||
<view class="face-region right-cheek {{regions[0].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="0">右区</view>
|
<view class="face-region right-cheek {{regions[0].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="0">{{i18n.common.regions.right}}</view>
|
||||||
<view class="face-region nose {{regions[3].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="3">中区</view>
|
<view class="face-region nose {{regions[3].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="3">{{i18n.common.regions.middle}}</view>
|
||||||
<view class="face-region chin {{regions[4].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="4">下区</view>
|
<view class="face-region chin {{regions[4].checked ? 'active' : ''}} {{selectedMode === 0 ? 'disabled' : ''}}" bindtap="onToggleRegion" data-idx="4">{{i18n.common.regions.bottom}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<button class="btn-primary" bindtap="onStart" disabled="{{submitting}}" loading="{{submitting}}">
|
<button class="btn-primary" bindtap="onStart" disabled="{{submitting}}" loading="{{submitting}}">
|
||||||
开始使用
|
{{i18n.setup.startBtn}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<view class="manual-entry" bindtap="onManual" style="text-align:center;color:#E8503A;font-size:26rpx;margin-top:28rpx;">{{i18n.manual.entryLink}}</view>
|
<view class="manual-entry" bindtap="onManual" style="text-align:center;color:#E8503A;font-size:26rpx;margin-top:28rpx;">{{i18n.manual.entryLink}}</view>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
var ble = require('../../services/ble')
|
var ble = require('../../services/ble')
|
||||||
|
var i18n = require('../../i18n/index')
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
@@ -8,6 +9,10 @@ Page({
|
|||||||
reconnecting: false
|
reconnecting: false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
onBack: function () {
|
onBack: function () {
|
||||||
wx.navigateBack({
|
wx.navigateBack({
|
||||||
fail: function () {
|
fail: function () {
|
||||||
@@ -28,7 +33,7 @@ Page({
|
|||||||
|
|
||||||
onNext: function () {
|
onNext: function () {
|
||||||
if (!ble.isConnected()) {
|
if (!ble.isConnected()) {
|
||||||
wx.showToast({ title: '设备未连接', icon: 'none' })
|
wx.showToast({ title: i18n.t('common.deviceNotConnected'), icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wx.redirectTo({
|
wx.redirectTo({
|
||||||
@@ -51,18 +56,18 @@ Page({
|
|||||||
onConnected: function () {
|
onConnected: function () {
|
||||||
clearTimeout(self._reconnectTimer)
|
clearTimeout(self._reconnectTimer)
|
||||||
self.setData({ connected: true, reconnecting: false })
|
self.setData({ connected: true, reconnecting: false })
|
||||||
wx.showToast({ title: '连接成功', icon: 'success' })
|
wx.showToast({ title: i18n.t('wearCheck.connectSuccess'), icon: 'success' })
|
||||||
},
|
},
|
||||||
onError: function (err) {
|
onError: function (err) {
|
||||||
clearTimeout(self._reconnectTimer)
|
clearTimeout(self._reconnectTimer)
|
||||||
self.setData({ reconnecting: false })
|
self.setData({ reconnecting: false })
|
||||||
wx.showToast({ title: err.msg || '连接失败', icon: 'none' })
|
wx.showToast({ title: err.msg || i18n.t('wearCheck.connectFailed'), icon: 'none' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
self._reconnectTimer = setTimeout(function () {
|
self._reconnectTimer = setTimeout(function () {
|
||||||
ble.stopScan()
|
ble.stopScan()
|
||||||
self.setData({ reconnecting: false })
|
self.setData({ reconnecting: false })
|
||||||
wx.showToast({ title: '搜索超时', icon: 'none' })
|
wx.showToast({ title: i18n.t('wearCheck.searchTimeout'), icon: 'none' })
|
||||||
}, 5000)
|
}, 5000)
|
||||||
}, 500)
|
}, 500)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
<view class="page-header page-header-pink" style="padding-top: {{statusBarHeight + 24}}px;">
|
||||||
<view class="nav-back" bindtap="onBack">‹ 返回</view>
|
<view class="nav-back" bindtap="onBack">‹ {{i18n.common.back}}</view>
|
||||||
<view class="page-header-title">确认佩戴</view>
|
<view class="page-header-title">{{i18n.wearCheck.title}}</view>
|
||||||
<view class="page-header-subtitle">请确保设备正确佩戴</view>
|
<view class="page-header-subtitle">{{i18n.wearCheck.subtitle}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content">
|
<view class="page-content">
|
||||||
@@ -11,24 +11,24 @@
|
|||||||
<view class="device-row-info">
|
<view class="device-row-info">
|
||||||
<view class="device-row-name">
|
<view class="device-row-name">
|
||||||
LumiFlow
|
LumiFlow
|
||||||
<text class="status-badge status-badge-green" wx:if="{{connected}}">已连接</text>
|
<text class="status-badge status-badge-green" wx:if="{{connected}}">{{i18n.wearCheck.connected}}</text>
|
||||||
<text class="status-badge" style="background:#ccc;" wx:else>未连接</text>
|
<text class="status-badge" style="background:#ccc;" wx:else>{{i18n.wearCheck.disconnected}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="device-row-battery">电量 {{battery}}%</view>
|
<view class="device-row-battery">{{i18n.wearCheck.battery}} {{battery}}%</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="guide-card">
|
<view class="guide-card">
|
||||||
<view class="guide-title">佩戴指引</view>
|
<view class="guide-title">{{i18n.wearCheck.guideTitle}}</view>
|
||||||
<view class="guide-img">设备佩戴示意图</view>
|
<view class="guide-img">{{i18n.wearCheck.guideImg}}</view>
|
||||||
<view class="guide-steps">
|
<view class="guide-steps">
|
||||||
<view class="guide-step">1. 取出设备,展开弹性绑带</view>
|
<view class="guide-step">{{i18n.wearCheck.step1}}</view>
|
||||||
<view class="guide-step">2. 将设备正确佩戴</view>
|
<view class="guide-step">{{i18n.wearCheck.step2}}</view>
|
||||||
<view class="guide-step">3. 调节绑带至舒适位置</view>
|
<view class="guide-step">{{i18n.wearCheck.step3}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<button class="btn-primary" wx:if="{{connected}}" bindtap="onNext">确认已佩戴,开始检测</button>
|
<button class="btn-primary" wx:if="{{connected}}" bindtap="onNext">{{i18n.wearCheck.confirmBtn}}</button>
|
||||||
<button class="btn-secondary" wx:if="{{!connected}}" bindtap="onRetry" disabled="{{reconnecting}}" loading="{{reconnecting}}">{{reconnecting ? '正在连接...' : '重新连接'}}</button>
|
<button class="btn-secondary" wx:if="{{!connected}}" bindtap="onRetry" disabled="{{reconnecting}}" loading="{{reconnecting}}">{{reconnecting ? i18n.wearCheck.connecting : i18n.wearCheck.reconnect}}</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户