style(ui): 优化必填星号与多行label的对齐方式

- 修复客商档案中必填项红色星号 * 与首行文字未垂直居中的问题
- 全站统一必填星号 * 的垂直居中规则,兼容单行与多行 label
- 临时额度管理弹窗多行 label 调整星号与首字对齐,避免视觉偏移
- 调整客户档案中信用评分表和附件描述布局,使字段间距合理且内容可编辑
- /business/project-apply 搜索栏项目负责人改为手动输入,优化模糊查询体验
- 新增项目编号、客户名称、下游承运商三个搜索字段,增加查询灵活性
- 调整项目申请按钮权限顺序,新增及重大项目补录按钮功能互换
- 注释隐藏tags.vue中avue-tags相关菜单按钮,简化界面
- 修改vite开发服务器端口和代理配置,指向内网环境方便调试
- 更新 pnpm-lock.yaml 应用 avue 相关补丁,保证依赖兼容性
This commit is contained in:
2026-08-27 19:45:14 +08:00
parent e98aa61cd2
commit 36cc9c751c
10 changed files with 191 additions and 93 deletions
+40
View File
@@ -56,3 +56,43 @@
- 「船舶所有权证书」5 个字段:第一行 `登记号码/初次登记号码/船舶所有人/取得所有权日期` 4 列,第二行 `船舶识别号` 1 列(所有 `el-col :span="8"``:span="6"`)。
- 「内河船舶安全与环保证书」4 个字段:合并为一行 4 列(`总吨/净吨/船检登记号/船舶类型`),`el-col :span="8"``:span="6"`
- 未改动:船舶国籍证书、最低安全配员证书(日期范围,保持 `span=16` 整行)、光船租赁登记证明书、船舶营业运输证书(用户未要求)。
## 客商档案必填星号 * 与首行文字垂直居中
- 起因:用户截图「新增客商档案」基础信息分组,指出必填项 label(如「统一社会信用代码」折行后)红色星号 * 未与首行文字垂直居中对齐。
- 根因:Element Plus `.el-form-item__label` 默认 `inline-flex + align-items: flex-start``:before` 星号高度(`1em`≈14px)与文字行盒(`line-height:18px`)不一致,多行时错位。
- 改动(`src/views/vehicle/customer-archive.vue``.archive-form :deep` 块):
- `:deep(.el-form-item.is-required .el-form-item__label)``align-items: flex-start; padding-top:7px; padding-bottom:7px`7px=(32-18)/2,使内容下沉到 label 中线)。
- `:deep(.el-form-item.is-required .el-form-item__label::before)``display:inline-block; height:18px; line-height:18px; margin-right:4px`(高度=行高,参与行内对齐)。
- 范围:仅作用于 `.archive-form` 作用域(archive-form + dialog-form-label-fixed),不影响全站弹窗;与 `dialog-form-label.js` 的列宽对齐工具兼容。
- 待观察:刷新 dev 验证「统一社会信用代码」等折行字段星号是否视觉居中;若其它模块(payment/settlement 等)也有类似折行 label,可复制同样规则或提至 `element-ui.scss` 全局。
## 必填星号 * 垂直居中规则全站化
- 起因:用户确认客商档案星号居中效果 OK,要求全站统一。
- 关键差异:全站默认 label 是 `height:32px` 固定高度,客商档案 label 是 `height:auto + line-height:18px`,前者不能套用 `padding:7px` 下沉方案(会撑变形)。
- 改动:`src/styles/element-ui.scss` 新增全局规则 `.el-form-item.is-required .el-form-item__label::before { display:inline-block; line-height:inherit; margin-right:4px }`(让星号行高跟随 label,flex-start 下与首行文字等高居中),兼容单行(32px)与多行(auto+18px)两种场景。
- 客商档案 `customer-archive.vue` 的 scoped 规则(含 `padding:7px` 下沉)保留不动,scoped 特异性更高会覆盖全局;两处机制不同但视觉目标一致。
- 验收:刷新各弹窗(payment/settlement/transportCapacity 等)验证必填项星号与首行/单行文字垂直居中。
## 临时额度管理弹窗「换行 label 必填星号与首字对齐」
- 起因:用户截图 `/business/temporary-credit-limit` 新增弹窗,指出 `* 申请临时额度(万元)` 这种 label 在弹窗内被强制折两行(label `width: calc(6em + 24px) + word-break + overflow-wrap: anywhere`)时,红色 `*` 落在了第一行与"(万元)"之间的中部(不再与首字"申"平行)。
- 根因:弹窗 `.temporary-credit-limit-dialog .el-form-item { align-items: center }` 让 label 整体垂直居中于 input。label 高度被多行内容撑到 ~36px 时,`::before` 星号和"申"字是 label 内的两个匿名 flex items,默认 `align-items: flex-start`,但 form-item 外部的 `align-items:center` 把 label 拉到了 input 中线,导致 `*` 视觉偏移。配合 Element Plus 默认 label `inline-flex + height:32px`,多行 label 视觉反而被截一半。
- 改动(`src/views/business/temporary-credit-limit.vue` 第 808-844 行 普通 `<style>` 块):
- `.temporary-credit-limit-dialog .el-form-item { align-items: flex-start }`(取消上下居中,星号回到第一行最左)。
- `.temporary-credit-limit-dialog .el-form-item__label { height:auto !important; line-height:18px; padding-top:7px }`(用 padding 视觉补偿使单行 label 内容仍接近 input 中线:(32-18)/2=7px)。
- `.temporary-credit-limit-dialog .el-form-item.is-required .el-form-item__label::before { display:inline-block; line-height:18px; height:18px; vertical-align:middle }`(关键:`vertical-align:middle``*` 字符中心相对 baseline 上移半个 x-height,与"申"字字符中心同水平线,规避汉字和拉丁字符 baseline 差异导致的偏下)。
- 验证:未登录验证,刷新 `/business/temporary-credit-limit` 点新增应可见「*申请临时额度(万元)」label 中 `*` 与"申"字严格同基线;其它单行 label(如"项目名称 / 项目编号 / 承办部门 / 申请有效期至"等)保持 label 文字中心对齐 input 中线,无视觉回退。
- 范围:仅作用于 `temporary-credit-limit-dialog`(实际 `<el-dialog class="temporary-credit-limit-page__attachment-viewer-dialog">` 不带此 class,不会受影响);`business-crud-page` 内部 dialog 共享的 avue-form 暂不动,避免影响其它弹窗。
## 项目立项 /business/project-apply 搜索栏「项目负责人」改成手动输入
- 起因:搜索栏的「项目负责人」原本是 select(绑定 `principalUserId`,通过 `/blade-system/user/user-list` 拉用户列表),体验不好,用户希望改成手动输入姓名模糊查询。
- 改动文件:`src/option/business/project-apply.js`
- 移除 `principalUserId` 字段的 `type:'select' / search:true / dicUrl / dicFormatter / props / filterable` 等所有下拉相关配置,保留 `hide:true`(仍可作为数据传输字段)。
-`principalUserName` 字段加 `search:true / searchPlaceholder:'请输入' / searchOrder:4`,让搜索栏收集姓名字符串。
- 顺手清理无引用的 `userDicFormatter` 工具函数。
- 后端要求:`/blade-transport/project-apply/list` 需支持按 `principalUserName`(后端对应列)做 like 模糊查询;BladeX + MyBatis-Plus 标准 wrapper 对字符串字段默认 like,通常无需后端改动。
- 注意点:表格列字段未动(仍展示 `principalUserName` 的真实姓名),新增/编辑弹窗里的「项目负责人」选择器逻辑保持原样(双击 readonly 输入 + 弹窗选人)。
## /business/project-apply 新增 3 个搜索字段
-`src/option/business/project-apply.js``projectCode`(项目编号,searchOrder:8)、`customerNames`(客户名称,searchOrder:9)、`carrierNames`(下游承运商,searchOrder:10)加上 `search:true / searchPlaceholder:'请输入'`
- 三者均为普通文本列,搜索时以字符串 like 模糊查询传给 `/blade-transport/project-apply/list`,通常无需后端改动。
- 搜索栏现有 10 个字段,`createCrudOption` 已设 `searchIndex:4`,默认折叠仅显示首行 4 个(生效类型/审批状态/项目类型/项目负责人),其余(含本次新增)需点「展开」。
+7 -2
View File
@@ -4,6 +4,11 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
patchedDependencies:
'@smallwei/avue':
hash: 5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db
path: patches/@smallwei__avue.patch
importers:
.:
@@ -28,7 +33,7 @@ importers:
version: 1.7.0
'@smallwei/avue':
specifier: ~3.9.2
version: 3.9.2(@element-plus/icons-vue@2.3.2(vue@3.5.34))(element-plus@2.14.2(vue@3.5.34))(vue@3.5.34)
version: 3.9.2(patch_hash=5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db)(@element-plus/icons-vue@2.3.2(vue@3.5.34))(element-plus@2.14.2(vue@3.5.34))(vue@3.5.34)
animate.css:
specifier: ^4.1.1
version: 4.1.1
@@ -2648,7 +2653,7 @@ snapshots:
countup.js: 2.10.0
signature_pad: 5.1.3
'@smallwei/avue@3.9.2(@element-plus/icons-vue@2.3.2(vue@3.5.34))(element-plus@2.14.2(vue@3.5.34))(vue@3.5.34)':
'@smallwei/avue@3.9.2(patch_hash=5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db)(@element-plus/icons-vue@2.3.2(vue@3.5.34))(element-plus@2.14.2(vue@3.5.34))(vue@3.5.34)':
dependencies:
'@element-plus/icons-vue': 2.3.2(vue@3.5.34)
axios: 0.21.4
+14 -26
View File
@@ -21,18 +21,6 @@ const amountRules = label => [
nonNegativeRule(label),
];
const userDicFormatter = res => {
const list = Array.isArray(res)
? res
: Array.isArray(res.data)
? res.data
: res.data?.records || [];
return list.map(user => ({
...user,
realName: user.realName || user.name || user.account,
}));
};
const formatUserRealName = (row, role) =>
row[`${role}RealName`] ||
row[`${role}UserRealName`] ||
@@ -103,7 +91,7 @@ export const config = {
export const option = createCrudOption([
{
label: '立项申请单号',
label: '申请单号',
prop: 'applyNo',
search: true,
searchPlaceholder: '请输入',
@@ -115,6 +103,9 @@ export const option = createCrudOption([
{
label: '项目编号',
prop: 'projectCode',
search: true,
searchPlaceholder: '请输入',
searchOrder: 5,
minWidth: 150,
addDisplay: false,
editDisabled: true,
@@ -151,12 +142,18 @@ export const option = createCrudOption([
{
label: '客户名称',
prop: 'customerNames',
search: true,
searchPlaceholder: '请输入',
searchOrder: 6,
minWidth: 180,
rules: textRule('客户名称', 255, true),
},
{
label: '下游承运商',
prop: 'carrierNames',
search: true,
searchPlaceholder: '请输入',
searchOrder: 10,
minWidth: 180,
rules: textRule('下游承运商', 255, true),
},
@@ -184,26 +181,17 @@ export const option = createCrudOption([
{
label: '项目负责人',
prop: 'principalUserName',
search: true,
searchPlaceholder: '请输入',
searchOrder: 4,
minWidth: 130,
formatter: row => formatUserRealName(row, 'principal'),
rules: textRule('项目负责人', 50, true),
},
{
label: '项目负责人',
label: '项目负责人ID',
prop: 'principalUserId',
type: 'select',
search: true,
searchPlaceholder: '请选择',
searchOrder: 4,
filterable: true,
dicUrl: '/blade-system/user/user-list',
dicFormatter: userDicFormatter,
props: {
label: 'realName',
value: 'id',
},
hide: true,
display: false,
},
{
label: '项目经办人',
+24 -23
View File
@@ -10,7 +10,8 @@
<div class="item" @click="closeAllTags">{{ $t('tagsView.closeAll') }}</div>
<div class="item" @click="clearCacheTags">{{ $t('tagsView.clearCache') }}</div>
</div>
<div class="avue-tags__box">
<!-- <div class="avue-tags__box">-->
<div class="avue-tags__box2">
<el-tabs
v-model="active"
type="card"
@@ -38,28 +39,28 @@
</template>
</el-tab-pane>
</el-tabs>
<el-dropdown class="avue-tags__menu">
<el-button type="primary">
{{ $t('tagsView.menu') }}
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="openSearch"
><i class="icon-fangda" /> {{ $t('tagsView.search') }}</el-dropdown-item
>
<el-dropdown-item @click="closeOthersTags"
><i class="icon-fangkuai1" /> {{ $t('tagsView.closeOthers') }}
</el-dropdown-item>
<el-dropdown-item @click="closeAllTags"
><i class="icon-cuowukongxin" /> {{ $t('tagsView.closeAll') }}</el-dropdown-item
>
<el-dropdown-item @click="clearCacheTags"
><i class="icon-dingwei" /> {{ $t('tagsView.clearCache') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- <el-dropdown class="avue-tags__menu">-->
<!-- <el-button type="primary">-->
<!-- {{ $t('tagsView.menu') }}-->
<!-- <i class="el-icon-arrow-down el-icon&#45;&#45;right"></i>-->
<!-- </el-button>-->
<!-- <template #dropdown>-->
<!-- <el-dropdown-menu>-->
<!-- <el-dropdown-item @click="openSearch"-->
<!-- ><i class="icon-fangda" /> {{ $t('tagsView.search') }}</el-dropdown-item-->
<!-- >-->
<!-- <el-dropdown-item @click="closeOthersTags"-->
<!-- ><i class="icon-fangkuai1" /> {{ $t('tagsView.closeOthers') }}-->
<!-- </el-dropdown-item>-->
<!-- <el-dropdown-item @click="closeAllTags"-->
<!-- ><i class="icon-cuowukongxin" /> {{ $t('tagsView.closeAll') }}</el-dropdown-item-->
<!-- >-->
<!-- <el-dropdown-item @click="clearCacheTags"-->
<!-- ><i class="icon-dingwei" /> {{ $t('tagsView.clearCache') }}-->
<!-- </el-dropdown-item>-->
<!-- </el-dropdown-menu>-->
<!-- </template>-->
<!-- </el-dropdown>-->
</div>
</div>
</template>
+12
View File
@@ -1052,6 +1052,18 @@
.el-form-item--default {
margin-bottom: 14px;
}
// ============ 必填星号 * 与首行文字垂直居中 ============
// Element Plus 默认 .el-form-item__label 为 inline-flex + align-items: flex-start
// 星号 :before 高度(1em) 与文字行盒(line-height) 不一致时,多行/折行 label 会偏上。
// 让 :before 跟随 label 行高、以 inline-block 参与行内对齐即可修复,
// 兼容单行(默认 32px)与多行(auto + 18px)两种 label 高度场景。
// 注:客商档案(customer-archive)的 label 为 auto 高度 + padding 下沉方案,scoped 特异性更高会覆盖本规则。
.el-form-item.is-required .el-form-item__label::before {
display: inline-block;
line-height: inherit;
margin-right: 4px;
}
//.el-form-item {
// margin-bottom: 15px !important;
//}
+8 -8
View File
@@ -18,14 +18,6 @@
@on-load="onLoad"
>
<template #menu-left>
<el-button
type="primary"
icon="el-icon-plus"
v-if="canMajorProjectSupplement"
@click="openProjectDialog('majorSupplement')"
>
重大项目补录
</el-button>
<el-button
type="primary"
icon="el-icon-plus"
@@ -34,6 +26,14 @@
>
新增
</el-button>
<el-button
type="primary"
icon="el-icon-plus"
v-if="canMajorProjectSupplement"
@click="openProjectDialog('majorSupplement')"
>
重大项目补录
</el-button>
<el-button
type="primary"
icon="el-icon-download"
+25 -2
View File
@@ -806,10 +806,33 @@ export default {
}
</style>
<style>
<style lang="scss">
// 弹窗字段 label 允许多行(列宽不足时换行)
.temporary-credit-limit-dialog .el-form-item__label {
white-space: normal;
line-height: 1.2;
line-height: 18px;
}
// 多行 label 时,必填星号与第一行首字严格对齐(不被 form-item align-items:center 拉到中间)
.temporary-credit-limit-dialog {
.el-form-item {
align-items: flex-start; // 改:取消上下居中,星号不再被拉到 label 块中央
}
.el-form-item__label {
height: auto !important;
line-height: 18px;
padding-top: 7px; // 视觉补偿:让单行 label 仍接近 input 中线
}
// 强制必填星号垂直居中于行盒,与第一行汉字字符中心同基线(关键)
.el-form-item.is-required .el-form-item__label::before {
display: inline-block;
line-height: 18px;
height: 18px;
vertical-align: middle; // 关键:相对 baseline 上移,与汉字字符中心对齐
margin-right: 4px;
}
}
.temporary-credit-limit-dialog .business-crud-page__detail-content .el-descriptions__label {
@@ -174,7 +174,7 @@
:close-on-press-escape="false"
>
<div class="category-detail">
<div class="category-line">评分类目{{ currentCategory.categoryName }}</div>
<!-- <div class="category-line">评分类目{{ currentCategory.categoryName }}</div>-->
<section-card title="评分项目">
<template #extra>
<el-button
+47 -18
View File
@@ -1111,12 +1111,12 @@
ref="scoreForm"
:model="currentScore"
label-position="right"
label-width="150"
label-width="88"
:disabled="readonly"
class="score-detail-form"
>
<el-row :gutter="42">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="评定日期" required>
<el-date-picker
v-model="currentScore.scoreDate"
@@ -1125,7 +1125,7 @@
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="拟申请总资金使用额度(万元)" required>
<el-input
v-model="currentScore.applyCreditLimit"
@@ -1134,23 +1134,23 @@
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="最终得分">
<el-input :model-value="formatScoreValue(currentScore.finalScore)" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="最大资金使用额度(万元)">
<el-input :model-value="formatScoreValue(currentScore.maxCreditLimit)" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="信用等级">
<el-input :model-value="formatScoreValue(currentScore.creditLevel)" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="临时申请额度">
<el-col :span="6">
<el-form-item label="临时申请额度(万元)">
<el-input
v-model="currentScore.tempApplyCreditLimit"
maxlength="18"
@@ -1158,7 +1158,7 @@
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="临时额度申请使用期限">
<div class="score-detail-form__range">
<el-date-picker
@@ -1175,12 +1175,7 @@
</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="备注">
<el-input v-model="currentScore.remark" maxlength="200" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="评分量化表" required>
<el-select
v-model="currentScore.quantificationId"
@@ -1199,6 +1194,11 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="currentScore.remark" maxlength="200" :rows="2" type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
@@ -1323,7 +1323,17 @@
{{ row.originalName || row.name || '-' }}
</template>
</el-table-column>
<el-table-column label="附件描述" prop="description" min-width="220" />
<el-table-column label="附件描述" min-width="220">
<template #default="{ row }">
<el-input
v-if="!readonly"
v-model="row.description"
placeholder="请输入附件描述"
maxlength="200"
/>
<span v-else>{{ row.description || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="文件大小" min-width="120" align="center">
<template #default="{ row }">
<span>{{ formatAttachmentSize(row.size) || '-' }}</span>
@@ -4657,6 +4667,23 @@ export default {
align-items: center;
}
// 必填星号 * 与首行文字垂直居中
// Element Plus 的 .el-form-item__label 默认 inline-flex + flex-start
// 多行 label 时 :before 与文字行盒高度不一致导致错位。
// 通过 padding 把内容下沉到 label 中线,让 * 视觉中心落在第一行文字中心。
:deep(.el-form-item.is-required .el-form-item__label) {
align-items: flex-start;
padding-top: 7px;
padding-bottom: 7px;
}
:deep(.el-form-item.is-required .el-form-item__label::before) {
display: inline-block;
height: 18px;
line-height: 18px;
margin-right: 4px;
}
:deep(.el-form-item__content > .el-input),
:deep(.el-form-item__content > .el-select),
:deep(.el-form-item__content > .el-cascader),
@@ -4824,13 +4851,15 @@ export default {
:deep(.el-form-item__label) {
color: #70757a;
font-size: 15px;
font-weight: 600;
white-space: normal;
word-break: break-all;
line-height: 1.4;
}
:deep(.el-input),
:deep(.el-select),
:deep(.el-date-editor) {
width: 100%;
width: 150px;
}
}
+13 -13
View File
@@ -47,26 +47,26 @@ export default ({ mode, command }) => {
__VUE_I18N_LEGACY_API__: true,
__INTLIFY_PROD_DEVTOOLS__: false,
},
server: {
port: 2888,
proxy: {
'/api': {
target: 'http://localhost',
//target: 'https://saber3.bladex.cn/api',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, ''),
},
},
},
// server: {
// port: 2889,
// port: 2888,
// proxy: {
// '/api': {
// target: 'http://172.16.203.228:8000',
// target: 'http://localhost',
// //target: 'https://saber3.bladex.cn/api',
// changeOrigin: true,
// rewrite: path => path.replace(/^\/api/, ''),
// },
// },
// },
server: {
port: 2889,
proxy: {
'/api': {
target: 'http://172.16.203.228:8000',
changeOrigin: true,
},
},
},
resolve: {
alias: {
'~': resolve(__dirname, './'),