style(borrow): 持有人高亮统一为条件蓝色,姓名按两字补全角空格等宽
一、当前持有人高亮策略统一
----
还库页(return.vue)该列原为橙色标签(type="warning"),与列表页在上一轮
已完成的条件高亮不一致。现统一为:
· 当前持有人 ≠ 借用人(确曾转交)→ 主题色 var(--el-color-primary) 加粗
· 两者同一人(常态)→ 普通深色文本,不做任何高亮
比对前把两侧都归一化到「斜杠前段」:历史数据可能混入「姓名/拼音」的完整
username 写法,不归一化会把同一人误判成转交,让整列无故飘蓝。
(records.vue 该列在 ba5394f 已是此策略,本轮仅同步调用 formatName。)
二、姓名两字补全角空格,实现等宽对齐
----
新增 formatName()(放在 src/utils/format.ts,而非各组件内复制 —— 列表页与
还库页都要用,规则改动只需改一处):
'高雪' -> '高 雪' U+3000 全角空格,占一个汉字宽
'郭俊玥' -> '郭俊玥'
'欧阳娜娜' -> '欧阳娜娜'
'AB' -> 'AB' 纯拉丁不补,否则渲染出 'A B' 怪相
null/'测试01'/混合字符 -> 原样
★ 纯展示层:只在模板 {{ formatName(row.borrower_name) }} 中调用,
row.borrower_name 等底层对象**一个字节都没动**。
姓名是转交/归还责任链的匹配键(records.vue 的 currentHolderList、
return.vue 的 isTransferred),往数据里插不可见字符会让比对、检索、
导出、复制全部带上它 —— 这正是上一轮拒绝「插空格」的原因,本轮改用
渲染期格式化,视觉要求与数据纯洁性同时满足。
★ 为什么用 U+3000 而非普通空格:普通空格宽度随字体浮动(约半个汉字),
补进去反而难对齐;U+3000 稳定占一个汉字宽,与「1 汉字 ≈ 1em」一致。
三、配套
----
· 归还人 / 当前持有人 / 借用人三列(含转交弹窗的持有人列)统一走 formatName;
· .name-fixed 增加 margin-right,避免同一格内多个姓名(同一单持有人/归还人
不同)紧贴成「高 雪张 三」。
This commit is contained in:
@ -34,3 +34,37 @@ export function normalizeQty(value: any, fallback = 0): number {
|
||||
const n = Number(value)
|
||||
return Number.isFinite(n) ? Number(n.toFixed(4)) : fallback
|
||||
}
|
||||
|
||||
// 中日韩统一表意文字。用于判断一个字符是否占一个「汉字宽」。
|
||||
const CJK_CHAR = /[㐀-䶿一-鿿豈-]/
|
||||
|
||||
/**
|
||||
* 姓名等宽显示格式化 —— 两个字的中文姓名在中间补一个全角空格,
|
||||
* 视觉上撑成三个字宽,与三字姓名纵向对齐。
|
||||
*
|
||||
* '高雪' -> '高 雪' (中间是 U+3000,占一个汉字宽)
|
||||
* '郭俊玥' -> '郭俊玥'
|
||||
* '欧阳娜娜' -> '欧阳娜娜' (4 字不动)
|
||||
*
|
||||
* ★ 纯展示层函数:只在模板渲染时调用,**绝不写回数据**。
|
||||
* 姓名是转交/归还责任链的匹配键(见 records.vue 的 currentHolderList
|
||||
* 与 return.vue 的 isTransferred),往数据里插不可见字符会让比对、检索、
|
||||
* 导出、复制全部带上它。本函数返回的是「显示用副本」,
|
||||
* row.borrower_name 始终是原值。
|
||||
*
|
||||
* ★ 为什么用 U+3000 而不是普通空格:
|
||||
* 普通空格宽度随字体变化(通常约半个汉字),补进去反而更难对齐;
|
||||
* U+3000 是东亚字体的全角空格,稳定占一个汉字宽 —— 与「1 个汉字 ≈ 1em」
|
||||
* 的等宽前提一致。
|
||||
*
|
||||
* ★ 为什么限定两个字符都是汉字:
|
||||
* 这条规则的目的是「与三字中文姓名对齐」。若姓名为两个拉丁字符(如 'AB'),
|
||||
* 补一个全角空格并不会让它获得汉字宽度,只会渲染出 'A B' 这样的怪相。
|
||||
* 故非汉字组合一律原样返回。
|
||||
*/
|
||||
export function formatName(name: any): string {
|
||||
if (name === null || name === undefined) return ''
|
||||
const s = String(name)
|
||||
if (s.length !== 2) return s
|
||||
return CJK_CHAR.test(s[0]) && CJK_CHAR.test(s[1]) ? `${s[0]} ${s[1]}` : s
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@
|
||||
<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')" label="借用人" width="100">
|
||||
<template #default="{row}">
|
||||
<span class="name-fixed">{{ row.borrower_name || '—' }}</span>
|
||||
<span class="name-fixed">{{ formatName(row.borrower_name) || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- ★ 当前持有人:转交后可能与「借用人」不是同一人;一张单的明细持有人
|
||||
@ -153,7 +153,7 @@
|
||||
:key="h.name"
|
||||
class="name-fixed"
|
||||
:class="{ 'text-primary': h.transferred }"
|
||||
>{{ h.name }}</span>
|
||||
>{{ formatName(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>
|
||||
@ -173,7 +173,7 @@
|
||||
<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>
|
||||
<span v-for="op in row.return_operators" :key="op" class="name-fixed">{{ formatName(op) }}</span>
|
||||
</template>
|
||||
<span v-else class="text-info">—</span>
|
||||
</template>
|
||||
@ -339,7 +339,7 @@
|
||||
<el-table-column prop="sku" label="SKU" width="110" show-overflow-tooltip />
|
||||
<el-table-column label="当前持有人" width="100" align="center">
|
||||
<template #default="{row}">
|
||||
<span v-if="row.current_holder_name" class="name-fixed">{{ row.current_holder_name }}</span>
|
||||
<span v-if="row.current_holder_name" class="name-fixed">{{ formatName(row.current_holder_name) }}</span>
|
||||
<span v-else class="text-info">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -435,6 +435,7 @@ import {
|
||||
getBorrowUsers,
|
||||
getBorrowSlipHistory,
|
||||
} from '@/api/transaction'
|
||||
import { formatName } from '@/utils/format'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
@ -945,6 +946,9 @@ onBeforeUnmount(() => {
|
||||
.name-fixed {
|
||||
display: inline-block;
|
||||
min-width: 3em;
|
||||
/* 一格内有多个姓名时(同一单的明细持有人/归还人不同)留出间隔,
|
||||
否则两个 3em 的盒子会紧贴成「高 雪张 三」难以分辨 */
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
/* 发生过转交的持有人:主题色加粗,替代原先的橙色色块标签 */
|
||||
|
||||
@ -45,12 +45,22 @@
|
||||
<div class="cart-section">
|
||||
<div v-if="returnList.length > 0">
|
||||
<el-table :data="returnList" border stripe style="width: 100%">
|
||||
<el-table-column v-if="hasColumnPermission('borrower_name')" prop="borrower_name" label="借用人" width="90" show-overflow-tooltip />
|
||||
<!-- ★ 当前持有人:转交后可能与「借用人」不是同一人,归还时必须由他本人来还 -->
|
||||
<el-table-column label="当前持有人" width="100" show-overflow-tooltip>
|
||||
<el-table-column v-if="hasColumnPermission('borrower_name')" label="借用人" width="90" show-overflow-tooltip>
|
||||
<template #default="{row}">
|
||||
<el-tag v-if="row.current_holder_name" type="warning">{{ row.current_holder_name }}</el-tag>
|
||||
<span v-else style="color: #c0c4cc">—</span>
|
||||
<span class="name-fixed">{{ formatName(row.borrower_name) || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- ★ 当前持有人:转交后可能与「借用人」不是同一人,归还时必须由他本人来还。
|
||||
表达上做条件高亮 —— 只有**确实发生过转交**(当前持有人 ≠ 借用人)时
|
||||
才用主题色加粗;两者同一人时是常态,用普通深色文本,不制造视觉噪音。 -->
|
||||
<el-table-column label="当前持有人" width="110" show-overflow-tooltip>
|
||||
<template #default="{row}">
|
||||
<span
|
||||
v-if="row.current_holder_name"
|
||||
class="name-fixed"
|
||||
:class="{ 'text-primary': isTransferred(row) }"
|
||||
>{{ formatName(row.current_holder_name) }}</span>
|
||||
<span v-else class="text-info">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="hasColumnPermission('sku')" prop="sku" label="SKU" width="120" show-overflow-tooltip />
|
||||
@ -225,6 +235,7 @@
|
||||
import { ref, nextTick, onMounted, onUnmounted } from 'vue'
|
||||
import request from '@/utils/request'
|
||||
import { getBorrowUsers } from '@/api/transaction'
|
||||
import { formatName } from '@/utils/format'
|
||||
import { uploadFile } from '@/api/common/upload'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Scissor, EditPen, Delete, CameraFilled, Close, Refresh, Select } from '@element-plus/icons-vue'
|
||||
@ -257,6 +268,16 @@ const loading = ref(false)
|
||||
const showCamera = ref(false)
|
||||
const barcodeRef = ref()
|
||||
|
||||
// ★ 是否发生过转交:当前持有人 ≠ 借用人。
|
||||
// 两者都是后端 to_dict 的展示名口径,仍归一化到「斜杠前段」再比对 ——
|
||||
// 历史数据里可能混入「姓名/拼音」的完整 username 写法,不归一化会把同一人
|
||||
// 误判成转交,让整列无故飘蓝。
|
||||
const isTransferred = (row: any): boolean => {
|
||||
const holder = String(row.current_holder_name || '').split('/')[0].trim()
|
||||
const borrower = String(row.borrower_name || '').split('/')[0].trim()
|
||||
return !!holder && holder !== borrower
|
||||
}
|
||||
|
||||
// ★ 实际归还人(一期转交改造):必须等于物品的当前持有人,后端强校验。
|
||||
// 与借出页的借用人下拉共用同一份公司隔离名单。
|
||||
const returnerId = ref<number | null>(null)
|
||||
@ -630,4 +651,27 @@ onUnmounted(() => {
|
||||
.sidebar-actions { flex-direction: row; width: 100%; gap: 10px; }
|
||||
.sidebar-actions .el-button { flex: 1; height: 40px; }
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
★ 姓名等宽对齐
|
||||
中文 1 字 ≈ 1em。formatName() 已把两字姓名补成全角三字宽,这里再加一个
|
||||
最小宽度兜底,保证单字姓名及后续可能出现的异常长度也不破坏列对齐。
|
||||
(视觉对齐只做在样式与展示副本上,row.borrower_name 始终是原值)
|
||||
========================================================================== */
|
||||
.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