🎈 perf: comprehensive dashboard improvements

- Enhance i18n error handling and code quality - Fix SSE data processing in chat page - Improve responsive design for extension page - Add better debugging tools for development"
This commit is contained in:
IGCrystal
2025-06-16 21:05:20 +08:00
parent 9d7ad7a18f
commit 96b565e1e8
4 changed files with 84 additions and 66 deletions
+5 -3
View File
@@ -57,13 +57,15 @@ export function useI18n() {
value = value[k];
} else {
console.warn(`Translation key not found: ${key}`);
return key; // 返回键名作为回退
// 返回带括号的键名,便于在开发时识别缺失的翻译
return `[MISSING: ${key}]`;
}
}
if (typeof value !== 'string') {
console.warn(`Translation value is not string: ${key}`);
return key;
console.warn(`Translation value is not string: ${key}`, value);
// 返回带括号的键名,便于在开发时识别类型错误的翻译
return `[INVALID: ${key}]`;
}
// 此时value确定是string类型
+23 -48
View File
@@ -127,70 +127,45 @@ export class I18nLoader {
}
}
/**
* 通用模块加载器 - 减少重复代码,提高可维护性
*/
private async loadModules(
locale: string,
prefix: string,
overrideList: string[] = []
): Promise<any> {
// 使用覆盖列表或从注册表中筛选符合前缀的模块名
const moduleNames = overrideList.length > 0
? overrideList
: Array.from(this.moduleRegistry.keys()).filter(key => key.startsWith(prefix));
const results = await Promise.all(
moduleNames.map(module => this.loadModule(locale, module))
);
return this.mergeModules(results, moduleNames);
}
/**
* 加载核心模块(最高优先级)
*/
async loadCoreModules(locale: string): Promise<any> {
const coreModules = [
'core/common',
'core/actions',
'core/status',
'core/navigation',
'core/header'
];
const results = await Promise.all(
coreModules.map(module => this.loadModule(locale, module))
);
return this.mergeModules(results, coreModules);
return this.loadModules(locale, 'core');
}
/**
* 加载功能模块
*/
async loadFeatureModules(locale: string, features?: string[]): Promise<any> {
const featureModules = features || [
'features/chat',
'features/extension',
'features/conversation',
'features/tooluse',
'features/provider',
'features/platform',
'features/config',
'features/console',
'features/about',
'features/settings',
'features/auth',
'features/chart',
'features/dashboard',
'features/alkaid/index',
'features/alkaid/knowledge-base',
'features/alkaid/memory'
];
const results = await Promise.all(
featureModules.map(module => this.loadModule(locale, module))
);
return this.mergeModules(results, featureModules);
return this.loadModules(locale, 'features', features || []);
}
/**
* 加载消息模块
*/
async loadMessageModules(locale: string): Promise<any> {
const messageModules = [
'messages/errors',
'messages/success',
'messages/validation'
];
const results = await Promise.all(
messageModules.map(module => this.loadModule(locale, module))
);
return this.mergeModules(results, messageModules);
return this.loadModules(locale, 'messages');
}
/**
+19 -1
View File
@@ -594,7 +594,25 @@ export default {
console.log(line)
// data: {"type": "plain", "data": "helloworld"}
let chunk_json = JSON.parse(line.replace('data: ', ''));
let chunk_json;
try {
chunk_json = JSON.parse(line.replace('data: ', ''));
} catch (parseError) {
console.warn('JSON解析失败:', line, parseError);
continue;
}
// 检查解析后的数据是否有效
if (!chunk_json || typeof chunk_json !== 'object') {
console.warn('无效的数据对象:', chunk_json);
continue;
}
// 检查是否有type字段
if (!chunk_json.hasOwnProperty('type')) {
console.warn('数据缺少type字段:', chunk_json);
continue;
}
if (chunk_json.type === 'heartbeat') {
continue; // 心跳包
+37 -14
View File
@@ -568,8 +568,10 @@ onMounted(async () => {
<!-- 标签页 -->
<v-card-text>
<div class="d-flex align-center mb-2" style="justify-content: space-between;">
<v-tabs v-model="activeTab" color="primary" class="mb-4">
<!-- 标签栏和搜索栏 - 响应式布局 -->
<div class="mb-4">
<!-- 标签栏 -->
<v-tabs v-model="activeTab" color="primary" class="mb-3">
<v-tab value="installed">
<v-icon class="mr-2">mdi-puzzle</v-icon>
{{ tm('tabs.installed') }}
@@ -580,20 +582,40 @@ onMounted(async () => {
</v-tab>
</v-tabs>
<v-text-field v-if="activeTab == 'market'" style="max-width: 300px;" v-model="marketSearch" density="compact"
:label="tm('search.marketPlaceholder')" prepend-inner-icon="mdi-magnify" variant="solo-filled" flat hide-details
single-line></v-text-field>
<v-text-field v-else style="max-width: 300px;" v-model="pluginSearch" density="compact" :label="tm('search.placeholder')" prepend-inner-icon="mdi-magnify"
variant="solo-filled" flat hide-details single-line></v-text-field>
<!-- 搜索栏 - 在移动端时独占一行 -->
<v-row class="mb-2">
<v-col cols="12" sm="6" md="4" lg="3">
<v-text-field
v-if="activeTab == 'market'"
v-model="marketSearch"
density="compact"
:label="tm('search.marketPlaceholder')"
prepend-inner-icon="mdi-magnify"
variant="solo-filled"
flat
hide-details
single-line>
</v-text-field>
<v-text-field
v-else
v-model="pluginSearch"
density="compact"
:label="tm('search.placeholder')"
prepend-inner-icon="mdi-magnify"
variant="solo-filled"
flat
hide-details
single-line>
</v-text-field>
</v-col>
</v-row>
</div>
<!-- 已安装插件标签页内容 -->
<v-tab-item v-show="activeTab === 'installed'">
<v-row class="mb-4">
<v-col cols="12" sm="6" md="6" class="d-flex align-center">
<v-col cols="12" class="d-flex align-center flex-wrap ga-2">
<v-btn-group variant="outlined" density="comfortable" color="primary">
<v-btn @click="isListView = false" :color="!isListView ? 'primary' : undefined"
:variant="!isListView ? 'flat' : 'outlined'">
@@ -605,13 +627,14 @@ onMounted(async () => {
</v-btn>
</v-btn-group>
<v-btn class="ml-2" @click="toggleShowReserved" prepend-icon="mdi-eye-settings-outline"
:color="showReserved ? 'primary' : undefined" :variant="showReserved ? 'flat' : 'outlined'">
<v-btn @click="toggleShowReserved" prepend-icon="mdi-eye-settings-outline"
:color="showReserved ? 'primary' : undefined" :variant="showReserved ? 'flat' : 'outlined'"
class="flex-shrink-0">
{{ showReserved ? tm('buttons.hideSystemPlugins') : tm('buttons.showSystemPlugins') }}
</v-btn>
<v-btn class="ml-2" prepend-icon="mdi-tune-vertical" color="primary" variant="outlined"
@click="getPlatformEnableConfig">
<v-btn prepend-icon="mdi-tune-vertical" color="primary" variant="outlined"
@click="getPlatformEnableConfig" class="flex-shrink-0">
{{ tm('buttons.platformConfig') }}
</v-btn>
</v-col>