Commit 79007a4a by Sweet Zhang

xuanran

parent eb0cc621
......@@ -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) {
......@@ -64,7 +56,6 @@ export function downloadPolicyFortuneAccount(data) {
url: '/csf/api/fortune/download/account',
method: 'post',
data: data,
responseType: 'blob'
})
}
......@@ -360,3 +351,47 @@ export function userSaleExpandDetail(data){
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
})
}
<template>
<el-form ref="formRef" :model="localModel" :rules="formRules" label-width="auto" v-bind="$attrs">
<el-form ref="formRef" :model="localModel" :rules="formRules" label-width="auto" v-bind="$attrs" :validate-on-rule-change="false">
<el-row :gutter="20">
<el-col v-for="item in visibleConfig" :key="item.prop" :span="item.span || 8">
<el-form-item :label="item.label" :prop="item.prop" :class="{ 'search-form-item': isSearch }">
<!-- Input -->
<el-input v-if="item.type === 'input'" v-model="localModel[item.prop]"
:placeholder="item.placeholder || `请输入${item.label}`" :clearable="true"
@input="(val) => handleNumberInput(val, item)" />
@input="(val) => handleNumberInput(val, item)"
@change="(val) => handleModelChange(val, item)" />
<!-- Select (支持 dictType / api / options) -->
<el-select v-else-if="item.type === 'select'" v-model="localModel[item.prop]"
:multiple="!!item.multiple" :placeholder="item.placeholder || `请选择${item.label}`"
:clearable="true" filterable :loading="remoteLoading[item.prop] || false" @change="() => { }"
@focus="() => loadRemoteOptions(item)"
:clearable="true" filterable :loading="remoteLoading[item.prop] || false"
@change="(val) => handleModelChange(val, item)" @focus="() => loadRemoteOptions(item)"
@filter-change="(keyword) => handleFilterChange(keyword, item)">
<el-option v-for="opt in getSelectOptions(item)" :key="opt.value" :label="opt.label"
:value="opt.value" />
</el-select>
<!-- Date -->
<el-date-picker v-else-if="item.type === 'date'" v-model="localModel[item.prop]" type="date"
:placeholder="`选择${item.label}`" value-format="YYYY-MM-DD" style="width: 100%"
:disabled-date="getDisabledDateFn(item)" @change="() => { }" />
:placeholder="`选择${item.label}`" :value-format="item.valueFormat || 'YYYY-MM-DD'"
style="width: 100%" :disabled-date="getDisabledDateFn(item)"
@change="(val) => handleModelChange(val, item)" />
<!-- Month -->
<el-date-picker v-else-if="item.type === 'month'" v-model="localModel[item.prop]" type="month"
:placeholder="`选择${item.label}`" :value-format="item.valueFormat || 'YYYY-MM'"
style="width: 100%" :disabled-date="getDisabledDateFn(item)"
@change="(val) => handleModelChange(val, item)" />
<!-- Daterange -->
<el-date-picker v-else-if="item.type === 'daterange'" v-model="localModel[item.prop]"
type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
value-format="YYYY-MM-DD" :disabled-date="getDisabledDateFn(item)" style="width: 100%"
@change="() => { }" />
:value-format="item.valueFormat || 'YYYY-MM-DD'" :disabled-date="getDisabledDateFn(item)"
style="width: 100%" @change="(val) => handleModelChange(val, item)" />
<!-- Checkbox Group -->
<el-checkbox-group v-else-if="item.type === 'checkbox-group'" v-model="localModel[item.prop]"
@change="() => { }">
@change="(val) => handleModelChange(val, item)">
<el-checkbox v-for="opt in getSelectOptions(item)" :key="opt.value" :label="opt.value">
{{ opt.label }}
</el-checkbox>
</el-checkbox-group>
<span v-else>Unsupported type: {{ item.type }}</span>
<!-- textarea -->
<el-input v-else-if="item.type === 'textarea'" v-model="localModel[item.prop]" style="width: 240px"
autosize type="textarea" placeholder="请输入" :clearable="true"
@change="(val) => handleModelChange(val, item)" />
<span v-else>不支持的类型: {{ item.type }}</span>
</el-form-item>
</el-col>
</el-row>
......@@ -131,11 +141,14 @@ const props = defineProps({
isSearch: { type: Boolean, default: false }
})
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(['update:modelValue', 'update'])
// ==================== Refs ====================
const formRef = ref(null)
const localModel = ref({})
// 使用 shallowRef 避免深层响应式(表单通常扁平)
const localModel = ref({ ...props.modelValue })
// 记录哪些字段的字典已加载
const dictLoaded = ref(new Set())
const internalConfig = ref([])
const remoteOptions = ref({}) // { prop: [options] }
const remoteLoading = ref({}) // { prop: boolean }
......@@ -158,18 +171,89 @@ const formRules = computed(() => {
return rules
})
// ==================== 双向绑定 ====================
watch(() => props.modelValue, (newVal) => {
if (newVal && typeof newVal === 'object') {
localModel.value = { ...newVal }
// 1. 外部 modelValue 变化时,安全同步(仅当内容不同时)
// 监听 config 变化(支持动态 config)
watch(
() => props.config,
(newConfig) => {
if (!newConfig || newConfig.length === 0) return
internalConfig.value = deepCloneConfig(newConfig)
// 构建初始模型
const initialModel = {}
for (const item of internalConfig.value) {
const key = item.prop
// 优先用父传值,否则用默认值
if (props.modelValue?.[key] !== undefined) {
initialModel[key] = props.modelValue[key]
} else if (item.multiple || ['checkbox-group', 'daterange'].includes(item.type)) {
initialModel[key] = item.defaultValue ?? []
} else {
initialModel[key] = item.defaultValue ?? ''
}
}
localModel.value = initialModel
},
{ immediate: true }
)
watch(
() => props.modelValue,
(newVal) => {
if (!newVal) return
// 只同步存在的字段,避免污染
for (const item of internalConfig.value) {
const key = item.prop
if (newVal.hasOwnProperty(key)) {
localModel.value[key] = newVal[key]
}
}
}
)
// 当字典加载完成时,触发同步
function markDictLoaded(prop) {
dictLoaded.value.add(prop)
// 尝试同步该字段
if (props.modelValue?.[prop] !== undefined) {
localModel.value[prop] = props.modelValue[prop]
}
}
// 2. 用户操作导致 localModel 变化时,emit(防抖可选)
function handleModelChange(value, item) {
console.log('✅ handleModelChange 被调用', { prop: item?.prop, value })
// 同步额外字段
if (item?.type === 'select' && item.onChangeExtraFields) {
const options = getSelectOptions(item)
const opt = options.find(o => o.value === value) // ✅ 现在 value 和 o.value 类型一致
if (opt) {
for (const [targetProp, sourceKey] of Object.entries(item.onChangeExtraFields)) {
localModel.value[targetProp] = opt.raw[sourceKey]
console.log(`✅ 同步 ${targetProp} =`, opt.raw[sourceKey])
}
}
}
}, { immediate: true, deep: true })
watch(localModel, (newVal) => {
if (props.modelValue !== undefined) {
emit('update:modelValue', { ...newVal })
// emit 更新
nextTick(() => {
if (!isEqualShallow(props.modelValue, localModel.value)) {
console.log('准备 emit modelValue:', localModel.value)
emit('update:modelValue', { ...localModel.value })
}
})
}
// 辅助函数:浅比较两个对象
function isEqualShallow(a, b) {
const keysA = Object.keys(a)
const keysB = Object.keys(b)
if (keysA.length !== keysB.length) return false
for (let key of keysA) {
if (a[key] !== b[key]) return false
}
}, { deep: true })
return true
}
// ==================== 加载字典选项 ====================
async function loadDictOptions(dictType) {
......@@ -184,7 +268,8 @@ async function loadDictOptions(dictType) {
const resp = await getDicts(dictType)
options = resp.data.map(p => ({
label: p.itemLabel,
value: p.itemValue
value: p.itemValue,
raw: p
}))
dictStore.setDict(dictType, options)
return options
......@@ -201,7 +286,7 @@ onMounted(async () => {
const initialData = {}
const dictTypePromises = []
const apiPromises = [] // ← 新增:收集 api 加载 promise
for (const item of internalConfig.value) {
const key = item.prop
if (localModel.value[key] == null) {
......@@ -219,10 +304,17 @@ onMounted(async () => {
dictTypePromises.push(
loadDictOptions(item.dictType).then(opts => {
remoteOptions.value[key] = opts
markDictLoaded(key) // ← 立即标记已加载
})
)
} // 预加载 api(远程接口)← 关键新增!
else if (item.type === 'select' && item.api) {
apiPromises.push(
loadRemoteOptionsForInit(item) // ← 专门用于初始化的加载函数
)
} else if (item.type === 'select' && item.options) {
remoteOptions.value[key] = [...item.options]
markDictLoaded(key)
}
// api 类型:延迟加载(focus 时)
}
......@@ -237,9 +329,57 @@ onMounted(async () => {
// ==================== 获取 select 选项 ====================
function getSelectOptions(item) {
return remoteOptions.value[item.prop] || []
const key = item.prop
// 字典选项
if (item.dictType || item.api) {
// 字典选项
return (remoteOptions.value[key] || []).map(opt => ({
value: opt.value,
label: opt.label,
raw: opt.raw // ← 必须存 raw
}))
} else if (item.options) {
// 静态选项s
return item.options.map(opt => ({
value: opt.value,
label: opt.label,
raw: opt.raw // 保留原始
}))
}
return []
}
async function loadRemoteOptionsForInit(item) {
const { prop, api, requestParams = {} } = item
try {
// 构造请求体:只传 requestParams,不传 keyword
const payload = {
...(requestParams || {})
}
const res = await request({
url: api,
method: 'post',
data: payload
})
const list = typeof item.transform === 'function'
? item.transform(res)
: res.data?.records || res.data || []
// 建议:统一转成字符串(或根据 item.valueType 判断)
const newOptions = list.map(i => ({
value: String(i[item.valueKey || 'value']), // ← 强制转字符串
label: i[item.labelKey || 'label'],
raw: i
}))
remoteOptions.value[prop] = newOptions
markDictLoaded(prop) // ← 关键:标记已加载
} catch (err) {
ElMessage.error(`预加载 ${item.label} 失败`)
remoteOptions.value[prop] = []
}
}
// ==================== 加载远程 API 选项(首次 focus 时加载,无搜索词) ====================
async function loadRemoteOptions(item) {
const { prop, api, requestParams = {}, keywordField, debounceWait, ...rest } = item
......@@ -264,10 +404,15 @@ async function loadRemoteOptions(item) {
? item.transform(res)
: res.data?.records || res.data || []
remoteOptions.value[prop] = list.map(i => ({
value: i[item.valueKey || 'value'],
label: i[item.labelKey || 'label']
// 建议:统一转成字符串(或根据 item.valueType 判断)
const newOptions = list.map(i => ({
value: String(i[item.valueKey || 'value']), // ← 强制转字符串
label: i[item.labelKey || 'label'],
raw: i
}))
remoteOptions.value[prop] = newOptions
// ✅ 关键:标记该字段字典已加载
markDictLoaded(prop)
} catch (err) {
ElMessage.error(`加载 ${item.label} 失败`)
remoteOptions.value[prop] = []
......@@ -305,7 +450,8 @@ function handleFilterChange(keyword, item) {
remoteOptions.value[prop] = list.map(i => ({
value: i[item.valueKey || 'value'],
label: i[item.labelKey || 'label']
label: i[item.labelKey || 'label'],
raw: i // ← 保存完整对象
}))
} catch (err) {
ElMessage.error(`搜索 ${item.label} 失败`)
......@@ -323,29 +469,42 @@ function handleNumberInput(value, item) {
let result = String(value ?? '').trim()
if (inputType === 'integer') {
// 只保留数字
result = result.replace(/[^\d]/g, '')
} else if (inputType === 'decimal') {
result = result.replace(/[^\d.]/g, '').replace(/^\./, '')
// 1. 只保留数字和小数点
result = result.replace(/[^\d.]/g, '')
// 2. 去掉开头的小数点(不允许 ".5" → 改为 "0.5" 更好,但这里先简单处理)
if (result.startsWith('.')) {
result = '0.' + result.slice(1)
}
// 3. 保证最多一个小数点
const parts = result.split('.')
if (parts.length > 2) {
result = parts[0] + '.' + parts.slice(1).join('')
}
// 4. 限制小数位数(但保留结尾的小数点!)
if (result.includes('.')) {
const [intPart, decPart] = result.split('.')
const trimmedDec = decPart.slice(0, decimalDigits)
result = intPart + (trimmedDec ? '.' + trimmedDec : '')
if (result.endsWith('.')) result = result.slice(0, -1)
// 如果小数部分超过限制,截断
if (decPart.length > decimalDigits) {
result = intPart + '.' + decPart.slice(0, decimalDigits)
}
// ✅ 不再删除结尾的 '.'
}
}
localModel.value = { ...localModel.value, [prop]: result }
// 防止重复赋值(可选优化)
if (localModel.value[prop] !== result) {
localModel.value = { ...localModel.value, [prop]: result }
}
}
// ==================== 暴露方法 ====================
defineExpose({
getSearchParams() {
return { ...localModel.value }
},
getFormData() {
return { ...localModel.value }
},
......
<!-- 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
......@@ -10,7 +10,7 @@
<!-- 列表区域 -->
<template #table>
<!-- 统计信息卡片 -->
<div class="statistics-container" v-if="statisticsData.totalPolicyCount > 0">
<div class="statistics-container" v-if="statisticsData.totalInAmount > 0">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
......@@ -23,8 +23,8 @@
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalPremiumAmount) }}</div>
<div class="card-label">总保</div>
<div class="card-value">{{ statisticsData.totalPolicyCount }}</div>
</div>
</el-card>
</el-col>
......@@ -32,7 +32,7 @@
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保费(HKD)</div>
<div class="card-value">{{ statisticsData.reconciliationCompanyCount }}</div>
<div class="card-value">{{ statisticsData.totalPremium }}</div>
</div>
</el-card>
</el-col>
......@@ -40,7 +40,7 @@
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">待出账金额</div>
<div class="card-value">{{ statisticsData.reconciliationRecordCount }}</div>
<div class="card-value">{{ statisticsData.pendingOutAmount }}</div>
</div>
</el-card>
</el-col>
......@@ -48,7 +48,7 @@
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">可出账金额</div>
<div class="card-value">{{ statisticsData.reconciliationSuccessCount }}</div>
<div class="card-value">{{ statisticsData.availableOutAmount }}</div>
</div>
</el-card>
</el-col>
......@@ -56,32 +56,43 @@
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">差额(估)</div>
<div class="card-value">{{ formatCurrency(statisticsData.reconciliationFailureAmount) }}</div>
<div class="card-value">{{ formatCurrency(statisticsData.differenceAmount) }}</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<el-table :data="tableData" height="400" border highlight-current-row style="width: 100%" v-loading="loading">
<el-table :data="tableData" @selection-change="handleSelectionChange" height="400" border highlight-current-row
style="width: 100%" v-loading="loading">
<el-table-column type="selection" width="40" />
<el-table-column prop="fortuneBizType" label="应付单类型" width="120" sortable>
<template #default="{ row }">
{{ getFortuneBizTypeLabel(row.fortuneBizType) }}
</template>
</el-table-column>
<el-table-column prop="policyNo" label="保单号" width="120" sortable />
<el-table-column prop="reconciliationCompany" label="保险公司" width="120" sortable />
<el-table-column prop="payableNo" label="累积已入账金额" width="120" sortable />
<el-table-column prop="payableNo" label="累积已入账比例" width="120" sortable />
<el-table-column prop="payableNo" label="出账项目" width="130" sortable />
<el-table-column prop="payableNo" label="出账期数" width="130" sortable />
<el-table-column prop="payableNo" label="总期数" width="120" sortable />
<el-table-column prop="payableNo" label="转介人" width="130" sortable />
<el-table-column prop="currency" label="所属团队" width="120" sortable />
<el-table-column prop="payableNo" label="应出账金额" width="140" sortable />
<el-table-column prop="commissionPeriod" label="出账币种" width="130" sortable />
<el-table-column prop="totalPeriod" label="已出账金额" width="120" sortable />
<el-table-column prop="commissionName" label="剩余出账金额" width="120" sortable />
<el-table-column prop="premium" label="本期出账金额" width="120" sortable />
<el-table-column prop="remark" label="剩余出账比例" width="120" sortable />
<el-table-column prop="status" label="出账状态" width="120" sortable />
<el-table-column prop="status" label="期交保费" width="120" sortable />
<el-table-column prop="status" label="出账日(实)" width="120" sortable />
<el-table-column prop="status" label="备注" width="120" sortable />
<el-table-column prop="insuranceCompany" label="保险公司" width="120" sortable />
<el-table-column prop="commissionPaidAmount" label="累积已入账金额" width="120" sortable />
<el-table-column prop="commissionPaidRatio" label="累积已入账比例" width="120" sortable />
<el-table-column prop="fortuneName" label="出账项目" width="130" sortable />
<el-table-column prop="fortunePeriod" label="出账期数" width="130" sortable />
<el-table-column prop="fortuneTotalPeriod" label="总期数" width="120" sortable />
<el-table-column prop="broker" label="转介人" width="130" sortable />
<el-table-column prop="team" label="所属团队" width="120" sortable />
<el-table-column prop="amount" label="应出账金额" width="140" sortable />
<el-table-column prop="currency" label="出账币种" width="130" sortable />
<el-table-column prop="fortunePaidAmount" label="已出账金额" width="120" sortable />
<el-table-column prop="fortuneUnpaidAmount" label="剩余出账金额" width="120" sortable />
<el-table-column prop="currentPaymentAmount" label="本期出账金额" width="120" sortable />
<el-table-column prop="fortuneUnpaidRatio" label="剩余出账比例" width="120" sortable />
<el-table-column prop="status" label="出账状态" width="120" sortable>
<template #default="{ row }">
{{ getDictLabel('csf_fortune_status', row.status) }}
</template>
</el-table-column>
<el-table-column prop="premium" label="期交保费" width="120" sortable />
<el-table-column prop="payoutDate" label="出账日(实)" width="120" sortable />
<el-table-column prop="remark" label="备注" width="120" sortable />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="{ row }">
<el-popover placement="right" :width="200" trigger="click">
......@@ -99,12 +110,28 @@
</template>
</el-table-column>
</el-table>
<!-- 表格操作菜单 -->
<div class="tableOptionContainer">
<el-button type="primary" :icon="Select" :disabled="updatePayRollStatusDisable"
@click="downloadPolicyFortuneAccountapi">完成检核</el-button>
</div>
</template>
</CommonPage>
<!-- 新增出账检核页面-->
<CommonDialog dialogTitle='新增出账检核' dialogWidth='80%' :openDialog=dialogFlag :showAction='false' :showClose='true'
@close='dialogFlag = false'>
<CommonDialog dialogTitle='新增出账检核' dialogWidth='80%' :openDialog='addCheckRecordFormDialogFlag' :showAction='true'
:showClose='true' @close='addCheckRecordFormDialogFlag = false' @confirm='addCheckRecordaddBatchapi'>
<SearchForm ref="addCheckRecordFormRef" :config="addCheckRecordConfig" v-model="addCheckRecordFormModel" />
</CommonDialog>
<!-- 导入出账检核页面 -->
<CommonDialog dialogTitle='新增出账检核' dialogWidth='80%' :openDialog='importCheckRecordFlag' :showAction='true'
:showClose='true' @close='importCheckRecordFlag = false'>
<FileUploadPreview :header-row="0" :required-fields="'amount,exchangeRate'" :use-chinese-header="true"
:transform-submit-data="transformToBackend" @submit="onSubmit" />
</CommonDialog>
<!-- 设置本期出账金额 -->
<CommonDialog dialogTitle='设置本期出账金额' dialogWidth='80%' :openDialog='setPayoutAmountDialogFlag' :showAction='true'
:showClose='true' @close='setPayoutAmountDialogFlag = false' @confirm='updatePayoutAmountapi'>
<SearchForm ref="setPayoutAmountFormRef" :config="setPayoutAmountConfig" v-model="setPayoutAmountFormModel" />
</CommonDialog>
</div>
</template>
......@@ -117,8 +144,9 @@ import SearchForm from '@/components/SearchForm/SearchForm.vue'
import { ElMessage } from 'element-plus'
import { formatCurrency } from '@/utils/number'
// 接口
import { getPolicyFortuneList } from '@/api/financial/commission'
import { getPolicyFortuneList, addCheckRecordaddBatch, updatePayoutAmount, downloadPolicyFortuneAccount } from '@/api/financial/commission'
import useUserStore from '@/store/modules/user'
import { loadDicts, getDictLabel } from '@/utils/useDict'
const userStore = useUserStore()
// 分页相关
......@@ -126,6 +154,7 @@ const currentPage = ref(1)
const pageSize = ref(10)
const pageTotal = ref(0)
const loading = ref(false)
const selectedRow = ref(null)
const searchFormRef = ref(null)
const searchParams = ref({})
const searchConfig = ref([
......@@ -135,11 +164,11 @@ const searchConfig = ref([
label: '保单号'
}, {
type: 'select',
prop: 'status',
prop: 'statusList',
label: '出账状态',
multiple: true,
dictType: 'csf_fortune_status'
},{
}, {
type: 'select',
prop: 'insuranceCompanyBizIdList',
label: '保险公司',
......@@ -148,6 +177,7 @@ const searchConfig = ref([
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入保险公司名称搜索',
debounceWait: 500, // 自定义防抖时间
multiple: true,
valueKey: 'insuranceCompanyBizId',
labelKey: 'abbreviation',
transform: (res) => {
......@@ -166,6 +196,7 @@ const searchConfig = ref([
},
placeholder: '输入产品计划名称搜索',
debounceWait: 500, // 自定义防抖时间
multiple: true,
valueKey: 'productLaunchBizId',
labelKey: 'productName',
transform: (res) => {
......@@ -177,7 +208,7 @@ const searchConfig = ref([
label: '出账日(估)',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间'
},
},
// {
// type: 'select',
// prop: 'status',
......@@ -189,10 +220,132 @@ const searchConfig = ref([
// 表格操作菜单
const dropdownItems = [
{ label: '设置本期出账金额', value: 'setPayRoll' },
{ label: '更新', value: 'editRecord' },
{ label: '查看记录', value: 'viewRecord' }
// { label: '更新', value: 'editRecord' },
// { label: '查看记录', value: 'viewRecord' }
]
// 应收单类型
const fortuneBizTypeOptions = [
{ value: 'R', label: '关联保单应付单' },
{ value: 'U', label: '非关联保单应付单' }
]
// 应付单类型通过value转成label
const getFortuneBizTypeLabel = (value) => {
const item = fortuneBizTypeOptions.find(item => item.value === value)
return item?.label || ''
}
// 新增出账检核
const addCheckRecordFormModel = ref({})
const addCheckRecordFormRef = ref(null)
const addCheckRecordFormDialogFlag = ref(false)
const addCheckRecordConfig = [
{
type: 'select',
prop: 'fortuneBizType',
label: '应付单类型',
options: fortuneBizTypeOptions
}, {
type: 'select',
prop: 'status',
label: '出账状态',
dictType: 'csf_expected_fortune_status'
}, {
type: 'input',
prop: 'policyNo',
label: '关联保单号',
visible: (formData) => formData.fortuneBizType === 'R',
}, {
type: 'input',
prop: 'fortunePeriod',
label: '佣金期数',
inputType: 'decimal',
visible: (formData) => formData.fortuneBizType === 'R',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
}, {
type: 'input',
prop: 'fortuneTotalPeriod',
label: '总期数',
inputType: 'decimal',
visible: (formData) => formData.fortuneBizType === 'R',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
}, {
type: 'date',
prop: 'actualPayoutDate',
label: '出账日(实)',
placeholder: '请选择',
maxDate: 'today'
}, {
type: 'input',
prop: 'amount',
label: '出账金额',
inputType: 'decimal',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
}, {
type: 'select',
prop: 'currency',
label: '出账币种',
dictType: 'bx_currency_type'
}, {
type: 'select',
prop: 'fortuneType',
label: '出账项目',
dictType: 'csf_fortune_type'
}, {
type: 'select',
prop: 'brokerBizId',
label: '转介人',
api: '/insurance/base/api/userSaleExpand/page',
keywordField: 'realName',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入转介人名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'userSaleBizId',
labelKey: 'realName',
onChangeExtraFields: {
broker: 'realName',// 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code'
},
transform: (res) => {
return res?.data.records || []
}
}
]
// 设置本期出账金额
const setPayoutAmountFormModel = ref({})
const setPayoutAmountFormRef = ref(null)
const setPayoutAmountDialogFlag = ref(false)
const setPayoutAmountConfig = [
{
type: 'input',
prop: 'currentPaymentAmount',
label: '出账金额',
inputType: 'decimal',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
}, {
type: 'select',
prop: 'currency',
label: '出账币种',
dictType: 'bx_currency_type'
}, {
type: 'select',
prop: 'status',
label: '出账状态',
dictType: 'csf_expected_fortune_status'
}
]
// 统计信息
const statisticsData = ref({})
// 弹窗相关
......@@ -200,10 +353,10 @@ const dialogFlag = ref(false)
// 按钮事件处理
const handleAdd = () => {
ElMessage.info('点击新增按钮')
addCheckRecordFormDialogFlag.value = true
}
const handleImport = () => {
ElMessage.info('点击导入按钮')
importCheckRecordFlag.value = true
}
const handleExport = () => {
ElMessage.info('点击导出按钮')
......@@ -288,11 +441,114 @@ const handleCurrentChange = (val) => {
// 表格数据
const tableData = ref([])
const handleSelect = (row) => {
console.log('选中行:', row)
const handleSelect = (e, row) => {
console.log('选中行:', e, row)
selectedRow.value = row
if (e === 'setPayRoll') {
setPayoutAmountDialogFlag.value = true
}
}
const addCheckRecordaddBatchapi = async (data) => {
const formData = addCheckRecordFormRef.value.getFormData()
console.log('新增出账检核记录:', formData)
const params = [
{ ...formData }
]
try {
const res = await addCheckRecordaddBatch(params)
if (res.code === 200) {
ElMessage.success('新增出账检核记录成功')
addCheckRecordFormDialogFlag.value = false
addCheckRecordFormRef.value.resetForm()
loadTableData()
} else {
ElMessage.error(res.msg || '新增出账检核记录失败')
}
} catch (error) {
console.error('新增出账检核记录失败:', error)
ElMessage.error(error.message || '新增出账检核记录失败')
}
}
// 设置本期出账金额
const updatePayoutAmountapi = async (data) => {
console.log('设置本期出账金额:', data)
const params = {
...setPayoutAmountFormRef.value.getFormData(),
fortuneBizId: selectedRow.value.fortuneBizId
}
try {
const res = await updatePayoutAmount(params)
if (res.code === 200) {
ElMessage.success('设置本期出账金额成功')
loadTableData()
} else {
ElMessage.error(res.msg || '设置本期出账金额失败')
}
} catch (error) {
console.error('设置本期出账金额失败:', error)
ElMessage.error(error.message || '设置本期出账金额失败')
}
}
const updatePayRollStatusDisable = ref(true)
const multipleSelection = ref([])
const handleSelectionChange = (val) => {
multipleSelection.value = val
console.log('全选:', val)
// 完成检核按钮是否禁用
updatePayRollStatusDisable.value = val.length === 0
}
const downloadPolicyFortuneAccountapi = async (data) => {
console.log('生成出账清单:', data)
try {
const res = await downloadPolicyFortuneAccount({
fortuneBizIdList: multipleSelection.value.map(item => item.fortuneBizId)
})
if (res.code === 200) {
ElMessage.success('完成检核,等待关账')
loadTableData()
}
} catch (error) {
console.error('检核失败:', error)
ElMessage.error(error.response?.data?.msg || '检核失败')
}
}
import FileUploadPreview from '@/components/fileUploadPreview/fileUploadPreview.vue'
const importCheckRecordFlag = ref(false)
// 如果后端接收的就是当前格式,可直接透传
const formatForBackend = (rows) => {
return rows.map(row => ({
...row,
amount: Number(row.amount) || 0,
exchangeRate: Number(row.exchangeRate) || 1
}))
}
const onSubmit = (data) => {
console.log('提交给后端的数据:', data)
// 调用保存 API
}
// 获取入账状态,字典值转化方法
onMounted(async () => {
try {
await loadDicts(['csf_fortune_status'])
} catch (error) {
console.error('字典加载失败', error)
} finally {
loading.value = false
}
})
</script>
<style scoped></style>
\ No newline at end of file
<style scoped>
.tableOptionContainer {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
</style>
\ No newline at end of file
......@@ -88,12 +88,23 @@
</div>
<el-table :data="tableData" ref="multipleTableRef" height="400" row-key="id" border highlight-current-row
style="width: 100%" v-loading="loading" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="40" />
<el-table-column type="selection" width="40" :selectable="selectableFn" />
<el-table-column prop="commissionStatus" label="比对状态" width="120" sortable fixed="left">
<template #default="{ row }">
{{ getDictLabel('csf_commission_status', row.commissionStatus) }}
</template>
</el-table-column>
<el-table-column prop="commissionBizType" label="业务类型" width="120" sortable>
<template #default="{ row }">
{{ getCommissionBizTypeLabel(row.commissionBizType) }}
</template>
</el-table-column>
<el-table-column prop="reconciliationYearMonth" label="检核年月" width="120" sortable />
<el-table-column prop="policyNo" label="保单号" width="120" sortable />
<el-table-column prop="reconciliationCompany" label="对账公司" width="120" sortable />
<el-table-column prop="commissionDate" label="入账日" width="120" sortable />
<el-table-column prop="status" label="入账状态" width="120" sortable :formatter="formatStatus" />
<el-table-column prop="commissionExpectedStatus" label="入账状态" width="120" sortable
:formatter="formatStatus" />
<el-table-column prop="currentCommissionRatio" label="本次入账比例" width="130" sortable />
<el-table-column prop="periodPaidRatio" label="累积入账比例" width="130" sortable />
<el-table-column prop="periodPendingRatio" label="待入账比例" width="120" sortable />
......@@ -105,7 +116,11 @@
<el-table-column prop="commissionName" label="入账项目" width="120" sortable />
<el-table-column prop="premium" label="期交保费" width="120" sortable />
<el-table-column prop="remark" label="备注" width="120" sortable />
<el-table-column prop="status" label="记录状态" width="120" sortable />
<el-table-column prop="isDeleted" label="记录状态" width="120" sortable>
<template #default="{ row }">
{{ row.isDeleted === 1 ? '无效' : '有效' }}
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="{ row }">
<el-popover placement="right" :width="200" trigger="click">
......@@ -135,17 +150,17 @@
@close='dialogFlag = false'>
<el-row>
<el-col :xs="24" :sm="24" :md="24" :lg="24">
<SearchForm ref="checkFormRef" :config="checkConfig" />
<SearchForm ref="checkFormRef" :config="checkConfig" v-model="checkFormData" />
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="3" :lg="3">
<el-button type="primary" :icon="Plus" :disabled="!checkFormRef?.getFormData().reconciliationYearMonth"
@click="addCheckRecordDialogFlag = true; searchParams = checkFormRef.getFormData()">新增</el-button>
<el-button type="primary" :icon="Plus" :disabled="!searchParams?.reconciliationYearMonth"
@click="addCheckRecordDialogFlag = true;">新增</el-button>
</el-col>
<el-col :xs="24" :sm="24" :md="3" :lg="3">
<el-button type="primary" :icon="Upload" :disabled="!checkFormRef?.getFormData().reconciliationYearMonth"
@click="fileUploadDialogFlag = true; searchParams = checkFormRef.getFormData()">导入</el-button>
<el-button type="primary" :icon="Upload" :disabled="!searchParams?.reconciliationYearMonth"
@click="fileUploadDialogFlag = true;">导入</el-button>
</el-col>
<el-col :xs="24" :sm="24" :md="3" :lg="3">
<el-button link type="primary">下载导入模板 </el-button>
......@@ -154,10 +169,18 @@
<el-row :gutter="10">
<el-col :xs="24" :sm="24" :md="24" :lg="24">
<el-table :data="checkRecordTableData" style="width: 100%">
<el-table-column prop="commissionBizType" label="应收款类型" width="120" />
<el-table-column prop="receivableNo" label="应收款编号" width="120" />
<el-table-column prop="commissionBizType" label="应收款类型" width="120">
<template #default="{ row }">
{{ getCommissionBizTypeLabel(row.commissionBizType) }}
</template>
</el-table-column>
<!-- <el-table-column prop="receivableNo" label="应收款编号" width="120" /> -->
<el-table-column prop="policyNo" label="关联保单号" width="120" />
<el-table-column prop="status" label="比对状态" width="120" />
<el-table-column prop="commissionStatus" label="比对状态" width="120" fixed="left">
<template #default="{ row }">
{{ getDictLabel('csf_commission_status', row.commissionStatus) }}
</template>
</el-table-column>
<el-table-column prop="commissionPeriod" label="佣金期数" width="120" />
<el-table-column prop="totalPeriod" label="总期数" width="120" />
<el-table-column prop="commissionDate" label="入账日(实)" width="120" />
......@@ -166,12 +189,17 @@
<el-table-column prop="commissionName" label="入账项目" width="120" />
<el-table-column prop="reconciliationCompany" label="对账公司" width="120" />
<!-- <el-table-column prop="zip" label="入账状态" width="120" /> -->
<el-table-column fixed="right" label="操作" min-width="120">
<template #default>
<el-button link type="primary" size="small" @click="checkRecordEdit(row)">
<el-table-column fixed="right" label="操作" min-width="180">
<template #default="{ row }">
<el-button type="primary" size="default" @click="checkRecordEdit(row)">
修改
</el-button>
<el-button link type="primary" size="small">删除</el-button>
<el-popconfirm confirm-button-text="Yes" cancel-button-text="No" :icon="InfoFilled" icon-color="#626AEF"
title="确认要删除吗?" @confirm="deletePolicyCommissionApi(row)">
<template #reference>
<el-button>删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
......@@ -181,33 +209,35 @@
</CommonDialog>
<!-- 新增检核记录弹窗 -->
<CommonDialog dialogTitle='新增检核记录' dialogWidth='80%' :openDialog=addCheckRecordDialogFlag :showAction='true'
:showClose='true' @close='addCheckRecordDialogFlag = false' @confirm='handleAddCheckRecord()'>
<CommonDialog :dialogTitle='editStatus.value == "add" ? "新增检核记录" : "修改检核记录"' dialogWidth='80%'
:openDialog=addCheckRecordDialogFlag :showAction='true' :showClose='true'
@close='addCheckRecordDialogFlag = false' @confirm='handleAddCheckRecord()'>
<el-row>
<el-col :xs="24" :sm="24" :md="24" :lg="24">
<SearchForm ref="addCheckRecordFormRef" :config="addCheckRecordConfig" />
<SearchForm ref="addCheckRecordFormRef" :config="addCheckRecordConfig" v-model="addReceivablesFormModel" />
</el-col>
</el-row>
</CommonDialog>
<CommonDialog dialogTitle='文件导入' dialogWidth='80%' :openDialog=fileUploadDialogFlag :showAction='true'
:showClose='true' @close='fileUploadDialogFlag = false; files = ""'>
<FileUpload v-model="files" :data="{ headerRow: 0 }" :file-type="['xlsx', 'xls']" :action="'/oss/api/excel/import'"
:limit="1" :fileSize="15" />
:showClose='true' @close='fileUploadDialogFlag = false; files = ""'
@confirm='fileUploadDialogFlag = false; files = ""'>
<FileUpload v-model="files" :data="{ reconciliationYearMonth: reconciliationYearMonth }"
:file-type="['xlsx', 'xls']" :action="'/csf/api/commission/upload/excel'" :limit="1" :fileSize="15" />
</CommonDialog>
<!-- 设置比对状态 -->
<CommonDialog dialogTitle='设置比对状态' dialogWidth='80%' :openDialog=setCompareStatusDialogFlag :showAction='true'
:showClose='true' @close='setCompareStatusDialogFlag = false;'>
<SearchForm ref="setCompareStatusFormRef" :config="setCompareStatusConfig" />
:showClose='true' @close='setCompareStatusDialogFlag = false;' @confirm='setCompareStatusapi'>
<SearchForm ref="setCompareStatusFormRef" :config="setCompareStatusConfig" v-model="setCompareStatusFormModel" />
</CommonDialog>
<!-- 更新数据 -->
<CommonDialog dialogTitle='更新数据' dialogWidth='80%' :openDialog=updateDataDialogFlag :showAction='true'
:showClose='true' @close='updateDataDialogFlag = false;'>
:showClose='true' @close='updateDataDialogFlag = false;' @confirm='updateDataapi'>
<SearchForm ref="updateDataFormRef" :config="updateDataConfig" v-model="updateDataFormModel" />
</CommonDialog>
<!-- 查看记录 -->
......@@ -226,11 +256,15 @@ import SearchForm from '@/components/SearchForm/SearchForm.vue'
import { ElMessage } from 'element-plus'
import { formatCurrency } from '@/utils/number'
// 接口
import { getPolicyCommissionList, generateCommissionRecord, addPayrollCheckRecord, commissionExpectedRecord } from '@/api/financial/commission'
import {
getPolicyCommissionList, generateCommissionRecord,
addPayrollCheckRecord, commissionExpectedRecord, updateCompareStatus, updateCommissionRecord, deletePolicyCommission
} from '@/api/financial/commission'
import { Plus, Upload, Select } from '@element-plus/icons-vue'
import FileUpload from '@/components/FileUpload/index.vue'
import { loadDicts, getDictLabel } from '@/utils/useDict'
const files = ref('')
const reconciliationYearMonth = ref('')
// 分页相关
const currentPage = ref(1)
const pageSize = ref(10)
......@@ -258,7 +292,7 @@ const searchConfig = ref([
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入对账公司名称搜索',
debounceWait: 500, // 自定义防抖时间
multiple: true,
multiple: true,
valueKey: 'reconciliationCompanyBizId',
labelKey: 'name',
transform: (res) => {
......@@ -293,27 +327,31 @@ const searchConfig = ref([
return res?.data.records || []
}
}, {
type: 'date',
type: 'month',
prop: 'reconciliationYearMonth',
label: '检核年月',
placeholder: '检核年月',
format: 'YYYY-MM',
valueFormat: 'YYYY-MM'
}
])
// 设置比对状态
const selectedRow = ref(null)
// 设置比对状态弹窗
const setCompareStatusDialogFlag = ref(false)
const setCompareStatusConfig = ref([
const setCompareStatusFormRef = ref(null)
const setCompareStatusConfig = [
{
type: 'select',
prop: 'status',
label: '比对状态',
dictType: 'csf_commission_status'
}
])
const setCompareStatusFormRef = ref(null)
]
const setCompareStatusFormModel = reactive({
status: ''
})
// 当比对状态为成功时可以选择
const selectableFn = (row) => row.commissionStatus == '1' && row.commissionBizType == 'R'
// 设置编辑状态,是新增还是修改
const editStatus = ref('add')
// 更新数据
const updateDataDialogFlag = ref(false)
// 查看记录
......@@ -323,22 +361,62 @@ const viewRecordDialogFlag = ref(false)
const fileUploadDialogFlag = ref(false)
const checkRecordTableData = ref([])
// 检核表单
const checkFormRef = ref(null)
const checkConfig = ref([
{
type: 'date',
type: 'month',
prop: 'reconciliationYearMonth',
label: '检核年月',
placeholder: '检核年月',
format: 'YYYY-MM',
valueFormat: 'YYYY-MM',
rules: [{ required: true, message: '请选择检核年月', trigger: 'blur' }]
}
])
const checkRecordEdit = (row) => {
// console.log('查看记录', row)
editStatus.value = 'edit'
addReceivablesFormModel.value = { ...row }
addCheckRecordDialogFlag.value = true
// console.log('父组件赋值',addReceivablesFormModel.value)
}
const checkFormData = ref({
reconciliationYearMonth: ''
})
// 监听 searchParams 变化,自动重新查询
watch(
() => ({ ...checkFormData.value }), // 深度监听对象内容
(newVal) => {
// console.log('搜索条件变化:', newVal)
// ✅ 在这里调用查询接口 或 触发列表刷新
if (newVal.reconciliationYearMonth) {
searchParams.value = checkFormRef.value.getFormData()
checkRecordQuery()
}
},
{ immediate: true } // 首次加载也执行(可选)
)
// 删除检核记录
const deletePolicyCommissionApi = async (row) => {
console.log(row)
try {
const res = await deletePolicyCommission({ commissionBizId: row.commissionBizId })
if (res.code === 200) {
ElMessage.success('删除成功')
checkRecordQuery()
return true
} else {
ElMessage.error(res.msg || '删除失败')
return false
}
} catch (error) {
ElMessage.error('删除失败')
return false
}
}
// 生成可出账记录按钮
......@@ -346,7 +424,7 @@ const updatePayRollStatusDisable = ref(true)
// 获取入账状态,字典值转化方法
onMounted(async () => {
try {
await loadDicts(['csf_expected_commission_status'])
await loadDicts(['csf_expected_commission_status', 'csf_commission_status'])
} catch (error) {
console.error('字典加载失败', error)
} finally {
......@@ -367,7 +445,8 @@ const getCommissionBizTypeLabel = (value) => {
const item = commissionBizTypeOptions.find(item => item.value === value)
return item?.label || ''
}
// 新增应收款管理
const addReceivablesFormModel = ref({})
// 新增检核记录表单
const addCheckRecordFormRef = ref(null)
const addCheckRecordConfig = ref([
......@@ -376,19 +455,28 @@ const addCheckRecordConfig = ref([
prop: 'commissionBizType',
label: '应收单类型',
placeholder: '应收单类型',
options: commissionBizTypeOptions
options: commissionBizTypeOptions,
rules: [
{ required: true, message: '请选择应收单类型', trigger: 'blur' },
]
},
{
type: 'input',
prop: 'policyNo',
label: '保单号'
label: '保单号',
visible: (formData) => formData.commissionBizType == 'R',
rules: [
{ required: true, message: '请输入保单号', trigger: 'blur' },
]
},
{
type: 'input',
prop: 'commissionPeriod',
label: '佣金期数',
inputType: 'decimal',
visible: (formData) => formData.commissionBizType == 'R',
rules: [
{ required: true, message: '请输入佣金期数', trigger: 'blur' },
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
}, {
......@@ -396,6 +484,7 @@ const addCheckRecordConfig = ref([
prop: 'totalPeriod',
label: '总期数',
inputType: 'decimal',
visible: (formData) => formData.commissionBizType == 'R',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
......@@ -403,47 +492,82 @@ const addCheckRecordConfig = ref([
type: 'date',
prop: 'commissionDate',
label: '入账日(实)',
placeholder: '请选择'
placeholder: '请选择',
maxDate: 'today',
rules: [
{ required: true, message: '请选择入账日(实)', trigger: 'blur' },
]
}, {
type: 'input',
prop: 'amount',
label: '入账金额',
inputType: 'decimal',
decimalDigits: 2,
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
{ required: true, message: '请输入金额', trigger: 'blur' },
{ pattern: /^\d+(\.\d{1,2})?$/, message: '最多两位小数', trigger: 'blur' }
]
}, {
type: 'select',
prop: 'currency',
label: '入账币种',
dictType: 'bx_currency_type'
dictType: 'bx_currency_type',
rules: [
{ required: true, message: '请选择入账币种', trigger: 'blur' },
]
}, {
type: 'select',
prop: 'commissionName',
prop: 'commissionInfo',
label: '入账项目',
dictType: 'csf_commission_type'
dictType: 'csf_commission_type',
valueKey: 'itemValue',
labelKey: 'itemLabel',
rules: [
{ required: true, message: '请选择入账项目', trigger: 'blur' },
],
onChangeExtraFields: {
commissionType: 'itemValue',// 自动同步 raw.name 到 reconciliationCompany
commissionName: 'itemLabel'
},
}, {
type: 'select',
prop: 'reconciliationCompanyBizIdList',
prop: 'reconciliationCompanyBizId',
label: '对账公司',
api: '/insurance/base/api/insuranceReconciliationCompany/page',
keywordField: 'name',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入对账公司名称搜索',
debounceWait: 500, // 自定义防抖时间
multiple: true,
onChangeExtraFields: {
reconciliationCompany: 'name',// 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code'
},
valueKey: 'reconciliationCompanyBizId',
labelKey: 'name',
transform: (res) => {
console.log(res)
return res?.data.records || []
}
}
},
rules: [
{ required: true, message: '请选择对账公司', trigger: 'blur' },
]
}, {
type: 'input',
prop: 'exchangeRate',
label: '结算汇率(实)',
inputType: 'decimal',
decimalDigits: 2,
rules: [
{ required: true, message: '请输入金额', trigger: 'blur' },
{ pattern: /^\d+(\.\d{1,2})?$/, message: '最多两位小数', trigger: 'blur' }
]
},
])
const addCheckRecordDialogFlag = ref(false)
const handleAddCheckRecord = async () => {
try {
const addCheckSearchParams = addCheckRecordFormRef.value.getFormData()
console.log('新增检核记录', addCheckSearchParams)
const params = {
...addCheckSearchParams,
reconciliationYearMonth: searchParams.value.reconciliationYearMonth
......@@ -473,13 +597,14 @@ const generateCommissionRecordapi = async () => {
console.error('生成可出账记录失败', error)
ElMessage.error('生成可出账记录失败')
}
}
// 表格操作菜单
const dropdownItems = [
{ label: '设置比对状态', value: 'setStatus' },
{ label: '更新数据', value: 'editRecord' },
{ label: '查看记录', value: 'viewRecord' }
{ label: '同步到应收款管理', value: 'syncToReceivable' },
// { label: '更新数据', value: 'editRecord' },
// { label: '查看记录', value: 'viewRecord' }
]
// 统计信息
......@@ -512,7 +637,9 @@ const handleQuery = async () => {
const checkRecordQuery = async () => {
try {
const params = {
reconciliationYearMonth: searchParams.value.reconciliationYearMonth
reconciliationYearMonth: searchParams.value.reconciliationYearMonth,
pageNo: 1,
pageSize: 30
}
const res = await commissionExpectedRecord(params)
checkRecordTableData.value = res.data.records || []
......@@ -566,7 +693,7 @@ const loadTableData = async (searchParams = {}) => {
...searchParams,
commissionDateStart: searchParams.commissionDate?.[0] || undefined,
commissionDateEnd: searchParams.commissionDate?.[1] || undefined,
commissionDate:undefined
commissionDate: undefined
}
const res = await getPolicyCommissionList(params)
tableData.value = res.data.page.records || []
......@@ -595,19 +722,68 @@ const handleCurrentChange = (val) => {
const tableData = ref([])
const handleSelect = (e, row) => {
selectedRow.value = row
console.log('选中行:', e, row)
if (row === 'setStatus') {
if (e === 'setStatus') {
setCompareStatusDialogFlag.value = true
return
} else if (row === 'editRecord') {
} else if (e === 'editRecord') {
updateDataDialogFlag.value = true
return
} else if (row === 'viewRecord') {
} else if (e === 'viewRecord') {
viewRecordDialogFlag.value = true
return
}
}
// 设置比对状态api
const setCompareStatusapi = async () => {
try {
const res = await updateCompareStatus({
...setCompareStatusFormRef.value.getFormData(),
commissionBizId: selectedRow.value.commissionBizId
})
if (res.code === 200) {
ElMessage.success('设置比对状态成功')
setCompareStatusDialogFlag.value = false
setCompareStatusFormRef.value.resetForm()
loadTableData()
} else {
ElMessage.error(res.msg || '设置比对状态失败')
}
} catch (error) {
console.error('设置比对状态失败', error)
ElMessage.error('设置比对状态失败')
}
}
// 更新数据api
const updateDataFormRef = ref(null)
const updateDataFormModel = ref({
commissionAmount: '',
commissionStatus: ''
})
const updateDataConfig = addCheckRecordConfig
const updateDataapi = async () => {
try {
const res = await updateCommissionRecord({
...updateDataFormRef.value.getFormData(),
commissionBizId: selectedRow.value.commissionBizId
})
if (res.code === 200) {
ElMessage.success('更新数据成功')
updateDataDialogFlag.value = false
updateDataFormRef.value.resetForm()
loadTableData()
} else {
ElMessage.error(res.msg || '更新数据失败')
}
} catch (error) {
console.error('更新数据失败', error)
ElMessage.error('更新数据失败')
}
}
</script>
......
......@@ -115,7 +115,7 @@ const searchConfig = ref([
type: 'select',
prop: 'statusList',
label: '出账状态',
dictType: 'csf_fortune_account_status',
dictType: 'csf_expected_fortune_status',
multiple: true,
}
])
......@@ -317,10 +317,10 @@ const dictLists = ref([])
// 获取出账状态字典值
const getDictLists = () => {
return new Promise((resolve, reject) => {
listType({ typeList: ['csf_fortune_status', 'bx_currency_type'] })
listType({ typeList: ['csf_expected_fortune_status', 'bx_currency_type'] })
.then(res => {
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 || []
console.log('获取到的字典数据:', dictLists.value)
// 处理币种字典值
......
......@@ -153,7 +153,7 @@
<!-- 新增出账记录 -->
<CommonDialog dialogTitle="新增出账记录" dialogWidth="80%" :openDialog="addPayRecordDialogVisible" :showAction="true"
:showClose="true" @close="addPayRecordDialogVisible = false" @confirm="handleConfirmAddPayRecord">
<SearchForm ref="addPayRecordFormRef" :config="addPayRecordFormConfig" />
<SearchForm ref="addPayRecordFormRef" :config="addPayRecordFormConfig" v-model="addPayRecordFormModel" />
</CommonDialog>
<!-- 设置出账状态 -->
<CommonDialog dialogTitle="设置出账状态" dialogWidth="80%" :openDialog="setPayRecordStatusDialogVisible"
......@@ -173,7 +173,7 @@ import { formatCurrency } from '@/utils/number'
import { expectedFortuneList, payRecordList, addPayRecord, userSaleExpandDetail } from '@/api/financial/commission'
import SearchForm from '@/components/SearchForm/SearchForm.vue'
import CommonDialog from '@/components/commonDialog'
import { getDictLabel } from '@/utils/useDict';
import { loadDicts, getDictLabel } from '@/utils/useDict'
import useUserStore from '@/store/modules/user'
// 设置出账状态
const setPayRecordStatusDialogVisible = ref(false)
......@@ -298,9 +298,12 @@ const searchConfig = ref([
])
const payRecordDialogTableVisible = ref(false)
// 新增出账记录
const addPayRecordFormModel = reactive({
fortuneBizType: 'U',
})
const addPayRecordDialogVisible = ref(false)
const addPayRecordFormRef = ref()
const addPayRecordFormConfig = ref([
const addPayRecordFormConfig = [
{
type: 'select',
prop: 'fortuneBizType',
......@@ -312,12 +315,14 @@ const addPayRecordFormConfig = ref([
}, {
type: 'input',
prop: 'policyNo',
label: '关联保单号'
label: '关联保单号',
visible: (formData) => formData.fortuneBizType === 'R',
}, {
type: 'input',
prop: 'fortunePeriod',
label: '佣金期数',
inputType: 'decimal',
visible: (formData) => formData.fortuneBizType === 'R',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
......@@ -326,6 +331,7 @@ const addPayRecordFormConfig = ref([
prop: 'fortuneTotalPeriod',
label: '总期数',
inputType: 'decimal',
visible: (formData) => formData.fortuneBizType === 'R',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
......@@ -338,7 +344,8 @@ const addPayRecordFormConfig = ref([
type: 'date',
prop: 'actualPayoutDate',
label: '出账日(实)',
placeholder: '请选择'
placeholder: '请选择',
maxDate: 'today'
}, {
type: 'input',
prop: 'amount',
......@@ -368,6 +375,10 @@ const addPayRecordFormConfig = ref([
debounceWait: 500, // 自定义防抖时间
valueKey: 'userSaleBizId',
labelKey: 'realName',
onChangeExtraFields: {
broker: 'realName',// 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code'
},
transform: (res) => {
return res?.data.records || []
}
......@@ -377,13 +388,14 @@ const addPayRecordFormConfig = ref([
label: '出账状态',
dictType: 'csf_expected_fortune_status'
}
])
]
const handleConfirmAddPayRecord = async () => {
const handleConfirmAddPayRecordparams = addPayRecordFormRef.value.getFormData()
try {
await addPayRecord([handleConfirmAddPayRecordparams])
ElMessage.success('新增出账记录成功')
addPayRecordDialogVisible.value = false
addPayRecordFormRef.value.resetForm()
} catch (error) {
ElMessage.error(error.message)
}
......@@ -595,7 +607,20 @@ const handleConfirmSetPayRecordStatus = async () => {
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>
......
......@@ -39,11 +39,7 @@
<el-table-column prop="receivableNo" label="应收款编号" width="120" />
<el-table-column prop="policyNo" label="保单号" width="120" fixed="left" sortable />
<el-table-column prop="reconciliationCompany" label="对账公司" width="120" sortable />
<el-table-column prop="status" label="入账状态" width="120" sortable>
<template #default="{ row }">
{{ getDictLabel('csf_expected_commission_status', row.status) }}
</template>
</el-table-column>
<el-table-column prop="status" label="入账状态" width="120" sortable :formatter="formatStatus" />
<el-table-column prop="commissionPeriod" label="入账期数" width="120" sortable />
<el-table-column prop="totalPeriod" label="入账总期数" width="120" sortable />
<el-table-column prop="commissionName" label="入账项目" width="120" sortable />
......@@ -140,7 +136,8 @@
<!-- 新增应收款管理 -->
<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" />
</CommonDialog>
</div>
......@@ -155,7 +152,7 @@ import { MoreFilled } from '@element-plus/icons-vue'
import { receivedFortuneList, updateCommissionExpected, commissionEntryEditRecords, exportReceivedFortune, commissionExpectedRecord, addReceivedFortune } from '@/api/financial/commission'
import { numberWithCommas } from '@/utils/index'
import SearchForm from '@/components/SearchForm/SearchForm.vue'
import { getDictLabel } from '@/utils/useDict';
import { loadDicts, getDictLabel } from '@/utils/useDict'
import { safeDownload } from '@/utils/safeDownload'
import useUserStore from '@/store/modules/user'
......@@ -221,11 +218,23 @@ const addReceivablesFormConfig = [
label: '入账金额',
inputType: 'decimal',
decimalDigits: 2,
visible: (formData) => formData.commissionBizType === 'U',
rules: [
{ required: true, 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',
prop: 'currency',
label: '入账币种',
......@@ -246,23 +255,33 @@ const addReceivablesFormConfig = [
debounceWait: 500, // 自定义防抖时间
valueKey: 'reconciliationCompanyBizId',
labelKey: 'name',
onChangeExtraFields: {
reconciliationCompany: 'name',// 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code'
},
transform: (res) => {
console.log('对账公司', res)
return res.data.records || []
}
}
]
// 弹窗表单重置
const resetAddReceivablesForm = () => {
addRecordRef.value.resetForm()
}
const handleConfirmAddReceivables = async () => {
const p = addRecordRef.value.getFormData()
console.log('新增应收款', p)
try {
await addReceivedFortune({
commissionExpectedAddDtoList: [p]
})
ElMessage.success('新增应收款成功')
addReceivablesDialogVisible.value = false
handleSearch()
resetAddReceivablesForm()
handleQuery()
} catch (error) {
ElMessage.error(error.message)
}
......@@ -648,6 +667,20 @@ const handleConfirmSetStatus = () => {
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>
......
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