@liujitcn/kratos-uni-app-system 0.0.19 → 0.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { onLoad } from '@dcloudio/uni-app'
3
- import { computed, nextTick, onBeforeUnmount, ref } from 'vue'
3
+ import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
4
4
  import { defAiMessageService, StreamAiMessageByChunkedRequest } from '../../../api/base/ai_message'
5
5
  import { defAiSessionService } from '../../../api/base/ai_session'
6
6
  import { defAiToolService } from '../../../api/base/ai_tool'
@@ -13,7 +13,7 @@ import { formatSrc } from '@liujitcn/kratos-uni-app-core/utils/index'
13
13
  import Composer from './components/Composer.vue'
14
14
  import SessionDrawer from './components/SessionDrawer.vue'
15
15
  import WelcomePanel from './components/WelcomePanel.vue'
16
- import { navigateAppRoute } from '@liujitcn/kratos-uni-app-core'
16
+ import { navigateAppRoute, useI18n } from '@liujitcn/kratos-uni-app-core'
17
17
  import {
18
18
  type AiStreamEvent,
19
19
  type AiStreamPayload,
@@ -56,11 +56,11 @@ type StreamTask = {
56
56
  }
57
57
 
58
58
  const AI_TERMINAL = Terminal.TERMINAL_APP
59
- const THINKING_MESSAGE_CONTENT = '正在回复'
60
59
  const LOCAL_USER_MESSAGE_PREFIX = 'ai-user-local'
61
60
  const PENDING_MESSAGE_ID = 'pending'
62
61
  const MAX_ATTACHMENT_COUNT = 6
63
62
  const STARTER_PROMPT_PAGE_SIZE = 4
63
+ const { locale, t } = useI18n()
64
64
 
65
65
  const windowInfo = uni.getWindowInfo()
66
66
  let safeAreaTop =
@@ -92,44 +92,45 @@ const runningStreamTaskMap = new Map<string, StreamTask>()
92
92
  const pendingDeltaMap = new Map<string, AiStreamPayload>()
93
93
  let pendingDeltaTimer = 0
94
94
 
95
- const starterShortcuts = ref<AiShortcut[]>([
95
+ const createStarterShortcuts = (): AiShortcut[] => [
96
96
  {
97
97
  key: 'summarize',
98
- title: '帮我总结一段内容',
99
- prompt: '请帮我总结以下内容',
98
+ title: t('system.ai.prompt.summary'),
99
+ prompt: t('system.ai.prompt.summaryContent'),
100
100
  action: undefined,
101
101
  required_tools: [],
102
102
  sort: 1,
103
- group: '文本助手',
103
+ group: t('system.ai.textAssistant'),
104
104
  },
105
105
  {
106
106
  key: 'rewrite',
107
- title: '帮我优化一段文字',
108
- prompt: '请帮我优化以下文字',
107
+ title: t('system.ai.prompt.optimize'),
108
+ prompt: t('system.ai.prompt.optimizeContent'),
109
109
  action: undefined,
110
110
  required_tools: [],
111
111
  sort: 2,
112
- group: '文本助手',
112
+ group: t('system.ai.textAssistant'),
113
113
  },
114
114
  {
115
115
  key: 'plan',
116
- title: '帮我制定一个计划',
117
- prompt: '请帮我制定一个清晰的执行计划',
116
+ title: t('system.ai.prompt.plan'),
117
+ prompt: t('system.ai.prompt.planContent'),
118
118
  action: undefined,
119
119
  required_tools: [],
120
120
  sort: 3,
121
- group: '效率助手',
121
+ group: t('system.ai.efficiencyAssistant'),
122
122
  },
123
123
  {
124
124
  key: 'ideas',
125
- title: '给我一些灵感',
126
- prompt: '请围绕这个主题给我一些新想法',
125
+ title: t('system.ai.prompt.idea'),
126
+ prompt: t('system.ai.prompt.ideaContent'),
127
127
  action: undefined,
128
128
  required_tools: [],
129
129
  sort: 4,
130
- group: '效率助手',
130
+ group: t('system.ai.efficiencyAssistant'),
131
131
  },
132
- ])
132
+ ]
133
+ const starterShortcuts = ref<AiShortcut[]>(createStarterShortcuts())
133
134
 
134
135
  const filteredSessions = computed(() => {
135
136
  const keyword = sessionKeyword.value.trim()
@@ -158,27 +159,29 @@ const starterPrompts = computed(() => {
158
159
  const aiGreetingPeriod = computed(() => {
159
160
  const hour = new Date().getHours()
160
161
  if (hour < 11) {
161
- return '上午'
162
+ return t('system.ai.period.morning')
162
163
  }
163
164
  if (hour < 14) {
164
- return '中午'
165
+ return t('system.ai.period.noon')
165
166
  }
166
167
  if (hour < 18) {
167
- return '下午'
168
+ return t('system.ai.period.afternoon')
168
169
  }
169
- return '晚上'
170
+ return t('system.ai.period.evening')
170
171
  })
171
- const aiGreetingMessage = computed(
172
- () => `您好,${aiGreetingPeriod.value}好!今天有什么需要我协助的吗?`,
172
+ const aiGreetingMessage = computed(() =>
173
+ t('system.ai.greeting', { period: aiGreetingPeriod.value }),
173
174
  )
174
175
  const composerPlaceholder = computed(() => {
175
176
  if (isRecording.value) {
176
- return '正在听...'
177
+ return t('system.ai.recording')
177
178
  }
178
179
  if (uploadingAttachment.value) {
179
- return '附件上传中...'
180
+ return t('system.ai.attachmentUploading')
180
181
  }
181
- return hasMessages.value ? '继续输入问题' : '输入你想了解的内容'
182
+ return hasMessages.value
183
+ ? t('system.ai.placeholder.continue')
184
+ : t('system.ai.placeholder.default')
182
185
  })
183
186
  const isSubmitDisabled = computed(
184
187
  () =>
@@ -193,6 +196,11 @@ onLoad(() => {
193
196
  void ensureSessionsLoaded()
194
197
  })
195
198
 
199
+ watch(locale, () => {
200
+ starterShortcuts.value = createStarterShortcuts()
201
+ void loadAiShortcuts()
202
+ })
203
+
196
204
  onBeforeUnmount(() => {
197
205
  cancelAllStreamTasks()
198
206
  clearPendingDelta()
@@ -224,16 +232,18 @@ const createSession = async () => {
224
232
  sessionKeyword.value = ''
225
233
  showSessionDrawer.value = false
226
234
  } catch (error) {
227
- showError(error, '创建会话失败')
235
+ showError(error, t('system.ai.createSessionFailed'))
228
236
  }
229
237
  }
230
238
 
231
239
  const deleteSession = async (sessionID: string) => {
232
240
  const session = sessions.value.find((item) => item.id === sessionID)
233
241
  const result = await uni.showModal({
234
- title: '删除会话',
235
- content: `是否删除「${session?.title || '当前会话'}」?`,
236
- confirmText: '删除',
242
+ title: t('system.ai.deleteSession'),
243
+ content: t('system.ai.deleteSessionConfirm', {
244
+ title: session?.title || t('system.ai.currentSession'),
245
+ }),
246
+ confirmText: t('common.action.delete'),
237
247
  confirmColor: '#cf4444',
238
248
  })
239
249
  if (!result.confirm) {
@@ -249,13 +259,13 @@ const deleteSession = async (sessionID: string) => {
249
259
  await ensureActiveSession()
250
260
  }
251
261
  } catch (error) {
252
- showError(error, '删除会话失败')
262
+ showError(error, t('system.ai.deleteSessionFailed'))
253
263
  }
254
264
  }
255
265
 
256
266
  const handleSessionAction = (session: AiSession) => {
257
267
  uni.showActionSheet({
258
- itemList: ['删除会话'],
268
+ itemList: [t('system.ai.deleteSession')],
259
269
  success: ({ tapIndex }) => {
260
270
  if (tapIndex === 0) {
261
271
  void deleteSession(session.id)
@@ -267,7 +277,7 @@ const handleSessionAction = (session: AiSession) => {
267
277
  const copyMessage = (item: ChatMessageItem) => {
268
278
  uni.setClipboardData({
269
279
  data: item.content,
270
- success: () => uni.showToast({ icon: 'none', title: '消息已复制' }),
280
+ success: () => uni.showToast({ icon: 'none', title: t('system.ai.copySuccess') }),
271
281
  })
272
282
  }
273
283
 
@@ -287,7 +297,7 @@ const deleteMessage = async (item: ChatMessageItem) => {
287
297
  (message) => message.messageID !== item.messageID,
288
298
  )
289
299
  } catch (error) {
290
- showError(error, '删除消息失败')
300
+ showError(error, t('system.ai.deleteMessageFailed'))
291
301
  }
292
302
  }
293
303
 
@@ -306,14 +316,17 @@ const regenerateMessage = async (item: ChatMessageItem) => {
306
316
  upsertSession(normalizeSession(response.session))
307
317
  }
308
318
  } catch (error) {
309
- showError(error, '重新生成失败')
319
+ showError(error, t('system.ai.regenerateFailed'))
310
320
  } finally {
311
321
  setSessionSending(activeSessionID.value, false)
312
322
  }
313
323
  }
314
324
 
315
325
  const handleMessageAction = (item: ChatMessageItem) => {
316
- const itemList = item.role === 'ai' ? ['复制', '删除', '重新生成'] : ['复制', '删除']
326
+ const itemList =
327
+ item.role === 'ai'
328
+ ? [t('system.ai.action.copy'), t('common.action.delete'), t('system.ai.action.regenerate')]
329
+ : [t('system.ai.action.copy'), t('common.action.delete')]
317
330
  uni.showActionSheet({
318
331
  itemList,
319
332
  success: ({ tapIndex }) => {
@@ -341,7 +354,7 @@ const handleSend = async () => {
341
354
  if (isSubmitDisabled.value) {
342
355
  return
343
356
  }
344
- const text = inputText.value.trim() || '请结合附件内容回答我的问题'
357
+ const text = inputText.value.trim() || t('system.ai.attachmentAnswer')
345
358
  inputText.value = ''
346
359
  const attachments = [...selectedAttachments.value]
347
360
  selectedAttachments.value = []
@@ -369,7 +382,7 @@ const handleToggleRecord = () => {
369
382
  isRecording.value = !isRecording.value
370
383
  uni.showToast({
371
384
  icon: 'none',
372
- title: isRecording.value ? '正在识别语音' : '已停止语音输入',
385
+ title: isRecording.value ? t('system.ai.recognizing') : t('system.ai.speechStopped'),
373
386
  })
374
387
  }
375
388
 
@@ -378,7 +391,10 @@ const handleAttachment = () => {
378
391
  return
379
392
  }
380
393
  if (selectedAttachments.value.length >= MAX_ATTACHMENT_COUNT) {
381
- uni.showToast({ icon: 'none', title: `最多上传 ${MAX_ATTACHMENT_COUNT} 个附件` })
394
+ uni.showToast({
395
+ icon: 'none',
396
+ title: t('system.ai.attachmentLimit', { count: MAX_ATTACHMENT_COUNT }),
397
+ })
382
398
  return
383
399
  }
384
400
 
@@ -396,7 +412,9 @@ const handleAttachment = () => {
396
412
  : []
397
413
  const files: AttachmentUpload[] = paths.map((path: string, index: number) => ({
398
414
  path,
399
- name: (tempFiles[index] as { name?: string } | undefined)?.name || `图片${index + 1}`,
415
+ name:
416
+ (tempFiles[index] as { name?: string } | undefined)?.name ||
417
+ t('system.ai.imageName', { index: index + 1 }),
400
418
  size: Number((tempFiles[index] as { size?: number } | undefined)?.size || 0),
401
419
  }))
402
420
  uploadingAttachment.value = true
@@ -414,7 +432,7 @@ const handleAttachment = () => {
414
432
  MAX_ATTACHMENT_COUNT,
415
433
  )
416
434
  } catch (error) {
417
- showError(error, '附件上传失败')
435
+ showError(error, t('system.ai.uploadFailed'))
418
436
  } finally {
419
437
  uploadingAttachment.value = false
420
438
  }
@@ -451,7 +469,7 @@ async function loadAiShortcuts() {
451
469
  starterPromptGroupIndex.value = 0
452
470
  }
453
471
  } catch (error) {
454
- showError(error, '加载快捷助手失败')
472
+ showError(error, t('system.ai.loadShortcutsFailed'))
455
473
  } finally {
456
474
  loadingShortcuts.value = false
457
475
  }
@@ -507,7 +525,7 @@ async function runAiTask(
507
525
  await chunkedTask.promise
508
526
  parser.flush()
509
527
  if (!task.finished && !task.aborted) {
510
- throw new Error('AI 助手流式响应未完整返回')
528
+ throw new Error(t('system.ai.responseIncomplete'))
511
529
  }
512
530
  // #endif
513
531
 
@@ -532,7 +550,7 @@ async function runAiTask(
532
550
  signal: controller.signal,
533
551
  })
534
552
  if (!response.body) {
535
- throw new Error('AI 助手流式响应为空')
553
+ throw new Error(t('system.ai.streamEmpty'))
536
554
  }
537
555
  await readAiEventStream(
538
556
  response.body,
@@ -540,7 +558,7 @@ async function runAiTask(
540
558
  controller.signal,
541
559
  )
542
560
  if (!task.finished && !task.aborted) {
543
- throw new Error('AI 助手流式响应未完整返回')
561
+ throw new Error(t('system.ai.responseIncomplete'))
544
562
  }
545
563
  handledByStream = true
546
564
  }
@@ -550,7 +568,7 @@ async function runAiTask(
550
568
  const response = await defAiMessageService.SendAiMessage(request)
551
569
  const nextMessages = normalizeNonStreamMessages(response)
552
570
  if (!nextMessages.length) {
553
- throw new Error('AI 助手响应为空')
571
+ throw new Error(t('system.ai.responseEmpty'))
554
572
  }
555
573
  const success = hasSuccessfulAiMessages(nextMessages)
556
574
  messages.value[sessionID] = replacePendingMessages(
@@ -570,7 +588,7 @@ async function runAiTask(
570
588
  }
571
589
  messages.value[sessionID] = markThinkingMessageFailed(messages.value[sessionID] ?? [])
572
590
  scrollChatToBottom()
573
- showError(error, 'AI 助手请求失败')
591
+ showError(error, t('system.ai.requestFailed'))
574
592
  } finally {
575
593
  if (task && runningStreamTaskMap.get(sessionID) === task) {
576
594
  runningStreamTaskMap.delete(sessionID)
@@ -721,7 +739,7 @@ function normalizeNonStreamMessages(response: unknown) {
721
739
  }
722
740
  const errorEvent = [...events].reverse().find((item) => item.event === 'error')
723
741
  if (errorEvent) {
724
- throw new Error('AI 助手请求失败')
742
+ throw new Error(t('system.ai.requestFailed'))
725
743
  }
726
744
  return []
727
745
  }
@@ -781,7 +799,7 @@ async function ensureSessionsLoaded() {
781
799
  await loadMessages(sessionID)
782
800
  }
783
801
  } catch (error) {
784
- showError(error, '加载会话失败')
802
+ showError(error, t('system.ai.loadSessionsFailed'))
785
803
  } finally {
786
804
  loadingSessions.value = false
787
805
  }
@@ -805,7 +823,7 @@ async function ensureActiveSession() {
805
823
 
806
824
  async function createRemoteSession() {
807
825
  const response = await defAiSessionService.CreateAiSession({
808
- title: '新会话',
826
+ title: t('system.ai.newSession'),
809
827
  terminal: AI_TERMINAL,
810
828
  })
811
829
  const session = response.session ? normalizeSession(response.session) : undefined
@@ -835,7 +853,7 @@ async function loadMessages(sessionID: string) {
835
853
  if (loadingSessionID.value === sessionID) {
836
854
  messages.value[sessionID] = []
837
855
  }
838
- showError(error, '加载消息失败')
856
+ showError(error, t('system.ai.loadMessagesFailed'))
839
857
  } finally {
840
858
  if (loadingSessionID.value === sessionID) {
841
859
  loadingSessionID.value = ''
@@ -846,7 +864,7 @@ async function loadMessages(sessionID: string) {
846
864
  function normalizeSession(session?: Partial<AiSession> | null): AiSession {
847
865
  return {
848
866
  id: String(session?.id ?? ''),
849
- title: String(session?.title ?? '新会话'),
867
+ title: String(session?.title ?? t('system.ai.newSession')),
850
868
  summary: String(session?.summary ?? ''),
851
869
  updated_at: session?.updated_at,
852
870
  terminal: Number(session?.terminal ?? AI_TERMINAL),
@@ -969,7 +987,7 @@ function createThinkingMessage(options?: { sessionID?: string; messageID?: strin
969
987
  input_content: undefined,
970
988
  output_content: {
971
989
  kind: 'text',
972
- content: THINKING_MESSAGE_CONTENT,
990
+ content: t('system.ai.thinking'),
973
991
  reply_source: '',
974
992
  model: '',
975
993
  fallback: false,
@@ -1030,7 +1048,7 @@ function appendStreamingDelta(current: ChatMessageItem[], payload: AiStreamPaylo
1030
1048
  if (item.streamKey !== streamKey || item.role === 'user') {
1031
1049
  return item
1032
1050
  }
1033
- const baseContent = item.content === THINKING_MESSAGE_CONTENT ? '' : item.content
1051
+ const baseContent = item.content === t('system.ai.thinking') ? '' : item.content
1034
1052
  return {
1035
1053
  ...item,
1036
1054
  content: `${baseContent}${payload.delta}`,
@@ -1047,8 +1065,7 @@ function markThinkingMessageFailed(current: ChatMessageItem[]) {
1047
1065
  return {
1048
1066
  ...item,
1049
1067
  status: AiMessageStatus.FAILED_AMS,
1050
- content:
1051
- item.role === 'ai' ? '这次回复没有成功返回,你可以直接重试刚才的问题。' : item.content,
1068
+ content: item.role === 'ai' ? t('system.ai.failedResponse') : item.content,
1052
1069
  }
1053
1070
  })
1054
1071
  }
@@ -1062,7 +1079,7 @@ function markStreamingError(current: ChatMessageItem[], payload: AiStreamPayload
1062
1079
  return {
1063
1080
  ...item,
1064
1081
  status: AiMessageStatus.FAILED_AMS,
1065
- content: '这次回复没有成功返回,你可以直接重试刚才的问题。',
1082
+ content: t('system.ai.failedResponse'),
1066
1083
  }
1067
1084
  })
1068
1085
  }
@@ -1075,7 +1092,7 @@ function sortMessages(list: ChatMessageItem[]) {
1075
1092
  if (left.role !== right.role) {
1076
1093
  return left.role === 'user' ? -1 : 1
1077
1094
  }
1078
- return left.messageID.localeCompare(right.messageID, 'zh-Hans-CN', { numeric: true })
1095
+ return left.messageID.localeCompare(right.messageID, locale.value, { numeric: true })
1079
1096
  }
1080
1097
  return leftTime - rightTime
1081
1098
  })
@@ -1127,7 +1144,7 @@ function isImageAttachment(attachment: AiAttachment) {
1127
1144
 
1128
1145
  function formatAttachmentMeta(attachment: AiAttachment) {
1129
1146
  if (!attachment.size) {
1130
- return '附件'
1147
+ return t('system.ai.attachment')
1131
1148
  }
1132
1149
  return `${Math.max(1, Math.round(attachment.size / 1024))} KB`
1133
1150
  }
@@ -1151,7 +1168,7 @@ function showError(error: unknown, fallback: string) {
1151
1168
  <button class="nav-back-button" hover-class="none" @tap="navigateBack">
1152
1169
  <uni-icons type="left" size="24" color="#111" />
1153
1170
  </button>
1154
- <view class="ai-navbar__title">AI 助手</view>
1171
+ <view class="ai-navbar__title">{{ t('system.ai.chatTitle') }}</view>
1155
1172
  <button class="nav-menu-button" hover-class="none" @tap="toggleSessionDrawer">
1156
1173
  <uni-icons type="bars" size="24" color="#111" />
1157
1174
  </button>
@@ -1192,7 +1209,7 @@ function showError(error: unknown, fallback: string) {
1192
1209
  @longpress="handleMessageAction(item)"
1193
1210
  >
1194
1211
  <view v-if="item.role === 'ai' && item.model" class="reply-meta">
1195
- <text class="reply-tag">模型回复</text>
1212
+ <text class="reply-tag">{{ t('system.ai.modelReply') }}</text>
1196
1213
  <text class="reply-model">{{ item.model }}</text>
1197
1214
  </view>
1198
1215
  <view class="bubble-content">{{ item.content }}</view>
@@ -1204,7 +1221,9 @@ function showError(error: unknown, fallback: string) {
1204
1221
  @tap="previewAttachment(attachment, item.attachments)"
1205
1222
  >
1206
1223
  <view class="attachment-icon">{{
1207
- isImageAttachment(attachment) ? '图' : '件'
1224
+ isImageAttachment(attachment)
1225
+ ? t('system.ai.imageAttachment')
1226
+ : t('system.ai.attachment')
1208
1227
  }}</view>
1209
1228
  <view class="attachment-info">
1210
1229
  <view class="attachment-name">{{ attachment.name }}</view>
@@ -1212,14 +1231,14 @@ function showError(error: unknown, fallback: string) {
1212
1231
  </view>
1213
1232
  </view>
1214
1233
  </view>
1215
- <view v-if="item.tools.length" class="tool-row"
1216
- >已调用:{{ formatTools(item.tools) }}</view
1217
- >
1234
+ <view v-if="item.tools.length" class="tool-row">{{
1235
+ t('system.ai.toolCalled', { tools: formatTools(item.tools) })
1236
+ }}</view>
1218
1237
  </view>
1219
1238
  </view>
1220
1239
  <view id="chat-bottom" class="chat-bottom"></view>
1221
1240
  </view>
1222
- <view v-if="loadingSessionID" class="loading-session">正在加载消息...</view>
1241
+ <view v-if="loadingSessionID" class="loading-session">{{ t('system.ai.loadMessages') }}</view>
1223
1242
  </scroll-view>
1224
1243
 
1225
1244
  <Composer
@@ -11,8 +11,10 @@ import { uploadFile } from '@liujitcn/kratos-uni-app-core/utils/file'
11
11
  import { navigateToLogin } from '@liujitcn/kratos-uni-app-core/utils/navigation'
12
12
  import defaultAvatar from '@liujitcn/kratos-uni-app-core/static/images/avatar.png'
13
13
  import navigatorBackground from '@liujitcn/kratos-uni-app-core/static/images/navigator_bg.png'
14
+ import { useI18n } from '@liujitcn/kratos-uni-app-core'
14
15
 
15
16
  const userStore = useUserStore()
17
+ const { t } = useI18n()
16
18
 
17
19
  // 获取屏幕边界到安全区域距离
18
20
  const { safeAreaInsets } = uni.getSystemInfoSync()
@@ -75,7 +77,7 @@ const onAvatarChange = async () => {
75
77
  const { path, size } = res.tempFiles[0]
76
78
  if (size > imgMaxSize.value) {
77
79
  await uni.showToast({
78
- title: '请上传小于1M的照片',
80
+ title: t('system.profile.photoLimit'),
79
81
  icon: 'none',
80
82
  duration: 1500,
81
83
  })
@@ -99,7 +101,7 @@ const onAvatarChange = async () => {
99
101
  const { tempFilePath, size } = res.tempFiles[0]
100
102
  if (size > imgMaxSize.value) {
101
103
  await uni.showToast({
102
- title: '请上传小于1M的照片',
104
+ title: t('system.profile.photoLimit'),
103
105
  icon: 'none',
104
106
  duration: 1500,
105
107
  })
@@ -123,9 +125,9 @@ const uploadAvatar = async (file: string) => {
123
125
  userInfo.value.avatar = fileInfo.url
124
126
  await defAuthService.UpdateUserProfile(userInfo.value)
125
127
  syncUserStoreProfile(userInfo.value)
126
- await uni.showToast({ icon: 'success', title: '更新成功' })
128
+ await uni.showToast({ icon: 'success', title: t('system.profile.updateSuccess') })
127
129
  } catch {
128
- await uni.showToast({ icon: 'error', title: '上传头像失败' })
130
+ await uni.showToast({ icon: 'error', title: t('system.profile.avatarUploadFailed') })
129
131
  }
130
132
  }
131
133
 
@@ -146,7 +148,7 @@ const onGetPhoneNumber: UniHelper.ButtonOnGetphonenumber = async (e) => {
146
148
  const res = await defAuthService.BindUserPhone({ code: e.detail.code || '' })
147
149
  userInfo.value.phone = res.phone
148
150
  syncUserStoreProfile(userInfo.value)
149
- await uni.showToast({ icon: 'success', title: '授权成功' })
151
+ await uni.showToast({ icon: 'success', title: t('system.profile.phoneAuthorizationSuccess') })
150
152
  }
151
153
 
152
154
  // #endif
@@ -168,7 +170,7 @@ const onSubmit = async () => {
168
170
  })
169
171
  // 更新Store昵称
170
172
  syncUserStoreProfile(userInfo.value)
171
- await uni.showToast({ icon: 'success', title: '保存成功' })
173
+ await uni.showToast({ icon: 'success', title: t('system.profile.saveSuccess') })
172
174
  setTimeout(() => {
173
175
  uni.navigateBack()
174
176
  }, 400)
@@ -180,7 +182,7 @@ const onSubmit = async () => {
180
182
  <!-- 导航栏 -->
181
183
  <view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
182
184
  <navigator open-type="navigateBack" class="back icon-left" hover-class="none"></navigator>
183
- <view class="title">个人信息</view>
185
+ <view class="title">{{ t('system.profile.title') }}</view>
184
186
  </view>
185
187
  <view class="avatar">
186
188
  <view @tap="onAvatarChange" class="avatar-content">
@@ -191,7 +193,7 @@ const onSubmit = async () => {
191
193
  mode="aspectFill"
192
194
  />
193
195
  <image v-else class="image" :src="defaultAvatar" mode="aspectFill"></image>
194
- <text class="text">点击修改头像</text>
196
+ <text class="text">{{ t('system.profile.avatarChange') }}</text>
195
197
  </view>
196
198
  </view>
197
199
  <!-- 表单 -->
@@ -199,13 +201,13 @@ const onSubmit = async () => {
199
201
  <!-- 表单内容 -->
200
202
  <view class="form-content">
201
203
  <view class="form-item" v-if="userInfo?.user_name">
202
- <text class="label">账号</text>
204
+ <text class="label">{{ t('system.profile.account') }}</text>
203
205
  <text class="account placeholder">{{ userInfo?.user_name }}</text>
204
206
  </view>
205
207
  <!-- #ifdef MP-WEIXIN -->
206
208
  <!-- 手机号 -->
207
209
  <view class="form-item">
208
- <text class="label">手机号</text>
210
+ <text class="label">{{ t('system.profile.mobile') }}</text>
209
211
  <view class="input">
210
212
  <text v-if="userInfo.phone" class="account">{{ userInfo.phone }}</text>
211
213
  <button
@@ -214,17 +216,22 @@ const onSubmit = async () => {
214
216
  open-type="getPhoneNumber"
215
217
  @getphonenumber="onGetPhoneNumber"
216
218
  >
217
- 微信授权手机号
219
+ {{ t('system.profile.phoneAuthorization') }}
218
220
  </button>
219
221
  </view>
220
222
  </view>
221
223
  <!-- #endif -->
222
224
  <view class="form-item">
223
- <text class="label">昵称</text>
224
- <input class="input" type="text" placeholder="请填写昵称" v-model="userInfo.nick_name" />
225
+ <text class="label">{{ t('system.profile.nickName') }}</text>
226
+ <input
227
+ class="input"
228
+ type="text"
229
+ :placeholder="t('system.profile.nickNamePlaceholder')"
230
+ v-model="userInfo.nick_name"
231
+ />
225
232
  </view>
226
233
  <view class="form-item">
227
- <text class="label">性别</text>
234
+ <text class="label">{{ t('system.profile.gender') }}</text>
228
235
  <radio-group @change="onGenderChange">
229
236
  <label class="radio" v-for="(item, index) in genderList" :key="index">
230
237
  <radio
@@ -238,7 +245,7 @@ const onSubmit = async () => {
238
245
  </view>
239
246
  </view>
240
247
  <!-- 提交按钮 -->
241
- <button @tap="onSubmit" class="form-button">保 存</button>
248
+ <button @tap="onSubmit" class="form-button">{{ t('common.action.save') }}</button>
242
249
  </view>
243
250
  </view>
244
251
  </template>