diff --git a/admin-frontend-vue/src/pages/records/RecordsDigitalHumanVersionPage.vue b/admin-frontend-vue/src/pages/records/RecordsDigitalHumanVersionPage.vue index fd8989f1..a6a8f7c0 100644 --- a/admin-frontend-vue/src/pages/records/RecordsDigitalHumanVersionPage.vue +++ b/admin-frontend-vue/src/pages/records/RecordsDigitalHumanVersionPage.vue @@ -1,11 +1,22 @@ @@ -107,36 +139,41 @@ onMounted(load) - + - + - - + + - - + + + + + - - - - + - + @@ -172,4 +209,5 @@ onMounted(load) .page-heading { display: flex; justify-content: space-between; align-items: flex-start; } .actions { display: flex; align-items: center; gap: 10px; } .dim { color: var(--el-text-color-secondary); font-size: 12px; } +.star { color: #e6a23c; font-weight: 700; } diff --git a/admin-frontend-vue/src/pages/records/digitalhuman-api.ts b/admin-frontend-vue/src/pages/records/digitalhuman-api.ts index 32e9405f..0570d273 100644 --- a/admin-frontend-vue/src/pages/records/digitalhuman-api.ts +++ b/admin-frontend-vue/src/pages/records/digitalhuman-api.ts @@ -29,3 +29,8 @@ export async function releaseDigitalHumanVersion(version: string): Promise export async function setLatestDigitalHumanVersion(version: string): Promise { await http.post(`${DIGITAL_HUMAN_VERSIONS_ENDPOINT}/${encodeURIComponent(version)}/set-latest`) } + +/** 删除数字人版本:DELETE /{version}。 */ +export async function deleteDigitalHumanVersion(version: string): Promise { + await http.delete(`${DIGITAL_HUMAN_VERSIONS_ENDPOINT}/${encodeURIComponent(version)}`) +} diff --git a/admin-frontend-vue/src/pages/records/digitalhuman-format.ts b/admin-frontend-vue/src/pages/records/digitalhuman-format.ts new file mode 100644 index 00000000..461cafe7 --- /dev/null +++ b/admin-frontend-vue/src/pages/records/digitalhuman-format.ts @@ -0,0 +1,9 @@ +/** 数字人版本展示格式化(module 13 task 261):MD5 截断展示;纯逻辑。 */ + +/** MD5 前 len 位(默认 12,对齐 admin)展示;空值回空串。 */ +export function md5Short(md5: string | null | undefined, len = 12): string { + const text = typeof md5 === 'string' ? md5.trim() : '' + if (!text) return '' + const max = typeof len === 'number' && Number.isFinite(len) && len >= 1 ? Math.floor(len) : 12 + return text.length <= max ? text : `${text.slice(0, max)}…` +} diff --git a/admin-frontend-vue/tests/task-261.test.ts b/admin-frontend-vue/tests/task-261.test.ts new file mode 100644 index 00000000..b6dbc58d --- /dev/null +++ b/admin-frontend-vue/tests/task-261.test.ts @@ -0,0 +1,65 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { readSource } from './helpers.ts' +import { digitalHumanStatusDisplay, latestBadgeText } from '../src/pages/records/digitalhuman-status-display.ts' +import { canReleaseDigitalHumanVersion } from '../src/pages/records/digitalhuman-release.ts' +import { canSetLatestDigitalHumanVersion } from '../src/pages/records/digitalhuman-latest.ts' +import { md5Short } from '../src/pages/records/digitalhuman-format.ts' + +// module 13 task 261:数字人版本页对齐 admin panel-digital-human-version(状态/最新★/大小/MD5截断/操作矩阵/上传文案)。 + +test('test_task_261_dh_normal_primary_path', () => { + // 正常主路径:状态映射与发布/设最新判定。 + assert.deepEqual(digitalHumanStatusDisplay('DRAFT'), { text: '草稿', tag: 'info' }) + assert.deepEqual(digitalHumanStatusDisplay('RELEASED'), { text: '已发布', tag: 'success' }) + assert.equal(canReleaseDigitalHumanVersion('DRAFT'), true) + assert.equal(canSetLatestDigitalHumanVersion('RELEASED'), true) + assert.equal(canSetLatestDigitalHumanVersion('DRAFT'), false) +}) + +test('test_task_261_dh_normal_variant_input', () => { + assert.equal(md5Short('a'.repeat(12)), 'a'.repeat(12), '恰好 12 位原样') + assert.equal(md5Short('a'.repeat(20)), `${'a'.repeat(12)}…`, '超长截 12 位并省略') + assert.equal(md5Short(''), '') + assert.equal(md5Short(null), '') +}) + +test('test_task_261_dh_normal_repeated_operation_is_idempotent', () => { + assert.equal(digitalHumanStatusDisplay('draft').text, '草稿') + assert.equal(digitalHumanStatusDisplay('draft').text, '草稿') +}) + +test('test_task_261_dh_boundary_empty_input', () => { + assert.equal(latestBadgeText(true), '最新') + assert.equal(latestBadgeText(false), '') + assert.equal(digitalHumanStatusDisplay('').text, '草稿', '未知状态归一草稿') +}) + +test('test_task_261_dh_boundary_single_item', () => { + const page = readSource('src/pages/records/RecordsDigitalHumanVersionPage.vue') + assert.match(page, /formatFileSize/, '文件大小走共享格式化') + assert.match(page, /md5Short/, 'MD5 截断展示') + assert.match(page, /canReleaseDigitalHumanVersion/, '发布按钮按状态显隐') +}) + +test('test_task_261_dh_boundary_limit_or_missing_field', () => { + // 边界上限:文件大小格式化(复用了版本页 formatFileSize)。 + const fmt = readSource('src/pages/records/version-format.ts') + assert.match(fmt, /formatFileSize/) +}) + +test('test_task_261_dh_invalid_input_rejected', () => { + // 异常:删除需二次确认;删除走后端 DELETE;删除失败有兜底。 + const page = readSource('src/pages/records/RecordsDigitalHumanVersionPage.vue') + assert.match(page, /此操作不可恢复/, '删除需危险二次确认') + assert.match(page, /删除失败/, '删除失败有兜底文案') + const api = readSource('src/pages/records/digitalhuman-api.ts') + assert.match(api, /http\.delete/, '删除调用 DELETE 适配') +}) + +test('test_task_261_dh_dependency_failure_returns_actionable_message', () => { + // 依赖失败:上传成功文案含“请在列表中点击发布”。 + const page = readSource('src/pages/records/RecordsDigitalHumanVersionPage.vue') + assert.match(page, /上传成功!版本:/, '上传成功文案需含版本') + assert.match(page, /请在列表中点击“发布”按钮/, '上传成功引导发布') +})