feat(auth): access token 缩到 2 小时 + refresh token 机制 #3

Merged
Blizzard merged 58 commits from feat/dev into main 2026-07-30 10:18:07 +00:00
3 changed files with 40 additions and 11 deletions
Showing only changes of commit cff6982613 - Show all commits
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="/admin/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/admin/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>admin</title> <title>admin</title>
<script type="module" crossorigin src="/admin/assets/index-BOP-ztlG.js"></script> <script type="module" crossorigin src="/admin/assets/index-BH08rQ_o.js"></script>
<link rel="stylesheet" crossorigin href="/admin/assets/index-PPcOOdtF.css"> <link rel="stylesheet" crossorigin href="/admin/assets/index-PPcOOdtF.css">
</head> </head>
<body> <body>
+37 -8
View File
@@ -26,6 +26,10 @@ const REPEATS = [
{ key: 'once', label: '一次性' }, { key: 'once', label: '一次性' },
] ]
const WEEKDAYS = ['一', '二', '三', '四', '五', '六', '日'] const WEEKDAYS = ['一', '二', '三', '四', '五', '六', '日']
// 阶段的展示顺序。后端 CareStages 就是这个顺序,但列表里要用它排序,
// 单独列一份免得依赖接口返回的时序
const STAGE_ORDER = ['刚到家 0-30 天', '幼年期', '青年期', '成年期', '中年期', '老年期']
const selectCls = 'h-9 rounded-md border border-input bg-card px-2 text-sm shrink-0' const selectCls = 'h-9 rounded-md border border-input bg-card px-2 text-sm shrink-0'
type Item = { type Item = {
@@ -78,6 +82,7 @@ function blank(kind: Item['kind']): Item {
export default function CareTemplates() { export default function CareTemplates() {
const [list, setList] = useState<Tpl[]>([]) const [list, setList] = useState<Tpl[]>([])
const [tab, setTab] = useState('cat')
const [types, setTypes] = useState<any[]>([]) const [types, setTypes] = useState<any[]>([])
const [stages, setStages] = useState<string[]>([]) const [stages, setStages] = useState<string[]>([])
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
@@ -106,7 +111,8 @@ export default function CareTemplates() {
}, []) }, [])
function create() { function create() {
setTpl({ id: '', species: 'cat', stage: '成年期', name: '', summary: '', sort: 0, enabled: true }) // 默认跟当前 tab 的物种,省一步点击
setTpl({ id: '', species: tab, stage: '成年期', name: '', summary: '', sort: 0, enabled: true })
setItems([]) setItems([])
setErr('') setErr('')
setOpen(true) setOpen(true)
@@ -159,6 +165,13 @@ export default function CareTemplates() {
upd(it._k, 'weekdays', next.join(',')) upd(it._k, 'weekdays', next.join(','))
} }
// 按 tab 过滤,并按阶段顺序排(数据库按 sort 排,但 sort 是全局的,
// 分物种看时会跳号)
const order = STAGE_ORDER
const shown = list
.filter((t) => t.species === tab)
.sort((a, b) => order.indexOf(a.stage) - order.indexOf(b.stage))
const plans = items.filter((i) => i.kind === 'plan') const plans = items.filter((i) => i.kind === 'plan')
const reminders = items.filter((i) => i.kind === 'reminder') const reminders = items.filter((i) => i.kind === 'reminder')
@@ -191,12 +204,30 @@ export default function CareTemplates() {
</Button> </Button>
</div> </div>
{/* 猫狗分开看。混在一起 10 套要上下找,而且阶段名一样(幼年期/青年期…),
扫一眼分不清哪套是猫的 */}
<div className="mb-4 flex items-center gap-2">
{SPECIES.map((s) => {
const n = list.filter((t) => t.species === s.key).length
return (
<Button
key={s.key}
size="sm"
variant={tab === s.key ? 'default' : 'outline'}
onClick={() => setTab(s.key)}
>
<s.Icon className="h-4 w-4" /> {s.label} {n}
</Button>
)
})}
</div>
<Card> <Card>
<CardContent className="pt-6"> <CardContent className="pt-6">
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead className="w-24">/</TableHead> <TableHead className="w-24"></TableHead>
<TableHead></TableHead> <TableHead></TableHead>
<TableHead></TableHead> <TableHead></TableHead>
<TableHead className="w-20"></TableHead> <TableHead className="w-20"></TableHead>
@@ -204,13 +235,11 @@ export default function CareTemplates() {
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{list.map((t) => { {shown.map((t) => {
const c = spread(t) const c = spread(t)
return ( return (
<TableRow key={t.id}> <TableRow key={t.id}>
<TableCell className="text-muted-foreground"> <TableCell className="text-muted-foreground">{t.stage}</TableCell>
{t.species === 'dog' ? '狗' : '猫'} · {t.stage}
</TableCell>
<TableCell> <TableCell>
<div className="font-medium">{t.name || '(未命名)'}</div> <div className="font-medium">{t.name || '(未命名)'}</div>
<div className="text-xs text-muted-foreground">{t.summary}</div> <div className="text-xs text-muted-foreground">{t.summary}</div>
@@ -232,10 +261,10 @@ export default function CareTemplates() {
</TableRow> </TableRow>
) )
})} })}
{!list.length && ( {!shown.length && (
<TableRow> <TableRow>
<TableCell colSpan={5} className="py-10 text-center text-muted-foreground"> <TableCell colSpan={5} className="py-10 text-center text-muted-foreground">
</TableCell> </TableCell>
</TableRow> </TableRow>
)} )}