列表和查看能看出登记人,编辑时只读,避免把账户转给别人。 Co-authored-by: Cursor <cursoragent@cursor.com>
620 lines
19 KiB
Vue
620 lines
19 KiB
Vue
<!-- OA 收款账户管理(租户级公共主数据) -->
|
||
<!-- eslint-disable vue/no-v-model-argument -->
|
||
<template>
|
||
<div class="page-container">
|
||
<section class="panel">
|
||
<div class="filter-block">
|
||
<el-form :model="searchForm" class="filter-form" @submit.prevent>
|
||
<div class="filter-grid">
|
||
<el-form-item label="关键词">
|
||
<el-input
|
||
v-model="searchForm.keyword"
|
||
placeholder="户名 / 账号 / 银行"
|
||
clearable
|
||
@keyup.enter="handleSearch"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="账户类型">
|
||
<el-select v-model="searchForm.accountType" placeholder="请选择" clearable>
|
||
<el-option
|
||
v-for="(label, value) in ACCOUNT_TYPE_LABEL"
|
||
:key="value"
|
||
:label="label"
|
||
:value="value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</div>
|
||
<div class="filter-actions-row">
|
||
<div class="filter-actions-end">
|
||
<el-button class="btn-ghost" @click="handleReset">
|
||
<el-icon><RefreshLeft /></el-icon>重置
|
||
</el-button>
|
||
<el-button class="btn-main" type="primary" @click="handleSearch">
|
||
<el-icon><Search /></el-icon>搜索
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</el-form>
|
||
</div>
|
||
|
||
<div class="list-toolbar">
|
||
<div class="list-toolbar-row">
|
||
<div class="list-toolbar-selected">
|
||
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
|
||
</div>
|
||
<el-button class="btn-main" type="primary" @click="handleAdd">
|
||
<el-icon><Plus /></el-icon>
|
||
新增账户
|
||
</el-button>
|
||
<el-button class="btn-ghost" :disabled="!selectedRows.length" @click="handleBatchDelete">
|
||
<el-icon><Delete /></el-icon>
|
||
批量删除
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="list-body list-wrap">
|
||
<el-table
|
||
class="list-table"
|
||
:data="tableData"
|
||
v-loading="loading"
|
||
stripe
|
||
row-key="id"
|
||
@selection-change="(rows: any[]) => (selectedRows = rows)"
|
||
>
|
||
<el-table-column type="selection" min-width="55" align="center" header-align="center" />
|
||
<el-table-column
|
||
type="index"
|
||
label="序号"
|
||
min-width="68"
|
||
align="center"
|
||
header-align="center"
|
||
/>
|
||
<el-table-column
|
||
prop="accountType"
|
||
label="账户类型"
|
||
min-width="120"
|
||
align="center"
|
||
header-align="center"
|
||
>
|
||
<template #default="{ row }">
|
||
<el-tag :type="typeTagType(row.accountType)" size="small">
|
||
{{ ACCOUNT_TYPE_LABEL[row.accountType as ReceiveAccountType] || row.accountType }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="accountName"
|
||
label="户名"
|
||
min-width="140"
|
||
align="left"
|
||
header-align="left"
|
||
show-overflow-tooltip
|
||
/>
|
||
<el-table-column
|
||
label="所属人"
|
||
min-width="120"
|
||
align="left"
|
||
header-align="left"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #default="{ row }">{{ formatDisplayOrDash(row.ownerName) }}</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="accountNo"
|
||
label="账号"
|
||
min-width="160"
|
||
align="left"
|
||
header-align="left"
|
||
show-overflow-tooltip
|
||
/>
|
||
<el-table-column
|
||
prop="bankName"
|
||
label="银行"
|
||
min-width="120"
|
||
align="left"
|
||
header-align="left"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #default="{ row }">
|
||
{{ row.accountType === 'alipay' ? '—' : formatDisplayOrDash(row.bankName) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
label="开户地 / 支行"
|
||
min-width="200"
|
||
align="left"
|
||
header-align="left"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #default="{ row }">
|
||
<template v-if="row.accountType === 'alipay'">—</template>
|
||
<template v-else>
|
||
<div>{{ regionText(row) }}</div>
|
||
<div v-if="row.bankBranch" class="cell-sub">{{ row.bankBranch }}</div>
|
||
</template>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="remark"
|
||
label="备注"
|
||
min-width="120"
|
||
align="left"
|
||
header-align="left"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #default="{ row }">{{ formatDisplayOrDash(row.remark) }}</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
label="操作"
|
||
min-width="260"
|
||
fixed="right"
|
||
align="center"
|
||
header-align="center"
|
||
>
|
||
<template #default="{ row }">
|
||
<div style="display: flex; justify-content: center; gap: 8px">
|
||
<el-button class="btn-op-item" @click="handleView(row)">
|
||
<el-icon><View /></el-icon>查看
|
||
</el-button>
|
||
<el-button class="btn-op-item" @click="handleEdit(row)">
|
||
<el-icon><Edit /></el-icon>编辑
|
||
</el-button>
|
||
<el-button class="btn-op-item" @click="handleDelete(row)">
|
||
<el-icon><Delete /></el-icon>删除
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<div class="pagination list-pager-bar">
|
||
<el-pagination
|
||
v-model:current-page="pager.page"
|
||
v-model:page-size="pager.pageSize"
|
||
:total="pager.total"
|
||
:page-sizes="[10, 20, 50]"
|
||
layout="total, sizes, prev, pager, next"
|
||
background
|
||
@size-change="loadData"
|
||
@current-change="loadData"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<el-dialog
|
||
v-model="dialogVisible"
|
||
:title="dialogTitle"
|
||
width="720px"
|
||
class="dialog-form-layout"
|
||
align-center
|
||
append-to-body
|
||
modal-class="protocol-dialog-mask"
|
||
destroy-on-close
|
||
:close-on-click-modal="false"
|
||
>
|
||
<el-form
|
||
ref="formRef"
|
||
:model="formData"
|
||
:rules="formRules"
|
||
label-position="top"
|
||
scroll-to-error
|
||
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
|
||
>
|
||
<div class="form-grid-2">
|
||
<el-form-item label="账户类型" prop="accountType" class="form-col-span-2">
|
||
<el-select
|
||
v-model="formData.accountType"
|
||
placeholder="请选择"
|
||
:disabled="isEdit"
|
||
@change="onTypeChange"
|
||
>
|
||
<el-option
|
||
v-for="(label, value) in ACCOUNT_TYPE_LABEL"
|
||
:key="value"
|
||
:label="label"
|
||
:value="value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="户名" prop="accountName">
|
||
<el-input v-model="formData.accountName" placeholder="请输入户名" maxlength="128" />
|
||
</el-form-item>
|
||
<el-form-item v-if="isEdit" label="所属人">
|
||
<el-input :model-value="editingOwnerName" disabled />
|
||
</el-form-item>
|
||
<el-form-item label="账号" prop="accountNo">
|
||
<el-input v-model="formData.accountNo" placeholder="请输入账号" maxlength="128" />
|
||
</el-form-item>
|
||
<template v-if="formData.accountType !== 'alipay'">
|
||
<el-form-item label="银行" prop="bankName">
|
||
<el-input
|
||
v-model="formData.bankName"
|
||
placeholder="如:中国工商银行"
|
||
maxlength="128"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="银行所在省" prop="bankProvince">
|
||
<el-input
|
||
v-model="formData.bankProvince"
|
||
placeholder="如:安徽省"
|
||
maxlength="64"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="银行所在市" prop="bankCity">
|
||
<el-input
|
||
v-model="formData.bankCity"
|
||
placeholder="如:合肥市"
|
||
maxlength="64"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="银行支行" prop="bankBranch" class="form-col-span-2">
|
||
<el-input
|
||
v-model="formData.bankBranch"
|
||
placeholder="请输入开户支行全称"
|
||
maxlength="256"
|
||
/>
|
||
</el-form-item>
|
||
</template>
|
||
<el-form-item label="备注" class="form-col-span-2">
|
||
<el-input
|
||
v-model="formData.remark"
|
||
type="textarea"
|
||
:rows="2"
|
||
placeholder="选填"
|
||
maxlength="512"
|
||
/>
|
||
</el-form-item>
|
||
</div>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button class="btn-ghost" @click="dialogVisible = false">
|
||
<el-icon><Close /></el-icon>
|
||
取消
|
||
</el-button>
|
||
<el-button class="btn-main" type="primary" :loading="submitLoading" @click="handleSubmit">
|
||
<el-icon><Check /></el-icon>
|
||
确定
|
||
</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<el-dialog
|
||
v-model="viewVisible"
|
||
title="查看收款账户"
|
||
width="640px"
|
||
class="dialog-form-layout"
|
||
align-center
|
||
append-to-body
|
||
modal-class="protocol-dialog-mask"
|
||
destroy-on-close
|
||
:close-on-click-modal="false"
|
||
>
|
||
<div v-if="viewRow" class="view-grid">
|
||
<div class="view-item">
|
||
<div class="k">账户类型</div>
|
||
<div class="v">
|
||
<el-tag :type="typeTagType(viewRow.accountType)" size="small">
|
||
{{
|
||
ACCOUNT_TYPE_LABEL[viewRow.accountType as ReceiveAccountType] ||
|
||
viewRow.accountType
|
||
}}
|
||
</el-tag>
|
||
</div>
|
||
</div>
|
||
<div class="view-item">
|
||
<div class="k">户名</div>
|
||
<div class="v">{{ formatDisplayOrDash(viewRow.accountName) }}</div>
|
||
</div>
|
||
<div class="view-item">
|
||
<div class="k">所属人</div>
|
||
<div class="v">{{ formatDisplayOrDash(viewRow.ownerName) }}</div>
|
||
</div>
|
||
<div class="view-item">
|
||
<div class="k">账号</div>
|
||
<div class="v">{{ formatDisplayOrDash(viewRow.accountNo) }}</div>
|
||
</div>
|
||
<template v-if="viewRow.accountType !== 'alipay'">
|
||
<div class="view-item">
|
||
<div class="k">银行</div>
|
||
<div class="v">{{ formatDisplayOrDash(viewRow.bankName) }}</div>
|
||
</div>
|
||
<div class="view-item">
|
||
<div class="k">银行所在省 / 市</div>
|
||
<div class="v">{{ regionText(viewRow) }}</div>
|
||
</div>
|
||
<div class="view-item full">
|
||
<div class="k">银行支行</div>
|
||
<div class="v">{{ formatDisplayOrDash(viewRow.bankBranch) }}</div>
|
||
</div>
|
||
</template>
|
||
<div class="view-item full">
|
||
<div class="k">备注</div>
|
||
<div class="v">{{ formatDisplayOrDash(viewRow.remark) }}</div>
|
||
</div>
|
||
</div>
|
||
<template #footer>
|
||
<el-button class="btn-main" type="primary" @click="viewVisible = false">
|
||
<el-icon><Check /></el-icon>
|
||
关闭
|
||
</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</section>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, onMounted, reactive, ref } from 'vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import {
|
||
Plus,
|
||
Close,
|
||
Check,
|
||
Edit,
|
||
Delete,
|
||
View,
|
||
RefreshLeft,
|
||
Search
|
||
} from '@element-plus/icons-vue'
|
||
import { formatDisplayOrDash } from '@/utils/datetime-display'
|
||
import {
|
||
fetchBatchDeleteOaReceiveAccounts,
|
||
fetchCreateOaReceiveAccount,
|
||
fetchDeleteOaReceiveAccount,
|
||
fetchOaReceiveAccountList,
|
||
fetchUpdateOaReceiveAccount
|
||
} from '@/api/oaApproval'
|
||
import {
|
||
ACCOUNT_TYPE_LABEL,
|
||
type ReceiveAccountType
|
||
} from '@/utils/oa-receive-account-value'
|
||
|
||
defineOptions({ name: 'OaApprovalReceiveAccounts' })
|
||
|
||
const searchForm = reactive({ keyword: '', accountType: '' })
|
||
const tableData = ref<any[]>([])
|
||
const selectedRows = ref<any[]>([])
|
||
const loading = ref(false)
|
||
const pager = reactive({ page: 1, pageSize: 10, total: 0 })
|
||
|
||
const dialogVisible = ref(false)
|
||
const dialogTitle = ref('新增收款账户')
|
||
const isEdit = ref(false)
|
||
const editingOwnerName = ref('')
|
||
const editingId = ref<string | null>(null)
|
||
const submitLoading = ref(false)
|
||
const formRef = ref()
|
||
const formData = reactive({
|
||
accountType: 'alipay' as ReceiveAccountType,
|
||
accountName: '',
|
||
accountNo: '',
|
||
bankName: '',
|
||
bankProvince: '',
|
||
bankCity: '',
|
||
bankBranch: '',
|
||
remark: ''
|
||
})
|
||
|
||
const requireWhenBank = (_rule: any, value: string, callback: (e?: Error) => void) => {
|
||
if (formData.accountType === 'alipay') {
|
||
callback()
|
||
return
|
||
}
|
||
if (!String(value || '').trim()) {
|
||
callback(new Error('请填写'))
|
||
return
|
||
}
|
||
callback()
|
||
}
|
||
|
||
const formRules = computed(() => ({
|
||
accountType: [{ required: true, message: '请选择账户类型', trigger: 'change' }],
|
||
accountName: [{ required: true, message: '请输入户名', trigger: 'blur' }],
|
||
accountNo: [{ required: true, message: '请输入账号', trigger: 'blur' }],
|
||
bankName: [{ validator: requireWhenBank, trigger: 'blur' }],
|
||
bankProvince: [{ validator: requireWhenBank, trigger: 'blur' }],
|
||
bankCity: [{ validator: requireWhenBank, trigger: 'blur' }],
|
||
bankBranch: [{ validator: requireWhenBank, trigger: 'blur' }]
|
||
}))
|
||
|
||
const viewVisible = ref(false)
|
||
const viewRow = ref<any>(null)
|
||
|
||
function typeTagType(t: string): 'success' | 'primary' | 'warning' | 'info' {
|
||
if (t === 'alipay') return 'success'
|
||
if (t === 'personal') return 'primary'
|
||
if (t === 'corporate') return 'warning'
|
||
return 'info'
|
||
}
|
||
|
||
function regionText(row: any) {
|
||
const p = String(row?.bankProvince || '').trim()
|
||
const c = String(row?.bankCity || '').trim()
|
||
if (p && c) return `${p} / ${c}`
|
||
return p || c || '—'
|
||
}
|
||
|
||
async function loadData() {
|
||
loading.value = true
|
||
try {
|
||
const res = await fetchOaReceiveAccountList({
|
||
keyword: searchForm.keyword || undefined,
|
||
accountType: searchForm.accountType || undefined,
|
||
page: pager.page,
|
||
pageSize: pager.pageSize
|
||
})
|
||
tableData.value = res?.list || []
|
||
pager.total = Number(res?.total || 0)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function handleSearch() {
|
||
pager.page = 1
|
||
loadData()
|
||
}
|
||
|
||
function handleReset() {
|
||
searchForm.keyword = ''
|
||
searchForm.accountType = ''
|
||
pager.page = 1
|
||
loadData()
|
||
}
|
||
|
||
function clearBankFields() {
|
||
formData.bankName = ''
|
||
formData.bankProvince = ''
|
||
formData.bankCity = ''
|
||
formData.bankBranch = ''
|
||
}
|
||
|
||
function onTypeChange() {
|
||
if (formData.accountType === 'alipay') clearBankFields()
|
||
formRef.value?.clearValidate?.()
|
||
}
|
||
|
||
function resetForm() {
|
||
formData.accountType = 'alipay'
|
||
formData.accountName = ''
|
||
formData.accountNo = ''
|
||
clearBankFields()
|
||
formData.remark = ''
|
||
editingId.value = null
|
||
}
|
||
|
||
function handleAdd() {
|
||
isEdit.value = false
|
||
dialogTitle.value = '新增收款账户'
|
||
resetForm()
|
||
editingOwnerName.value = ''
|
||
dialogVisible.value = true
|
||
formRef.value?.clearValidate?.()
|
||
}
|
||
|
||
function handleEdit(row: any) {
|
||
isEdit.value = true
|
||
dialogTitle.value = '编辑收款账户'
|
||
editingId.value = row.id
|
||
formData.accountType = (row.accountType || 'alipay') as ReceiveAccountType
|
||
formData.accountName = row.accountName || ''
|
||
formData.accountNo = row.accountNo || ''
|
||
formData.bankName = row.bankName || ''
|
||
formData.bankProvince = row.bankProvince || ''
|
||
formData.bankCity = row.bankCity || ''
|
||
formData.bankBranch = row.bankBranch || ''
|
||
formData.remark = row.remark || ''
|
||
if (formData.accountType === 'alipay') clearBankFields()
|
||
editingOwnerName.value = row.ownerName || '—'
|
||
dialogVisible.value = true
|
||
formRef.value?.clearValidate?.()
|
||
}
|
||
|
||
function handleView(row: any) {
|
||
viewRow.value = row
|
||
viewVisible.value = true
|
||
}
|
||
|
||
async function handleSubmit() {
|
||
try {
|
||
await formRef.value?.validate?.()
|
||
} catch {
|
||
ElMessageBox.alert('请完善必填项后再提交', '提示', { type: 'warning' })
|
||
return
|
||
}
|
||
submitLoading.value = true
|
||
try {
|
||
const payload: Record<string, unknown> = {
|
||
accountType: formData.accountType,
|
||
accountName: formData.accountName.trim(),
|
||
accountNo: formData.accountNo.trim(),
|
||
remark: formData.remark?.trim() || undefined
|
||
}
|
||
if (formData.accountType === 'alipay') {
|
||
payload.bankName = ''
|
||
payload.bankProvince = ''
|
||
payload.bankCity = ''
|
||
payload.bankBranch = ''
|
||
} else {
|
||
payload.bankName = formData.bankName.trim()
|
||
payload.bankProvince = formData.bankProvince.trim()
|
||
payload.bankCity = formData.bankCity.trim()
|
||
payload.bankBranch = formData.bankBranch.trim()
|
||
}
|
||
if (isEdit.value && editingId.value) {
|
||
await fetchUpdateOaReceiveAccount(editingId.value, payload)
|
||
} else {
|
||
await fetchCreateOaReceiveAccount(payload)
|
||
}
|
||
dialogVisible.value = false
|
||
await loadData()
|
||
} finally {
|
||
submitLoading.value = false
|
||
}
|
||
}
|
||
|
||
async function handleDelete(row: any) {
|
||
await ElMessageBox.confirm(`确定删除收款账户「${row.accountName}」吗?`, '提示', {
|
||
type: 'warning'
|
||
})
|
||
await fetchDeleteOaReceiveAccount(row.id)
|
||
await loadData()
|
||
}
|
||
|
||
async function handleBatchDelete() {
|
||
if (!selectedRows.value.length) return
|
||
await ElMessageBox.confirm(
|
||
`确定删除已选 ${selectedRows.value.length} 个收款账户吗?`,
|
||
'提示',
|
||
{ type: 'warning' }
|
||
)
|
||
const ids = selectedRows.value.map((r) => r.id).filter(Boolean)
|
||
await fetchBatchDeleteOaReceiveAccounts(ids)
|
||
selectedRows.value = []
|
||
await loadData()
|
||
}
|
||
|
||
onMounted(() => {
|
||
loadData()
|
||
})
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.cell-sub {
|
||
margin-top: 2px;
|
||
font-size: 12px;
|
||
color: var(--mint-text-muted, #64748b);
|
||
}
|
||
|
||
.view-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 14px 20px;
|
||
}
|
||
|
||
.view-item.full {
|
||
grid-column: 1 / -1;
|
||
}
|
||
|
||
.view-item .k {
|
||
margin-bottom: 4px;
|
||
font-size: 12px;
|
||
color: var(--mint-text-muted, #64748b);
|
||
}
|
||
|
||
.view-item .v {
|
||
font-weight: 500;
|
||
word-break: break-all;
|
||
}
|
||
|
||
@media (max-width: 640px) {
|
||
.view-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
</style>
|