feat(i18n): 中英文框架(命名空间字典+语言切换)+profile 接入参考+注册3个新页面
这个提交包含在:
@@ -1,4 +1,5 @@
|
||||
var http = require('./utils/request')
|
||||
var i18n = require('./i18n/index')
|
||||
|
||||
App({
|
||||
globalData: {
|
||||
@@ -12,6 +13,8 @@ App({
|
||||
onLaunch: function () {
|
||||
var sysInfo = wx.getSystemInfoSync()
|
||||
this.globalData.statusBarHeight = sysInfo.statusBarHeight || 44
|
||||
i18n.getLocale()
|
||||
i18n.applyTabBar()
|
||||
this.checkLogin()
|
||||
},
|
||||
|
||||
|
||||
+4
-1
@@ -17,7 +17,10 @@
|
||||
"pages/profile/profile",
|
||||
"pages/history/history",
|
||||
"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": {
|
||||
"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 config = require('../../config/env')
|
||||
var i18n = require('../../i18n/index')
|
||||
var app = getApp()
|
||||
|
||||
Page({
|
||||
@@ -16,9 +17,30 @@ Page({
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
i18n.bind(this)
|
||||
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 () {
|
||||
var self = this
|
||||
self.setData({ loading: true })
|
||||
@@ -33,9 +55,11 @@ Page({
|
||||
})
|
||||
|
||||
var p2 = api.getSubscription().then(function (sub) {
|
||||
var remaining = sub.remaining_days || 0
|
||||
self.setData({
|
||||
subscription: sub,
|
||||
subRemaining: sub.remaining_days || 0
|
||||
subRemaining: remaining,
|
||||
subRemainingText: i18n.t('profile.remainDays', { days: remaining })
|
||||
})
|
||||
}).catch(function (err) {
|
||||
console.error('getSubscription failed', err)
|
||||
@@ -50,19 +74,19 @@ Page({
|
||||
var self = this
|
||||
if (this.data.deviceCount > 0) {
|
||||
wx.showActionSheet({
|
||||
itemList: ['解绑当前设备'],
|
||||
itemList: [i18n.t('profile.unbindAction')],
|
||||
success: function (res) {
|
||||
if (res.tapIndex === 0) {
|
||||
wx.showModal({
|
||||
title: '确认解绑',
|
||||
content: '解绑后将无法使用该设备,确定要解绑吗?',
|
||||
title: i18n.t('profile.unbindConfirmTitle'),
|
||||
content: i18n.t('profile.unbindConfirmText'),
|
||||
success: function (modalRes) {
|
||||
if (modalRes.confirm) {
|
||||
api.unbindDevice().then(function () {
|
||||
wx.showToast({ title: '已解绑', icon: 'success' })
|
||||
wx.showToast({ title: i18n.t('profile.unbindDone'), icon: 'success' })
|
||||
self.loadProfile()
|
||||
}).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 nickname = (self.data.editNickname || '').trim()
|
||||
if (!nickname) {
|
||||
wx.showToast({ title: '昵称不能为空', icon: 'none' })
|
||||
wx.showToast({ title: i18n.t('profile.nicknameRequired'), icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
@@ -170,18 +194,18 @@ Page({
|
||||
})
|
||||
}).then(function () {
|
||||
self.setData({ saving: false, editing: false })
|
||||
wx.showToast({ title: '保存成功', icon: 'success' })
|
||||
wx.showToast({ title: i18n.t('common.saveSuccess'), icon: 'success' })
|
||||
self.loadProfile()
|
||||
}).catch(function (err) {
|
||||
self.setData({ saving: false })
|
||||
wx.showToast({ title: err.message || '保存失败', icon: 'none' })
|
||||
wx.showToast({ title: err.message || i18n.t('common.saveFailed'), icon: 'none' })
|
||||
})
|
||||
},
|
||||
|
||||
onLogout: function () {
|
||||
wx.showModal({
|
||||
title: '退出登录',
|
||||
content: '确定要退出登录吗?',
|
||||
title: i18n.t('profile.logoutConfirmTitle'),
|
||||
content: i18n.t('profile.logoutConfirmText'),
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
wx.removeStorageSync('token')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<view class="page">
|
||||
<view class="loading-container" wx:if="{{loading}}">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">加载中...</text>
|
||||
<text class="loading-text">{{i18n.common.loading}}</text>
|
||||
</view>
|
||||
|
||||
<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>
|
||||
<view class="user-avatar" wx:else>👤</view>
|
||||
<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>
|
||||
<view class="edit-profile-btn" bindtap="onEditProfile">编辑资料</view>
|
||||
<view class="edit-profile-btn" bindtap="onEditProfile">{{i18n.profile.editProfile}}</view>
|
||||
</view>
|
||||
|
||||
<view class="sub-card" wx:if="{{subscription && subscription.plan === 'free'}}">
|
||||
<view class="sub-left">
|
||||
<view class="sub-name">✨ 智能模式</view>
|
||||
<view class="sub-remain">已开放</view>
|
||||
<view class="sub-name">✨ {{i18n.profile.smartMode}}</view>
|
||||
<view class="sub-remain">{{i18n.profile.smartModeOpen}}</view>
|
||||
</view>
|
||||
<text class="status-badge status-badge-gold">免费</text>
|
||||
<text class="status-badge status-badge-gold">{{i18n.profile.free}}</text>
|
||||
</view>
|
||||
<view class="sub-card" wx:elif="{{subscription && subscription.status === 'active' && subRemaining > 0}}">
|
||||
<view class="sub-left">
|
||||
<view class="sub-name">✨ 智能模式</view>
|
||||
<view class="sub-remain">剩余 {{subRemaining}}天</view>
|
||||
<view class="sub-name">✨ {{i18n.profile.smartMode}}</view>
|
||||
<view class="sub-remain">{{subRemainingText}}</view>
|
||||
</view>
|
||||
<text class="status-badge status-badge-gold">使用中</text>
|
||||
<text class="status-badge status-badge-gold">{{i18n.profile.inUse}}</text>
|
||||
</view>
|
||||
<view class="sub-card sub-card-inactive" wx:else bindtap="onViewSubscription">
|
||||
<view class="sub-left">
|
||||
<view class="sub-name">智能模式</view>
|
||||
<view class="sub-remain">未订阅</view>
|
||||
<view class="sub-name">{{i18n.profile.smartMode}}</view>
|
||||
<view class="sub-remain">{{i18n.profile.notSubscribed}}</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 class="menu-list">
|
||||
<view class="menu-item" bindtap="onManageDevice">
|
||||
<view class="menu-icon">📱</view>
|
||||
<view class="menu-text">我的设备</view>
|
||||
<text class="menu-device-status" wx:if="{{deviceCount > 0}}">已绑定</text>
|
||||
<text class="menu-device-status menu-device-unbound" wx:else>未绑定</text>
|
||||
<view class="menu-text">{{i18n.profile.menuDevice}}</view>
|
||||
<text class="menu-device-status" wx:if="{{deviceCount > 0}}">{{i18n.profile.bound}}</text>
|
||||
<text class="menu-device-status menu-device-unbound" wx:else>{{i18n.profile.unbound}}</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="onViewHistory">
|
||||
<view class="menu-icon">📋</view>
|
||||
<view class="menu-text">使用记录</view>
|
||||
<view class="menu-text">{{i18n.profile.menuHistory}}</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="onViewSubscription" wx:if="{{!subscription || subscription.plan !== 'free'}}">
|
||||
<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>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="onHelp">
|
||||
<view class="menu-icon">❓</view>
|
||||
<view class="menu-text">使用帮助</view>
|
||||
<view class="menu-text">{{i18n.profile.menuHelp}}</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="onContact">
|
||||
<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>
|
||||
</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>
|
||||
@@ -75,19 +86,19 @@
|
||||
<!-- Edit profile modal -->
|
||||
<view class="edit-mask" wx:if="{{editing}}" bindtap="onCancelEdit"></view>
|
||||
<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">
|
||||
<image class="edit-avatar-img" wx:if="{{editAvatarUrl}}" src="{{editAvatarUrl}}" mode="aspectFill"></image>
|
||||
<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 class="edit-field">
|
||||
<text class="edit-label">昵称</text>
|
||||
<input class="edit-input" value="{{editNickname}}" bindinput="onEditNicknameInput" placeholder="请输入昵称" maxlength="20" />
|
||||
<text class="edit-label">{{i18n.profile.nickname}}</text>
|
||||
<input class="edit-input" value="{{editNickname}}" bindinput="onEditNicknameInput" placeholder="{{i18n.profile.nicknamePlaceholder}}" maxlength="20" />
|
||||
</view>
|
||||
<view class="edit-actions">
|
||||
<button class="btn-secondary edit-btn" bindtap="onCancelEdit">取消</button>
|
||||
<button class="btn-primary edit-btn" bindtap="onSaveProfile" disabled="{{saving}}" loading="{{saving}}">保存</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}}">{{i18n.common.save}}</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
在新工单中引用
屏蔽一个用户