From e2ca3bfa81cbb4b67b10ccb61fd55e8ca4710876 Mon Sep 17 00:00:00 2001 From: Zhou Weidong <502333560@qq.com> Date: Thu, 22 Oct 2026 14:47:01 +0800 Subject: [PATCH] =?UTF-8?q?refactor(admin):=20=E6=8B=86=E5=88=86=E5=89=A7?= =?UTF-8?q?=E9=9B=86=E8=BD=AC=E7=A0=81=E4=BA=A7=E7=89=A9=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plans/2026-10-22-transcode-panel-split.md | 26 + docs/plans/PROGRESS.md | 49 ++ .../upload/EpisodeTranscodeFrames.vue | 315 +++++++++ .../upload/EpisodeTranscodePanel.vue | 666 ++---------------- .../upload/EpisodeTranscodeRenditions.vue | 119 ++++ .../upload/EpisodeTranscodeStatusBadge.vue | 45 ++ .../upload/EpisodeTranscodeStatusPanel.vue | 155 ++++ .../upload/episodeTranscodeUi.ts | 93 +++ 8 files changed, 860 insertions(+), 608 deletions(-) create mode 100644 docs/plans/2026-10-22-transcode-panel-split.md create mode 100644 src/views/contentManagement/upload/EpisodeTranscodeFrames.vue create mode 100644 src/views/contentManagement/upload/EpisodeTranscodeRenditions.vue create mode 100644 src/views/contentManagement/upload/EpisodeTranscodeStatusBadge.vue create mode 100644 src/views/contentManagement/upload/EpisodeTranscodeStatusPanel.vue create mode 100644 src/views/contentManagement/upload/episodeTranscodeUi.ts diff --git a/docs/plans/2026-10-22-transcode-panel-split.md b/docs/plans/2026-10-22-transcode-panel-split.md new file mode 100644 index 0000000..7d27a25 --- /dev/null +++ b/docs/plans/2026-10-22-transcode-panel-split.md @@ -0,0 +1,26 @@ +# 剧集转码面板组件拆分 + +## 目标 + +- 将 `EpisodeTranscodePanel.vue` 从 816 行降到 500 行以内。 +- 状态轮询、分页请求与切集重置继续由父面板统一管理。 +- UI 按职责拆为状态徽标、状态操作区、清晰度表格、帧图预览。 + +## 组件 + +1. `EpisodeTranscodeStatusBadge.vue`:折叠标题中的状态与进度。 +2. `EpisodeTranscodeStatusPanel.vue`:状态、进度、重试次数、错误与重转按钮。 +3. `EpisodeTranscodeRenditions.vue`:清晰度表格、复制地址、新窗口打开。 +4. `EpisodeTranscodeFrames.vue`:逐帧网格、雪碧图、分页与放大预览。 + +公共状态映射与格式化放入 `episodeTranscodeUi.ts`。 + +## 风险 + +- 父组件仍负责全部接口、轮询、请求序号和分页状态,子组件只通过 props/emits 交互。 +- 不新增依赖、不改接口和 locales 文案。 + +## 验证 + +- `wc -l` 确认所有相关文件不超过 500 行。 +- `pnpm lint:eslint`、`pnpm build:dev`。 diff --git a/docs/plans/PROGRESS.md b/docs/plans/PROGRESS.md index fc4aa24..c714667 100644 --- a/docs/plans/PROGRESS.md +++ b/docs/plans/PROGRESS.md @@ -51,3 +51,52 @@ Build successful. Please see dist-dev directory ``` 解读:开发构建成功;circular chunk 为仓库既有 Element Plus 分包警告,本次未改构建配置。 + +## 2026-10-22 剧集转码面板组件拆分 + +- 分支:`feature/transcode-panel-split` +- 基线:`develop` / `origin/develop` = `52b10d3` +- 计划:`docs/plans/2026-10-22-transcode-panel-split.md` + +已读取 `AGENTS.md` 与 `CODING_STANDARDS.md`。当前 +`EpisodeTranscodePanel.vue` 为 816 行,超过单文件 500 行红线。 +拆分将保持父组件的数据获取、轮询清理、分页请求与切集重置逻辑不变。 + +### 拆分结果 + +```text +$ wc -l src/views/contentManagement/upload/EpisodeTranscodePanel.vue \ + src/views/contentManagement/upload/EpisodeTranscodeStatusBadge.vue \ + src/views/contentManagement/upload/EpisodeTranscodeStatusPanel.vue \ + src/views/contentManagement/upload/EpisodeTranscodeRenditions.vue \ + src/views/contentManagement/upload/EpisodeTranscodeFrames.vue \ + src/views/contentManagement/upload/episodeTranscodeUi.ts +266 EpisodeTranscodePanel.vue +45 EpisodeTranscodeStatusBadge.vue +155 EpisodeTranscodeStatusPanel.vue +119 EpisodeTranscodeRenditions.vue +315 EpisodeTranscodeFrames.vue +93 episodeTranscodeUi.ts +``` + +解读:父面板只保留接口调用、轮询、请求序号、分页状态与组件组合; +状态、清晰度、帧图分别独立,全部文件均低于 500 行。 + +### 门禁 + +```text +$ pnpm ts:check +> vue-tsc --noEmit --skipLibCheck +(通过) + +$ pnpm lint:eslint +> eslint --fix --ext .js,.ts,.vue ./src +(0 error / 0 warning) + +$ pnpm build:dev + WARN Circular chunk: element-plus -> vue-chunks -> element-plus. +Build successful. Please see dist-dev directory +``` + +解读:组件 props/emits、类型检查、ESLint 和开发构建全部通过; +循环 chunk 为项目既有警告。轮询启停、接口路径、分页参数和 locales 文案未改变。 diff --git a/src/views/contentManagement/upload/EpisodeTranscodeFrames.vue b/src/views/contentManagement/upload/EpisodeTranscodeFrames.vue new file mode 100644 index 0000000..d2c3d50 --- /dev/null +++ b/src/views/contentManagement/upload/EpisodeTranscodeFrames.vue @@ -0,0 +1,315 @@ + + + + + + + + diff --git a/src/views/contentManagement/upload/EpisodeTranscodePanel.vue b/src/views/contentManagement/upload/EpisodeTranscodePanel.vue index a9fcc53..72a8134 100644 --- a/src/views/contentManagement/upload/EpisodeTranscodePanel.vue +++ b/src/views/contentManagement/upload/EpisodeTranscodePanel.vue @@ -1,55 +1,36 @@ - + + + + + diff --git a/src/views/contentManagement/upload/EpisodeTranscodeStatusBadge.vue b/src/views/contentManagement/upload/EpisodeTranscodeStatusBadge.vue new file mode 100644 index 0000000..47d0d31 --- /dev/null +++ b/src/views/contentManagement/upload/EpisodeTranscodeStatusBadge.vue @@ -0,0 +1,45 @@ + + + + + + + + diff --git a/src/views/contentManagement/upload/EpisodeTranscodeStatusPanel.vue b/src/views/contentManagement/upload/EpisodeTranscodeStatusPanel.vue new file mode 100644 index 0000000..c876a70 --- /dev/null +++ b/src/views/contentManagement/upload/EpisodeTranscodeStatusPanel.vue @@ -0,0 +1,155 @@ + + + + + + + + diff --git a/src/views/contentManagement/upload/episodeTranscodeUi.ts b/src/views/contentManagement/upload/episodeTranscodeUi.ts new file mode 100644 index 0000000..38b0610 --- /dev/null +++ b/src/views/contentManagement/upload/episodeTranscodeUi.ts @@ -0,0 +1,93 @@ +// Author: fruit.com.cn +// 剧集转码面板的状态映射与展示格式化 + +export type TranscodeTagType = 'primary' | 'success' | 'warning' | 'danger' | 'info' +export type TranscodeProgressStatus = 'success' | 'exception' | undefined + +export const THUMBNAIL_PAGE_SIZE = 100 +export const THUMBNAIL_PAGE_SIZES = [100, 200, 500] + +const STATUS_KEYS: Record = { + QUEUED: 'queued', + RUNNING: 'running', + SUCCESS: 'success', + FAILED: 'failed', + EMPTY: 'empty', + NO_VIDEO: 'noVideo', + NO_MEDIA_ASSET: 'noMediaAsset', + NO_TASK: 'noTask', + NO_EPISODE: 'noEpisode', + EPISODE_NOT_FOUND: 'episodeNotFound', + INVALID: 'invalid', + READY: 'ready' +} +const BYTES_PER_KB = 1024 +const BYTES_PER_MB = BYTES_PER_KB * 1024 +const BYTES_PER_GB = BYTES_PER_MB * 1024 +const SECONDS_PER_MINUTE = 60 +const MILLIS_PER_SECOND = 1000 + +export function transcodeStatusKey(status?: string): string { + return STATUS_KEYS[String(status || '').toUpperCase()] || 'unknown' +} + +export function transcodeStatusType(status?: string): TranscodeTagType { + switch (String(status || '').toUpperCase()) { + case 'SUCCESS': + return 'success' + case 'RUNNING': + return 'warning' + case 'FAILED': + return 'danger' + case 'QUEUED': + return 'primary' + default: + return 'info' + } +} + +export function transcodeProgressStatus(status?: string): TranscodeProgressStatus { + const normalized = String(status || '').toUpperCase() + if (normalized === 'SUCCESS') return 'success' + if (normalized === 'FAILED') return 'exception' + return undefined +} + +export function safeTranscodeProgress(value?: number): number { + return Math.min(100, Math.max(0, Number(value || 0))) +} + +export function renditionStatusLabelKey(status?: string): string { + return transcodeStatusKey(status) +} + +export function renditionStatusType(status?: string): TranscodeTagType { + const normalized = String(status || '').toUpperCase() + if (normalized === 'READY') return 'success' + if (normalized === 'FAILED') return 'danger' + return 'info' +} + +export function formatBytes(value?: number): string { + const size = Number(value || 0) + if (size >= BYTES_PER_GB) return `${(size / BYTES_PER_GB).toFixed(2)} GB` + if (size >= BYTES_PER_MB) return `${(size / BYTES_PER_MB).toFixed(1)} MB` + if (size >= BYTES_PER_KB) return `${Math.round(size / BYTES_PER_KB)} KB` + return `${size} B` +} + +export function formatDuration(seconds?: number): string { + const total = Math.max(0, Number(seconds || 0)) + const minutes = Math.floor(total / SECONDS_PER_MINUTE) + const remainder = total % SECONDS_PER_MINUTE + return `${String(minutes).padStart(2, '0')}:${String(remainder).padStart(2, '0')}` +} + +export function formatTimeMs(milliseconds?: number): string { + const totalMs = Math.max(0, Number(milliseconds || 0)) + const totalSeconds = Math.floor(totalMs / MILLIS_PER_SECOND) + const minutes = Math.floor(totalSeconds / SECONDS_PER_MINUTE) + const seconds = totalSeconds % SECONDS_PER_MINUTE + const tenths = Math.floor((totalMs % MILLIS_PER_SECOND) / 100) + return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${tenths}` +}