style(borrow): 借还记录页视觉降噪、移除前端假排序、主行改按整单汇总
一、视觉降噪
----
· 「当前持有人」由橙色标签改为纯文本:
未转交 → 普通文本;发生过转交(当前持有人 ≠ 借用人)→ 主题色加粗。
原先一律用色块,一屏几十个标签会把真正需要关注的「已转交」淹没。
· 「无限期」由蓝色标签改为灰色小字:它不是异常,不该和红色的「已逾期」抢
视觉重心。(沿用系统既有术语「无限期」,与借出页「无限期/长期借用」一致)
· 转交弹窗内的持有人列同步降噪,保持同一表达。
二、移除前端假排序
----
「借出时间」列的 sortable 是 Element Plus 的**前端排序**,只会重排当前页的
10 条,反而打乱后端按单号聚合的「逾期优先」排序(有限期在前、按最早应还
升序、无限期按最早借出升序)。已移除。若将来需要按其他字段排序,应改用
sortable="custom" 并由后端接管。
三、主行改按整单汇总
----
主行字段原样取「首条明细」(groupMap 首次遇到即定型),多明细单会失真。
现对四项做整单聚合:
· 状态 —— 全部报废→已报废;全部结清→已还;仍有未还且已还过一部分→
部分归还;全部未还→未还。
修复「首条已还清、其余未还 → 主行显示已还,却仍出现在未归还
页签」的口径矛盾(该页签由后端按整单聚合过滤)。
· 归还人 —— 去重汇总(原只显示首条明细的经手人)
· 归还时间 —— 取**最晚**(整单结清的那一刻;原显示最早那笔)
· 应还时间 —— 取**最早**(与后端排序键 min(expected_return_time) 对齐)
四、姓名定宽对齐
----
中文 1 字 ≈ 1em,故姓名统一占 3em(.name-fixed):「石利」与「郭俊玥」占同样
横向空间,一列名字纵向对齐。
★ 刻意用 CSS 而非往数据里插全角空格:插字符会污染真实数据(复制、导出、检索、
姓名比对全带着它),而转交/归还的责任链恰恰依赖姓名比对。
★ 如实说明:上述聚合里,状态/归还人/应还时间三项在当前数据上**尚无可见变化**
—— 实测单号内状态完全一致、归还人同为一人、应还时间因同批发货天然相同。
它们是防住「部分归还 + 多明细」组合的前瞻性修正。真正改变显示的只有「归还
时间取最晚」:BOR-20260616-0001(21 条明细、21 个不同归还时间)等 2 张单。
This commit is contained in:
@ -136,31 +136,48 @@
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column v-if="hasColumnPermission('borrow_no')" prop="borrow_no" label="单号" width="180" show-overflow-tooltip />
|
||||
<el-table-column v-if="hasColumnPermission('borrower_name')" prop="borrower_name" label="借用人" width="100" />
|
||||
<!-- ★ 当前持有人:转交后可能与「借用人」不是同一人。
|
||||
一张单的明细可能持有人各不相同,故去重后逐个展示。 -->
|
||||
<el-table-column label="当前持有人" width="120">
|
||||
<el-table-column v-if="hasColumnPermission('borrower_name')" label="借用人" width="100">
|
||||
<template #default="{row}">
|
||||
<template v-if="currentHolders(row).length">
|
||||
<el-tag
|
||||
v-for="h in currentHolders(row)"
|
||||
:key="h"
|
||||
type="warning"
|
||||
size="small"
|
||||
style="margin: 1px 2px"
|
||||
>{{ h }}</el-tag>
|
||||
</template>
|
||||
<span v-else-if="row.status === 'returned'" style="color: #67C23A; font-size: 12px">已回库</span>
|
||||
<span v-else style="color: #c0c4cc">—</span>
|
||||
<span class="name-fixed">{{ row.borrower_name || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="hasColumnPermission('borrow_time')" prop="borrow_time" label="借出时间" width="160" sortable />
|
||||
<!-- ★ 当前持有人:转交后可能与「借用人」不是同一人;一张单的明细持有人
|
||||
也可能各不相同,故去重后逐个展示。
|
||||
表达上做了降噪:未转交=普通文本,转过交=主题色加粗。原先一律用橙色
|
||||
标签,一屏几十个色块会把真正需要关注的「已转交」淹没。 -->
|
||||
<el-table-column label="当前持有人" width="130">
|
||||
<template #default="{row}">
|
||||
<template v-if="currentHolderList(row).length">
|
||||
<span
|
||||
v-for="h in currentHolderList(row)"
|
||||
:key="h.name"
|
||||
class="name-fixed"
|
||||
:class="{ 'text-primary': h.transferred }"
|
||||
>{{ h.name }}</span>
|
||||
</template>
|
||||
<span v-else-if="row.status === 'returned' || row.status === 'scrapped'" class="text-info">已回库</span>
|
||||
<span v-else class="text-info">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- ★ 去掉 sortable:这是 Element Plus 的**前端排序**,只会重排当前页的
|
||||
10 条,反而打乱后端按单号聚合的「逾期优先」排序,让列表看起来没规律。
|
||||
若将来确实需要按其他字段排序,应改为 sortable="custom" 并由后端接管。 -->
|
||||
<el-table-column v-if="hasColumnPermission('borrow_time')" prop="borrow_time" label="借出时间" width="160" />
|
||||
<el-table-column label="借出物品" width="90" align="center">
|
||||
<template #default="{row}">
|
||||
<el-tag type="info">{{ row.children ? row.children.length : 0 }} 项</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="hasColumnPermission('return_operator')" prop="return_operator" label="归还人" width="100" />
|
||||
<!-- ★ 归还人改为整单汇总:主行原取「首条明细」的归还人,多明细分批归还时
|
||||
会只显示其中一位,令人误以为其他人没还过。 -->
|
||||
<el-table-column v-if="hasColumnPermission('return_operator')" label="归还人" width="110">
|
||||
<template #default="{row}">
|
||||
<template v-if="row.return_operators && row.return_operators.length">
|
||||
<span v-for="op in row.return_operators" :key="op" class="name-fixed">{{ op }}</span>
|
||||
</template>
|
||||
<span v-else class="text-info">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column v-if="hasColumnPermission('expected_return_time') || hasColumnPermission('return_time')" label="归还时间 / 预计" min-width="200">
|
||||
<template #default="{row}">
|
||||
@ -168,8 +185,11 @@
|
||||
<el-tag type="success" size="small">实际</el-tag>
|
||||
{{ row.return_time || '-' }}
|
||||
</div>
|
||||
<!-- ★ 降噪:「无限期」不是异常,不该用蓝色标签抢视觉重心 ——
|
||||
把注意力留给红色的「已逾期」。改为灰色小字。(沿用系统既有术语
|
||||
「无限期」,与借出页的「无限期/长期借用」保持一致) -->
|
||||
<div v-else-if="!row.expected_return_time">
|
||||
<el-tag type="primary" size="small">无限期</el-tag>
|
||||
<span class="text-info">无限期</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span>预计 {{ formatExpectedTime(row.expected_return_time).text }} </span>
|
||||
@ -319,7 +339,8 @@
|
||||
<el-table-column prop="sku" label="SKU" width="110" show-overflow-tooltip />
|
||||
<el-table-column label="当前持有人" width="100" align="center">
|
||||
<template #default="{row}">
|
||||
<el-tag type="warning" size="small">{{ row.current_holder_name || '—' }}</el-tag>
|
||||
<span v-if="row.current_holder_name" class="name-fixed">{{ row.current_holder_name }}</span>
|
||||
<span v-else class="text-info">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="待还数量" width="90" align="center">
|
||||
@ -602,14 +623,59 @@ const confirmScrap = async () => {
|
||||
const isUnreturned = (row: any) =>
|
||||
row.status === 'borrowed' || row.status === 'partial_returned'
|
||||
|
||||
// 当前持有人:整单下各明细可能持有人不同(转交是逐条明细进行的),去重后展示
|
||||
const currentHolders = (row: any): string[] => {
|
||||
const names = (row.children || [])
|
||||
.map((c: any) => c.current_holder_name)
|
||||
.filter((n: any) => !!n)
|
||||
return Array.from(new Set(names)) as string[]
|
||||
// 当前持有人:整单下各明细可能持有人不同(转交是逐条明细进行的),去重后展示。
|
||||
// ★ 同时标出该持有人是否「因转交而来」(≠ 初始借用人)—— 转交过的用主题色加粗,
|
||||
// 未转交的保持普通文本,靠颜色而非色块承担区分度。
|
||||
const currentHolderList = (row: any): Array<{ name: string; transferred: boolean }> => {
|
||||
const borrower = String(row.borrower_name || '').split('/')[0].trim()
|
||||
const seen = new Map<string, boolean>()
|
||||
;(row.children || []).forEach((c: any) => {
|
||||
const name = c.current_holder_name
|
||||
if (!name) return
|
||||
const transferred = String(name).split('/')[0].trim() !== borrower
|
||||
// 同名只要有一次是「转交而来」就按转交样式展示
|
||||
seen.set(name, (seen.get(name) || false) || transferred)
|
||||
})
|
||||
return Array.from(seen, ([name, transferred]) => ({ name, transferred }))
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// ★ 整单汇总:主行字段原样取「首条明细」,多明细单会失真,此处按整单重算
|
||||
//
|
||||
// 失真场景:一张单有 2 条明细,第一条已还清、第二条未还 ——
|
||||
// · 主行状态显示「已还」,却仍出现在「未归还」页签里
|
||||
// (该页签由后端按整单聚合过滤:单号下至少一条未还);
|
||||
// · 归还人只显示第一条的经手人,另一位经手人被隐藏。
|
||||
// ==========================================================================
|
||||
const aggregateStatus = (children: any[]): string => {
|
||||
if (!children.length) return 'borrowed'
|
||||
// 全部报废
|
||||
if (children.every(c => c.status === 'scrapped')) return 'scrapped'
|
||||
// 全部结清(已还 + 已报废;报废在后端口径里同样计入 is_returned)
|
||||
const isClosed = (c: any) => c.status === 'returned' || c.status === 'scrapped'
|
||||
if (children.every(isClosed)) return 'returned'
|
||||
// 仍有未还:若已还过一部分,则呈现「部分归还」而非笼统的「未还」
|
||||
const returnedSome = children.some(
|
||||
(c: any) => c.status === 'returned' || c.status === 'partial_returned'
|
||||
)
|
||||
return returnedSome ? 'partial_returned' : 'borrowed'
|
||||
}
|
||||
|
||||
// 归还人汇总:去重(同一库管经手多条明细时只显示一次)
|
||||
const aggregateReturnOperators = (children: any[]): string[] => {
|
||||
const ops = new Set<string>()
|
||||
children.forEach((c: any) => {
|
||||
if (c.return_operator) ops.add(String(c.return_operator))
|
||||
})
|
||||
return Array.from(ops)
|
||||
}
|
||||
|
||||
// 最早 / 最晚时间取值:字符串为 'YYYY-MM-DD HH:mm' 格式,可直接比较
|
||||
const earliest = (arr: any[]): string | null =>
|
||||
arr.filter(Boolean).sort().shift() ?? null
|
||||
const latest = (arr: any[]): string | null =>
|
||||
arr.filter(Boolean).sort().pop() ?? null
|
||||
|
||||
const transferDialogVisible = ref(false)
|
||||
const transferSubmitting = ref(false)
|
||||
const transferBorrowNo = ref('')
|
||||
@ -753,13 +819,21 @@ const fetchData = async () => {
|
||||
groupMap.get(item.borrow_no).children.push(item)
|
||||
})
|
||||
|
||||
// ★ 整单状态重算:所有明细都已报废 → 整单标记 scrapped
|
||||
// ★ 整单汇总重算:主行字段默认取「首条明细」,多明细单在部分归还/分批归还时
|
||||
// 会失真,故对状态、归还人、时间字段按整单重算(详见 aggregateStatus 注释)
|
||||
list.value = Array.from(groupMap.values()).map(group => {
|
||||
const allScrapped = group.children.length > 0 &&
|
||||
group.children.every((c: any) => c.status === 'scrapped')
|
||||
if (allScrapped) {
|
||||
group.status = 'scrapped'
|
||||
}
|
||||
const children = group.children || []
|
||||
group.status = aggregateStatus(children)
|
||||
group.return_operators = aggregateReturnOperators(children)
|
||||
|
||||
// 预计归还时间取**最早**:后端排序用的正是单内 min(expected_return_time),
|
||||
// 主行若显示首条明细的值,多明细单的可见列就会与排序不一致 ——
|
||||
// 看上去「排得没规律」,实则列里显示的根本不是排序依据。
|
||||
group.expected_return_time = earliest(children.map((c: any) => c.expected_return_time))
|
||||
|
||||
// 归还时间取**最晚**:状态为「已还」时代表整单结清的那一刻
|
||||
group.return_time = latest(children.map((c: any) => c.return_time))
|
||||
|
||||
return group
|
||||
})
|
||||
|
||||
@ -857,4 +931,31 @@ onBeforeUnmount(() => {
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
★ 姓名定宽对齐
|
||||
中文姓名以「字」为单位占宽,1 个全角字 ≈ 1em,故统一占 3em:
|
||||
「石利」与「郭俊玥」占同样的横向空间,一列名字纵向对齐、不再参差。
|
||||
|
||||
★ 为什么用 CSS 而不是往数据里插空格(如「石 利」):
|
||||
插字符会污染真实数据 —— 复制出去、导出、检索、姓名比对全都带着那个空格,
|
||||
而转交/归还的责任链恰恰依赖姓名比对(见 currentHolderList 的归一化逻辑)。
|
||||
同一个视觉目标,用样式实现不伤数据。
|
||||
========================================================================== */
|
||||
.name-fixed {
|
||||
display: inline-block;
|
||||
min-width: 3em;
|
||||
}
|
||||
|
||||
/* 发生过转交的持有人:主题色加粗,替代原先的橙色色块标签 */
|
||||
.text-primary {
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 中性/次要信息:灰字,不抢视觉重心 */
|
||||
.text-info {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user