Appearance
UI 组件(components)
入口:import { ... } from '@pidaqing/uploader/components';主题:import '@pidaqing/uploader/components/theme.css'
本文档与源码 v0.2.0(tag
sdk/v0.2.0)逐项核对。组件是视图壳:状态单源在usePartakeUpload(composable),所有动作以事件上抛,业务方在父层接事件即可(方案 §11.3 关键决策 2)。Task类型见 core 文档。
<PartakeUploader>(整件组件)
拖拽区 + 工具栏(统计 + 全部启/停/续/取消)+ 任务列表一体;内部组合 PartakeDropZone + PartakeTaskList,control-base 接入即用,无需自行接线。
props
§10 配置透传项(controlBase 必填,其余可选;默认值与 core DEFAULT_CONFIG 对齐,见 core 配置表):
ts
interface PartakeUploaderProps {
/** §10 配置透传(与 ManagerOptions 同字段) */
controlBase: string; // 必填;BFF 控制面地址
client?: ControlClient; // 测试点注入
concurrency?: number; // 单任务分片并发
globalConcurrency?: number | 'auto'; // 跨任务并发('auto' 探测硬件)
maxRetry?: number; // 失败自动重试上限
maxUrlRenewalsPerChunk?: number; // 分片预签名续期上限
completeTimeout?: number; // complete 轮询超时 ms
pollInterval?: number; // 轮询间隔 ms
pollTimeout?: number; // 单次轮询超时 ms
autoStart?: boolean; // 入队即自动开始(默认 true)
hashWorkerEnabled?: boolean; // Web Worker 计算 hash(默认 true)
maxHashSyncSize?: number; // 超过该字节数强制走 Worker
validate?: { maxSize?: number; minSize?: number; accept?: string };
allowDuplicate?: boolean; // 同 hash 是否允许重复入队(默认 false)
speedLimit?: number; // 全局限速 KB/s(0 不限)
urlWaterline?: number | 'auto'; // 预取水位('auto' 由并发推导)
safeFileName?: boolean; // 服务端文件名净化(默认 true)
onMetrics?: (e: unknown) => void; // 埋点回调
/** 组件级 */
accept?: string; // 透传文件输入 accept
multiple?: boolean; // 多选(默认 true)
labels?: DeepPartial<PartakeLabels>; // 文案深合并覆写
}⚠️ Boolean prop 默认值语义:
multiple/autoStart/hashWorkerEnabled/allowDuplicate/safeFileName均在组件内显式声明默认值——Vue 对「未传的 Boolean prop」隐式置false,若不做显式默认会破坏「未配置 → coreDEFAULT_CONFIG取值」的透传语义。
emits
| 事件 | 载荷 | 触发时机 |
|---|---|---|
complete | task: Task | 状态迁移至 completed |
fail | task: Task | 迁移至 quarantined / failed / error |
abort | task: Task | 迁移至 aborted |
retry | task: Task | 用户点击行内 Retry(error 行) |
emits 基于终态迁移检测(watch 状态快照字符串):仅
status/paused变化触发,进度类变化不产生事件。
slots
| 名称 | slot props | 说明 |
|---|---|---|
trigger | — | 自定义触发区(默认渲染 labels.dropHint 文案);内容点击冒泡至 DropZone 根即触发文件选择 |
empty | — | 任务列表空态(默认 labels.empty) |
task | task: Task, labels: PartakeLabels | 整行覆写(覆写后默认行 UI 不再渲染) |
vue
<PartakeUploader control-base="/api/partake" @complete="onComplete" @fail="onFail">
<template #task="{ task, labels }">
<div>{{ task.fileName }} → {{ labels.buttons.download }}</div>
</template>
</PartakeUploader><PartakeDropZone>(原子组件)
拖拽 / 点选文件采集区,独立可用,也被 <PartakeUploader> 组合。
ts
withDefaults(defineProps<{ accept?: string; multiple?: boolean; hint?: string }>(), {
accept: undefined,
multiple: true,
hint: 'Drop files here or click to select',
});| prop | 类型 | 默认 | 说明 |
|---|---|---|---|
accept | string | undefined | 透传文件输入 accept |
multiple | boolean | true | 多选;false 时拖拽/点选只取第一个文件 |
hint | string | 英文默认 | 提示文案(仅独立使用时生效——被 <PartakeUploader> 组合时提示由 trigger slot / labels.dropHint 接管) |
emits:pick — [files: File[]](拖拽或点选后回调,非空才触发;同文件再次选择仍触发——内部 input value 已复位)
expose:open() — 编程式打开文件选择器
slots:default — 覆写提示文案(优先级高于 hint)
键盘可达性:根元素 role="button" + tabindex="0",Enter / Space 均触发选择。
<PartakeTaskList>(原子组件)
任务列表视图壳:默认行 = 文件名 + 大小 + 状态 + (uploading 进度条)+ (扫描/错误详情)+ 按状态推导的动作按钮;所有动作以 emit 上抛。
ts
defineProps<{ tasks: Task[]; labels?: Partial<PartakeLabels> }>();| prop | 类型 | 说明 |
|---|---|---|
tasks | Task[] | 必填;任务列表(来自 usePartakeUpload().tasks) |
labels | Partial<PartakeLabels> | 文案覆写(经 mergeLabels 深合并为完整包) |
emits(载荷均为 task: Task,按行状态推导展示哪些动作):
| 事件 | 行状态 |
|---|---|
start | idle(待开始) |
pause | uploading(上传中) |
resume | paused(已暂停) |
abort | paused / uploading(危险色) |
retry | error |
remove | 除 uploading/completing 外均可 |
download | completed |
默认行行为细节:
completing阶段(上传中-合并)禁一切动作(状态锁定,按钮不渲染);quarantined行展示scanDetail(病毒名 + 联系管理员);failed行展示 reason + 联系管理员;error行展示task.error.message+ Retry;- paused 徽标覆盖行内状态文案,附 resume 入口(§11.1)。
slots:empty(空态,默认 labels.empty);task(:task :labels 整行覆写)。
文案与工具函数(labels 模块)
ts
export type StatusTone = 'active' | 'paused' | 'success' | 'danger' | 'muted';
export type PartakeLabels = {
dropHint: string; // 触发区提示(Uploader trigger / DropZone 默认文案)
empty: string; // 任务列表空态
paused: string; // 暂停徽标
scanContact: string; // 联系管理员入口文案
virusPrefix: string; // 病毒名前缀
reasonPrefix: string; // 失败原因前缀
phase: Record<TaskPhase, string>; // idle/hashing/initing/uploading/completing
status: Record<Exclude<TaskStatus, 'idle' | 'uploading'>, string>;
buttons: { start; pause; resume; abort; retry; remove; download; startAll; pauseAll; resumeAll; abortAll: string };
stats: { total; uploading; completed; failed; error: string };
};| 符号 | 签名 | 说明 |
|---|---|---|
defaultLabels | PartakeLabels | 默认英文文案包(开源中性,深合并基底) |
mergeLabels | (override?: DeepPartial<PartakeLabels>) => PartakeLabels | 深合并(一层对象内合并:phase/status/buttons/stats 各自浅合并,无需写全量文案);未传返回 defaultLabels |
statusText | (task: Task, labels: PartakeLabels) => { text: string; tone: StatusTone } | §6.2 状态映射核心:paused 优先(覆盖 phase 文案);终态取 status 文案;其余取 phase 文案;tone 驱动行样式 |
scanDetail | (task: Task, labels: PartakeLabels) => string | null | 扫描信息行:quarantined + scan.result === 'infected' → virusPrefix + virusName + scanContact;failed → reasonPrefix + reason + scanContact(reason 取 scan.reason ?? error.message);其余 null |
formatBytes | (n: number) => string | 1024 进制 B/KB/MB/GB/TB;非有限数/负数 → '0 B';<10 保留两位小数,≥10 一位 |
DeepPartial | type | 嵌套可选(labels 定制入参) |
StatusTone | type | 状态色五档 |
主题定制(theme.css)
全部样式基于组件容器上的 CSS 变量 token,零 UI 库依赖;在 :root 或任意祖先容器覆写同名变量即可整体换肤。token 均为 --partake-* 真实定义(v0.2.0 核对):
| token | 默认 | 用途 |
|---|---|---|
--partake-color-primary | #4c6ef5 | 主色(hover/active 边框、进度条、primary 按钮) |
--partake-color-success | #2f9e44 | 完成态 |
--partake-color-danger | #e03131 | 错误/危险(quarantined/failed/error 行、danger 按钮) |
--partake-color-warning | #f08c00 | 暂停态 |
--partake-color-text | #1f2328 | 正文 |
--partake-color-text-secondary | #656d76 | 次要文本(大小、空态、统计) |
--partake-color-border | #d0d7de | 边框 |
--partake-color-surface | #ffffff | 表面(按钮底色) |
--partake-color-surface-hover | #f3f4f6 | 表面 hover |
--partake-progress-bg | #e9ecef | 进度条轨道 |
--partake-radius | 6px | 圆角 |
--partake-font-size | 14px | 基准字号 |
css
/* 覆写示例:业务主色 + 深色表面 */
:root {
--partake-color-primary: #1f6feb;
--partake-radius: 8px;
}