diff --git a/.gitignore b/.gitignore index f7a0ce1..9c96374 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,6 @@ backend/pytest-of-*/ # Vditor 运行时资源(lute/i18n/icons,约 3.8 MB) # 由 web/scripts/vendor-vditor.mjs 从 node_modules 生成,不进版本库 web/public/vditor/ + +# 本机部署/调试的临时脚本与缓存 +.tmp/ diff --git a/2.Design/ROADMAP-M0-M1.md b/2.Design/ROADMAP-M0-M1.md index 40e92f9..8415e82 100644 --- a/2.Design/ROADMAP-M0-M1.md +++ b/2.Design/ROADMAP-M0-M1.md @@ -359,7 +359,7 @@ 重做不是换配色,而是**先立设计系统、再让页面去用它**,否则下次还是东拼西凑。 ### 4.1 设计系统(两端共用一套 token) -- [x] 色板:纸白 `paper`、墨绿 `primary`、陶土橙 `clay`,语义色 sage/wheat/rust/sky(Web `tailwind.config.js`,安卓 `ui/Theme.kt` 同名同值) +- [x] 色板:纸白 `paper`、景泰蓝 `primary`、陶土橙 `clay`,语义色 sage/wheat/rust/sky(Web `tailwind.config.js`,安卓 `ui/Theme.kt` 同名同值)(2026-11-01 主色由墨绿改蓝系,语义色不变) - [x] 字号阶:11/12/13/14/15/18/22/26/36,正文 14、辅助 11/12;细描边 + 极轻投影 + 14dp 圆角 - [x] 组件:卡片 / 按钮(primary、ghost、subtle、clay、danger)/ 状态 pill / 输入 / 空态 / 骨架屏 / 抽屉 - [x] 图标:Web 用自绘线性图标集(`components/icons.tsx`),安卓用 Material Icons + 少量自绘,线宽一致 @@ -436,7 +436,7 @@ - [x] `ui/UpdateDialog.kt`:当前/新版本号、更新说明、下载进度、失败重试;「允许安装未知应用」只跳系统设置,不代点 - [x] 权限回来那一跳要接住:用户在系统设置里开完开关回到 App,再点同一个按钮时先复查权限,开了就直接装——否则按钮点了没反应 - [x] 启动时自动查一次;首页底部有「检查更新」手动入口,结果用一句话回给用户 -- [x] 图标换成与 Web 的 `` 同一套图形(墨绿圆角底 + 三条米白圆头线 + 陶土点),生成 `mipmap-*/ic_launcher{,_round}.png` 与自适应图标 `mipmap-anydpi-v26/ic_launcher{,_round}.xml`,512px 预览存 `2.Design/screens/android-icon-512.png` +- [x] 图标换成与 Web 的 `` 同一套图形(景泰蓝圆角底 + 三条浅蓝白圆头线 + 亮蓝点),生成 `mipmap-*/ic_launcher{,_round}.png` 与自适应图标 `mipmap-anydpi-v26/ic_launcher{,_round}.xml`,512px 预览存 `2.Design/screens/android-icon-512.png` - [x] 版本 `versionCode = 2` / `versionName = "0.2.0"`;发布签名从 `android/keystore.properties` 读,无该文件时回落调试签名(保证别人 clone 下来也能 `assembleRelease`) ### 6.3 构建环境(本机踩过的坑) diff --git a/2.Design/screens/android-icon-512.png b/2.Design/screens/android-icon-512.png index 78ceb41..82bbb62 100644 Binary files a/2.Design/screens/android-icon-512.png and b/2.Design/screens/android-icon-512.png differ diff --git a/README.md b/README.md index d9f89cd..ea59a73 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ | 安卓端任务 / 看板 / 工单 / 知识 / 成员五个页面 | ✅ 编译成 APK(10.1 MB),模拟器上逐页跑通,截图见 [`2.Design/screens/`](2.Design/screens) | | 工单:升级不搬数据、受理→办理→提交→验收、驳回、改派、站内通知 | ✅ 后端 + Web + 安卓已通,双人 e2e 8 项全绿 | | 知识:工单验收自动生成草稿、核对后发布、来源可追、手记 | ✅ 后端 + Web + 安卓已通,e2e 7 项全绿 | -| 界面:两端统一设计系统(纸白 / 墨绿 / 陶土橙,语义色、字号阶、组件库) | ✅ 已重做,见 [`2.Design/ROADMAP-M0-M1.md`](2.Design/ROADMAP-M0-M1.md) 第 4 节 | +| 界面:两端统一设计系统(纸白 / 景泰蓝 / 陶土橙,语义色、字号阶、组件库) | ✅ 已重做,见 [`2.Design/ROADMAP-M0-M1.md`](2.Design/ROADMAP-M0-M1.md) 第 4 节 | | 富文本:备注 / 工单说明 / 知识正文支持图文混排与附件 | ✅ Web 用 Vditor 编辑、安卓用 Markwon 预览编辑(e2e 15 项全绿);接口见 [`2.Design/API.md`](2.Design/API.md) 第 6 节 | | 知识切片与语义检索(混合打分、只搜已发布、带来源) | ✅ 已跑通:`real[]` + SQL 余弦(pgvector 待独立实例),`pytest` 5 个用例 + `e2e/retrieval.mjs` 6 项 | | 后端测试 | ✅ 79 passed,ruff 干净 | diff --git a/android/README.md b/android/README.md index 2a9da35..68fc342 100644 --- a/android/README.md +++ b/android/README.md @@ -90,7 +90,7 @@ app/src/main/java/com/dadao/task/ │ ├── Api.kt OkHttp + kotlinx.serialization;信封解包、业务码抛 ApiException │ └── TokenStore.kt 令牌本地存储(TODO:换 EncryptedSharedPreferences) └── ui/ - ├── Theme.kt 与 Web 一致的墨青 + 陶土色板 + ├── Theme.kt 与 Web 一致的景泰蓝 + 陶土色板 ├── LoginScreen.kt 手机号 + 验证码 ├── HomeScreen.kt 工作区列表 / 新建 / 生成邀请码 ├── TaskListScreen.kt 任务视图切换、快速新建、勾选完成、详情与评论 diff --git a/android/app/src/main/java/com/dadao/task/ui/KnowledgeScreen.kt b/android/app/src/main/java/com/dadao/task/ui/KnowledgeScreen.kt index 4e47380..10eff2b 100644 --- a/android/app/src/main/java/com/dadao/task/ui/KnowledgeScreen.kt +++ b/android/app/src/main/java/com/dadao/task/ui/KnowledgeScreen.kt @@ -329,7 +329,7 @@ private fun KnowledgeBody(text: String) { setTextIsSelectable(true) textSize = 14f setLineSpacing(0f, 1.5f) - setTextColor(android.graphics.Color.parseColor("#33443F")) + setTextColor(android.graphics.Color.parseColor("#262D39")) markwon.setMarkdown(this, text) } }, diff --git a/android/app/src/main/java/com/dadao/task/ui/RichTextEditorScreen.kt b/android/app/src/main/java/com/dadao/task/ui/RichTextEditorScreen.kt index e0b8d5b..0fbbd67 100644 --- a/android/app/src/main/java/com/dadao/task/ui/RichTextEditorScreen.kt +++ b/android/app/src/main/java/com/dadao/task/ui/RichTextEditorScreen.kt @@ -350,7 +350,7 @@ private fun PreviewBody( textSize = 15f setPadding(0, 0, 0, 0) setLineSpacing(0f, 1.5f) - setTextColor(android.graphics.Color.parseColor("#4A5854")) + setTextColor(android.graphics.Color.parseColor("#4E5867")) markwon.setMarkdown(this, text) } }, diff --git a/android/app/src/main/java/com/dadao/task/ui/Theme.kt b/android/app/src/main/java/com/dadao/task/ui/Theme.kt index febaa3b..0088571 100644 --- a/android/app/src/main/java/com/dadao/task/ui/Theme.kt +++ b/android/app/src/main/java/com/dadao/task/ui/Theme.kt @@ -13,22 +13,22 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp /** - * 与 Web 端 tailwind.config.js 同一套色板:暖白纸面 + 墨松绿主色 + 陶土橙强调。 + * 与 Web 端 tailwind.config.js 同一套色板:暖白纸面 + 景泰蓝主色 + 陶土橙强调。 * 两端看同一张截图时,颜色应该能对上,所以这里的值不要单独调。 */ -val Paper = Color(0xFFFBF9F6) -val PaperSunken = Color(0xFFF3F0EA) -val Line = Color(0xFFE4E0D8) -val LineStrong = Color(0xFFD3CEC2) +val Paper = Color(0xFFF9FAFC) +val PaperSunken = Color(0xFFF3F5F9) +val Line = Color(0xFFE2E6EC) +val LineStrong = Color(0xFFD3D9E1) -val Ink = Color(0xFF1F2A28) -val InkSoft = Color(0xFF4A5854) -val InkMuted = Color(0xFF7C8985) +val Ink = Color(0xFF262D39) +val InkSoft = Color(0xFF4E5867) +val InkMuted = Color(0xFF6C7788) -val Primary = Color(0xFF2A5A50) -val PrimaryDeep = Color(0xFF1E443C) -val PrimarySoft = Color(0xFFE3EDE8) -val PrimaryTint = Color(0xFFD2E3DB) +val Primary = Color(0xFF274B73) +val PrimaryDeep = Color(0xFF1F3B5B) +val PrimarySoft = Color(0xFFDCE9F7) +val PrimaryTint = Color(0xFFB9D3EC) val Clay = Color(0xFFC2703D) val ClaySoft = Color(0xFFFBEADF) @@ -39,8 +39,8 @@ val Wheat = Color(0xFFA97C25) val WheatSoft = Color(0xFFF8EED7) val Rust = Color(0xFFB04A32) val RustSoft = Color(0xFFFBE5DF) -val Sky = Color(0xFF3E6F91) -val SkySoft = Color(0xFFE4EEF5) +val Sky = Color(0xFF2F86B0) +val SkySoft = Color(0xFFE8F4FA) private val colors = lightColorScheme( primary = Primary, diff --git a/android/app/src/main/res/drawable/ic_launcher_foreground.xml b/android/app/src/main/res/drawable/ic_launcher_foreground.xml index e146031..79103c1 100644 --- a/android/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/android/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -7,20 +7,20 @@ android:viewportHeight="108"> diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index cef75a5..19681ca 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -1,5 +1,5 @@ - - #2F6054 + + #274B73 diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml index 7412c85..a12cb34 100644 --- a/android/app/src/main/res/values/themes.xml +++ b/android/app/src/main/res/values/themes.xml @@ -2,7 +2,7 @@ diff --git a/tools/make-icons.py b/tools/make-icons.py new file mode 100644 index 0000000..3c07cb8 --- /dev/null +++ b/tools/make-icons.py @@ -0,0 +1,76 @@ +"""按 Web Logo(icons.tsx 的 )重绘 PWA 图标:景泰蓝底 + 三条浅蓝白线 + 一点亮蓝。 + +用法:python tools/make-icons.py +输出覆盖 web/public/icons/*.png(与 SVG 同一套比例:64 画布,rx=18,线宽 4,点半径 6)。 +""" + +from __future__ import annotations + +from pathlib import Path + +from PIL import Image, ImageDraw + +ROOT = Path(__file__).resolve().parents[1] +OUT = ROOT / "web" / "public" / "icons" + +BG = "#274B73" +LINE = "#EAF3FB" +DOT = "#5CA8E8" + +SS = 8 # 超采样倍数,边缘才不毛 + + +def draw(size: int, *, rounded: bool, inset: float = 0.0) -> Image.Image: + """在 size×size 上画 logo。inset 是安全区留白比例(maskable 图标要留)。""" + big = size * SS + img = Image.new("RGBA", (big, big), (0, 0, 0, 0)) + d = ImageDraw.Draw(img) + + if rounded: + d.rounded_rectangle([0, 0, big - 1, big - 1], radius=big * 18 / 64, fill=BG) + else: + d.rectangle([0, 0, big, big], fill=BG) + + # 64 画布坐标 → 像素;inset 让图形整体缩小居中(给 maskable 留安全区) + scale = big / 64 * (1 - 2 * inset) + off = big * inset + + def box(x: float, y: float) -> tuple[float, float]: + return off + x * scale, off + y * scale + + stroke = max(1, round(4 * scale)) + for y, x2 in ((23, 46), (32, 37), (41, 30)): + x1, yy = box(18, y) + d.line([x1, yy, *box(x2, y)], fill=LINE, width=stroke) + # 圆头:PIL 的 line 没有 cap,用小圆补两端 + r = stroke / 2 + for cx, cy in ((x1, yy), box(x2, y)): + d.ellipse([cx - r, cy - r, cx + r, cy + r], fill=LINE) + + cx, cy = box(46, 41) + r = 6 * scale + d.ellipse([cx - r, cy - r, cx + r, cy + r], fill=DOT) + + return img.resize((size, size), Image.LANCZOS) + + +def main() -> None: + OUT.mkdir(parents=True, exist_ok=True) + # 圆角版:favicon 之外的常规 PWA 图标 + draw(192, rounded=True).save(OUT / "icon-192.png") + draw(512, rounded=True).save(OUT / "icon-512.png") + # iOS 会自己裁圆角,底图必须是方的 + draw(180, rounded=False).save(OUT / "apple-touch-icon.png") + # maskable:图形缩进 12% 安全区 + draw(512, rounded=False, inset=0.12).save(OUT / "maskable-512.png") + # 文档里引用的安卓图标预览 + preview = ROOT / "2.Design" / "screens" / "android-icon-512.png" + preview.parent.mkdir(parents=True, exist_ok=True) + draw(512, rounded=True).save(preview) + for p in sorted(OUT.glob("*.png")): + print(p.relative_to(ROOT), Image.open(p).size) + print(preview.relative_to(ROOT), Image.open(preview).size) + + +if __name__ == "__main__": + main() diff --git a/web/index.html b/web/index.html index c6eed9b..b265564 100644 --- a/web/index.html +++ b/web/index.html @@ -5,13 +5,13 @@ 达道Task - + - + diff --git a/web/public/icons/apple-touch-icon.png b/web/public/icons/apple-touch-icon.png index d19f7d3..2610586 100644 Binary files a/web/public/icons/apple-touch-icon.png and b/web/public/icons/apple-touch-icon.png differ diff --git a/web/public/icons/icon-192.png b/web/public/icons/icon-192.png index f248111..1cf1a9b 100644 Binary files a/web/public/icons/icon-192.png and b/web/public/icons/icon-192.png differ diff --git a/web/public/icons/icon-512.png b/web/public/icons/icon-512.png index 3786a0e..82bbb62 100644 Binary files a/web/public/icons/icon-512.png and b/web/public/icons/icon-512.png differ diff --git a/web/public/icons/maskable-512.png b/web/public/icons/maskable-512.png index c81e760..12d3fff 100644 Binary files a/web/public/icons/maskable-512.png and b/web/public/icons/maskable-512.png differ diff --git a/web/src/components/icons.tsx b/web/src/components/icons.tsx index a147fc7..241bd03 100644 --- a/web/src/components/icons.tsx +++ b/web/src/components/icons.tsx @@ -290,13 +290,13 @@ export const Spinner = ({ size = 16, className = "" }: { size?: number; classNam export const Logo = ({ size = 28 }: { size?: number }) => ( - + - + ); diff --git a/web/tailwind.config.js b/web/tailwind.config.js index e03b597..b39d57e 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -6,42 +6,42 @@ export default { colors: { // 底色:暖白纸面,不用纯白,避免刺眼、也让卡片有层次 paper: { - 50: "#FCFCFA", - 100: "#F7F7F3", - 200: "#F0F0EA", - 300: "#E6E6DE", + 50: "#F9FAFC", + 100: "#F3F5F9", + 200: "#E9EDF3", + 300: "#DFE5ED", }, - // 分隔线与描边 + // 分隔线与描边(冷灰,跟蓝主色同一个中性轴) line: { - 100: "#ECECE6", - 200: "#E1E1D9", - 300: "#D2D2C8", + 100: "#EDEFF3", + 200: "#E2E6EC", + 300: "#D3D9E1", }, // 文字与中性 ink: { - 50: "#F6F8F7", - 100: "#E9EDEB", - 200: "#D7DDDA", - 300: "#BAC3C0", - 400: "#96A19D", - 500: "#6D7874", - 600: "#4E5956", - 700: "#39443F", - 800: "#252E2A", - 900: "#161C1A", + 50: "#F6F8FA", + 100: "#E9EEF3", + 200: "#D7DEE7", + 300: "#B8C2CE", + 400: "#929EAD", + 500: "#6C7788", + 600: "#4E5867", + 700: "#3A4352", + 800: "#262D39", + 900: "#161C25", }, - // 主色:墨松绿,克制、有"工作台"气质,避开任务工具常见的荧光蓝 + // 主色:景泰蓝(沉一点的靛蓝),克制、有"工作台"气质,避开任务工具常见的荧光蓝 primary: { - 50: "#EFF5F3", - 100: "#DBE9E4", - 200: "#B7D2C9", - 300: "#8CB6A9", - 400: "#619688", - 500: "#41796A", - 600: "#2F6054", - 700: "#264E44", - 800: "#1E3E36", - 900: "#172F29", + 50: "#EFF5FB", + 100: "#DCE9F7", + 200: "#B9D3EC", + 300: "#8FB6DD", + 400: "#6094C8", + 500: "#3E76AC", + 600: "#2E5D8D", + 700: "#274B73", + 800: "#1F3B5B", + 900: "#1A2F48", }, // 强调色:陶土橙,只用在"需要人注意"的地方 clay: { @@ -59,7 +59,8 @@ export default { sage: { 500: "#2F7D5E", 50: "#EAF4EF" }, wheat: { 500: "#A97C25", 50: "#F8F1E2" }, rust: { 500: "#B04A32", 50: "#FAEDE9" }, - sky: { 500: "#3E6F91", 50: "#EBF2F7" }, + // 信息色:比主色更亮更青一档,免得和 primary 的「进行中」糊在一起 + sky: { 500: "#2F86B0", 50: "#E8F4FA" }, }, fontFamily: { sans: [ @@ -93,8 +94,8 @@ export default { "3xl": "24px", }, boxShadow: { - card: "0 1px 2px rgba(22,28,26,0.04), 0 8px 24px -16px rgba(22,28,26,0.35)", - pop: "0 2px 6px rgba(22,28,26,0.06), 0 20px 48px -24px rgba(22,28,26,0.35)", + card: "0 1px 2px rgba(20,27,36,0.04), 0 8px 24px -16px rgba(20,27,36,0.35)", + pop: "0 2px 6px rgba(20,27,36,0.06), 0 20px 48px -24px rgba(20,27,36,0.35)", inset: "inset 0 1px 0 rgba(255,255,255,0.6)", }, keyframes: {