feat(i18n): 中英文框架(命名空间字典+语言切换)+profile 接入参考+注册3个新页面
这个提交包含在:
@@ -1,4 +1,5 @@
|
|||||||
var http = require('./utils/request')
|
var http = require('./utils/request')
|
||||||
|
var i18n = require('./i18n/index')
|
||||||
|
|
||||||
App({
|
App({
|
||||||
globalData: {
|
globalData: {
|
||||||
@@ -12,6 +13,8 @@ App({
|
|||||||
onLaunch: function () {
|
onLaunch: function () {
|
||||||
var sysInfo = wx.getSystemInfoSync()
|
var sysInfo = wx.getSystemInfoSync()
|
||||||
this.globalData.statusBarHeight = sysInfo.statusBarHeight || 44
|
this.globalData.statusBarHeight = sysInfo.statusBarHeight || 44
|
||||||
|
i18n.getLocale()
|
||||||
|
i18n.applyTabBar()
|
||||||
this.checkLogin()
|
this.checkLogin()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -17,7 +17,10 @@
|
|||||||
"pages/profile/profile",
|
"pages/profile/profile",
|
||||||
"pages/history/history",
|
"pages/history/history",
|
||||||
"pages/help/help",
|
"pages/help/help",
|
||||||
"pages/contact/contact"
|
"pages/contact/contact",
|
||||||
|
"pages/light-info/light-info",
|
||||||
|
"pages/manual-treatment/manual-treatment",
|
||||||
|
"pages/scan-report/scan-report"
|
||||||
],
|
],
|
||||||
"window": {
|
"window": {
|
||||||
"navigationBarBackgroundColor": "#ffffff",
|
"navigationBarBackgroundColor": "#ffffff",
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
// Lightweight i18n for WeChat Mini Program (no official i18n API).
|
||||||
|
//
|
||||||
|
// Usage in a page:
|
||||||
|
// var i18n = require('../../i18n/index')
|
||||||
|
// onShow: function () { i18n.bind(this) } // injects `i18n` + `locale` into page data
|
||||||
|
// WXML: {{i18n.common.loading}} / {{i18n.profile.title}}
|
||||||
|
// JS strings: i18n.t('common.loading')
|
||||||
|
//
|
||||||
|
// Locale is persisted in storage; switching locale notifies listeners and the
|
||||||
|
// convention is that every page re-runs i18n.bind(this) in onShow, so tab pages
|
||||||
|
// pick up the new language the next time they are shown.
|
||||||
|
|
||||||
|
var locales = require('./locales/index')
|
||||||
|
|
||||||
|
var STORAGE_KEY = 'app_locale'
|
||||||
|
var DEFAULT_LOCALE = 'zh'
|
||||||
|
var SUPPORTED = ['zh', 'en']
|
||||||
|
var LOCALE_NAMES = { zh: '中文', en: 'English' }
|
||||||
|
|
||||||
|
var _listeners = []
|
||||||
|
var _current = null
|
||||||
|
|
||||||
|
function getSystemLocale() {
|
||||||
|
try {
|
||||||
|
var sys = wx.getSystemInfoSync()
|
||||||
|
var lang = (sys.language || '').toLowerCase()
|
||||||
|
return lang.indexOf('en') === 0 ? 'en' : 'zh'
|
||||||
|
} catch (e) {
|
||||||
|
return DEFAULT_LOCALE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLocale() {
|
||||||
|
if (_current) return _current
|
||||||
|
var saved = ''
|
||||||
|
try { saved = wx.getStorageSync(STORAGE_KEY) } catch (e) {}
|
||||||
|
_current = SUPPORTED.indexOf(saved) !== -1 ? saved : getSystemLocale()
|
||||||
|
return _current
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLocale(locale) {
|
||||||
|
if (SUPPORTED.indexOf(locale) === -1 || locale === _current) return
|
||||||
|
_current = locale
|
||||||
|
try { wx.setStorageSync(STORAGE_KEY, locale) } catch (e) {}
|
||||||
|
applyTabBar()
|
||||||
|
for (var i = 0; i < _listeners.length; i++) {
|
||||||
|
try { _listeners[i](locale) } catch (e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChange(fn) { if (typeof fn === 'function') _listeners.push(fn) }
|
||||||
|
function offChange(fn) {
|
||||||
|
_listeners = _listeners.filter(function (f) { return f !== fn })
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDict() {
|
||||||
|
var loc = getLocale()
|
||||||
|
return locales[loc] || locales[DEFAULT_LOCALE]
|
||||||
|
}
|
||||||
|
|
||||||
|
function _lookup(dict, key) {
|
||||||
|
if (!dict) return undefined
|
||||||
|
var parts = key.split('.')
|
||||||
|
var cur = dict
|
||||||
|
for (var i = 0; i < parts.length; i++) {
|
||||||
|
if (cur == null) return undefined
|
||||||
|
cur = cur[parts[i]]
|
||||||
|
}
|
||||||
|
return cur
|
||||||
|
}
|
||||||
|
|
||||||
|
// t('profile.title', {name: 'x'}) — falls back to zh, then to the raw key.
|
||||||
|
function t(key, params) {
|
||||||
|
var loc = getLocale()
|
||||||
|
var val = _lookup(locales[loc], key)
|
||||||
|
if (val === undefined) val = _lookup(locales[DEFAULT_LOCALE], key)
|
||||||
|
if (typeof val !== 'string') return val === undefined ? key : val
|
||||||
|
if (params) {
|
||||||
|
val = val.replace(/\{(\w+)\}/g, function (m, k) {
|
||||||
|
return params[k] !== undefined ? params[k] : m
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject the full dictionary + current locale into a page/component's data.
|
||||||
|
function bind(page) {
|
||||||
|
if (page && typeof page.setData === 'function') {
|
||||||
|
page.setData({ i18n: getDict(), locale: getLocale() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh the bottom tabBar labels to the current locale.
|
||||||
|
function applyTabBar() {
|
||||||
|
var d = getDict()
|
||||||
|
var tab = (d && d.common && d.common.tab) || {}
|
||||||
|
var items = [
|
||||||
|
{ index: 0, text: tab.home },
|
||||||
|
{ index: 1, text: tab.records },
|
||||||
|
{ index: 2, text: tab.mine }
|
||||||
|
]
|
||||||
|
items.forEach(function (it) {
|
||||||
|
if (!it.text) return
|
||||||
|
wx.setTabBarItem({ index: it.index, text: it.text, fail: function () {} })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
SUPPORTED: SUPPORTED,
|
||||||
|
LOCALE_NAMES: LOCALE_NAMES,
|
||||||
|
getLocale: getLocale,
|
||||||
|
setLocale: setLocale,
|
||||||
|
getSystemLocale: getSystemLocale,
|
||||||
|
onChange: onChange,
|
||||||
|
offChange: offChange,
|
||||||
|
getDict: getDict,
|
||||||
|
bind: bind,
|
||||||
|
applyTabBar: applyTabBar,
|
||||||
|
t: t
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
// Shared strings used across many pages.
|
||||||
|
module.exports = {
|
||||||
|
zh: {
|
||||||
|
tab: { home: '首页', records: '记录', mine: '我的' },
|
||||||
|
loading: '加载中...',
|
||||||
|
confirm: '确定',
|
||||||
|
cancel: '取消',
|
||||||
|
save: '保存',
|
||||||
|
saving: '保存中...',
|
||||||
|
back: '返回',
|
||||||
|
retry: '重试',
|
||||||
|
close: '关闭',
|
||||||
|
done: '完成',
|
||||||
|
start: '开始',
|
||||||
|
stop: '停止',
|
||||||
|
unknown: '未知',
|
||||||
|
saveSuccess: '保存成功',
|
||||||
|
saveFailed: '保存失败',
|
||||||
|
networkError: '网络异常,请重试',
|
||||||
|
deviceNotConnected: '设备未连接',
|
||||||
|
regions: {
|
||||||
|
right: '右区',
|
||||||
|
left: '左区',
|
||||||
|
top: '上区',
|
||||||
|
middle: '中区',
|
||||||
|
bottom: '下区'
|
||||||
|
},
|
||||||
|
wavelengths: {
|
||||||
|
red: '红光',
|
||||||
|
infrared: '红外光',
|
||||||
|
uv: '紫外光',
|
||||||
|
yellow: '黄光'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
tab: { home: 'Home', records: 'Records', mine: 'Me' },
|
||||||
|
loading: 'Loading...',
|
||||||
|
confirm: 'OK',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
save: 'Save',
|
||||||
|
saving: 'Saving...',
|
||||||
|
back: 'Back',
|
||||||
|
retry: 'Retry',
|
||||||
|
close: 'Close',
|
||||||
|
done: 'Done',
|
||||||
|
start: 'Start',
|
||||||
|
stop: 'Stop',
|
||||||
|
unknown: 'Unknown',
|
||||||
|
saveSuccess: 'Saved',
|
||||||
|
saveFailed: 'Save failed',
|
||||||
|
networkError: 'Network error, please retry',
|
||||||
|
deviceNotConnected: 'Device not connected',
|
||||||
|
regions: {
|
||||||
|
right: 'Right',
|
||||||
|
left: 'Left',
|
||||||
|
top: 'Top',
|
||||||
|
middle: 'Center',
|
||||||
|
bottom: 'Bottom'
|
||||||
|
},
|
||||||
|
wavelengths: {
|
||||||
|
red: 'Red',
|
||||||
|
infrared: 'Infrared',
|
||||||
|
uv: 'UV',
|
||||||
|
yellow: 'Yellow'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// Merges per-namespace dictionaries into { zh: {...}, en: {...} }.
|
||||||
|
// Each namespace file exports { zh: {...}, en: {...} } and owns its own strings,
|
||||||
|
// so parallel work on different features never touches the same file.
|
||||||
|
|
||||||
|
var common = require('./common')
|
||||||
|
var profile = require('./profile')
|
||||||
|
var lightInfo = require('./lightInfo')
|
||||||
|
var manual = require('./manual')
|
||||||
|
var report = require('./report')
|
||||||
|
|
||||||
|
var namespaces = {
|
||||||
|
common: common,
|
||||||
|
profile: profile,
|
||||||
|
lightInfo: lightInfo,
|
||||||
|
manual: manual,
|
||||||
|
report: report
|
||||||
|
}
|
||||||
|
|
||||||
|
var zh = {}
|
||||||
|
var en = {}
|
||||||
|
for (var ns in namespaces) {
|
||||||
|
if (!namespaces.hasOwnProperty(ns)) continue
|
||||||
|
zh[ns] = namespaces[ns].zh || {}
|
||||||
|
en[ns] = namespaces[ns].en || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { zh: zh, en: en }
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Strings for the light-therapy efficacy intro page (pages/light-info).
|
||||||
|
// Owned by the light-info feature. Keep zh/en keys in sync.
|
||||||
|
module.exports = {
|
||||||
|
zh: {},
|
||||||
|
en: {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Strings for the manual treatment (direct wavelength/function select) page
|
||||||
|
// (pages/manual-treatment). Owned by the manual-mode feature. Keep zh/en in sync.
|
||||||
|
module.exports = {
|
||||||
|
zh: {},
|
||||||
|
en: {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// "我的" page strings + language switcher.
|
||||||
|
module.exports = {
|
||||||
|
zh: {
|
||||||
|
notLoggedIn: '未登录',
|
||||||
|
editProfile: '编辑资料',
|
||||||
|
editProfileTitle: '编辑资料',
|
||||||
|
changeAvatar: '点击更换头像',
|
||||||
|
nickname: '昵称',
|
||||||
|
nicknamePlaceholder: '请输入昵称',
|
||||||
|
nicknameRequired: '昵称不能为空',
|
||||||
|
smartMode: '智能模式',
|
||||||
|
smartModeOpen: '已开放',
|
||||||
|
free: '免费',
|
||||||
|
inUse: '使用中',
|
||||||
|
remainDays: '剩余 {days}天',
|
||||||
|
notSubscribed: '未订阅',
|
||||||
|
goSubscribe: '去订阅 ›',
|
||||||
|
menuDevice: '我的设备',
|
||||||
|
bound: '已绑定',
|
||||||
|
unbound: '未绑定',
|
||||||
|
menuHistory: '使用记录',
|
||||||
|
menuSubscription: '订阅管理',
|
||||||
|
menuHelp: '使用帮助',
|
||||||
|
menuContact: '联系我们',
|
||||||
|
menuLightInfo: '光疗功效介绍',
|
||||||
|
language: '语言 / Language',
|
||||||
|
logout: '退出登录',
|
||||||
|
logoutConfirmTitle: '退出登录',
|
||||||
|
logoutConfirmText: '确定要退出登录吗?',
|
||||||
|
unbindConfirmTitle: '确认解绑',
|
||||||
|
unbindConfirmText: '解绑后将无法使用该设备,确定要解绑吗?',
|
||||||
|
unbindAction: '解绑当前设备',
|
||||||
|
unbindDone: '已解绑',
|
||||||
|
unbindFailed: '解绑失败'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
notLoggedIn: 'Not logged in',
|
||||||
|
editProfile: 'Edit',
|
||||||
|
editProfileTitle: 'Edit Profile',
|
||||||
|
changeAvatar: 'Tap to change avatar',
|
||||||
|
nickname: 'Nickname',
|
||||||
|
nicknamePlaceholder: 'Enter nickname',
|
||||||
|
nicknameRequired: 'Nickname is required',
|
||||||
|
smartMode: 'Smart Mode',
|
||||||
|
smartModeOpen: 'Available',
|
||||||
|
free: 'Free',
|
||||||
|
inUse: 'Active',
|
||||||
|
remainDays: '{days} days left',
|
||||||
|
notSubscribed: 'Not subscribed',
|
||||||
|
goSubscribe: 'Subscribe ›',
|
||||||
|
menuDevice: 'My Device',
|
||||||
|
bound: 'Bound',
|
||||||
|
unbound: 'Not bound',
|
||||||
|
menuHistory: 'Usage Records',
|
||||||
|
menuSubscription: 'Subscription',
|
||||||
|
menuHelp: 'Help',
|
||||||
|
menuContact: 'Contact Us',
|
||||||
|
menuLightInfo: 'Light Therapy Guide',
|
||||||
|
language: '语言 / Language',
|
||||||
|
logout: 'Log Out',
|
||||||
|
logoutConfirmTitle: 'Log Out',
|
||||||
|
logoutConfirmText: 'Are you sure you want to log out?',
|
||||||
|
unbindConfirmTitle: 'Confirm Unbind',
|
||||||
|
unbindConfirmText: 'You will not be able to use this device after unbinding. Continue?',
|
||||||
|
unbindAction: 'Unbind current device',
|
||||||
|
unbindDone: 'Unbound',
|
||||||
|
unbindFailed: 'Unbind failed'
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// Strings for the scan report page (pages/scan-report).
|
||||||
|
module.exports = {
|
||||||
|
zh: {
|
||||||
|
title: '肌肤扫描报告',
|
||||||
|
subtitle: '基于多光谱反射的肤质倾向参考',
|
||||||
|
scannedAt: '扫描时间',
|
||||||
|
overallTitle: '整体倾向',
|
||||||
|
regionTitle: '分区分析',
|
||||||
|
recommendTitle: '推荐护理',
|
||||||
|
noProblem: '未见明显倾向',
|
||||||
|
startTreatment: '开始推荐护理',
|
||||||
|
manualInstead: '我自己选',
|
||||||
|
disclaimer: '本报告依据光反射信号给出肤质倾向参考,不构成医疗诊断。实际护理请结合自身情况。',
|
||||||
|
conceptNotice: '当前诊断依据尚在标定中,结果仅供参考',
|
||||||
|
level: { low: '轻度', mid: '中度', high: '明显' },
|
||||||
|
problems: {
|
||||||
|
aging: '光老化 / 暗沉',
|
||||||
|
acne: '痘痘 / 出油',
|
||||||
|
pigment: '色素 / 暗黄',
|
||||||
|
deep: '深层循环 / 疲劳'
|
||||||
|
},
|
||||||
|
regionNames: {
|
||||||
|
right: '右脸',
|
||||||
|
left: '左脸',
|
||||||
|
top: '额头',
|
||||||
|
middle: '鼻区',
|
||||||
|
bottom: '下巴'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Skin Scan Report',
|
||||||
|
subtitle: 'Skin-tendency reference from multispectral reflection',
|
||||||
|
scannedAt: 'Scanned at',
|
||||||
|
overallTitle: 'Overall Tendency',
|
||||||
|
regionTitle: 'Zone Analysis',
|
||||||
|
recommendTitle: 'Recommended Care',
|
||||||
|
noProblem: 'No notable tendency',
|
||||||
|
startTreatment: 'Start Recommended Care',
|
||||||
|
manualInstead: 'Choose Myself',
|
||||||
|
disclaimer: 'This report gives a skin-tendency reference from light-reflection signals and is not a medical diagnosis. Please consider your own condition.',
|
||||||
|
conceptNotice: 'Diagnosis thresholds are still being calibrated; results are for reference only',
|
||||||
|
level: { low: 'Mild', mid: 'Moderate', high: 'Notable' },
|
||||||
|
problems: {
|
||||||
|
aging: 'Photoaging / Dullness',
|
||||||
|
acne: 'Acne / Oiliness',
|
||||||
|
pigment: 'Pigment / Sallowness',
|
||||||
|
deep: 'Deep circulation / Fatigue'
|
||||||
|
},
|
||||||
|
regionNames: {
|
||||||
|
right: 'Right cheek',
|
||||||
|
left: 'Left cheek',
|
||||||
|
top: 'Forehead',
|
||||||
|
middle: 'Nose',
|
||||||
|
bottom: 'Chin'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
var api = require('../../utils/api')
|
var api = require('../../utils/api')
|
||||||
var config = require('../../config/env')
|
var config = require('../../config/env')
|
||||||
|
var i18n = require('../../i18n/index')
|
||||||
var app = getApp()
|
var app = getApp()
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
@@ -16,9 +17,30 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onShow: function () {
|
onShow: function () {
|
||||||
|
i18n.bind(this)
|
||||||
this.loadProfile()
|
this.loadProfile()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onLightInfo: function () {
|
||||||
|
wx.navigateTo({ url: '/pages/light-info/light-info' })
|
||||||
|
},
|
||||||
|
|
||||||
|
onSwitchLanguage: function () {
|
||||||
|
var self = this
|
||||||
|
var supported = i18n.SUPPORTED
|
||||||
|
var names = supported.map(function (l) { return i18n.LOCALE_NAMES[l] || l })
|
||||||
|
wx.showActionSheet({
|
||||||
|
itemList: names,
|
||||||
|
success: function (res) {
|
||||||
|
var target = supported[res.tapIndex]
|
||||||
|
if (target && target !== i18n.getLocale()) {
|
||||||
|
i18n.setLocale(target)
|
||||||
|
i18n.bind(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
loadProfile: function () {
|
loadProfile: function () {
|
||||||
var self = this
|
var self = this
|
||||||
self.setData({ loading: true })
|
self.setData({ loading: true })
|
||||||
@@ -33,9 +55,11 @@ Page({
|
|||||||
})
|
})
|
||||||
|
|
||||||
var p2 = api.getSubscription().then(function (sub) {
|
var p2 = 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('profile.remainDays', { days: remaining })
|
||||||
})
|
})
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
console.error('getSubscription failed', err)
|
console.error('getSubscription failed', err)
|
||||||
@@ -50,19 +74,19 @@ Page({
|
|||||||
var self = this
|
var self = this
|
||||||
if (this.data.deviceCount > 0) {
|
if (this.data.deviceCount > 0) {
|
||||||
wx.showActionSheet({
|
wx.showActionSheet({
|
||||||
itemList: ['解绑当前设备'],
|
itemList: [i18n.t('profile.unbindAction')],
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
if (res.tapIndex === 0) {
|
if (res.tapIndex === 0) {
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '确认解绑',
|
title: i18n.t('profile.unbindConfirmTitle'),
|
||||||
content: '解绑后将无法使用该设备,确定要解绑吗?',
|
content: i18n.t('profile.unbindConfirmText'),
|
||||||
success: function (modalRes) {
|
success: function (modalRes) {
|
||||||
if (modalRes.confirm) {
|
if (modalRes.confirm) {
|
||||||
api.unbindDevice().then(function () {
|
api.unbindDevice().then(function () {
|
||||||
wx.showToast({ title: '已解绑', icon: 'success' })
|
wx.showToast({ title: i18n.t('profile.unbindDone'), icon: 'success' })
|
||||||
self.loadProfile()
|
self.loadProfile()
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
wx.showToast({ title: err.message || '解绑失败', icon: 'none' })
|
wx.showToast({ title: err.message || i18n.t('profile.unbindFailed'), icon: 'none' })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,7 +175,7 @@ Page({
|
|||||||
var self = this
|
var self = this
|
||||||
var nickname = (self.data.editNickname || '').trim()
|
var nickname = (self.data.editNickname || '').trim()
|
||||||
if (!nickname) {
|
if (!nickname) {
|
||||||
wx.showToast({ title: '昵称不能为空', icon: 'none' })
|
wx.showToast({ title: i18n.t('profile.nicknameRequired'), icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,18 +194,18 @@ Page({
|
|||||||
})
|
})
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
self.setData({ saving: false, editing: false })
|
self.setData({ saving: false, editing: false })
|
||||||
wx.showToast({ title: '保存成功', icon: 'success' })
|
wx.showToast({ title: i18n.t('common.saveSuccess'), icon: 'success' })
|
||||||
self.loadProfile()
|
self.loadProfile()
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
self.setData({ saving: false })
|
self.setData({ saving: false })
|
||||||
wx.showToast({ title: err.message || '保存失败', icon: 'none' })
|
wx.showToast({ title: err.message || i18n.t('common.saveFailed'), icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
onLogout: function () {
|
onLogout: function () {
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '退出登录',
|
title: i18n.t('profile.logoutConfirmTitle'),
|
||||||
content: '确定要退出登录吗?',
|
content: i18n.t('profile.logoutConfirmText'),
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
wx.removeStorageSync('token')
|
wx.removeStorageSync('token')
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="loading-container" wx:if="{{loading}}">
|
<view class="loading-container" wx:if="{{loading}}">
|
||||||
<view class="loading-spinner"></view>
|
<view class="loading-spinner"></view>
|
||||||
<text class="loading-text">加载中...</text>
|
<text class="loading-text">{{i18n.common.loading}}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="page-content" wx:if="{{!loading}}">
|
<view class="page-content" wx:if="{{!loading}}">
|
||||||
@@ -9,65 +9,76 @@
|
|||||||
<image class="user-avatar-img" wx:if="{{userInfo.avatar}}" src="{{userInfo.avatar}}" mode="aspectFill"></image>
|
<image class="user-avatar-img" wx:if="{{userInfo.avatar}}" src="{{userInfo.avatar}}" mode="aspectFill"></image>
|
||||||
<view class="user-avatar" wx:else>👤</view>
|
<view class="user-avatar" wx:else>👤</view>
|
||||||
<view class="user-info">
|
<view class="user-info">
|
||||||
<view class="user-name">{{userInfo.nickname || '未登录'}}</view>
|
<view class="user-name">{{userInfo.nickname || i18n.profile.notLoggedIn}}</view>
|
||||||
<view class="user-phone">{{userInfo.phone || ''}}</view>
|
<view class="user-phone">{{userInfo.phone || ''}}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="edit-profile-btn" bindtap="onEditProfile">编辑资料</view>
|
<view class="edit-profile-btn" bindtap="onEditProfile">{{i18n.profile.editProfile}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="sub-card" wx:if="{{subscription && subscription.plan === 'free'}}">
|
<view class="sub-card" wx:if="{{subscription && subscription.plan === 'free'}}">
|
||||||
<view class="sub-left">
|
<view class="sub-left">
|
||||||
<view class="sub-name">✨ 智能模式</view>
|
<view class="sub-name">✨ {{i18n.profile.smartMode}}</view>
|
||||||
<view class="sub-remain">已开放</view>
|
<view class="sub-remain">{{i18n.profile.smartModeOpen}}</view>
|
||||||
</view>
|
</view>
|
||||||
<text class="status-badge status-badge-gold">免费</text>
|
<text class="status-badge status-badge-gold">{{i18n.profile.free}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="sub-card" wx:elif="{{subscription && subscription.status === 'active' && subRemaining > 0}}">
|
<view class="sub-card" wx:elif="{{subscription && subscription.status === 'active' && subRemaining > 0}}">
|
||||||
<view class="sub-left">
|
<view class="sub-left">
|
||||||
<view class="sub-name">✨ 智能模式</view>
|
<view class="sub-name">✨ {{i18n.profile.smartMode}}</view>
|
||||||
<view class="sub-remain">剩余 {{subRemaining}}天</view>
|
<view class="sub-remain">{{subRemainingText}}</view>
|
||||||
</view>
|
</view>
|
||||||
<text class="status-badge status-badge-gold">使用中</text>
|
<text class="status-badge status-badge-gold">{{i18n.profile.inUse}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="sub-card sub-card-inactive" wx:else bindtap="onViewSubscription">
|
<view class="sub-card sub-card-inactive" wx:else bindtap="onViewSubscription">
|
||||||
<view class="sub-left">
|
<view class="sub-left">
|
||||||
<view class="sub-name">智能模式</view>
|
<view class="sub-name">{{i18n.profile.smartMode}}</view>
|
||||||
<view class="sub-remain">未订阅</view>
|
<view class="sub-remain">{{i18n.profile.notSubscribed}}</view>
|
||||||
</view>
|
</view>
|
||||||
<text class="status-badge" style="background:#eee;color:#999;">去订阅 ›</text>
|
<text class="status-badge" style="background:#eee;color:#999;">{{i18n.profile.goSubscribe}}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="menu-list">
|
<view class="menu-list">
|
||||||
<view class="menu-item" bindtap="onManageDevice">
|
<view class="menu-item" bindtap="onManageDevice">
|
||||||
<view class="menu-icon">📱</view>
|
<view class="menu-icon">📱</view>
|
||||||
<view class="menu-text">我的设备</view>
|
<view class="menu-text">{{i18n.profile.menuDevice}}</view>
|
||||||
<text class="menu-device-status" wx:if="{{deviceCount > 0}}">已绑定</text>
|
<text class="menu-device-status" wx:if="{{deviceCount > 0}}">{{i18n.profile.bound}}</text>
|
||||||
<text class="menu-device-status menu-device-unbound" wx:else>未绑定</text>
|
<text class="menu-device-status menu-device-unbound" wx:else>{{i18n.profile.unbound}}</text>
|
||||||
<text class="menu-arrow">›</text>
|
<text class="menu-arrow">›</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="menu-item" bindtap="onViewHistory">
|
<view class="menu-item" bindtap="onViewHistory">
|
||||||
<view class="menu-icon">📋</view>
|
<view class="menu-icon">📋</view>
|
||||||
<view class="menu-text">使用记录</view>
|
<view class="menu-text">{{i18n.profile.menuHistory}}</view>
|
||||||
<text class="menu-arrow">›</text>
|
<text class="menu-arrow">›</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="menu-item" bindtap="onViewSubscription" wx:if="{{!subscription || subscription.plan !== 'free'}}">
|
<view class="menu-item" bindtap="onViewSubscription" wx:if="{{!subscription || subscription.plan !== 'free'}}">
|
||||||
<view class="menu-icon">💳</view>
|
<view class="menu-icon">💳</view>
|
||||||
<view class="menu-text">订阅管理</view>
|
<view class="menu-text">{{i18n.profile.menuSubscription}}</view>
|
||||||
|
<text class="menu-arrow">›</text>
|
||||||
|
</view>
|
||||||
|
<view class="menu-item" bindtap="onLightInfo">
|
||||||
|
<view class="menu-icon">💡</view>
|
||||||
|
<view class="menu-text">{{i18n.profile.menuLightInfo}}</view>
|
||||||
<text class="menu-arrow">›</text>
|
<text class="menu-arrow">›</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="menu-item" bindtap="onHelp">
|
<view class="menu-item" bindtap="onHelp">
|
||||||
<view class="menu-icon">❓</view>
|
<view class="menu-icon">❓</view>
|
||||||
<view class="menu-text">使用帮助</view>
|
<view class="menu-text">{{i18n.profile.menuHelp}}</view>
|
||||||
<text class="menu-arrow">›</text>
|
<text class="menu-arrow">›</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="menu-item" bindtap="onContact">
|
<view class="menu-item" bindtap="onContact">
|
||||||
<view class="menu-icon">📞</view>
|
<view class="menu-icon">📞</view>
|
||||||
<view class="menu-text">联系我们</view>
|
<view class="menu-text">{{i18n.profile.menuContact}}</view>
|
||||||
|
<text class="menu-arrow">›</text>
|
||||||
|
</view>
|
||||||
|
<view class="menu-item" bindtap="onSwitchLanguage">
|
||||||
|
<view class="menu-icon">🌐</view>
|
||||||
|
<view class="menu-text">{{i18n.profile.language}}</view>
|
||||||
|
<text class="menu-device-status">{{locale === 'en' ? 'English' : '中文'}}</text>
|
||||||
<text class="menu-arrow">›</text>
|
<text class="menu-arrow">›</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="logout-btn" bindtap="onLogout">退出登录</view>
|
<view class="logout-btn" bindtap="onLogout">{{i18n.profile.logout}}</view>
|
||||||
|
|
||||||
<view class="version-text">v0.1.8</view>
|
<view class="version-text">v0.1.8</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -75,19 +86,19 @@
|
|||||||
<!-- Edit profile modal -->
|
<!-- Edit profile modal -->
|
||||||
<view class="edit-mask" wx:if="{{editing}}" bindtap="onCancelEdit"></view>
|
<view class="edit-mask" wx:if="{{editing}}" bindtap="onCancelEdit"></view>
|
||||||
<view class="edit-modal" wx:if="{{editing}}">
|
<view class="edit-modal" wx:if="{{editing}}">
|
||||||
<view class="edit-modal-title">编辑资料</view>
|
<view class="edit-modal-title">{{i18n.profile.editProfileTitle}}</view>
|
||||||
<view class="edit-avatar-row" bindtap="onPickAvatar">
|
<view class="edit-avatar-row" bindtap="onPickAvatar">
|
||||||
<image class="edit-avatar-img" wx:if="{{editAvatarUrl}}" src="{{editAvatarUrl}}" mode="aspectFill"></image>
|
<image class="edit-avatar-img" wx:if="{{editAvatarUrl}}" src="{{editAvatarUrl}}" mode="aspectFill"></image>
|
||||||
<view class="edit-avatar-placeholder" wx:else>👤</view>
|
<view class="edit-avatar-placeholder" wx:else>👤</view>
|
||||||
<text class="edit-avatar-hint">点击更换头像</text>
|
<text class="edit-avatar-hint">{{i18n.profile.changeAvatar}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="edit-field">
|
<view class="edit-field">
|
||||||
<text class="edit-label">昵称</text>
|
<text class="edit-label">{{i18n.profile.nickname}}</text>
|
||||||
<input class="edit-input" value="{{editNickname}}" bindinput="onEditNicknameInput" placeholder="请输入昵称" maxlength="20" />
|
<input class="edit-input" value="{{editNickname}}" bindinput="onEditNicknameInput" placeholder="{{i18n.profile.nicknamePlaceholder}}" maxlength="20" />
|
||||||
</view>
|
</view>
|
||||||
<view class="edit-actions">
|
<view class="edit-actions">
|
||||||
<button class="btn-secondary edit-btn" bindtap="onCancelEdit">取消</button>
|
<button class="btn-secondary edit-btn" bindtap="onCancelEdit">{{i18n.common.cancel}}</button>
|
||||||
<button class="btn-primary edit-btn" bindtap="onSaveProfile" disabled="{{saving}}" loading="{{saving}}">保存</button>
|
<button class="btn-primary edit-btn" bindtap="onSaveProfile" disabled="{{saving}}" loading="{{saving}}">{{i18n.common.save}}</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户