feat: 继续完成剩下组件的国际化

ExtensionCard.vue - 插件卡片组件 WaitingForRestart.vue - 重启等待组件 ReadmeDialog.vue - README对话框组件 AstrBotConfig.vue - 配置编辑器组件 ListConfigItem.vue - 列表配置项组件 ItemCardGrid.vue - 卡片网格组件
ChatPage.vue - 聊天页面的录音提示文本 ConfigPage.vue - 配置页面的状态消息 ExtensionPage.vue - 插件页面的加载和状态文本 OnlineTime.vue - 仪表板运行时间组件
This commit is contained in:
IGCrystal
2025-06-16 22:44:44 +08:00
parent 7cfcf056f9
commit 7c27520d57
18 changed files with 259 additions and 69 deletions
@@ -107,7 +107,7 @@ function saveEditedContent() {
color="primary"
class="editor-fullscreen-btn"
@click="openEditorDialog(key, iterable, metadata[metadataKey].items[key]?.editor_theme, metadata[metadataKey].items[key]?.editor_language)"
title="全屏编辑"
:title="t('core.common.editor.fullscreen')"
>
<v-icon>mdi-fullscreen</v-icon>
</v-btn>
@@ -288,10 +288,10 @@ function saveEditedContent() {
<v-btn icon @click="dialog = false">
<v-icon>mdi-close</v-icon>
</v-btn>
<v-toolbar-title>编辑内容 - {{ currentEditingKey }}</v-toolbar-title>
<v-toolbar-title>{{ t('core.common.editor.editingTitle') }} - {{ currentEditingKey }}</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items>
<v-btn variant="text" @click="saveEditedContent">保存</v-btn>
<v-btn variant="text" @click="saveEditedContent">{{ t('core.common.save') }}</v-btn>
</v-toolbar-items>
</v-toolbar>
<v-card-text class="pa-0">
@@ -309,12 +309,17 @@ function saveEditedContent() {
<script>
import ListConfigItem from './ListConfigItem.vue';
import { useI18n } from '@/i18n/composables';
export default {
name: 'AstrBotConfig',
components: {
ListConfigItem
},
setup() {
const { t } = useI18n();
return { t };
},
props: {
metadata: {
type: Object,
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { ref, computed, inject } from 'vue';
import {useCustomizerStore} from "@/stores/customizer";
import { useModuleI18n } from '@/i18n/composables';
const props = defineProps({
extension: {
@@ -31,6 +32,9 @@ const emit = defineEmits([
const reveal = ref(false);
// 国际化
const { tm } = useModuleI18n('features/extension');
// 操作函数
const configure = () => {
emit('configure', props.extension);
@@ -47,13 +51,13 @@ const reloadExtension = () => {
const $confirm = inject("$confirm");
const uninstallExtension = async () => {
if (typeof $confirm !== "function") {
console.error("$confirm 未正确注册");
console.error(tm("card.errors.confirmNotRegistered"));
return;
}
const confirmed = await $confirm({
title: "删除确认",
message: "你确定要删除当前插件吗?",
title: tm("dialogs.uninstall.title"),
message: tm("dialogs.uninstall.message"),
});
if (confirmed) {
@@ -90,13 +94,13 @@ const viewReadme = () => {
<template v-slot:activator="{ props: tooltipProps }">
<v-icon v-bind="tooltipProps" color="warning" class="ml-2" icon="mdi-update" size="small"></v-icon>
</template>
<span>有新版本可用: {{ extension.online_version }}</span>
<span>{{ tm("card.status.hasUpdate") }}: {{ extension.online_version }}</span>
</v-tooltip>
<v-tooltip location="top" v-if="!extension.activated && !marketMode">
<template v-slot:activator="{ props: tooltipProps }">
<v-icon v-bind="tooltipProps" color="error" class="ml-2" icon="mdi-cancel" size="small"></v-icon>
</template>
<span>该插件已经被禁用</span>
<span>{{ tm("card.status.disabled") }}</span>
</v-tooltip>
</p>
@@ -111,7 +115,7 @@ const viewReadme = () => {
</v-chip>
<v-chip color="primary" label size="small" class="ml-2" v-if="extension.handlers?.length">
<v-icon icon="mdi-cogs" start></v-icon>
{{ extension.handlers?.length }}个行为
{{ extension.handlers?.length }}{{ tm("card.status.handlersCount") }}
</v-chip>
</div>
@@ -127,16 +131,16 @@ const viewReadme = () => {
borderRadius: '8px',
objectFit: 'cover',
objectPosition: 'center'
}" alt="logo" />
}" :alt="tm('card.alt.logo')" />
</div>
</v-card-text>
<v-card-actions style="margin-left: 0px; gap: 2px;">
<v-btn color="teal-accent-4" text="查看文档" variant="text" @click="viewReadme"></v-btn>
<v-btn v-if="!marketMode" color="teal-accent-4" text="操作" variant="text" @click="reveal = true"></v-btn>
<v-btn v-if="marketMode && !extension?.installed" color="teal-accent-4" text="安装" variant="text"
<v-btn color="teal-accent-4" :text="tm('buttons.viewDocs')" variant="text" @click="viewReadme"></v-btn>
<v-btn v-if="!marketMode" color="teal-accent-4" :text="tm('buttons.actions')" variant="text" @click="reveal = true"></v-btn>
<v-btn v-if="marketMode && !extension?.installed" color="teal-accent-4" :text="tm('buttons.install')" variant="text"
@click="emit('install', extension)"></v-btn>
<v-btn v-if="marketMode && extension?.installed" color="teal-accent-4" text="已安装" variant="text" disabled></v-btn>
<v-btn v-if="marketMode && extension?.installed" color="teal-accent-4" :text="tm('status.installed')" variant="text" disabled></v-btn>
</v-card-actions>
<v-expand-transition v-if="!marketMode">
@@ -145,7 +149,7 @@ const viewReadme = () => {
<v-card-text style="overflow-y: auto;">
<div class="d-flex align-center mb-4">
<img v-if="extension.logo" :src="extension.logo"
style="height: 50px; width: 50px; border-radius: 8px; margin-right: 16px;" alt="扩展图标" />
style="height: 50px; width: 50px; border-radius: 8px; margin-right: 16px;" :alt="tm('card.alt.extensionIcon')" />
<h3>{{ extension.name }}</h3>
</div>
@@ -159,39 +163,39 @@ const viewReadme = () => {
}">
<v-btn prepend-icon="mdi-cog" color="primary" variant="tonal" @click="configure"
:block="$vuetify.display.xs">
插件配置
{{ tm("card.actions.pluginConfig") }}
</v-btn>
<v-btn prepend-icon="mdi-delete" color="error" variant="tonal" @click="uninstallExtension"
:block="$vuetify.display.xs">
卸载插件
{{ tm("card.actions.uninstallPlugin") }}
</v-btn>
<v-btn prepend-icon="mdi-reload" color="primary" variant="tonal" @click="reloadExtension"
:block="$vuetify.display.xs">
重载插件
{{ tm("card.actions.reloadPlugin") }}
</v-btn>
<v-btn :prepend-icon="extension.activated ? 'mdi-cancel' : 'mdi-check-circle'"
:color="extension.activated ? 'error' : 'success'" variant="tonal" @click="toggleActivation"
:block="$vuetify.display.xs">
{{ extension.activated ? '禁用' : '启用' }}插件
{{ extension.activated ? tm('buttons.disable') : tm('buttons.enable') }}{{ tm("card.actions.togglePlugin") }}
</v-btn>
<v-btn prepend-icon="mdi-cogs" color="info" variant="tonal" @click="viewHandlers"
:block="$vuetify.display.xs">
查看行为 ({{ extension.handlers.length }})
{{ tm("card.actions.viewHandlers") }} ({{ extension.handlers.length }})
</v-btn>
<v-btn prepend-icon="mdi-update" color="primary" variant="tonal" :disabled="!extension?.has_update "
@click="updateExtension" :block="$vuetify.display.xs">
更新到 {{ extension.online_version || extension.version }}
{{ tm("card.actions.updateTo") }} {{ extension.online_version || extension.version }}
</v-btn>
</div>
</v-card-text>
<v-card-actions class="pt-0 d-flex justify-center">
<v-btn color="teal-accent-4" text="返回" variant="text" @click="reveal = false"></v-btn>
<v-btn color="teal-accent-4" :text="tm('buttons.back')" variant="text" @click="reveal = false"></v-btn>
</v-card-actions>
</v-card>
</v-expand-transition>
@@ -3,7 +3,7 @@
<v-row v-if="items.length === 0">
<v-col cols="12" class="text-center pa-8">
<v-icon size="64" color="grey-lighten-1">{{ emptyIcon }}</v-icon>
<p class="text-grey mt-4">{{ emptyText }}</p>
<p class="text-grey mt-4">{{ displayEmptyText }}</p>
</v-col>
</v-row>
@@ -24,7 +24,7 @@
@update:model-value="toggleEnabled(item)"
></v-switch>
</template>
<span>{{ getItemEnabled(item) ? '已启用' : '已禁用' }}</span>
<span>{{ getItemEnabled(item) ? t('core.common.itemCard.enabled') : t('core.common.itemCard.disabled') }}</span>
</v-tooltip>
</v-card-title>
@@ -43,7 +43,7 @@
prepend-icon="mdi-delete"
@click="$emit('delete', item)"
>
删除
{{ t('core.common.itemCard.delete') }}
</v-btn>
<v-btn
variant="text"
@@ -52,7 +52,7 @@
prepend-icon="mdi-pencil"
@click="$emit('edit', item)"
>
编辑
{{ t('core.common.itemCard.edit') }}
</v-btn>
</v-card-actions>
</v-card>
@@ -62,8 +62,14 @@
</template>
<script>
import { useI18n } from '@/i18n/composables';
export default {
name: 'ItemCardGrid',
setup() {
const { t } = useI18n();
return { t };
},
props: {
items: {
type: Array,
@@ -83,10 +89,15 @@ export default {
},
emptyText: {
type: String,
default: '暂无数据'
default: null
}
},
emits: ['toggle-enabled', 'delete', 'edit'],
computed: {
displayEmptyText() {
return this.emptyText || this.t('core.common.itemCard.noData');
}
},
methods: {
getItemTitle(item) {
return item[this.titleField];
@@ -37,11 +37,11 @@
</v-list-item>
</v-list>
<div style="display: flex; align-items: center;">
<v-text-field v-model="newItem" label="添加新项,按回车确认添加" @keyup.enter="addItem" clearable dense hide-details
<v-text-field v-model="newItem" :label="t('core.common.list.addItemPlaceholder')" @keyup.enter="addItem" clearable dense hide-details
variant="outlined" density="compact"></v-text-field>
<v-btn @click="addItem" text variant="tonal">
<v-icon>mdi-plus</v-icon>
添加
{{ t('core.common.list.addButton') }}
</v-btn>
</div>
@@ -49,8 +49,14 @@
</template>
<script>
import { useI18n } from '@/i18n/composables';
export default {
name: 'ListConfigItem',
setup() {
const { t } = useI18n();
return { t };
},
props: {
value: {
type: Array,
@@ -1,9 +1,10 @@
<script setup>
import { ref, watch, onMounted } from 'vue';
import { ref, watch, onMounted, computed } from 'vue';
import axios from 'axios';
import { marked } from 'marked';
import hljs from 'highlight.js';
import 'highlight.js/styles/github.css';
import { useI18n } from '@/i18n/composables';
const props = defineProps({
show: {
@@ -22,6 +23,9 @@ const props = defineProps({
const emit = defineEmits(['update:show']);
// 国际化
const { t } = useI18n();
const content = ref(null);
const error = ref(null);
const loading = ref(false);
@@ -54,10 +58,10 @@ async function fetchReadme() {
if (res.data.status === 'ok') {
content.value = res.data.data.content;
} else {
error.value = res.data.message || '获取README失败';
error.value = res.data.message || t('core.common.readme.errors.fetchFailed');
}
} catch (err) {
error.value = err.message || '获取README时发生错误';
error.value = err.message || t('core.common.readme.errors.fetchError');
} finally {
loading.value = false;
}
@@ -99,13 +103,23 @@ function renderMarkdown(content) {
function refreshReadme() {
fetchReadme();
}
// 计算属性处理双向绑定
const _show = computed({
get() {
return props.show;
},
set(value) {
emit('update:show', value);
}
});
</script>
<template>
<v-dialog v-model="_show" width="800" persistent>
<v-card>
<v-card-title class="d-flex justify-space-between align-center">
<span class="text-h5">插件说明文档</span>
<span class="text-h5">{{ t('core.common.readme.title') }}</span>
<v-btn icon @click="$emit('update:show', false)">
<v-icon>mdi-close</v-icon>
</v-btn>
@@ -119,21 +133,21 @@ function refreshReadme() {
prepend-icon="mdi-github"
@click="openRepoInNewTab()"
>
GitHub中查看仓库
{{ t('core.common.readme.buttons.viewOnGithub') }}
</v-btn>
<v-btn
color="secondary"
prepend-icon="mdi-refresh"
@click="refreshReadme()"
>
刷新文档
{{ t('core.common.readme.buttons.refresh') }}
</v-btn>
</div>
<!-- 加载中 -->
<div v-if="loading" class="d-flex flex-column align-center justify-center" style="height: 100%;">
<v-progress-circular indeterminate color="primary" size="64" class="mb-4"></v-progress-circular>
<p class="text-body-1 text-center">正在加载README文档...</p>
<p class="text-body-1 text-center">{{ t('core.common.readme.loading') }}</p>
</div>
<!-- 内容显示 -->
@@ -148,14 +162,14 @@ function refreshReadme() {
<!-- 无内容提示 -->
<div v-else class="d-flex flex-column align-center justify-center" style="height: 100%;">
<v-icon size="64" color="warning" class="mb-4">mdi-file-question-outline</v-icon>
<p class="text-body-1 text-center mb-4">该插件未提供文档链接或GitHub仓库地址<br>请查看插件市场或联系插件作者获取更多信息</p>
<p class="text-body-1 text-center mb-4">{{ t('core.common.readme.empty.title') }}<br>{{ t('core.common.readme.empty.subtitle') }}</p>
</div>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" variant="tonal" @click="$emit('update:show', false)">
关闭
{{ t('core.common.close') }}
</v-btn>
</v-card-actions>
</v-card>
@@ -1,7 +1,7 @@
<template>
<v-dialog v-model="visible" persistent max-width="400">
<v-card>
<v-card-title>正在等待 AstrBot 重启...</v-card-title>
<v-card-title>{{ t('core.common.restart.waiting') }}</v-card-title>
<v-card-text>
<v-progress-linear indeterminate color="primary"></v-progress-linear>
</v-card-text>
@@ -11,12 +11,16 @@
<script>
import axios from 'axios'
import { useCommonStore } from '@/stores/common';
import { useI18n } from '@/i18n/composables';
export default {
name: 'WaitingForRestart',
setup() {
const { t } = useI18n();
return { t };
},
data() {
return {
visible: false,
@@ -47,7 +51,7 @@ export default {
}, 1000)
} else {
if (this.cnt == 10) {
this.status = '拉取状态达到最大次数,请手动检查。'
this.status = this.t('core.common.restart.maxRetriesReached')
}
this.cnt = 0
setTimeout(() => {
@@ -36,5 +36,40 @@
"confirmMessage": "Are you sure you want to perform this action?",
"confirmButton": "Confirm",
"cancelButton": "Cancel"
},
"restart": {
"waiting": "Waiting for AstrBot to restart...",
"maxRetriesReached": "Maximum retry attempts reached, please check manually."
},
"readme": {
"title": "Extension Documentation",
"buttons": {
"viewOnGithub": "View Repository on GitHub",
"refresh": "Refresh Documentation"
},
"loading": "Loading README documentation...",
"errors": {
"fetchFailed": "Failed to fetch README",
"fetchError": "Error occurred while fetching README"
},
"empty": {
"title": "This extension does not provide documentation link or GitHub repository address.",
"subtitle": "Please check the extension marketplace or contact the extension author for more information."
}
},
"editor": {
"fullscreen": "Fullscreen Edit",
"editingTitle": "Editing Content"
},
"list": {
"addItemPlaceholder": "Add new item, press Enter to confirm",
"addButton": "Add"
},
"itemCard": {
"enabled": "Enabled",
"disabled": "Disabled",
"delete": "Delete",
"edit": "Edit",
"noData": "No data available"
}
}
@@ -6,7 +6,9 @@
"send": "Send",
"clear": "Clear",
"upload": "Upload File",
"voice": "Voice Input"
"voice": "Voice Input",
"recordingPrompt": "Recording, please speak...",
"chatPrompt": "Let's chat!"
},
"message": {
"user": "User",
@@ -5,7 +5,10 @@
"status": {
"loading": "Loading...",
"dataError": "Failed to fetch data",
"noticeError": "Failed to fetch notice"
"noticeError": "Failed to fetch notice",
"online": "Online",
"uptime": "Uptime",
"memoryUsage": "Memory Usage"
},
"stats": {
"totalMessage": {
@@ -29,14 +29,17 @@
"close": "Close",
"save": "Save",
"saveAndClose": "Save and Close",
"cancel": "Cancel"
"cancel": "Cancel",
"actions": "Actions",
"back": "Back"
},
"status": {
"enabled": "Enabled",
"disabled": "Disabled",
"system": "System",
"loading": "Loading...",
"installed": "Installed"
"installed": "Installed",
"unknown": "Unknown"
},
"tooltips": {
"enable": "Click to Enable",
@@ -99,6 +102,10 @@
"loading": {
"title": "Loading...",
"logs": "Logs"
},
"uninstall": {
"title": "Confirm Deletion",
"message": "Are you sure you want to delete this extension?"
}
},
"messages": {
@@ -127,5 +134,27 @@
"fromUrl": "Install from URL",
"selectFile": "Select File",
"enterUrl": "Enter extension repository URL"
},
"card": {
"actions": {
"pluginConfig": "Extension Config",
"uninstallPlugin": "Uninstall Extension",
"reloadPlugin": "Reload Extension",
"togglePlugin": "Extension",
"viewHandlers": "View Handlers",
"updateTo": "Update to"
},
"status": {
"hasUpdate": "New version available",
"disabled": "This extension is disabled",
"handlersCount": " handlers"
},
"alt": {
"logo": "logo",
"extensionIcon": "extension icon"
},
"errors": {
"confirmNotRegistered": "$confirm not properly registered"
}
}
}
@@ -36,5 +36,40 @@
"confirmMessage": "你确定要执行此操作吗?",
"confirmButton": "确定",
"cancelButton": "取消"
},
"restart": {
"waiting": "正在等待 AstrBot 重启...",
"maxRetriesReached": "拉取状态达到最大次数,请手动检查。"
},
"readme": {
"title": "插件说明文档",
"buttons": {
"viewOnGithub": "在GitHub中查看仓库",
"refresh": "刷新文档"
},
"loading": "正在加载README文档...",
"errors": {
"fetchFailed": "获取README失败",
"fetchError": "获取README时发生错误"
},
"empty": {
"title": "该插件未提供文档链接或GitHub仓库地址。",
"subtitle": "请查看插件市场或联系插件作者获取更多信息。"
}
},
"editor": {
"fullscreen": "全屏编辑",
"editingTitle": "编辑内容"
},
"list": {
"addItemPlaceholder": "添加新项,按回车确认添加",
"addButton": "添加"
},
"itemCard": {
"enabled": "已启用",
"disabled": "已禁用",
"delete": "删除",
"edit": "编辑",
"noData": "暂无数据"
}
}
@@ -6,7 +6,9 @@
"send": "发送",
"clear": "清空",
"upload": "上传文件",
"voice": "语音输入"
"voice": "语音输入",
"recordingPrompt": "录音中,请说话...",
"chatPrompt": "聊天吧!"
},
"message": {
"user": "用户",
@@ -5,7 +5,10 @@
"status": {
"loading": "加载中...",
"dataError": "获取数据失败",
"noticeError": "获取公告失败"
"noticeError": "获取公告失败",
"online": "在线",
"uptime": "运行时间",
"memoryUsage": "内存占用"
},
"stats": {
"totalMessage": {
@@ -29,14 +29,17 @@
"close": "关闭",
"save": "保存",
"saveAndClose": "保存并关闭",
"cancel": "取消"
"cancel": "取消",
"actions": "操作",
"back": "返回"
},
"status": {
"enabled": "启用",
"disabled": "禁用",
"system": "系统",
"loading": "加载中...",
"installed": "已安装"
"installed": "已安装",
"unknown": "未知"
},
"tooltips": {
"enable": "点击启用",
@@ -99,6 +102,10 @@
"loading": {
"title": "加载中...",
"logs": "日志"
},
"uninstall": {
"title": "删除确认",
"message": "你确定要删除当前插件吗?"
}
},
"messages": {
@@ -127,5 +134,27 @@
"fromUrl": "从链接安装",
"selectFile": "选择文件",
"enterUrl": "输入插件仓库链接"
},
"card": {
"actions": {
"pluginConfig": "插件配置",
"uninstallPlugin": "卸载插件",
"reloadPlugin": "重载插件",
"togglePlugin": "插件",
"viewHandlers": "查看行为",
"updateTo": "更新到"
},
"status": {
"hasUpdate": "有新版本可用",
"disabled": "该插件已经被禁用",
"handlersCount": "个行为"
},
"alt": {
"logo": "logo",
"extensionIcon": "扩展图标"
},
"errors": {
"confirmNotRegistered": "$confirm 未正确注册"
}
}
}
+2 -2
View File
@@ -690,12 +690,12 @@ export default {
};
this.mediaRecorder.start();
this.isRecording = true;
this.inputFieldLabel = "录音中,请说话...";
this.inputFieldLabel = this.tm('features.chat.input.recordingPrompt');
},
async stopRecording() {
this.isRecording = false;
this.inputFieldLabel = "聊天吧!";
this.inputFieldLabel = this.tm('features.chat.input.chatPrompt');
this.mediaRecorder.stop();
this.mediaRecorder.onstop = async () => {
const audioBlob = new Blob(this.audioChunks, { type: 'audio/wav' });
+5 -5
View File
@@ -157,11 +157,11 @@ export default {
// 安全访问翻译的计算属性
messages() {
return {
loadError: "配置加载失败",
saveSuccess: "配置保存成功",
saveError: "配置保存失败",
configApplied: "配置应用成功",
configApplyError: "配置应用失败"
loadError: this.tm('features.config.messages.loadError'),
saveSuccess: this.tm('features.config.messages.saveSuccess'),
saveError: this.tm('features.config.messages.saveError'),
configApplied: this.tm('features.config.messages.configApplied'),
configApplyError: this.tm('features.config.messages.configApplyError')
};
}
},
+10 -8
View File
@@ -28,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("");
@@ -178,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('features.extension.status.unknown');
} else {
extension.has_update = false;
}
@@ -204,6 +204,7 @@ const uninstallExtension = async (extension_name) => {
};
const updateExtension = async (extension_name) => {
loadingDialog.title = tm('features.extension.status.loading');
loadingDialog.show = true;
try {
const res = await axios.post('/api/plugin/update', {
@@ -464,6 +465,7 @@ const newExtension = async () => {
return;
}
loading_.value = true;
loadingDialog.title = tm('features.extension.status.loading');
loadingDialog.show = true;
if (upload_file.value !== null) {
toast(tm('messages.installing'), "primary");
@@ -8,15 +8,15 @@
</div>
<div class="stat-content">
<div class="stat-title">运行时间</div>
<h3 class="uptime-value">{{ stat.running || '加载中...' }}</h3>
<div class="stat-title">{{ tm('features.dashboard.status.uptime') }}</div>
<h3 class="uptime-value">{{ stat.running || tm('features.dashboard.status.loading') }}</h3>
</div>
<v-spacer></v-spacer>
<div class="uptime-status">
<v-icon icon="mdi-circle" size="10" color="success" class="blink-animation"></v-icon>
<span class="status-text">在线</span>
<span class="status-text">{{ tm('features.dashboard.status.online') }}</span>
</div>
</div>
</v-card-text>
@@ -30,7 +30,7 @@
</div>
<div class="stat-content">
<div class="stat-title">内存占用</div>
<div class="stat-title">{{ tm('features.dashboard.status.memoryUsage') }}</div>
<div class="memory-values">
<h3 class="memory-value">{{ stat.memory?.process || 0 }} <span class="memory-unit">MiB</span></h3>
<span class="memory-separator">/</span>
@@ -53,13 +53,19 @@
</template>
<script>
import { useModuleI18n } from '@/i18n/composables';
export default {
name: 'OnlineTime',
setup() {
const { tm } = useModuleI18n('features/dashboard');
return { tm };
},
props: ['stat'],
data: () => ({
stat: {
memory: { process: 0, system: 0 },
running: "加载中...",
running: "",
},
}),
computed: {