refactor(ai-chat): 重构AI聊天国际化文件结构

- 将 ai-chat 语言文件从 ai/ 迁移至 ai-chat/ 目录
- 重构消息组件及多个子组件
This commit is contained in:
2026-05-18 15:36:13 +08:00
parent 5c56cfaaa7
commit 556ce9d132
12 changed files with 312 additions and 96 deletions

View File

@@ -3,7 +3,7 @@
<view class="avatar-selector-mask" @click="close"></view>
<view class="avatar-selector-panel">
<view class="avatar-selector-header">
<text>更换头像</text>
<text>{{ $lang('avatarSelectorTitle') }}</text>
<button class="close-btn" @click="close">
<text class="iconfont icon-close"></text>
</button>
@@ -15,10 +15,10 @@
<view class="avatar-selector-actions">
<button class="avatar-select-action" @click="chooseAvatarFromAlbum">
<text class="iconfont icon-album"></text>
<text>从相册选择</text>
<text>{{ $lang('chooseFromAlbum') }}</text>
</button>
<button class="avatar-select-action cancel" @click="close">
<text>取消</text>
<text>{{ $lang('cancel') }}</text>
</button>
</view>
</view>
@@ -80,7 +80,7 @@ export default {
reader.readAsDataURL(file)
this.$emit('avatar-change', tempFilePath)
this.close()
uni.showToast({ title: '头像更换成功', icon: 'success', duration: 1500 })
uni.showToast({ title: this.$lang('avatarChangeSuccess'), icon: 'success', duration: 1500 })
} else {
this.close()
}
@@ -99,11 +99,11 @@ export default {
uni.setStorageSync('user_avatar', tempFilePath)
this.$emit('avatar-change', tempFilePath)
this.close()
uni.showToast({ title: '头像更换成功', icon: 'success', duration: 1500 })
uni.showToast({ title: this.$lang('avatarChangeSuccess'), icon: 'success', duration: 1500 })
},
fail: (error) => {
if (error.errMsg && error.errMsg.includes('deny')) {
uni.showToast({ title: '请开启相册权限', icon: 'none', duration: 2000 })
uni.showToast({ title: this.$lang('enableAlbumPermission'), icon: 'none', duration: 2000 })
}
this.close()
}

View File

@@ -3,7 +3,7 @@
<view class="emoji-mask" @click="hideEmojiPanel"></view>
<view class="emoji-panel">
<view class="emoji-header">
<text>表情</text>
<text>{{ $lang('emojiTitle') }}</text>
<button class="close-btn" @click="hideEmojiPanel">
<text>×</text>
</button>

View File

@@ -3,7 +3,7 @@
<view class="nickname-editor-mask" @click="close"></view>
<view class="nickname-editor-panel">
<view class="nickname-editor-header">
<text>修改昵称</text>
<text>{{ $lang('editNickname') }}</text>
<button class="close-btn" @click="close">
<text class="iconfont icon-close"></text>
</button>
@@ -13,21 +13,21 @@
<input
v-model="tempNickname"
class="nickname-input"
placeholder="请输入您的新昵称"
:placeholder="$lang('newNicknamePlaceholder')"
:maxlength="12"
type="text" />
</view>
<view class="nickname-tip">昵称长度限制1-12个字符,仅自己可见</view>
<view class="nickname-tip">{{ $lang('nicknameTip') }}</view>
<view class="nickname-editor-actions">
<button class="nickname-action-btn cancel" @click="close">
<text>取消</text>
<text>{{ $lang('cancel') }}</text>
</button>
<button
class="nickname-action-btn confirm"
:class="{ disabled: !tempNickname.trim() }"
:disabled="!tempNickname.trim()"
@click="saveNickname">
<text>保存</text>
<text>{{ $lang('save') }}</text>
</button>
</view>
</view>
@@ -87,7 +87,7 @@ export default {
uni.setStorageSync('user_nickname', newNickname)
this.$emit('nickname-change', newNickname)
this.close()
uni.showToast({ title: '昵称修改成功', icon: 'success', duration: 1500 })
uni.showToast({ title: this.$lang('nicknameChangeSuccess'), icon: 'success', duration: 1500 })
}
}
}

View File

@@ -1,9 +1,9 @@
<template>
<view class="ai-chat-search">
<!-- 搜索入口按钮(浮动在消息区右上角) -->
<view class="search-entrance" v-if="!showSearchBar" @click="toggleSearch">
<view class="search-entrance" :class="{ 'icon-only': !showLabel }" v-if="!showSearchBar" @click="toggleSearch">
<text class="iconfont icon-sousuo search-entrance-icon"></text>
<text class="search-entrance-label">搜索</text>
<text class="search-entrance-label" v-if="showLabel">{{ $lang('searchEntrance') }}</text>
</view>
<!-- 搜索浮条 -->
@@ -14,7 +14,7 @@
<input
class="search-bar-input"
v-model="searchKeyword"
placeholder="搜索聊天记录"
:placeholder="$lang('searchPlaceholder')"
:focus="true"
@input="onSearchInput"
@confirm="doSearch" />
@@ -27,7 +27,7 @@
<text class="search-bar-nav" :class="{ disabled: searchResultIndex >= searchResults.length - 1 }" @click="scrollToNextResult">▼</text>
</view>
</view>
<text class="search-bar-close" @click="closeSearch">完成</text>
<text class="search-bar-close" @click="closeSearch">{{ $lang('searchClose') }}</text>
</view>
</view>
</view>
@@ -39,6 +39,7 @@
*
* Props:
* messages: Array - 原始消息列表
* showLabel: Boolean - (默认 true) 是否显示"搜索"文字;设为 false 则只显示搜索图标
*
* Events:
* scroll-to: (messageKey: string) => void - 请求父组件滚动到指定消息
@@ -53,6 +54,11 @@ export default {
messages: {
type: Array,
default: () => []
},
// 是否显示文字标签;设为 false 则只显示搜索图标
showLabel: {
type: Boolean,
default: true
}
},
@@ -66,6 +72,7 @@ export default {
},
computed: {
// 过滤后的消息列表(与 v-for 保持一致)
filteredMessages() {
return this.messages.filter(
@@ -150,7 +157,7 @@ export default {
this._emitScrollTo(filteredIdx)
})
} else {
uni.showToast({ title: '未找到匹配结果', icon: 'none' })
uni.showToast({ title: this.$lang('noMatchResult'), icon: 'none' })
}
},
@@ -174,7 +181,7 @@ export default {
if (idx >= 0) {
this.scrollToSearchResult(idx)
} else {
uni.showToast({ title: '已到顶部', icon: 'none' })
uni.showToast({ title: this.$lang('searchAtTop'), icon: 'none' })
}
},
@@ -183,7 +190,7 @@ export default {
if (idx < this.searchResults.length) {
this.scrollToSearchResult(idx)
} else {
uni.showToast({ title: '已到底部', icon: 'none' })
uni.showToast({ title: this.$lang('searchAtBottom'), icon: 'none' })
}
},
@@ -251,7 +258,7 @@ export default {
// 搜索入口按钮
.search-entrance {
position: absolute;
top: 88rpx;
top: 44rpx;
right: 16rpx;
z-index: 10;
display: flex;
@@ -277,6 +284,15 @@ export default {
&:active {
background: rgba(255, 255, 255, 0.8);
}
// 仅显示图标模式(圆形小按钮)
&.icon-only {
width: 56rpx;
height: 56rpx;
padding: 0;
justify-content: center;
border-radius: 50%;
}
}
// 搜索浮条

View File

@@ -3,7 +3,7 @@
<view class="tools-mask" @click="hideToolsPanel"></view>
<view class="tools-panel">
<view class="tools-header">
<text>更多功能</text>
<text>{{ $lang('moreFunctions') }}</text>
<button class="close-btn" @click="hideToolsPanel">
<text class="iconfont icon-close"></text>
</button>
@@ -13,25 +13,25 @@
<view class="tool-icon">
<text class="iconfont icon-image"></text>
</view>
<text class="tool-text">图片</text>
<text class="tool-text">{{ $lang('image') }}</text>
</view>
<view class="tool-item" @click="selectFile">
<view class="tool-icon">
<text class="iconfont icon-file"></text>
</view>
<text class="tool-text">文件</text>
<text class="tool-text">{{ $lang('file') }}</text>
</view>
<view class="tool-item" @click="startRecord">
<view class="tool-icon">
<text class="iconfont icon-voice"></text>
</view>
<text class="tool-text">语音</text>
<text class="tool-text">{{ $lang('voice') }}</text>
</view>
<view class="tool-item" @click="selectLocation">
<view class="tool-icon">
<text class="iconfont icon-location"></text>
</view>
<text class="tool-text">位置</text>
<text class="tool-text">{{ $lang('location') }}</text>
</view>
</view>
</view>

View File

@@ -3,7 +3,7 @@
<view class="voice-mask" @click="hideVoicePanel"></view>
<view class="voice-input-panel">
<view class="voice-header">
<text>语音输入</text>
<text>{{ $lang('voiceInputTitle') }}</text>
<button class="close-btn" @click="hideVoicePanel">
<text class="iconfont icon-close"></text>
</button>
@@ -16,7 +16,7 @@
class="wave-bar"
:style="{ animationDelay: (i * 0.1) + 's' }"></view>
</view>
<text class="voice-tip">{{ voiceInputing ? '正在录音,松开结束' : '点击开始录音' }}</text>
<text class="voice-tip">{{ voiceInputing ? $lang('recordingTip') : $lang('startRecordTip') }}</text>
</view>
<view class="voice-actions">
<button class="voice-btn" @click="toggleVoiceInput">
@@ -86,7 +86,7 @@ export default {
// 录音中,可扩展实时转文字
})
.catch(error => {
uni.showToast({ title: '语音输入失败', icon: 'none' })
uni.showToast({ title: this.$lang('voiceInputFailed'), icon: 'none' })
this.voiceInputing = false
})
}