task-274(验收反馈): 15 列表页表格列 width→min-width 自适应(DuplicateCheck 例外)

This commit is contained in:
2026-09-06 01:50:38 +08:00
parent 0315a3a93d
commit c3dc2a3f2f
16 changed files with 109 additions and 50 deletions
@@ -97,10 +97,10 @@ onMounted(loadGroups)
</el-row>
<el-card shadow="never">
<el-table v-loading="loading" :data="pagedRows" stripe>
<el-table-column type="index" label="序号" width="80" />
<el-table-column type="index" label="序号" min-width="80" />
<el-table-column prop="name" label="分组名称" min-width="180" />
<el-table-column prop="leaderUsername" label="组长" width="170" />
<el-table-column prop="memberCount" label="组员数量" width="100" />
<el-table-column prop="leaderUsername" label="组长" min-width="170" />
<el-table-column prop="memberCount" label="组员数量" min-width="100" />
<el-table-column label="组员" min-width="240">
<template #default="{ row }">
<template v-if="row.memberUsernames.length">
@@ -115,7 +115,7 @@ onMounted(loadGroups)
<el-table-column prop="updatedAt" label="修改时间" min-width="170">
<template #default="{ row }">{{ formatDateTime(row.updatedAt || row.createdAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="140" fixed="right">
<el-table-column label="操作" min-width="140" fixed="right">
<template #default="{ row }">
<el-button link type="primary" :disabled="!canEditOf(row)" @click="openEdit(row)">编辑</el-button>
<el-button link type="danger" :disabled="!canEditOf(row)" @click="removeGroup(row)">删除</el-button>
@@ -182,7 +182,7 @@ onMounted(loadMenus)
<el-card shadow="never">
<p class="menu-drag-tip">拖动每行左侧的手柄可调整同级菜单的显示顺序松开后自动保存</p>
<el-table v-loading="loading" :data="rows" row-key="id" default-expand-all stripe>
<el-table-column label="排序" width="64">
<el-table-column label="排序" min-width="64">
<template #default="{ row }">
<span
class="menu-drag-handle"
@@ -196,9 +196,9 @@ onMounted(loadMenus)
></span>
</template>
</el-table-column>
<el-table-column prop="id" label="ID" width="70" />
<el-table-column prop="id" label="ID" min-width="70" />
<el-table-column prop="name" label="菜单名称" min-width="180" />
<el-table-column label="菜单类型" width="110">
<el-table-column label="菜单类型" min-width="110">
<template #default="{ row }">
<el-tag size="small" :type="(row as MenuManageNode).menuType === 'app' ? 'info' : 'primary'">{{ menuTypeLabel((row as MenuManageNode).menuType) }}</el-tag>
</template>
@@ -211,7 +211,7 @@ onMounted(loadMenus)
<el-table-column prop="createdAt" label="创建时间" min-width="150">
<template #default="{ row }">{{ formatDateTime((row as MenuManageNode).createdAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right">
<el-table-column label="操作" min-width="160" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="openEdit(row as MenuManageNode)">编辑</el-button>
<el-button link type="danger" :disabled="menuDeleteReason(row as MenuManageNode) !== undefined" @click="removeMenu(row as MenuManageNode)">删除</el-button>
@@ -157,18 +157,18 @@ onMounted(loadUsers)
</template>
</el-alert>
<el-table v-loading="loading" :data="rows" stripe :empty-text="userListEmptyHint()">
<el-table-column prop="id" label="ID" width="90" />
<el-table-column prop="id" label="ID" min-width="90" />
<el-table-column prop="username" label="用户名" min-width="180" />
<el-table-column label="角色" width="140">
<el-table-column label="角色" min-width="140">
<template #default="{ row }">
<span>{{ roleLabel(row.role) }}</span>
</template>
</el-table-column>
<el-table-column prop="creatorUsername" label="所属管理员" width="160" />
<el-table-column prop="createdAt" label="创建时间" width="180">
<el-table-column prop="creatorUsername" label="所属管理员" min-width="160" />
<el-table-column prop="createdAt" label="创建时间" min-width="180">
<template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right">
<el-table-column label="操作" min-width="160" fixed="right">
<template #default="{ row }">
<el-button link type="primary" :disabled="!editableOf(row)" @click="openEdit(row)">编辑</el-button>
<el-button
@@ -198,7 +198,7 @@ onMounted(() => {
<el-table-column prop="createdAt" label="创建时间" min-width="170">
<template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<el-table-column label="操作" min-width="150" fixed="right">
<template #default="{ row }">
<el-button text type="primary" size="small" @click="openEdit(row as InvalidAsinItem)">编辑</el-button>
<el-button text type="danger" size="small" @click="remove(row as InvalidAsinItem)">删除</el-button>
@@ -358,19 +358,19 @@ onMounted(() => {
<el-card shadow="never">
<el-table v-loading="loading" :data="rows" stripe border>
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="id" label="ID" min-width="80" />
<el-table-column prop="dataValue" label="ASIN" min-width="170" />
<el-table-column label="国家" width="100" align="center">
<el-table-column label="国家" min-width="100" align="center">
<template #default="{ row }">{{ asinCountryLabel((row as DedupeTotalItem).country || '') }}</template>
</el-table-column>
<el-table-column prop="username" label="用户名" width="140" />
<el-table-column label="分组" width="150">
<el-table-column prop="username" label="用户名" min-width="140" />
<el-table-column label="分组" min-width="150">
<template #default="{ row }">{{ (row as DedupeTotalItem).groupName || '未分组' }}</template>
</el-table-column>
<el-table-column prop="createdAt" label="创建时间" width="170">
<el-table-column prop="createdAt" label="创建时间" min-width="170">
<template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<el-table-column label="操作" min-width="150" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="openEdit(row as DedupeTotalItem)">编辑</el-button>
<el-button link type="danger" @click="removeRow(row as DedupeTotalItem)">删除</el-button>
@@ -331,10 +331,10 @@ onMounted(loadTree)
<el-table-column label="层级路径" min-width="240">
<template #default="{ row }">{{ (row as ProductCategoryNode).path || (row as ProductCategoryNode).name }}</template>
</el-table-column>
<el-table-column label="排序" width="90">
<el-table-column label="排序" min-width="90">
<template #default="{ row }">{{ (row as ProductCategoryNode).sortOrder ?? '—' }}</template>
</el-table-column>
<el-table-column label="来源" width="100">
<el-table-column label="来源" min-width="100">
<template #default="{ row }">
<el-tag size="small" effect="plain" :type="(row as ProductCategoryNode).isBuiltin ? 'info' : 'success'">
{{ productCategorySourceLabel(row as ProductCategoryNode) }}
@@ -344,7 +344,7 @@ onMounted(loadTree)
<el-table-column label="说明" min-width="180">
<template #default="{ row }">{{ (row as ProductCategoryNode).description || '—' }}</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<el-table-column label="操作" min-width="150" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="openEdit(row as ProductCategoryNode)">编辑</el-button>
<el-button link type="danger" :disabled="!canDelete(row as ProductCategoryNode)" @click="removeCategory(row as ProductCategoryNode)">删除</el-button>
@@ -361,10 +361,10 @@ onMounted(() => {
<el-card shadow="never">
<el-table v-loading="loading" :data="displayRows" :span-method="spanMethod" stripe border>
<el-table-column label="序号" width="80">
<el-table-column label="序号" min-width="80">
<template #default="{ row }">{{ row.rowNo }}</template>
</el-table-column>
<el-table-column label="分组" width="130">
<el-table-column label="分组" min-width="130">
<template #default="{ row }">{{ row.item.groupName || '—' }}</template>
</el-table-column>
<el-table-column label="店铺" min-width="150">
@@ -376,13 +376,13 @@ onMounted(() => {
<span v-else class="dim">-</span>
</template>
</el-table-column>
<el-table-column label="国家" width="110">
<el-table-column label="国家" min-width="110">
<template #default="{ row }">
<span v-if="row.country">{{ asinCountryLabel(row.country) }}</span>
<span v-else class="dim">-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="90" fixed="right">
<el-table-column label="操作" min-width="90" fixed="right">
<template #default="{ row }">
<el-button v-if="row.isFirst" link type="primary" @click="openConfig(row.item as QueryAsinItem)">配置</el-button>
</template>
@@ -407,10 +407,10 @@ onMounted(() => {
<el-card shadow="never">
<el-table v-loading="loading" :data="displayRows" :span-method="spanMethod" stripe border>
<el-table-column label="序号" width="80">
<el-table-column label="序号" min-width="80">
<template #default="{ row }">{{ row.rowNo }}</template>
</el-table-column>
<el-table-column label="分组" width="130">
<el-table-column label="分组" min-width="130">
<template #default="{ row }">{{ row.item.groupName || '—' }}</template>
</el-table-column>
<el-table-column label="店铺" min-width="150">
@@ -422,7 +422,7 @@ onMounted(() => {
<span v-else class="dim">-</span>
</template>
</el-table-column>
<el-table-column label="国家" width="110">
<el-table-column label="国家" min-width="110">
<template #default="{ row }">
<span v-if="row.country">{{ asinCountryLabel(row.country) }}</span>
<span v-else class="dim">-</span>
@@ -434,7 +434,7 @@ onMounted(() => {
<span v-else class="dim">-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="90" fixed="right">
<el-table-column label="操作" min-width="90" fixed="right">
<template #default="{ row }">
<el-button v-if="row.isFirst" link type="primary" @click="openConfig(row.item as SkipPriceItem)">配置</el-button>
</template>
@@ -190,23 +190,23 @@ onMounted(load)
<el-card shadow="never">
<el-table v-loading="loading" :data="pagedItems" stripe border empty-text="暂无数字人版本">
<el-table-column prop="version" label="版本号" min-width="150" />
<el-table-column label="状态" width="100">
<el-table-column label="状态" min-width="100">
<template #default="{ row }">
<el-tag :type="statusOf(row as DigitalHumanVersion).tag" size="small">
{{ statusOf(row as DigitalHumanVersion).text }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="最新" width="60" align="center">
<el-table-column label="最新" min-width="60" align="center">
<template #default="{ row }">
<span v-if="(row as DigitalHumanVersion).isLatest" class="star" title="最新版本"></span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="文件大小" width="100">
<el-table-column label="文件大小" min-width="100">
<template #default="{ row }">{{ digitalHumanVersionFileSizeText((row as DigitalHumanVersion).fileSize) }}</template>
</el-table-column>
<el-table-column label="MD5" width="130">
<el-table-column label="MD5" min-width="130">
<template #default="{ row }">
<span :title="(row as DigitalHumanVersion).md5 || ''">{{ (row as DigitalHumanVersion).md5 ? `${((row as DigitalHumanVersion).md5 || '').substring(0, 12)}...` : '—' }}</span>
</template>
@@ -219,7 +219,7 @@ onMounted(load)
<el-table-column label="发布时间" min-width="170">
<template #default="{ row }">{{ formatDateTime((row as DigitalHumanVersion).releasedAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="240" fixed="right">
<el-table-column label="操作" min-width="240" fixed="right">
<template #default="{ row }">
<el-button v-if="canReleaseDigitalHumanVersion((row as DigitalHumanVersion).status)" text type="warning" size="small" :loading="confirming === (row as DigitalHumanVersion).version" @click="release(row as DigitalHumanVersion)">发布</el-button>
<el-button v-if="!canReleaseDigitalHumanVersion((row as DigitalHumanVersion).status) && canSetLatestDigitalHumanVersion((row as DigitalHumanVersion).status) && !(row as DigitalHumanVersion).isLatest" text type="primary" size="small" :loading="confirming === (row as DigitalHumanVersion).version" @click="setLatest(row as DigitalHumanVersion)">设为最新</el-button>
@@ -163,8 +163,8 @@ onMounted(() => {
</template>
</el-alert>
<el-table v-loading="loading" :data="rows" stripe :empty-text="emptyText">
<el-table-column prop="id" label="ID" width="90" />
<el-table-column prop="username" label="用户" width="140" />
<el-table-column prop="id" label="ID" min-width="90" />
<el-table-column prop="username" label="用户" min-width="140" />
<el-table-column label="类型" min-width="150">
<template #default="{ row }">
{{ historyPanelTypeLabel((row as HistoryRecordItem).panelType) }}
@@ -191,7 +191,7 @@ onMounted(() => {
<span v-else class="dim"></span>
</template>
</el-table-column>
<el-table-column label="操作" width="110" fixed="right">
<el-table-column label="操作" min-width="110" fixed="right">
<template #default="{ row }">
<el-button text type="primary" size="small" @click="openPreview(row as HistoryRecordItem)">查看大图</el-button>
</template>
@@ -110,7 +110,7 @@ onMounted(load)
<el-table-column prop="createdAt" label="创建时间" min-width="190">
<template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="90" fixed="right">
<el-table-column label="操作" min-width="90" fixed="right">
<template #default="{ row }">
<a v-if="(row as SoftwareVersionItem).fileUrl" class="el-link el-link--primary" :href="(row as SoftwareVersionItem).fileUrl" download>下载</a>
<span v-else class="dim"></span>
@@ -126,7 +126,7 @@ onMounted(load)
<el-card shadow="never">
<el-table v-loading="loading" :data="rows" stripe border>
<el-table-column type="index" label="序号" width="80" :index="(i: number) => (page - 1) * pageSize + i + 1" />
<el-table-column type="index" label="序号" min-width="80" :index="(i: number) => (page - 1) * pageSize + i + 1" />
<el-table-column prop="remarkName" label="备注名" min-width="140">
<template #default="{ row }">{{ (row as ShopKeyItem).remarkName || '—' }}</template>
</el-table-column>
@@ -138,7 +138,7 @@ onMounted(load)
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="白名单状态" width="150" align="center">
<el-table-column label="白名单状态" min-width="150" align="center">
<template #default="{ row }">
<el-tooltip :content="whitelistTooltip(row as ShopKeyItem)" placement="top">
<el-tag :type="whitelistStatusMeta((row as ShopKeyItem).ipWhitelistStatus).tag" size="small">
@@ -153,7 +153,7 @@ onMounted(load)
<el-table-column prop="updatedAt" label="修改时间" min-width="170">
<template #default="{ row }">{{ formatDateTime(row.updatedAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right">
<el-table-column label="操作" min-width="160" fixed="right">
<template #default="{ row }">
<el-button text type="primary" size="small" @click="openEdit(row as ShopKeyItem)">编辑</el-button>
<el-button text type="danger" size="small" @click="remove(row as ShopKeyItem)">删除</el-button>
@@ -220,7 +220,7 @@ onMounted(() => {
<el-card shadow="never">
<el-table v-loading="loading" :data="rows" stripe border>
<el-table-column type="index" label="序号" width="80" :index="(i: number) => (page - 1) * pageSize + i + 1" />
<el-table-column type="index" label="序号" min-width="80" :index="(i: number) => (page - 1) * pageSize + i + 1" />
<el-table-column prop="groupName" label="分组" min-width="120" />
<el-table-column prop="shopName" label="店铺名" min-width="160" />
<el-table-column prop="mallName" label="店铺商城名" min-width="120" />
@@ -228,7 +228,7 @@ onMounted(() => {
<template #default="{ row }">{{ (row as ShopSummary).znUsername || '—' }}</template>
</el-table-column>
<el-table-column prop="account" label="账号" min-width="140" />
<el-table-column label="密码" width="170">
<el-table-column label="密码" min-width="170">
<template #default="{ row }">
<span class="pwd-cell">{{ shownPassword(row as ShopSummary) }}</span>
<el-button
@@ -248,7 +248,7 @@ onMounted(() => {
<el-table-column prop="updatedAt" label="修改时间" min-width="170">
<template #default="{ row }">{{ formatDateTime(row.updatedAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="220" fixed="right">
<el-table-column label="操作" min-width="220" fixed="right">
<template #default="{ row }">
<el-button text type="primary" size="small" @click="openEdit(row as ShopSummary)">编辑</el-button>
<el-button text type="warning" size="small" @click="showCredential(row as ShopSummary)">凭证</el-button>
@@ -356,9 +356,9 @@ onMounted(load)
<el-dialog v-model="permissionVisible" title="视频任务数据范围授权" width="620px">
<p class="dim">勾选允许查看/操作视频任务的用户(仅数据范围)。当前已授权 {{ permissionGrantedCount }} 人。</p>
<el-table :data="permissionItems" border size="small" max-height="420">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="id" label="ID" min-width="80" />
<el-table-column prop="username" label="用户名" min-width="180" />
<el-table-column label="授权" width="90" align="center">
<el-table-column label="授权" min-width="90" align="center">
<template #default="{ row }">
<el-checkbox :model-value="(row as TaskPermissionItem).granted" @change="toggleGrant(row as TaskPermissionItem)" />
</template>
@@ -361,9 +361,9 @@ onMounted(load)
<el-dialog v-model="permissionVisible" title="店铺数据任务数据范围授权" width="620px">
<p class="dim">勾选允许查看/操作店铺数据任务的用户(仅数据范围)。当前已授权 {{ grantedCount }} 人。</p>
<el-table :data="permissionItems" border size="small" max-height="420">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="id" label="ID" min-width="80" />
<el-table-column prop="username" label="用户名" min-width="180" />
<el-table-column label="授权" width="90" align="center">
<el-table-column label="授权" min-width="90" align="center">
<template #default="{ row }">
<el-checkbox :model-value="(row as TaskPermissionItem).granted" @change="toggleGrant(row as TaskPermissionItem)" />
</template>
@@ -0,0 +1,59 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { readSource } from './helpers.ts'
// 验收反馈:所有列表的宽度列自适应。
// 不变式:列表页 el-table-column 一律用 min-width(弹性分配,随容器自适应),不得残留固定 width。
// 例外:DuplicateCheckPage 不动;dialog/表单的 width 不属于表格列。
const PAGES = [
'src/pages/account/GroupsPage.vue',
'src/pages/account/MenusPage.vue',
'src/pages/account/UsersPage.vue',
'src/pages/asin/AsinInvalidPage.vue',
'src/pages/asin/DedupeRegistryPage.vue',
'src/pages/asin/ProductCategoryPage.vue',
'src/pages/asin/QueryAsinPage.vue',
'src/pages/asin/SkipPricePage.vue',
'src/pages/records/RecordsDigitalHumanVersionPage.vue',
'src/pages/records/RecordsHistoryPage.vue',
'src/pages/records/RecordsSoftwareVersionPage.vue',
'src/pages/shop/ShopKeysPage.vue',
'src/pages/shop/ShopManagePage.vue',
'src/pages/tasks/ImageVideoTasksPage.vue',
'src/pages/tasks/ShopDataTasksPage.vue',
]
function columnWidthLines(src: string): string[] {
// 只检查 el-table-column 标签行内出现的固定 width="数字"。
return src
.split('\n')
.filter((line) => /<el-table-column[^>]*\swidth="\d+"/.test(line))
}
test('align_column_width_normal_primary_path', () => {
for (const page of PAGES) {
const bad = columnWidthLines(readSource(page))
assert.equal(bad.length, 0, `${page} 表格列不得残留固定 width(应改 min-width: ${bad.join(' | ')}`)
}
})
test('align_column_width_normal_variant_input', () => {
// 自适应不意味着无下限:至少保留一处 min-width 以保证内容可读。
for (const page of PAGES) {
const src = readSource(page)
assert.match(src, /el-table-column[^>]*min-width|el-table-column/, `${page} 应有表格列定义`)
}
})
test('align_column_width_boundary_empty_input', () => {
// 例外页 DuplicateCheckPage 保持自身实现不动。
const dup = readSource('src/pages/tasks/DuplicateCheckPage.vue')
assert.match(dup, /el-table-column/, 'DuplicateCheckPage 表格仍在且未被修改')
})
test('align_column_width_dependency_failure_returns_actionable_message', () => {
// dialog 宽度(如 width="620px")与表单宽度不受影响——只约束 el-table-column 行。
const shop = readSource('src/pages/tasks/ShopDataTasksPage.vue')
assert.match(shop, /el-dialog[^>]*width="\d+px"/, 'el-dialog 自身宽度保留')
})