fix: resolve mobile type and lint errors

This commit is contained in:
Tyke Chen
2026-07-10 11:45:16 +08:00
parent a5aee109fe
commit 687b6db96b
28 changed files with 1441 additions and 1416 deletions
+9 -8
View File
@@ -1,9 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { onHide, onLaunch, onShow } from '@dcloudio/uni-app' import { onHide, onLaunch, onShow } from '@dcloudio/uni-app'
import { watch, onMounted } from 'vue' import { onMounted, watch } from 'vue'
import { usePageAuth } from '@/hooks/usePageAuth' import { usePageAuth } from '@/hooks/usePageAuth'
import { useConfigStore } from '@/store'
import { t } from '@/i18n' import { t } from '@/i18n'
import { useConfigStore } from '@/store'
import { useLangStore } from '@/store/lang' import { useLangStore } from '@/store/lang'
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only' import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
@@ -37,9 +37,9 @@ function updateTabBarText() {
success: () => {}, success: () => {},
fail: (err) => { fail: (err) => {
console.log('设置首页tabBar文本失败:', err) console.log('设置首页tabBar文本失败:', err)
} },
}) })
// 设置配网tabBar文本 // 设置配网tabBar文本
uni.setTabBarItem({ uni.setTabBarItem({
index: 1, index: 1,
@@ -47,9 +47,9 @@ function updateTabBarText() {
success: () => {}, success: () => {},
fail: (err) => { fail: (err) => {
console.log('设置配网tabBar文本失败:', err) console.log('设置配网tabBar文本失败:', err)
} },
}) })
// 设置系统tabBar文本 // 设置系统tabBar文本
uni.setTabBarItem({ uni.setTabBarItem({
index: 2, index: 2,
@@ -57,9 +57,10 @@ function updateTabBarText() {
success: () => {}, success: () => {},
fail: (err) => { fail: (err) => {
console.log('设置系统tabBar文本失败:', err) console.log('设置系统tabBar文本失败:', err)
} },
}) })
} catch (error) { }
catch (error) {
console.log('更新tabBar文本时出错:', error) console.log('更新tabBar文本时出错:', error)
} }
} }
File diff suppressed because it is too large Load Diff
+10 -10
View File
@@ -1,23 +1,23 @@
import type { Language } from '@/store/lang'
import { ref } from 'vue' import { ref } from 'vue'
import { useLangStore } from '@/store/lang' import { useLangStore } from '@/store/lang'
import type { Language } from '@/store/lang'
import de from './de'
import en from './en'
import pt_BR from './pt_BR'
import vi from './vi'
// 导入各个语言的翻译文件 // 导入各个语言的翻译文件
import zh_CN from './zh_CN' import zh_CN from './zh_CN'
import en from './en'
import zh_TW from './zh_TW' import zh_TW from './zh_TW'
import de from './de'
import vi from './vi'
import pt_BR from './pt_BR'
// 语言包映射 // 语言包映射
const messages = { const messages = {
zh_CN: zh_CN, zh_CN,
en, en,
zh_TW: zh_TW, zh_TW,
de, de,
vi, vi,
pt_BR: pt_BR, pt_BR,
} }
// 当前使用的语言 // 当前使用的语言
@@ -42,7 +42,7 @@ export function t(key: string, params?: Record<string, string | number>): string
// 直接查找扁平键名 // 直接查找扁平键名
if (langMessages && typeof langMessages === 'object' && key in langMessages) { if (langMessages && typeof langMessages === 'object' && key in langMessages) {
let value = langMessages[key] const value = langMessages[key]
if (typeof value === 'string') { if (typeof value === 'string') {
// 处理参数替换 // 处理参数替换
if (params) { if (params) {
@@ -76,4 +76,4 @@ export function getSupportedLanguages(): { code: Language, name: string }[] {
{ code: 'vi', name: 'Tiếng Việt' }, { code: 'vi', name: 'Tiếng Việt' },
{ code: 'pt_BR', name: 'Português (Brasil)' }, { code: 'pt_BR', name: 'Português (Brasil)' },
] ]
} }
+3 -3
View File
@@ -29,9 +29,9 @@ export default {
'login.requiredCaptcha': 'O código de verificação não pode estar vazio', 'login.requiredCaptcha': 'O código de verificação não pode estar vazio',
'login.requiredMobile': 'Por favor, insira um número de telefone válido', 'login.requiredMobile': 'Por favor, insira um número de telefone válido',
'login.captchaError': 'Erro no código de verificação gráfico', 'login.captchaError': 'Erro no código de verificação gráfico',
'login.forgotPassword': 'Esqueceu a Senha', 'login.forgotPassword': 'Esqueceu a Senha',
'login.userAgreement': 'Termos de Uso', 'login.userAgreement': 'Termos de Uso',
'login.privacyPolicy': 'Política de Privacidade', 'login.privacyPolicy': 'Política de Privacidade',
// Register page // Register page
'register.pageTitle': 'Cadastro', 'register.pageTitle': 'Cadastro',
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -29,9 +29,9 @@ export default {
'login.requiredCaptcha': '驗證碼不能為空', 'login.requiredCaptcha': '驗證碼不能為空',
'login.requiredMobile': '請輸入正確的手機號碼', 'login.requiredMobile': '請輸入正確的手機號碼',
'login.captchaError': '圖形驗證碼錯誤', 'login.captchaError': '圖形驗證碼錯誤',
'login.forgotPassword': '忘記密碼', 'login.forgotPassword': '忘記密碼',
'login.userAgreement': '用戶協議', 'login.userAgreement': '用戶協議',
'login.privacyPolicy': '隱私政策', 'login.privacyPolicy': '隱私政策',
// 忘記密碼頁面 // 忘記密碼頁面
'retrievePassword.title': '重置密碼', 'retrievePassword.title': '重置密碼',
+1 -1
View File
@@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { ConfigProviderThemeVars } from 'wot-design-uni' import type { ConfigProviderThemeVars } from 'wot-design-uni/components/wd-config-provider/types'
const themeVars: ConfigProviderThemeVars = { const themeVars: ConfigProviderThemeVars = {
// colorTheme: 'red', // colorTheme: 'red',
@@ -38,10 +38,8 @@ onLoad(() => {
<wd-tabbar <wd-tabbar
v-if="customTabbarEnable" v-if="customTabbarEnable"
v-model="tabbarStore.curIdx" v-model="tabbarStore.curIdx"
bordered
safe-area-inset-bottom safe-area-inset-bottom bordered placeholder fixed
placeholder
fixed
@change="selectTabBar" @change="selectTabBar"
> >
<block v-for="(item, idx) in tabbarList" :key="item.path"> <block v-for="(item, idx) in tabbarList" :key="item.path">
+1 -1
View File
@@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { ConfigProviderThemeVars } from 'wot-design-uni' import type { ConfigProviderThemeVars } from 'wot-design-uni/components/wd-config-provider/types'
import FgTabbar from './fg-tabbar/fg-tabbar.vue' import FgTabbar from './fg-tabbar/fg-tabbar.vue'
const themeVars: ConfigProviderThemeVars = { const themeVars: ConfigProviderThemeVars = {
+6 -7
View File
@@ -1,15 +1,14 @@
import { VueQueryPlugin } from '@tanstack/vue-query' import { VueQueryPlugin } from '@tanstack/vue-query'
import { createSSRApp } from 'vue' import { createSSRApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import { routeInterceptor } from './router/interceptor'
import store from './store'
import '@/style/index.scss'
import 'virtual:uno.css'
// 导入国际化相关功能 // 导入国际化相关功能
import { initI18n } from './i18n' import { initI18n } from './i18n'
import { useLangStore } from './store/lang'
import { routeInterceptor } from './router/interceptor'
import store from './store'
import '@/style/index.scss'
import 'virtual:uno.css'
export function createApp() { export function createApp() {
const app = createSSRApp(App) const app = createSSRApp(App)
@@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { AgentSnapshot, AgentSnapshotData } from '@/api/agent/types' import type { AgentSnapshot, AgentSnapshotData } from '@/api/agent/types'
import { computed, ref, watch } from 'vue' import { computed, ref, watch } from 'vue'
import { useMessage } from 'wot-design-uni' import { useMessage } from 'wot-design-uni/components/wd-message-box'
import { import {
deleteAgentSnapshot, deleteAgentSnapshot,
getAgentDetail, getAgentDetail,
@@ -10,7 +10,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onLoad } from '@dcloudio/uni-app' import { onLoad } from '@dcloudio/uni-app'
import { computed, onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import CustomTabs from '@/components/custom-tabs/index.vue' import CustomTabs from '@/components/custom-tabs/index.vue'
import { t } from '@/i18n' import { t } from '@/i18n'
import ChatHistory from '@/pages/chat-history/index.vue' import ChatHistory from '@/pages/chat-history/index.vue'
@@ -9,7 +9,7 @@
</route> </route>
<script lang="ts" setup> <script lang="ts" setup>
import { useMessage } from 'wot-design-uni' import { useMessage } from 'wot-design-uni/components/wd-message-box'
import { getMcpAddress, getMcpTools } from '@/api/agent/agent' import { getMcpAddress, getMcpTools } from '@/api/agent/agent'
import { t } from '@/i18n' import { t } from '@/i18n'
import { usePluginStore } from '@/store' import { usePluginStore } from '@/store'
@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { useToast } from 'wot-design-uni' import { useToast } from 'wot-design-uni/components/wd-toast'
import { t } from '@/i18n' import { t } from '@/i18n'
// 类型定义 // 类型定义
@@ -228,15 +228,15 @@ async function generateAndPlay() {
generating.value = true generating.value = true
try { try {
console.log(t('deviceConfig.generatingUltrasonicConfigAudio') + '...') console.log(`${t('deviceConfig.generatingUltrasonicConfigAudio')}...`)
// 准备配网数据 - 参考HTML文件格式 // 准备配网数据 - 参考HTML文件格式
const dataStr = `${props.selectedNetwork.ssid}\n${props.password}` const dataStr = `${props.selectedNetwork.ssid}\n${props.password}`
const textBytes = stringToBytes(dataStr) const textBytes = stringToBytes(dataStr)
const fullBytes = [...START_BYTES, ...textBytes, checksum(textBytes), ...END_BYTES] const fullBytes = [...START_BYTES, ...textBytes, checksum(textBytes), ...END_BYTES]
console.log(t('deviceConfig.configData') + ':', { ssid: props.selectedNetwork.ssid, password: props.password }) console.log(`${t('deviceConfig.configData')}:`, { ssid: props.selectedNetwork.ssid, password: props.password })
console.log(t('deviceConfig.dataBytesLength') + ':', textBytes.length) console.log(`${t('deviceConfig.dataBytesLength')}:`, textBytes.length)
// 转换为比特流 // 转换为比特流
let bits: number[] = [] let bits: number[] = []
@@ -244,7 +244,7 @@ async function generateAndPlay() {
bits = bits.concat(toBits(b)) bits = bits.concat(toBits(b))
}) })
console.log(t('deviceConfig.bitStreamLength') + ':', bits.length) console.log(`${t('deviceConfig.bitStreamLength')}:`, bits.length)
// AFSK调制 - 减少采样率降低文件大小 // AFSK调制 - 减少采样率降低文件大小
const reducedSampleRate = 22050 // 降低采样率 const reducedSampleRate = 22050 // 降低采样率
@@ -267,7 +267,7 @@ async function generateAndPlay() {
const base64 = arrayBufferToBase64(wavBuffer) const base64 = arrayBufferToBase64(wavBuffer)
const dataUri = `data:audio/wav;base64,${base64}` const dataUri = `data:audio/wav;base64,${base64}`
console.log(t('deviceConfig.base64Length') + ':', base64.length, t('deviceConfig.about'), Math.round(base64.length / 1024), 'KB') console.log(`${t('deviceConfig.base64Length')}:`, base64.length, t('deviceConfig.about'), Math.round(base64.length / 1024), 'KB')
// 检查数据大小 // 检查数据大小
if (base64.length > 1024 * 1024) { // 超过1MB if (base64.length > 1024 * 1024) { // 超过1MB
@@ -277,7 +277,7 @@ async function generateAndPlay() {
audioFilePath.value = dataUri audioFilePath.value = dataUri
audioGenerated.value = true audioGenerated.value = true
console.log(t('deviceConfig.audioGenerationSuccess') + ',比特流长度:', bits.length, t('deviceConfig.samplePoints') + ':', floatBuf.length) console.log(`${t('deviceConfig.audioGenerationSuccess')},比特流长度:`, bits.length, `${t('deviceConfig.samplePoints')}:`, floatBuf.length)
toast.success(t('deviceConfig.soundWaveGenerationSuccess')) toast.success(t('deviceConfig.soundWaveGenerationSuccess'))
@@ -287,8 +287,8 @@ async function generateAndPlay() {
}, 800) // 增加延迟时间 }, 800) // 增加延迟时间
} }
catch (error) { catch (error) {
console.error(t('deviceConfig.audioGenerationFailed') + ':', error) console.error(`${t('deviceConfig.audioGenerationFailed')}:`, error)
toast.error(`${t('deviceConfig.soundWaveGenerationFailed')}: ${error.message || error}`) toast.error(`${t('deviceConfig.soundWaveGenerationFailed')}: ${error.message || error}`)
} }
finally { finally {
generating.value = false generating.value = false
@@ -384,7 +384,7 @@ async function playAudio() {
}) })
innerAudioContext.onError((error) => { innerAudioContext.onError((error) => {
console.error(t('deviceConfig.audioPlaybackFailed') + ':', error) console.error(`${t('deviceConfig.audioPlaybackFailed')}:`, error)
playing.value = false playing.value = false
let errorMsg = t('deviceConfig.audioPlaybackFailed') let errorMsg = t('deviceConfig.audioPlaybackFailed')
@@ -417,10 +417,10 @@ async function playAudio() {
}, 300) }, 300)
} }
catch (error) { catch (error) {
console.error(t('deviceConfig.audioPlaybackError') + ':', error) console.error(`${t('deviceConfig.audioPlaybackError')}:`, error)
playing.value = false playing.value = false
await cleanupAudio() await cleanupAudio()
toast.error(`${t('deviceConfig.playbackFailed')}: ${error.message}`) toast.error(`${t('deviceConfig.playbackFailed')}: ${error.message}`)
} }
} }
@@ -433,7 +433,7 @@ async function cleanupAudio() {
console.log(t('deviceConfig.cleaningUpAudioContext')) console.log(t('deviceConfig.cleaningUpAudioContext'))
} }
catch (e) { catch (e) {
console.log(t('deviceConfig.cleaningUpAudioContextFailed') + ':', e) console.log(`${t('deviceConfig.cleaningUpAudioContextFailed')}:`, e)
} }
finally { finally {
audioContext.value = null audioContext.value = null
@@ -483,38 +483,38 @@ async function stopAudio() {
:disabled="!canGenerate" :disabled="!canGenerate"
@click="generateAndPlay" @click="generateAndPlay"
> >
{{ generating ? t('deviceConfig.generating') : '🎵 ' + t('deviceConfig.generateAndPlaySoundWave') }} {{ generating ? t('deviceConfig.generating') : `🎵 ${t('deviceConfig.generateAndPlaySoundWave')}` }}
</wd-button> </wd-button>
<wd-button <wd-button
v-if="audioGenerated" v-if="audioGenerated"
type="success" type="success"
size="large" size="large"
block block
:loading="playing" :loading="playing"
@click="playAudio" @click="playAudio"
> >
{{ playing ? t('deviceConfig.playing') : '🔊 ' + t('deviceConfig.playSoundWave') }} {{ playing ? t('deviceConfig.playing') : `🔊 ${t('deviceConfig.playSoundWave')}` }}
</wd-button> </wd-button>
<wd-button <wd-button
v-if="playing" v-if="playing"
type="warning" type="warning"
size="large" size="large"
block block
@click="stopAudio" @click="stopAudio"
> >
{{ t('deviceConfig.stopPlaying') }} {{ t('deviceConfig.stopPlaying') }}
</wd-button> </wd-button>
</view> </view>
<!-- 音频控制选项 --> <!-- 音频控制选项 -->
<view v-if="audioGenerated" class="audio-options"> <view v-if="audioGenerated" class="audio-options">
<view class="option-item"> <view class="option-item">
<wd-checkbox v-model="autoLoop"> <wd-checkbox v-model="autoLoop">
{{ t('deviceConfig.autoLoopPlaySoundWave') }} {{ t('deviceConfig.autoLoopPlaySoundWave') }}
</wd-checkbox> </wd-checkbox>
</view> </view>
</view> </view>
<!-- 音频播放器 --> <!-- 音频播放器 -->
@@ -531,33 +531,33 @@ async function stopAudio() {
<!-- 使用说明 --> <!-- 使用说明 -->
<view class="help-section"> <view class="help-section">
<view class="help-title"> <view class="help-title">
{{ t('deviceConfig.ultrasonicConfigInstructions') }} {{ t('deviceConfig.ultrasonicConfigInstructions') }}
</view>
<view class="help-content">
<text class="help-item">
1. {{ t('deviceConfig.ensureWifiNetworkSelectedAndPasswordEntered') }}
</text>
<text class="help-item">
2. {{ t('deviceConfig.clickGenerateAndPlaySoundWave') }}
</text>
<text class="help-item">
3. {{ t('deviceConfig.bringPhoneCloseToXiaozhiDevice') }}
</text>
<text class="help-item">
4. {{ t('deviceConfig.duringAudioPlaybackXiaozhiWillReceive') }}
</text>
<text class="help-item">
5. {{ t('deviceConfig.afterConfigSuccessDeviceWillConnect') }}
</text>
<text class="help-tip">
{{ t('deviceConfig.usesAfskModulation') }}
</text>
<text class="help-tip">
{{ t('deviceConfig.ensureModeratePhoneVolume') }}
</text>
</view>
</view> </view>
<view class="help-content">
<text class="help-item">
1. {{ t('deviceConfig.ensureWifiNetworkSelectedAndPasswordEntered') }}
</text>
<text class="help-item">
2. {{ t('deviceConfig.clickGenerateAndPlaySoundWave') }}
</text>
<text class="help-item">
3. {{ t('deviceConfig.bringPhoneCloseToXiaozhiDevice') }}
</text>
<text class="help-item">
4. {{ t('deviceConfig.duringAudioPlaybackXiaozhiWillReceive') }}
</text>
<text class="help-item">
5. {{ t('deviceConfig.afterConfigSuccessDeviceWillConnect') }}
</text>
<text class="help-tip">
{{ t('deviceConfig.usesAfskModulation') }}
</text>
<text class="help-tip">
{{ t('deviceConfig.ensureModeratePhoneVolume') }}
</text>
</view>
</view>
</view> </view>
</template> </template>
@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
import { useToast } from 'wot-design-uni' import { useToast } from 'wot-design-uni/components/wd-toast'
import { t } from '@/i18n' import { t } from '@/i18n'
// 类型定义 // 类型定义
@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { onMounted, ref } from 'vue'
import { t } from '@/i18n' import { t } from '@/i18n'
import UltrasonicConfig from './components/ultrasonic-config.vue' import UltrasonicConfig from './components/ultrasonic-config.vue'
import WifiConfig from './components/wifi-config.vue' import WifiConfig from './components/wifi-config.vue'
@@ -68,12 +68,10 @@ function onNetworkSelected(network: WiFiNetwork | null, password: string) {
function onConnectionStatusChange(connected: boolean) { function onConnectionStatusChange(connected: boolean) {
console.log('ESP32连接状态:', connected) console.log('ESP32连接状态:', connected)
} }
// 在组件挂载后设置导航栏标题 // 在组件挂载后设置导航栏标题
import { onMounted } from 'vue'
onMounted(() => { onMounted(() => {
uni.setNavigationBarTitle({ uni.setNavigationBarTitle({
title: t('deviceConfig.pageTitle') title: t('deviceConfig.pageTitle'),
}) })
}) })
</script> </script>
@@ -86,18 +84,18 @@ onMounted(() => {
<!-- 配网方式选择 --> <!-- 配网方式选择 -->
<view class="pb-[20rpx] first:pt-[20rpx]"> <view class="pb-[20rpx] first:pt-[20rpx]">
<text class="text-[32rpx] text-[#232338] font-bold"> <text class="text-[32rpx] text-[#232338] font-bold">
{{ t('deviceConfig.configMethod') }} {{ t('deviceConfig.configMethod') }}
</text> </text>
</view> </view>
<view class="mb-[24rpx] border border-[#eeeeee] rounded-[20rpx] bg-[#fbfbfb] p-[24rpx]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);"> <view class="mb-[24rpx] border border-[#eeeeee] rounded-[20rpx] bg-[#fbfbfb] p-[24rpx]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
<view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:border-[#336cff] active:bg-[#eef3ff]" @click="showConfigTypeSelector"> <view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:border-[#336cff] active:bg-[#eef3ff]" @click="showConfigTypeSelector">
<text class="text-[28rpx] text-[#232338] font-medium"> <text class="text-[28rpx] text-[#232338] font-medium">
{{ t('deviceConfig.configMethod') }} {{ t('deviceConfig.configMethod') }}
</text> </text>
<text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]"> <text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
{{ configType === 'wifi' ? t('deviceConfig.wifiConfig') : t('deviceConfig.ultrasonicConfig') }} {{ configType === 'wifi' ? t('deviceConfig.wifiConfig') : t('deviceConfig.ultrasonicConfig') }}
</text> </text>
<wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" /> <wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view> </view>
</view> </view>
@@ -105,8 +103,8 @@ onMounted(() => {
<!-- WiFi网络选择 --> <!-- WiFi网络选择 -->
<view class="pb-[20rpx]"> <view class="pb-[20rpx]">
<text class="text-[32rpx] text-[#232338] font-bold"> <text class="text-[32rpx] text-[#232338] font-bold">
{{ t('deviceConfig.networkConfig') }} {{ t('deviceConfig.networkConfig') }}
</text> </text>
</view> </view>
<view class="mb-[24rpx] border border-[#eeeeee] rounded-[20rpx] bg-[#fbfbfb] p-[24rpx]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);"> <view class="mb-[24rpx] border border-[#eeeeee] rounded-[20rpx] bg-[#fbfbfb] p-[24rpx]" style="box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);">
@@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Device, FirmwareType } from '@/api/device' import type { Device, FirmwareType } from '@/api/device'
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
import { useMessage } from 'wot-design-uni' import { useMessage } from 'wot-design-uni/components/wd-message-box'
import { bindDevice, bindDeviceManual, getBindDevices, getFirmwareTypes, unbindDevice, updateDeviceAutoUpdate } from '@/api/device' import { bindDevice, bindDeviceManual, getBindDevices, getFirmwareTypes, unbindDevice, updateDeviceAutoUpdate } from '@/api/device'
import { t } from '@/i18n' import { t } from '@/i18n'
import { toast } from '@/utils/toast' import { toast } from '@/utils/toast'
@@ -9,23 +9,23 @@
</route> </route>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, ref } from "vue"; import { onLoad } from '@dcloudio/uni-app'
import { onLoad } from "@dcloudio/uni-app"; import { computed, onMounted, ref } from 'vue'
import { useConfigStore } from "@/store";
import { getEnvBaseUrl, sm2Encrypt } from "@/utils";
import { toast } from "@/utils/toast";
// 导入国际化相关功能
import { t, initI18n } from "@/i18n";
// 导入API接口 // 导入API接口
import { retrievePassword, sendSmsCode } from "@/api/auth"; import { retrievePassword, sendSmsCode } from '@/api/auth'
// 导入国际化相关功能
import { initI18n, t } from '@/i18n'
import { useConfigStore } from '@/store'
import { getEnvBaseUrl, sm2Encrypt } from '@/utils'
import { toast } from '@/utils/toast'
// 获取屏幕边界到安全区域距离 // 获取屏幕边界到安全区域距离
let safeAreaInsets; let safeAreaInsets
let systemInfo; let systemInfo
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
// 微信小程序使用新的API // 微信小程序使用新的API
systemInfo = uni.getWindowInfo(); systemInfo = uni.getWindowInfo()
safeAreaInsets = systemInfo.safeArea safeAreaInsets = systemInfo.safeArea
? { ? {
top: systemInfo.safeArea.top, top: systemInfo.safeArea.top,
@@ -33,147 +33,148 @@ safeAreaInsets = systemInfo.safeArea
bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom, bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
left: systemInfo.safeArea.left, left: systemInfo.safeArea.left,
} }
: null; : null
// #endif // #endif
// #ifndef MP-WEIXIN // #ifndef MP-WEIXIN
// 其他平台继续使用uni API // 其他平台继续使用uni API
systemInfo = uni.getSystemInfoSync(); systemInfo = uni.getSystemInfoSync()
safeAreaInsets = systemInfo.safeAreaInsets; safeAreaInsets = systemInfo.safeAreaInsets
// #endif // #endif
// 表单数据 // 表单数据
interface ForgotPasswordData { interface ForgotPasswordData {
mobile: string; mobile: string
captcha: string; captcha: string
captchaId: string; captchaId: string
mobileCaptcha: string; mobileCaptcha: string
newPassword: string; newPassword: string
confirmPassword: string; confirmPassword: string
areaCode: string; areaCode: string
} }
const formData = ref<ForgotPasswordData>({ const formData = ref<ForgotPasswordData>({
mobile: "", mobile: '',
captcha: "", captcha: '',
captchaId: "", captchaId: '',
mobileCaptcha: "", mobileCaptcha: '',
newPassword: "", newPassword: '',
confirmPassword: "", confirmPassword: '',
areaCode: "+86", areaCode: '+86',
}); })
// 验证码图片 // 验证码图片
const captchaImage = ref(""); const captchaImage = ref('')
const loading = ref(false); const loading = ref(false)
// 获取配置store // 获取配置store
const configStore = useConfigStore(); const configStore = useConfigStore()
// State for area code action sheet // State for area code action sheet
const showAreaCodeSheet = ref(false); const showAreaCodeSheet = ref(false)
const selectedAreaCode = ref("+86"); const selectedAreaCode = ref('+86')
// 短信验证码倒计时
const smsCountdown = ref(0)
const smsLoading = ref(false)
const areaCodeList = computed(() => const areaCodeList = computed(() =>
(configStore.config.mobileAreaList || []).map((item) => { (configStore.config.mobileAreaList || []).map((item) => {
return { return {
value: item.key, value: item.key,
label: `${item.name} (${item.key})`, label: `${item.name} (${item.key})`,
}; }
}) }),
); )
const canSendMobileCaptcha = computed(() => { const canSendMobileCaptcha = computed(() => {
const mobile = formData.value.mobile; const mobile = formData.value.mobile
const phoneRegex = /^1[3-9]\d{9}$/; const phoneRegex = /^1[3-9]\d{9}$/
return phoneRegex.test(mobile) && smsCountdown.value === 0; return phoneRegex.test(mobile) && smsCountdown.value === 0
}); })
// SM2公钥 // SM2公钥
const sm2PublicKey = computed(() => { const sm2PublicKey = computed(() => {
return configStore.config.sm2PublicKey; return configStore.config.sm2PublicKey
}); })
// 短信验证码倒计时
const smsCountdown = ref(0);
const smsLoading = ref(false);
// 打开区号选择弹窗 // 打开区号选择弹窗
function openAreaCodeSheet() { function openAreaCodeSheet() {
showAreaCodeSheet.value = true; showAreaCodeSheet.value = true
} }
// 选择区号 // 选择区号
function selectAreaCode(item: { value: string; label: string }) { function selectAreaCode(item: { value: string, label: string }) {
selectedAreaCode.value = item.value; selectedAreaCode.value = item.value
formData.value.areaCode = item.value; formData.value.areaCode = item.value
showAreaCodeSheet.value = false; showAreaCodeSheet.value = false
} }
// 关闭区号选择弹窗 // 关闭区号选择弹窗
function closeAreaCodeSheet() { function closeAreaCodeSheet() {
showAreaCodeSheet.value = false; showAreaCodeSheet.value = false
} }
// 生成UUID // 生成UUID
function generateUUID() { function generateUUID() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0; const r = (Math.random() * 16) | 0
const v = c === "x" ? r : (r & 0x3) | 0x8; const v = c === 'x' ? r : (r & 0x3) | 0x8
return v.toString(16); return v.toString(16)
}); })
} }
// 获取图形验证码 // 获取图形验证码
async function refreshCaptcha() { async function refreshCaptcha() {
const uuid = generateUUID(); const uuid = generateUUID()
formData.value.captchaId = uuid; formData.value.captchaId = uuid
captchaImage.value = `${getEnvBaseUrl()}/user/captcha?uuid=${uuid}&t=${Date.now()}`; captchaImage.value = `${getEnvBaseUrl()}/user/captcha?uuid=${uuid}&t=${Date.now()}`
} }
// 发送短信验证码 // 发送短信验证码
async function handleSendSmsCode() { async function handleSendSmsCode() {
// 手机号格式验证 // 手机号格式验证
const phoneRegex = /^1[3-9]\d{9}$/; const phoneRegex = /^1[3-9]\d{9}$/
if (!phoneRegex.test(formData.value.mobile)) { if (!phoneRegex.test(formData.value.mobile)) {
toast.warning(t("retrievePassword.inputCorrectMobile")); toast.warning(t('retrievePassword.inputCorrectMobile'))
return; return
} }
if (!formData.value.captcha) { if (!formData.value.captcha) {
toast.warning(t("retrievePassword.captchaRequired")); toast.warning(t('retrievePassword.captchaRequired'))
return; return
} }
try { try {
smsLoading.value = true; smsLoading.value = true
// 将手机号转换为国际格式 // 将手机号转换为国际格式
const internationalPhone = formData.value.areaCode + formData.value.mobile; const internationalPhone = formData.value.areaCode + formData.value.mobile
// 调用发送短信验证码API // 调用发送短信验证码API
await sendSmsCode({ await sendSmsCode({
phone: internationalPhone, phone: internationalPhone,
captcha: formData.value.captcha, captcha: formData.value.captcha,
captchaId: formData.value.captchaId captchaId: formData.value.captchaId,
}) })
toast.success(t("retrievePassword.captchaSendSuccess")); toast.success(t('retrievePassword.captchaSendSuccess'))
// 开始倒计时 // 开始倒计时
smsCountdown.value = 60; smsCountdown.value = 60
const timer = setInterval(() => { const timer = setInterval(() => {
smsCountdown.value--; smsCountdown.value--
if (smsCountdown.value <= 0) { if (smsCountdown.value <= 0) {
clearInterval(timer); clearInterval(timer)
} }
}, 1000); }, 1000)
} catch (error: any) { }
catch (error: any) {
// 处理验证码错误 // 处理验证码错误
if (error.message.includes("请求错误[10067]")) { if (error.message.includes('请求错误[10067]')) {
toast.warning(t("login.captchaError")); toast.warning(t('login.captchaError'))
} }
// 发送失败重新获取图形验证码 // 发送失败重新获取图形验证码
refreshCaptcha(); refreshCaptcha()
} finally { }
smsLoading.value = false; finally {
smsLoading.value = false
} }
} }
@@ -181,64 +182,65 @@ async function handleSendSmsCode() {
async function handleResetPassword() { async function handleResetPassword() {
// 表单验证 // 表单验证
if (!formData.value.mobile) { if (!formData.value.mobile) {
toast.warning(t("retrievePassword.mobileRequired")); toast.warning(t('retrievePassword.mobileRequired'))
return; return
} }
// 手机号格式验证 // 手机号格式验证
const phoneRegex = /^1[3-9]\d{9}$/; const phoneRegex = /^1[3-9]\d{9}$/
if (!phoneRegex.test(formData.value.mobile)) { if (!phoneRegex.test(formData.value.mobile)) {
toast.warning(t("retrievePassword.inputCorrectMobile")); toast.warning(t('retrievePassword.inputCorrectMobile'))
return; return
} }
// 将手机号转换为国际格式 // 将手机号转换为国际格式
const internationalPhone = formData.value.areaCode + formData.value.mobile; const internationalPhone = formData.value.areaCode + formData.value.mobile
if (!formData.value.captcha) { if (!formData.value.captcha) {
toast.warning(t("retrievePassword.captchaRequired")); toast.warning(t('retrievePassword.captchaRequired'))
return; return
} }
if (!formData.value.mobileCaptcha) { if (!formData.value.mobileCaptcha) {
toast.warning(t("retrievePassword.mobileCaptchaRequired")); toast.warning(t('retrievePassword.mobileCaptchaRequired'))
return; return
} }
if (!formData.value.newPassword) { if (!formData.value.newPassword) {
toast.warning(t("retrievePassword.newPasswordRequired")); toast.warning(t('retrievePassword.newPasswordRequired'))
return; return
} }
if (!formData.value.confirmPassword) { if (!formData.value.confirmPassword) {
toast.warning(t("retrievePassword.confirmNewPasswordRequired")); toast.warning(t('retrievePassword.confirmNewPasswordRequired'))
return; return
} }
if (formData.value.newPassword !== formData.value.confirmPassword) { if (formData.value.newPassword !== formData.value.confirmPassword) {
toast.warning(t("retrievePassword.passwordsNotMatch")); toast.warning(t('retrievePassword.passwordsNotMatch'))
return; return
} }
try { try {
loading.value = true; loading.value = true
// 检查SM2公钥是否配置 // 检查SM2公钥是否配置
if (!sm2PublicKey.value) { if (!sm2PublicKey.value) {
toast.warning(t("sm2.publicKeyNotConfigured")); toast.warning(t('sm2.publicKeyNotConfigured'))
return; return
} }
// 加密密码 // 加密密码
let encryptedPassword; let encryptedPassword
try { try {
// 拼接图形验证码和新密码进行加密 // 拼接图形验证码和新密码进行加密
const captchaAndPassword = formData.value.captcha + formData.value.newPassword; const captchaAndPassword = formData.value.captcha + formData.value.newPassword
encryptedPassword = sm2Encrypt(sm2PublicKey.value, captchaAndPassword); encryptedPassword = sm2Encrypt(sm2PublicKey.value, captchaAndPassword)
} catch (error) { }
console.error("密码加密失败:", error); catch (error) {
toast.warning(t("sm2.encryptionFailed")); console.error('密码加密失败:', error)
return; toast.warning(t('sm2.encryptionFailed'))
return
} }
// 调用重置密码API // 调用重置密码API
@@ -246,53 +248,56 @@ async function handleResetPassword() {
phone: internationalPhone, phone: internationalPhone,
code: formData.value.mobileCaptcha, code: formData.value.mobileCaptcha,
password: encryptedPassword, password: encryptedPassword,
captchaId: formData.value.captchaId captchaId: formData.value.captchaId,
}) })
toast.success(t("retrievePassword.passwordUpdateSuccess")); toast.success(t('retrievePassword.passwordUpdateSuccess'))
// 跳转到登录页 // 跳转到登录页
setTimeout(() => { setTimeout(() => {
uni.redirectTo({ uni.redirectTo({
url: "/pages/login/index", url: '/pages/login/index',
}); })
}, 1000); }, 1000)
} catch (error: any) { }
catch (error: any) {
// 处理验证码错误 // 处理验证码错误
if (error.message.includes("请求错误[10067]")) { if (error.message.includes('请求错误[10067]')) {
toast.warning(t("login.captchaError")); toast.warning(t('login.captchaError'))
} }
// 重置失败重新获取验证码 // 重置失败重新获取验证码
refreshCaptcha(); refreshCaptcha()
} finally { }
loading.value = false; finally {
loading.value = false
} }
} }
// 返回登录 // 返回登录
function goBack() { function goBack() {
uni.redirectTo({ uni.redirectTo({
url: "/pages/login/index", url: '/pages/login/index',
}); })
} }
// 页面加载时获取验证码 // 页面加载时获取验证码
onLoad(() => { onLoad(() => {
refreshCaptcha(); refreshCaptcha()
}); })
// 组件挂载时确保配置已加载 // 组件挂载时确保配置已加载
onMounted(async () => { onMounted(async () => {
if (!configStore.config.name) { if (!configStore.config.name) {
try { try {
await configStore.fetchPublicConfig(); await configStore.fetchPublicConfig()
} catch (error) { }
console.error("获取配置失败:", error); catch (error) {
console.error('获取配置失败:', error)
} }
} }
// 初始化国际化 // 初始化国际化
initI18n(); initI18n()
}); })
</script> </script>
<template> <template>
@@ -471,7 +476,7 @@ onMounted(async () => {
min-height: 100vh; min-height: 100vh;
&::before { &::before {
content: ""; content: '';
position: absolute; position: absolute;
top: -50%; top: -50%;
left: -50%; left: -50%;
@@ -713,10 +718,7 @@ onMounted(async () => {
margin-bottom: 30rpx; margin-bottom: 30rpx;
box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3); box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3);
transition: all 0.3s ease; transition: all 0.3s ease;
background-color: var( background-color: var(--wot-button-primary-bg-color, var(--wot-color-theme, #4d80f0));
--wot-button-primary-bg-color,
var(--wot-color-theme, #4d80f0)
);
text-align: center; text-align: center;
line-height: 80rpx; line-height: 80rpx;
@@ -14,7 +14,7 @@
import type { Agent } from '@/api/agent/types' import type { Agent } from '@/api/agent/types'
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
// 在组件挂载后设置导航栏标题 // 在组件挂载后设置导航栏标题
import { useMessage } from 'wot-design-uni' import { useMessage } from 'wot-design-uni/components/wd-message-box'
import useZPaging from 'z-paging/components/z-paging/js/hooks/useZPaging.js' import useZPaging from 'z-paging/components/z-paging/js/hooks/useZPaging.js'
import { createAgent, deleteAgent, getAgentList } from '@/api/agent/agent' import { createAgent, deleteAgent, getAgentList } from '@/api/agent/agent'
import { t } from '@/i18n' import { t } from '@/i18n'
@@ -167,7 +167,7 @@ function openCreateDialog() {
msg: '', msg: '',
inputPlaceholder: t('home.inputPlaceholder'), inputPlaceholder: t('home.inputPlaceholder'),
inputValue: '', inputValue: '',
inputPattern: /^.{1,64}$/i, inputPattern: /^.{1,64}$/,
inputError: t('home.createError'), inputError: t('home.createError'),
confirmButtonText: t('home.createNow'), confirmButtonText: t('home.createNow'),
cancelButtonText: t('common.cancel'), cancelButtonText: t('common.cancel'),
@@ -401,7 +401,9 @@ onMounted(async () => {
<text class="policy-link" @click="goToUserAgreement"> <text class="policy-link" @click="goToUserAgreement">
{{ t('login.userAgreement') }} {{ t('login.userAgreement') }}
</text> </text>
<text class="policy-divider">|</text> <text class="policy-divider">
{{ '|' }}
</text>
<text class="policy-link" @click="goToPrivacyPolicy"> <text class="policy-link" @click="goToPrivacyPolicy">
{{ t('login.privacyPolicy') }} {{ t('login.privacyPolicy') }}
</text> </text>
@@ -9,15 +9,14 @@
</route> </route>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, ref } from 'vue'; import { computed, onMounted, ref } from 'vue'
import { register, sendSmsCode } from '@/api/auth'; import { register, sendSmsCode } from '@/api/auth'
import { useConfigStore } from '@/store';
import { getEnvBaseUrl } from '@/utils';
import { toast } from '@/utils/toast';
// 导入国际化相关功能 // 导入国际化相关功能
import { t, initI18n } from '@/i18n'; import { initI18n, t } from '@/i18n'
import { useConfigStore } from '@/store'
// 导入SM2加密工具 // 导入SM2加密工具
import { sm2Encrypt } from '@/utils'; import { getEnvBaseUrl, sm2Encrypt } from '@/utils'
import { toast } from '@/utils/toast'
// 获取屏幕边界到安全区域距离 // 获取屏幕边界到安全区域距离
let safeAreaInsets let safeAreaInsets
@@ -135,9 +134,9 @@ function generateUUID() {
// 获取验证码 // 获取验证码
async function refreshCaptcha() { async function refreshCaptcha() {
const uuid = generateUUID(); const uuid = generateUUID()
formData.value.captchaId = uuid; formData.value.captchaId = uuid
captchaImage.value = `${getEnvBaseUrl()}/user/captcha?uuid=${uuid}&t=${Date.now()}`; captchaImage.value = `${getEnvBaseUrl()}/user/captcha?uuid=${uuid}&t=${Date.now()}`
} }
// 发送短信验证码 // 发送短信验证码
@@ -253,7 +252,8 @@ async function handleRegister() {
// 拼接验证码和密码 // 拼接验证码和密码
const captchaAndPassword = formData.value.captcha + formData.value.password const captchaAndPassword = formData.value.captcha + formData.value.password
encryptedPassword = sm2Encrypt(sm2PublicKey.value, captchaAndPassword) encryptedPassword = sm2Encrypt(sm2PublicKey.value, captchaAndPassword)
} catch (error) { }
catch (error) {
console.error('密码加密失败:', error) console.error('密码加密失败:', error)
toast.warning(t('sm2.encryptionFailed')) toast.warning(t('sm2.encryptionFailed'))
return return
@@ -275,7 +275,7 @@ async function handleRegister() {
// 跳转到登录页 // 跳转到登录页
setTimeout(() => { setTimeout(() => {
uni.redirectTo({ uni.redirectTo({
url: '/pages/login/index' url: '/pages/login/index',
}) })
}, 1000) }, 1000)
} }
@@ -299,7 +299,7 @@ async function handleRegister() {
// 返回登录 // 返回登录
function goBack() { function goBack() {
uni.redirectTo({ uni.redirectTo({
url: '/pages/login/index' url: '/pages/login/index',
}) })
} }
@@ -321,8 +321,6 @@ onMounted(async () => {
// 初始化国际化 // 初始化国际化
initI18n() initI18n()
}) })
</script> </script>
<template> <template>
@@ -385,41 +383,41 @@ onMounted(async () => {
<view class="input-group"> <view class="input-group">
<view class="input-wrapper"> <view class="input-wrapper">
<wd-input <wd-input
v-model="formData.password" v-model="formData.password"
custom-class="styled-input" custom-class="styled-input"
no-border no-border
:placeholder="t('register.enterPassword')" :placeholder="t('register.enterPassword')"
show-password show-password
:maxlength="20" :maxlength="20"
/> />
</view> </view>
</view> </view>
<view class="input-group"> <view class="input-group">
<view class="input-wrapper"> <view class="input-wrapper">
<wd-input <wd-input
v-model="formData.confirmPassword" v-model="formData.confirmPassword"
custom-class="styled-input" custom-class="styled-input"
no-border no-border
:placeholder="t('register.confirmPassword')" :placeholder="t('register.confirmPassword')"
show-password show-password
:maxlength="20" :maxlength="20"
/> />
</view> </view>
</view> </view>
<view class="input-group"> <view class="input-group">
<view class="input-wrapper captcha-wrapper"> <view class="input-wrapper captcha-wrapper">
<wd-input <wd-input
v-model="formData.captcha" v-model="formData.captcha"
custom-class="styled-input" custom-class="styled-input"
no-border no-border
:placeholder="t('register.enterCode')" :placeholder="t('register.enterCode')"
:maxlength="6" :maxlength="6"
/> />
<view class="captcha-image" @click="refreshCaptcha"> <view class="captcha-image" @click="refreshCaptcha">
<image :src="captchaImage" class="captcha-img" /> <image :src="captchaImage" class="captcha-img" />
</view> </view>
</view> </view>
</view> </view>
@@ -427,21 +425,21 @@ onMounted(async () => {
<view v-if="enableMobileRegister" class="input-group"> <view v-if="enableMobileRegister" class="input-group">
<view class="input-wrapper sms-wrapper"> <view class="input-wrapper sms-wrapper">
<wd-input <wd-input
v-model="formData.mobileCaptcha" v-model="formData.mobileCaptcha"
custom-class="styled-input" custom-class="styled-input"
no-border no-border
:placeholder="t('register.enterCode')" :placeholder="t('register.enterCode')"
type="number" type="number"
:maxlength="6" :maxlength="6"
/> />
<wd-button <wd-button
:loading="smsLoading" :loading="smsLoading"
:disabled="smsCountdown > 0" :disabled="smsCountdown > 0"
custom-class="sms-btn" custom-class="sms-btn"
@click="sendSmsVerification" @click="sendSmsVerification"
> >
{{ smsCountdown > 0 ? `${smsCountdown}s` : t('register.getCode') }} {{ smsCountdown > 0 ? `${smsCountdown}s` : t('register.getCode') }}
</wd-button> </wd-button>
</view> </view>
</view> </view>
@@ -462,7 +460,6 @@ onMounted(async () => {
{{ t('register.loginNow') }} {{ t('register.loginNow') }}
</text> </text>
</view> </view>
</view> </view>
</view> </view>
@@ -508,8 +505,6 @@ onMounted(async () => {
</view> </view>
</view> </view>
</wd-action-sheet> </wd-action-sheet>
</view> </view>
</template> </template>
@@ -11,7 +11,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Language } from '@/store/lang' import type { Language } from '@/store/lang'
import { computed, onMounted, reactive, ref } from 'vue' import { computed, onMounted, reactive, ref } from 'vue'
import { useToast } from 'wot-design-uni' import { useToast } from 'wot-design-uni/components/wd-toast'
import { changeLanguage, getCurrentLanguage, getSupportedLanguages, t } from '@/i18n' import { changeLanguage, getCurrentLanguage, getSupportedLanguages, t } from '@/i18n'
import { useConfigStore } from '@/store' import { useConfigStore } from '@/store'
import { clearServerBaseUrlOverride, getEnvBaseUrl, getServerBaseUrlOverride, setServerBaseUrlOverride } from '@/utils' import { clearServerBaseUrlOverride, getEnvBaseUrl, getServerBaseUrlOverride, setServerBaseUrlOverride } from '@/utils'
@@ -265,7 +265,7 @@ function showAbout() {
title: t('settings.aboutApp', { appName: import.meta.env.VITE_APP_TITLE }), title: t('settings.aboutApp', { appName: import.meta.env.VITE_APP_TITLE }),
content: t('settings.aboutContent', { content: t('settings.aboutContent', {
appName: import.meta.env.VITE_APP_TITLE, appName: import.meta.env.VITE_APP_TITLE,
version: '0.9.5' version: '0.9.5',
}), }),
showCancel: false, showCancel: false,
confirmText: t('common.confirm'), confirmText: t('common.confirm'),
@@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { ChatHistory, CreateSpeakerData, VoicePrint } from '@/api/voiceprint' import type { ChatHistory, CreateSpeakerData, VoicePrint } from '@/api/voiceprint'
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
import { useMessage } from 'wot-design-uni' import { useMessage } from 'wot-design-uni/components/wd-message-box'
import { useToast } from 'wot-design-uni/components/wd-toast' import { useToast } from 'wot-design-uni/components/wd-toast'
import { createVoicePrint, deleteVoicePrint, getAudioDownloadId, getChatHistory, getVoicePrintList, updateVoicePrint } from '@/api/voiceprint' import { createVoicePrint, deleteVoicePrint, getAudioDownloadId, getChatHistory, getVoicePrintList, updateVoicePrint } from '@/api/voiceprint'
import { t } from '@/i18n' import { t } from '@/i18n'
+1 -1
View File
@@ -1,7 +1,7 @@
import type { PublicConfig } from '@/api/auth' import type { PublicConfig } from '@/api/auth'
import { getPublicConfig } from '@/api/auth'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue' import { ref } from 'vue'
import { getPublicConfig } from '@/api/auth'
// 初始化状态 // 初始化状态
const initialConfigState: PublicConfig = { const initialConfigState: PublicConfig = {
+4 -3
View File
@@ -1,11 +1,12 @@
import { ref } from 'vue' import type { Ref } from 'vue'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue'
// 支持的语言类型 // 支持的语言类型
export type Language = 'zh_CN' | 'en' | 'zh_TW' | 'de' | 'vi' | 'pt_BR' export type Language = 'zh_CN' | 'en' | 'zh_TW' | 'de' | 'vi' | 'pt_BR'
export interface LangStore { export interface LangStore {
currentLang: Language currentLang: Ref<Language>
changeLang: (lang: Language) => void changeLang: (lang: Language) => void
} }
@@ -37,4 +38,4 @@ export const useLangStore = defineStore(
}, },
}, },
}, },
) )
+1 -1
View File
@@ -201,7 +201,7 @@ export function getEnvBaseUploadUrl() {
/** /**
* 生成SM2密钥对(十六进制格式) * 生成SM2密钥对(十六进制格式)
* @returns {Object} 包含公钥和私钥的对象 * @returns {object} 包含公钥和私钥的对象
*/ */
export function generateSm2KeyPairHex() { export function generateSm2KeyPairHex() {
// 使用sm-crypto库生成SM2密钥对 // 使用sm-crypto库生成SM2密钥对
+30
View File
@@ -0,0 +1,30 @@
import type { Component } from 'vue'
// wot-design-uni 1.9.1 exposes raw Vue source through its global declarations.
// Keep the components used by this app recognizable without pulling that source into vue-tsc.
declare module 'vue' {
export interface GlobalComponents {
WdActionSheet: Component
WdButton: Component
WdCheckbox: Component
WdConfigProvider: Component
WdFab: Component
WdIcon: Component
WdImg: Component
WdInput: Component
WdLoading: Component
WdMessageBox: Component
WdNavbar: Component
WdPicker: Component
WdPopup: Component
WdSegmented: Component
WdSlider: Component
WdStatusTip: Component
WdSwipeAction: Component
WdSwitch: Component
WdTabbar: Component
WdTabbarItem: Component
WdTag: Component
WdToast: Component
}
}
-1
View File
@@ -14,7 +14,6 @@
"@dcloudio/types", "@dcloudio/types",
"@uni-helper/uni-types", "@uni-helper/uni-types",
"@types/wechat-miniprogram", "@types/wechat-miniprogram",
"wot-design-uni/global.d.ts",
"z-paging/types", "z-paging/types",
"./src/typings.d.ts" "./src/typings.d.ts"
], ],