From d41f77b49cb073380e2f298641534fd26db4d096 Mon Sep 17 00:00:00 2001
From: eafonyang
Date: Mon, 10 Aug 2026 18:50:14 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AE=98=E7=BD=91=E5=BC=80=E5=8F=91=200810-v2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
dashboard/backend/src/routes/www/global.ts | 13 +-
.../backend/src/schemas/www/globalSettings.ts | 1 +
dashboard/frontend/patches/vdirs@0.1.8.patch | 158 +++++++++
dashboard/frontend/pnpm-lock.yaml | 9 +-
dashboard/frontend/pnpm-workspace.yaml | 2 +
dashboard/frontend/src/typings/api/www.d.ts | 2 +
.../src/views/www/global/basic/index.vue | 27 +-
.../src/views/www/news/articles/index.vue | 31 +-
.../src/views/www/page/about/index.vue | 209 +++++++++++-
.../src/views/www/page/support/index.vue | 301 +++++++++++++++++-
db/01_global_settings.sql | 1 +
db/07_pages.sql | 6 +-
db/migrations/x10_product2_spec_audio.sql | 33 ++
db/migrations/x11_product2_spec_input.sql | 48 +++
db/migrations/x12_product2_spec_output.sql | 20 ++
.../x13_site_settings_enabled_locales.sql | 5 +
db/migrations/x9_product2_spec_basic.sql | 15 +
tmp_home_check.png | Bin 40881 -> 0 bytes
www/app/components/AppFooter.vue | 4 +-
www/app/components/AppHeader.vue | 34 +-
.../section/layouts/LayoutSpecTable.vue | 4 +-
www/app/pages/about.vue | 10 +-
www/app/types/site.ts | 2 +
23 files changed, 875 insertions(+), 60 deletions(-)
create mode 100644 dashboard/frontend/patches/vdirs@0.1.8.patch
create mode 100644 db/migrations/x10_product2_spec_audio.sql
create mode 100644 db/migrations/x11_product2_spec_input.sql
create mode 100644 db/migrations/x12_product2_spec_output.sql
create mode 100644 db/migrations/x13_site_settings_enabled_locales.sql
create mode 100644 db/migrations/x9_product2_spec_basic.sql
delete mode 100644 tmp_home_check.png
diff --git a/dashboard/backend/src/routes/www/global.ts b/dashboard/backend/src/routes/www/global.ts
index 6102b7a..990438c 100644
--- a/dashboard/backend/src/routes/www/global.ts
+++ b/dashboard/backend/src/routes/www/global.ts
@@ -29,15 +29,25 @@ router.get('/api/www/global/site', async (_req: Request, res: Response) => {
// PUT /api/www/global/site
router.put('/api/www/global/site', async (req: Request, res: Response) => {
try {
- const { site_title, favicon_url, logo_dark_url, logo_light_url } = req.body;
+ const { site_title, favicon_url, logo_dark_url, logo_light_url, enabled_locales } = req.body;
const [existing] = await db.select().from(wwwSiteSettings).limit(1);
+ // 多语言:前端可能传数组或逗号分隔字符串,白名单过滤后落库;空值保留原配置
+ let localesValue: string | undefined;
+ if (enabled_locales !== undefined) {
+ const list = (Array.isArray(enabled_locales) ? enabled_locales : String(enabled_locales).split(','))
+ .map((v: unknown) => String(v).trim())
+ .filter(v => v === 'zh' || v === 'en');
+ if (list.length > 0) localesValue = [...new Set(list)].join(',');
+ }
+
if (existing) {
await db.update(wwwSiteSettings).set({
siteTitle: site_title ?? existing.siteTitle,
faviconUrl: favicon_url ?? existing.faviconUrl,
logoDarkUrl: logo_dark_url ?? existing.logoDarkUrl,
logoLightUrl: logo_light_url ?? existing.logoLightUrl,
+ enabledLocales: localesValue ?? existing.enabledLocales,
}).where(eq(wwwSiteSettings.id, existing.id));
} else {
await db.insert(wwwSiteSettings).values({
@@ -45,6 +55,7 @@ router.put('/api/www/global/site', async (req: Request, res: Response) => {
faviconUrl: favicon_url || '',
logoDarkUrl: logo_dark_url || '',
logoLightUrl: logo_light_url || '',
+ enabledLocales: localesValue || 'zh,en',
});
}
diff --git a/dashboard/backend/src/schemas/www/globalSettings.ts b/dashboard/backend/src/schemas/www/globalSettings.ts
index 75036d1..78e0cdb 100644
--- a/dashboard/backend/src/schemas/www/globalSettings.ts
+++ b/dashboard/backend/src/schemas/www/globalSettings.ts
@@ -7,6 +7,7 @@ export const wwwSiteSettings = mysqlTable('www_site_settings', {
faviconUrl: varchar('favicon_url', { length: 500 }).notNull().default(''),
logoDarkUrl: varchar('logo_dark_url', { length: 500 }).notNull().default(''),
logoLightUrl: varchar('logo_light_url', { length: 500 }).notNull().default(''),
+ enabledLocales: varchar('enabled_locales', { length: 20 }).notNull().default('zh,en'),
updatedAt: datetime('updated_at').notNull().default(sql`CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`),
});
diff --git a/dashboard/frontend/patches/vdirs@0.1.8.patch b/dashboard/frontend/patches/vdirs@0.1.8.patch
new file mode 100644
index 0000000..40066ad
--- /dev/null
+++ b/dashboard/frontend/patches/vdirs@0.1.8.patch
@@ -0,0 +1,158 @@
+diff --git a/es/clickoutside.js b/es/clickoutside.js
+index 3dcb41588ad00463f58be7c972882bc9d49c0f06..eef77f163c02a525f2810ab9294cb4179c3bac97 100644
+--- a/es/clickoutside.js
++++ b/es/clickoutside.js
+@@ -13,6 +13,12 @@ const clickoutside = {
+ }
+ },
+ updated(el, { value, modifiers }) {
++ // patched: `updated` may fire without `mounted` (teleport remount), initialize lazily
++ if (!el[ctxKey]) {
++ el[ctxKey] = {
++ handler: undefined
++ };
++ }
+ const ctx = el[ctxKey];
+ if (typeof value === 'function') {
+ if (ctx.handler) {
+@@ -43,6 +49,8 @@ const clickoutside = {
+ }
+ },
+ unmounted(el, { modifiers }) {
++ if (!el[ctxKey])
++ return;
+ const { handler } = el[ctxKey];
+ if (handler) {
+ off('clickoutside', el, handler, {
+diff --git a/es/mousemoveoutside.js b/es/mousemoveoutside.js
+index f15be455deb61f8af83abc285b46f7931fd1cb06..2f2c6ffe9c33ace8e23dc3c51d75420dc563e95e 100644
+--- a/es/mousemoveoutside.js
++++ b/es/mousemoveoutside.js
+@@ -11,6 +11,12 @@ const mousemoveoutside = {
+ }
+ },
+ updated(el, { value }) {
++ // patched: `updated` may fire without `mounted` (teleport remount), initialize lazily
++ if (!el[ctxKey]) {
++ el[ctxKey] = {
++ handler: undefined
++ };
++ }
+ const ctx = el[ctxKey];
+ if (typeof value === 'function') {
+ if (ctx.handler) {
+@@ -33,6 +39,8 @@ const mousemoveoutside = {
+ }
+ },
+ unmounted(el) {
++ if (!el[ctxKey])
++ return;
+ const { handler } = el[ctxKey];
+ if (handler) {
+ off('mousemoveoutside', el, handler);
+diff --git a/es/zindexable/index.js b/es/zindexable/index.js
+index 8066b8c39003c8b09419586b125c5b866eb5c788..b7680bedb5823c2464305081164284c189a426b1 100644
+--- a/es/zindexable/index.js
++++ b/es/zindexable/index.js
+@@ -17,6 +17,13 @@ const zindexable = {
+ updated(el, bindings) {
+ const { value = {} } = bindings;
+ const { zIndex, enabled } = value;
++ // patched: `updated` may fire without `mounted` (teleport remount), initialize lazily
++ if (!el[ctx]) {
++ el[ctx] = {
++ enabled: false,
++ initialized: false
++ };
++ }
+ const cachedEnabled = el[ctx].enabled;
+ if (enabled && !cachedEnabled) {
+ zIndexManager.ensureZIndex(el, zIndex);
+@@ -25,7 +32,7 @@ const zindexable = {
+ el[ctx].enabled = !!enabled;
+ },
+ unmounted(el, bindings) {
+- if (!el[ctx].initialized)
++ if (!el[ctx] || !el[ctx].initialized)
+ return;
+ const { value = {} } = bindings;
+ const { zIndex } = value;
+diff --git a/lib/clickoutside.js b/lib/clickoutside.js
+index 300e65614d1789e1ee477460b58b88aab94b6588..af39ce4e8b5deac36fac9ea8560aba676e852a54 100644
+--- a/lib/clickoutside.js
++++ b/lib/clickoutside.js
+@@ -15,6 +15,12 @@ const clickoutside = {
+ }
+ },
+ updated(el, { value, modifiers }) {
++ // patched: `updated` may fire without `mounted` (teleport remount), initialize lazily
++ if (!el[ctxKey]) {
++ el[ctxKey] = {
++ handler: undefined
++ };
++ }
+ const ctx = el[ctxKey];
+ if (typeof value === 'function') {
+ if (ctx.handler) {
+@@ -45,6 +51,8 @@ const clickoutside = {
+ }
+ },
+ unmounted(el, { modifiers }) {
++ if (!el[ctxKey])
++ return;
+ const { handler } = el[ctxKey];
+ if (handler) {
+ (0, evtd_1.off)('clickoutside', el, handler, {
+diff --git a/lib/mousemoveoutside.js b/lib/mousemoveoutside.js
+index daf5afa22dd0a715c91bf81703161878ffbfb6fd..1608823f4257d924e870710bf55a295255948d74 100644
+--- a/lib/mousemoveoutside.js
++++ b/lib/mousemoveoutside.js
+@@ -13,6 +13,12 @@ const mousemoveoutside = {
+ }
+ },
+ updated(el, { value }) {
++ // patched: `updated` may fire without `mounted` (teleport remount), initialize lazily
++ if (!el[ctxKey]) {
++ el[ctxKey] = {
++ handler: undefined
++ };
++ }
+ const ctx = el[ctxKey];
+ if (typeof value === 'function') {
+ if (ctx.handler) {
+@@ -35,6 +41,8 @@ const mousemoveoutside = {
+ }
+ },
+ unmounted(el) {
++ if (!el[ctxKey])
++ return;
+ const { handler } = el[ctxKey];
+ if (handler) {
+ (0, evtd_1.off)('mousemoveoutside', el, handler);
+diff --git a/lib/zindexable/index.js b/lib/zindexable/index.js
+index ac608e9c13376a0e21f9fea8802c1d7bfb9d1c58..9305912d112a91dc0f6eb354bb2aaa95b3351dca 100644
+--- a/lib/zindexable/index.js
++++ b/lib/zindexable/index.js
+@@ -19,6 +19,13 @@ const zindexable = {
+ updated(el, bindings) {
+ const { value = {} } = bindings;
+ const { zIndex, enabled } = value;
++ // patched: `updated` may fire without `mounted` (teleport remount), initialize lazily
++ if (!el[ctx]) {
++ el[ctx] = {
++ enabled: false,
++ initialized: false
++ };
++ }
+ const cachedEnabled = el[ctx].enabled;
+ if (enabled && !cachedEnabled) {
+ z_index_manager_1.default.ensureZIndex(el, zIndex);
+@@ -27,7 +34,7 @@ const zindexable = {
+ el[ctx].enabled = !!enabled;
+ },
+ unmounted(el, bindings) {
+- if (!el[ctx].initialized)
++ if (!el[ctx] || !el[ctx].initialized)
+ return;
+ const { value = {} } = bindings;
+ const { zIndex } = value;
diff --git a/dashboard/frontend/pnpm-lock.yaml b/dashboard/frontend/pnpm-lock.yaml
index d3c4fe3..278388c 100644
--- a/dashboard/frontend/pnpm-lock.yaml
+++ b/dashboard/frontend/pnpm-lock.yaml
@@ -4,6 +4,9 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
+patchedDependencies:
+ vdirs@0.1.8: d2c61ceb63f1b1d07b884b02d6d92e59099989b7f5a59a39301adad995e046b5
+
importers:
.:
@@ -7516,7 +7519,7 @@ snapshots:
lodash-es: 4.18.1
seemly: 0.3.10
treemate: 0.3.11
- vdirs: 0.1.8(vue@3.5.34(typescript@6.0.3))
+ vdirs: 0.1.8(patch_hash=d2c61ceb63f1b1d07b884b02d6d92e59099989b7f5a59a39301adad995e046b5)(vue@3.5.34(typescript@6.0.3))
vooks: 0.2.12(vue@3.5.34(typescript@6.0.3))
vue: 3.5.34(typescript@6.0.3)
vueuc: 0.4.65(vue@3.5.34(typescript@6.0.3))
@@ -8555,7 +8558,7 @@ snapshots:
vary@1.1.2: {}
- vdirs@0.1.8(vue@3.5.34(typescript@6.0.3)):
+ vdirs@0.1.8(patch_hash=d2c61ceb63f1b1d07b884b02d6d92e59099989b7f5a59a39301adad995e046b5)(vue@3.5.34(typescript@6.0.3)):
dependencies:
evtd: 0.2.4
vue: 3.5.34(typescript@6.0.3)
@@ -8742,7 +8745,7 @@ snapshots:
css-render: 0.15.14
evtd: 0.2.4
seemly: 0.3.10
- vdirs: 0.1.8(vue@3.5.34(typescript@6.0.3))
+ vdirs: 0.1.8(patch_hash=d2c61ceb63f1b1d07b884b02d6d92e59099989b7f5a59a39301adad995e046b5)(vue@3.5.34(typescript@6.0.3))
vooks: 0.2.12(vue@3.5.34(typescript@6.0.3))
vue: 3.5.34(typescript@6.0.3)
diff --git a/dashboard/frontend/pnpm-workspace.yaml b/dashboard/frontend/pnpm-workspace.yaml
index 68c2949..e147270 100644
--- a/dashboard/frontend/pnpm-workspace.yaml
+++ b/dashboard/frontend/pnpm-workspace.yaml
@@ -9,3 +9,5 @@ allowBuilds:
shamefullyHoist: true
ignoreWorkspaceRootCheck: true
linkWorkspacePackages: true
+patchedDependencies:
+ vdirs@0.1.8: patches/vdirs@0.1.8.patch
diff --git a/dashboard/frontend/src/typings/api/www.d.ts b/dashboard/frontend/src/typings/api/www.d.ts
index 7123f4d..5cd5935 100644
--- a/dashboard/frontend/src/typings/api/www.d.ts
+++ b/dashboard/frontend/src/typings/api/www.d.ts
@@ -7,6 +7,8 @@ declare namespace Api {
favicon_url: string;
logo_dark_url: string;
logo_light_url: string;
+ /** 启用的语言(逗号分隔):zh,en */
+ enabled_locales: string;
updated_at?: string;
}
diff --git a/dashboard/frontend/src/views/www/global/basic/index.vue b/dashboard/frontend/src/views/www/global/basic/index.vue
index acfa018..4a1f98f 100644
--- a/dashboard/frontend/src/views/www/global/basic/index.vue
+++ b/dashboard/frontend/src/views/www/global/basic/index.vue
@@ -14,11 +14,23 @@ const formData = reactive({
site_title: '',
favicon_url: '',
logo_dark_url: '',
- logo_light_url: ''
+ logo_light_url: '',
+ enabled_locales: ['zh', 'en'] as string[]
});
+const localeOptions = [
+ { label: '中文', value: 'zh' },
+ { label: '英文', value: 'en' }
+];
+
const rules: FormRules = {
- site_title: [{ required: true, message: '请输入站点标题', trigger: ['blur', 'input'] }]
+ site_title: [{ required: true, message: '请输入站点标题', trigger: ['blur', 'input'] }],
+ enabled_locales: [
+ {
+ validator: (_rule, value: string[]) => (value.length > 0 ? true : new Error('至少选择一种语言')),
+ trigger: ['blur', 'change']
+ }
+ ]
};
async function loadData() {
@@ -30,13 +42,16 @@ async function loadData() {
formData.favicon_url = data.favicon_url || '';
formData.logo_dark_url = data.logo_dark_url || '';
formData.logo_light_url = data.logo_light_url || '';
+ formData.enabled_locales = (data.enabled_locales || 'zh,en').split(',').filter(v => v === 'zh' || v === 'en');
}
}
async function handleSave() {
await formRef.value?.validate();
saving.value = true;
- const { error } = await fetchUpdateSiteSettings({ ...formData });
+ // 多语言以逗号分隔字符串落库
+ const { enabled_locales, ...rest } = formData;
+ const { error } = await fetchUpdateSiteSettings({ ...rest, enabled_locales: enabled_locales.join(',') });
saving.value = false;
if (!error) {
window.$message?.success('保存成功');
@@ -61,6 +76,12 @@ onMounted(loadData);
+
+
+
+
可多选;仅选一种语言时,官网顶部不再显示语言切换按钮
+
+
diff --git a/dashboard/frontend/src/views/www/news/articles/index.vue b/dashboard/frontend/src/views/www/news/articles/index.vue
index e83b64e..51bcd40 100644
--- a/dashboard/frontend/src/views/www/news/articles/index.vue
+++ b/dashboard/frontend/src/views/www/news/articles/index.vue
@@ -2,10 +2,12 @@
import { computed, h, onMounted, reactive, ref } from 'vue';
import { NButton, NSpace, NTag, type DataTableColumns, type FormInst, type FormRules } from 'naive-ui';
import MediaSelectInput from '../../components/MediaSelectInput.vue';
+import RichTextEditor from '../../components/RichTextEditor.vue';
import {
fetchCreateNewsArticle,
fetchDeleteNewsArticle,
fetchGetNews,
+ fetchGetNewsArticle,
fetchUpdateNewsArticle
} from '@/service/api';
@@ -41,7 +43,7 @@ const formData = reactive({
content_zh: '',
content_en: '',
status: 'draft' as string,
- published_at: null as string | null
+ published_at: null as number | null
});
const rules: FormRules = {
@@ -140,8 +142,9 @@ function handleAdd() {
dialogVisible.value = true;
}
-function handleEdit(row: Api.Www.NewsArticle) {
+async function handleEdit(row: Api.Www.NewsArticle) {
dialogTitle.value = '编辑文章';
+ // 列表接口不返回正文,先回填列表字段,再拉详情补全,避免保存时把正文覆盖为空
formData.id = row.id;
formData.title_zh = row.title_zh;
formData.title_en = row.title_en;
@@ -149,11 +152,19 @@ function handleEdit(row: Api.Www.NewsArticle) {
formData.summary_zh = row.summary_zh;
formData.summary_en = row.summary_en;
formData.cover_url = row.cover_url;
- formData.content_zh = row.content_zh;
- formData.content_en = row.content_en;
+ formData.content_zh = '';
+ formData.content_en = '';
formData.status = row.status;
- formData.published_at = row.published_at;
+ formData.published_at = row.published_at ? new Date(row.published_at).getTime() : null;
dialogVisible.value = true;
+
+ const { data, error } = await fetchGetNewsArticle(row.id);
+ if (error || !data || data.id !== formData.id) return;
+ formData.summary_zh = data.summary_zh ?? formData.summary_zh;
+ formData.summary_en = data.summary_en ?? formData.summary_en;
+ formData.cover_url = data.cover_url ?? formData.cover_url;
+ formData.content_zh = data.content_zh || '';
+ formData.content_en = data.content_en || '';
}
function handleDelete(row: Api.Www.NewsArticle) {
@@ -175,7 +186,9 @@ function handleDelete(row: Api.Www.NewsArticle) {
async function handleSubmit() {
await formRef.value?.validate();
submitLoading.value = true;
- const { id, ...payload } = formData;
+ const { id, published_at, ...rest } = formData;
+ // 时间戳 → ISO 字符串(UTC),与后端 DATETIME 存储格式对齐
+ const payload = { ...rest, published_at: published_at ? new Date(published_at).toISOString() : null };
const { error } = id
? await fetchUpdateNewsArticle(id, payload)
: await fetchCreateNewsArticle(payload);
@@ -277,16 +290,16 @@ onMounted(loadData);
-
+
-
+
-
+
diff --git a/dashboard/frontend/src/views/www/page/about/index.vue b/dashboard/frontend/src/views/www/page/about/index.vue
index 261dc68..606d17d 100644
--- a/dashboard/frontend/src/views/www/page/about/index.vue
+++ b/dashboard/frontend/src/views/www/page/about/index.vue
@@ -1,27 +1,90 @@
-
-
-
-
-
- 保存
-
-
-
+
+
+
+
+
+ 保存
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 特性 {{ idx + 1 }}
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + 添加特性
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 保存
+
+
+
+
diff --git a/dashboard/frontend/src/views/www/page/support/index.vue b/dashboard/frontend/src/views/www/page/support/index.vue
index 2a48686..03f0534 100644
--- a/dashboard/frontend/src/views/www/page/support/index.vue
+++ b/dashboard/frontend/src/views/www/page/support/index.vue
@@ -1,27 +1,131 @@
-
-
-
-
-
- 保存
-
-
-
+
+
+
+
+
+ 保存
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 分类 {{ cIdx + 1 }}
+
+ 删除分类
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 问答 {{ iIdx + 1 }}
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + 添加问答
+
+
+
+
+ + 添加分类
+
+
+
+
+
+
+
+
+
+ 资源 {{ idx + 1 }}
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + 添加资源
+
+
+
+
+
+
+
+
+
+ 条目 {{ idx + 1 }}
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + 添加条目
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 保存
+
+
+
+
diff --git a/db/01_global_settings.sql b/db/01_global_settings.sql
index 7de34d6..95d19ee 100644
--- a/db/01_global_settings.sql
+++ b/db/01_global_settings.sql
@@ -13,6 +13,7 @@ CREATE TABLE IF NOT EXISTS `www_site_settings` (
`favicon_url` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'Favicon 素材路径',
`logo_dark_url` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'Logo 深色版(浅色区块上用)',
`logo_light_url` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'Logo 浅色版(深色区块上用)',
+ `enabled_locales` VARCHAR(20) NOT NULL DEFAULT 'zh,en' COMMENT '启用的语言(逗号分隔):zh,en',
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
diff --git a/db/07_pages.sql b/db/07_pages.sql
index 91acafc..cd55f44 100644
--- a/db/07_pages.sql
+++ b/db/07_pages.sql
@@ -105,11 +105,11 @@ CREATE TABLE IF NOT EXISTS `www_support_settings` (
`hero_subtitle_en` VARCHAR(200) NOT NULL DEFAULT '' COMMENT 'Hero 副标题(英文)',
`hero_bg_url` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'Hero 背景图',
-- Section 1: 常见问题分类
- `s1_faq_categories` JSON COMMENT 'FAQ 分类 JSON: [{icon, name_zh, name_en, desc_zh, desc_en}]',
+ `s1_faq_categories` JSON COMMENT 'FAQ 分类 JSON: [{title_zh, title_en, items: [{question_zh, question_en, answer_zh, answer_en}]}]',
-- Section 2: 下载中心
- `s2_downloads` JSON COMMENT '下载资源 JSON: [{name_zh, name_en, file_url, version}]',
+ `s2_downloads` JSON COMMENT '下载资源 JSON: [{name_zh, name_en, file_url, file_size}]',
-- Section 3: 联系方式
- `s3_contact` JSON COMMENT '联系信息 JSON: [{title_zh, title_en, info_zh, info_en, image_url}]',
+ `s3_contact` JSON COMMENT '联系信息 JSON: [{label_zh, label_en, value_zh, value_en}]',
-- Section 4: CTA
`s4_cta_zh` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'CTA 按钮(中文)',
`s4_cta_en` VARCHAR(100) NOT NULL DEFAULT '' COMMENT 'CTA 按钮(英文)',
diff --git a/db/migrations/x10_product2_spec_audio.sql b/db/migrations/x10_product2_spec_audio.sql
new file mode 100644
index 0000000..0c1d56e
--- /dev/null
+++ b/db/migrations/x10_product2_spec_audio.sql
@@ -0,0 +1,33 @@
+-- 产品 id=2 参数规格区块(www_section_blocks.id=43)「音频参数」分组(www_spec_groups.id=9)参数项录入
+-- 英文按产品资料原文录入,中文为对应原文;多行参数值以换行分隔;sort_order 按展示顺序递增
+INSERT INTO www_spec_items (spec_group_id, name_zh, name_en, value_zh, value_en, sort_order) VALUES
+(9, '播放和DAC解码', 'Play & DAC',
+ '支持立体声 DSD512,PCM 768KHz 32位',
+ 'support DSD512,PCM 768KHz 32-bit', 0),
+(9, '蓝牙音频输入', 'Bluetooth audio input',
+ '蓝牙 BT5.0,支持 SBC/AAC',
+ 'BT5.0,support AAC/SBC', 1),
+(9, 'USB-B音频输入', 'USB-B audio input',
+ '支持 Windows (10, 11),Android,IOS。支持立体声 DSD512,PCM 768KHz 32位',
+ 'support Windows(10, 11),Android,IOS。support DSD512,PCM 768KHz 32-bit', 2),
+(9, '光纤/同轴音频输入', 'Optical/Coaxial audio input',
+ '支持立体声 PCM 192KHz 24位,DoP64',
+ 'support PCM 192KHz 24-bit,DoP64', 3),
+(9, 'HDMI ARC', 'HDMI ARC',
+ '支持立体声 PCM 192KHz 24位',
+ 'support PCM 192KHz 24-bit', 4),
+(9, '模拟前级音频输入', 'Analog input',
+ 'RCA(非平衡),最大增益 +10dB',
+ 'RCA (unbalance),max gain +10dB', 5),
+(9, '模拟音频输出接口', 'Analog audio output interface',
+ 'XLR(平衡)+ RCA(非平衡)+ 低音炮输出',
+ 'XLR(balanced)+RCA(unbalanced)+subwoofer output', 6),
+(9, 'XLR输出音频特性', 'XLR Audio Output',
+ '输出电平:4.2Vrms@0dBFS\n频率响应:20Hz~20KHz (±0.25dB)\n动态范围:> 128dB\n信噪比:>128dB\nTHD+N:<0.000096% (-120.3dB) @ 不计权\n串扰:>-117dB',
+ 'output level 4.2Vrms@0dBFS\nFrequency Response:20Hz~20KHz(±0.25dB)\nDynamic Range:> 128dB\nSignal-to-Noise Ratio(SNR):>128dB\nTHD+N:<0.000096%(-120.3dB)@ Unweighted\nCrosstalk:>-117dB', 7),
+(9, 'RCA输出音频特性', 'RCA Audio Output',
+ '输出电平:2.1Vrms@0dBFS\n频率响应:20Hz~20KHz (±0.25dB)\n动态范围:> 125dB\n信噪比:>125dB\nTHD+N:<0.00010% (-119dB) @ 不计权\n串扰:>-114dB',
+ 'output level 2.1Vrms@0dBFS\nFrequency Response:20Hz~20KHz(±0.25dB)\nDynamic Range:> 125dB\nSignal-to-Noise Ratio(SNR):>125dB\nTHD+N:<0.00010%(-119dB)@ Unweighted\nCrosstalk:>-114dB', 8),
+(9, '耳机输出音频特性', 'Headphone Audio Output',
+ '输出电平:4-15Vrms@0dBFS\n频率响应:20Hz~20KHz (±0.25dB)\n动态范围:> 128dB\n信噪比:>128dB\nTHD+N:<0.00016% (-116dB) @ 不计权',
+ 'output level 4-15Vrms@0dBFS\nFrequency Response:20Hz~20KHz(±0.25dB)\nDynamic Range:> 128dB\nSignal-to-Noise Ratio(SNR):>128dB\nTHD+N:<0.00016%(-116dB)@ Unweighted', 9);
diff --git a/db/migrations/x11_product2_spec_input.sql b/db/migrations/x11_product2_spec_input.sql
new file mode 100644
index 0000000..7cd7a41
--- /dev/null
+++ b/db/migrations/x11_product2_spec_input.sql
@@ -0,0 +1,48 @@
+-- 产品 id=2 参数规格区块(www_section_blocks.id=43)「输入」分组(www_spec_groups.id=10)参数项录入
+-- 中英文按产品资料录入;同一参数中英文不一致时以英文值为准(含拼写勘误:TIRGGER→TRIGGER、去除中文多余字符)
+INSERT INTO www_spec_items (spec_group_id, name_zh, name_en, value_zh, value_en, sort_order) VALUES
+(10, 'USB-A Port', 'USB-A Port',
+ '仅限更新固件用',
+ 'For Firmware Updates Only', 0),
+(10, 'USB-B Port', 'USB-B Port',
+ 'USB Audio系统兼容性: Windows (10, 11),Android,IOS系统 最高支持立体声 DSD512,PCM 768KHz/32Bit',
+ 'USB Audio System Compatibility: Windows (10, 11), Android, iOS; Supports up to Stereo DSD512, PCM 768kHz/32-Bit', 1),
+(10, 'USB Type-C', 'USB Type-C',
+ 'USB Audio系统兼容性: Windows (10, 11),Android,IOS系统 最高支持立体声 DSD512,PCM 768KHz/32Bit',
+ 'USB Audio System Compatibility: Windows (10, 11), Android, iOS; Supports up to Stereo DSD512, PCM 768kHz/32-Bit', 2),
+(10, '模拟单端(输入)', 'Analog (unbalanced input)',
+ 'RAC*2(非平衡),最大增益 +10dB',
+ 'RAC*2 (unbalanced),Max Gain +10dB', 3),
+(10, '光纤', 'Optical',
+ '最高支持立体声 PCM 192KHz 24Bit、DoP64',
+ 'Support up to PCM 192KHz 24-bit,DoP64', 4),
+(10, '同轴', 'Coaxial',
+ 'RAC*1',
+ 'RAC*1', 5),
+(10, '蓝牙接收5.0', 'Bluetooth input',
+ '蓝牙BT5.0,支持SBC/AAC',
+ 'BT5.0,support AAC/SBC', 6),
+(10, 'HDMI ARC', 'HDMI ARC',
+ '最高支持立体声PCM 192KHz 24Bit',
+ 'support PCM 192KHz 24-bit', 7),
+(10, 'TRIGGER接口', 'TRIGGER Connection',
+ '标准3.5mm耳机插座(TRIGGER IN接口*1+TRIGGER OUT接口*1)',
+ '3.5mm Headphone Jack (TRIGGER IN ×1 + TRIGGER OUT ×1)', 8),
+(10, 'WiFi', 'WiFi',
+ 'WiFi 2.4G / 5G',
+ 'WiFi 2.4G / 5G', 9),
+(10, '音量旋钮', 'Volume Knob',
+ '编码器旋钮',
+ 'Encoder Knob', 10),
+(10, '开机按钮', 'Power Button',
+ '单按静音,长按待机',
+ 'Single Press: Mute, Long Press: Standby', 11),
+(10, '电源开关', 'Power Switch',
+ '船形开关',
+ 'Rocker Switch', 12),
+(10, '电源', 'Power Supply',
+ 'AC 100-240V~50/60Hz',
+ 'AC 100-240V~50/60Hz', 13),
+(10, '遥控器', 'Controller',
+ '红外遥控器12键',
+ '12-Button Infrared Remote Control', 14);
diff --git a/db/migrations/x12_product2_spec_output.sql b/db/migrations/x12_product2_spec_output.sql
new file mode 100644
index 0000000..4a6d805
--- /dev/null
+++ b/db/migrations/x12_product2_spec_output.sql
@@ -0,0 +1,20 @@
+-- 产品 id=2 参数规格区块(www_section_blocks.id=43)「输出」分组(www_spec_groups.id=11)参数项录入
+-- 中英文按产品资料录入;勘误:分组英文标题 Ouput→Output;英文参数名 Unbalalced→Unbalanced
+UPDATE www_spec_groups SET title_en = 'Output' WHERE id = 11 AND title_en = 'Ouput';
+
+INSERT INTO www_spec_items (spec_group_id, name_zh, name_en, value_zh, value_en, sort_order) VALUES
+(11, '平衡线路输出接口', 'Balanced Circuit Output',
+ 'XLR3平衡插座',
+ 'XLR3 Balanced Socket', 0),
+(11, '单端线路输出接口', 'Unbalanced Circuit Output',
+ 'RCA插座*2',
+ 'RCA*2', 1),
+(11, '低音炮输出接口', 'Subwoofer Output',
+ 'RCA插座*2',
+ 'RCA*2', 2),
+(11, '平衡耳机输出接口', 'Balanced Headphone Output',
+ '标准4.4mm插座*1+XLR4平衡插座*1',
+ '4.4mm Socket ×1 + XLR4 Balanced Socket ×1', 3),
+(11, '单端耳机输出接口', 'Unbalanced Headphone Output',
+ '标准6.35mm 插座',
+ '6.35mm Socket', 4);
diff --git a/db/migrations/x13_site_settings_enabled_locales.sql b/db/migrations/x13_site_settings_enabled_locales.sql
new file mode 100644
index 0000000..8c2d5e6
--- /dev/null
+++ b/db/migrations/x13_site_settings_enabled_locales.sql
@@ -0,0 +1,5 @@
+-- x13: 站点信息新增「启用的多语言」配置
+-- 逗号分隔的语言代码(zh,en),决定 www 前台展示哪些语言;
+-- 仅选一个语言时,前台隐藏顶部导航的语言切换按钮。
+ALTER TABLE `www_site_settings`
+ ADD COLUMN `enabled_locales` VARCHAR(20) NOT NULL DEFAULT 'zh,en' COMMENT '启用的语言(逗号分隔):zh,en' AFTER `logo_light_url`;
diff --git a/db/migrations/x9_product2_spec_basic.sql b/db/migrations/x9_product2_spec_basic.sql
new file mode 100644
index 0000000..0831cc7
--- /dev/null
+++ b/db/migrations/x9_product2_spec_basic.sql
@@ -0,0 +1,15 @@
+-- 产品 id=2 参数规格区块(www_section_blocks.id=43)「基本规格」分组(www_spec_groups.id=8)参数项录入
+-- 英文按产品资料原文录入,中文为对应翻译;sort_order 按展示顺序递增
+INSERT INTO www_spec_items (spec_group_id, name_zh, name_en, value_zh, value_en, sort_order) VALUES
+(8, '型号', 'Model', 'X9', 'X9', 0),
+(8, '显示屏', 'Display', '4 英寸 TFT 全贴合触摸屏(960×400)', '4-inch TFT Fully Laminated Touchscreen (960X400)', 1),
+(8, '硬件方案', 'Hardware solution','DAC:AK4191EQ+AK4499EX;蓝牙解码:QCC5125', 'DAC: AK4191EQ+AK4499EX; Bluetooth decoding: QCC5125', 2),
+(8, '音频处理器', 'Audio processor', 'AKM 7739 DSP', 'AKM 7739 DSP', 3),
+(8, '运放芯片', 'Op-Amp chip', 'OPA1612', 'OPA1612', 4),
+(8, '尺寸', 'dimension', '宽:300mm × 长:206mm × 高:65mm', 'Width: 300mm * Length: 206mm * Height: 65mm', 5),
+(8, '电源', 'Power supply', '超低噪声线性电源', 'Ultra-Low Noise Linear Power Supply', 6),
+(8, '额定功率', 'Rated Power', '25W', '25W', 7),
+(8, '包装清单', 'Packing list', '电源线 ×1、遥控器 ×1、USB-B 线缆 ×1、用户手册 ×1', 'Power Cable ×1, Remote Controller ×1, USB-B Cable×1, User Manual ×1', 8),
+(8, '颜色', 'Color', '银色', 'Silver', 9),
+(8, '重量', 'Weight', '3.72kg', '3.72kg', 10),
+(8, '操作方式', 'Operation method', '触摸屏、红外 + 蓝牙遥控、Luxsin App(兼容 Android 和 iOS)', 'Touchscreen, Infrared + Bluetooth Remote Control, Luxsin App (compatible with Android and iOS)', 11);
diff --git a/tmp_home_check.png b/tmp_home_check.png
deleted file mode 100644
index d8bd5b3c721247ba8e6529affb4ba43b5410821e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 40881
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
diff --git a/www/app/components/AppFooter.vue b/www/app/components/AppFooter.vue
index f9d3615..c54a199 100644
--- a/www/app/components/AppFooter.vue
+++ b/www/app/components/AppFooter.vue
@@ -43,7 +43,7 @@ const wechatHover = ref(false);
{{ footer.contact_phone }}
-
+
{{ footer.contact_email }}
@@ -92,7 +92,7 @@ const wechatHover = ref(false);
{{ footer?.copyright_text || t('footer.copyright', { year: currentYear }) }}
-
{{ footer.icp_number }}
+
{{ footer.icp_number }}
{{ footer.police_number }}
diff --git a/www/app/components/AppHeader.vue b/www/app/components/AppHeader.vue
index 314d77e..eb232c2 100644
--- a/www/app/components/AppHeader.vue
+++ b/www/app/components/AppHeader.vue
@@ -11,6 +11,32 @@ const appStore = useAppStore();
const { navItems, navAppearance, productSeries, site } = storeToRefs(siteStore);
+// ===== 多语言:按站点配置决定可切换的语言 =====
+// 启用的语言代码;未配置时默认全部语言
+const enabledLocaleCodes = computed(() => {
+ const all: string[] = (unref(locales) || []).map(l => l.code);
+ const raw = site.value?.enabled_locales;
+ if (!raw) return all;
+ const list = raw.split(',').map(s => s.trim()).filter(c => all.includes(c));
+ return list.length ? list : all;
+});
+
+// 实际展示的切换按钮 = i18n locales ∩ 启用列表
+const activeLocales = computed(() => (unref(locales) || []).filter(l => enabledLocaleCodes.value.includes(l.code)));
+
+// 只启用一种语言时不显示切换按钮
+const showLocaleSwitch = computed(() => activeLocales.value.length > 1);
+
+// 当前语言被停用(如只启用中文时访问 /en/*):跳转到首个启用语言的对应页面
+watch(
+ [enabledLocaleCodes, () => locale.value],
+ ([codes]) => {
+ if (!site.value || codes.includes(locale.value)) return;
+ const target = codes[0];
+ if (target) navigateTo(switchLocalePath(target as typeof locale.value));
+ }
+);
+
// 滚动状态:滚动后强制毛玻璃背景 + 深色文字
const { y: scrollY } = useWindowScroll();
const isScrolled = computed(() => scrollY.value > 24);
@@ -186,9 +212,9 @@ function closeMobile() {
-
+
-
+
void) {
{{ isEn ? item.name_en : item.name_zh }}
-
+
{{ isEn ? item.value_en : item.value_zh }}
@@ -127,7 +127,7 @@ function onLeave(el: Element, done: () => void) {
{{ isEn ? item.name_en : item.name_zh }}
-
+
{{ isEn ? item.value_en : item.value_zh }}
diff --git a/www/app/pages/about.vue b/www/app/pages/about.vue
index 18cb60d..16d9a60 100644
--- a/www/app/pages/about.vue
+++ b/www/app/pages/about.vue
@@ -63,10 +63,10 @@ const hasS3 = computed(() => Boolean(s3Title.value || s3Cta.value));
-
+
-
-
+
+
{{ s1Title }}
@@ -79,11 +79,11 @@ const hasS3 = computed(() => Boolean(s3Title.value || s3Cta.value));
-
diff --git a/www/app/types/site.ts b/www/app/types/site.ts
index e2ba619..c2c2866 100644
--- a/www/app/types/site.ts
+++ b/www/app/types/site.ts
@@ -11,6 +11,8 @@ export interface SiteSettings {
favicon_url: string;
logo_dark_url: string;
logo_light_url: string;
+ /** 启用的语言(逗号分隔):zh,en */
+ enabled_locales?: string;
}
export interface FooterSettings {