Files
tms-erp-web/src/views/payment/bill-payment-form.vue
T
gxwebsoft bb97c00a6a refactor(ui): 调整多处文本框行数为2行以优化界面布局
- 将锁定和解锁原因文本框行数由3改为2行
- 调整支付单据和发票相关备注输入框行数为2行
- 优化业务组件中多处备注和说明文本框行数为2行
- 修改异常处理及风险处置页面文本框行数为2行
- 更新项目申请及合同变更中的多处文本框行数为2行
- 在发货地址输入框中添加“选择”链接,提升地址选择体验
- 规范备注输入框显示,统一字数限制及行数设置
- 统一调整信用评分、路线变更备注输入框行数及样式样式调整
2026-08-25 19:57:17 +08:00

422 lines
13 KiB
Vue

<template>
<basic-container class="bill-payment-form-page">
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-position="right"
label-width="auto"
class="bill-payment-form-page__form"
>
<section-card title="基本信息">
<el-row :gutter="24">
<el-col :span="6">
<el-form-item label="票据号码" prop="billLedgerId">
<el-input
v-model="form.billNo"
readonly
:disabled="readonly"
placeholder="请选择汇票台账"
>
<template #append>
<el-button :icon="Search" :disabled="readonly" @click="openBillDialog" />
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="可用余额">
<el-input :model-value="formatMoney(form.availableBalance)" disabled />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="付款日期" prop="paymentDate">
<el-date-picker
v-model="form.paymentDate"
type="date"
value-format="YYYY-MM-DD"
:disabled="readonly"
placeholder="请选择"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="票面金额">
<el-input :model-value="formatMoney(form.faceAmount)" disabled />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="本次使用" prop="usedAmount">
<el-input-number
v-model="form.usedAmount"
:disabled="readonly"
:min="0"
:precision="2"
:controls="false"
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark" class="bill-payment-form-page__remark">
<el-input
v-model="form.remark"
:disabled="readonly"
type="textarea"
:rows="2"
maxlength="200"
show-word-limit
placeholder="请输入"
/>
</el-form-item>
</el-col>
</el-row>
</section-card>
<section-card title="附件信息">
<el-table :data="form.attachments" border>
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column label="文件名" min-width="220">
<template #default="{ row }">
<el-link
v-if="row.url || row.link"
type="primary"
:href="row.url || row.link"
target="_blank"
>
{{ row.originalName || row.name || '附件' }}
</el-link>
<span v-else>{{ row.originalName || row.name || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="附件描述" min-width="240">
<template #default="{ row }">
<el-input v-model="row.description" :disabled="readonly" maxlength="200" />
</template>
</el-table-column>
<el-table-column label="文件大小" width="120" align="center">
<template #default="{ row }">{{ fileSize(row) }}</template>
</el-table-column>
<el-table-column prop="uploadUserName" label="上传人" width="130" align="center" />
<el-table-column prop="uploadTime" label="上传时间" width="170" align="center" />
<el-table-column label="操作" width="100" align="center">
<template #default="{ $index }">
<el-link v-if="!readonly" type="danger" @click="form.attachments.splice($index, 1)"
>删除</el-link
>
</template>
</el-table-column>
</el-table>
<vehicle-attachment-upload
v-if="!readonly"
v-model="form.attachments"
class="bill-payment-form-page__uploader"
:multiple="true"
:limit="20"
:max-size="500"
:file-types="attachmentFileTypes"
:show-file-list="false"
button-text="上传"
@change="normalizeAttachments"
/>
</section-card>
<div class="bill-payment-form-page__actions">
<template v-if="!readonly">
<el-button type="primary" :loading="saving" :disabled="submitting" @click="saveDraft">
保存
</el-button>
<el-button type="primary" :loading="submitting" :disabled="saving" @click="submitForm">
提交
</el-button>
</template>
<el-button @click="goBack">取消</el-button>
</div>
</el-form>
<el-dialog v-model="billDialog.visible" title="选择汇票台账" width="86%" append-to-body>
<div class="bill-payment-form-page__dialog-search">
<el-input
v-model="billDialog.keyword"
clearable
placeholder="票据号码、出票单位或收票单位"
@keyup.enter="loadBillOptions"
/>
<el-button type="primary" @click="loadBillOptions">查询</el-button>
</div>
<el-table
v-loading="billDialog.loading"
:data="billDialog.rows"
border
highlight-current-row
@current-change="billDialog.current = $event"
@row-dblclick="selectBill"
>
<el-table-column prop="billNo" label="票据编号" min-width="170" />
<el-table-column prop="issuerName" label="出票单位" min-width="160" />
<el-table-column prop="receiverName" label="收票单位" min-width="160" />
<el-table-column label="票面金额" min-width="130" align="right">
<template #default="{ row }">{{ formatMoney(row.faceAmount) }}</template>
</el-table-column>
<el-table-column label="可用余额" min-width="130" align="right">
<template #default="{ row }">{{ formatMoney(row.availableBalance) }}</template>
</el-table-column>
<el-table-column prop="maturityDate" label="到期日期" min-width="120" />
</el-table>
<template #footer>
<el-button @click="billDialog.visible = false">取消</el-button>
<el-button type="primary" @click="selectBill">确定</el-button>
</template>
</el-dialog>
</basic-container>
</template>
<script>
import { Search } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import * as api from '@/api/payment/billPayment';
import * as billLedgerApi from '@/api/payment/billLedger';
const emptyForm = () => ({
id: null,
billLedgerId: null,
billNo: '',
faceAmount: 0,
availableBalance: 0,
usedAmount: null,
deptId: null,
deptName: '',
paymentDate: '',
attachments: [],
remark: '',
});
export default {
name: 'BillPaymentForm',
data() {
return {
Search,
form: emptyForm(),
saving: false,
submitting: false,
billDialog: { visible: false, loading: false, keyword: '', rows: [], current: null },
attachmentFileTypes: [
'pdf',
'bmp',
'jpeg',
'png',
'jpg',
'doc',
'docx',
'ppt',
'pptx',
'xlsx',
'xls',
'eml',
'msg',
'zip',
],
rules: {
billLedgerId: [{ required: true, message: '请选择汇票台账', trigger: 'change' }],
usedAmount: [{ validator: this.validateUsedAmount, trigger: 'change' }],
paymentDate: [{ required: true, message: '请选择付款日期', trigger: 'change' }],
remark: [{ max: 200, message: '备注不能超过200个字符', trigger: 'blur' }],
},
};
},
computed: {
...mapGetters(['userInfo']),
recordId() {
return this.$route.query.id || '';
},
readonly() {
return this.$route.query.mode === 'view';
},
},
created() {
this.initialize();
},
methods: {
unwrapData(response) {
const body = response?.data || response || {};
return body?.data || body;
},
parse(value) {
if (!value) return [];
if (Array.isArray(value)) return value;
try {
return JSON.parse(value) || [];
} catch {
return [];
}
},
async initialize() {
if (this.recordId) {
const data = this.unwrapData(await api.getDetail(this.recordId));
this.form = {
...emptyForm(),
...data,
attachments: this.parse(data.attachmentsJson),
};
return;
}
this.form.deptId = this.userInfo?.deptId || this.userInfo?.dept_id || null;
this.form.deptName = this.userInfo?.deptName || this.userInfo?.dept_name || '';
this.form.paymentDate = this.$dayjs().format('YYYY-MM-DD');
},
validateUsedAmount(rule, value, callback) {
const amount = Number(value);
const balance = Number(this.form.availableBalance || 0);
if (!Number.isFinite(amount) || amount <= 0) {
callback(new Error('本次使用必须大于0'));
return;
}
if (amount > balance) {
callback(new Error('本次使用不能超过汇票可用余额'));
return;
}
callback();
},
async openBillDialog() {
this.billDialog.visible = true;
this.billDialog.keyword = this.form.billNo || '';
await this.loadBillOptions();
},
async loadBillOptions() {
this.billDialog.loading = true;
try {
this.billDialog.rows =
this.unwrapData(
await billLedgerApi.getAvailableOptions(
this.billDialog.keyword,
this.form.deptId,
this.form.billLedgerId
)
) || [];
} finally {
this.billDialog.loading = false;
}
},
selectBill(row) {
const selected = row?.id ? row : this.billDialog.current;
if (!selected) {
this.$message.warning('请选择汇票台账');
return;
}
Object.assign(this.form, {
billLedgerId: selected.id,
billNo: selected.billNo,
faceAmount: selected.faceAmount,
availableBalance: selected.availableBalance,
});
this.billDialog.visible = false;
this.$refs.formRef?.validateField('billLedgerId').catch(() => {});
this.$refs.formRef?.validateField('usedAmount').catch(() => {});
},
normalizeAttachments(files) {
const userName = this.userInfo?.realName || this.userInfo?.userName || '';
const time = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
this.form.attachments = (files || []).map(file => ({
...file,
description: file.description || '',
uploadUserName: file.uploadUserName || userName,
uploadTime: file.uploadTime || time,
}));
},
fileSize(file) {
if (typeof file.size === 'string' && /[a-z]/i.test(file.size)) return file.size;
const bytes = Number(file.size || file.fileSize || 0);
if (!bytes) return '-';
if (bytes < 1024) return `${bytes}B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
},
payload() {
return {
id: this.form.id,
billLedgerId: this.form.billLedgerId,
usedAmount: this.form.usedAmount,
paymentDate: this.form.paymentDate,
attachmentsJson: JSON.stringify(this.form.attachments || []),
remark: this.form.remark,
};
},
async saveDraft() {
await this.$refs.formRef.validate();
this.saving = true;
try {
const id = this.unwrapData(await api.save(this.payload()));
this.form.id = id;
this.$message.success('保存成功');
return true;
} finally {
this.saving = false;
}
},
async submitForm() {
await this.$refs.formRef.validate();
this.submitting = true;
try {
const id = this.unwrapData(await api.save(this.payload()));
this.form.id = id;
await api.submit({ id: this.form.id });
this.$message.success('提交成功');
this.goBack();
} finally {
this.submitting = false;
}
},
goBack() {
this.$router.push('/payment/bill-payment');
},
formatMoney(value) {
return Number(value || 0).toFixed(2);
},
},
};
</script>
<style scoped lang="scss">
.bill-payment-form-page__form :deep(.el-form-item) {
margin-bottom: 16px;
}
.bill-payment-form-page__form :deep(.el-input),
.bill-payment-form-page__form :deep(.el-input-number),
.bill-payment-form-page__form :deep(.el-date-editor) {
width: 100%;
}
.bill-payment-form-page__remark :deep(.el-form-item__content) {
margin-left: 0 !important;
}
.bill-payment-form-page__uploader {
margin-top: 12px;
}
.bill-payment-form-page__actions {
display: flex;
justify-content: flex-start;
gap: 8px;
padding: 16px 0 8px;
}
.bill-payment-form-page__dialog-search {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-bottom: 12px;
}
.bill-payment-form-page__dialog-search .el-input {
width: 360px;
}
.bill-payment-form-page :deep(.el-table) {
--el-table-border-color: #eff1f7;
}
.bill-payment-form-page :deep(.el-table__row:nth-child(even) > td.el-table__cell) {
background: #fafafa;
}
@media (max-width: 1024px) {
.bill-payment-form-page :deep(.el-col-6) {
max-width: 100%;
flex: 0 0 100%;
}
}
</style>