Commit 79007a4a by Sweet Zhang

xuanran

parent eb0cc621
...@@ -9,14 +9,6 @@ export function getPolicyCommissionList(data) { ...@@ -9,14 +9,6 @@ export function getPolicyCommissionList(data) {
}) })
} }
// 更新保单来佣信息
export function updatePolicyCommission(data) {
return request({
url: '/csf/api/commission/update',
method: 'post',
data: data
})
}
// 生成可出账记录 // 生成可出账记录
export function generateCommissionRecord(data) { export function generateCommissionRecord(data) {
...@@ -64,7 +56,6 @@ export function downloadPolicyFortuneAccount(data) { ...@@ -64,7 +56,6 @@ export function downloadPolicyFortuneAccount(data) {
url: '/csf/api/fortune/download/account', url: '/csf/api/fortune/download/account',
method: 'post', method: 'post',
data: data, data: data,
responseType: 'blob'
}) })
} }
...@@ -360,3 +351,47 @@ export function userSaleExpandDetail(data){ ...@@ -360,3 +351,47 @@ export function userSaleExpandDetail(data){
method: 'get', method: 'get',
}) })
} }
// 更新比对状态
export function updateCompareStatus(data){
return request({
url: '/csf/api/commission/updateCompareStatus',
method: 'post',
data: data
})
}
// 更新数据
export function updateCommissionRecord(data){
return request({
url: '/csf/api/commission/update',
method: 'post',
data: data
})
}
// 新增出账检核记录
export function addCheckRecordaddBatch(data){
return request({
url: '/csf/api/fortune/addBatch',
method: 'post',
data: data
})
}
// 设置本期出账金额
export function updatePayoutAmount(data){
return request({
url: '/csf/api/fortune/update',
method: 'post',
data: data
})
}
// 同步预计来佣
export function syncExpectedCommission(data){
return request({
url: '/csf/api/commission/addToExpected',
method: 'post',
data: data
})
}
<!-- ExcelUploadPreview.vue -->
<template>
<div class="excel-upload-preview">
<!-- 文件上传 -->
<el-upload ref="uploadRef" :auto-upload="false" :show-file-list="false" accept=".xlsx,.xls,.csv"
:before-upload="handleBeforeUpload" @change="handleFileChange">
<el-button type="primary">选择 Excel 文件</el-button>
<span v-if="fileName" class="ml-2">已选择:{{ fileName }}</span>
</el-upload>
<!-- 解析消息 -->
<el-alert v-if="parseMessage" :title="parseMessage" :type="parsedValid ? 'success' : 'warning'"
style="margin-top: 12px" />
<!-- 错误信息 -->
<el-alert v-if="errorMessages.length" :closable="false" type="error" style="margin-top: 12px">
<template #default>
<div v-for="(msg, idx) in errorMessages" :key="idx">{{ msg }}</div>
</template>
</el-alert>
<!-- 表格 -->
<el-table v-if="editableData.length" :data="editableData" style="width: 100%; margin-top: 16px" border
max-height="400" size="small">
<!-- 动态列 -->
<el-table-column v-for="header in headers" :key="header" :prop="header" :label="getHeaderLabel(header)"
min-width="120">
<template #default="{ row, $index }">
<el-input v-if="editRowIndex === $index && editColumn === header" v-model="row[header]"
@blur="saveEdit" @keyup.enter="saveEdit" ref="currentInput" />
<span v-else @click="startEdit($index, header)">
{{ row[header] ?? '' }}
</span>
</template>
</el-table-column>
<!-- 操作列 -->
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ $index }">
<el-button v-if="editRowIndex === $index" type="text" size="small" @click="cancelEdit">
取消
</el-button>
<el-button v-else type="text" size="small" @click="startEdit($index, headers[0])">
编辑
</el-button>
<el-button type="text" size="small" style="color: #f56c6c" @click="deleteRow($index)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 提交按钮 -->
<el-button v-if="showConfirm && editableData.length" type="success" style="margin-top: 16px"
:loading="submitting" @click="handleSubmit">
确认提交(共 {{ editableData.length }} 条)
</el-button>
</div>
</template>
<script setup>
import { ref, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import request from '@/utils/request'
const props = defineProps({
showConfirm: { type: Boolean, default: true },
beforeUpload: { type: Function, default: null },
headerRow: { type: Number, default: 0 },
checkStartRow: { type: Number, default: 3},
requiredFields: { type: String, default: '' },
useChineseHeader: { type: Boolean, default: true },
transformSubmitData: {
type: Function,
default: (rows) => rows
}
})
const emit = defineEmits(['submit', 'parsed'])
// refs
const uploadRef = ref(null)
const currentInput = ref(null)
const fileName = ref('')
const headers = ref([])
const chineseHeadersMap = ref({})
const editableData = ref([]) // ✅ 所有操作在此数组上进行
const editRowIndex = ref(-1)
const editColumn = ref(null)
const parseMessage = ref('')
const parsedValid = ref(true)
const errorMessages = ref([])
const submitting = ref(false)
// 获取表头显示文本
const getHeaderLabel = (header) => {
return props.useChineseHeader ? (chineseHeadersMap.value[header] || header) : header
}
// 上传前校验
const handleBeforeUpload = (file) => {
if (props.beforeUpload && !props.beforeUpload(file)) return false
if (!/\.(xlsx|xls|csv)$/i.test(file.name)) {
ElMessage.error('仅支持 Excel 或 CSV 文件')
return false
}
return true
}
// 解析文件
const handleFileChange = async (uploadFile) => {
const file = uploadFile.raw
if (!file) return
fileName.value = file.name
parseMessage.value = ''
parsedValid.value = true
errorMessages.value = []
chineseHeadersMap.value = {}
const formData = new FormData()
formData.append('file', file)
formData.append('headerRow', props.headerRow.toString())
formData.append('checkStartRow', props.checkStartRow.toString())
if (props.requiredFields.length > 0) {
formData.append('requiredFields', props.requiredFields)
}
try {
const res = await request.post('/oss/api/excel/import', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
if (res.code !== 200 || !res.data?.success) {
throw new Error(res.msg || '解析失败')
}
const parsed = res.data
parseMessage.value = parsed.message || ''
parsedValid.value = parsed.valid !== false
errorMessages.value = parsed.errorMessages || []
const rawData = parsed.data || []
headers.value = parsed.headers || []
if (rawData.length === 0) {
editableData.value = []
} else {
if (props.useChineseHeader) {
const firstRow = rawData[0]
const map = {}
headers.value.forEach(key => {
map[key] = firstRow[key] || key
})
chineseHeadersMap.value = map
editableData.value = rawData.slice(1).map(row => ({ ...row }))
} else {
editableData.value = rawData.map(row => ({ ...row }))
}
}
emit('parsed', editableData.value)
uploadRef.value.clearFiles()
} catch (err) {
console.error('Parse error:', err)
ElMessage.error('文件解析失败:' + (err.message || ''))
}
}
// 开始编辑
const startEdit = (index, column) => {
editRowIndex.value = index
editColumn.value = column
nextTick(() => {
currentInput.value?.focus()
})
}
// 保存编辑
const saveEdit = () => {
editRowIndex.value = -1
editColumn.value = null
}
// 取消编辑
const cancelEdit = () => {
// 无需回滚,因为我们直接操作 editableData(无原始快照)
// 如果需要“撤销到初始状态”,可保留 originalData
saveEdit()
}
// 删除行
const deleteRow = (index) => {
ElMessageBox.confirm('确定删除此行?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
editableData.value.splice(index, 1)
// 重置编辑状态(避免索引错乱)
if (editRowIndex.value === index) {
editRowIndex.value = -1
editColumn.value = null
} else if (editRowIndex.value > index) {
editRowIndex.value-- // 索引前移
}
}).catch(() => { })
}
// 提交最终数据
const handleSubmit = () => {
if (editableData.value.length === 0) {
ElMessage.warning('没有可提交的数据')
return
}
submitting.value = true
try {
// 调用 transformSubmitData 进行格式转换(如类型转换)
const finalData = props.transformSubmitData([...editableData.value])
emit('submit', finalData)
} catch (err) {
ElMessage.error('提交失败:' + (err.message || ''))
} finally {
submitting.value = false
}
}
</script>
<style scoped>
.ml-2 {
margin-left: 8px;
}
</style>
\ No newline at end of file
...@@ -115,7 +115,7 @@ const searchConfig = ref([ ...@@ -115,7 +115,7 @@ const searchConfig = ref([
type: 'select', type: 'select',
prop: 'statusList', prop: 'statusList',
label: '出账状态', label: '出账状态',
dictType: 'csf_fortune_account_status', dictType: 'csf_expected_fortune_status',
multiple: true, multiple: true,
} }
]) ])
...@@ -317,10 +317,10 @@ const dictLists = ref([]) ...@@ -317,10 +317,10 @@ const dictLists = ref([])
// 获取出账状态字典值 // 获取出账状态字典值
const getDictLists = () => { const getDictLists = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
listType({ typeList: ['csf_fortune_status', 'bx_currency_type'] }) listType({ typeList: ['csf_expected_fortune_status', 'bx_currency_type'] })
.then(res => { .then(res => {
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
const dictData = res.data.find(item => item.dictType === 'csf_fortune_status') const dictData = res.data.find(item => item.dictType === 'csf_expected_fortune_status')
dictLists.value = dictData?.dictItemList || [] dictLists.value = dictData?.dictItemList || []
console.log('获取到的字典数据:', dictLists.value) console.log('获取到的字典数据:', dictLists.value)
// 处理币种字典值 // 处理币种字典值
......
...@@ -153,7 +153,7 @@ ...@@ -153,7 +153,7 @@
<!-- 新增出账记录 --> <!-- 新增出账记录 -->
<CommonDialog dialogTitle="新增出账记录" dialogWidth="80%" :openDialog="addPayRecordDialogVisible" :showAction="true" <CommonDialog dialogTitle="新增出账记录" dialogWidth="80%" :openDialog="addPayRecordDialogVisible" :showAction="true"
:showClose="true" @close="addPayRecordDialogVisible = false" @confirm="handleConfirmAddPayRecord"> :showClose="true" @close="addPayRecordDialogVisible = false" @confirm="handleConfirmAddPayRecord">
<SearchForm ref="addPayRecordFormRef" :config="addPayRecordFormConfig" /> <SearchForm ref="addPayRecordFormRef" :config="addPayRecordFormConfig" v-model="addPayRecordFormModel" />
</CommonDialog> </CommonDialog>
<!-- 设置出账状态 --> <!-- 设置出账状态 -->
<CommonDialog dialogTitle="设置出账状态" dialogWidth="80%" :openDialog="setPayRecordStatusDialogVisible" <CommonDialog dialogTitle="设置出账状态" dialogWidth="80%" :openDialog="setPayRecordStatusDialogVisible"
...@@ -173,7 +173,7 @@ import { formatCurrency } from '@/utils/number' ...@@ -173,7 +173,7 @@ import { formatCurrency } from '@/utils/number'
import { expectedFortuneList, payRecordList, addPayRecord, userSaleExpandDetail } from '@/api/financial/commission' import { expectedFortuneList, payRecordList, addPayRecord, userSaleExpandDetail } from '@/api/financial/commission'
import SearchForm from '@/components/SearchForm/SearchForm.vue' import SearchForm from '@/components/SearchForm/SearchForm.vue'
import CommonDialog from '@/components/commonDialog' import CommonDialog from '@/components/commonDialog'
import { getDictLabel } from '@/utils/useDict'; import { loadDicts, getDictLabel } from '@/utils/useDict'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
// 设置出账状态 // 设置出账状态
const setPayRecordStatusDialogVisible = ref(false) const setPayRecordStatusDialogVisible = ref(false)
...@@ -298,9 +298,12 @@ const searchConfig = ref([ ...@@ -298,9 +298,12 @@ const searchConfig = ref([
]) ])
const payRecordDialogTableVisible = ref(false) const payRecordDialogTableVisible = ref(false)
// 新增出账记录 // 新增出账记录
const addPayRecordFormModel = reactive({
fortuneBizType: 'U',
})
const addPayRecordDialogVisible = ref(false) const addPayRecordDialogVisible = ref(false)
const addPayRecordFormRef = ref() const addPayRecordFormRef = ref()
const addPayRecordFormConfig = ref([ const addPayRecordFormConfig = [
{ {
type: 'select', type: 'select',
prop: 'fortuneBizType', prop: 'fortuneBizType',
...@@ -312,12 +315,14 @@ const addPayRecordFormConfig = ref([ ...@@ -312,12 +315,14 @@ const addPayRecordFormConfig = ref([
}, { }, {
type: 'input', type: 'input',
prop: 'policyNo', prop: 'policyNo',
label: '关联保单号' label: '关联保单号',
visible: (formData) => formData.fortuneBizType === 'R',
}, { }, {
type: 'input', type: 'input',
prop: 'fortunePeriod', prop: 'fortunePeriod',
label: '佣金期数', label: '佣金期数',
inputType: 'decimal', inputType: 'decimal',
visible: (formData) => formData.fortuneBizType === 'R',
rules: [ rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' } { pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
] ]
...@@ -326,6 +331,7 @@ const addPayRecordFormConfig = ref([ ...@@ -326,6 +331,7 @@ const addPayRecordFormConfig = ref([
prop: 'fortuneTotalPeriod', prop: 'fortuneTotalPeriod',
label: '总期数', label: '总期数',
inputType: 'decimal', inputType: 'decimal',
visible: (formData) => formData.fortuneBizType === 'R',
rules: [ rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' } { pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
] ]
...@@ -338,7 +344,8 @@ const addPayRecordFormConfig = ref([ ...@@ -338,7 +344,8 @@ const addPayRecordFormConfig = ref([
type: 'date', type: 'date',
prop: 'actualPayoutDate', prop: 'actualPayoutDate',
label: '出账日(实)', label: '出账日(实)',
placeholder: '请选择' placeholder: '请选择',
maxDate: 'today'
}, { }, {
type: 'input', type: 'input',
prop: 'amount', prop: 'amount',
...@@ -368,6 +375,10 @@ const addPayRecordFormConfig = ref([ ...@@ -368,6 +375,10 @@ const addPayRecordFormConfig = ref([
debounceWait: 500, // 自定义防抖时间 debounceWait: 500, // 自定义防抖时间
valueKey: 'userSaleBizId', valueKey: 'userSaleBizId',
labelKey: 'realName', labelKey: 'realName',
onChangeExtraFields: {
broker: 'realName',// 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code'
},
transform: (res) => { transform: (res) => {
return res?.data.records || [] return res?.data.records || []
} }
...@@ -377,13 +388,14 @@ const addPayRecordFormConfig = ref([ ...@@ -377,13 +388,14 @@ const addPayRecordFormConfig = ref([
label: '出账状态', label: '出账状态',
dictType: 'csf_expected_fortune_status' dictType: 'csf_expected_fortune_status'
} }
]) ]
const handleConfirmAddPayRecord = async () => { const handleConfirmAddPayRecord = async () => {
const handleConfirmAddPayRecordparams = addPayRecordFormRef.value.getFormData() const handleConfirmAddPayRecordparams = addPayRecordFormRef.value.getFormData()
try { try {
await addPayRecord([handleConfirmAddPayRecordparams]) await addPayRecord([handleConfirmAddPayRecordparams])
ElMessage.success('新增出账记录成功') ElMessage.success('新增出账记录成功')
addPayRecordDialogVisible.value = false addPayRecordDialogVisible.value = false
addPayRecordFormRef.value.resetForm()
} catch (error) { } catch (error) {
ElMessage.error(error.message) ElMessage.error(error.message)
} }
...@@ -595,7 +607,20 @@ const handleConfirmSetPayRecordStatus = async () => { ...@@ -595,7 +607,20 @@ const handleConfirmSetPayRecordStatus = async () => {
ElMessage.error('设置出账状态失败') ElMessage.error('设置出账状态失败')
} }
} }
// 获取入账状态,字典值转化方法
onMounted(async () => {
try {
await loadDicts(['csf_expected_fortune_status'])
} catch (error) {
console.error('字典加载失败', error)
} finally {
loading.value = false
}
})
// 格式化函数(每次渲染都会调用,所以能拿到最新字典)
const formatStatus = (row, column) => {
return getDictLabel('csf_expected_fortune_status', row.status) // 实时查缓存
}
</script> </script>
......
...@@ -39,11 +39,7 @@ ...@@ -39,11 +39,7 @@
<el-table-column prop="receivableNo" label="应收款编号" width="120" /> <el-table-column prop="receivableNo" label="应收款编号" width="120" />
<el-table-column prop="policyNo" label="保单号" width="120" fixed="left" sortable /> <el-table-column prop="policyNo" label="保单号" width="120" fixed="left" sortable />
<el-table-column prop="reconciliationCompany" label="对账公司" width="120" sortable /> <el-table-column prop="reconciliationCompany" label="对账公司" width="120" sortable />
<el-table-column prop="status" label="入账状态" width="120" sortable> <el-table-column prop="status" label="入账状态" width="120" sortable :formatter="formatStatus" />
<template #default="{ row }">
{{ getDictLabel('csf_expected_commission_status', row.status) }}
</template>
</el-table-column>
<el-table-column prop="commissionPeriod" label="入账期数" width="120" sortable /> <el-table-column prop="commissionPeriod" label="入账期数" width="120" sortable />
<el-table-column prop="totalPeriod" label="入账总期数" width="120" sortable /> <el-table-column prop="totalPeriod" label="入账总期数" width="120" sortable />
<el-table-column prop="commissionName" label="入账项目" width="120" sortable /> <el-table-column prop="commissionName" label="入账项目" width="120" sortable />
...@@ -140,7 +136,8 @@ ...@@ -140,7 +136,8 @@
<!-- 新增应收款管理 --> <!-- 新增应收款管理 -->
<CommonDialog dialogTitle="新增应收款" dialogWidth="80%" :openDialog="addReceivablesDialogVisible" <CommonDialog dialogTitle="新增应收款" dialogWidth="80%" :openDialog="addReceivablesDialogVisible"
@close="addReceivablesDialogVisible = false" @confirm="handleConfirmAddReceivables"> @close="addReceivablesDialogVisible = false; resetAddReceivablesForm()"
@confirm="handleConfirmAddReceivables">
<SearchForm ref="addRecordRef" :config="addReceivablesFormConfig" v-model="addReceivablesFormModel" /> <SearchForm ref="addRecordRef" :config="addReceivablesFormConfig" v-model="addReceivablesFormModel" />
</CommonDialog> </CommonDialog>
</div> </div>
...@@ -155,7 +152,7 @@ import { MoreFilled } from '@element-plus/icons-vue' ...@@ -155,7 +152,7 @@ import { MoreFilled } from '@element-plus/icons-vue'
import { receivedFortuneList, updateCommissionExpected, commissionEntryEditRecords, exportReceivedFortune, commissionExpectedRecord, addReceivedFortune } from '@/api/financial/commission' import { receivedFortuneList, updateCommissionExpected, commissionEntryEditRecords, exportReceivedFortune, commissionExpectedRecord, addReceivedFortune } from '@/api/financial/commission'
import { numberWithCommas } from '@/utils/index' import { numberWithCommas } from '@/utils/index'
import SearchForm from '@/components/SearchForm/SearchForm.vue' import SearchForm from '@/components/SearchForm/SearchForm.vue'
import { getDictLabel } from '@/utils/useDict'; import { loadDicts, getDictLabel } from '@/utils/useDict'
import { safeDownload } from '@/utils/safeDownload' import { safeDownload } from '@/utils/safeDownload'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
...@@ -221,11 +218,23 @@ const addReceivablesFormConfig = [ ...@@ -221,11 +218,23 @@ const addReceivablesFormConfig = [
label: '入账金额', label: '入账金额',
inputType: 'decimal', inputType: 'decimal',
decimalDigits: 2, decimalDigits: 2,
visible: (formData) => formData.commissionBizType === 'U',
rules: [ rules: [
{ required: true, message: '请输入金额', trigger: 'blur' }, { required: true, message: '请输入金额', trigger: 'blur' },
{ pattern: /^\d+(\.\d{1,2})?$/, message: '最多两位小数', trigger: 'blur' } { pattern: /^\d+(\.\d{1,2})?$/, message: '最多两位小数', trigger: 'blur' }
] ]
}, { }, {
type: 'input',
prop: 'commissionRatio',
label: '入账比例(%)',
inputType: 'decimal',
decimalDigits: 2,
visible: (formData) => formData.commissionBizType === 'R',
rules: [
{ required: true, message: '请输入入账比例', trigger: 'blur' },
{ pattern: /^\d+(\.\d{1,2})?$/, message: '最多两位小数', trigger: 'blur' }
]
}, {
type: 'select', type: 'select',
prop: 'currency', prop: 'currency',
label: '入账币种', label: '入账币种',
...@@ -246,23 +255,33 @@ const addReceivablesFormConfig = [ ...@@ -246,23 +255,33 @@ const addReceivablesFormConfig = [
debounceWait: 500, // 自定义防抖时间 debounceWait: 500, // 自定义防抖时间
valueKey: 'reconciliationCompanyBizId', valueKey: 'reconciliationCompanyBizId',
labelKey: 'name', labelKey: 'name',
onChangeExtraFields: {
reconciliationCompany: 'name',// 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code'
},
transform: (res) => { transform: (res) => {
console.log('对账公司', res) console.log('对账公司', res)
return res.data.records || [] return res.data.records || []
} }
} }
] ]
// 弹窗表单重置
const resetAddReceivablesForm = () => {
addRecordRef.value.resetForm()
}
const handleConfirmAddReceivables = async () => { const handleConfirmAddReceivables = async () => {
const p = addRecordRef.value.getFormData() const p = addRecordRef.value.getFormData()
console.log('新增应收款', p)
try { try {
await addReceivedFortune({ await addReceivedFortune({
commissionExpectedAddDtoList: [p] commissionExpectedAddDtoList: [p]
}) })
ElMessage.success('新增应收款成功') ElMessage.success('新增应收款成功')
addReceivablesDialogVisible.value = false addReceivablesDialogVisible.value = false
handleSearch() resetAddReceivablesForm()
handleQuery()
} catch (error) { } catch (error) {
ElMessage.error(error.message) ElMessage.error(error.message)
} }
...@@ -648,6 +667,20 @@ const handleConfirmSetStatus = () => { ...@@ -648,6 +667,20 @@ const handleConfirmSetStatus = () => {
ElMessage.info('已取消修改') ElMessage.info('已取消修改')
}) })
} }
// 获取入账状态,字典值转化方法
onMounted(async () => {
try {
await loadDicts(['csf_expected_commission_status'])
} catch (error) {
console.error('字典加载失败', error)
} finally {
loading.value = false
}
})
// 格式化函数(每次渲染都会调用,所以能拿到最新字典)
const formatStatus = (row, column) => {
return getDictLabel('csf_expected_commission_status', row.status) // 实时查缓存
}
</script> </script>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment