refactor: 清理死代码/提前失败/高内聚低耦合,重写文档
死代码清理: - 删除未使用的 UI 组件 Card.vue / Tag.vue - 移除 app.config.js 死字段 healthTimeoutMs(×2)与 isDev() - 移除 PageShell.vue 死 prop padded(恒为默认值) - 移除 useLocale.js 死导出 isZh/pick/locale - 移除 router/index.js 不可达 try/catch,简化为直接 .map() - 移除 package.json 重复依赖 ical.js(子模块自带) - 移除 i18n 死键 common.notAvailable / common.serviceUnavailable - 移除 vite.config.js 陈旧的 /448 /449 dev proxy(z449 为构建期组件,不走该 URL) 提前失败/日志: - useI18nLazy.js switchLocale 加载语言包失败时 console.error 记录(原静默吞错) - modules/index.js 字段不全的模块跳过时 console.warn 告警(原与文档承诺不符、静默跳过) - deploy.sh 补齐 zWhiteBoard / zPDF_package 的 npm install 与子模块初始化 (均为构建期组件 import,全新检出时缺少依赖会构建失败) 文档对齐现状(无 iframe、无外部域名): - README 改写为简洁版,修正 iframe/ical.js/子模块数量过时描述 - architecture.md 修正 requiresAlive 示例(同源 /api/health,非外部域名) 与「仅白板使用」断言(PDF 也用);移除已删 padded prop - add-module.md 同步移除 padded、修正 requiresAlive 示例 - submodule-timeTableFix.md 修正 ical.js 归属(子模块自带,非父项目共享) - submodule-zPDF_package.md 子项目数量 3 -> 4 - 新增 submodule-zWhiteBoard.md - readme-fixes.md 记录本次勘误
This commit is contained in:
@@ -1,28 +0,0 @@
|
||||
<script setup>
|
||||
defineProps({
|
||||
hoverable: { type: Boolean, default: false }
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card" :class="{ 'card--hover': hoverable }">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-sm);
|
||||
padding: var(--space-lg);
|
||||
}
|
||||
.card--hover {
|
||||
transition: transform var(--transition), box-shadow var(--transition);
|
||||
}
|
||||
.card--hover:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-hover);
|
||||
}
|
||||
</style>
|
||||
@@ -3,17 +3,15 @@ import { computed } from 'vue'
|
||||
import { useLocale } from '../../composables/useLocale.js'
|
||||
|
||||
// ★ 可复用页面外壳 -- 统一页面的容器宽度、垂直节奏与标题,消除各模块重复包裹。
|
||||
// 高内聚:所有页面布局令牌集中于此;低耦合:模块只声明 titleKey/fluid/padded,
|
||||
// 高内聚:所有页面布局令牌集中于此;低耦合:模块只声明 titleKey/fluid,
|
||||
// 不关心 .container 实现。新增页签照此复用,无需各写一套 wrapper。
|
||||
//
|
||||
// Props:
|
||||
// titleKey: i18n 键(如 'tabs.whiteboard'),渲染为页面主标题;不传则无标题
|
||||
// fluid: true 时突破 1080px 限制(全宽 + 左右 padding),如日历页
|
||||
// padded: false 时去掉默认上下间距(页面完全自管布局时用)
|
||||
const props = defineProps({
|
||||
titleKey: { type: String, default: '' },
|
||||
fluid: { type: Boolean, default: false },
|
||||
padded: { type: Boolean, default: true }
|
||||
fluid: { type: Boolean, default: false }
|
||||
})
|
||||
|
||||
const { t } = useLocale()
|
||||
@@ -21,7 +19,7 @@ const title = computed(() => (props.titleKey ? t(props.titleKey) : ''))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-shell" :class="{ 'page-shell--padded': padded }">
|
||||
<div class="page-shell">
|
||||
<div :class="fluid ? 'page-shell__fluid' : 'container page-shell__inner'">
|
||||
<div v-if="title || $slots.actions" class="page-shell__head">
|
||||
<h1 v-if="title" class="page-shell__title">{{ title }}</h1>
|
||||
@@ -35,7 +33,7 @@ const title = computed(() => (props.titleKey ? t(props.titleKey) : ''))
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page-shell--padded {
|
||||
.page-shell {
|
||||
padding: var(--space-xl) 0 var(--space-2xl);
|
||||
}
|
||||
/* 标准容器宽度复用全局 .container(max-width 1080px,居中,左右 padding) */
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<script setup>
|
||||
defineProps({
|
||||
tag: { type: String, default: 'span' }
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="tag" class="tag">
|
||||
<slot />
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 10px;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 500;
|
||||
color: var(--accent);
|
||||
background: var(--accent-weak);
|
||||
border-radius: 999px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
@@ -14,6 +14,9 @@ export function useI18nLazy() {
|
||||
switching.value = true
|
||||
try {
|
||||
await loadLocaleAsync(locale)
|
||||
} catch (e) {
|
||||
// 语言包加载失败不能静默:记录到控制台,便于排查(如 chunk 404)
|
||||
console.error('[mainPage] 加载语言包失败', locale, e)
|
||||
} finally {
|
||||
switching.value = false
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 共享的语言辅助 composable,消除各组件重复定义 L()
|
||||
// 共享的语言辅助 composable,消除各组件重复定义。
|
||||
// 用法:
|
||||
// const { t, locale, isZh, pick } = useLocale()
|
||||
// t('some.key') // 走 i18n(推荐,文案在 locale 文件)
|
||||
// isZh.value // 判断当前是否中文
|
||||
// pick({ zh, en }) // 从 {zh,en} 对象取当前语言值(用于数据文件)
|
||||
// const { t } = useLocale()
|
||||
// t('some.key') // 走 i18n(文案在 locale 文件)
|
||||
// 页签包装器透传 locale 给子模块时直接读 i18n.global.locale(见各 wrapper)。
|
||||
export function useLocale() {
|
||||
const { t, locale } = useI18n({ useScope: 'global' })
|
||||
const isZh = computed(() => locale.value === 'zh-CN')
|
||||
const pick = (obj) => (isZh.value ? obj.zh : obj.en)
|
||||
return { t, locale, isZh, pick }
|
||||
const { t } = useI18n({ useScope: 'global' })
|
||||
return { t }
|
||||
}
|
||||
|
||||
@@ -20,18 +20,14 @@ export const appConfig = {
|
||||
// 存活探针路径;周期探测,挂掉则隐藏页签并卸载组件
|
||||
healthPath: '/api/health',
|
||||
// 默认打开的记事本 id(Whiteboard.vue 的 resolveBoardId 回退用)
|
||||
defaultBoardId: 'share',
|
||||
// 探测超时(毫秒)
|
||||
healthTimeoutMs: 4000
|
||||
defaultBoardId: 'share'
|
||||
},
|
||||
// PDF 转换服务(zTools2 提供 /api/pdf/* REST API;zPDF_package 组件同源 fetch 调用)
|
||||
pdf: {
|
||||
// 服务根地址(含协议与域名);默认空串 = 同源相对路径
|
||||
baseUrl: BASE_URL,
|
||||
// 存活探针路径;周期探测,挂掉则隐藏页签并卸载组件
|
||||
healthPath: '/api/health',
|
||||
// 探测超时(毫秒)
|
||||
healthTimeoutMs: 4000
|
||||
healthPath: '/api/health'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,11 +41,3 @@ export function healthUrl(service) {
|
||||
if (!cfg) return ''
|
||||
return `${cfg.baseUrl}${cfg.healthPath}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前是否为开发/测试环境(便于组件按环境调整行为)。
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isDev() {
|
||||
return !import.meta.env.PROD
|
||||
}
|
||||
|
||||
@@ -5,9 +5,7 @@ export default {
|
||||
title: 'Zikai'
|
||||
},
|
||||
common: {
|
||||
loading: 'Loading…',
|
||||
notAvailable: 'Not available',
|
||||
serviceUnavailable: 'Service is temporarily unavailable. Please try again later.'
|
||||
loading: 'Loading…'
|
||||
},
|
||||
tabs: {
|
||||
mobileGame: 'Mobile Game Project',
|
||||
|
||||
@@ -4,9 +4,7 @@ export default {
|
||||
title: 'Zikai'
|
||||
},
|
||||
common: {
|
||||
loading: '加载中…',
|
||||
notAvailable: '暂不可用',
|
||||
serviceUnavailable: '服务暂不可用,请稍后再试。'
|
||||
loading: '加载中…'
|
||||
},
|
||||
tabs: {
|
||||
mobileGame: '移动游戏项目',
|
||||
|
||||
@@ -19,11 +19,16 @@
|
||||
|
||||
const moduleFiles = import.meta.glob('./*/index.js', { eager: true })
|
||||
|
||||
const modules = Object.values(moduleFiles)
|
||||
.map((mod) => mod?.default)
|
||||
.filter(Boolean)
|
||||
// 字段完整性兜底:缺 id/tabKey/component 的模块无法生成页签与路由,跳过
|
||||
.filter((m) => m && m.id && m.tabKey && typeof m.component === 'function')
|
||||
const modules = Object.entries(moduleFiles)
|
||||
.map(([path, mod]) => ({ path, mod: mod?.default }))
|
||||
.filter(({ mod }) => mod != null)
|
||||
// 字段完整性兜底:缺 id/tabKey/component 的模块无法生成页签与路由,跳过并告警
|
||||
.filter(({ path, mod }) => {
|
||||
if (mod.id && mod.tabKey && typeof mod.component === 'function') return true
|
||||
console.warn('[mainPage] 模块缺少必填字段,已跳过', path)
|
||||
return false
|
||||
})
|
||||
.map(({ mod }) => mod)
|
||||
// order 缺省 999:排在所有显式 order 页签之后(hidden 页常用 99 之类显式值)
|
||||
.sort((a, b) => (a.order ?? 999) - (b.order ?? 999))
|
||||
|
||||
|
||||
@@ -2,24 +2,17 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import modules from '../modules/index.js'
|
||||
|
||||
const moduleRoutes = modules
|
||||
.map((m) => {
|
||||
try {
|
||||
// 模块可声明 param(如 'boardId')以接受 /<id>/<param> 形式的子路径,
|
||||
// 形如 /whiteboard/share。可选参数 (? 后缀) 使 /whiteboard 仍可访问。
|
||||
const paramPart = m.param ? `/:${m.param}?` : ''
|
||||
return {
|
||||
path: `/${m.id}${paramPart}`,
|
||||
name: m.id,
|
||||
component: m.component,
|
||||
meta: { moduleId: m.id }
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`[router] 模块 ${m?.id} 路由生成失败,已跳过:`, e)
|
||||
return null
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
const moduleRoutes = modules.map((m) => {
|
||||
// 模块可声明 param(如 'boardId')以接受 /<id>/<param> 形式的子路径,
|
||||
// 形如 /whiteboard/share。可选参数 (? 后缀) 使 /whiteboard 仍可访问。
|
||||
const paramPart = m.param ? `/:${m.param}?` : ''
|
||||
return {
|
||||
path: `/${m.id}${paramPart}`,
|
||||
name: m.id,
|
||||
component: m.component,
|
||||
meta: { moduleId: m.id }
|
||||
}
|
||||
})
|
||||
|
||||
const routes = [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user