@usethink/cf-admin-fe
v0.2.8
Published
Vue 3 frontend admin kit for CF product family — composables, shell, ConfigField, list loader, login chrome, request/token/raw factories, error/batch helpers, system-config engine, nav helpers, i18n seeds, recipes (FE only; not cf-core)
Downloads
570
Maintainers
Readme
@usethink/cf-admin-fe
面向 Cloudflare 产品家族(cf-shop、cf-lottery、cf-auth 以及未来的 CF 管理端应用)的 Vue 3 管理端基础套件(admin foundation)。
状态: 0.2.8 — P0 三件套:resolveAdminT / tKit、AdminShell/ConfigField/MetadataDetail 契约测、size-report。完整用法见 docs/002_cf-admin-fe使用说明书与注意事项.md;起步清单见 recipes/README.md;路线图见 docs/003_cf-admin-fe极致化方案与取舍_2026-08-05.md。
核心目标: 最大化共享 管理端前端基础套件,让 CF 产品快速搭建 — 不通过克隆业务页面实现,也 不 强迫每个产品套用 shop 的 Bearer 模式。
| 会话模型 | 套件 | 线上消费方 |
|---|---|---|
| Bearer JWT + localStorage TTL | createAdminTokenSession | cf-shop, cf-lottery |
| Cookie HTTP 会话 + 本地守卫标志 | createAdminFlagSession + credentials: 'include' + 可选 onSuccess | cf-auth(验证方,非克隆目标) |
| 分层 | 包 | 内容 |
|---|---|---|
| Workers / 基础设施 | @usethink/cf-core | HTTP、crypto、media、secrets 等 |
| 管理端 UI 套件 | @usethink/cf-admin-fe | 表格 UX、shell、ConfigField、request/token 工厂、导航助手、i18n 种子、recipes、CSS |
为什么叫 cf-admin-fe(而不是 cf-admin)
仅前端。 -fe 后缀使 UI 套件代码与 Worker/API 基础设施保持区分。
不是业务中间层:不包含订单、抽奖、RBAC 产品页面或支付 UI。
边界(新增 API 前请先阅读)
cf-admin-fe 是 管理端基础套件,不是通用 Vue 组件库,也不是 Element Plus。
| 应做 | 不应做 | |---|---| | AdminPagination、AdminModal、ConfirmDialog、Toast、AdminShell、ConfigField | 日期选择器、级联、树、富文本、数据网格框架 | | 已被 ≥2 个线上消费方 验证的纯函数助手(或经验证的第三条路径,如 cookie 认证) | “以后可能有用”的抽象 | | 轻量 request/session 工厂 + 列表加载器 + 导航助手 | 业务页面、OAuth 流程、抽奖 L3、商城结算 |
心智成本才是真正的成本。 每次套件变更都必须让 cf-shop、cf-lottery、cf-auth 保持绿色(测试 + 类型检查)。有 3 个管理端时该成本可以摊销;只有 1 个管理端时就不值得。
提取气味的检验标准
- 好:两个产品已经复制粘贴了同样的 10 行 → 提取。
- 坏:只有 cf-lottery 需要 → 留在产品内。
- 坏:为了“一致性”而包装第三方日期选择器 → 在产品里用轻量专用库。
安装
# monorepo / 兄弟模板(开发期推荐)
npm install file:../cf-admin-fe
# 或发布后
npm install @usethink/cf-admin-fePeer 依赖:vue ^3.4。可选 peer 依赖:vue-i18n(组件在存在时会调用 $t)。
批量复制剧本(几分钟搭建新管理端)
快速路径:复制 recipes
包在 recipes/ 下提供 复制即用的起步代码(monorepo 中也可通过 @usethink/cf-admin-fe/recipes/* import 路径阅读):
| Recipe | 用途 |
|---|---|
| recipes/README.md | 有序检查清单 |
| recipes/reexports/* | 轻量 composable/组件/utils 再导出 |
| recipes/useAdminAuth.shop.ts | 单键 Bearer 会话 |
| recipes/useAdminRequest.ts | createAdminRequest + 401 处理器 |
| recipes/AdminLayout.vue | AdminShell + 产品菜单 hooks |
| recipes/router-guard.snippet.ts | decideAdminRouteAccess 守卫 |
| recipes/i18n-merge.snippet.ts | mergeAdminKitMessages |
| recipes/main-styles.snippet.ts | CSS 入口选项 |
参考线上消费方:cf-shop(Bearer)、cf-lottery(Bearer + L3 胶水)、cf-auth(cookie + flag — 用于 验证 套件 cookie 路径,不要复制其 OAuth/业务页面)。
1. 接入包 + 样式 + 全局 toast
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import '@usethink/cf-admin-fe/styles' // 或仅 primitives,见 assets/admin.css
createApp(App).use(router).use(i18n).mount('#app')<!-- App.vue:根节点挂一次 -->
<ToastContainer />2. 导入清单(最大化复用套件——不要重新实现)
| 需求 | 从哪导入 | 备注 |
|---|---|---|
| 表格分页/每页条数 | useTablePagination | /composables |
| 多选 + shift 连选 | useTableSelection | |
| 确认 + 可选复选框 | useConfirmDialog + ConfirmDialog | |
| Toast 队列 | useToast + ToastContainer | |
| 顺序批量操作 | useAdminBatchOperation | |
| 剪贴板 | copyWithToast(管理端主路径)/ copyText(按钮反馈)/ writeClipboardText(静默或降级) | |
| 弹窗 / 分页外壳 | AdminModal / AdminPagination | |
| 系统配置卡片 | ConfigField | 枚举 + 分单位金额的超集 |
| 侧边栏外壳 | AdminShell + useAdminSidebar | 菜单数据留在产品中 |
| Bearer token 会话 | createAdminTokenSession | 多 key / 伪装 = 产品侧 |
| Cookie 本地守卫标志 | createAdminFlagSession | HTTP cookie 是产品侧;flag 属于套件 |
| 管理端 fetch | createAdminRequest | http-ok | ok-envelope;cookie:credentials + onSuccess |
| 401 → 登录页 | createAdminUnauthorizedHandler | 只注入 clearSession |
| 路由守卫 | decideAdminRouteAccess | 之后再加产品专属规则 |
| 登录重定向 | resolveAdminLoginRedirect | 防开放重定向 |
| i18n 种子 | mergeAdminKitMessages | /i18n — 宿主覆盖优先 |
| 日期 | formatDate 等 | /utils |
| 异常转字符串 | toErrorMessage | toast / loader onError |
| 批量 toast 色调 | resolveBatchToastType / buildBatchToast | 纯函数;产品保留 i18n 键 |
| 批量数量上限守卫 | checkAdminBatchLimit | 默认 20 = auth 风格;对接 max(N) 的 API 须传 { limit: N }(如 shop 批量删 200) |
| 列表 limit/offset(字符串) | buildAdminOffsetParams / applyAdminOffsetParams | 仅 URLSearchParams / query string;产出 { limit: string, offset: string } |
| CSV 导出 | downloadCsv, safeCsvCell | 防公式注入 |
| 遮罩滚动锁 | lockBodyScroll / unlockBodyScroll | 引用计数 |
| 分 ↔ 元 | formatCents, parseYuanToCents | 多币种仍属产品侧 |
分页形态(必读,防误用):
| 后端契约 | 做法 | 参考 |
|---|---|---|
| Query:limit/offset 字符串(URLSearchParams) | buildAdminOffsetParams(page, limit) 或 applyAdminOffsetParams | cf-lottery 平台审计 / 租户 / webhook / 兑换码 |
| Body 或类型化 filter:limit/offset 数字 | 产品手写 offset: (page - 1) * limit(数字) | cf-shop Balance / Recharges / Vouchers 等 |
| Cursor | 产品自管;勿套 offset helper | shop 部分订单/日志列表 |
不要为了「用上 helper」把 number body 改成 string query。number 形态 helper 仅当 ≥2 产品 同形复制时再提案(观察期,见 docs/002 §5.8)。
优先使用 子路径 导入(/composables、/utils、/components/*.vue、/i18n),避免 Vitest 解析根 SFC 桶文件。
3. 最小产品胶水层(保持精简)
// useAdminAuth — shop 简化版(见 recipes/useAdminAuth.shop.ts)
import { createAdminTokenSession } from '@usethink/cf-admin-fe/composables'
const session = createAdminTokenSession({ storageKey: 'admin_token' })
export const readAdminToken = () => session.readToken()
export function useAdminAuth() {
return { token: session.token, isLoggedIn: session.isLoggedIn, setToken: session.setToken, clearToken: session.clearToken }
}// useAdminRequest — 尽量复用套件工厂
import { createAdminRequest } from '@usethink/cf-admin-fe/composables'
import { createAdminUnauthorizedHandler } from '@usethink/cf-admin-fe/utils'
import { readAdminToken, useAdminAuth } from './useAdminAuth'
const onUnauthorized = createAdminUnauthorizedHandler({
clearSession: () => useAdminAuth().clearToken(),
})
export const adminRequest = createAdminRequest({
getToken: () => readAdminToken(),
resultMode: 'ok-envelope', // shop 风格 body 可用 'http-ok'
errorField: 'message',
onUnauthorized,
})// i18n — 填补套件键;菜单/品牌文案留产品
import { mergeAdminKitMessages } from '@usethink/cf-admin-fe/i18n'
messages: {
'zh-CN': mergeAdminKitMessages(zhHost, 'zh-CN'),
en: mergeAdminKitMessages(enHost, 'en'),
}// router — 先走共享门禁
import { decideAdminRouteAccess } from '@usethink/cf-admin-fe/utils'
const d = decideAdminRouteAccess({ hasToken: !!readAdminToken(), requiresAuth: to.meta.requiresAuth, requiresGuest: to.meta.requiresGuest, fullPath: to.fullPath })
if (d.type === 'login') return { name: 'AdminLogin', query: { redirect: d.redirect }, replace: true }
if (d.type === 'home') return { name: 'AdminDashboard', replace: true }
// 产品专属:引导 / 平台角色 / …产品可将路径别名(@/composables/*)保留为 再导出,这样视图无需大规模改写。
4. 必须留在产品侧的内容(不要强行塞进套件)
- 业务管理端页面(
views/admin/*、抽奖views/platform/*页面主体) - 菜单项列表、页面标题、品牌名(传入
AdminShell) - 业务 API 模块(
api/admin.ts的方法面 / 业务 DTO)— 只有传输层使用createAdminRequest - 多租户伪装 / 平台角色键(抽奖 L3);使用
AdminShell上的#banner/#nav-extra插槽 - Cookie-flag 管理端(cf-auth)— 认证模型不同;仍可使用分页/toast/弹窗/utils
- 订单状态 / 多币种金额库(
@shared/money、订单状态映射) - 抽奖列表 UX 启发式(
admin-list-ux) - 产品错误类型(
AdminApiError)与业务下载方法签名(围绕createAdminRequestRaw的 token 优先包装) - 套件原语之外的业务专属 CSS(伪装横幅、语言选择、平台导航强调色)
5. i18n
推荐: mergeAdminKitMessages(host, locale),这样新产品永远不会遗漏套件键。
宿主仍应提供产品菜单(adminLayout.menu*)与业务字符串。字段级配置文案:adminConfig.fields.<key>.{label,description,effect} — 缺失时 ConfigField 通过 $te 回退到后端文本。
公开 API 面
根 / composables
useTablePagination、useTableSelection、useConfirmDialog、useToast、MAX_TOASTSuseAdminBatchOperation、writeClipboardText、copyText、copyWithToast(剪贴板 + 成功/失败 toast;与按钮内反馈的copyText互补)createAdminTokenSession、useAdminTokenSession、parseStoredAdminToken、readAdminTokenFromStorage、DEFAULT_ADMIN_TOKEN_TTL_MScreateAdminRequest、createAdminRequestRaw、AdminRequestErroruseAdminSidebaruseAdminListLoader— 列表页的竞态安全loading/loadError/ 序列
Utils(@usethink/cf-admin-fe/utils)
- 日期时间:
formatDate、toDateTimeLocalValue、dateTimeLocalToIso、isoToDateTimeLocal、formatIpFingerprint - CSV:
downloadCsv、safeCsvCell - 滚动:
lockBodyScroll、unlockBodyScroll、isBodyScrollLocked - 金额最小单位:
normalizeCents、formatCents、parseYuanToCents - 导航:
createAdminUnauthorizedHandler、decideAdminRouteAccess、resolveAdminLoginRedirect - 错误:
toErrorMessage - 批量 UX 纯函数:
buildBatchToast、resolveBatchToastType、formatBatchCountsMessage、formatDefaultBatchMixedMessage、hasBatchFailures、checkAdminBatchLimit、DEFAULT_ADMIN_BATCH_LIMIT(默认 20;产品max(N)须显式limit) - 列表查询:
buildAdminOffsetParams、applyAdminOffsetParams(1-based 页码 →limit/offset字符串,面向 URLSearchParams;不是 number body filter) - 日志元数据:
formatMetadata(安全JSON.stringify,抗循环引用;旧页可只用纯函数) - 系统配置引擎(纯函数):
groupDefinitionsByName、countSectionItems、buildActiveGroupedDefinitions、countAdvancedItems、resolveSystemConfigSectionId、getSystemConfigSection、listedSystemConfigGroups、auditSystemConfigSectionCoverage— 分节 id 联合、分组列表、危险区、API 由产品侧负责
i18n(@usethink/cf-admin-fe/i18n)
adminKitMessages、adminKitMessagesZhCN、adminKitMessagesEn、mergeAdminKitMessages
组件(@usethink/cf-admin-fe/components)
AdminPagination、AdminModal、ConfirmDialog、ToastContainerConfigField(boolean / 枚举多选 / integer / cents / sensitive / textarea)AdminShell(响应式侧边栏 + header / nav-extra / banner 插槽)AdminLoginShell(展示型登录卡片外壳;认证表单由产品侧实现)AdminMetadataDetail(审计日志详情弹层;依赖宿主adminLogs.*i18n 键;新页优先;已有自研详情 markup 的旧页不必强迁)
类型(@usethink/cf-admin-fe/types)
AdminSystemConfigFieldDefinition/ 别名AdminSystemConfigDefinition、ConfigFieldStatus等
样式
import '@usethink/cf-admin-fe/styles'
// 或按需:
import '@usethink/cf-admin-fe/styles/tokens.css'
import '@usethink/cf-admin-fe/styles/admin-primitives.css' // 含 .lang-select
import '@usethink/cf-admin-fe/styles/login-primitives.css' // .admin-login / .login-cardRecipes
参见 recipes/README.md。列表骨架见 recipes/list-page.snippet.ts(含 string vs number offset、批量 limit 注释)。
说明书与接线对照
- 完整用法 / 注意事项:
docs/002_cf-admin-fe使用说明书与注意事项.md - cf-shop 已接线什么、故意不接什么: 同文档 §5.8
- 提取决策与结案残留:
docs/001_…差距分析(勿当待办路线图)
开发与封包
npm install
npm test
npm run type-check
npm run clean # 清空 dist/,避免过期声明混入
npm run build # clean + 仅生成 composables/utils/types 的 .d.ts
npm pack # 触发 prepack→build;入口仍以 src/ 为准封包注意:
files含src、dist、recipes、README.md、LICENSEdist/在.gitignore中,由prepack现场生成;不要手写或提交旧版根dist/index.d.ts- 运行时
exports指向src/(Vue SFC + TS 源);dist仅为声明辅助
版本记录
- 0.1.x — 表格/toast/弹窗/分页原语
- 0.2.0 — 深度提取:ConfigField、AdminShell、token/request 工厂、日期时间/CSV/滚动/分位金额工具
- 0.2.1 — 导航助手、i18n 合并种子、recipes
- 0.2.2 — 系统配置纯函数引擎、
createAdminRequestRaw、AdminSystemConfigDefinition别名 - 0.2.3 —
useAdminListLoader、AdminLoginShell+ login-primitives CSS、原语中的.lang-select - 0.2.4 — OTP 助手、更深化的 recipes
- 0.2.5 —
createAdminFlagSession、requestonSuccess/ credentials、cookie recipes(cf-auth 验证) - 0.2.6 —
toErrorMessage、批量 toast/数量上限;formatMetadata+AdminMetadataDetail;buildAdminOffsetParams/applyAdminOffsetParams;copyWithToast;recipes/list-page.snippet.ts;封包 clean/prepack/LICENSE;完整说明书docs/002_… - 0.2.6 文档收口(无 API 变更) — README/recipes/001/002 对齐:string vs number offset、batch
limit须跟后端 max、shop §5.8 接线对照、MetadataDetail「新页优先」 - 0.2.7 —
.table-wrap去掉scrollbar-gutter: stable,消除无溢出时表格右侧缺口 - 0.2.8 —
resolveAdminT/tKit;AdminShell / ConfigField / AdminMetadataDetail 契约测;npm run size-report(CSS/源码规模 + runtime dep 门禁)
许可证
MIT(见仓库根目录 LICENSE)
