Compare commits

..

2 Commits

Author SHA1 Message Date
root
9ed486c343 docs: 精简 README(结构/依赖/使用),细节移至 docs/integration.md 2026-07-24 09:15:23 +00:00
root
a91c379096 fix: 组件改用 useLocale 直读自身 i18n 实例(修复嵌入时文案回退成 key)
useI18n({ useScope: 'global' }) 在嵌入时解析到宿主已安装的 i18n 实例,
而非本子项目实例 -> mobileGame.* 文案回退成 i18n key(显示 ID)。
改为 useLocale composable 直接读本模块级 i18n.global,无论是否 app.use() 都正确。
2026-07-24 08:59:35 +00:00
13 changed files with 87 additions and 65 deletions

View File

@@ -1,18 +1,6 @@
# z449 - 移动游戏项目展示页
跨学期 Unity 移动游戏毕设448/449的展示页项目概览、团队、玩家排行榜、版本迭代记录、ML-Agents 实验。
纯前端 Vue 3 应用,独立可渲染与维护,亦可作为 [mainPage](https://git.zikai.wang/zikai/zMainPage) 的子模块git submodule构建期集成展示。
## 功能
- Hero 区:项目元数据(技术栈 / 时间 / 课程)
- 概览 + 截图轮播(自动播放)
- 团队成员卡片
- 玩家分数排行榜(复用 449 现有 PHP 接口 `/449/449rest.php`
- 版本演进时间线2D -> 3D 形态转变,多线并行)
- ML-Agents 强化学习演示视频
- 中英双语(中文默认,英文懒加载)
跨学期 Unity 移动游戏毕设448/449的展示页项目概览、团队、玩家排行榜、版本迭代记录、ML-Agents 实验。纯前端 Vue 3 应用,独立可渲染与维护,亦可作为 [mainPage](https://git.zikai.wang/zikai/zMainPage) 的子模块集成展示。
## 项目结构
@@ -22,51 +10,43 @@ z449/
├── package.json
├── vite.config.js 含 /448 /449 开发代理
├── .npmrc npmmirror 镜像加速
├── src/
│ ├── main.js 独立 app 挂载i18n + 全局样式)
│ ├── App.vue ★ 便携式根组件:接 :locale prop嵌入与独立两用
├── config.js 静态配置(资源路径 / 排行榜接口 / 子标签
├── i18n/ 独立 vue-i18n 实例 + 中英 localemobileGame.* 文案单一来源
├── components/ Hero / 轮播 / 子标签 / 排行榜 / 时间线 / 实验
│ └── ui/ PageShell / Tag自洽 UI 基元)
├── data/ 团队成员 / 版本演进数据
│ └── styles/ variables.css令牌+ base.css重置仅独立 app 加载
└── docs/ 设计文档
├── README.md
├── docs/ 设计文档
└── src/
├── main.js 独立 app 挂载i18n + 全局样式
├── App.vue 便携式根组件(嵌入与独立两用
├── config.js 静态配置(资源路径 / 排行榜接口 / 子标签)
├── i18n/ 独立 vue-i18n 实例 + 中英 locale
├── composables/
│ └── useLocale.js 语言辅助(直读自身 i18n 实例
├── components/ Hero / 轮播 / 子标签 / 排行榜 / 时间线 / 实验
│ └── ui/ PageShell / Tag
├── data/ 团队成员 / 版本演进数据
└── styles/ variables.css令牌+ base.css重置仅独立 app 加载)
```
## 嵌入 mainPagei18n 桥接)
## 外部依赖
`App.vue` 是便携式组件:
| 项 | 说明 |
|----|------|
| Node.js | ≥ 18 |
| `/448` 静态资源 | 游戏截图、WebGL 试玩、APK 下载(站点根提供) |
| `/449` 静态资源 + PHP | 玩家分数排行榜接口 `/449/449rest.php`(站点根提供) |
- **嵌入时**mainPage 侧包装器 `import` 本项目 `src/App.vue`,把父项目当前语言经 `:locale` prop 传入。`App.vue``watch` 该 prop同步到本项目独立的 `vue-i18n` 实例 `locale`,从而跟随父项目语言开关响应式切换。文案来自本项目自有 locale 文件,不共享父项目的 messages
- **独立运行时**:不接收 prop默认中文可自管语言切换。
> 开发时 `vite.config.js` 已把 `/448` `/449` 代理到 `localhost:8080`;生产由 Apache 静态 / PHP 服务提供
样式隔离:子组件均为 scoped全局重置`variables.css` / `base.css`)仅在独立 app 的 `main.js` 加载,嵌入时不加载,复用宿主 `:root` 令牌级联。
## Ubuntu 从零安装
## 如何使用
```bash
# 1. 安装 Node.js 18+
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
# 2. 克隆并安装
# 克隆并安装
git clone https://git.zikai.wang/zikai/z449.git
cd z449
npm install # 已配 npmmirror 镜像加速
# 3. 构建生产版本
npm run build # 产物输出到 dist/
npm run dev # 开发服务器 http://localhost:5174
npm run build # 构建到 dist/
```
开发模式:
```bash
npm run dev # http://localhost:5174
```
> 排行榜与游戏资源依赖站点根的 `/448` `/449` 静态资源与 `/449/449rest.php` 接口。开发时 `vite.config.js` 已配代理到 `localhost:8080`;生产由 Apache 静态 / PHP 服务提供。
## 技术栈
| 项 | 选择 |
@@ -75,3 +55,7 @@ npm run dev # http://localhost:5174
| 构建 | Vite 5 |
| 国际化 | vue-i18n 9中文同步英文懒加载 |
| 包源 | npmmirror.com中国镜像加速 |
## 了解更多
- [嵌入 mainPagei18n prop 桥接 / 样式隔离)](./docs/integration.md)

24
docs/integration.md Normal file
View File

@@ -0,0 +1,24 @@
# 嵌入 mainPage
z449 可作为 [mainPage](https://git.zikai.wang/zikai/zMainPage) 的子模块git submodule经构建期组件集成展示位于 mainPage 的 `third_party/z449`
## 便携式 App.vue
`src/App.vue` 是嵌入与独立两用的根组件:
- **嵌入时**mainPage 侧包装器 `import` 本项目 `src/App.vue`,把父项目当前语言经 `:locale` prop 传入。`App.vue``watch` 该 prop同步到本项目独立的 `vue-i18n` 实例 `locale`,从而跟随父项目语言开关响应式切换。文案来自本项目自有 locale 文件,不共享父项目的 messages。
- **独立运行时**:不接收 prop默认中文。
## i18n 桥接关键点
组件内用 `useLocale` composable **直接读自身模块级 i18n 实例**`i18n.global`),而非 `useI18n({ useScope: 'global' })`
原因:`useI18n({ useScope: 'global' })` 解析的是「当前运行 app 已安装的全局 i18n」。嵌入 mainPage 时那是父项目的实例(不含本项目 `mobileGame.*` 文案),会导致文案回退成 i18n key显示 ID 而非文字)。改为直接引用本项目实例的 `.global`,无论是否 `app.use()` 都拿到本项目的 `t`
## 样式隔离
子组件均为 scoped全局重置`variables.css` / `base.css`)仅在独立 app 的 `main.js` 加载,嵌入时不加载,复用宿主 `:root` 令牌级联。
## 集成 / 升级 / 移除
详见 mainPage 仓库的 [`docs/submodule-z449.md`](https://git.zikai.wang/zikai/zMainPage/src/branch/main/docs/submodule-z449.md)(克隆含子模块、升级 gitlink、移除页签等流程

View File

@@ -4,7 +4,7 @@
// 语言切换:嵌入时父项目经 :locale prop 传入当前语言watch 同步到本实例;
// 独立运行时由本组件内的语言开关 + loadLocaleAsync 自管。
import { ref, watch, defineAsyncComponent } from 'vue'
import { useI18n } from 'vue-i18n'
import { useLocale } from './composables/useLocale.js'
import i18n, { loadLocaleAsync } from './i18n/index.js'
import HeroSection from './components/HeroSection.vue'
import ScreenshotCarousel from './components/ScreenshotCarousel.vue'
@@ -28,7 +28,7 @@ watch(
{ immediate: true }
)
const { t } = useI18n({ useScope: 'global' })
const { t } = useLocale()
const tabs = subTabs
const activeTab = ref(defaultSubTab)

View File

@@ -1,8 +1,8 @@
<script setup>
import { useI18n } from 'vue-i18n'
import { useLocale } from '../composables/useLocale.js'
import { assets } from '../config.js'
const { t } = useI18n({ useScope: 'global' })
const { t } = useLocale()
</script>
<template>

View File

@@ -1,9 +1,9 @@
<script setup>
import { useI18n } from 'vue-i18n'
import { useLocale } from '../composables/useLocale.js'
import Tag from './ui/Tag.vue'
import { heroMeta } from '../config.js'
const { t } = useI18n({ useScope: 'global' })
const { t } = useLocale()
</script>
<template>

View File

@@ -1,8 +1,8 @@
<script setup>
import { useI18n } from 'vue-i18n'
import { useLocale } from '../composables/useLocale.js'
import VersionTimeline from './VersionTimeline.vue'
const { t } = useI18n({ useScope: 'global' })
const { t } = useLocale()
</script>
<template>

View File

@@ -1,9 +1,9 @@
<script setup>
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useLocale } from '../composables/useLocale.js'
import { leaderboard } from '../config.js'
const { t } = useI18n({ useScope: 'global' })
const { t } = useLocale()
const rows = ref([])
const state = ref('loading') // loading | ok | error

View File

@@ -1,9 +1,9 @@
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useLocale } from '../composables/useLocale.js'
import { assets } from '../config.js'
const { t } = useI18n({ useScope: 'global' })
const { t } = useLocale()
const images = assets.screenshots
const current = ref(0)
let timer = null

View File

@@ -1,5 +1,5 @@
<script setup>
import { useI18n } from 'vue-i18n'
import { useLocale } from '../composables/useLocale.js'
const props = defineProps({
tabs: { type: Array, required: true }, // [{ id, labelKey }]
@@ -7,7 +7,7 @@ const props = defineProps({
})
const emit = defineEmits(['update:modelValue'])
const { t } = useI18n({ useScope: 'global' })
const { t } = useLocale()
</script>
<template>

View File

@@ -1,9 +1,9 @@
<script setup>
import { useI18n } from 'vue-i18n'
import { useLocale } from '../composables/useLocale.js'
import TeamCard from './TeamCard.vue'
import Leaderboard from './Leaderboard.vue'
const { t } = useI18n({ useScope: 'global' })
const { t } = useLocale()
</script>
<template>

View File

@@ -1,9 +1,9 @@
<script setup>
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useLocale } from '../composables/useLocale.js'
import { versions, tracks } from '../data/versions.js'
const { t } = useI18n({ useScope: 'global' })
const { t } = useLocale()
// 按 tracks 数组顺序分组3D 最终形态在前);同一线内按 order 排序
const grouped = computed(() => {

View File

@@ -1,6 +1,6 @@
<script setup>
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useLocale } from '../../composables/useLocale.js'
// 可复用页面外壳 -- 统一页面的容器宽度、垂直节奏与标题。
// Props:
@@ -13,7 +13,7 @@ const props = defineProps({
padded: { type: Boolean, default: true }
})
const { t } = useI18n({ useScope: 'global' })
const { t } = useLocale()
const title = computed(() => (props.titleKey ? t(props.titleKey) : ''))
</script>

View File

@@ -0,0 +1,14 @@
import { computed } from 'vue'
import i18n from '../i18n/index.js'
// ★ 子项目自有的语言辅助 composable。
// 关键:直接读模块级 i18n 实例的 global composer而非 useI18n() 注入。
// 原因useI18n({ useScope: 'global' }) 解析的是「当前运行 app 已安装的全局 i18n」
// 嵌入 mainPage 时那是父项目的实例(不含本子项目 mobileGame.* 文案)-> 文案回退成 key。
// 改为直接引用本子项目实例的 .global无论是否 app.use() 都拿到本子项目的 t。
export function useLocale() {
const { t, locale } = i18n.global
const isZh = computed(() => locale.value === 'zh-CN')
const pick = (obj) => (isZh.value ? obj.zh : obj.en)
return { t, locale, isZh, pick }
}