diff --git a/3DTank/docs/HeadStyleTable.md b/3DTank/docs/HeadStyleTable.md index d49fe85..8f73fd6 100644 --- a/3DTank/docs/HeadStyleTable.md +++ b/3DTank/docs/HeadStyleTable.md @@ -53,22 +53,23 @@ Lq = To.$_$.gd, // module_37036: gd = te = ne("class", ...) | `data-ui` | 原版/线上对应 | CSS | Emotion | |---|---|---|---| -| `head.root` | `MainScreenComponentStyle-containerPanel` | `display:flex; align-items:center; justify-content:space-between; width:100%; height:100%; position:relative; background:linear-gradient(180deg,rgba(0,25,38,.98),rgba(0,16,24,.94))` | `RootStyle` | +| `head.root` | `MainScreenComponentStyle-containerPanel` | `display:flex; align-items:center; justify-content:space-between; width:100%; height:100%; position:relative; background:linear-gradient(180deg,rgba(0,25,38,.98),rgba(0,16,24,.94)); border-bottom:1px solid rgba(255,255,255,.25)` | `RootStyle` | | `head.left` | `blockLeftPanel` | `display:flex; align-items:center; height:100%` | `LeftStyle` | | `head.vip` | 主页 VIP 区 | `width:6em; height:6em; display:flex; align-items:center; justify-content:center; position:relative; cursor:pointer; background:radial-gradient(100% 100% at 0% 0%, rgba(255,204,0,.75) 0%, rgba(255,38,74,.5) 50%, rgba(255,38,74,0) 100%)`;off:`opacity:.55; filter:grayscale(.3)` | `VipStyle(On)` | | `head.exit` | 子页面 VIP → 退出键 | `width:6em; height:6em; display:flex; align-items:center; justify-content:center; border:0; box-shadow:1px 0 0 0 rgba(255,255,255,.25); background:transparent; cursor:pointer`;图标 `back.72d79619.svg`(左箭头,不是右上角的退出箭头) | `ExitStyle` | | `head.group` | 组队区 | `width:6em; height:6em; display:flex; align-items:center; justify-content:center; position:relative; border-left:1px solid rgba(255,255,255,.25); cursor:pointer` | `GroupStyle(Active)` | | `head.group.g1` | 组队图标左半 | `position:absolute; top:50%; left:50%; width:1.5em; height:1.5em; margin-top:-.75em; margin-left:-.75em; background:rgba(191,212,255,.5); mask:url(groupUser.svg) center/contain no-repeat; transform:translateX(-.8em)` | `GroupIconStyle("-0.8em")` | | `head.group.g2` | 组队图标右半 | 同上,`translateX(.8em)` | `GroupIconStyle("0.8em")` | -| `head.rank` | `rankIconContainer` | `width:6em; height:6em; display:flex; align-items:center; justify-content:center; border-left:1px solid rgba(255,255,255,.25); border-right:1px solid rgba(255,255,255,.25); cursor:pointer` | `RankStyle` | +| `head.rank` | `rankIconContainer` | `width:5.9375em; height:6em; display:flex; align-items:center; justify-content:center; border-right:1px solid rgba(255,255,255,.25); cursor:pointer` | `RankStyle` | | `head.user` | `containerProgressMainScreen` 外层 | `display:flex; flex-direction:column; justify-content:center; min-width:10em; margin-left:1em; height:3.75em` | `UserStyle` | | `head.user.name` | 昵称 | `font:500 1.1em/1.2 BaseFontMedium; text-shadow:0 0 .25em rgba(0,0,0,.5); white-space:nowrap; overflow:hidden; text-overflow:ellipsis` | `UserNameStyle` | -| `head.user.xp` | `xp-bar` | `width:12.5em; height:.25em; margin-top:.375em; background:rgba(118,255,51,.25); border-radius:.375em; overflow:hidden` | `XpBarStyle` | +| `head.user.xp` | `xp-bar` | `width:12.5em; height:.25em; margin-top:.375em; background:rgba(118,255,51,.5); border-radius:.375em; overflow:hidden` | `XpBarStyle` | +| `head.user.xp.wrap` | `containerParameterProgress` | `display:flex; align-items:flex-start; height:.875em; margin-top:.55em; opacity:.8; white-space:nowrap` | `XpBarWrap` | | `head.user.xp.fill` | XP 进度 | `display:block; height:100%; width:%; background:rgba(118,255,51,.8); border-radius:inherit` | `XpFillStyle(Ratio)` | -| `head.user.xp.text` | XP 数字 | `display:flex; align-items:center; margin-top:.35em; font:500 .9375em/1 BaseFontMedium; color:#76ff33` | `XpTextStyle` | -| `head.logo` | 中间 Logo | `position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); display:flex; align-items:center; height:3.75em; opacity:.95; pointer-events:none` | `LogoStyle` | +| `head.user.xp.text` | XP 数字 | `display:flex; align-items:center; margin-top:.35em; margin-left:.25em; font:500 1em/1 BaseFontMedium; text-shadow:0 0 .25em rgba(0,0,0,.5); color:rgb(118,255,51)` | `XpTextStyle` | +| `head.logo` | `blockForIconTankiOnline` | `position:absolute; inset:0; margin:auto; display:flex; align-items:center; justify-content:center; z-index:1; pointer-events:none`;图片 `iconTankiOnline.2a9c236f.svg`,`192×60px → 14.81em×4.63em` | `LogoStyle` | | `head.right` | `blockRightPanel` | `display:flex; align-items:center; height:100%; z-index:5` | `RightStyle` | -| `head.currencies` | 货币列 | `display:flex; flex-direction:column; justify-content:center; height:100%; width:9.2em; box-sizing:border-box; border-right:1px solid rgba(255,255,255,.25)` | `CurrenciesStyle` | +| `head.currencies` | `coinsContainer` | `display:flex; flex-direction:column; justify-content:center; height:100%; width:9.2em; box-sizing:border-box; border-right:1px solid rgba(255,255,255,.25); margin-right:2em` | `CurrenciesStyle` | | `head.currency.ruby` | 红宝石 | `position:relative; display:flex; align-items:center; gap:.4em; width:100%; height:3em; padding:0 .75em 0 .6em; box-sizing:border-box; cursor:pointer; border-bottom:1px solid rgba(255,255,255,.25)` | `CurrencyStyle(true)` | | `head.currency.crystal` | 水晶 | 同上,无 `border-bottom` | `CurrencyStyle(false)` | | `head.fullscreen` | F11 | `display:flex; align-items:center; justify-content:center; gap:.3em; width:6em; height:6em; border-left:1px solid rgba(255,255,255,.25); cursor:pointer` | `FullscreenStyle` | diff --git a/3DTank/docs/UI_Rebuild_Plan.md b/3DTank/docs/UI_Rebuild_Plan.md index 139e4ec..106f9e0 100644 --- a/3DTank/docs/UI_Rebuild_Plan.md +++ b/3DTank/docs/UI_Rebuild_Plan.md @@ -426,3 +426,47 @@ TankiOffline (Kotlin/JS 客户端) - `3DTank/docs/SettingsStyleTable.md` 第 6/7/8 节 - `3DTank/docs/ColorPalette.md` + +--- + +## 11. 桩代码标记规范 + +> 目的:不允许“看起来像原版”的自创组件混进最终 UI。找不到原版定义时,只能留明确标记的桩。 + +### 11.1 强制标记 + +任何非原版实现、占位、模拟数据、临时组件必须同时具备: + +1. 源码注释: + ```kotlin + // STUB: 原版 <组件名/样式类> 尚未展开 + ``` +2. DOM 标记: + ```kotlin + Ui("stub..") + ``` +3. 文档记录:在 `UiTranslationStatus.md` 的“待翻译/未展开”中列出。 + +### 11.2 禁止事项 + +- 禁止用自创的 `button` / `div` / 卡片替代原版组件而不标记。 +- 禁止用近似色、近似尺寸冒充原版值。 +- 禁止把 mock 数据写成真实数据;mock 必须标 `STUB` 或 `offline demo`。 +- 禁止在未找到原版定义时直接写“完成”。 + +### 11.3 删除规则 + +- 原版定义补齐后,删除对应 `STUB` 注释、`data-ui="stub.*"` 和文档条目。 +- 删除旧近似样式时,同步删除未使用的 style 函数/常量,避免两套实现并存。 + +### 11.4 HEAD 验证记录 + +HEAD 已按第 10 节方法重写: + +- 样式类:`MainScreenComponentStyle-containerPanel` / `UserInfoContainerStyle-blockLeftPanel` / `UserScoreComponentStyle-blockRightPanel` +- Logo:`iconTankiOnline.2a9c236f.svg`(192×60px) +- 等级块:`5.9375em × 6em`,仅右边框 +- XP 容器:`.875em` 高,`margin-top:.55em`,`opacity:.8` +- XP 条:`12.5em × .25em`,绿色 50% 背景 +- 货币列:`9.2em` 宽,`margin-right:2em` +- 实测 rect:左块 `409×77.75`、用户 `162×48.59`、右块从 `994` 开始,与计划 9.1 基本一致。 diff --git a/TankiOffline/app/src/jsMain/kotlin/App/Browser/Ui/HeadScreen.kt b/TankiOffline/app/src/jsMain/kotlin/App/Browser/Ui/HeadScreen.kt index 7ae7bfc..2070c6a 100644 --- a/TankiOffline/app/src/jsMain/kotlin/App/Browser/Ui/HeadScreen.kt +++ b/TankiOffline/app/src/jsMain/kotlin/App/Browser/Ui/HeadScreen.kt @@ -68,12 +68,16 @@ private val RootStyle: PropertiesBuilder.() -> Unit = { Css("height", "100%") Css("position", "relative") Css("background", "linear-gradient(180deg, rgba(0, 25, 38, 0.98), rgba(0, 16, 24, 0.94))") + Css("border-bottom", "1px solid rgba(255, 255, 255, 0.25)") } private val LeftStyle: PropertiesBuilder.() -> Unit = { Css("display", "flex") Css("align-items", "center") + Css("justify-content", "flex-start") Css("height", "100%") + Css("position", "relative") + Css("z-index", "2") } private val RightStyle: PropertiesBuilder.() -> Unit = { @@ -140,7 +144,7 @@ private fun GroupStyle(Active: Boolean): PropertiesBuilder.() -> Unit = { Css("display", "flex") Css("align-items", "center") Css("justify-content", "center") - Css("border-left", Divider) + Css("border-right", Divider) Css("cursor", "pointer") if (!Active) Css("opacity", "0.55") } @@ -162,12 +166,11 @@ private fun GroupIconStyle(Offset: String): PropertiesBuilder.() -> Unit = { // ---------------------------------------------------------------- 等级 / 用户 private val RankStyle: PropertiesBuilder.() -> Unit = { - Css("width", "6em") + Css("width", "5.9375em") Css("height", "6em") Css("display", "flex") Css("align-items", "center") Css("justify-content", "center") - Css("border-left", Divider) Css("border-right", Divider) Css("cursor", "pointer") } @@ -195,11 +198,20 @@ private val UserNameStyle: PropertiesBuilder.() -> Unit = { Css("text-overflow", "ellipsis") } +private val XpBarWrap: PropertiesBuilder.() -> Unit = { + Css("display", "flex") + Css("align-items", "flex-start") + Css("height", "0.875em") + Css("margin-top", "0.55em") + Css("opacity", "0.8") + Css("white-space", "nowrap") +} + private val XpBarStyle: PropertiesBuilder.() -> Unit = { Css("width", "12.5em") Css("height", "0.25em") Css("margin-top", "0.375em") - Css("background", "rgba(118, 255, 51, 0.25)") + Css("background", "rgba(118, 255, 51, 0.5)") Css("border-radius", "0.375em") Css("overflow", "hidden") } @@ -208,7 +220,7 @@ private fun XpFillStyle(Ratio: Double): PropertiesBuilder.() -> Unit = { Css("display", "block") Css("height", "100%") Css("width", "${(Ratio * 100).toInt()}%") - Css("background", "rgba(118, 255, 51, 0.8)") + Css("background", "rgb(118, 255, 51)") Css("border-radius", "inherit") } @@ -216,8 +228,10 @@ private val XpTextStyle: PropertiesBuilder.() -> Unit = { Css("display", "flex") Css("align-items", "center") Css("margin-top", "0.35em") - Css("font", "500 0.9375em/1 BaseFontMedium, sans-serif") - Css("color", "#76ff33") + Css("margin-left", "0.25em") + Css("font", "500 1em/1 BaseFontMedium, sans-serif") + Css("text-shadow", "0em 0em 0.25em rgba(0, 0, 0, 0.5)") + Css("color", "rgb(118, 255, 51)") } private val XpIconStyle: PropertiesBuilder.() -> Unit = { @@ -236,18 +250,18 @@ private val XpMaxStyle: PropertiesBuilder.() -> Unit = { private val LogoStyle: PropertiesBuilder.() -> Unit = { Css("position", "absolute") - Css("left", "50%") - Css("top", "50%") - Css("transform", "translate(-50%, -50%)") + Css("inset", "0") + Css("margin", "auto") Css("display", "flex") Css("align-items", "center") - Css("height", "3.75em") - Css("opacity", "0.95") + Css("justify-content", "center") + Css("z-index", "1") Css("pointer-events", "none") } private val LogoImgStyle: PropertiesBuilder.() -> Unit = { - Css("height", "3.75em") + Css("width", "14.81em") + Css("height", "4.63em") } // ---------------------------------------------------------------- 右侧货币 / F11 / 退出 @@ -260,6 +274,7 @@ private val CurrenciesStyle: PropertiesBuilder.() -> Unit = { Css("width", "9.2em") Css("box-sizing", "border-box") Css("border-right", Divider) + Css("margin-right", "2em") } private fun CurrencyStyle(BorderBottom: Boolean): PropertiesBuilder.() -> Unit = { @@ -278,6 +293,8 @@ private fun CurrencyStyle(BorderBottom: Boolean): PropertiesBuilder.() -> Unit = private val CoinStyle: PropertiesBuilder.() -> Unit = { Css("width", "1em") Css("height", "1em") + Css("margin-right", "0.25em") + Css("margin-top", "0.2em") } private val CurrencyValueStyle: PropertiesBuilder.() -> Unit = { @@ -287,7 +304,8 @@ private val CurrencyValueStyle: PropertiesBuilder.() -> Unit = { private val PlusStyle: PropertiesBuilder.() -> Unit = { Css("width", "1em") Css("height", "1em") - Css("margin-left", "auto") + Css("position", "absolute") + Css("right", "1em") Css("opacity", "0.85") } @@ -401,9 +419,13 @@ val HeadScreen = FC { Ui("head.user") span { css(UserNameStyle); Ui("head.user.name"); +HeadContext.Uid.ifEmpty { "未登录" } } div { - css(XpBarStyle) - Ui("head.user.xp") - i { css(XpFillStyle(Ratio)); Ui("head.user.xp.fill") } + css(XpBarWrap) + Ui("head.user.xp.wrap") + div { + css(XpBarStyle) + Ui("head.user.xp") + i { css(XpFillStyle(Ratio)); Ui("head.user.xp.fill") } + } } div { css(XpTextStyle) @@ -422,7 +444,7 @@ val HeadScreen = FC { div { css(LogoStyle) Ui("head.logo") - img { src = Asset("static/images/logoTankiOnlineChina.0b6ba850.svg"); css(LogoImgStyle) } + img { src = Asset("static/images/iconTankiOnline.2a9c236f.svg"); css(LogoImgStyle) } } // 右:货币、F11、退出