8f35e6b265
- 将所有 el-table 表头背景色统一设为浅灰 #fafafa,覆盖普通表格、avue-crud 和固定列 - 移除业务组件内冗余的表头样式,改由全局样式统一控制 - 调整多处弹窗(预结算单、正式结算单、运输对账单、应收明细等)内容板块,改用全局 section-card 组件实现白底卡片分组 - 为多个弹窗内的搜索区域增加白底、圆角和阴影样式,提升视觉层次感 - 缩减多处表格“操作”列宽度以适配新布局 - 付款申请弹窗按钮由主色调 primary 改为 success,符合全站按钮风格规范 - 统一调整弹窗内表单、按钮、间距及背景细节,改善界面体验和视觉一致性
372 lines
12 KiB
Vue
372 lines
12 KiB
Vue
<template>
|
||
<el-dialog v-model="visible" :title="title" width="94%" top="3vh" append-to-body destroy-on-close>
|
||
<div v-loading="loading" class="settlement-adjustment-editor">
|
||
<section-card title="基本信息">
|
||
<el-form
|
||
ref="formRef"
|
||
:model="form"
|
||
:rules="rules"
|
||
label-position="right"
|
||
label-width="auto"
|
||
>
|
||
<el-row :gutter="24">
|
||
<el-col v-for="field in fields" :key="field.prop" :span="8"
|
||
><el-form-item :label="field.label" :prop="field.prop"
|
||
><el-select
|
||
v-if="field.prop === 'formalSettlementId' && editable"
|
||
v-model="form.formalSettlementId"
|
||
filterable
|
||
remote
|
||
clearable
|
||
:remote-method="loadFormalSettlements"
|
||
placeholder="请选择正式结算单"
|
||
@change="handleFormalChange"
|
||
><el-option
|
||
v-for="item in candidates"
|
||
:key="item.id"
|
||
:label="`${item.formalSettlementNo} / ${item.projectName || ''}`"
|
||
:value="item.id" /></el-select
|
||
><el-input
|
||
v-else-if="field.prop === 'adjustmentNo'"
|
||
v-model="form[field.prop]"
|
||
disabled
|
||
/><el-input-number
|
||
v-else-if="field.money && editable"
|
||
v-model="form[field.prop]"
|
||
:precision="2"
|
||
:controls="false"
|
||
disabled
|
||
/><span v-else>{{
|
||
displayValue(
|
||
field.prop === 'settlementTypeName' ? settlementTypeName : form[field.prop]
|
||
)
|
||
}}</span></el-form-item
|
||
></el-col
|
||
>
|
||
<el-col :span="24"
|
||
><el-form-item label="备注" prop="remark"
|
||
><el-input
|
||
v-if="editable"
|
||
v-model="form.remark"
|
||
type="textarea"
|
||
maxlength="200"
|
||
show-word-limit
|
||
/><span v-else>{{ displayValue(form.remark) }}</span></el-form-item
|
||
></el-col
|
||
>
|
||
</el-row>
|
||
</el-form>
|
||
</section-card>
|
||
|
||
<section-card title="调整费用">
|
||
<template #extra>
|
||
<el-button
|
||
v-if="editable && form.formalSettlementId"
|
||
type="primary"
|
||
plain
|
||
@click="openFeeDialog"
|
||
>添加费用</el-button
|
||
>
|
||
</template>
|
||
<el-table :data="details" border>
|
||
<el-table-column type="index" label="序号" width="64" align="center" /><el-table-column
|
||
prop="feeType"
|
||
label="费用类型"
|
||
min-width="150"
|
||
align="center"
|
||
/><el-table-column
|
||
prop="feeItem"
|
||
label="费用项目"
|
||
min-width="180"
|
||
align="center"
|
||
show-overflow-tooltip
|
||
/><el-table-column
|
||
prop="originalAmountTax"
|
||
label="原金额(含税)"
|
||
min-width="145"
|
||
align="center"
|
||
><template #default="{ row }">{{
|
||
formatMoney(row.originalAmountTax)
|
||
}}</template></el-table-column
|
||
>
|
||
<el-table-column label="调整金额(含税)" min-width="160" align="center"
|
||
><template #default="{ row }"
|
||
><el-input-number
|
||
v-if="editable"
|
||
v-model="row.adjustmentAmountTax"
|
||
:precision="2"
|
||
:controls="false"
|
||
@change="recalculate"
|
||
/><span v-else :class="{ negative: Number(row.adjustmentAmountTax) < 0 }">{{
|
||
formatMoney(row.adjustmentAmountTax)
|
||
}}</span></template
|
||
></el-table-column
|
||
>
|
||
<el-table-column label="调整金额(不含税)" min-width="170" align="center"
|
||
><template #default="{ row }"
|
||
><el-input-number
|
||
v-if="editable"
|
||
v-model="row.adjustmentAmountNoTax"
|
||
:precision="2"
|
||
:controls="false"
|
||
/><span v-else>{{ formatMoney(row.adjustmentAmountNoTax) }}</span></template
|
||
></el-table-column
|
||
>
|
||
<el-table-column label="备注" min-width="180" align="center"
|
||
><template #default="{ row }"
|
||
><el-input v-if="editable" v-model="row.remark" maxlength="200" /><span v-else>{{
|
||
displayValue(row.remark)
|
||
}}</span></template
|
||
></el-table-column
|
||
>
|
||
<el-table-column v-if="editable" label="操作" width="90" align="center"
|
||
><template #default="{ $index }"
|
||
><el-link
|
||
type="danger"
|
||
@click="
|
||
details.splice($index, 1);
|
||
recalculate();
|
||
"
|
||
>删除</el-link
|
||
></template
|
||
></el-table-column
|
||
>
|
||
<template #empty><el-empty description="请选择调整费用明细" /></template>
|
||
</el-table>
|
||
<div class="settlement-adjustment-editor__summary">
|
||
调整金额合计:<strong :class="{ negative: Number(form.adjustmentAmount) < 0 }">{{
|
||
formatMoney(form.adjustmentAmount)
|
||
}}</strong
|
||
>;调整后结算金额:<strong>{{ formatMoney(form.adjustedSettlementAmount) }}</strong>
|
||
</div>
|
||
</section-card>
|
||
</div>
|
||
<template #footer
|
||
><el-button @click="visible = false">取消</el-button
|
||
><el-button v-if="editable" type="primary" :loading="saving" @click="handleSave"
|
||
>保存</el-button
|
||
></template
|
||
>
|
||
|
||
<el-dialog v-model="feeDialog.visible" title="选择调整费用" width="88%" append-to-body>
|
||
<el-table
|
||
ref="feeTable"
|
||
:data="feeRows"
|
||
border
|
||
@selection-change="feeDialog.selected = $event"
|
||
><el-table-column type="selection" width="52" align="center" /><el-table-column
|
||
type="index"
|
||
label="序号"
|
||
width="64"
|
||
align="center"
|
||
/><el-table-column
|
||
prop="documentNo"
|
||
label="单据号"
|
||
min-width="150"
|
||
align="center"
|
||
/><el-table-column
|
||
prop="feeType"
|
||
label="费用类型"
|
||
min-width="140"
|
||
align="center"
|
||
/><el-table-column
|
||
prop="feeItem"
|
||
label="费用项目"
|
||
min-width="180"
|
||
align="center"
|
||
show-overflow-tooltip
|
||
/><el-table-column
|
||
prop="originalAmountTax"
|
||
label="原金额(含税)"
|
||
min-width="145"
|
||
align="center"
|
||
><template #default="{ row }">{{
|
||
formatMoney(row.originalAmountTax)
|
||
}}</template></el-table-column
|
||
></el-table
|
||
>
|
||
<template #footer
|
||
><el-button @click="feeDialog.visible = false">取消</el-button
|
||
><el-button type="primary" @click="confirmFees">确定</el-button></template
|
||
>
|
||
</el-dialog>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script>
|
||
import * as api from '@/api/settlement/settlementAdjustment';
|
||
import {
|
||
createSettlementAdjustmentForm,
|
||
settlementAdjustmentFormFields,
|
||
} from '@/option/settlement/settlementAdjustmentForm';
|
||
export default {
|
||
name: 'SettlementAdjustmentEditor',
|
||
props: { modelValue: Boolean, recordId: [String, Number], readonly: Boolean },
|
||
emits: ['update:modelValue', 'success'],
|
||
data: () => ({
|
||
loading: false,
|
||
saving: false,
|
||
form: createSettlementAdjustmentForm(),
|
||
fields: settlementAdjustmentFormFields,
|
||
details: [],
|
||
candidates: [],
|
||
feeRows: [],
|
||
rules: {
|
||
formalSettlementId: [{ required: true, message: '请选择关联正式结算单', trigger: 'change' }],
|
||
},
|
||
feeDialog: { visible: false, selected: [] },
|
||
}),
|
||
computed: {
|
||
visible: {
|
||
get() {
|
||
return this.modelValue;
|
||
},
|
||
set(value) {
|
||
this.$emit('update:modelValue', value);
|
||
},
|
||
},
|
||
editable() {
|
||
return !this.readonly;
|
||
},
|
||
title() {
|
||
return this.readonly ? '查看结算调整单' : this.recordId ? '编辑结算调整单' : '新增结算调整单';
|
||
},
|
||
settlementTypeName() {
|
||
return this.form.settlementType === 'receivable'
|
||
? '应收'
|
||
: this.form.settlementType === 'payable'
|
||
? '应付'
|
||
: '';
|
||
},
|
||
},
|
||
watch: {
|
||
modelValue(value) {
|
||
if (value) this.initialize();
|
||
},
|
||
},
|
||
methods: {
|
||
async initialize() {
|
||
this.form = createSettlementAdjustmentForm();
|
||
this.details = [];
|
||
this.candidates = [];
|
||
this.feeRows = [];
|
||
if (!this.recordId) {
|
||
await this.loadFormalSettlements('');
|
||
return;
|
||
}
|
||
this.loading = true;
|
||
try {
|
||
const { data } = await api.getDetail(this.recordId);
|
||
this.form = { ...createSettlementAdjustmentForm(), ...data };
|
||
this.details = (data.details || []).map(item => ({
|
||
...item,
|
||
adjustmentAmountTax: Number(item.adjustmentAmountTax || 0),
|
||
adjustmentAmountNoTax:
|
||
item.adjustmentAmountNoTax == null ? null : Number(item.adjustmentAmountNoTax),
|
||
}));
|
||
await this.loadFormalSettlements(this.form.formalSettlementNo || '');
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
async loadFormalSettlements(keyword = '') {
|
||
const { data } = await api.getFormalSettlements(keyword);
|
||
this.candidates = data?.data || data || [];
|
||
},
|
||
async handleFormalChange(id) {
|
||
const item = this.candidates.find(row => String(row.id) === String(id));
|
||
if (!item) return;
|
||
Object.assign(this.form, item, {
|
||
formalSettlementId: id,
|
||
formalSettlementNo: item.formalSettlementNo,
|
||
originalSettlementAmount: Number(item.settlementAmount || 0),
|
||
settlementTypeName: item.settlementTypeName,
|
||
});
|
||
this.details = [];
|
||
this.recalculate();
|
||
},
|
||
async openFeeDialog() {
|
||
const { data } = await api.getFormalDetails(this.form.formalSettlementId);
|
||
const used = new Set(this.details.map(item => String(item.formalSettlementDetailFeeId)));
|
||
this.feeRows = (data?.data || data || []).filter(
|
||
item => !used.has(String(item.formalSettlementDetailFeeId))
|
||
);
|
||
this.feeDialog = { visible: true, selected: [] };
|
||
},
|
||
confirmFees() {
|
||
this.feeDialog.selected.forEach(item =>
|
||
this.details.push({
|
||
...item,
|
||
adjustmentAmountTax: 0,
|
||
adjustmentAmountNoTax: null,
|
||
remark: '',
|
||
})
|
||
);
|
||
this.recalculate();
|
||
this.feeDialog.visible = false;
|
||
},
|
||
recalculate() {
|
||
const total = this.details.reduce(
|
||
(sum, row) => sum + Number(row.adjustmentAmountTax || 0),
|
||
0
|
||
);
|
||
this.form.adjustmentAmount = Number(total.toFixed(2));
|
||
this.form.adjustedSettlementAmount = Number(
|
||
(Number(this.form.originalSettlementAmount || 0) + total).toFixed(2)
|
||
);
|
||
},
|
||
async handleSave() {
|
||
await this.$refs.formRef.validate();
|
||
if (!this.details.length) return this.$message.warning('请至少添加一条调整费用');
|
||
this.saving = true;
|
||
try {
|
||
await api.save({
|
||
id: this.form.id,
|
||
formalSettlementId: this.form.formalSettlementId,
|
||
remark: this.form.remark,
|
||
details: this.details.map(item => ({
|
||
formalSettlementDetailId: item.formalSettlementDetailId,
|
||
formalSettlementDetailFeeId: item.formalSettlementDetailFeeId,
|
||
feeType: item.feeType,
|
||
feeItem: item.feeItem,
|
||
adjustmentAmountTax: item.adjustmentAmountTax,
|
||
adjustmentAmountNoTax: item.adjustmentAmountNoTax,
|
||
remark: item.remark,
|
||
})),
|
||
});
|
||
this.$message.success('保存成功');
|
||
this.visible = false;
|
||
this.$emit('success');
|
||
} finally {
|
||
this.saving = false;
|
||
}
|
||
},
|
||
displayValue(value) {
|
||
return value === null || value === undefined || value === '' ? '-' : value;
|
||
},
|
||
formatMoney(value) {
|
||
return Number(value || 0).toFixed(2);
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.settlement-adjustment-editor__summary {
|
||
padding: 12px 0;
|
||
text-align: right;
|
||
}
|
||
.settlement-adjustment-editor :deep(.el-form-item) {
|
||
margin-bottom: 16px;
|
||
}
|
||
.settlement-adjustment-editor :deep(.el-table) {
|
||
--el-table-border-color: #eff1f7;
|
||
}
|
||
.settlement-adjustment-editor :deep(.el-table__body tr:nth-child(even) > td.el-table__cell) {
|
||
background: #fafafa;
|
||
}
|
||
.negative {
|
||
color: #f56c6c;
|
||
}
|
||
</style>
|