refactor(settlement): 全面重构结算相关页面为 avue-crud 组件
- formal-settlement.vue 中移除旧搜索区,移除多余样式,实现结算表格面板按激活标签条件渲染 - 替换 formal-settlement-table-panel.vue 为基于 avue-crud,简化表格和操作,优化分页和搜索事件处理 - 新版 formal-settlement 中统一使用 onLoad 替代 loadTable 方法,简化数据加载逻辑 - 删除不必要的样式代码,统一表格奇偶行背景颜色,调整整体样式提升视觉一致性 - 结算导入页面 settlement-import.vue 同样迁移至 avue-crud 组件实现,实现搜索、分页、刷新等功能事件解耦 - 优化分页数据结构,统一使用 currentPage 和 pageSize 命名,支持多页容量选择 - 调整部分接口调用及事件回调,保障新组件兼容已有业务流程及权限控制 - 移除废弃的内部变量与混入,针对权限控制和字段渲染进行明确封装和暴露 - 保持功能行为不变的前提下提升代码可维护性和复用性,增强组件封装与状态同步能力
This commit is contained in:
@@ -1,89 +1,39 @@
|
||||
<template>
|
||||
<basic-container class="formal-page">
|
||||
<section v-show="searchVisible" class="formal-page__search">
|
||||
<el-form :model="query" label-position="right" label-width="88px" @submit.prevent>
|
||||
<div class="formal-page__search-grid">
|
||||
<el-form-item v-for="field in visibleSearchFields" :key="field.prop" :label="field.label">
|
||||
<el-date-picker
|
||||
v-if="field.type === 'daterange'"
|
||||
v-model="query[field.prop]"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
format="YYYY-MM-DD"
|
||||
range-separator="~"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
/>
|
||||
<el-select
|
||||
v-else-if="field.type === 'select'"
|
||||
v-model="query[field.prop]"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in field.options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-cascader
|
||||
v-else-if="field.type === 'cascader'"
|
||||
v-model="query[field.prop]"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<el-input v-else v-model="query[field.prop]" clearable placeholder="请输入" />
|
||||
</el-form-item>
|
||||
<div class="formal-page__search-actions">
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
<el-button @click="resetSearch">重置</el-button>
|
||||
<el-button text :icon="searchExpanded ? ArrowUp : ArrowDown" @click="toggleSearch">
|
||||
{{ searchExpanded ? '收起' : '展开' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</section>
|
||||
|
||||
<section class="formal-page__table-panel">
|
||||
<el-tabs
|
||||
v-if="!lockedSettlementType"
|
||||
type="border-card"
|
||||
v-model="activeSettlementType"
|
||||
@tab-change="handleSettlementTypeChange"
|
||||
>
|
||||
<el-tab-pane label="应付" name="payable">
|
||||
<formal-settlement-table-panel
|
||||
:key="'payable'"
|
||||
v-bind="tablePanelProps"
|
||||
v-on="tablePanelListeners"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="应收" name="receivable">
|
||||
<formal-settlement-table-panel
|
||||
:key="'receivable'"
|
||||
v-bind="tablePanelProps"
|
||||
v-on="tablePanelListeners"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<formal-settlement-table-panel
|
||||
v-else
|
||||
:key="activeSettlementType"
|
||||
v-bind="tablePanelProps"
|
||||
v-on="tablePanelListeners"
|
||||
/>
|
||||
</section>
|
||||
<el-tabs
|
||||
v-if="!lockedSettlementType"
|
||||
type="border-card"
|
||||
v-model="activeSettlementType"
|
||||
@tab-change="handleSettlementTypeChange"
|
||||
>
|
||||
<el-tab-pane label="应付" name="payable">
|
||||
<formal-settlement-table-panel
|
||||
v-if="activeSettlementType === 'payable'"
|
||||
key="payable"
|
||||
v-bind="tablePanelProps"
|
||||
v-on="tablePanelListeners"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="应收" name="receivable">
|
||||
<formal-settlement-table-panel
|
||||
v-if="activeSettlementType === 'receivable'"
|
||||
key="receivable"
|
||||
v-bind="tablePanelProps"
|
||||
v-on="tablePanelListeners"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<formal-settlement-table-panel
|
||||
v-else
|
||||
key="locked"
|
||||
v-bind="tablePanelProps"
|
||||
v-on="tablePanelListeners"
|
||||
/>
|
||||
<formal-settlement-editor
|
||||
v-model="editor.visible"
|
||||
:record-id="editor.id"
|
||||
:readonly="editor.readonly"
|
||||
@success="loadTable"
|
||||
@success="onLoad(page)"
|
||||
/>
|
||||
<el-dialog
|
||||
v-model="paymentDialog.visible"
|
||||
@@ -152,7 +102,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { createSettlementTransfer } from '@/utils/settlement-transfer';
|
||||
import {
|
||||
@@ -164,33 +113,13 @@ import { isAdministrator } from '@/utils/auth';
|
||||
import { downloadFile } from '@/utils/util';
|
||||
import * as api from '@/api/settlement/formalSettlement';
|
||||
import * as paymentApi from '@/api/payment/paymentApplication';
|
||||
import {
|
||||
invoiceStatusName,
|
||||
formalSettlementSearchFields,
|
||||
paymentStatusOptions,
|
||||
} from '@/option/settlement/formalSettlementSearch';
|
||||
import { formalSettlementOption } from '@/option/settlement/formalSettlement';
|
||||
import organizationSearch from '@/mixins/organization-search';
|
||||
import { formalSettlementTableColumns } from '@/option/settlement/formalSettlementTable';
|
||||
import FormalSettlementEditor from './components/formal-settlement-editor.vue';
|
||||
import FormalSettlementTablePanel from './components/formal-settlement-table-panel.vue';
|
||||
import { submitMkApprovalFlow, loadMkEnabled } from '@/utils/mk-approval';
|
||||
import { getMKApprovalUrl } from '@/api/system/business-process';
|
||||
|
||||
const emptyQuery = () => ({
|
||||
formalSettlementNo: '',
|
||||
preSettlementNo: '',
|
||||
projectName: '',
|
||||
deptName: [],
|
||||
contractNo: '',
|
||||
payerName: '',
|
||||
payeeName: '',
|
||||
invoiceStatus: '',
|
||||
paymentStatus: '',
|
||||
approvalStatus: '',
|
||||
kingdeeSyncStatus: '',
|
||||
createDateRange: [],
|
||||
});
|
||||
|
||||
export default {
|
||||
name: 'FormalSettlement',
|
||||
components: { FormalSettlementEditor, FormalSettlementTablePanel },
|
||||
@@ -205,19 +134,14 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ArrowDown,
|
||||
ArrowUp,
|
||||
query: emptyQuery(),
|
||||
searchExpanded: false,
|
||||
searchVisible: true,
|
||||
option: formalSettlementOption(),
|
||||
query: {},
|
||||
activeSettlementType: 'payable',
|
||||
searchFields: formalSettlementSearchFields,
|
||||
columns: formalSettlementTableColumns,
|
||||
rows: [],
|
||||
selection: [],
|
||||
loading: false,
|
||||
syncLoading: false,
|
||||
page: { current: 1, size: 10, total: 0 },
|
||||
page: { currentPage: 1, pageSize: 10, pageSizes: [10, 20, 50, 100], total: 0 },
|
||||
editor: { visible: false, id: null, readonly: false },
|
||||
paymentDialog: {
|
||||
visible: false,
|
||||
@@ -248,14 +172,13 @@ export default {
|
||||
},
|
||||
tablePanelProps() {
|
||||
return {
|
||||
option: this.option,
|
||||
rows: this.rows,
|
||||
columns: this.columns,
|
||||
loading: this.loading,
|
||||
page: this.page,
|
||||
hasPermission: this.hasPermissionFn,
|
||||
syncLoading: this.syncLoading,
|
||||
mkEnabled: this.mkEnabled,
|
||||
searchVisible: this.searchVisible,
|
||||
showPaymentAction: this.activeSettlementType === 'payable',
|
||||
billLabel: this.billLabel,
|
||||
};
|
||||
@@ -266,20 +189,16 @@ export default {
|
||||
payment: this.openPaymentDialog,
|
||||
sync: this.handleSync,
|
||||
export: this.handleExport,
|
||||
refresh: this.loadTable,
|
||||
'page-change': this.onPageChange,
|
||||
refresh: () => this.onLoad(this.page),
|
||||
load: this.onLoad,
|
||||
action: this.onAction,
|
||||
'search-change': this.searchChange,
|
||||
'search-reset': this.searchReset,
|
||||
'update:selection': selection => {
|
||||
this.selection = selection;
|
||||
},
|
||||
'update:searchVisible': visible => {
|
||||
this.searchVisible = visible;
|
||||
},
|
||||
};
|
||||
},
|
||||
visibleSearchFields() {
|
||||
return this.searchExpanded ? this.searchFields : this.searchFields.slice(0, 4);
|
||||
},
|
||||
paymentAvailable() {
|
||||
return this.paymentRemaining(this.paymentDialog.row);
|
||||
},
|
||||
@@ -293,6 +212,10 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
activeSettlementType() {
|
||||
// 切换结算类型后由新挂载的 avue-crud 触发 on-load,此处只重置分页,避免重复请求
|
||||
this.page.currentPage = 1;
|
||||
},
|
||||
'$route.query.detailNo'(detailNo) {
|
||||
if (detailNo) this.openRouteDetail(detailNo);
|
||||
},
|
||||
@@ -300,13 +223,9 @@ export default {
|
||||
created() {
|
||||
// 标签机制按 fullPath 缓存同页多实例:仅当前路由实例应响应 detailNo 跳转,防止缓存旧实例也弹窗
|
||||
this.ownFullPath = this.$route.fullPath;
|
||||
this.loadOrganizationOptions();
|
||||
},
|
||||
mounted() {
|
||||
if (this.lockedSettlementType === 'payable' || this.lockedSettlementType === 'receivable') {
|
||||
this.activeSettlementType = this.lockedSettlementType;
|
||||
}
|
||||
this.loadOrganizationOptions();
|
||||
this.loadTable();
|
||||
this.openRouteDetail();
|
||||
loadMkEnabled()
|
||||
.then(enabled => {
|
||||
@@ -319,18 +238,18 @@ export default {
|
||||
activated() {
|
||||
if (sessionStorage.getItem('formal-settlement-list-refresh') === '1') {
|
||||
sessionStorage.removeItem('formal-settlement-list-refresh');
|
||||
this.loadTable();
|
||||
this.onLoad(this.page);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hasPermission(actionOrCode) {
|
||||
return this.hasPermissionFn(actionOrCode);
|
||||
},
|
||||
async loadTable() {
|
||||
async onLoad(page) {
|
||||
this.loading = true;
|
||||
try {
|
||||
const params = this.buildQueryParams();
|
||||
const response = await api.getList(this.page.current, this.page.size, params);
|
||||
const response = await api.getList(page.currentPage, page.pageSize, params);
|
||||
const data = this.unwrapData(response);
|
||||
this.rows = data.records || [];
|
||||
this.page.total = data.total || 0;
|
||||
@@ -338,25 +257,24 @@ export default {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
handleSearch() {
|
||||
this.page.current = 1;
|
||||
this.loadTable();
|
||||
searchChange(params, done) {
|
||||
// 所属组织级联返回 id 路径,转成组织名称传给后端
|
||||
this.normalizeOrganizationSearch(params, 'deptName');
|
||||
const range = params.createDateRange || [];
|
||||
delete params.createDateRange;
|
||||
this.query = {
|
||||
...params,
|
||||
createStartDate: range[0] || '',
|
||||
createEndDate: range[1] || '',
|
||||
};
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
resetSearch() {
|
||||
this.query = emptyQuery();
|
||||
this.handleSearch();
|
||||
},
|
||||
toggleSearch() {
|
||||
this.searchExpanded = !this.searchExpanded;
|
||||
},
|
||||
handleSizeChange() {
|
||||
this.page.current = 1;
|
||||
this.loadTable();
|
||||
},
|
||||
onPageChange({ current, size }) {
|
||||
this.page.current = current;
|
||||
this.page.size = size;
|
||||
this.loadTable();
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
onAction({ type, row }) {
|
||||
const handlers = {
|
||||
@@ -418,7 +336,7 @@ export default {
|
||||
await this.$confirm(`确认删除该${this.billLabel}草稿?`, '提示', { type: 'warning' });
|
||||
await api.remove(row.id);
|
||||
this.$message.success('删除成功');
|
||||
this.loadTable();
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
async handleSubmit(row) {
|
||||
await this.$confirm('提交后来源预付结算单将保持锁定,确认提交?', '提示', { type: 'warning' });
|
||||
@@ -432,7 +350,7 @@ export default {
|
||||
mkEnabled: this.mkEnabled,
|
||||
});
|
||||
this.$message.success('提交成功');
|
||||
this.loadTable();
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
handleMkAudit(row) {
|
||||
if (!row?.id) {
|
||||
@@ -465,7 +383,7 @@ export default {
|
||||
} else {
|
||||
this.$message.success(res.data.msg || '审批通过');
|
||||
}
|
||||
this.loadTable();
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
async handleReturn(row) {
|
||||
const { value } = await this.$prompt('请输入驳回原因', '审批驳回', {
|
||||
@@ -473,7 +391,7 @@ export default {
|
||||
});
|
||||
await api.returnBill({ id: row.id, reason: value });
|
||||
this.$message.success('已驳回');
|
||||
this.loadTable();
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
async handleVoid(row) {
|
||||
const { value } = await this.$prompt('请输入作废原因', `作废${this.billLabel}`, {
|
||||
@@ -481,7 +399,7 @@ export default {
|
||||
});
|
||||
await api.voidBill({ id: row.id, reason: value });
|
||||
this.$message.success('已作废');
|
||||
this.loadTable();
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
selectedOne(action) {
|
||||
if (this.selection.length !== 1) {
|
||||
@@ -511,7 +429,7 @@ export default {
|
||||
lastFailReason = String(error?.message || error || '未知错误').trim() || '未知错误';
|
||||
}
|
||||
}
|
||||
await this.loadTable();
|
||||
await this.onLoad(this.page);
|
||||
if (rows.length === 1) {
|
||||
if (failCount) {
|
||||
await this.$alert(`同步失败,${lastFailReason}`, '同步结果', {
|
||||
@@ -635,7 +553,7 @@ export default {
|
||||
);
|
||||
this.paymentDialog.visible = false;
|
||||
this.selection = [];
|
||||
this.loadTable();
|
||||
this.onLoad(this.page);
|
||||
} finally {
|
||||
this.paymentDialog.loading = false;
|
||||
}
|
||||
@@ -685,15 +603,8 @@ export default {
|
||||
);
|
||||
},
|
||||
buildQueryParams() {
|
||||
// 所属组织级联返回 id 路径,转成组织名称传给后端(用副本,避免污染搜索框回显)
|
||||
const params = this.normalizeOrganizationSearch({ ...this.query }, 'deptName');
|
||||
const params = { ...this.query };
|
||||
params.settlementType = this.lockedSettlementType || this.activeSettlementType;
|
||||
const range = params.createDateRange || [];
|
||||
delete params.createDateRange;
|
||||
if (range.length === 2) {
|
||||
params.createStartDate = range[0];
|
||||
params.createEndDate = range[1];
|
||||
}
|
||||
return params;
|
||||
},
|
||||
unwrapData(response) {
|
||||
@@ -701,8 +612,8 @@ export default {
|
||||
return body?.data || body;
|
||||
},
|
||||
handleSettlementTypeChange() {
|
||||
this.page.current = 1;
|
||||
this.loadTable();
|
||||
// 分页重置与数据加载由 activeSettlementType watcher + 新挂载 crud 的 on-load 完成
|
||||
this.selection = [];
|
||||
},
|
||||
escapeHtml(value) {
|
||||
return String(value || '-').replace(
|
||||
@@ -710,24 +621,6 @@ export default {
|
||||
char => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char])
|
||||
);
|
||||
},
|
||||
isEditable(row) {
|
||||
return ['draft', 'returned'].includes(row.approvalStatus);
|
||||
},
|
||||
statusType(status) {
|
||||
return (
|
||||
{ approved: 'success', reviewing: 'warning', returned: 'danger', voided: 'info' }[status] ||
|
||||
''
|
||||
);
|
||||
},
|
||||
invoiceName(value, settlementType) {
|
||||
return invoiceStatusName(value, settlementType);
|
||||
},
|
||||
paymentName(value) {
|
||||
return paymentStatusOptions.find(item => item.value === value)?.label || value || '-';
|
||||
},
|
||||
displayValue(value) {
|
||||
return value === null || value === undefined || value === '' ? '-' : value;
|
||||
},
|
||||
formatMoney(value, currency = 'RMB') {
|
||||
return `${Number(value || 0).toFixed(2)} ${currency || 'RMB'}`;
|
||||
},
|
||||
@@ -740,62 +633,21 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.formal-page__search {
|
||||
padding: 12px 12px 4px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.formal-page__search-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 8px 24px;
|
||||
align-items: start;
|
||||
}
|
||||
.formal-page__search :deep(.el-form-item) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.formal-page__search :deep(.el-form-item__label) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.formal-page__search :deep(.el-input),
|
||||
.formal-page__search :deep(.el-select),
|
||||
.formal-page__search :deep(.el-date-editor) {
|
||||
width: 100%;
|
||||
}
|
||||
.formal-page__search-actions {
|
||||
grid-column: 1 / -1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.formal-page__table-panel {
|
||||
margin-top: 8px;
|
||||
.formal-page {
|
||||
:deep(.el-table) {
|
||||
--el-table-border-color: #eff1f7;
|
||||
}
|
||||
:deep(.el-table__body tr:nth-child(even) > td.el-table__cell),
|
||||
:deep(.el-table__body tr:nth-child(even) > td.el-table-fixed-column--left),
|
||||
:deep(.el-table__body tr:nth-child(even) > td.el-table-fixed-column--right) {
|
||||
background: #fafafa;
|
||||
}
|
||||
}
|
||||
.formal-payment-dialog__batch-tip {
|
||||
margin-bottom: 12px;
|
||||
color: #606266;
|
||||
}
|
||||
.formal-page :deep(.el-table) {
|
||||
--el-table-border-color: #eff1f7;
|
||||
}
|
||||
.formal-page :deep(.el-table__body tr:nth-child(even) > td.el-table__cell),
|
||||
.formal-page :deep(.el-table__body tr:nth-child(even) > td.el-table-fixed-column--left),
|
||||
.formal-page :deep(.el-table__body tr:nth-child(even) > td.el-table-fixed-column--right) {
|
||||
background: #fafafa;
|
||||
}
|
||||
:deep(.formal-page.basic-container .basic-container__card > .el-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
@media (max-width: 1200px) {
|
||||
.formal-page__search-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
@media (max-width: 760px) {
|
||||
.formal-page__search-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user