Merge branch 'refs/heads/feature/bfi-api-rate' into feature/hcs-api-p0-web

# Conflicts:
#	src/option/business/contract-manage.js
This commit is contained in:
刘泉佳
2026-10-15 04:13:05 +08:00
120 changed files with 10791 additions and 2542 deletions
+1 -1
View File
@@ -529,7 +529,7 @@ export default {
<style scoped lang="scss">
.bill-ledger-form-page__form :deep(.el-form-item) {
margin-bottom: 14px;
margin-bottom: 24px;
}
.bill-ledger-form-page__basic-grid {
display: grid;
+4
View File
@@ -1,6 +1,7 @@
<template>
<basic-container class="bill-ledger-page">
<bill-ledger-search
v-if="searchVisible"
:query="query"
:loading="loading"
@search="handleSearch"
@@ -20,10 +21,12 @@
:rows="rows"
:loading="loading"
:page="page"
v-model:search-visible="searchVisible"
@add="openCreate"
@view="openView"
@edit="openEdit"
@delete="handleDelete"
@refresh="loadData"
@page-change="handlePageChange"
@size-change="handleSizeChange"
/>
@@ -124,6 +127,7 @@ export default {
return {
query: emptyQuery(),
counts: { all: 0, within30: 0, within90: 0, over90: 0 },
searchVisible: true,
rows: [],
loading: false,
page: { current: 1, size: 10, total: 0 },
+1 -1
View File
@@ -367,7 +367,7 @@ export default {
<style scoped lang="scss">
.bill-payment-form-page__form :deep(.el-form-item) {
margin-bottom: 14px;
margin-bottom: 24px;
}
.bill-payment-form-page__form :deep(.el-input),
.bill-payment-form-page__form :deep(.el-input-number),
+4
View File
@@ -1,6 +1,7 @@
<template>
<basic-container class="bill-payment-page">
<bill-payment-search
v-if="searchVisible"
:query="query"
:loading="loading"
@search="handleSearch"
@@ -11,6 +12,7 @@
:loading="loading"
:page="page"
:mk-enabled="mkEnabled"
v-model:search-visible="searchVisible"
@add="openCreate"
@view="openView"
@edit="openEdit"
@@ -20,6 +22,7 @@
@approve="handleApprove"
@return="handleReturn"
@void="handleVoid"
@refresh="loadTable"
@page-change="handlePageChange"
@size-change="handleSizeChange"
/>
@@ -47,6 +50,7 @@ export default {
data() {
return {
query: emptyQuery(),
searchVisible: true,
rows: [],
loading: false,
page: { current: 1, size: 10, total: 0 },
@@ -52,12 +52,13 @@
<el-icon><Delete /></el-icon>
<span>重置</span>
</el-button>
<el-tooltip :content="expanded ? '折叠' : '展开'" placement="top">
<el-button text @click="expanded = !expanded">
<el-icon><component :is="expanded ? ArrowUp : ArrowDown" /></el-icon>
<span>{{ expanded ? '折叠' : '展开' }}</span>
</el-button>
</el-tooltip>
<el-button
v-if="showSearchToggle"
text
:icon="expanded ? ArrowUp : ArrowDown"
@click="expanded = !expanded"
>{{ expanded ? '收起' : '展开' }}</el-button
>
</div>
</el-form>
</section>
@@ -81,10 +82,18 @@ export default {
Delete,
Search,
expanded: false,
// 与 avue-crud 一致:搜索项数量超过 searchIndex(默认一行 4 项)时才显示“展开/收起”
searchIndex: 4,
searchItemCount: 6,
billTypeOptions,
maturityStatusOptions,
};
},
computed: {
showSearchToggle() {
return this.searchItemCount > this.searchIndex;
},
},
};
</script>
@@ -4,11 +4,19 @@
<el-button v-if="hasPermission('bill_ledger_add')" type="primary" @click="$emit('add')">
新增
</el-button>
<table-header-tools
:columns="columns"
v-model:visible-column-keys="visibleColumnKeys"
v-model:table-size="tableSize"
:search-visible="searchVisible"
@update:search-visible="$emit('update:searchVisible', $event)"
@refresh="$emit('refresh')"
/>
</div>
<el-table v-loading="loading" :data="rows" border>
<el-table v-loading="loading" :data="rows" :size="tableSize" border>
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
<el-table-column
v-for="column in columns"
v-for="column in visibleColumns"
:key="column.prop"
v-bind="column"
align="center"
@@ -60,20 +68,30 @@
<script>
import { mapGetters } from 'vuex';
import { billLedgerTableColumns } from '@/option/payment/billLedger';
import TableHeaderTools from '@/components/table-header-tools/main.vue';
export default {
name: 'BillLedgerTable',
components: { TableHeaderTools },
props: {
rows: { type: Array, default: () => [] },
loading: { type: Boolean, default: false },
page: { type: Object, required: true },
searchVisible: { type: Boolean, default: true },
},
emits: ['add', 'view', 'edit', 'delete', 'page-change', 'size-change'],
emits: ['add', 'view', 'edit', 'delete', 'page-change', 'size-change', 'refresh', 'update:searchVisible'],
data() {
return { columns: billLedgerTableColumns };
return {
columns: billLedgerTableColumns,
visibleColumnKeys: billLedgerTableColumns.map(column => column.prop),
tableSize: 'default',
};
},
computed: {
...mapGetters(['permission']),
visibleColumns() {
return this.columns.filter(column => this.visibleColumnKeys.includes(column.prop));
},
},
methods: {
hasPermission(code) {
@@ -96,7 +114,7 @@ export default {
.bill-ledger-table__toolbar {
display: flex;
align-items: center;
justify-content: flex-start;
justify-content: space-between;
padding: 12px 0;
}
.bill-ledger-table__links {
@@ -43,11 +43,13 @@
<div class="bill-payment-search__actions">
<el-button type="primary" :loading="loading" @click="$emit('search')">查询</el-button>
<el-button @click="$emit('reset')">重置</el-button>
<el-tooltip :content="expanded ? '折叠' : '展开'" placement="top">
<el-button text @click="expanded = !expanded">
<el-icon><component :is="expanded ? ArrowUp : ArrowDown" /></el-icon>
</el-button>
</el-tooltip>
<el-button
v-if="showSearchToggle"
text
:icon="expanded ? ArrowUp : ArrowDown"
@click="expanded = !expanded"
>{{ expanded ? '收起' : '展开' }}</el-button
>
</div>
</el-form>
</section>
@@ -67,7 +69,20 @@ export default {
},
emits: ['search', 'reset'],
data() {
return { ArrowDown, ArrowUp, expanded: false, approvalStatusOptions };
return {
ArrowDown,
ArrowUp,
expanded: false,
// 与 avue-crud 一致:搜索项数量超过 searchIndex(默认一行 4 项)时才显示“展开/收起”
searchIndex: 4,
searchItemCount: 4,
approvalStatusOptions,
};
},
computed: {
showSearchToggle() {
return this.searchItemCount > this.searchIndex;
},
},
mounted() {
this.loadOrganizationOptions();
@@ -4,11 +4,19 @@
<el-button v-if="hasPermission('bill_payment_add')" type="primary" @click="$emit('add')">
新增
</el-button>
<table-header-tools
:columns="columns"
v-model:visible-column-keys="visibleColumnKeys"
v-model:table-size="tableSize"
:search-visible="searchVisible"
@update:search-visible="$emit('update:searchVisible', $event)"
@refresh="$emit('refresh')"
/>
</div>
<el-table v-loading="loading" :data="rows" border>
<el-table v-loading="loading" :data="rows" :size="tableSize" border>
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
<el-table-column
v-for="column in columns"
v-for="column in visibleColumns"
:key="column.prop"
v-bind="column"
align="center"
@@ -61,7 +69,7 @@
<el-link
v-if="
mkEnabled &&
row.approvalStatus === 'reviewing' &&
['reviewing', 'approved'].includes(row.approvalStatus) &&
hasPermission('bill_payment_approve')
"
type="primary"
@@ -115,15 +123,18 @@
<script>
import { mapGetters } from 'vuex';
import { billPaymentTableColumns } from '@/option/payment/billPayment';
import TableHeaderTools from '@/components/table-header-tools/main.vue';
import { isAdministrator } from '@/utils/auth';
export default {
name: 'BillPaymentTable',
components: { TableHeaderTools },
props: {
rows: { type: Array, default: () => [] },
loading: { type: Boolean, default: false },
page: { type: Object, required: true },
mkEnabled: { type: Boolean, default: false },
searchVisible: { type: Boolean, default: true },
},
emits: [
'add',
@@ -137,12 +148,21 @@ export default {
'void',
'page-change',
'size-change',
'refresh',
'update:searchVisible',
],
data() {
return { columns: billPaymentTableColumns };
return {
columns: billPaymentTableColumns,
visibleColumnKeys: billPaymentTableColumns.map(column => column.prop),
tableSize: 'default',
};
},
computed: {
...mapGetters(['permission', 'userInfo']),
visibleColumns() {
return this.columns.filter(column => this.visibleColumnKeys.includes(column.prop));
},
isAdmin() {
return isAdministrator(this.userInfo);
},
@@ -173,7 +193,8 @@ export default {
}
.bill-payment-table__toolbar {
display: flex;
justify-content: flex-start;
align-items: center;
justify-content: space-between;
padding: 12px 0;
}
.bill-payment-table__links {
+77 -20
View File
@@ -1,6 +1,6 @@
<template>
<basic-container class="invoice-page">
<section class="invoice-page__search">
<section v-if="searchVisible" class="invoice-page__search">
<el-form :model="query" label-position="right" label-width="88px" @submit.prevent>
<div class="invoice-page__search-grid">
<el-form-item label="单据号">
@@ -45,11 +45,13 @@
<div class="invoice-page__search-actions">
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
<el-tooltip :content="searchExpanded ? '折叠' : '展开'" placement="top">
<el-button text @click="searchExpanded = !searchExpanded">
<el-icon><component :is="searchExpanded ? ArrowUp : ArrowDown" /></el-icon>
</el-button>
</el-tooltip>
<el-button
v-if="showSearchToggle"
text
:icon="searchExpanded ? ArrowUp : ArrowDown"
@click="searchExpanded = !searchExpanded"
>{{ searchExpanded ? '收起' : '展开' }}</el-button
>
</div>
</el-form>
</section>
@@ -79,18 +81,26 @@
>导出</el-button
>
</div>
<div class="invoice-page__toolbar-right">
<el-tooltip content="刷新" placement="top">
<el-button text :icon="Refresh" @click="loadTable" />
</el-tooltip>
</div>
<table-header-tools
:columns="columns"
v-model:visible-column-keys="visibleColumnKeys"
v-model:table-size="tableSize"
v-model:search-visible="searchVisible"
@refresh="loadTable"
/>
</div>
<el-table v-loading="loading" :data="rows" border @selection-change="selection = $event">
<el-table
v-loading="loading"
:data="rows"
:size="tableSize"
border
@selection-change="selection = $event"
>
<el-table-column type="selection" width="52" fixed="left" align="center" />
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
<el-table-column
v-for="column in columns"
v-for="column in visibleColumns"
:key="column.prop"
v-bind="column"
align="center"
@@ -115,6 +125,12 @@
{{ displayValue(row[column.prop]) }}
</el-tag>
<span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
<template v-else-if="column.prop === 'kingdeeLockStatus'">
<el-tooltip v-if="lockTag(row).show" :content="lockTag(row).tip" placement="top">
<el-tag :type="lockTag(row).type" class="status-text">{{ lockTag(row).label }}</el-tag>
</el-tooltip>
<span v-else>-</span>
</template>
<span v-else>{{ displayValue(row[column.prop]) }}</span>
</template>
</el-table-column>
@@ -155,7 +171,7 @@
v-if="
mkEnabled &&
hasPermission('invoice_application_approve') &&
row.approvalStatus === 'reviewing'
['reviewing', 'approved'].includes(row.approvalStatus)
"
type="primary"
@click="handleMkAudit(row)"
@@ -183,7 +199,9 @@
>
<el-link
v-if="
hasPermission('invoice_application_void') && row.approvalStatus === 'approved'
hasPermission('invoice_application_void') &&
row.approvalStatus === 'approved' &&
row.kingdeeLockStatus !== 'locked'
"
type="danger"
@click="handleVoid(row)"
@@ -210,12 +228,13 @@
</template>
<script>
import { ArrowDown, ArrowUp, Refresh } from '@element-plus/icons-vue';
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import * as XLSX from 'xlsx';
import * as api from '@/api/payment/invoiceApplication';
import * as formalSettlementApi from '@/api/settlement/formalSettlement';
import { invoiceApplicationTableColumns } from '@/option/payment/invoiceApplication';
import TableHeaderTools from '@/components/table-header-tools/main.vue';
import organizationSearch from '@/mixins/organization-search';
import { getMKApprovalUrl } from '@/api/system/business-process';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
@@ -231,17 +250,23 @@ const emptyQuery = () => ({
export default {
name: 'InvoiceApplication',
components: { TableHeaderTools },
mixins: [organizationSearch],
data() {
return {
ArrowDown,
ArrowUp,
Refresh,
query: emptyQuery(),
searchExpanded: false,
// 与 avue-crud 一致:搜索项数量超过 searchIndex(默认一行 4 项)时才显示“展开/收起”
searchIndex: 4,
searchItemCount: 5,
searchVisible: true,
tableSize: 'default',
approvalStatusOptions: api.approvalStatusOptions,
kingdeeStatusOptions: api.kingdeeStatusOptions,
columns: invoiceApplicationTableColumns,
visibleColumnKeys: invoiceApplicationTableColumns.map(column => column.prop),
rows: [],
selection: [],
loading: false,
@@ -252,6 +277,12 @@ export default {
},
computed: {
...mapGetters(['permission']),
visibleColumns() {
return this.columns.filter(column => this.visibleColumnKeys.includes(column.prop));
},
showSearchToggle() {
return this.searchItemCount > this.searchIndex;
},
},
mounted() {
this.loadOrganizationOptions();
@@ -377,8 +408,13 @@ export default {
},
async handleApprove(row) {
await this.$confirm('确认审批通过该开票申请?', '提示', { type: 'warning' });
await api.approve({ id: row.id });
this.$message.success('审批通过');
const res = await api.approve({ id: row.id });
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
if (res.data.data === false) {
this.$message.warning(res.data.msg);
} else {
this.$message.success(res.data.msg || '审批通过');
}
this.loadTable();
},
async handleReturn(row) {
@@ -448,6 +484,28 @@ export default {
''
);
},
lockTag(row) {
if (row.kingdeeLockStatus === 'locked') {
return { show: true, type: 'danger', label: '金蝶锁定中', tip: '该单据已被金蝶锁定,不可编辑/作废' };
}
if (row.kingdeeLockStatus === 'failed') {
if ((row.kingdeeTryCount || 0) >= 5) {
return {
show: true,
type: 'danger',
label: '锁定失败·已达上限',
tip: row.kingdeeLockFailReason || '自动补偿次数已用完,请重推「同步金蝶」立即补锁',
};
}
return {
show: true,
type: 'warning',
label: '锁定失败·补偿中',
tip: row.kingdeeLockFailReason || '系统将自动补偿重锁',
};
}
return { show: false };
},
displayValue(value) {
return value === null || value === undefined || value === '' ? '-' : value;
},
@@ -496,7 +554,6 @@ export default {
padding: 12px 0;
}
.invoice-page__toolbar-left,
.invoice-page__toolbar-right,
.invoice-page__links {
display: flex;
align-items: center;
+1 -1
View File
@@ -610,7 +610,7 @@ export default {
padding-bottom: 72px;
}
.receipt-form-page__form :deep(.el-form-item) {
margin-bottom: 14px;
margin-bottom: 24px;
}
.receipt-form-page__form :deep(.el-select),
.receipt-form-page__form :deep(.el-input-number) {
+46 -19
View File
@@ -1,6 +1,6 @@
<template>
<basic-container class="receipt-page">
<section class="receipt-page__search">
<section v-if="searchVisible" class="receipt-page__search">
<el-form :model="query" label-position="right" label-width="88px" @submit.prevent>
<div class="receipt-page__search-grid">
<el-form-item label="发票号码">
@@ -54,11 +54,13 @@
<div class="receipt-page__search-actions">
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
<el-tooltip :content="searchExpanded ? '折叠' : '展开'" placement="top">
<el-button text @click="searchExpanded = !searchExpanded">
<el-icon><component :is="searchExpanded ? ArrowUp : ArrowDown" /></el-icon>
</el-button>
</el-tooltip>
<el-button
v-if="showSearchToggle"
text
:icon="searchExpanded ? ArrowUp : ArrowDown"
@click="searchExpanded = !searchExpanded"
>{{ searchExpanded ? '收起' : '展开' }}</el-button
>
</div>
</el-form>
</section>
@@ -85,18 +87,26 @@
>导出</el-button
>
</div>
<div class="receipt-page__toolbar-right">
<el-tooltip content="刷新" placement="top">
<el-button text :icon="Refresh" @click="loadTable" />
</el-tooltip>
</div>
<table-header-tools
:columns="columns"
v-model:visible-column-keys="visibleColumnKeys"
v-model:table-size="tableSize"
v-model:search-visible="searchVisible"
@refresh="loadTable"
/>
</div>
<el-table v-loading="loading" :data="rows" border @selection-change="selection = $event">
<el-table
v-loading="loading"
:data="rows"
:size="tableSize"
border
@selection-change="selection = $event"
>
<el-table-column type="selection" width="52" fixed="left" align="center" />
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
<el-table-column
v-for="column in columns"
v-for="column in visibleColumns"
:key="column.prop"
v-bind="column"
align="center"
@@ -150,7 +160,7 @@
v-if="
mkEnabled &&
hasPermission('invoice_receipt_approve') &&
row.approvalStatus === 'reviewing'
['reviewing', 'approved'].includes(row.approvalStatus)
"
type="primary"
@click="handleMkAudit(row)"
@@ -203,11 +213,12 @@
</template>
<script>
import { ArrowDown, ArrowUp, Refresh } from '@element-plus/icons-vue';
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import * as XLSX from 'xlsx';
import * as api from '@/api/payment/invoiceReceipt';
import { invoiceReceiptTableColumns } from '@/option/payment/invoiceReceipt';
import TableHeaderTools from '@/components/table-header-tools/main.vue';
import organizationSearch from '@/mixins/organization-search';
import { getMKApprovalUrl } from '@/api/system/business-process';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
@@ -224,17 +235,23 @@ const emptyQuery = () => ({
export default {
name: 'InvoiceReceipt',
components: { TableHeaderTools },
mixins: [organizationSearch],
data() {
return {
ArrowDown,
ArrowUp,
Refresh,
query: emptyQuery(),
searchExpanded: false,
// 与 avue-crud 一致:搜索项数量超过 searchIndex(默认一行 4 项)时才显示“展开/收起”
searchIndex: 4,
searchItemCount: 6,
searchVisible: true,
tableSize: 'default',
approvalStatusOptions: api.approvalStatusOptions,
kingdeePushStatusOptions: api.kingdeePushStatusOptions,
columns: invoiceReceiptTableColumns,
visibleColumnKeys: invoiceReceiptTableColumns.map(column => column.prop),
rows: [],
selection: [],
loading: false,
@@ -245,6 +262,12 @@ export default {
},
computed: {
...mapGetters(['permission']),
visibleColumns() {
return this.columns.filter(column => this.visibleColumnKeys.includes(column.prop));
},
showSearchToggle() {
return this.searchItemCount > this.searchIndex;
},
},
mounted() {
this.loadOrganizationOptions();
@@ -342,8 +365,13 @@ export default {
},
async handleApprove(row) {
await this.$confirm('确认审批通过该收票登记?', '提示', { type: 'warning' });
await api.approve({ id: row.id });
this.$message.success('审批通过');
const res = await api.approve({ id: row.id });
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
if (res.data.data === false) {
this.$message.warning(res.data.msg);
} else {
this.$message.success(res.data.msg || '审批通过');
}
this.loadTable();
},
async handleReturn(row) {
@@ -456,7 +484,6 @@ export default {
padding: 12px 0;
}
.receipt-page__toolbar-left,
.receipt-page__toolbar-right,
.receipt-page__links {
display: flex;
align-items: center;
+79 -72
View File
@@ -36,23 +36,25 @@
<el-col v-if="isSettlementRelatedType" :span="6"
><el-form-item label="结算单号" prop="referenceId">
<div class="payment-form-page__settlement-nos">
<template v-if="referenceSettlementLinks.length">
<el-link
<el-select
ref="settlementNoSelectRef"
:model-value="referenceSettlementNos"
multiple
collapse-tags
collapse-tags-tooltip
:max-collapse-tags="1"
:disabled="readonly"
placeholder="请输入"
class="payment-form-page__settlement-select"
@visible-change="handleSettlementSelectVisible"
>
<el-option
v-for="item in referenceSettlementLinks"
:key="`${item.id || item.settlementNo}`"
type="primary"
@click="openSettlementDetail(item)"
>
{{ item.settlementNo }}
</el-link>
</template>
<el-input
v-else
v-model="referenceLabel"
readonly
:disabled="readonly"
placeholder="请选择预结算/正式结算单"
/>
:label="item.settlementNo"
:value="item.settlementNo"
/>
</el-select>
<el-button
v-if="!readonly"
class="payment-form-page__settlement-select-btn"
@@ -100,7 +102,7 @@
<el-option
v-for="item in contractOptions"
:key="item.id"
:label="item.contractName || item.contractNo"
:label="formatContractOptionLabel(item)"
:value="item.id"
/>
</el-select>
@@ -256,7 +258,7 @@
</el-table-column>
</el-table>
</section-card>
<section-card v-if="isSettlementRelatedType" title="发票信息">
<section-card v-if="showInvoiceSection" title="发票信息">
<el-table :data="form.invoices" border>
<el-table-column type="index" label="序号" width="70" />
<el-table-column prop="settlementNo" label="结算单号" min-width="120">
@@ -360,19 +362,22 @@
value="special_approval" /><el-option
label="其他"
value="other" /></el-select></template></el-table-column
><el-table-column label="文件名" min-width="220"
><el-table-column
label="文件名"
min-width="220"
show-overflow-tooltip
class-name="is-ellipsis-link"
><template #default="{ row }"
><div
class="payment-form-page__attachment-file-name-cell"
:title="attachmentFileName(row) || '-'"
>
<span
class="payment-form-page__attachment-file-name"
:class="{ 'is-disabled': !attachmentFileUrl(row) }"
@click="attachmentFileUrl(row) && previewAttachment(row)"
>{{ attachmentFileName(row) || '-' }}</span
>
</div></template
><span
class="attachment-file-name"
:class="{ 'is-disabled': !attachmentFileUrl(row) }"
role="button"
tabindex="0"
@click="attachmentFileUrl(row) && previewAttachment(row)"
@keydown.enter="attachmentFileUrl(row) && previewAttachment(row)"
@keydown.space.prevent="attachmentFileUrl(row) && previewAttachment(row)"
>{{ attachmentFileName(row) || '-' }}</span
></template
></el-table-column
><el-table-column label="附件描述" min-width="220"
><template #default="{ row }"
@@ -448,7 +453,7 @@
></el-table
>
<div v-else>
<div class="payment-form-page__reference-search">
<el-card class="payment-form-page__reference-search">
<el-form
:model="referenceQuery"
label-position="right"
@@ -486,7 +491,7 @@
<el-button @click="resetReferenceSearch">清空</el-button>
</div>
</el-form>
</div>
</el-card>
<el-tabs v-model="referenceTab" @tab-change="handleReferenceTabChange">
<el-tab-pane v-if="canSelectFormalSettlement" label="正式结算单" name="formal">
<el-table
@@ -677,6 +682,7 @@ import {
getList as getCustomerArchiveList,
} from '@/api/vehicle/customer-archive';
import { readSettlementTransfer, removeSettlementTransfer } from '@/utils/settlement-transfer';
import { formatContractOptionLabel } from '@/utils/contract';
import { downloadFileByUrl } from '@/utils/util';
import { submitMkApprovalFlow } from '@/utils/mk-approval';
import {
@@ -898,6 +904,10 @@ export default {
isSettlementRelatedType() {
return ['progress_advance', 'settlement_payment'].includes(this.form.paymentType);
},
// 项目预付、进度预付不展示发票信息,仅尾款付款展示
showInvoiceSection() {
return this.form.paymentType === 'settlement_payment';
},
canSelectPreSettlement() {
// 进度预付仅预结算;尾款付款可正式/预结算
return ['progress_advance', 'settlement_payment'].includes(this.form.paymentType);
@@ -950,6 +960,9 @@ export default {
referenceType: this.form.preSettlementId ? 'pre' : 'formal',
}));
},
referenceSettlementNos() {
return this.referenceSettlementLinks.map(item => item.settlementNo).filter(Boolean);
},
settlementRows() {
if (this.referenceRows.length) {
return this.referenceRows.map(item => ({
@@ -1060,6 +1073,7 @@ export default {
this.initialize();
},
methods: {
formatContractOptionLabel,
hasPermission(code) {
return this.permission?.[code] !== false;
},
@@ -1083,7 +1097,7 @@ export default {
!this.form.preSettlementId &&
!this.form.preSettlementIds?.length
) {
callback(new Error('请选择结算单'));
callback(new Error('请输入'));
return;
}
callback();
@@ -1144,7 +1158,7 @@ export default {
callback();
},
validateInvoices() {
if (this.form.paymentType === 'project_advance') return;
if (!this.showInvoiceSection) return;
for (const invoice of this.form.invoices || []) {
if (String(invoice.settlementNo || '').length > 100) {
throw new Error('结算单号不能超过100个字符');
@@ -1926,6 +1940,7 @@ export default {
this.contractPaymentRatioExceeded = exceeded;
},
handleTypeChange() {
// 切换类型时清空发票;预付类型本身也不再展示/提交发票
this.form.invoices = [];
this.paymentRecords = [];
Object.assign(this.form, {
@@ -2130,8 +2145,20 @@ export default {
const project = this.projectOptions.find(item => String(item.id) === String(projectId));
this.form.projectName = project?.projectName || project?.projectShortName || '';
Object.assign(this.form, {
deptId: project?.undertakeDeptId || project?.businessDeptId || null,
deptName: project?.undertakeDeptName || project?.businessDeptName || '',
deptId:
String(project?.undertakeDeptId || '')
.split(/[,,]/)
.map(item => item.trim())
.find(Boolean) ||
project?.businessDeptId ||
null,
deptName:
String(project?.undertakeDeptName || '')
.split(/[,,、]/)
.map(item => item.trim())
.find(Boolean) ||
project?.businessDeptName ||
'',
contractId: null,
contractNo: '',
contractName: '',
@@ -2299,6 +2326,13 @@ export default {
}
return Promise.all(loaders);
},
handleSettlementSelectVisible(visible) {
// 结算单号仅做展示,通过「选择」按钮维护,避免下拉改值
if (!visible) return;
this.$nextTick(() => {
this.$refs.settlementNoSelectRef?.blur?.();
});
},
handleReferenceSearch() {
this.formalPage.current = 1;
this.prePage.current = 1;
@@ -2662,7 +2696,7 @@ export default {
attachmentsJson: JSON.stringify(attachments || []),
remark,
invoices:
paymentType === 'project_advance'
paymentType === 'project_advance' || paymentType === 'progress_advance'
? []
: invoices.map(invoice => ({
settlementNo: invoice.settlementNo || '',
@@ -2772,13 +2806,18 @@ export default {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
width: 100%;
min-height: 32px;
}
.payment-form-page__settlement-nos :deep(.el-input) {
.payment-form-page__settlement-select {
flex: 1;
min-width: 120px;
min-width: 0;
}
.payment-form-page__settlement-select :deep(.el-select__wrapper) {
width: 100%;
}
.payment-form-page__settlement-select :deep(.el-tag__close) {
display: none;
}
.payment-form-page__settlement-select-btn {
flex-shrink: 0;
@@ -2855,38 +2894,6 @@ export default {
justify-content: center;
gap: 8px;
}
.payment-form-page__attachment-file-name-cell {
display: block;
width: 100%;
min-width: 0;
overflow: hidden;
text-align: left;
text-overflow: ellipsis;
white-space: nowrap;
}
.payment-form-page__attachment-file-name {
display: block;
width: 100%;
overflow: hidden;
color: var(--el-text-color-regular);
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
transition: color 0.2s ease;
&:hover {
color: #409eff;
}
&.is-disabled {
color: var(--el-text-color-regular);
cursor: default;
&:hover {
color: var(--el-text-color-regular);
}
}
}
.payment-form-page__attachment-upload .vehicle-attachment-upload {
width: auto;
}
@@ -2894,7 +2901,7 @@ export default {
display: inline-flex;
}
.payment-form-page :deep(.el-form-item) {
margin-bottom: 14px;
margin-bottom: 24px;
}
.payment-form-page :deep(.el-input-number .el-input__inner) {
text-align: left;
+241 -352
View File
@@ -1,289 +1,190 @@
<template>
<basic-container class="payment-page">
<section class="payment-page__search">
<el-form :model="query" label-position="right" label-width="88px">
<div class="payment-page__search-grid">
<el-form-item label="单据号"
><el-input v-model="query.paymentNo" clearable
/></el-form-item>
<el-form-item label="日期"
><el-date-picker
v-model="query.applyDateRange"
type="daterange"
value-format="YYYY-MM-DD"
range-separator="~"
start-placeholder="年/月/日"
end-placeholder="年/月/日"
/></el-form-item>
<el-form-item label="客户名称"
><el-input v-model="query.payeeName" clearable
/></el-form-item>
<el-form-item label="项目名称"
><el-input v-model="query.projectName" clearable
/></el-form-item>
<template v-if="searchExpanded">
<el-form-item label="所属组织"
><el-cascader
v-model="query.deptPath"
:options="organizationTreeOptions"
:props="organizationCascaderProps"
filterable
clearable
style="width: 100%"
placeholder="请选择 所属组织"
@change="val => { query.deptName = findOrgById(val)?.rawLabel || '' }"
/></el-form-item>
<el-form-item label="关联结算单"
><el-input v-model="query.settlementNo" clearable
/></el-form-item>
<el-form-item label="付款类型"
><el-select v-model="query.paymentType" clearable
><el-option
v-for="item in paymentTypeOptions"
:key="item.value"
v-bind="item" /></el-select
></el-form-item>
<el-form-item label="审核状态"
><el-select v-model="query.approvalStatus" clearable
><el-option
v-for="item in approvalStatusOptions"
:key="item.value"
v-bind="item" /></el-select
></el-form-item>
<el-form-item label="金蝶单据状态"
><el-select v-model="query.kingdeeStatus" clearable
><el-option
v-for="item in kingdeeStatusOptions"
:key="item.value"
v-bind="item" /></el-select
></el-form-item>
</template>
</div>
<div class="payment-page__search-actions">
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
<el-button text @click="searchExpanded = !searchExpanded"
><el-icon><component :is="searchExpanded ? ArrowUp : ArrowDown" /></el-icon
></el-button>
</div>
</el-form>
</section>
<section class="payment-page__table-panel">
<div class="payment-page__toolbar">
<div class="payment-page__toolbar-left">
<el-button
v-if="hasPermission('payment_application_add')"
type="primary"
@click="openCreate"
>新增付款申请</el-button
>
<el-button
v-if="hasPermission('payment_application_sync')"
type="primary"
plain
:loading="syncLoading"
@click="handleSync"
>批量同步</el-button
>
<el-button
v-if="hasPermission('payment_application_sync')"
type="primary"
plain
:loading="resultSyncing"
@click="handleSyncResult"
>同步付款结果</el-button
>
<el-button
v-if="hasPermission('payment_application_add')"
type="primary"
plain
@click="openSettlementPayment"
>申请付尾款</el-button
>
<el-button
v-if="hasPermission('payment_application_add')"
type="primary"
plain
@click="openProjectAdvance"
>预付申请</el-button
>
<el-button
v-if="hasPermission('payment_application_export')"
type="primary"
plain
@click="handleExport"
>导出</el-button
>
</div>
<el-button text :icon="Refresh" @click="loadTable" />
</div>
<el-table v-loading="loading" :data="rows" border @selection-change="selection = $event">
<el-table-column type="selection" width="52" fixed="left" align="center" />
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
<el-table-column
v-for="column in columns"
:key="column.prop"
v-bind="column"
align="center"
show-overflow-tooltip
<avue-crud
ref="crud"
:option="option"
:table-loading="loading"
:data="rows"
v-model:page="page"
@search-change="searchChange"
@search-reset="searchReset"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@refresh-change="refreshChange"
@on-load="onLoad"
>
<template #menu-left>
<el-button
v-if="hasPermission('payment_application_add')"
type="primary"
@click="openCreate"
>新增付款申请</el-button
>
<template #default="{ row }">
<el-link v-if="column.link" type="primary" @click="openView(row)">{{
row[column.prop]
}}</el-link>
<el-tag
v-else-if="column.status"
:type="statusType(row.approvalStatus)"
class="status-text"
>{{ row[column.prop] || '-' }}</el-tag
>
<span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
<span v-else>{{ displayValue(row[column.prop]) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="320" fixed="right" align="center">
<template #default="{ row }"
><div class="payment-page__links">
<el-link
v-if="hasPermission('payment_application_view')"
type="primary"
@click="openView(row)"
>查看</el-link
>
<el-link
v-if="
hasPermission('payment_application_edit') &&
['draft', 'returned'].includes(row.approvalStatus)
"
type="primary"
@click="openEdit(row)"
>编辑</el-link
>
<el-link
v-if="
hasPermission('payment_application_submit') &&
['draft', 'returned'].includes(row.approvalStatus)
"
type="primary"
@click="handleSubmit(row)"
>提交</el-link
>
<el-link
v-if="
mkEnabled &&
hasPermission('payment_application_approve') &&
row.approvalStatus === 'reviewing'
"
type="primary"
@click="handleMkAudit(row)"
>流程</el-link
>
<el-link
v-if="
!mkEnabled &&
hasPermission('payment_application_approve') &&
row.approvalStatus === 'reviewing'
"
type="primary"
@click="handleApprove(row)"
>通过</el-link
>
<el-link
v-if="
!mkEnabled &&
hasPermission('payment_application_approve') &&
row.approvalStatus === 'reviewing'
"
type="danger"
@click="handleReturn(row)"
>驳回</el-link
>
<el-link
v-if="
hasPermission('payment_application_void') && row.approvalStatus === 'approved'
"
type="danger"
@click="handleVoid(row)"
>作废</el-link
>
<el-link
v-if="hasPermission('payment_application_delete') && row.approvalStatus === 'draft'"
type="danger"
@click="handleDelete(row)"
>删除</el-link
>
</div></template
>
</el-table-column>
</el-table>
<div class="payment-page__pagination">
<el-pagination
v-model:current-page="page.current"
v-model:page-size="page.size"
:total="page.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadTable"
@size-change="handleSizeChange"
/>
</div>
</section>
<el-button
v-if="hasPermission('payment_application_sync')"
type="primary"
plain
@click="handleSync"
>批量同步</el-button
>
<el-button
v-if="hasPermission('payment_application_add')"
type="primary"
plain
@click="openSettlementPayment"
>申请付尾款</el-button
>
<el-button
v-if="hasPermission('payment_application_add')"
type="primary"
plain
@click="openProjectAdvance"
>预付申请</el-button
>
<el-button
v-if="hasPermission('payment_application_export')"
type="primary"
plain
@click="handleExport"
>导出</el-button
>
</template>
<template #paymentNo="{ row }">
<el-link type="primary" @click="openView(row)">{{ row.paymentNo }}</el-link>
</template>
<template #approvalStatusName="{ row }">
<el-tag :type="statusType(row.approvalStatus)" class="status-text">{{
row.approvalStatusName || '-'
}}</el-tag>
</template>
<template #kingdeeLockStatus="{ row }">
<el-tooltip v-if="lockTag(row).show" :content="lockTag(row).tip" placement="top">
<el-tag :type="lockTag(row).type" class="status-text">{{ lockTag(row).label }}</el-tag>
</el-tooltip>
<span v-else>-</span>
</template>
<template #menu="{ row }">
<el-link
v-if="hasPermission('payment_application_view')"
type="primary"
@click="openView(row)"
>查看</el-link
>
<el-link
v-if="
hasPermission('payment_application_edit') &&
['draft', 'returned'].includes(row.approvalStatus)
"
type="primary"
@click="openEdit(row)"
>编辑</el-link
>
<el-link
v-if="
hasPermission('payment_application_submit') &&
['draft', 'returned'].includes(row.approvalStatus)
"
type="primary"
@click="handleSubmit(row)"
>提交</el-link
>
<el-link
v-if="
mkEnabled &&
hasPermission('payment_application_approve') &&
['reviewing', 'approved'].includes(row.approvalStatus)
"
type="primary"
@click="handleMkAudit(row)"
>流程</el-link
>
<el-link
v-if="
!mkEnabled &&
hasPermission('payment_application_approve') &&
row.approvalStatus === 'reviewing'
"
type="primary"
@click="handleApprove(row)"
>通过</el-link
>
<el-link
v-if="
!mkEnabled &&
hasPermission('payment_application_approve') &&
row.approvalStatus === 'reviewing'
"
type="danger"
@click="handleReturn(row)"
>驳回</el-link
>
<el-link
v-if="
hasPermission('payment_application_void') &&
row.approvalStatus === 'approved' &&
row.kingdeeLockStatus !== 'locked'
"
type="danger"
@click="handleVoid(row)"
>作废</el-link
>
<el-link
v-if="hasPermission('payment_application_delete') && row.approvalStatus === 'draft'"
type="danger"
@click="handleDelete(row)"
>删除</el-link
>
</template>
</avue-crud>
</basic-container>
</template>
<script>
import { ArrowDown, ArrowUp, Refresh } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import * as api from '@/api/payment/paymentApplication';
import { paymentApplicationTableColumns } from '@/option/payment/paymentApplication';
import { paymentApplicationOption } from '@/option/payment/paymentApplication';
import organizationSearch from '@/mixins/organization-search';
import { getMKApprovalUrl } from '@/api/system/business-process';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
const emptyQuery = () => ({
paymentNo: '',
applyDateRange: [],
payeeName: '',
projectName: '',
deptName: '',
deptPath: [],
settlementNo: '',
paymentType: '',
approvalStatus: '',
kingdeeStatus: '',
});
export default {
name: 'PaymentApplication',
mixins: [organizationSearch],
data() {
return {
ArrowDown,
ArrowUp,
Refresh,
query: emptyQuery(),
searchExpanded: false,
paymentTypeOptions: api.paymentTypeOptions,
approvalStatusOptions: api.approvalStatusOptions,
kingdeeStatusOptions: api.kingdeeStatusOptions,
columns: paymentApplicationTableColumns,
rows: [],
selection: [],
loading: false,
syncLoading: false,
resultSyncing: false,
page: { current: 1, size: 10, total: 0 },
query: {},
option: {},
page: {
currentPage: 1,
pageSize: 10,
pageSizes: [10, 20, 50, 100],
total: 0,
},
mkEnabled: false,
};
},
computed: {
...mapGetters(['permission']),
visibleSearchFields() {
return this.searchExpanded ? this.columns : this.columns.slice(0, 4);
},
},
created() {
this.option = paymentApplicationOption({
paymentTypeOptions: api.paymentTypeOptions,
approvalStatusOptions: api.approvalStatusOptions,
kingdeeStatusOptions: api.kingdeeStatusOptions,
});
this.loadOrganizationOptions();
},
mounted() {
this.loadOrganizationOptions();
this.loadTable();
loadMkEnabled()
.then(enabled => {
this.mkEnabled = !!enabled;
@@ -300,39 +201,52 @@ export default {
const body = response?.data || response || {};
return body?.data || body;
},
buildParams() {
const params = { ...this.query };
onLoad(page) {
this.loading = true;
api
.getList(page.currentPage, page.pageSize, { ...this.query })
.then(res => {
const data = this.unwrapData(res);
this.rows = data.records || [];
this.page.total = Number(data.total || 0);
})
.finally(() => {
this.loading = false;
this.selection = [];
this.$refs.crud?.toggleSelection?.();
});
},
searchChange(params, done) {
// 所属组织级联返回 id 路径数组,转成部门名称给后端
this.normalizeOrganizationSearch(params, 'deptName');
// 日期区间拆成后端需要的起止字段
const range = params.applyDateRange || [];
delete params.applyDateRange;
if (range.length === 2) {
params.applyStartDate = range[0];
params.applyEndDate = range[1];
}
return params;
this.query = {
...params,
applyStartDate: range[0] || '',
applyEndDate: range[1] || '',
};
this.page.currentPage = 1;
this.onLoad(this.page);
done();
},
async loadTable() {
this.loading = true;
try {
const data = this.unwrapData(
await api.getList(this.page.current, this.page.size, this.buildParams())
);
this.rows = data.records || [];
this.page.total = data.total || 0;
} finally {
this.loading = false;
}
searchReset() {
this.query = {};
this.page.currentPage = 1;
this.onLoad(this.page);
},
handleSearch() {
this.page.current = 1;
this.loadTable();
currentChange(currentPage) {
this.page.currentPage = currentPage;
},
resetSearch() {
this.query = emptyQuery();
this.handleSearch();
sizeChange(pageSize) {
this.page.pageSize = pageSize;
},
handleSizeChange() {
this.page.current = 1;
this.loadTable();
refreshChange() {
this.onLoad(this.page);
},
selectionChange(list) {
this.selection = list;
},
openCreate() {
this.$router.push({ path: '/payment/payment-application/form', query: { mode: 'add' } });
@@ -372,7 +286,7 @@ export default {
await this.$confirm('确认删除该付款申请?', '提示', { type: 'warning' });
await api.remove(row.id);
this.$message.success('删除成功');
this.loadTable();
this.refreshChange();
},
async handleSubmit(row) {
const previousApprovalStatus = row.approvalStatus || '';
@@ -384,12 +298,17 @@ export default {
approvalStatus: previousApprovalStatus,
});
this.$message.success('提交成功');
this.loadTable();
this.refreshChange();
},
async handleApprove(row) {
await api.approve({ id: row.id });
this.$message.success('审批通过');
this.loadTable();
const res = await api.approve({ id: row.id });
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
if (res.data.data === false) {
this.$message.warning(res.data.msg);
} else {
this.$message.success(res.data.msg || '审批通过');
}
this.refreshChange();
},
handleMkAudit(row) {
if (!row?.id) {
@@ -413,18 +332,18 @@ export default {
const { value } = await this.$prompt('请输入驳回原因', '审批驳回');
await api.returnBill({ id: row.id, reason: value });
this.$message.success('已驳回');
this.loadTable();
this.refreshChange();
},
async handleVoid(row) {
const { value } = await this.$prompt('请输入作废原因', '作废付款申请');
await api.voidBill({ id: row.id, reason: value });
this.$message.success('作废成功');
this.loadTable();
this.refreshChange();
},
async handleSyncRow(row) {
const data = this.unwrapData(await api.syncKingdee(row.id));
this.$message.success(`已生成金蝶付款单:${data}`);
await this.loadTable();
await this.refreshChange();
this.warnAttachmentNotUploaded([row.id]);
},
async handleSync() {
@@ -447,7 +366,7 @@ export default {
} else {
this.$message.success(`已同步${data.length}条付款申请`);
}
await this.loadTable();
await this.refreshChange();
this.warnAttachmentNotUploaded(rows.map(row => row.id));
} finally {
this.syncLoading = false;
@@ -477,9 +396,15 @@ export default {
this.resultSyncing = false;
}
},
handleExport() {
const headers = this.columns.map(item => item.label);
const rows = this.rows.map(row => this.columns.map(item => row[item.prop] || ''));
const columns = this.option.column.filter(column => !column.hide && column.prop);
const headers = columns.map(item => item.label);
const rows = this.rows.map(row =>
columns.map(item =>
row[item.prop] === null || row[item.prop] === undefined ? '' : row[item.prop]
)
);
const csv = [headers, ...rows]
.map(item => item.map(value => `"${String(value).replaceAll('"', '""')}"`).join(','))
.join('\n');
@@ -496,6 +421,28 @@ export default {
''
);
},
lockTag(row) {
if (row.kingdeeLockStatus === 'locked') {
return { show: true, type: 'danger', label: '金蝶锁定中', tip: '该单据已被金蝶锁定,不可编辑/作废' };
}
if (row.kingdeeLockStatus === 'failed') {
if ((row.kingdeeTryCount || 0) >= 5) {
return {
show: true,
type: 'danger',
label: '锁定失败·已达上限',
tip: row.kingdeeLockFailReason || '自动补偿次数已用完,请重推「同步金蝶」立即补锁',
};
}
return {
show: true,
type: 'warning',
label: '锁定失败·补偿中',
tip: row.kingdeeLockFailReason || '系统将自动补偿重锁',
};
}
return { show: false };
},
displayValue(value) {
return value === null || value === undefined || value === '' ? '-' : value;
},
@@ -507,54 +454,6 @@ export default {
</script>
<style scoped lang="scss">
.payment-page__search {
padding: 12px 12px 4px;
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.payment-page__search-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 8px 24px;
}
.payment-page__search :deep(.el-form-item) {
margin-bottom: 8px;
}
.payment-page__search :deep(.el-input),
.payment-page__search :deep(.el-select),
.payment-page__search :deep(.el-date-editor) {
width: 100%;
}
.payment-page__search-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-bottom: 8px;
}
.payment-page__table-panel {
margin-top: 8px;
}
.payment-page__toolbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
}
.payment-page__toolbar-left {
display: flex;
gap: 8px;
}
.payment-page__links {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
}
.payment-page__pagination {
display: flex;
justify-content: flex-end;
margin-top: 12px;
}
.payment-page :deep(.el-table) {
--el-table-border-color: #eff1f7;
}
@@ -564,14 +463,4 @@ export default {
:deep(.payment-page.basic-container .basic-container__card > .el-card__body) {
padding: 0;
}
@media (max-width: 1200px) {
.payment-page__search-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 760px) {
.payment-page__search-grid {
grid-template-columns: 1fr;
}
}
</style>
+32 -11
View File
@@ -1,6 +1,6 @@
<template>
<basic-container class="claim-record-page">
<section-card class="claim-record-page__search">
<section-card v-if="searchVisible" class="claim-record-page__search">
<el-form :model="query" label-position="right" label-width="88px" @submit.prevent>
<div class="claim-record-page__search-grid">
<el-form-item label="认领通知单">
@@ -80,11 +80,13 @@
<div class="claim-record-page__search-actions">
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
<el-tooltip :content="searchExpanded ? '折叠' : '展开'" placement="top">
<el-button text @click="searchExpanded = !searchExpanded">
<el-icon><component :is="searchExpanded ? ArrowUp : ArrowDown" /></el-icon>
</el-button>
</el-tooltip>
<el-button
v-if="showSearchToggle"
text
:icon="searchExpanded ? ArrowUp : ArrowDown"
@click="searchExpanded = !searchExpanded"
>{{ searchExpanded ? '收起' : '展开' }}</el-button
>
</div>
</el-form>
</section-card>
@@ -94,11 +96,18 @@
<el-button :loading="refreshing" @click="handleRefreshKingdeeResult">
查询金蝶认领结果
</el-button>
<table-header-tools
:columns="columns"
v-model:visible-column-keys="visibleColumnKeys"
v-model:table-size="tableSize"
v-model:search-visible="searchVisible"
@refresh="loadTable"
/>
</div>
<el-table v-loading="loading" :data="rows" border>
<el-table v-loading="loading" :data="rows" :size="tableSize" border>
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
<el-table-column
v-for="column in columns"
v-for="column in visibleColumns"
:key="column.prop"
v-bind="column"
align="center"
@@ -157,6 +166,7 @@ import { mapGetters } from 'vuex';
import * as api from '@/api/payment/receiptClaimRecord';
import { receiptClaimRecordTableColumns } from '@/option/payment/receiptClaimRecord';
import SectionCard from '@/components/section-card/main.vue';
import TableHeaderTools from '@/components/table-header-tools/main.vue';
import { isAdministrator } from '@/utils/auth';
const emptyQuery = () => ({
@@ -176,17 +186,23 @@ const emptyQuery = () => ({
export default {
name: 'ReceiptClaimRecord',
components: { SectionCard },
components: { SectionCard, TableHeaderTools },
data() {
return {
ArrowDown,
ArrowUp,
query: emptyQuery(),
searchExpanded: false,
// 与 avue-crud 一致:搜索项数量超过 searchIndex(默认一行 4 项)时才显示“展开/收起”
searchIndex: 4,
searchItemCount: 11,
searchVisible: true,
tableSize: 'default',
claimStatusOptions: api.claimStatusOptions,
kingdeeBillStatusOptions: api.kingdeeBillStatusOptions,
pushStatusOptions: api.pushStatusOptions,
columns: receiptClaimRecordTableColumns,
visibleColumnKeys: receiptClaimRecordTableColumns.map(column => column.prop),
rows: [],
loading: false,
refreshing: false,
@@ -195,6 +211,12 @@ export default {
},
computed: {
...mapGetters(['permission', 'userInfo']),
visibleColumns() {
return this.columns.filter(column => this.visibleColumnKeys.includes(column.prop));
},
showSearchToggle() {
return this.searchItemCount > this.searchIndex;
},
isAdmin() {
return isAdministrator(this.userInfo);
},
@@ -314,9 +336,8 @@ export default {
}
.claim-record-page__toolbar {
display: flex;
justify-content: flex-start;
justify-content: space-between;
align-items: center;
gap: 8px;
padding: 12px 0;
}
.claim-record-page__pagination {
+57 -24
View File
@@ -1,6 +1,6 @@
<template>
<basic-container class="receipt-flow-page">
<section class="receipt-flow-page__search">
<section v-if="searchVisible" class="receipt-flow-page__search">
<el-form :model="query" label-position="right" label-width="88px" @submit.prevent>
<div class="receipt-flow-page__search-grid">
<el-form-item label="认领通知单">
@@ -9,6 +9,9 @@
<el-form-item label="对方户名">
<el-input v-model="query.counterpartyName" clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="对方开户行">
<el-input v-model="query.counterpartyBank" clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="对方账号">
<el-input v-model="query.counterpartyAccount" clearable placeholder="请输入" />
</el-form-item>
@@ -86,44 +89,56 @@
<div class="receipt-flow-page__search-actions">
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
<el-tooltip :content="searchExpanded ? '折叠' : '展开'" placement="top">
<el-button text @click="searchExpanded = !searchExpanded">
<el-icon><component :is="searchExpanded ? ArrowUp : ArrowDown" /></el-icon>
</el-button>
</el-tooltip>
<el-button
v-if="showSearchToggle"
text
:icon="searchExpanded ? ArrowUp : ArrowDown"
@click="searchExpanded = !searchExpanded"
>{{ searchExpanded ? '收起' : '展开' }}</el-button
>
</div>
</el-form>
</section>
<section class="receipt-flow-page__table-panel">
<div class="receipt-flow-page__toolbar">
<el-button
v-if="hasPermission('receipt_flow_sync')"
type="primary"
:loading="syncing"
@click="handleSync"
>
手动同步流水
</el-button>
<el-button
v-if="hasPermission('receipt_flow_claim')"
type="primary"
:disabled="!selectedRows.length"
@click="openBatchClaim"
>
批量认领
</el-button>
<div class="receipt-flow-page__toolbar-left">
<el-button
v-if="hasPermission('receipt_flow_sync')"
type="primary"
:loading="syncing"
@click="handleSync"
>
手动同步流水
</el-button>
<el-button
v-if="hasPermission('receipt_flow_claim')"
type="primary"
:disabled="!selectedRows.length"
@click="openBatchClaim"
>
批量认领
</el-button>
</div>
<table-header-tools
:columns="columns"
v-model:visible-column-keys="visibleColumnKeys"
v-model:table-size="tableSize"
v-model:search-visible="searchVisible"
@refresh="loadTable"
/>
</div>
<el-table
v-loading="loading"
:data="rows"
:size="tableSize"
border
@selection-change="selectedRows = $event"
>
<el-table-column type="selection" width="52" align="center" />
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column
v-for="column in columns"
v-for="column in visibleColumns"
:key="column.prop"
v-bind="column"
align="center"
@@ -271,6 +286,7 @@ import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import * as api from '@/api/payment/receiptFlow';
import { receiptFlowTableColumns } from '@/option/payment/receiptFlow';
import TableHeaderTools from '@/components/table-header-tools/main.vue';
import { isAdministrator } from '@/utils/auth';
const emptyQuery = () => ({
@@ -291,6 +307,7 @@ const emptyQuery = () => ({
export default {
name: 'ReceiptFlow',
components: { TableHeaderTools },
data() {
return {
ArrowDown,
@@ -300,18 +317,30 @@ export default {
pushStatusOptions: api.pushStatusOptions,
kingdeeBillStatusOptions: api.kingdeeBillStatusOptions,
columns: receiptFlowTableColumns,
visibleColumnKeys: receiptFlowTableColumns.map(column => column.prop),
searchVisible: true,
tableSize: 'default',
rows: [],
selectedRows: [],
loading: false,
syncing: false,
pushingId: null,
searchExpanded: false,
// 与 avue-crud 一致:搜索项数量超过 searchIndex(默认一行 4 项)时才显示“展开/收起”
searchIndex: 4,
searchItemCount: 7,
page: { current: 1, size: 10, total: 0 },
detailDialog: { visible: false, loading: false, data: {}, claimRecords: [] },
};
},
computed: {
...mapGetters(['permission', 'userInfo']),
visibleColumns() {
return this.columns.filter(column => this.visibleColumnKeys.includes(column.prop));
},
showSearchToggle() {
return this.searchItemCount > this.searchIndex;
},
isAdmin() {
return isAdministrator(this.userInfo);
},
@@ -507,11 +536,15 @@ export default {
}
.receipt-flow-page__toolbar {
display: flex;
justify-content: flex-start;
justify-content: space-between;
align-items: center;
gap: 8px;
padding: 12px 0;
}
.receipt-flow-page__toolbar-left {
display: flex;
gap: 8px;
}
.receipt-flow-page__pagination {
display: flex;
justify-content: flex-end;