diff --git a/game-studio/src/components/EmptyState.vue b/game-studio/src/components/EmptyState.vue
index 15b8719f..b638f8a8 100644
--- a/game-studio/src/components/EmptyState.vue
+++ b/game-studio/src/components/EmptyState.vue
@@ -3,14 +3,18 @@
* EmptyState —— 空态/错误态占位(列表无数据、加载失败、无项目等场景复用)。
* 支持图标、标题、描述、可选操作按钮(slot 或 actionText+emit)。
*/
+import Icon from './Icon.vue'
+
withDefaults(
defineProps<{
/** 主标题 */
title?: string
/** 描述文案 */
description?: string
- /** 顶部图标字符(emoji 或符号,深色风用简洁符号) */
+ /** 顶部图标字符(emoji 或符号)——新代码优先用 iconName(SVG) */
icon?: string
+ /** SVG 图标名(传则用 渲染,优先于 icon;满足 no-emoji-icons 红线) */
+ iconName?: string
/** 操作按钮文案(传则显示按钮,点击触发 action 事件) */
actionText?: string
}>(),
@@ -28,7 +32,10 @@ const emit = defineEmits<{ (e: 'action'): void }>()
-
{{ icon }}
+
+
+ {{ icon }}
+
{{ title }}
{{ description }}
diff --git a/game-studio/src/components/GameCard.vue b/game-studio/src/components/GameCard.vue
index 3b7ab0e1..20caa04d 100644
--- a/game-studio/src/components/GameCard.vue
+++ b/game-studio/src/components/GameCard.vue
@@ -6,6 +6,7 @@
* 不直接发请求(互动由 InteractBar / 父级 store 处理),保持组件纯展示 + 事件上抛。
*/
import { computed } from 'vue'
+import Icon from './Icon.vue'
import type { FeedCard } from '../api/types'
import { resolveCoverStyle } from '../utils/coverGradient'
@@ -74,7 +75,7 @@ const fallbackVars = computed
>(() => {
{{ card.title }}
- ▶
+
@@ -84,8 +85,10 @@ const fallbackVars = computed>(() => {
@{{ card.authorName }}
- ▶ {{ card.playCount }}
- ♥ {{ card.likeCount }}
+ {{ card.playCount }}
+
+ {{ card.likeCount }}
+
@@ -106,7 +109,8 @@ const fallbackVars = computed>(() => {
background-color: var(--panel);
background-size: cover;
background-position: center;
- border-radius: var(--radius);
+ /* 统一圆角令牌(旧 --radius=8 等同 --radius-md,此处沿用默认中号圆角) */
+ border-radius: var(--radius-md);
overflow: hidden;
cursor: pointer;
}
@@ -120,10 +124,11 @@ const fallbackVars = computed>(() => {
justify-content: center;
padding: 24px;
overflow: hidden;
- /* --cover-grad 由组件按 hash 注入;变量缺省时退回原中性渐变(健壮兜底) */
+ /* --cover-grad 由组件按 hash 注入;变量缺省时退回原中性渐变(健壮兜底,用面板令牌) */
background: var(--cover-grad, linear-gradient(135deg, var(--panel), var(--panel-2)));
}
-/* 右上柔光聚光点(对齐 demo .game-cover::before 的暖光球,落位由 --cover-glow-* 错开) */
+/* 右上柔光聚光点(对齐 demo .game-cover::before 的暖光球,落位由 --cover-glow-* 错开)。
+ 此处 50% 为「径向柔光球」装饰(非 UI 控件/头像),与 loading 旋转环同属圆形例外,保留圆。 */
.game-card__cover-fallback::before {
content: '';
position: absolute;
@@ -132,8 +137,8 @@ const fallbackVars = computed>(() => {
width: 64px;
height: 64px;
border-radius: 50%;
- background: var(--cover-glow, rgba(49, 230, 255, 0.28));
- box-shadow: 0 0 30px var(--cover-glow, rgba(49, 230, 255, 0.28));
+ background: var(--cover-glow, color-mix(in srgb, var(--accent) 28%, transparent));
+ box-shadow: 0 0 30px var(--cover-glow, color-mix(in srgb, var(--accent) 28%, transparent));
pointer-events: none;
}
/* 底部波形装饰(对齐 demo .game-cover::after 的折线波纹,向下渐隐融入卡底;相位由变体类切换 clip-path) */
@@ -144,7 +149,11 @@ const fallbackVars = computed>(() => {
right: -8%;
bottom: 0;
height: 54%;
- background: linear-gradient(180deg, var(--cover-wave, rgba(49, 230, 255, 0.4)), rgba(9, 11, 21, 0.96));
+ background: linear-gradient(
+ 180deg,
+ var(--cover-wave, color-mix(in srgb, var(--accent) 40%, transparent)),
+ color-mix(in srgb, var(--bg) 96%, transparent)
+ );
clip-path: polygon(0 100%, 16% 40%, 27% 72%, 42% 25%, 58% 72%, 74% 36%, 100% 100%);
pointer-events: none;
}
@@ -165,9 +174,9 @@ const fallbackVars = computed>(() => {
font-weight: 700;
line-height: 1.35;
color: var(--ink);
- text-shadow: 0 2px 12px rgba(0, 0, 0, 0.45);
+ text-shadow: 0 2px 12px color-mix(in srgb, #000 45%, transparent);
}
-/* 中央播放按钮 */
+/* 中央播放按钮(半透深底 + 霓虹描边玻璃感;改统一圆角矩形,杀正圆) */
.game-card__play {
position: absolute;
top: 50%;
@@ -178,11 +187,10 @@ const fallbackVars = computed>(() => {
display: flex;
align-items: center;
justify-content: center;
- font-size: 22px;
color: var(--ink);
- background: rgba(7, 9, 17, 0.55);
- border: 1px solid var(--cyan);
- border-radius: 50%;
+ background: color-mix(in srgb, var(--bg) 55%, transparent);
+ border: 1px solid var(--accent);
+ border-radius: var(--radius-lg);
backdrop-filter: blur(2px);
}
/* 底部信息条 */
@@ -211,6 +219,9 @@ const fallbackVars = computed>(() => {
align-items: flex-end;
}
.game-card__stat {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
font-size: 12px;
color: var(--muted);
}
diff --git a/game-studio/src/components/Icon.vue b/game-studio/src/components/Icon.vue
index 9eeb3b10..3f2fe634 100644
--- a/game-studio/src/components/Icon.vue
+++ b/game-studio/src/components/Icon.vue
@@ -24,6 +24,12 @@ const PATHS: Record = {
spark: '',
settings: '',
globe: '',
+ flag: '',
+ projects: '',
+ dashboard: '',
+ enterprise: '',
+ help: '',
+ warning: '',
}
// 找不到的图标渲染空 svg(不报错)
diff --git a/game-studio/src/components/InteractBar.vue b/game-studio/src/components/InteractBar.vue
index ed3a30bb..3ae4fadc 100644
--- a/game-studio/src/components/InteractBar.vue
+++ b/game-studio/src/components/InteractBar.vue
@@ -4,6 +4,8 @@
* 纯展示 + 事件上抛:点击各按钮 emit('interact', action),由父级调 feedStore.interact 处理乐观更新。
* action 取值严格贴 feed.yaml FeedInteractReqVO.action:1点赞 2收藏 3分享 4举报。
*/
+import { useI18n } from 'vue-i18n'
+import Icon from './Icon.vue'
import type { FeedAction } from '../api/types'
defineProps<{
@@ -17,14 +19,17 @@ defineProps<{
/** 互动事件:上抛 action(1赞2藏3享4报) */
const emit = defineEmits<{ (e: 'interact', action: FeedAction): void }>()
+
+// i18n:标签文案走 feed 命名空间(globalInjection 已开,模板亦可用 $t)
+const { t } = useI18n()
-
+
@@ -68,16 +73,16 @@ const emit = defineEmits<{ (e: 'interact', action: FeedAction): void }>()
display: flex;
align-items: center;
justify-content: center;
- font-size: 20px;
- /* 抖音式悬浮玻璃感(对齐 demo .feed-action i,spec §3.3 V-1):
+ /* 抖音式悬浮玻璃感(对齐 mockup .feed-rail button,spec §3.3 V-1):
半透白底 + 半透白描边 + 轻投影,配 backdrop-filter 模糊出通透玻璃层次。 */
- background: rgba(255, 255, 255, 0.1);
- border: 1px solid rgba(255, 255, 255, 0.15);
- box-shadow: 0 12px 28px rgba(0, 0, 0, 0.26);
+ background: color-mix(in srgb, var(--ink) 10%, transparent);
+ border: 1px solid color-mix(in srgb, var(--ink) 15%, transparent);
+ box-shadow: 0 12px 28px color-mix(in srgb, #000 26%, transparent);
/* 模糊适度(8px)兼顾移动端性能;-webkit- 前缀兜底 iOS Safari/旧 WebView */
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
- border-radius: 50%;
+ /* 统一圆角矩形(对齐 mockup .feed-rail button 的 --radius-lg,杀正圆药丸) */
+ border-radius: var(--radius-lg);
transition: transform 0.1s ease, color 0.15s ease, border-color 0.15s ease;
}
.interact-bar__item:active .interact-bar__icon {
@@ -92,9 +97,9 @@ const emit = defineEmits<{ (e: 'interact', action: FeedAction): void }>()
color: var(--pink);
border-color: var(--pink);
}
-/* 已藏高亮 amber */
+/* 已藏高亮(warning=amber 语义层,杀硬编码) */
.interact-bar__item.is-active-fav .interact-bar__icon {
- color: var(--amber);
- border-color: var(--amber);
+ color: var(--warning);
+ border-color: var(--warning);
}
diff --git a/game-studio/src/components/TabBar.vue b/game-studio/src/components/TabBar.vue
index 7d69b016..39e044a5 100644
--- a/game-studio/src/components/TabBar.vue
+++ b/game-studio/src/components/TabBar.vue
@@ -11,11 +11,15 @@
import { useRoute, useRouter } from 'vue-router'
import { computed, onMounted } from 'vue'
import { storeToRefs } from 'pinia'
+import { useI18n } from 'vue-i18n'
import { useMessageStore } from '../store/message'
import { useUserStore } from '../store/user'
+import Icon from './Icon.vue'
const route = useRoute()
const router = useRouter()
+// 文案 i18n:四入口走全局命名空间 common.nav.*(已在 i18n/index.ts 注册,全局作用域直接取)
+const { t } = useI18n()
// 消息未读角标:与消息中心同源(store 单一数据源)
const messageStore = useMessageStore()
const { unreadTotal } = storeToRefs(messageStore)
@@ -30,10 +34,10 @@ interface TabItem {
match: string
/** 额外高亮前缀(如「我的」同时覆盖 /project 二级页),可选 */
extraMatch?: string
- /** 文案 */
- label: string
- /** 图标字符 */
- icon: string
+ /** 文案 i18n key(全局命名空间 common.nav.*,模板内经 t() 取) */
+ labelKey: string
+ /** 图标名(统一走 ,替代旧 Unicode 字形) */
+ iconName: string
/** 是否显示未读红点(仅「消息」Tab) */
showBadge?: boolean
}
@@ -41,10 +45,10 @@ interface TabItem {
/** 四入口(顺序:游戏流 / 创作 / 消息 / 我的)。
* 「我的」指向个人中心 /profile,并额外高亮 /project(我的项目二级页归属「我的」)。 */
const tabs: TabItem[] = [
- { path: '/feed', match: '/feed', label: '游戏流', icon: '◎' },
- { path: '/create', match: '/create', label: '创作', icon: '✦' },
- { path: '/message', match: '/message', label: '消息', icon: '✉', showBadge: true },
- { path: '/profile', match: '/profile', extraMatch: '/project', label: '我的', icon: '◰' },
+ { path: '/feed', match: '/feed', labelKey: 'common.nav.feed', iconName: 'feed' },
+ { path: '/create', match: '/create', labelKey: 'common.nav.create', iconName: 'create' },
+ { path: '/message', match: '/message', labelKey: 'common.nav.message', iconName: 'message', showBadge: true },
+ { path: '/profile', match: '/profile', extraMatch: '/project', labelKey: 'common.nav.me', iconName: 'me' },
]
/** 判断某 tab 是否被当前 path 命中(match 或 extraMatch 的精确/前缀匹配) */
@@ -88,13 +92,13 @@ onMounted(() => {
@click="go(tab)"
>
- {{ tab.icon }}
+
{{ unreadTotal > 99 ? '99+' : unreadTotal }}
- {{ tab.label }}
+ {{ t(tab.labelKey) }}
@@ -121,10 +125,9 @@ onMounted(() => {
}
.tabbar__icon {
position: relative;
- font-size: 20px;
line-height: 1;
}
-/* 未读红点角标(叠在图标右上角,pink 点缀,与消息中心同色) */
+/* 未读红点角标(叠在图标右上角,danger 点缀,与消息中心同色) */
.tabbar__badge {
position: absolute;
top: -6px;
@@ -139,15 +142,17 @@ onMounted(() => {
font-size: 10px;
font-weight: 700;
line-height: 1;
- color: #2a0610;
- background: var(--pink);
- border-radius: 8px;
+ /* danger 之上的近黑前景令牌(原 #2a0610 硬编码) */
+ color: var(--on-danger);
+ background: var(--danger);
+ /* 统一圆角矩形(原 8px 近正圆改 --radius-xs) */
+ border-radius: var(--radius-xs);
}
.tabbar__label {
font-size: 11px;
}
-/* 激活态 cyan 高亮 */
+/* 激活态 accent 高亮 */
.tabbar__item.is-active {
- color: var(--cyan);
+ color: var(--accent);
}
diff --git a/game-studio/src/i18n/index.ts b/game-studio/src/i18n/index.ts
index 190ff328..d2e4e307 100644
--- a/game-studio/src/i18n/index.ts
+++ b/game-studio/src/i18n/index.ts
@@ -6,13 +6,22 @@
import { createI18n } from 'vue-i18n'
import zhCN from '../locales/zh-CN'
import enUS from '../locales/en-US'
+// 各页域命名空间(P4 起按面拆分文件,集中在此合入全局 messages)。
+// 注:profile 走页内 local-scope 注入(见 Profile.vue),故此处只并 feed / create。
+import feedZh from '../locales/feed.zh'
+import feedEn from '../locales/feed.en'
+import createZh from '../locales/create.zh'
+import createEn from '../locales/create.en'
export const i18n = createI18n({
legacy: false, // 组合式 API(useI18n / $t),关闭 legacy 选项式
globalInjection: true, // 模板内可直接用 $t
locale: 'zh-CN',
fallbackLocale: 'zh-CN',
- messages: { 'zh-CN': zhCN, 'en-US': enUS },
+ messages: {
+ 'zh-CN': { ...zhCN, ...feedZh, ...createZh },
+ 'en-US': { ...enUS, ...feedEn, ...createEn },
+ },
})
export default i18n
diff --git a/game-studio/src/locales/create.en.ts b/game-studio/src/locales/create.en.ts
new file mode 100644
index 00000000..58bd4b73
--- /dev/null
+++ b/game-studio/src/locales/create.en.ts
@@ -0,0 +1,31 @@
+/**
+ * 创作入口页(/create)英文文案(与 create.zh.ts 同构)。缺 key 时由 i18n 回落 zh-CN。
+ * 由主代理在 i18n/index.ts 中合并进 en-US。
+ */
+export default {
+ create: {
+ // —— 顶部标题区 ——
+ title: 'One sentence, one game',
+ subtitle: 'Pick a template, describe your idea in one line, and AI builds it',
+ // —— 模板选择区 ——
+ pickTemplate: 'Pick a template',
+ templateLoading: 'Loading templates…',
+ // 真加载失败(网络/服务异常)
+ loadFailedTitle: 'Failed to load templates',
+ loadFailedDesc: 'Network error or service not ready, please retry',
+ reload: 'Reload',
+ // 空(非故障):玩法模板体系升级中
+ upgradingTitle: 'Template system is upgrading',
+ upgradingDesc: 'The new one-sentence creator is coming soon — explore the feed first',
+ goFeed: 'Go to feed',
+ // —— 一句话描述输入区 ——
+ describe: 'Describe in one line',
+ promptPlaceholder: 'e.g. an idle game where tapping earns coins, faster and faster',
+ // 示例 Prompt 一键填入前缀(后接模板示例文案)
+ useExample: 'Use example: ',
+ // —— 底部提交 ——
+ submit: 'Generate',
+ submitting: 'Creating…',
+ tip: 'Generation takes a few seconds; track status on the progress page',
+ },
+}
diff --git a/game-studio/src/locales/create.zh.ts b/game-studio/src/locales/create.zh.ts
new file mode 100644
index 00000000..e49f4ac7
--- /dev/null
+++ b/game-studio/src/locales/create.zh.ts
@@ -0,0 +1,32 @@
+/**
+ * 创作入口页(/create)中文文案。命名空间:create。
+ * 由主代理在 i18n/index.ts 中合并进 zh-CN(与 create.en.ts 同构)。
+ * 通用词(提交/取消/重试/加载/空态)复用 common.action / common.state,不在此重复定义。
+ */
+export default {
+ create: {
+ // —— 顶部标题区 ——
+ title: '一句话,做个小游戏',
+ subtitle: '选一个玩法模板,用一句话描述你的创意,AI 帮你生成',
+ // —— 模板选择区 ——
+ pickTemplate: '选择玩法模板',
+ templateLoading: '模板加载中…',
+ // 真加载失败(网络/服务异常)
+ loadFailedTitle: '模板加载失败',
+ loadFailedDesc: '网络异常或服务未就绪,请重试',
+ reload: '重新加载',
+ // 空(非故障):玩法模板体系升级中
+ upgradingTitle: '玩法模板体系升级中',
+ upgradingDesc: '新版「一句话创作」即将上线,先去游戏流逛逛吧',
+ goFeed: '去游戏流',
+ // —— 一句话描述输入区 ——
+ describe: '用一句话描述',
+ promptPlaceholder: '例如:做一个点击就能赚金币、越点越快的放置小游戏',
+ // 示例 Prompt 一键填入前缀(后接模板示例文案)
+ useExample: '用示例:',
+ // —— 底部提交 ——
+ submit: '开始生成',
+ submitting: '正在创建…',
+ tip: '生成约需数秒,期间可在进度页查看状态',
+ },
+}
diff --git a/game-studio/src/locales/feed.en.ts b/game-studio/src/locales/feed.en.ts
new file mode 100644
index 00000000..a105765a
--- /dev/null
+++ b/game-studio/src/locales/feed.en.ts
@@ -0,0 +1,40 @@
+/**
+ * 消费侧 Feed 簇文案(英文,与 feed.zh 同构)。命名空间:feed。
+ * 缺 key 时由 i18n 回落 zh-CN。
+ */
+export default {
+ feed: {
+ /** 顶部混合流入口名 */
+ recommend: 'For You',
+ /** 专区名兜底 */
+ zone: 'Zone',
+ /** 首屏加载提示(带专区名插值) */
+ loadingZone: 'Loading {name}…',
+ /** 空态标题 */
+ emptyTitle: 'No games here yet',
+ /** 空态描述 */
+ emptyDesc: 'Try another zone, or check back later',
+ /** 空态/重新拉取按钮 */
+ refresh: 'Refresh',
+ /** 互动转正引导弹窗:标题 */
+ loginTitle: 'Sign in to interact',
+ /** 互动转正引导弹窗:正文 */
+ loginMessage: 'Sign in to like, save and share the games you love',
+ /** 互动转正引导弹窗:确认按钮 */
+ loginConfirm: 'Sign in',
+ /** 互动转正引导弹窗:取消按钮 */
+ loginCancel: 'Not now',
+ /** 举报理由输入提示 */
+ reportPrompt: 'Please describe the issue (inappropriate content / infringement, etc.)',
+ /** 举报提交成功提示 */
+ reportDone: 'Report submitted, we will review it shortly',
+ /** 互动条:收藏标签 */
+ favorite: 'Save',
+ /** 互动条:分享标签 */
+ share: 'Share',
+ /** 互动条:举报标签 */
+ report: 'Report',
+ /** 互动条:点赞标签 */
+ like: 'Like',
+ },
+}
diff --git a/game-studio/src/locales/feed.zh.ts b/game-studio/src/locales/feed.zh.ts
new file mode 100644
index 00000000..cefe6fb6
--- /dev/null
+++ b/game-studio/src/locales/feed.zh.ts
@@ -0,0 +1,41 @@
+/**
+ * 消费侧 Feed 簇文案(中文基线)。命名空间:feed。
+ * 由 i18n/index.ts 合并进 zh-CN messages(主代理负责合并)。
+ * 通用词(立即玩/分享/重试/更多/加载中/这里还没有内容…)复用 common.*,本文件只放 Feed 专属文案。
+ */
+export default {
+ feed: {
+ /** 顶部混合流入口名(非专区时的标题) */
+ recommend: '推荐',
+ /** 专区名兜底(zones 里查不到对应 id 时) */
+ zone: '专区',
+ /** 首屏加载提示(带专区名插值),如「正在加载 推荐…」 */
+ loadingZone: '正在加载 {name}…',
+ /** 空态标题(加载完成但无数据) */
+ emptyTitle: '这里还没有游戏',
+ /** 空态描述 */
+ emptyDesc: '换个专区,或稍后再来看看',
+ /** 空态/重新拉取按钮(区别于 common.action.retry「重试」,此处语义为刷新当前流) */
+ refresh: '刷新',
+ /** 互动转正引导弹窗:标题 */
+ loginTitle: '登录后才能互动',
+ /** 互动转正引导弹窗:正文 */
+ loginMessage: '登录账号即可点赞、收藏、分享你喜欢的小游戏',
+ /** 互动转正引导弹窗:确认按钮(跳登录) */
+ loginConfirm: '去登录',
+ /** 互动转正引导弹窗:取消按钮(留在当前流) */
+ loginCancel: '再看看',
+ /** 举报理由输入提示(reason 必填,契约 action=4) */
+ reportPrompt: '请填写举报理由(违规内容/侵权等)',
+ /** 举报提交成功提示 */
+ reportDone: '已提交举报,平台将尽快处理',
+ /** 互动条:收藏标签 */
+ favorite: '藏',
+ /** 互动条:分享标签 */
+ share: '享',
+ /** 互动条:举报标签 */
+ report: '报',
+ /** 互动条:点赞标签(likeCount 为空时回退展示) */
+ like: '赞',
+ },
+}
diff --git a/game-studio/src/locales/profile.en.ts b/game-studio/src/locales/profile.en.ts
new file mode 100644
index 00000000..71b12d97
--- /dev/null
+++ b/game-studio/src/locales/profile.en.ts
@@ -0,0 +1,30 @@
+/**
+ * 「我的」个人中心页域文案(英文,与 profile.zh.ts 同构)。命名空间 profile.*。
+ * 缺 key 时由 vue-i18n 回落基线(zh-CN)。作为 Profile.vue 局部 i18n 消息注入。
+ */
+export default {
+ profile: {
+ // 账号信息
+ guest: 'Guest',
+ avatarFallback: 'U', // 无头像兜底首字(英文用 U=User)
+ creatorTag: 'Creator',
+ // 创作成长卡
+ levelLabel: 'Creator level',
+ publishedLabel: 'Published works',
+ nextMilestone: '{n} more works to the next milestone',
+ // 功能入口菜单
+ menu: {
+ projects: 'My projects',
+ dashboard: 'Analytics',
+ biz: 'Enterprise',
+ message: 'Inbox',
+ settings: 'Settings',
+ help: 'Help & feedback',
+ },
+ // 占位功能项点击提示
+ comingSoon: '{name} is coming soon',
+ // 退出登录
+ logout: 'Log out',
+ logoutConfirm: 'You will need to sign in again to view your projects and creations. Log out?',
+ },
+}
diff --git a/game-studio/src/locales/profile.zh.ts b/game-studio/src/locales/profile.zh.ts
new file mode 100644
index 00000000..25a48dfc
--- /dev/null
+++ b/game-studio/src/locales/profile.zh.ts
@@ -0,0 +1,33 @@
+/**
+ * 「我的」个人中心页域文案(中文基线)。命名空间 profile.*。
+ * 作为 Profile.vue 局部 i18n 消息注入(useI18n useScope:'local'),不入全局 i18n/index.ts。
+ * 通用词(取消/退出登录等)优先复用 common.*,本文件只放页面专属文案。
+ * 中英同构:profile.en.ts 必须保持完全相同的 key 结构。
+ */
+export default {
+ profile: {
+ // 账号信息
+ guest: '游客', // 未取到昵称时的占位昵称
+ avatarFallback: '游', // 无头像时取昵称首字兜底,昵称也缺失时用此字
+ creatorTag: '创作者', // 创作者白名单标识
+ // 创作成长卡
+ levelLabel: '创作等级',
+ publishedLabel: '已发布作品',
+ // 距下个里程碑还差 {n} 个作品({n} 由模板插值)
+ nextMilestone: '距下个里程碑还差 {n} 个作品',
+ // 功能入口菜单
+ menu: {
+ projects: '我的项目',
+ dashboard: '创作数据',
+ biz: '企业定制',
+ message: '消息',
+ settings: '设置',
+ help: '帮助与反馈',
+ },
+ // 占位功能项点击提示:{name}功能即将上线({name} 为入口名)
+ comingSoon: '{name}功能即将上线',
+ // 退出登录
+ logout: '退出登录', // 入口按钮 + 二次确认标题/确认按钮共用
+ logoutConfirm: '退出后需重新登录才能查看你的项目与创作,确定退出吗?',
+ },
+}
diff --git a/game-studio/src/views/create/Create.vue b/game-studio/src/views/create/Create.vue
index fb31869f..cbe448af 100644
--- a/game-studio/src/views/create/Create.vue
+++ b/game-studio/src/views/create/Create.vue
@@ -16,10 +16,15 @@
*/
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
+import { useI18n } from 'vue-i18n'
import { useCreateStore } from '../../store/create'
import { useProjectStore } from '../../store/project'
import type { AigcTemplate } from '../../api/types'
import { LoadingBar, EmptyState, AppButton } from '../../components'
+import Icon from '../../components/Icon.vue'
+
+// i18n:文案统一走 create 命名空间(create.zh.ts / create.en.ts)+ common 通用词,禁裸中文
+const { t } = useI18n()
const router = useRouter()
const createStore = useCreateStore()
@@ -126,40 +131,40 @@ onMounted(loadTemplates)
- 选择玩法模板
+ {{ t('create.pickTemplate') }}
-
- 模板加载中…
+
+ {{ t('create.templateLoading') }}
@@ -178,7 +183,9 @@ onMounted(loadTemplates)
{{ tpl.name }}
{{ tpl.description }}
- ✓
+
+
+
@@ -187,7 +194,7 @@ onMounted(loadTemplates)
- 用一句话描述
+ {{ t('create.describe') }}
@@ -196,7 +203,7 @@ onMounted(loadTemplates)
class="prompt-input"
:maxlength="PROMPT_MAX"
rows="4"
- placeholder="例如:做一个点击就能赚金币、越点越快的放置小游戏"
+ :placeholder="t('create.promptPlaceholder')"
/>
@@ -216,9 +224,9 @@ onMounted(loadTemplates)
@@ -242,8 +250,8 @@ onMounted(loadTemplates)
margin: 0;
font-size: 22px;
font-weight: 800;
- /* 青→紫渐变标题,呼应科技风 */
- background: linear-gradient(90deg, var(--cyan), var(--violet));
+ /* 设计体系签名渐变(青→绿,--grad-primary)填充标题文字,主题恒定、呼应科技风 */
+ background: var(--grad-primary);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
@@ -271,9 +279,10 @@ onMounted(loadTemplates)
.section-label .dot {
width: 6px;
height: 6px;
- border-radius: 50%;
- background: var(--cyan);
- box-shadow: 0 0 8px var(--cyan);
+ /* 统一圆角矩形(禁正圆),小尺寸用最小圆角 --radius-xs */
+ border-radius: var(--radius-xs);
+ background: var(--accent);
+ box-shadow: 0 0 8px var(--accent);
}
/* —— 模板加载条 —— */
@@ -310,7 +319,7 @@ onMounted(loadTemplates)
padding: 8px;
background: var(--panel);
border: 1px solid var(--border);
- border-radius: var(--radius);
+ border-radius: var(--radius-md);
text-align: left;
transition: border-color 0.15s ease, transform 0.05s ease;
}
@@ -318,14 +327,15 @@ onMounted(loadTemplates)
transform: scale(0.98);
}
.tpl-card.is-selected {
- border-color: var(--cyan);
- box-shadow: 0 0 0 1px var(--cyan), 0 0 12px rgba(49, 230, 255, 0.25);
+ border-color: var(--accent);
+ /* 选中辉光:手抄 rgba 改为语义令牌 + color-mix(强调色 25% 透明) */
+ box-shadow: 0 0 0 1px var(--accent), 0 0 12px color-mix(in srgb, var(--accent) 25%, transparent);
}
.tpl-cover {
width: 100%;
height: 84px;
object-fit: cover;
- border-radius: 6px;
+ border-radius: var(--radius-sm);
background: var(--panel-2);
}
.tpl-name {
@@ -352,17 +362,17 @@ onMounted(loadTemplates)
display: flex;
align-items: center;
justify-content: center;
- font-size: 12px;
- color: #04222a;
- background: var(--cyan);
- border-radius: 50%;
+ /* 深色 on 霓虹前景统一走 --on-accent;圆角矩形(禁正圆)用 --radius-sm */
+ color: var(--on-accent);
+ background: var(--accent);
+ border-radius: var(--radius-sm);
}
/* —— Prompt 输入框 —— */
.prompt-box {
background: var(--panel);
border: 1px solid var(--border);
- border-radius: var(--radius);
+ border-radius: var(--radius-md);
padding: 12px;
}
.prompt-input {
@@ -390,13 +400,20 @@ onMounted(loadTemplates)
.prompt-example {
flex: 1;
min-width: 0;
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
border: 0;
background: transparent;
- color: var(--amber);
+ /* 警示/进行中语义色(原 --amber → --warning) */
+ color: var(--warning);
font-size: 12px;
text-align: left;
padding: 0;
- /* 单行省略 */
+}
+/* 示例文案单行省略(图标不参与省略,故省略改挂内层文本) */
+.prompt-example-text {
+ min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
diff --git a/game-studio/src/views/feed/Feed.vue b/game-studio/src/views/feed/Feed.vue
index cb7cb884..d0218a96 100644
--- a/game-studio/src/views/feed/Feed.vue
+++ b/game-studio/src/views/feed/Feed.vue
@@ -15,11 +15,13 @@
*/
import { ref, computed, onMounted, onActivated, watch, nextTick } from 'vue'
import { useRoute, useRouter } from 'vue-router'
+import { useI18n } from 'vue-i18n'
import { storeToRefs } from 'pinia'
import { showConfirmDialog } from 'vant'
import { useFeedStore } from '../../store/feed'
import { useUserStore } from '../../store/user'
import { GameCard, InteractBar, LoadingBar, EmptyState } from '../../components'
+import Icon from '../../components/Icon.vue'
import { track } from '../../telemetry'
import type { FeedAction } from '../../api/types'
@@ -32,6 +34,8 @@ const props = defineProps<{ zoneId?: string }>()
const route = useRoute()
const router = useRouter()
+// i18n:本页文案走 feed 命名空间,通用词复用 common.*(globalInjection 已开,模板亦可用 $t)
+const { t } = useI18n()
const feedStore = useFeedStore()
const userStore = useUserStore()
// 注意:仅对 state 用 storeToRefs(保持响应式);actions 直接从 store 解构(函数无需 ref)。
@@ -56,8 +60,8 @@ const resolvedZoneId = computed(() => {
/** 当前专区名(顶部标题展示;混合流为「推荐」) */
const currentZoneName = computed(() => {
- if (resolvedZoneId.value == null) return '推荐'
- return zones.value.find((z) => z.id === resolvedZoneId.value)?.name ?? '专区'
+ if (resolvedZoneId.value == null) return t('feed.recommend')
+ return zones.value.find((z) => z.id === resolvedZoneId.value)?.name ?? t('feed.zone')
})
/* ============================================================================
@@ -156,10 +160,10 @@ async function onInteract(action: FeedAction): Promise {
if (!userStore.isRealLogin()) {
try {
await showConfirmDialog({
- title: '登录后才能互动',
- message: '登录账号即可点赞、收藏、分享你喜欢的小游戏',
- confirmButtonText: '去登录',
- cancelButtonText: '再看看',
+ title: t('feed.loginTitle'),
+ message: t('feed.loginMessage'),
+ confirmButtonText: t('feed.loginConfirm'),
+ cancelButtonText: t('feed.loginCancel'),
})
// 确认:跳登录并带当前路由回跳(登录成功回到本流继续互动)
router.push({ path: '/login', query: { redirect: route.fullPath } })
@@ -179,11 +183,11 @@ async function onInteract(action: FeedAction): Promise {
// 举报:契约要求 reason 必填(action=4 转 compliance);骨架用浏览器 prompt 极简采集
if (action === 4) {
- const reason = window.prompt('请填写举报理由(违规内容/侵权等)') || ''
+ const reason = window.prompt(t('feed.reportPrompt')) || ''
if (!reason.trim()) return // 未填写则取消,不发请求(契约 reason 必填)
await feedStore.interact(card.gameId, 4, reason.trim()).catch(() => {})
track('report', { reason: reason.trim() }, { gameId: card.gameId, versionId: card.versionId })
- window.alert('已提交举报,平台将尽快处理')
+ window.alert(t('feed.reportDone'))
return
}
@@ -211,7 +215,7 @@ function onPlay(payload: { gameId: number; versionId: number }): void {
:class="{ 'is-active': resolvedZoneId == null }"
@click="router.push('/feed')"
>
- 推荐
+ {{ t('feed.recommend') }}
+
+
+
-
退出登录
+
{{ t('profile.logout') }}
@@ -247,7 +265,8 @@ onMounted(() => {
flex: 0 0 60px;
width: 60px;
height: 60px;
- border-radius: 50%;
+ /* 统一圆角矩形(设计体系禁正圆/药丸;原 50% 正圆改 --radius-lg 圆角方) */
+ border-radius: var(--radius-lg);
background-color: var(--panel-2);
background-size: cover;
background-position: center;
@@ -283,10 +302,19 @@ onMounted(() => {
margin-top: 6px;
padding: 0 8px;
font-size: 11px;
- color: var(--cyan);
- border: 1px solid var(--cyan);
- border-radius: var(--radius);
- background: color-mix(in srgb, var(--cyan) 10%, transparent);
+ color: var(--accent);
+ border: 1px solid var(--accent);
+ border-radius: var(--radius-sm);
+ background: color-mix(in srgb, var(--accent) 10%, transparent);
+}
+
+/* 外观设置卡(主题 + 语言切换;与下方菜单卡同一视觉规格) */
+.profile__appearance {
+ margin: 0 12px 12px;
+ padding: 14px;
+ background: var(--panel);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
}
/* 创作成长卡 */
@@ -295,7 +323,7 @@ onMounted(() => {
padding: 14px;
background: var(--panel);
border: 1px solid var(--border);
- border-radius: var(--radius);
+ border-radius: var(--radius-lg);
}
.profile__level-row {
display: flex;
@@ -310,7 +338,7 @@ onMounted(() => {
.profile__level-val {
font-size: 15px;
font-weight: 700;
- color: var(--green);
+ color: var(--success);
}
.profile__level-next {
margin: 6px 0 0;
@@ -325,7 +353,7 @@ onMounted(() => {
flex-direction: column;
background: var(--panel);
border: 1px solid var(--border);
- border-radius: var(--radius);
+ border-radius: var(--radius-lg);
overflow: hidden;
}
.profile__item {
@@ -344,11 +372,10 @@ onMounted(() => {
.profile__item:active {
background: var(--panel-2);
}
+/* 行首图标(已改为 SVG,currentColor 跟随此处 color;尺寸由组件 :size 控制) */
.profile__item-icon {
flex: 0 0 22px;
- font-size: 17px;
- color: var(--cyan);
- text-align: center;
+ color: var(--accent);
}
.profile__item-label {
flex: 1;
@@ -365,15 +392,16 @@ onMounted(() => {
font-size: 11px;
font-weight: 700;
line-height: 1;
- color: #2a0610;
- background: var(--pink);
- border-radius: 9px;
+ /* danger/pink 之上的近黑前景令牌(原 #2a0610 硬编码) */
+ color: var(--on-danger);
+ background: var(--danger);
+ /* 统一圆角矩形(原 9px 近正圆药丸改 --radius-xs) */
+ border-radius: var(--radius-xs);
}
+/* 行尾箭头(已改为 ;color 跟随 muted) */
.profile__item-chevron {
flex: 0 0 auto;
- font-size: 20px;
color: var(--muted);
- line-height: 1;
}
/* 退出登录 */