-
控制台
+
{{ tm('title') }}
- 安装 pip 库
+ {{ tm('pipInstall.button') }}
- 安装 Pip 库
+ {{ tm('pipInstall.dialogTitle') }}
-
-
- 强制 PyPI 软件仓库链接 > 配置项 `PyPI 软件仓库地址`
+
+
+ {{ tm('pipInstall.mirrorHint') }}
{{ status }}
@@ -37,7 +39,7 @@ import axios from 'axios';
- 安装
+ {{ tm('pipInstall.installButton') }}
diff --git a/dashboard/src/views/ConversationPage.vue b/dashboard/src/views/ConversationPage.vue
index c82daabff..1c5695bdd 100644
--- a/dashboard/src/views/ConversationPage.vue
+++ b/dashboard/src/views/ConversationPage.vue
@@ -5,10 +5,10 @@
- mdi-chat-processing对话管理
+ mdi-chat-processing{{ tm('title') }}
- 管理和查看用户对话历史记录
+ {{ tm('subtitle') }}
@@ -17,10 +17,10 @@
mdi-filter-variant
- 筛选条件
+ {{ tm('filters.title') }}
- mdi-refresh重置
+ mdi-refresh{{ tm('filters.reset') }}
@@ -29,7 +29,7 @@
-
@@ -40,7 +40,7 @@
-
-
@@ -63,32 +63,32 @@
mdi-message
- 对话历史
+ {{ tm('history.title') }}
{{ pagination.total || 0 }}
- 刷新
+ {{ tm('history.refresh') }}
-
mdi-chat
- {{ item.title || '无标题对话' }}
+ {{ item.title || tm('status.noTitle') }}
- {{ item.sessionInfo.platform || '未知' }}
+ {{ item.sessionInfo.platform || tm('status.unknown') }}
@@ -100,7 +100,7 @@
- {{ item.sessionInfo.sessionId || '未知' }}
+ {{ item.sessionInfo.sessionId || tm('status.unknown') }}
@@ -115,15 +115,15 @@
- mdi-eye查看
+ mdi-eye{{ tm('actions.view') }}
- mdi-pencil编辑
+ mdi-pencil{{ tm('actions.edit') }}
- mdi-delete删除
+ mdi-delete{{ tm('actions.delete') }}
@@ -131,7 +131,7 @@
mdi-chat-remove
- 暂无对话记录
+ {{ tm('status.noData') }}
@@ -150,7 +150,7 @@
mdi-eye
- {{ selectedConversation?.title || '无标题对话' }}
+ {{ selectedConversation?.title || tm('status.noTitle') }}
@@ -170,12 +170,12 @@
{{ isEditingHistory ? 'mdi-eye' : 'mdi-pencil' }}
- {{ isEditingHistory ? '预览模式' : '编辑对话' }}
+ {{ isEditingHistory ? tm('dialogs.view.previewMode') : tm('dialogs.view.editMode') }}
mdi-content-save
- 保存修改
+ {{ tm('dialogs.view.saveChanges') }}
@@ -196,7 +196,7 @@
mdi-chat-remove
-
对话内容为空
+
{{ tm('status.emptyContent') }}
@@ -219,7 +219,7 @@
@@ -247,7 +247,7 @@
- 关闭
+ {{ tm('dialogs.view.close') }}
@@ -258,12 +258,12 @@
mdi-pencil
- 编辑对话信息
+ {{ tm('dialogs.edit.title') }}
-
@@ -273,10 +273,10 @@
- 取消
+ {{ tm('dialogs.edit.cancel') }}
- 保存
+ {{ tm('dialogs.edit.save') }}
@@ -287,11 +287,11 @@
mdi-alert
- 确认删除
+ {{ tm('dialogs.delete.title') }}
- 确定要删除对话 {{ selectedConversation?.title || '无标题对话' }} 吗?此操作不可恢复。
+ {{ tm('dialogs.delete.message', { title: selectedConversation?.title || tm('status.noTitle') }) }}
@@ -299,10 +299,10 @@
- 取消
+ {{ tm('dialogs.delete.cancel') }}
- 删除
+ {{ tm('dialogs.delete.confirm') }}
@@ -320,6 +320,7 @@ import axios from 'axios';
import { VueMonacoEditor } from '@guolao/vue-monaco-editor';
import { marked } from 'marked';
import { useCommonStore } from '@/stores/common';
+import { useI18n, useModuleI18n } from '@/i18n/composables';
marked.setOptions({
breaks: true
@@ -331,20 +332,23 @@ export default {
VueMonacoEditor
},
+ setup() {
+ const { t, locale } = useI18n();
+ const { tm } = useModuleI18n('features/conversation');
+
+ return {
+ t,
+ tm,
+ locale
+ };
+ },
+
data() {
return {
// 表格数据
conversations: [],
search: '',
- headers: [
- { title: '对话标题', key: 'title', sortable: true },
- { title: '平台', key: 'platform', sortable: true, width: '120px' },
- { title: '类型', key: 'messageType', sortable: true, width: '100px' },
- { title: 'ID', key: 'sessionId', sortable: true, width: '100px' },
- { title: '创建时间', key: 'created_at', sortable: true, width: '180px' },
- { title: '更新时间', key: 'updated_at', sortable: true, width: '180px' },
- { title: '操作', key: 'actions', sortable: false, align: 'center', width: '240px' }
- ],
+ headers: [],
// 筛选条件
platformFilter: [],
@@ -443,6 +447,19 @@ export default {
},
computed: {
+ // 动态表头
+ tableHeaders() {
+ return [
+ { title: this.tm('table.headers.title'), key: 'title', sortable: true },
+ { title: this.tm('table.headers.platform'), key: 'platform', sortable: true, width: '120px' },
+ { title: this.tm('table.headers.type'), key: 'messageType', sortable: true, width: '100px' },
+ { title: this.tm('table.headers.sessionId'), key: 'sessionId', sortable: true, width: '100px' },
+ { title: this.tm('table.headers.createdAt'), key: 'created_at', sortable: true, width: '180px' },
+ { title: this.tm('table.headers.updatedAt'), key: 'updated_at', sortable: true, width: '180px' },
+ { title: this.tm('table.headers.actions'), key: 'actions', sortable: false, align: 'center', width: '240px' }
+ ];
+ },
+
// 可用平台列表
availablePlatforms() {
const platforms = []
@@ -462,8 +479,8 @@ export default {
// 可用消息类型列表
messageTypeItems() {
return [
- { title: '群聊', value: 'GroupMessage' },
- { title: '私聊', value: 'FriendMessage' },
+ { title: this.tm('messageTypes.group'), value: 'GroupMessage' },
+ { title: this.tm('messageTypes.friend'), value: 'FriendMessage' },
];
},
@@ -492,7 +509,7 @@ export default {
this.fetchConversations();
},
- _methods: {
+ methods: {
// Monaco编辑器挂载后的回调
onMonacoMounted(editor) {
this.monacoEditor = editor;
@@ -572,9 +589,9 @@ export default {
// 获取消息类型的显示文本
getMessageTypeDisplay(messageType) {
const typeMap = {
- 'GroupMessage': '群聊',
- 'FriendMessage': '私聊',
- 'default': '未知'
+ 'GroupMessage': this.tm('messageTypes.group'),
+ 'FriendMessage': this.tm('messageTypes.friend'),
+ 'default': this.tm('messageTypes.unknown')
};
return typeMap[messageType] || typeMap.default;
@@ -620,7 +637,7 @@ export default {
if (!data || !data.conversations) {
console.error('API 返回数据格式不符合预期:', data);
- this.showErrorMessage('API 返回数据格式不符合预期');
+ this.showErrorMessage(this.tm('messages.fetchError'));
return;
}
@@ -643,7 +660,7 @@ export default {
console.warn('API 响应中没有分页信息');
}
} else {
- this.showErrorMessage(response.data.message || '获取对话列表失败');
+ this.showErrorMessage(response.data.message || this.tm('messages.fetchError'));
}
} catch (error) {
console.error('获取对话列表出错:', error);
@@ -651,7 +668,7 @@ export default {
console.error('错误响应数据:', error.response.data);
console.error('错误状态码:', error.response.status);
}
- this.showErrorMessage(error.response?.data?.message || error.message || '获取对话列表失败');
+ this.showErrorMessage(error.response?.data?.message || error.message || this.tm('messages.fetchError'));
} finally {
// this.loading = false;
setTimeout(() => {
@@ -685,11 +702,11 @@ export default {
}
this.dialogView = true;
} else {
- this.showErrorMessage(response.data.message || '获取对话详情失败');
+ this.showErrorMessage(response.data.message || this.tm('messages.historyError'));
}
} catch (error) {
console.error('获取对话详情出错:', error);
- this.showErrorMessage(error.response?.data?.message || error.message || '获取对话详情失败');
+ this.showErrorMessage(error.response?.data?.message || error.message || this.tm('messages.historyError'));
} finally {
this.loading = false;
}
@@ -707,7 +724,7 @@ export default {
try {
historyJson = JSON.parse(this.editedHistory);
} catch (e) {
- this.showErrorMessage('JSON格式错误,请检查您的输入');
+ this.showErrorMessage(this.tm('messages.invalidJson'));
return;
}
@@ -719,14 +736,14 @@ export default {
if (response.data.status === "ok") {
this.conversationHistory = historyJson;
- this.showSuccessMessage('对话历史更新成功');
+ this.showSuccessMessage(this.tm('messages.historySaveSuccess'));
this.isEditingHistory = false;
} else {
- this.showErrorMessage(response.data.message || '更新对话历史失败');
+ this.showErrorMessage(response.data.message || this.tm('messages.historySaveError'));
}
} catch (error) {
console.error('更新对话历史出错:', error);
- this.showErrorMessage(error.response?.data?.message || error.message || '更新对话历史失败');
+ this.showErrorMessage(error.response?.data?.message || error.message || this.tm('messages.historySaveError'));
} finally {
this.savingHistory = false;
}
@@ -735,7 +752,7 @@ export default {
// 关闭对话历史对话框
closeHistoryDialog() {
if (this.isEditingHistory) {
- if (confirm('您有未保存的更改,确定要关闭吗?')) {
+ if (confirm(this.tm('dialogs.view.confirmClose'))) {
this.dialogView = false;
}
} else {
@@ -772,15 +789,15 @@ export default {
}
this.dialogEdit = false;
- this.showSuccessMessage('对话信息更新成功');
+ this.showSuccessMessage(this.tm('messages.saveSuccess'));
// 刷新数据
this.fetchConversations();
} else {
- this.showErrorMessage(response.data.message || '更新对话信息失败');
+ this.showErrorMessage(response.data.message || this.tm('messages.saveError'));
}
} catch (error) {
- this.showErrorMessage(error.response?.data?.message || error.message || '更新对话信息失败');
+ this.showErrorMessage(error.response?.data?.message || error.message || this.tm('messages.saveError'));
} finally {
this.loading = false;
}
@@ -810,12 +827,12 @@ export default {
}
this.dialogDelete = false;
- this.showSuccessMessage('对话删除成功');
+ this.showSuccessMessage(this.tm('messages.deleteSuccess'));
} else {
- this.showErrorMessage(response.data.message || '删除对话失败');
+ this.showErrorMessage(response.data.message || this.tm('messages.deleteError'));
}
} catch (error) {
- this.showErrorMessage(error.response?.data?.message || error.message || '删除对话失败');
+ this.showErrorMessage(error.response?.data?.message || error.message || this.tm('messages.deleteError'));
} finally {
this.loading = false;
}
@@ -823,10 +840,11 @@ export default {
// 格式化时间戳
formatTimestamp(timestamp) {
- if (!timestamp) return '未知时间';
+ if (!timestamp) return this.tm('status.unknown');
const date = new Date(timestamp * 1000);
- return new Intl.DateTimeFormat('zh-CN', {
+ const locale = this.locale || 'zh-CN';
+ return new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
@@ -860,7 +878,7 @@ export default {
} else if (typeof content === 'string') {
// 处理字符串内容
final_content = content;
- } else if (!final_content) return '空消息';
+ } else if (!final_content) return this.tm('status.emptyContent');
// 使用marked处理Markdown格式
return marked(final_content);
},
@@ -878,13 +896,7 @@ export default {
this.messageType = 'error';
this.showMessage = true;
}
- },
- get methods() {
- return this._methods;
- },
- set methods(value) {
- this._methods = value;
- },
+ }
}
diff --git a/dashboard/src/views/ExtensionPage.vue b/dashboard/src/views/ExtensionPage.vue
index 681f66a59..89f722101 100644
--- a/dashboard/src/views/ExtensionPage.vue
+++ b/dashboard/src/views/ExtensionPage.vue
@@ -5,11 +5,14 @@ import ConsoleDisplayer from '@/components/shared/ConsoleDisplayer.vue';
import ReadmeDialog from '@/components/shared/ReadmeDialog.vue';
import axios from 'axios';
import { useCommonStore } from '@/stores/common';
+import { useI18n, useModuleI18n } from '@/i18n/composables';
import { ref, computed, onMounted, reactive } from 'vue';
const commonStore = useCommonStore();
+const { t } = useI18n();
+const { tm } = useModuleI18n('features/extension');
const activeTab = ref('installed');
const extension_data = reactive({
data: [],
@@ -25,12 +28,12 @@ const extension_config = reactive({
config: {}
});
const pluginMarketData = ref([]);
-const loadingDialog = reactive({
- show: false,
- title: "加载中...",
- statusCode: 0, // 0: loading, 1: success, 2: error,
- result: ""
-});
+ const loadingDialog = reactive({
+ show: false,
+ title: "",
+ statusCode: 0, // 0: loading, 1: success, 2: error,
+ result: ""
+ });
const showPluginInfoDialog = ref(false);
const selectedPlugin = ref({});
const curr_namespace = ref("");
@@ -62,34 +65,34 @@ const showPluginFullName = ref(false);
const marketSearch = ref("");
const filterKeys = ['name', 'desc', 'author'];
-const plugin_handler_info_headers = [
- { title: '行为类型', key: 'event_type_h' },
- { title: '描述', key: 'desc', maxWidth: '250px' },
- { title: '具体类型', key: 'type' },
- { title: '触发方式', key: 'cmd' },
-];
+const plugin_handler_info_headers = computed(() => [
+ { title: tm('table.headers.eventType'), key: 'event_type_h' },
+ { title: tm('table.headers.description'), key: 'desc', maxWidth: '250px' },
+ { title: tm('table.headers.specificType'), key: 'type' },
+ { title: tm('table.headers.trigger'), key: 'cmd' },
+]);
// 插件表格的表头定义
-const pluginHeaders = [
- { title: '名称', key: 'name', width: '200px' },
- { title: '描述', key: 'desc', maxWidth: '250px' },
- { title: '版本', key: 'version', width: '100px' },
- { title: '作者', key: 'author', width: '100px' },
- { title: '状态', key: 'status', width: '80px' },
- { title: '操作', key: 'actions', sortable: false, width: '220px' }
-];
+const pluginHeaders = computed(() => [
+ { title: tm('table.headers.name'), key: 'name', width: '200px' },
+ { title: tm('table.headers.description'), key: 'desc', maxWidth: '250px' },
+ { title: tm('table.headers.version'), key: 'version', width: '100px' },
+ { title: tm('table.headers.author'), key: 'author', width: '100px' },
+ { title: tm('table.headers.status'), key: 'status', width: '80px' },
+ { title: tm('table.headers.actions'), key: 'actions', sortable: false, width: '220px' }
+]);
// 插件市场表头
-const pluginMarketHeaders = [
- { title: '名称', key: 'name', maxWidth: '200px' },
- { title: '描述', key: 'desc', maxWidth: '250px' },
- { title: '作者', key: 'author', maxWidth: '90px' },
- { title: 'Star数', key: 'stars', maxWidth: '80px' },
- { title: '最近更新', key: 'updated_at', maxWidth: '100px' },
- { title: '标签', key: 'tags', maxWidth: '100px' },
- { title: '操作', key: 'actions', sortable: false }
-];
+const pluginMarketHeaders = computed(() => [
+ { title: tm('table.headers.name'), key: 'name', maxWidth: '200px' },
+ { title: tm('table.headers.description'), key: 'desc', maxWidth: '250px' },
+ { title: tm('table.headers.author'), key: 'author', maxWidth: '90px' },
+ { title: tm('table.headers.stars'), key: 'stars', maxWidth: '80px' },
+ { title: tm('table.headers.lastUpdate'), key: 'updated_at', maxWidth: '100px' },
+ { title: tm('table.headers.tags'), key: 'tags', maxWidth: '100px' },
+ { title: tm('table.headers.actions'), key: 'actions', sortable: false }
+]);
// 过滤要显示的插件
@@ -131,7 +134,7 @@ const toast = (message, success) => {
const resetLoadingDialog = () => {
loadingDialog.show = false;
- loadingDialog.title = "加载中...";
+ loadingDialog.title = tm('dialogs.loading.title');
loadingDialog.statusCode = 0;
loadingDialog.result = "";
};
@@ -175,8 +178,8 @@ const checkUpdate = () => {
if (matchedPlugin) {
extension.online_version = matchedPlugin.version;
- extension.has_update = extension.version !== matchedPlugin.version &&
- matchedPlugin.version !== "未知";
+ extension.has_update = extension.version !== matchedPlugin.version &&
+ matchedPlugin.version !== tm('status.unknown');
} else {
extension.has_update = false;
}
@@ -185,7 +188,7 @@ const checkUpdate = () => {
};
const uninstallExtension = async (extension_name) => {
- toast("正在卸载" + extension_name, "primary");
+ toast(tm('messages.uninstalling') + " " + extension_name, "primary");
try {
const res = await axios.post('/api/plugin/uninstall', { name: extension_name });
if (res.data.status === "error") {
@@ -201,6 +204,7 @@ const uninstallExtension = async (extension_name) => {
};
const updateExtension = async (extension_name) => {
+ loadingDialog.title = tm('status.loading');
loadingDialog.show = true;
try {
const res = await axios.post('/api/plugin/update', {
@@ -216,14 +220,14 @@ const updateExtension = async (extension_name) => {
Object.assign(extension_data, res.data);
onLoadingDialogResult(1, res.data.message);
setTimeout(async () => {
- toast(`正在刷新插件列表...`, "info", 2000);
+ toast(tm('messages.refreshing'), "info", 2000);
try {
await getExtensions();
- toast("插件列表已刷新!", "success");
+ toast(tm('messages.refreshSuccess'), "success");
} catch (error) {
const errorMsg = error.response?.data?.message || error.message || String(error);
- toast(`刷新插件列表时发生错误: ${errorMsg}`, "error");
+ toast(`${tm('messages.refreshFailed')}: ${errorMsg}`, "error");
}
}, 1000);
} catch (err) {
@@ -301,7 +305,7 @@ const reloadPlugin = async (plugin_name) => {
toast(res.data.message, "error");
return;
}
- toast("重载成功", "success");
+ toast(tm('messages.reloadSuccess'), "success");
getExtensions();
} catch (err) {
toast(err, "error");
@@ -330,7 +334,7 @@ const getPlatformEnableConfig = async () => {
// 如果没有平台,给出提示但仍显示对话框
if (platformEnableData.platforms.length === 0) {
- toast("未添加任何平台适配器,请先在平台管理中添加平台", "warning");
+ toast(tm('dialogs.platformConfig.noAdaptersDesc'), "warning");
} else {
// 确保每个平台都有一个配置对象
platformEnableData.platforms.forEach(platform => {
@@ -349,7 +353,7 @@ const getPlatformEnableConfig = async () => {
platformEnableDialog.value = true;
} catch (err) {
- toast("获取平台插件配置失败: " + err, "error");
+ toast(tm('messages.getPlatformConfigFailed') + " " + err, "error");
} finally {
loadingPlatformData.value = false;
}
@@ -371,7 +375,7 @@ const savePlatformEnableConfig = async () => {
toast(res.data.message, "success");
platformEnableDialog.value = false;
} catch (err) {
- toast("保存平台插件配置失败: " + err, "error");
+ toast(tm('messages.savePlatformConfigFailed') + " " + err, "error");
} finally {
loadingPlatformData.value = false;
}
@@ -452,18 +456,19 @@ const checkAlreadyInstalled = () => {
const newExtension = async () => {
if (extension_url.value === "" && upload_file.value === null) {
- toast("请填写插件链接或上传插件文件", "error");
+ toast(tm('messages.fillUrlOrFile'), "error");
return;
}
if (extension_url.value !== "" && upload_file.value !== null) {
- toast("请不要同时填写插件链接和上传插件文件", "error");
+ toast(tm('messages.dontFillBoth'), "error");
return;
}
loading_.value = true;
+ loadingDialog.title = tm('status.loading');
loadingDialog.show = true;
if (upload_file.value !== null) {
- toast("正在从文件安装插件", "primary");
+ toast(tm('messages.installing'), "primary");
const formData = new FormData();
formData.append('file', upload_file.value);
axios.post('/api/plugin/install-upload', formData, {
@@ -490,7 +495,7 @@ const newExtension = async () => {
onLoadingDialogResult(2, err, -1);
});
} else {
- toast("正在从链接 " + extension_url.value + " 安装插件...", "primary");
+ toast(tm('messages.installingFromUrl') + " " + extension_url.value, "primary");
axios.post('/api/plugin/install',
{
url: extension_url.value,
@@ -513,7 +518,7 @@ const newExtension = async () => {
});
}).catch((err) => {
loading_.value = false;
- toast("安装插件失败: " + err, "error");
+ toast(tm('messages.installFailed') + " " + err, "error");
onLoadingDialogResult(2, err, -1);
});
}
@@ -537,7 +542,7 @@ onMounted(async () => {
checkAlreadyInstalled();
checkUpdate();
} catch (err) {
- console.error("获取插件市场数据失败:", err);
+ toast(tm('messages.getMarketDataFailed') + " " + err, "error");
}
});
@@ -555,42 +560,64 @@ onMounted(async () => {