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 }>() @@ -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)
-

一句话,做个小游戏

-

选一个玩法模板,用一句话描述你的创意,AI 帮你生成

+

{{ t('create.title') }}

+

{{ t('create.subtitle') }}

- - 模板加载中… + + {{ t('create.templateLoading') }}
@@ -178,7 +183,9 @@ onMounted(loadTemplates) {{ tpl.name }} {{ tpl.description }} - ✓ + + +
@@ -187,7 +194,7 @@ onMounted(loadTemplates)
@@ -196,7 +203,7 @@ onMounted(loadTemplates) class="prompt-input" :maxlength="PROMPT_MAX" rows="4" - placeholder="例如:做一个点击就能赚金币、越点越快的放置小游戏" + :placeholder="t('create.promptPlaceholder')" />
@@ -206,7 +213,8 @@ onMounted(loadTemplates) type="button" @click="useExample" > - 💡 用示例:{{ selectedTemplate.examplePrompt }} + + {{ t('create.useExample') }}{{ selectedTemplate.examplePrompt }} {{ prompt.length }}/{{ PROMPT_MAX }}
@@ -216,9 +224,9 @@ onMounted(loadTemplates)
- {{ submitting ? '正在创建…' : '开始生成' }} + {{ submitting ? t('create.submitting') : t('create.submit') }} -

生成约需数秒,期间可在进度页查看状态

+

{{ t('create.tip') }}

@@ -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') }} - + + + @@ -292,8 +301,12 @@ function onPlay(payload: { gameId: number; versionId: number }): void { gap: 8px; padding: 12px 14px; overflow-x: auto; - /* 顶部渐隐遮罩,保证白字在封面上可读 */ - background: linear-gradient(180deg, rgba(7, 9, 17, 0.7), rgba(7, 9, 17, 0)); + /* 顶部渐隐遮罩,保证白字在封面上可读(用 --bg 令牌,随主题走) */ + background: linear-gradient( + 180deg, + color-mix(in srgb, var(--bg) 70%, transparent), + color-mix(in srgb, var(--bg) 0%, transparent) + ); -webkit-overflow-scrolling: touch; } .feed__zones::-webkit-scrollbar { @@ -304,15 +317,16 @@ function onPlay(payload: { gameId: number; versionId: number }): void { padding: 5px 14px; font-size: 13px; color: var(--muted); - background: rgba(16, 21, 34, 0.55); + background: color-mix(in srgb, var(--panel) 55%, transparent); border: 1px solid var(--border); - border-radius: 999px; + /* 统一圆角矩形(杀 999px 药丸) */ + border-radius: var(--radius-md); white-space: nowrap; } .feed__zone.is-active { - color: var(--bg); - background: var(--cyan); - border-color: var(--cyan); + color: var(--on-accent); + background: var(--accent); + border-color: var(--accent); font-weight: 600; } @@ -383,13 +397,20 @@ function onPlay(payload: { gameId: number; versionId: number }): void { display: flex; align-items: center; justify-content: center; - font-size: 14px; color: var(--ink); - background: rgba(16, 21, 34, 0.6); + background: color-mix(in srgb, var(--panel) 60%, transparent); border: 1px solid var(--border); - border-radius: 50%; + /* 统一圆角矩形(杀正圆按钮) */ + border-radius: var(--radius-lg); } .feed__nav-btn:disabled { opacity: 0.3; } +/* chevron 右向 → 旋转成上/下箭头(纯展示旋转,不改交互) */ +.feed__nav-ico--up { + transform: rotate(-90deg); +} +.feed__nav-ico--down { + transform: rotate(90deg); +} diff --git a/game-studio/src/views/profile/Profile.vue b/game-studio/src/views/profile/Profile.vue index 4d4c0cad..bbe69133 100644 --- a/game-studio/src/views/profile/Profile.vue +++ b/game-studio/src/views/profile/Profile.vue @@ -14,15 +14,26 @@ */ import { onMounted, ref } from 'vue' import { useRouter } from 'vue-router' +import { useI18n } from 'vue-i18n' import { showConfirmDialog, showToast } from 'vant' import { getMe, type PlayerMeResp } from '../../api/passport' import * as communityApi from '../../api/community' import { useUserStore } from '../../store/user' import { useMessageStore } from '../../store/message' import { AppButton } from '../../components' +import Icon from '../../components/Icon.vue' +import AppearanceSwitch from '../../components/AppearanceSwitch.vue' +import profileZh from '../../locales/profile.zh' +import profileEn from '../../locales/profile.en' import type { CommunityLevel } from '../../api/types' const router = useRouter() +// 页域 i18n:profile.* 文案以局部消息注入(不污染全局 i18n/index.ts); +// common.* 等全局文案经 vue-i18n 根回落(fallbackRoot)仍可用 t() 取到。 +const { t } = useI18n({ + useScope: 'local', + messages: { 'zh-CN': profileZh, 'en-US': profileEn }, +}) // 登录态 store:账号信息兜底源 + 登出动作(复用既有 logout(),不重实现) const userStore = useUserStore() // 消息 store:用于「消息」入口的未读角标展示(与底部 Tab 红点同源) @@ -101,9 +112,10 @@ function goBiz() { router.push('/biz') } -/** 占位功能项点击(设置/帮助本波未实现,给可感知的提示而非静默) */ +/** 占位功能项点击(设置/帮助本波未实现,给可感知的提示而非静默)。 + * name 传入已 i18n 的入口名,套 profile.comingSoon 模板插值。 */ function onPlaceholder(name: string) { - showToast(`${name}功能即将上线`) + showToast(t('profile.comingSoon', { name })) } /** @@ -114,10 +126,10 @@ function onPlaceholder(name: string) { async function onLogout() { try { await showConfirmDialog({ - title: '退出登录', - message: '退出后需重新登录才能查看你的项目与创作,确定退出吗?', - confirmButtonText: '退出登录', - cancelButtonText: '取消', + title: t('profile.logout'), + message: t('profile.logoutConfirm'), + confirmButtonText: t('profile.logout'), + cancelButtonText: t('common.action.cancel'), }) } catch { // 用户点取消:showConfirmDialog 以 reject 表示,吞掉即可,不执行登出 @@ -147,25 +159,25 @@ onMounted(() => { class="profile__avatar" :style="{ backgroundImage: me?.avatar ? `url(${me.avatar})` : undefined }" > - {{ (me?.nickname || '游').slice(0, 1) }} + {{ (me?.nickname || t('profile.avatarFallback')).slice(0, 1) }}
-

{{ me?.nickname || '游客' }}

+

{{ me?.nickname || t('profile.guest') }}

{{ me.mobile }}

- 创作者 + {{ t('profile.creatorTag') }}
- 创作等级 + {{ t('profile.levelLabel') }} Lv.{{ level.level }}
- 已发布作品 + {{ t('profile.publishedLabel') }} {{ level.publishedCount }}
@@ -173,56 +185,62 @@ onMounted(() => { v-if="level.nextMilestoneTarget" class="profile__level-next" > - 距下个里程碑还差 {{ Math.max(0, level.nextMilestoneTarget - level.publishedCount) }} 个作品 + {{ t('profile.nextMilestone', { n: Math.max(0, level.nextMilestoneTarget - level.publishedCount) }) }}

+ +
+ +
+
- 退出登录 + {{ 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; } /* 退出登录 */