feat(web): 重做设计系统与全部页面,前端观感对齐当下审美
用户反馈"UI 太丑、太简陋"。不是换配色,而是先立 token 再让页面去用: - tailwind.config.js:纸白 paper / 墨绿 primary / 陶土橙 clay + 语义色 sage/wheat/rust/sky,字号阶、圆角阶、投影、动效 - index.css:卡片、按钮五种变体、输入、pill/chip、导航、骨架屏 - 新增 icons.tsx(线性图标集)与 ui.tsx(Button/Sheet/Toast/空态/骨架/PageHeader) - 新增 AppShell 取代 Layout + WorkspaceNav:桌面侧栏 + 移动顶部横条,工单未读角标 - 六个页面全部重写:登录、首页、任务、看板、工单、知识、成员、邀请落地 - 详情抽屉改用 Sheet,工单态补齐需求人/办理人/退回次数/动作按钮 e2e 因 DOM 升级同步调整(抽屉按 role=dialog 定位、下拉改用 aria-label), 三套用例(7 + 8 + 7 项)全部重跑通过;tsc 0 错,构建 45 modules / gzip 76.70 kB。
This commit is contained in:
+103
-30
@@ -1,7 +1,8 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { useAuth } from "../App";
|
||||
import { Layout } from "../components/Layout";
|
||||
import { Book, Check, Logo, TicketIcon } from "../components/icons";
|
||||
import { Button, Field, InlineAlert } from "../components/ui";
|
||||
import { ApiError, api, session, type Tokens } from "../lib/client";
|
||||
import type { User } from "../lib/types";
|
||||
|
||||
@@ -15,6 +16,20 @@ function safeNext(raw: string | null): string | null {
|
||||
return raw;
|
||||
}
|
||||
|
||||
const HIGHLIGHTS = [
|
||||
{ icon: <Check size={15} />, title: "一件事,从头到尾", desc: "收下、开工、完成,状态一路可见" },
|
||||
{
|
||||
icon: <TicketIcon size={15} />,
|
||||
title: "卡住就变成工单",
|
||||
desc: "原任务自动挂着等,办完再回来,不丢线",
|
||||
},
|
||||
{
|
||||
icon: <Book size={15} />,
|
||||
title: "办完自动留档",
|
||||
desc: "验收通过生成知识草稿,核对后进团队库",
|
||||
},
|
||||
];
|
||||
|
||||
export function LoginPage() {
|
||||
const { reload } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
@@ -66,66 +81,124 @@ export function LoginPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div className="mx-auto mt-10 max-w-sm">
|
||||
<div className="card">
|
||||
<h1 className="text-lg font-semibold">登录 / 注册</h1>
|
||||
<p className="mt-1 text-sm text-ink-500">手机号验证码登录,未注册会自动创建账号。</p>
|
||||
<div className="flex min-h-full flex-col lg:flex-row">
|
||||
{/* 左:品牌页 */}
|
||||
<div className="relative hidden flex-1 overflow-hidden bg-primary-900 lg:flex lg:flex-col lg:justify-between lg:p-12">
|
||||
<div className="grid-lines absolute inset-0 opacity-40" aria-hidden />
|
||||
<div className="absolute -left-24 -top-24 h-80 w-80 rounded-full bg-primary-700/60 blur-3xl" aria-hidden />
|
||||
<div className="absolute -bottom-32 right-0 h-96 w-96 rounded-full bg-clay-500/20 blur-3xl" aria-hidden />
|
||||
|
||||
<form className="mt-6 space-y-4" onSubmit={submit}>
|
||||
<div>
|
||||
<label className="label" htmlFor="phone">
|
||||
手机号
|
||||
</label>
|
||||
<div className="relative flex items-center gap-2.5 text-paper-50">
|
||||
<Logo size={32} />
|
||||
<span className="text-lg font-semibold tracking-tight">达道Task</span>
|
||||
</div>
|
||||
|
||||
<div className="relative max-w-md">
|
||||
<h1 className="font-display text-4xl font-semibold leading-[1.15] text-paper-50">
|
||||
从任务到知识,
|
||||
<br />
|
||||
一条道走到底。
|
||||
</h1>
|
||||
<p className="mt-4 text-base leading-relaxed text-paper-200/80">
|
||||
你自己的待办、需要别人搭把手的时刻、以及干完活留下的经验——本来就是一回事。
|
||||
</p>
|
||||
|
||||
<ul className="mt-10 space-y-5">
|
||||
{HIGHLIGHTS.map((h) => (
|
||||
<li key={h.title} className="flex gap-3">
|
||||
<span className="mt-0.5 grid h-7 w-7 shrink-0 place-items-center rounded-lg border border-paper-50/20 bg-paper-50/10 text-paper-100">
|
||||
{h.icon}
|
||||
</span>
|
||||
<span>
|
||||
<span className="block text-sm font-medium text-paper-50">{h.title}</span>
|
||||
<span className="block text-sm text-paper-200/65">{h.desc}</span>
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p className="relative text-2xs text-paper-200/45">
|
||||
个人先用着,团队按席位。数据在你自己的工作区里。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 右:表单 */}
|
||||
<main className="flex flex-1 items-center justify-center bg-paper-100 px-5 py-12">
|
||||
<div className="w-full max-w-sm">
|
||||
<div className="mb-8 flex items-center gap-2.5 lg:hidden">
|
||||
<Logo size={30} />
|
||||
<div className="leading-tight">
|
||||
<div className="text-sm font-semibold text-ink-900">达道Task</div>
|
||||
<div className="text-2xs text-ink-400">任务 · 工单 · 知识</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-semibold">登录</h2>
|
||||
<p className="mt-1.5 text-sm text-ink-500">手机号验证码登录,第一次登录会自动创建账号。</p>
|
||||
|
||||
<form className="mt-7 space-y-4" onSubmit={submit}>
|
||||
<Field label="手机号">
|
||||
<input
|
||||
id="phone"
|
||||
className="input"
|
||||
className="input h-11"
|
||||
inputMode="numeric"
|
||||
autoComplete="tel"
|
||||
placeholder="13800138000"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value.trim())}
|
||||
maxLength={11}
|
||||
/>
|
||||
</div>
|
||||
</Field>
|
||||
|
||||
<div>
|
||||
<label className="label" htmlFor="code">
|
||||
验证码
|
||||
</label>
|
||||
<Field label="验证码">
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
id="code"
|
||||
className="input"
|
||||
className="input h-11 tracking-[0.3em]"
|
||||
inputMode="numeric"
|
||||
autoComplete="one-time-code"
|
||||
placeholder="6 位数字"
|
||||
value={code}
|
||||
onChange={(e) => setCode(e.target.value.trim())}
|
||||
maxLength={6}
|
||||
/>
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
className="btn-ghost shrink-0"
|
||||
variant="ghost"
|
||||
className="h-11 shrink-0 px-3.5"
|
||||
disabled={!phoneValid || busy || countdown > 0}
|
||||
onClick={sendCode}
|
||||
>
|
||||
{countdown > 0 ? `${countdown}s` : "获取验证码"}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Field>
|
||||
|
||||
{devCode && (
|
||||
<p className="rounded-lg bg-accent-500/10 px-3 py-2 text-xs text-accent-600">
|
||||
开发环境验证码:{devCode}(生产环境会走短信通道,不回显)
|
||||
</p>
|
||||
<div className="flex items-start gap-2 rounded-lg border border-clay-500/20 bg-clay-50 px-3 py-2 text-xs text-clay-700">
|
||||
<span className="mt-px text-clay-500">开发环境验证码:{devCode}</span>
|
||||
<span className="text-clay-600/70">(生产环境走短信通道,不回显)</span>
|
||||
</div>
|
||||
)}
|
||||
{error && <p className="text-sm text-red-600">{error}</p>}
|
||||
{error && <InlineAlert>{error}</InlineAlert>}
|
||||
|
||||
<button className="btn-primary w-full" disabled={busy || !phoneValid || code.length < 4}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
size="lg"
|
||||
className="mt-2 w-full"
|
||||
disabled={busy || !phoneValid || code.length < 4}
|
||||
>
|
||||
{busy ? "处理中…" : "登录"}
|
||||
</button>
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<p className="mt-8 text-2xs leading-relaxed text-ink-400">
|
||||
登录即表示同意《服务条款》与《隐私政策》。我们不会把你的内容用于训练模型。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user