style(billing-plan-editor): 使用 SectionCard 包裹计费方案编辑区块
- 引入 SectionCard 组件包裹计费方案显示和编辑区域 - 为默认方案信息添加带提示的 Info 图标和 Tooltip - 优化模板缩进和结构,提升可读性 feat(contract-manage): 优化合同管理搜索和操作栏体验 - 允许签约类型字段作为搜索条件且支持排序和提示 - 将签约类型从快捷搜索区移动到标准搜索表单中 - 废除冗余的 contractSignType 状态,使用表单绑定自动处理 - 调整搜索栏快捷筛选区域顺序,实现与按钮组的视觉统一 fix(contract-manage): 修正底部操作栏文案及按钮显示逻辑 - 底部操作栏将“关闭”文案修改为“取消”,符合统一规范 - 草稿编辑页底部按钮增加[暂存/提交临时/提交正式],一致新增页操作 - 按钮样式调整,采用右侧对齐并使用按键间距12px替代gap style(contract-manage-change): 统一变更页底部操作栏样式和交互 - 底部操作栏使用固定悬浮布局,避免内容被遮挡 - 调整按钮顺序为[取消][提交],并修改“关闭”为“取消” - 增加统一的按钮间距与操作栏背景和阴影样式 - 为页面容器添加底部内边距,保障悬浮栏空间 style(contract-manage): 修复“合同文件”标题样式加粗问题 - 补充 scoped 样式,利用 :deep 影响子组件中标题的字体加粗和颜色 fix(customer-archive): 调整文本行高以优化显示效果 - 将内容文本的行高从18px调整为16px,提升视觉紧凑度 style(customer-archive): 调整必填项标签布局 - 删除因padding造成的标签视觉错位,确保label与控件垂直居中对齐
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
# 2026-08-28 工作日志
|
||||
|
||||
## contract-manage 表单底部操作栏间距/文案修复
|
||||
- 文件:`src/views/business/contract-manage.vue`
|
||||
- 问题:底部浮动操作栏 `.contract-manage-page__footer` 用 `gap:8px`,与全站统一 12px 标准不一致("间距不一致不统一")。
|
||||
- 修复:去掉 `gap:8px`,改用 `.el-button + .el-button { margin-left:12px }`,对齐全站"去 gap、12px"规范。
|
||||
- 文案:底部"关闭"→"取消"(底部操作栏统一命名规范;详情弹窗/流程图弹窗的"关闭"为只读/纯关闭场景,按规范保留)。
|
||||
|
||||
## contract-manage 草稿编辑页按钮缺失修复
|
||||
- 问题:编辑态(含草稿编辑)底部只有单个"提交"(submitEdit),缺"暂存/提交临时合同/提交正式合同",与新增页不一致。
|
||||
- 修复:新增 computed `isDraftEdit`(`formMode==='edit' && form.approvalStatus==='draft'`);footer 三按钮条件由 `formMode==='add'` 改为 `formMode==='add' || isDraftEdit`,草稿编辑复用 `submitCreate`(按 id upsert + temporary/formal 提升),非草稿编辑仍走 submitEdit 单个"提交"。
|
||||
|
||||
## contract-manage-change 变更页底部操作栏统一改造
|
||||
- 文件:`src/views/business/contract-manage-change.vue`
|
||||
- 问题:`.page-footer` 左侧对齐、`gap:16px`、不悬浮,且按钮顺序/命名(提交→关闭)与新增页规范相反。
|
||||
- 修复:
|
||||
- DOM:按钮重排为 `[取消][提交]`(次要居左、主操作居右),"关闭"→"取消"。
|
||||
- 样式:`.page-footer` 改为 `position:fixed` 悬浮吸底(left:230px / avue--collapse 60px / avue-layout--horizontal 0)、`justify-content:flex-end` 右对齐、去 gap 改用 `.el-button+.el-button{margin-left:12px}` 统一 12px、白底+上边框+上投影。
|
||||
- 容器 `.contract-change-page` 加 `padding-bottom:72px`,避免内容被悬浮栏遮挡。
|
||||
|
||||
## contract-manage 新增页"合同文件"标题未加粗修复
|
||||
- 文件:`src/views/business/contract-manage.vue`
|
||||
- 问题:`attachment-section` 子组件内渲染的 `.dialog-section-title` 不受当前页面 scoped `> .dialog-section-title` 样式命中,导致"合同文件"标题字重未加粗(截图反馈)。
|
||||
- 修复:在 `.contract-manage-form__section` 样式规则中补充 `:deep(.dialog-section-title)`,使直接子元素和子组件内部标题均统一为 16px/600/#303133。
|
||||
|
||||
## contract-manage 搜索栏"时间段快捷筛选"组位置前置
|
||||
- 文件:`src/views/business/contract-manage.vue` + `src/option/business/contract-manage.js`
|
||||
- 第一轮(理解偏差):把"签约类型"select 和 check-tag 组一起塞进 `#search-menu` slot。主人纠正:「签约类型」是普通搜索字段,应与第一行 8 个搜索项同排,不是 slot 内的辅助元素。
|
||||
- 第二轮(终态):
|
||||
- option 把 `signType` 加 `search:true / searchOrder:10 / searchPlaceholder:'请选择'`,作为 Avue 标准搜索字段,与其它 8 项同排(放在末尾)。
|
||||
- 页面 `#search-menu` slot 移除「签约类型」select,仅留 5 个 el-check-tag。
|
||||
- 页面 data 移除冗余的 `contractSignType:''`,onLoad/searchReset 不再手动维护 signType(Avue search form 自动绑定到 query)。
|
||||
- 样式:`.el-select { width: 180px }` 已成 dead code,移除。
|
||||
- `:deep(...)` 调整:去掉 `margin-right:auto`,只留 `order:-1`。`.avue-form__menu` 本身 `flex / flex-end`,check-tag 组排首项后,整体仍紧靠右侧,与 `[查询][重置][收起]` 形成 `[快捷筛选] [查询] [重置] [收起]` 右对齐序列。
|
||||
|
||||
@@ -251,6 +251,7 @@ export const option = createCrudOption([
|
||||
searchPlaceholder: '请输入',
|
||||
order: 180,
|
||||
slot: true,
|
||||
overHidden: true,
|
||||
minWidth: 240,
|
||||
rules: textRule('合同名称', 100, true),
|
||||
},
|
||||
@@ -318,7 +319,9 @@ export const option = createCrudOption([
|
||||
label: '签约类型',
|
||||
prop: 'signType',
|
||||
type: 'select',
|
||||
search: false,
|
||||
search: true,
|
||||
searchOrder: 10,
|
||||
searchPlaceholder: '请选择',
|
||||
span: 8,
|
||||
order: 190,
|
||||
dicData: signTypeOptions,
|
||||
|
||||
@@ -6,68 +6,72 @@
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<el-descriptions v-if="readonly" :column="3" class="billing-plan-detail">
|
||||
<el-descriptions-item label="方案名称">{{
|
||||
displayValue(draft.planName)
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="运输方式">{{
|
||||
transportModeLabel(draft.transportMode)
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="默认方案">{{
|
||||
isDefaultPlan(draft) ? '是' : '否'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{ displayValue(draft.remark) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-form
|
||||
v-else
|
||||
ref="formRef"
|
||||
:model="draft"
|
||||
:rules="formRules"
|
||||
label-position="right"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8"
|
||||
|
||||
<section-card>
|
||||
<el-descriptions v-if="readonly" :column="3" class="billing-plan-detail">
|
||||
<el-descriptions-item label="方案名称">{{
|
||||
displayValue(draft.planName)
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="运输方式">{{
|
||||
transportModeLabel(draft.transportMode)
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="默认方案">{{
|
||||
isDefaultPlan(draft) ? '是' : '否'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{ displayValue(draft.remark) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-form
|
||||
v-else
|
||||
ref="formRef"
|
||||
:model="draft"
|
||||
:rules="formRules"
|
||||
label-position="right"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8"
|
||||
><el-form-item label="方案名称" prop="planName"
|
||||
><el-input
|
||||
v-model="draft.planName"
|
||||
maxlength="100"
|
||||
:disabled="readonly" /></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="8"
|
||||
><el-input
|
||||
v-model="draft.planName"
|
||||
maxlength="100"
|
||||
:disabled="readonly" /></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="8"
|
||||
><el-form-item label="运输方式" prop="transportMode"
|
||||
><el-select
|
||||
v-model="draft.transportMode"
|
||||
clearable
|
||||
filterable
|
||||
:loading="transportModeLoading"
|
||||
placeholder="请选择运输方式"
|
||||
@visible-change="visible => visible && ensureTransportModes()"
|
||||
><el-option
|
||||
v-for="item in transportModeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value" /></el-select></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="8"
|
||||
><el-select
|
||||
v-model="draft.transportMode"
|
||||
clearable
|
||||
filterable
|
||||
:loading="transportModeLoading"
|
||||
placeholder="请选择运输方式"
|
||||
@visible-change="visible => visible && ensureTransportModes()"
|
||||
><el-option
|
||||
v-for="item in transportModeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value" /></el-select></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="8"
|
||||
><el-form-item label="默认方案"
|
||||
><el-checkbox v-model="draft.defaultPlan" :disabled="readonly">默认方案</el-checkbox
|
||||
><el-icon class="billing-plan-editor__default-tip" @click="showDefaultPlanTip"
|
||||
><InfoFilled
|
||||
/></el-icon>
|
||||
></el-form-item
|
||||
><el-checkbox v-model="draft.defaultPlan" :disabled="readonly">默认方案</el-checkbox
|
||||
><el-tooltip content="同一运输方式仅支持配置一个默认计费方案" placement="top">
|
||||
<el-icon class="billing-plan-editor__default-tip"
|
||||
><InfoFilled
|
||||
/></el-icon> </el-tooltip
|
||||
></el-form-item
|
||||
></el-col
|
||||
>
|
||||
<el-col :span="24"
|
||||
>
|
||||
<el-col :span="24"
|
||||
><el-form-item label="备注"
|
||||
><el-input
|
||||
v-model="draft.remark"
|
||||
maxlength="100"
|
||||
show-word-limit
|
||||
:disabled="readonly" /></el-form-item
|
||||
></el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
><el-input
|
||||
v-model="draft.remark"
|
||||
maxlength="100"
|
||||
show-word-limit
|
||||
:disabled="readonly" /></el-form-item
|
||||
></el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</section-card>
|
||||
<div class="rule-head">
|
||||
<el-link v-if="!readonly" type="primary" @click="addRule">+添加规则</el-link>
|
||||
</div>
|
||||
@@ -362,6 +366,7 @@ import { getList as getFeeItemList } from '@/api/base/fee-item';
|
||||
import { getLazyTree as getRegionLazyTree } from '@/api/base/region';
|
||||
import { InfoFilled } from '@element-plus/icons-vue';
|
||||
import { getDictionary } from '@/api/system/dictbiz';
|
||||
import SectionCard from '@/components/section-card/main.vue';
|
||||
|
||||
const clone = value => JSON.parse(JSON.stringify(value));
|
||||
const defaultRule = () => ({
|
||||
@@ -391,7 +396,7 @@ const defaultRule = () => ({
|
||||
});
|
||||
|
||||
export default {
|
||||
components: { InfoFilled },
|
||||
components: { SectionCard, InfoFilled },
|
||||
props: {
|
||||
modelValue: Boolean,
|
||||
value: { type: Object, default: () => ({}) },
|
||||
@@ -874,9 +879,6 @@ export default {
|
||||
this.visible = false;
|
||||
});
|
||||
},
|
||||
showDefaultPlanTip() {
|
||||
this.$message.info('同一运输方式仅支持配置一个默认计费方案');
|
||||
},
|
||||
openMatch(row, index) {
|
||||
this.matchIndex = index;
|
||||
this.matchForm = { ...defaultRule().matchCondition, ...(row.matchCondition || {}) };
|
||||
|
||||
@@ -83,7 +83,10 @@
|
||||
</section>
|
||||
|
||||
<section class="change-section change-reason-section"><div class="dialog-section-title">变更原因</div><el-form-item prop="changeReason"><el-input v-model="form.changeReason" type="textarea" :rows="2" maxlength="2000" show-word-limit placeholder="请输入变更原因" /></el-form-item><div class="dialog-section-title">变更材料</div><el-upload action="#" :auto-upload="false" multiple :show-file-list="false" @change="handleChangeMaterial"><el-button plain>上传变更材料</el-button></el-upload></section>
|
||||
<div class="page-footer"><el-button type="primary" @click="submit">提交</el-button><el-button @click="$router.back()">关闭</el-button></div>
|
||||
<div class="page-footer">
|
||||
<el-button @click="$router.back()">取消</el-button>
|
||||
<el-button type="primary" @click="submit">提交</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<el-dialog v-model="documentPreviewVisible" :title="previewFile.name || '附件预览'" append-to-body destroy-on-close width="90%" top="4vh">
|
||||
@@ -151,11 +154,11 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.contract-change-page { min-height: 100%; background: #f5f6fa; }
|
||||
<style scoped lang="scss">
|
||||
.contract-change-page { min-height: 100%; padding-bottom: 72px; background: #f5f6fa; }
|
||||
.contract-change-page :deep(.basic-container__card) { border: 0; background: transparent; box-shadow: none; }
|
||||
.contract-change-form { background: #f5f6fa; }
|
||||
.change-section { padding: 20px 24px; background: #fff; border-bottom: 1px solid #eff1f7; }
|
||||
.change-section { padding: 20px 24px; background: #fff; border-bottom: 1px solid #eff1f7; margin-top: 12px; }
|
||||
.contract-basic-section { margin-bottom: 12px; padding: 14px 16px 16px; overflow: hidden; background: #fff; border-bottom: 0; border-radius: 6px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); }
|
||||
.contract-basic-section > .dialog-section-title { margin-bottom: 20px; color: #303133; }
|
||||
.contract-basic-section__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 48px; }
|
||||
@@ -182,8 +185,34 @@ export default {
|
||||
.attachment-upload .el-upload { display: inline-flex; }
|
||||
.settlement-switch { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
|
||||
.settlement-form { display: grid; grid-template-columns: repeat(3, minmax(240px, 1fr)); gap: 8px 28px; }
|
||||
.change-reason-section { border: 1px dashed #ff8f9a; margin: 20px 16px; }
|
||||
.page-footer { display: flex; gap: 16px; padding: 20px 40px; border-top: 1px solid #eff1f7; }
|
||||
.change-reason-section { margin: 20px 0; }
|
||||
.page-footer {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 230px;
|
||||
z-index: 20;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
min-height: 64px;
|
||||
box-sizing: border-box;
|
||||
padding: 12px 24px;
|
||||
background: #fff;
|
||||
border-top: 1px solid #eff1f7;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
|
||||
|
||||
// 统一 12px 间距,与全站底部操作栏(去 gap,由相邻按钮 margin 提供)一致
|
||||
.el-button + .el-button {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
:global(.avue--collapse .page-footer) {
|
||||
left: 60px;
|
||||
}
|
||||
:global(.avue-layout--horizontal .page-footer) {
|
||||
left: 0;
|
||||
}
|
||||
@media (max-width: 1200px) { .contract-basic-section__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 32px; } }
|
||||
@media (max-width: 768px) { .contract-basic-section__grid { grid-template-columns: 1fr; } }
|
||||
</style>
|
||||
|
||||
@@ -33,16 +33,7 @@
|
||||
</template>
|
||||
|
||||
<template #search-menu>
|
||||
<div v-show="searchExpanded" class="contract-manage-page__expiry-search">
|
||||
<span>签约类型:</span>
|
||||
<el-select v-model="contractSignType" clearable placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in signTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<div class="contract-manage-page__expiry-search">
|
||||
<el-check-tag
|
||||
v-for="item in expiryTagOptions"
|
||||
:key="item.value"
|
||||
@@ -55,7 +46,7 @@
|
||||
</template>
|
||||
|
||||
<template #contractName="{ row }">
|
||||
<el-link v-if="row.contractName" type="primary" @click.stop="openDetail(row)">
|
||||
<el-link v-if="row.contractName" type="primary" class="contract-name-cell" @click.stop="openDetail(row)">
|
||||
{{ row.contractName }}
|
||||
</el-link>
|
||||
<span v-else>-</span>
|
||||
@@ -492,8 +483,8 @@
|
||||
</el-form>
|
||||
|
||||
<div class="contract-manage-page__footer">
|
||||
<el-button @click="closeForm">关闭</el-button>
|
||||
<template v-if="formMode === 'add'">
|
||||
<el-button @click="closeForm">取消</el-button>
|
||||
<template v-if="formMode === 'add' || isDraftEdit">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@@ -1111,7 +1102,6 @@ export default {
|
||||
page: { currentPage: 1, pageSize: 10, pageSizes: [10, 20, 50, 100], total: 0 },
|
||||
selectionList: [],
|
||||
searchExpanded: false,
|
||||
contractSignType: '',
|
||||
contractExpiryScope: '',
|
||||
contractExpiryStats: { all: 0, within30: 0, within90: 0, over90: 0, expired: 0 },
|
||||
expiryTagOptions: [
|
||||
@@ -1215,6 +1205,10 @@ export default {
|
||||
formMode() {
|
||||
return this.$route.query.mode === 'edit' ? 'edit' : 'add';
|
||||
},
|
||||
// 草稿(approvalStatus=draft)编辑页:按钮与新增页一致(暂存/提交临时/提交正式)
|
||||
isDraftEdit() {
|
||||
return this.formMode === 'edit' && this.form.approvalStatus === 'draft';
|
||||
},
|
||||
formPageTitle() {
|
||||
return this.$route.query.name || `${this.formMode === 'edit' ? '编辑' : '新增'}合同管理`;
|
||||
},
|
||||
@@ -1397,7 +1391,6 @@ export default {
|
||||
const query = this.normalizeSearch({
|
||||
...params,
|
||||
...this.query,
|
||||
...(this.contractSignType ? { signType: this.contractSignType } : {}),
|
||||
...(this.contractExpiryScope ? { expireScope: this.contractExpiryScope } : {}),
|
||||
});
|
||||
this.api
|
||||
@@ -1421,7 +1414,6 @@ export default {
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.contractSignType = '';
|
||||
this.contractExpiryScope = '';
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page);
|
||||
@@ -2103,11 +2095,13 @@ export default {
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding-top: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.el-select {
|
||||
width: 180px;
|
||||
}
|
||||
// 将 #search-menu slot 容器(即"时间段快捷筛选"组)排到「查询 / 重置 / 收起」之前;
|
||||
// .avue-form__menu 本身就是 flex + flex-end,整组内容自然靠右,无需额外推挤
|
||||
:deep(.avue-crud__search .avue-form__menu > .contract-manage-page__expiry-search) {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
@@ -2119,13 +2113,17 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
min-height: 64px;
|
||||
box-sizing: border-box;
|
||||
padding: 12px 24px;
|
||||
background: #fff;
|
||||
border-top: 1px solid #eff1f7;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
|
||||
|
||||
// 统一 12px 间距,与全站底部操作栏(去 gap,由相邻按钮 margin 提供)一致
|
||||
.el-button + .el-button {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&__flow {
|
||||
@@ -2144,6 +2142,17 @@ export default {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
// 搜索区域白底卡片包裹
|
||||
:deep(.avue-crud__search) {
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
:deep(.avue-crud__search:not(.el-card)) {
|
||||
padding: 12px 12px 4px;
|
||||
}
|
||||
|
||||
&--form {
|
||||
min-height: 100%;
|
||||
padding-bottom: 72px;
|
||||
@@ -2180,7 +2189,8 @@ export default {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
> .dialog-section-title {
|
||||
> .dialog-section-title,
|
||||
:deep(.dialog-section-title) {
|
||||
margin-bottom: 20px;
|
||||
color: #303133;
|
||||
font-size: 16px;
|
||||
|
||||
@@ -4659,7 +4659,7 @@ export default {
|
||||
white-space: normal !important;
|
||||
word-break: break-all;
|
||||
overflow-wrap: anywhere;
|
||||
line-height: 18px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
// 锁定表单项 label 与控件垂直居中对齐(避免长 label 折行时视觉错位)
|
||||
@@ -4673,8 +4673,6 @@ export default {
|
||||
// 通过 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) {
|
||||
|
||||
Reference in New Issue
Block a user