Commit 08e9e2d3 by yuzhenWang

用户体验优化1

parent 634376d0
......@@ -490,7 +490,6 @@ export function billBatchSave(data) {
})
}
// 拆分出账查询-计算目标金额
export function billCalculateToAmount(data) {
return request({
......@@ -508,3 +507,35 @@ export function CommissionExpectedChangeStatus(data) {
data: data
})
}
// 出账检核--分期出账
export function billSplitApi(data) {
return request({
url: 'csf/api/fortune/split',
method: 'post',
data: data
})
}
//用保单查询结算汇率
export function commissionExchangeRateApi(data) {
return request({
url: 'csf/api/commission/commission_exchange_rate',
method: 'post',
data: data
})
}
//出账检核--设置出账年月(单个)
export function actualPayoutDateApi(data) {
return request({
url: 'csf/api/fortune/edit/actual_payout_date',
method: 'post',
data: data
})
}
//出账检核--修改结算汇率
export function editExchangeRateApi(data) {
return request({
url: 'csf/api/fortune/edit/exchange_rate',
method: 'post',
data: data
})
}
......@@ -139,3 +139,10 @@ export function addSinglePremiumRemittance(data) {
data: data
})
}
// 保单详情
export function policyDetail(policyNo) {
return request({
url: `/csf/api/policy_follow/detail?policyNo=${policyNo}`,
method: 'get'
})
}
......@@ -256,3 +256,4 @@ export function getProductList(data) {
data: data
})
}
<!-- searchForm -->
<template>
<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 || 6">
<el-form-item
:label="item.label"
:prop="item.prop"
:class="{ 'search-form-item': isSearch }"
:label-position="item.labelPosition || 'top'"
>
<!-- Input -->
<el-input
v-if="item.type === 'input'"
v-model="localModel[item.prop]"
:placeholder="item.placeholder || `请输入${item.label}`"
:clearable="true"
:disabled="item.disabled"
@input="val => handleNumberInput(val, item)"
@change="val => handleModelChange(val, item)"
:formatter="value => {
if (!item.inputType || !value) return value;
const str = String(value);
// 如果包含小数点
if (str.indexOf('.') > -1) {
const [int, dec] = str.split('.');
// 只格式化整数部分,保留小数部分原样
return int.replace(/\B(?=(\d{3})+(?!\d))/g, ',') + '.' + dec;
}
// 如果没有小数点,直接格式化
return str.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}"
:parser="value => value.replace(/\$\s?|(,*)/g, '')"
/>
<!-- 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
:disabled="item.disabled"
:loading="remoteLoading[item.prop] || false"
@change="val => handleModelChange(val, item)"
@focus="() => loadRemoteOptions(item)"
@filter-change="keyword => handleFilterChange(keyword, item)"
<div>
<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 || 6">
<el-form-item
:label="item.label"
:prop="item.prop"
:class="{ 'search-form-item': isSearch }"
:label-position="item.labelPosition || 'top'"
>
<el-option
v-for="opt in getSelectOptions(item)"
:key="opt.value"
:label="opt.label"
:value="opt.value"
<!-- Input -->
<el-input
v-if="item.type === 'input'"
v-model="localModel[item.prop]"
:placeholder="item.placeholder || `请输入${item.label}`"
:clearable="true"
:disabled="item.disabled"
@input="val => handleNumberInput(val, item)"
@change="val => handleModelChange(val, item)"
:formatter="
value => {
if (!item.inputType || !value) return value
const str = String(value)
// 如果包含小数点
if (str.indexOf('.') > -1) {
const [int, dec] = str.split('.')
// 只格式化整数部分,保留小数部分原样
return int.replace(/\B(?=(\d{3})+(?!\d))/g, ',') + '.' + dec
}
// 如果没有小数点,直接格式化
return str.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
"
:parser="value => value.replace(/\$\s?|(,*)/g, '')"
/>
</el-select>
<!-- Date -->
<el-date-picker
v-else-if="item.type === 'date'"
v-model="localModel[item.prop]"
type="date"
:placeholder="`选择${item.label}`"
:disabled="item.disabled"
: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="item.disabled"
: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="item.startPlaceholder || '开始日期'"
:end-placeholder="item.endPlaceholder || '结束日期'"
:value-format="item.valueFormat || 'YYYY-MM-DD'"
:disabled="item.disabled"
:disabled-date="getDisabledDateFn(item)"
style="width: 100%"
@change="val => handleModelChange(val, item)"
/>
<!-- Monthrange (新增) -->
<el-date-picker
v-else-if="item.type === 'monthrange'"
v-model="localModel[item.prop]"
type="monthrange"
range-separator="至"
:start-placeholder="item.startPlaceholder || '开始月份'"
:end-placeholder="item.endPlaceholder || '结束月份'"
:value-format="item.valueFormat || 'YYYY-MM'"
:disabled="item.disabled"
: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]"
:disabled="item.disabled"
@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>
<!-- textarea -->
<el-input
v-else-if="item.type === 'textarea'"
v-model="localModel[item.prop]"
style="width: 240px"
autosize
:disabled="item.disabled"
type="textarea"
placeholder="请输入"
:clearable="true"
@change="val => handleModelChange(val, item)"
/>
<!-- Upload 回显值得时候数据格式至少是[{url: '必须要传', name: 'name不是必须的根据需要传值'}]-->
<el-upload
v-else-if="item.type === 'upload'"
v-model:file-list="localModel[item.prop]"
:action="item.action"
:headers="item.headers"
:multiple="!!item.multiple"
:limit="item.limit || (item.multiple ? 999 : 1)"
:accept="item.accept"
:list-type="item.listType || 'text'"
:disabled="item.disabled"
:auto-upload="true"
:show-file-list="item.showFileList"
:on-exceed="handleExceed"
:before-upload="file => beforeUpload(file, item)"
:on-success="(res, file, fileList) => handleUploadSuccess(res, file, fileList, item)"
:on-error="(err, file, fileList) => handleUploadError(err, file, fileList, item)"
:on-remove="(file, fileList) => handleUploadRemove(file, fileList, item)"
>
<el-icon class="iconStyle" :size="20" v-if="item.uploadType === 'image'">
<Upload />
</el-icon>
<el-button
v-else
size="small"
type="primary"
:link="item.link"
<!-- 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
:disabled="item.disabled"
:loading="remoteLoading[item.prop] || false"
@change="val => handleModelChange(val, item)"
@focus="() => loadRemoteOptions(item)"
@filter-change="keyword => handleFilterChange(keyword, item)"
>
{{ '点击上传文件' }}
</el-button>
<template #tip v-if="item.maxSize || item.accept">
<div class="el-upload__tip">
<span v-if="item.maxSize">大小不超过 {{ formatFileSize(item.maxSize) }}</span>
<span v-if="item.accept">支持格式:{{ item.accept }}</span>
<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}`"
:disabled="item.disabled"
: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="item.disabled"
: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="item.startPlaceholder || '开始日期'"
:end-placeholder="item.endPlaceholder || '结束日期'"
:value-format="item.valueFormat || 'YYYY-MM-DD'"
:disabled="item.disabled"
:disabled-date="getDisabledDateFn(item)"
style="width: 100%"
@change="val => handleModelChange(val, item)"
/>
<!-- Monthrange (新增) -->
<el-date-picker
v-else-if="item.type === 'monthrange'"
v-model="localModel[item.prop]"
type="monthrange"
range-separator="至"
:start-placeholder="item.startPlaceholder || '开始月份'"
:end-placeholder="item.endPlaceholder || '结束月份'"
:value-format="item.valueFormat || 'YYYY-MM'"
:disabled="item.disabled"
: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]"
:disabled="item.disabled"
@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>
<!-- textarea -->
<el-input
v-else-if="item.type === 'textarea'"
v-model="localModel[item.prop]"
style="width: 240px"
autosize
:disabled="item.disabled"
type="textarea"
placeholder="请输入"
:clearable="true"
@change="val => handleModelChange(val, item)"
/>
<!-- Upload 回显值得时候数据格式至少是[{url: '必须要传', name: 'name不是必须的根据需要传值'}]-->
<!-- <el-upload
v-else-if="item.type === 'upload'"
v-model:file-list="localModel[item.prop]"
:action="item.action"
:headers="item.headers"
:multiple="!!item.multiple"
:limit="item.limit || (item.multiple ? 999 : 1)"
:accept="item.accept"
:list-type="item.listType || 'text'"
:disabled="item.disabled"
:auto-upload="true"
:show-file-list="item.showFileList"
:on-exceed="handleExceed"
:before-upload="file => beforeUpload(file, item)"
:on-success="(res, file, fileList) => handleUploadSuccess(res, file, fileList, item)"
:on-error="(err, file, fileList) => handleUploadError(err, file, fileList, item)"
:on-remove="(file, fileList) => handleUploadRemove(file, fileList, item)"
>
<el-icon class="iconStyle" :size="20" v-if="item.uploadType === 'image'">
<Upload />
</el-icon>
<el-button
v-else
size="small"
type="primary"
:link="item.link"
:disabled="item.disabled"
>
{{ '点击上传文件' }}
</el-button>
<template #tip v-if="item.maxSize || item.accept">
<div class="el-upload__tip">
<span v-if="item.maxSize">大小不超过 {{ formatFileSize(item.maxSize) }}</span>
<span v-if="item.accept">支持格式:{{ item.accept }}</span>
</div>
</template>
</el-upload> -->
<!-- Upload 回显值时数据格式至少是 [{url: '必须', name: '可选'}] -->
<template v-else-if="item.type === 'upload'">
<!-- 🔽 默认模式:完整使用 el-upload(含自带文件列表) -->
<el-upload
v-if="!item.customFileList"
v-model:file-list="localModel[item.prop]"
:action="item.action"
:headers="item.headers"
:multiple="!!item.multiple"
:limit="item.limit || (item.multiple ? 999 : 1)"
:accept="item.accept"
:list-type="item.listType || 'text'"
:disabled="item.disabled"
:auto-upload="true"
:show-file-list="item.showFileList !== false"
:on-exceed="handleExceed"
:before-upload="file => beforeUpload(file, item)"
:on-success="
(res, file, fileList) => handleUploadSuccess(res, file, fileList, item)
"
:on-error="(err, file, fileList) => handleUploadError(err, file, fileList, item)"
:on-remove="(file, fileList) => handleUploadRemove(file, fileList, item)"
>
<el-icon class="iconStyle" :size="20" v-if="item.uploadType === 'image'">
<Upload />
</el-icon>
<el-button
v-else
size="small"
type="primary"
:link="item.link"
:disabled="item.disabled"
>
点击上传文件
</el-button>
<template #tip v-if="item.maxSize || item.accept">
<div class="el-upload__tip">
<span v-if="item.maxSize">大小不超过 {{ formatFileSize(item.maxSize) }}</span>
<span v-if="item.accept">支持格式:{{ item.accept }}</span>
</div>
</template>
</el-upload>
<!-- 🔽 自定义列表模式:只保留上传触发器,自己渲染文件列表 -->
<div v-else class="custom-upload-wrapper">
<el-upload
v-model:file-list="localModel[item.prop]"
:action="item.action"
:headers="item.headers"
:multiple="!!item.multiple"
:limit="item.limit || (item.multiple ? 999 : 1)"
:accept="item.accept"
:disabled="item.disabled"
:auto-upload="true"
:show-file-list="false"
:on-exceed="handleExceed"
:before-upload="file => beforeUpload(file, item)"
:on-success="
(res, file, fileList) => handleUploadSuccess(res, file, fileList, item)
"
:on-error="(err, file, fileList) => handleUploadError(err, file, fileList, item)"
:on-remove="(file, fileList) => handleUploadRemove(file, fileList, item)"
>
<el-button type="primary" size="small" :disabled="item.disabled">
{{ item.customUploadButtonText || '点击上传文件' }}
</el-button>
<template #tip v-if="item.maxSize || item.accept">
<div class="el-upload__tip">
<span v-if="item.maxSize">大小不超过 {{ formatFileSize(item.maxSize) }}</span>
<span v-if="item.accept">支持格式:{{ item.accept }}</span>
</div>
</template>
</el-upload>
<!-- 自定义文件列表 -->
<div v-if="(localModel[item.prop] || []).length" class="custom-file-list">
<div
v-for="(file, idx) in localModel[item.prop]"
:key="file.uid || idx"
class="file-item"
>
<span class="file-name" :title="file.name">{{ file.name }}</span>
<div class="file-actions">
<el-button link type="primary" size="small" @click="previewFile(file, item)">
查看
</el-button>
<el-button link type="danger" size="small" @click="removeFile(file, item)">
删除
</el-button>
</div>
</div>
</div>
</div>
</template>
</el-upload>
<span v-else>不支持的类型: {{ item.type }}</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
<span v-else>不支持的类型: {{ item.type }}</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 文件预览弹窗(页面内查看,不打开新窗口) -->
<el-dialog
v-model="previewDialogVisible"
:title="previewFileName"
width="70%"
:close-on-click-modal="false"
destroy-on-close
@close="previewUrl = ''"
>
<div class="preview-container">
<!-- 图片预览 -->
<div v-if="previewFileType === 'image'" class="preview-image-wrapper">
<img :src="previewUrl" class="preview-image" alt="预览图片" />
</div>
<!-- PDF 预览(使用 iframe 内嵌) -->
<iframe
v-else-if="previewFileType === 'pdf'"
:src="previewUrl"
class="preview-pdf"
frameborder="0"
></iframe>
<!-- 不支持预览的文件类型 -->
<div v-else-if="previewFileType === 'unsupported'" class="preview-unsupported">
<el-icon :size="48" color="#909399"><Document /></el-icon>
<p>暂不支持预览此类型文件</p>
<el-button type="primary" @click="previewDialogVisible = false"> 关闭 </el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, watch, onMounted, nextTick, computed } from 'vue'
import { ElMessage } from 'element-plus'
import { Upload } from '@element-plus/icons-vue'
import { Upload, Document } from '@element-plus/icons-vue'
// 🔑 引入你的字典方法
import useDictStore from '@/store/modules/dict'
import { getDicts } from '@/api/system/dict/data'
import request from '@/utils/request'
import dayjs from 'dayjs'
// ==================== 上传文件 ====================
// ==================== 文件预览弹窗 ====================
const previewDialogVisible = ref(false)
const previewUrl = ref('')
const previewFileName = ref('')
const previewFileType = ref('') // 'image', 'pdf', 'unsupported'
//新增文件上传自定义方法开始
// 预览文件(支持图片和PDF)
// 预览文件(页面内弹窗,不打开新窗口)
function previewFile(file, item) {
const url = file.url || file.response?.data?.url
if (!url) {
ElMessage.warning('文件地址不存在')
return
}
const ext = (file.name || '').split('.').pop().toLowerCase()
previewUrl.value = url
previewFileName.value = file.name || '文件'
if (['jpg', 'jpeg', 'png', 'webp', 'gif', 'bmp', 'svg'].includes(ext)) {
previewFileType.value = 'image'
previewDialogVisible.value = true
} else if (ext === 'pdf') {
previewFileType.value = 'pdf'
previewDialogVisible.value = true
} else {
// 不支持预览的文件类型,弹窗显示提示
previewFileType.value = 'unsupported'
previewDialogVisible.value = true
}
}
// 删除文件(复用原有删除逻辑)
function removeFile(file, item) {
const fileList = localModel.value[item.prop] || []
const newList = fileList.filter(f => f.uid !== file.uid)
handleUploadRemove(file, newList, item) // 调用原有的删除处理函数
}
//新增文件上传自定义方法结束
// 文件大小格式化
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes'
......@@ -336,7 +510,13 @@ const props = defineProps({
isSearch: { type: Boolean, default: false }
})
const emit = defineEmits(['update:modelValue', 'update', 'selectChange', 'uploadSuccess'])
const emit = defineEmits([
'update:modelValue',
'update',
'selectChange',
'uploadSuccess',
'inputChange'
])
// ==================== Refs ====================
const formRef = ref(null)
......@@ -382,7 +562,10 @@ watch(
// 优先用父传值,否则用默认值
if (props.modelValue?.[key] !== undefined) {
initialModel[key] = props.modelValue[key]
} else if (item.multiple || ['checkbox-group', 'daterange','monthrange'].includes(item.type)) {
} else if (
item.multiple ||
['checkbox-group', 'daterange', 'monthrange'].includes(item.type)
) {
initialModel[key] = item.defaultValue ?? []
} else {
initialModel[key] = item.defaultValue ?? ''
......@@ -419,7 +602,7 @@ function syncModelFromProps(newModelValue, newConfig) {
const key = item.prop
if (newModelValue.hasOwnProperty(key)) {
synced[key] = newModelValue[key]
} else if (item.multiple || ['checkbox-group', 'daterange','monthrange'].includes(item.type)) {
} else if (item.multiple || ['checkbox-group', 'daterange', 'monthrange'].includes(item.type)) {
synced[key] = item.defaultValue ?? []
} else {
synced[key] = item.defaultValue ?? ''
......@@ -543,6 +726,8 @@ function handleModelChange(value, item) {
} else if (item.type == 'upload') {
// 传给父组件最新的上传值newModel
emit('uploadSuccess', item.prop, newModel)
} else if (item.type == 'input') {
emit('inputChange', item.prop, value, item)
}
console.groupEnd()
}
......@@ -594,7 +779,7 @@ onMounted(async () => {
if (localModel.value[key] == null) {
if (item.multiple) {
initialData[key] = item.defaultValue ?? []
} else if (['checkbox-group', 'daterange','monthrange'].includes(item.type)) {
} else if (['checkbox-group', 'daterange', 'monthrange'].includes(item.type)) {
initialData[key] = item.defaultValue ?? []
} else {
initialData[key] = item.defaultValue ?? ''
......@@ -908,7 +1093,7 @@ defineExpose({
const resetData = {}
internalConfig.value.forEach(item => {
const key = item.prop
if (['checkbox-group', 'daterange','monthrange'].includes(item.type) || item.multiple) {
if (['checkbox-group', 'daterange', 'monthrange'].includes(item.type) || item.multiple) {
resetData[key] = item.defaultValue ?? []
} else if (item.type === 'upload') {
resetData[key] = item.defaultValue ?? [] // upload 也是数组
......@@ -960,6 +1145,75 @@ defineExpose({
</script>
<style scoped>
/* 预览弹窗样式 */
.preview-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
}
.preview-image-wrapper {
text-align: center;
}
.preview-image {
max-width: 100%;
max-height: 70vh;
object-fit: contain;
}
.preview-pdf {
width: 100%;
height: 70vh;
}
.preview-unsupported {
text-align: center;
padding: 40px;
}
.preview-unsupported p {
margin: 16px 0;
color: #909399;
}
.custom-upload-wrapper {
width: 100%;
}
.custom-file-list {
margin-top: 12px;
border: 1px solid #dcdfe6;
border-radius: 4px;
background-color: #fff;
}
.file-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
border-bottom: 1px solid #ebeef5;
}
.file-item:last-child {
border-bottom: none;
}
.file-name {
flex: 1;
font-size: 14px;
color: #606266;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 16px;
}
.file-actions {
display: flex;
gap: 12px;
}
.formBox {
box-sizing: border-box;
}
......
<template>
<div class="common-table">
<div class="addBox">
<el-button v-if="showAdd" type="primary" @click="addTableData">
{{ addBtnTxt }}
</el-button>
</div>
<el-table v-loading="loading" :data="tableData" border style="width: 100%" v-bind="$attrs">
<!-- 动态列 -->
<el-table-column
v-for="col in visibleColumns"
:key="col.prop"
:prop="col.prop"
:width="col.width"
:min-width="col.minWidth"
:fixed="col.fixed"
:align="col.align || 'left'"
:show-overflow-tooltip="col.showOverflowTooltip"
>
<!-- 自定义表头:显示必填红色星号 -->
<template #header>
<span v-if="checkColumnRequired(col)" class="required-star"> *</span>
<span>{{ col.label }}</span>
</template>
<template #default="{ row, $index }">
<!-- 可编辑列:始终显示编辑组件 -->
<div v-if="col.editable !== false">
<el-form-item
:rules="getColumnRules(col)"
:error="getRowError(row, col.prop)"
style="margin-bottom: 0"
>
<!-- 输入框(文本/数字/textarea) -->
<el-input
v-if="col.editType === 'input'"
:model-value="getInputDisplayValue(row, col)"
:placeholder="col.placeholder || `请输入${col.label}`"
:clearable="true"
:type="col.inputType === 'textarea' ? 'textarea' : 'text'"
:rows="col.rows || 2"
:disabled="col.disabled"
@update:model-value="val => handleInputChange(row, col, val, $index)"
@blur="handleInputBlur(row, col, $index)"
/>
<!-- 下拉选择 -->
<el-select
v-else-if="col.editType === 'select'"
:model-value="row[col.prop]"
:placeholder="col.placeholder || `请选择${col.label}`"
:clearable="true"
filterable
:allow-create="col.allowCreate"
:default-first-option="col.allowCreate"
:remote="!!col.api"
:remote-method="keyword => handleRemoteSearch(keyword, col)"
:loading="remoteLoading[col.prop]"
:disabled="col.disabled"
@update:model-value="val => handleSelectChange(row, col, val, $index)"
@focus="() => loadRemoteOptions(col)"
>
<el-option
v-for="opt in getSelectOptions(col)"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
<!-- 日期选择器 -->
<el-date-picker
v-else-if="col.editType === 'date'"
:model-value="row[col.prop]"
type="date"
:placeholder="`选择${col.label}`"
:value-format="col.valueFormat || 'YYYY-MM-DD'"
style="width: 100%"
:disabled="col.disabled"
:disabled-date="getDisabledDateFn(col)"
@update:model-value="val => handleCellChange(row, col, val, $index)"
/>
<!-- 月份选择器 -->
<el-date-picker
v-else-if="col.editType === 'month'"
:model-value="row[col.prop]"
type="month"
:placeholder="`选择${col.label}`"
:value-format="col.valueFormat || 'YYYY-MM'"
style="width: 100%"
:disabled="col.disabled"
:disabled-date="getDisabledDateFn(col)"
@update:model-value="val => handleCellChange(row, col, val, $index)"
/>
<!-- 日期范围 -->
<el-date-picker
v-else-if="col.editType === 'daterange'"
:model-value="row[col.prop]"
type="daterange"
range-separator="至"
:start-placeholder="col.startPlaceholder || '开始日期'"
:end-placeholder="col.endPlaceholder || '结束日期'"
:value-format="col.valueFormat || 'YYYY-MM-DD'"
style="width: 100%"
:disabled="col.disabled"
:disabled-date="getDisabledDateFn(col)"
@update:model-value="val => handleCellChange(row, col, val, $index)"
/>
<!-- 月份范围 -->
<el-date-picker
v-else-if="col.editType === 'monthrange'"
:model-value="row[col.prop]"
type="monthrange"
range-separator="至"
:start-placeholder="col.startPlaceholder || '开始月份'"
:end-placeholder="col.endPlaceholder || '结束月份'"
:value-format="col.valueFormat || 'YYYY-MM'"
style="width: 100%"
:disabled="col.disabled"
:disabled-date="getDisabledDateFn(col)"
@update:model-value="val => handleCellChange(row, col, val, $index)"
/>
<!-- 上传组件 -->
<div v-else-if="col.editType === 'upload'" class="table-upload-wrapper">
<el-upload
:file-list="getFileListDisplay(row[col.prop])"
:action="col.action || '/api/upload'"
:headers="col.headers"
:multiple="col.multiple || false"
:limit="col.limit || 1"
:accept="col.accept"
:disabled="col.disabled"
:auto-upload="true"
:show-file-list="false"
:before-upload="file => beforeUpload(file, col)"
:on-success="
(res, file, fileList) => handleUploadSuccess(row, col, res, file, fileList)
"
:on-error="
(err, file, fileList) => handleUploadError(row, col, err, file, fileList)
"
>
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
<div class="upload-file-list" v-if="getFileListDisplay(row[col.prop]).length">
<div
v-for="(file, idx) in getFileListDisplay(row[col.prop])"
:key="idx"
class="file-item"
>
<span class="file-name">{{ file.name || file.url }}</span>
<el-button link type="danger" size="small" @click="removeFile(row, col, idx)"
>删除</el-button
>
</div>
</div>
</div>
<!-- 自定义渲染(插槽) -->
<slot
v-else
:name="`edit-${col.prop}`"
:row="row"
:column="col"
:updateValue="val => handleCellChange(row, col, val, $index)"
/>
</el-form-item>
</div>
<!-- 不可编辑列:只读显示 -->
<div v-else>
<slot :name="`column-${col.prop}`" :row="row" :column="col" :value="row[col.prop]">
<span v-if="col.editType === 'select'">
{{ getSelectLabel(col, row[col.prop]) }}
</span>
<span v-else-if="col.editType === 'upload' && row[col.prop]">
<span v-for="(file, idx) in getFileListDisplay(row[col.prop])" :key="idx">
<el-link type="primary" @click="previewFile(file)">{{
file.name || file.url
}}</el-link>
<span v-if="idx < getFileListDisplay(row[col.prop]).length - 1">, </span>
</span>
</span>
<span v-else-if="col.formatter">
{{ col.formatter(row[col.prop], row) }}
</span>
<span v-else>{{ row[col.prop] || '-' }}</span>
</slot>
</div>
</template>
</el-table-column>
<!-- 操作栏 -->
<el-table-column
v-if="showOperation"
:label="operationLabel"
:width="operationWidth"
:fixed="operationFixed"
align="center"
>
<template #default="{ row, $index }">
<div class="operation-buttons">
<slot name="operation" :row="row" :index="$index">
<el-button link type="danger" size="small" @click="handleDelete(row)">删除</el-button>
</slot>
</div>
</template>
</el-table-column>
<!-- 额外插槽列 -->
<slot name="append" />
</el-table>
<!-- 分页组件 -->
<div v-if="showPagination" class="table-pagination">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="pageSizes"
:background="paginationBackground"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<!-- 文件预览弹窗 -->
<el-dialog v-model="previewDialogVisible" :title="previewFileName" width="60%" destroy-on-close>
<div class="preview-container">
<img v-if="previewFileType === 'image'" :src="previewUrl" style="max-width: 100%" />
<iframe
v-else-if="previewFileType === 'pdf'"
:src="previewUrl"
style="width: 100%; height: 70vh"
></iframe>
<div v-else class="preview-unsupported">暂不支持预览此类型文件</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { getDicts } from '@/api/system/dict/data'
import useDictStore from '@/store/modules/dict'
import request from '@/utils/request'
import dayjs from 'dayjs'
// ==================== Props ====================
const props = defineProps({
tableData: { type: Array, default: () => [] },
columns: { type: Array, default: () => [] },
rowIdKey: { type: String, default: 'id' },
showOperation: { type: Boolean, default: true },
operationLabel: { type: String, default: '操作' },
operationWidth: { type: String, default: '120' },
operationFixed: { type: String, default: 'right' },
showPagination: { type: Boolean, default: false },
total: { type: Number, default: 0 },
page: { type: Number, default: 1 },
limit: { type: Number, default: 20 },
pageSizes: { type: Array, default: () => [10, 20, 30, 50] },
paginationBackground: { type: Boolean, default: true },
loading: { type: Boolean, default: false },
showAdd: { type: Boolean, default: true },
addBtnTxt: { type: String, default: '新增' }
})
const emit = defineEmits([
'update:page',
'update:limit',
'update:tableData',
'delete',
'selectChange',
'uploadSuccess',
'add',
'cell-change'
])
// ==================== 内部状态 ====================
// 行级错误信息 { [rowId]: { [prop]: errorMsg } }
const rowErrors = ref({})
// 远程选项存储
const remoteOptions = ref({})
const remoteLoading = ref({})
// 字典加载标记
const dictLoaded = ref(new Set())
// 当前页码/每页条数
const currentPage = ref(props.page)
const pageSize = ref(props.limit)
// 预览弹窗状态
const previewDialogVisible = ref(false)
const previewUrl = ref('')
const previewFileName = ref('')
const previewFileType = ref('')
// ==================== 计算属性 ====================
const visibleColumns = computed(() => {
return props.columns.filter(col => {
if (typeof col.visible === 'function') return col.visible()
return col.visible !== false
})
})
// 检查列是否必填
function checkColumnRequired(col) {
if (col.required === true) return true
if (col.rules && Array.isArray(col.rules)) {
return col.rules.some(rule => rule.required === true)
}
return false
}
// 获取行的错误信息
function getRowError(row, prop) {
const rowId = row[props.rowIdKey]
return rowErrors.value[rowId]?.[prop] || ''
}
// 获取列校验规则
function getColumnRules(col) {
if (col.rules) return col.rules
if (col.required) {
return [{ required: true, message: `${col.label}不能为空`, trigger: 'blur' }]
}
return []
}
// 获取 Select 选项列表
function getSelectOptions(col) {
const key = col.prop
if (col.dictType) {
const dictStore = useDictStore()
const dictOpts = dictStore.getDict(col.dictType) || []
return dictOpts.map(opt => ({ value: opt.value, label: opt.label, raw: opt }))
}
if (col.api) {
return (remoteOptions.value[key] || []).map(opt => ({
value: opt.value,
label: opt.label,
raw: opt.raw
}))
}
if (col.options) {
return col.options.map(opt => ({ value: opt.value, label: opt.label, raw: opt.raw }))
}
return []
}
// 获取 Select 选中项的 label
function getSelectLabel(col, value) {
const options = getSelectOptions(col)
if (value === undefined || value === null) return '-'
if (col.multiple && Array.isArray(value)) {
return value.map(v => options.find(opt => opt.value === v)?.label || v).join(', ')
}
const found = options.find(opt => opt.value === value)
return found ? found.label : value
}
// ==================== 数字输入格式化与解析 ====================
// 修改 getInputDisplayValue:所有类型优先使用临时编辑值
function getInputDisplayValue(row, col) {
const rowId = row[props.rowIdKey]
const editingKey = `${rowId}_${col.prop}`
// 1. 正在编辑中 → 显示临时输入值
if (editingTempValue.value[editingKey] !== undefined) {
return editingTempValue.value[editingKey]
}
const { inputType } = col
let rawValue = row[col.prop]
// 2. 非数字类型直接返回原始值
if (!['integer', 'decimalNumber', 'decimal'].includes(inputType)) {
return rawValue ?? ''
}
// 3. 数字类型:失焦状态下格式化显示(千分位 + 小数位)
if (rawValue === undefined || rawValue === null) return ''
let num = typeof rawValue === 'number' ? rawValue : parseFloat(rawValue)
if (isNaN(num)) return ''
const { decimalDigits = 2 } = col
num = parseFloat(num.toFixed(decimalDigits))
const parts = num.toString().split('.')
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
return parts.join('.')
}
// 临时编辑值存储(用于聚焦时保留用户输入原文)
const editingTempValue = ref({})
// 输入时实时处理(过滤非法字符,并保留临时编辑值)
function handleInputChange(row, col, rawValue, rowIndex) {
const { inputType, decimalDigits = 2 } = col
let filtered = rawValue
if (inputType === 'integer') {
filtered = rawValue.replace(/[^-\d]/g, '')
if (filtered === '-') {
// 保留负号
} else if (filtered !== '') {
let num = parseInt(filtered, 10)
if (isNaN(num)) filtered = ''
else filtered = num.toString()
}
} else if (inputType === 'decimalNumber') {
let temp = rawValue.replace(/[^-\d.]/g, '')
if (temp.startsWith('-')) {
temp = '-' + temp.slice(1).replace(/-/g, '')
} else {
temp = temp.replace(/-/g, '')
}
if (temp === '-') {
filtered = '-'
} else {
const pointIndex = temp.indexOf('.')
if (pointIndex !== -1) {
const intPart = temp.slice(0, pointIndex).replace(/[^\d-]/g, '')
let decPart = temp.slice(pointIndex + 1).replace(/\D/g, '')
if (decPart.length > decimalDigits) decPart = decPart.slice(0, decimalDigits)
filtered = intPart + (decPart ? '.' + decPart : '.')
} else {
filtered = temp
}
}
} else if (inputType === 'decimal') {
let temp = rawValue.replace(/[^\d.]/g, '')
const pointIndex = temp.indexOf('.')
if (pointIndex !== -1) {
const intPart = temp.slice(0, pointIndex).replace(/\D/g, '')
let decPart = temp.slice(pointIndex + 1).replace(/\D/g, '')
if (decPart.length > decimalDigits) decPart = decPart.slice(0, decimalDigits)
filtered = intPart + (decPart ? '.' + decPart : '.')
} else {
filtered = temp
}
} else {
// textarea 或普通文本
filtered = rawValue
}
// const rowId = row[props.rowIdKey]
// const editingKey = `${rowId}_${col.prop}`
// // 保证响应式更新
// editingTempValue.value = {
// ...editingTempValue.value,
// [editingKey]: filtered
// }
const rowId = row[props.rowIdKey]
const editingKey = `${rowId}_${col.prop}`
// 直接赋值,无需展开整个对象
editingTempValue.value[editingKey] = filtered
}
// 失焦时:将临时编辑值转换为真实值并更新到表格数据
function handleInputBlur(row, col, rowIndex) {
const rowId = row[props.rowIdKey]
const editingKey = `${rowId}_${col.prop}`
const rawStr = editingTempValue.value[editingKey]
// 没有临时编辑值,说明用户未输入任何字符,直接返回
if (rawStr === undefined) {
return
}
delete editingTempValue.value[editingKey] // 清除临时值
// 用户清空了输入框
if (rawStr === '') {
const newTableData = [...props.tableData]
newTableData[rowIndex] = { ...row, [col.prop]: null }
emit('update:tableData', newTableData)
validateField(newTableData[rowIndex], col.prop)
return
}
const { inputType, decimalDigits = 2 } = col
let finalValue = null
if (inputType === 'integer') {
const match = rawStr.match(/^-?\d+/)
if (match) {
const num = parseInt(match[0], 10)
if (!isNaN(num)) finalValue = num
}
} else if (inputType === 'decimalNumber') {
const num = parseFloat(rawStr)
if (!isNaN(num)) finalValue = parseFloat(num.toFixed(decimalDigits))
} else if (inputType === 'decimal') {
const num = parseFloat(rawStr)
if (!isNaN(num) && num >= 0) finalValue = parseFloat(num.toFixed(decimalDigits))
} else {
// textarea 或普通文本
finalValue = rawStr
}
const newTableData = [...props.tableData]
newTableData[rowIndex] = { ...row, [col.prop]: finalValue }
emit('update:tableData', newTableData)
emit('cell-change', {
row: newTableData[rowIndex],
col,
newValue: finalValue,
oldValue: row[col.prop],
rowIndex,
prop: col.prop
})
validateField(newTableData[rowIndex], col.prop)
}
// ==================== 数据更新与校验 ====================
// 处理单元格值变更
function handleCellChange(row, col, newValue, rowIndex) {
const rowId = row[props.rowIdKey]
const oldValue = row[col.prop]
if (oldValue === newValue) return
// 更新数据
const newTableData = [...props.tableData]
newTableData[rowIndex] = { ...row, [col.prop]: newValue }
emit('update:tableData', newTableData)
emit('cell-change', {
row: newTableData[rowIndex],
col,
newValue,
oldValue,
rowIndex,
prop: col.prop
})
// 触发字段校验
validateField(newTableData[rowIndex], col.prop)
}
// 处理 Select 变化(支持额外字段赋值)
function handleSelectChange(row, col, value, rowIndex) {
const newTableData = [...props.tableData]
const updatedRow = { ...row, [col.prop]: value }
// 处理额外字段赋值
if (col.onChangeExtraFields) {
const options = getSelectOptions(col)
const opt = options.find(o => o.value === value)
if (opt?.raw) {
for (const [targetProp, sourceKey] of Object.entries(col.onChangeExtraFields)) {
updatedRow[targetProp] = opt.raw[sourceKey]
}
}
}
newTableData[rowIndex] = updatedRow
emit('update:tableData', newTableData)
emit('selectChange', col.prop, value, col)
validateField(updatedRow, col.prop)
}
// 校验单个字段
async function validateField(row, prop) {
const col = props.columns.find(c => c.prop === prop)
if (!col) return true
const rules = getColumnRules(col)
if (!rules.length) return true
const value = row[prop]
const rowId = row[props.rowIdKey]
let errorMsg = ''
for (const rule of rules) {
if (rule.required && (value === undefined || value === null || value === '')) {
errorMsg = rule.message || `${col.label}不能为空`
break
}
if (rule.pattern && !rule.pattern.test(String(value))) {
errorMsg = rule.message || `${col.label}格式不正确`
break
}
if (rule.validator && typeof rule.validator === 'function') {
try {
await rule.validator(null, value, () => {})
} catch (err) {
errorMsg = err.message
break
}
}
}
if (!rowErrors.value[rowId]) {
rowErrors.value[rowId] = {}
}
rowErrors.value[rowId][prop] = errorMsg
return !errorMsg
}
// ==================== 全局校验方法(暴露给父组件) ====================
function validateAll() {
const errors = []
const allValid = []
for (let i = 0; i < props.tableData.length; i++) {
const row = props.tableData[i]
const rowId = row[props.rowIdKey]
let rowValid = true
for (const col of visibleColumns.value) {
if (col.editable === false) continue
const rules = getColumnRules(col)
if (rules.length === 0) continue
const value = row[col.prop]
let errorMsg = ''
for (const rule of rules) {
if (rule.required && (value === undefined || value === null || value === '')) {
errorMsg = rule.message || `${col.label}不能为空`
break
}
if (rule.pattern && !rule.pattern.test(String(value))) {
errorMsg = rule.message || `${col.label}格式不正确`
break
}
}
if (errorMsg) {
rowValid = false
errors.push({
rowIndex: i,
rowId,
prop: col.prop,
label: col.label,
message: errorMsg,
row
})
if (!rowErrors.value[rowId]) rowErrors.value[rowId] = {}
rowErrors.value[rowId][col.prop] = errorMsg
} else {
if (rowErrors.value[rowId]) {
rowErrors.value[rowId][col.prop] = ''
}
}
}
allValid.push(rowValid)
}
const valid = allValid.every(v => v === true)
return { valid, errors }
}
async function validateAndShow() {
const { valid, errors } = validateAll()
if (!valid && errors.length > 0) {
const firstError = errors[0]
ElMessage.warning(firstError.message)
return false
}
return true
}
// ==================== 远程加载 ====================
async function loadDictOptions(dictType) {
const dictStore = useDictStore()
let options = dictStore.getDict(dictType)
if (options && options.length > 0) return options
try {
const resp = await getDicts(dictType)
options = resp.data.map(p => ({ label: p.itemLabel, value: p.itemValue, raw: p }))
dictStore.setDict(dictType, options)
return options
} catch (err) {
ElMessage.error(`字典 ${dictType} 加载失败`)
return []
}
}
async function loadRemoteOptions(col) {
const { prop, api, requestParams } = col
if (!api) return
if (remoteOptions.value[prop]?.length > 0) return
try {
remoteLoading.value[prop] = true
const payload = {
...(typeof requestParams === 'function' ? requestParams() : requestParams || {})
}
const res = await request({ url: api, method: 'post', data: payload })
const list =
typeof col.transform === 'function' ? col.transform(res) : res.data?.records || res.data || []
remoteOptions.value[prop] = list.map(i => ({
value: String(i[col.valueKey || 'value']),
label: i[col.labelKey || 'label'],
raw: i
}))
} catch (err) {
ElMessage.error(`加载 ${col.label} 失败`)
remoteOptions.value[prop] = []
} finally {
remoteLoading.value[prop] = false
}
}
let searchTimeouts = {}
async function handleRemoteSearch(keyword, col) {
const { prop, api, requestParams, keywordField = 'keyword', debounceWait = 300 } = col
if (!api) return
clearTimeout(searchTimeouts[prop])
searchTimeouts[prop] = setTimeout(async () => {
try {
remoteLoading.value[prop] = true
const payload = {
...(typeof requestParams === 'function' ? requestParams() : requestParams || {}),
[keywordField]: keyword
}
const res = await request({ url: api, method: 'post', data: payload })
const list =
typeof col.transform === 'function'
? col.transform(res)
: res.data?.records || res.data || []
remoteOptions.value[prop] = list.map(i => ({
value: String(i[col.valueKey || 'value']),
label: i[col.labelKey || 'label'],
raw: i
}))
} catch (err) {
ElMessage.error(`搜索 ${col.label} 失败`)
} finally {
remoteLoading.value[prop] = false
}
}, debounceWait)
}
function getDisabledDateFn(col) {
const { minDate, maxDate } = col
if (minDate == null && maxDate == null) return () => false
return date => {
const current = dayjs(date).startOf('day')
let minD = null,
maxD = null
if (minDate != null) {
const val = typeof minDate === 'function' ? minDate() : minDate
minD = val ? dayjs(val).startOf('day') : null
}
if (maxDate != null) {
const val = typeof maxDate === 'function' ? maxDate() : maxDate
maxD = val ? dayjs(val).startOf('day') : null
}
return (minD && current.isBefore(minD)) || (maxD && current.isAfter(maxD))
}
}
// ==================== 上传相关 ====================
function getFileListDisplay(val) {
if (!val) return []
if (Array.isArray(val)) return val
try {
return JSON.parse(val)
} catch {
return [{ url: val, name: '文件' }]
}
}
function beforeUpload(file, col) {
if (col.maxSize && file.size > col.maxSize) {
ElMessage.error(`文件 ${file.name} 超出大小限制`)
return false
}
if (col.accept) {
const ext = '.' + file.name.split('.').pop().toLowerCase()
const allowed = col.accept.split(',').map(s => s.trim().toLowerCase())
if (!allowed.includes(ext)) {
ElMessage.error(`文件类型不支持,仅支持:${col.accept}`)
return false
}
}
return true
}
function handleUploadSuccess(row, col, response, file, fileList) {
const data = response.data || response
const url = data.url || data.fileUrl || data.path
const name = data.name || data.fileName || file.name
if (!url) {
ElMessage.error('上传成功但未返回文件地址')
return
}
const currentList = getFileListDisplay(row[col.prop])
const newFile = { uid: file.uid, url, name }
const newList = [...currentList, newFile]
const rowIndex = props.tableData.findIndex(r => r[props.rowIdKey] === row[props.rowIdKey])
if (rowIndex !== -1) {
const newTableData = [...props.tableData]
newTableData[rowIndex] = { ...row, [col.prop]: newList }
emit('update:tableData', newTableData)
}
ElMessage.success(`${file.name} 上传成功`)
emit('uploadSuccess', col.prop, newList)
}
function handleUploadError(row, col, error, file, fileList) {
ElMessage.error(`文件 ${file.name} 上传失败`)
}
function removeFile(row, col, index) {
const currentList = getFileListDisplay(row[col.prop])
const newList = [...currentList]
newList.splice(index, 1)
const rowIndex = props.tableData.findIndex(r => r[props.rowIdKey] === row[props.rowIdKey])
if (rowIndex !== -1) {
const newTableData = [...props.tableData]
newTableData[rowIndex] = { ...row, [col.prop]: newList }
emit('update:tableData', newTableData)
}
}
function previewFile(file) {
const url = file.url
if (!url) return
const ext = (file.name || '').split('.').pop().toLowerCase()
previewUrl.value = url
previewFileName.value = file.name || '文件'
if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext)) {
previewFileType.value = 'image'
previewDialogVisible.value = true
} else if (ext === 'pdf') {
previewFileType.value = 'pdf'
previewDialogVisible.value = true
} else {
window.open(url, '_blank')
}
}
// ==================== 行操作 ====================
function handleDelete(row) {
emit('delete', row)
}
//新增表格数据
const addTableData = () => {
emit('add')
}
// ==================== 分页处理 ====================
function handleSizeChange(val) {
pageSize.value = val
emit('update:limit', val)
emit('update:page', currentPage.value)
}
function handleCurrentChange(val) {
currentPage.value = val
emit('update:page', val)
}
watch(
() => props.page,
val => {
currentPage.value = val
}
)
watch(
() => props.limit,
val => {
pageSize.value = val
}
)
// ==================== 初始化 ====================
onMounted(async () => {
for (const col of props.columns) {
if (col.editType === 'select') {
if (col.dictType) {
const opts = await loadDictOptions(col.dictType)
remoteOptions.value[col.prop] = opts
dictLoaded.value.add(col.prop)
} else if (col.api && col.preload !== false) {
await loadRemoteOptions(col)
} else if (col.options) {
remoteOptions.value[col.prop] = [...col.options]
}
}
}
})
// 暴露方法给父组件
defineExpose({
validateAll,
validateAndShow,
refreshRemoteOptions: async prop => {
const col = props.columns.find(c => c.prop === prop)
if (col && col.api) {
remoteOptions.value[prop] = []
await loadRemoteOptions(col)
}
},
clearErrors: () => {
rowErrors.value = {}
}
})
</script>
<style scoped>
.common-table {
width: 100%;
}
.addBox {
margin-bottom: 10px;
}
.table-pagination {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
.operation-buttons {
display: flex;
gap: 8px;
justify-content: center;
}
.table-upload-wrapper {
display: flex;
flex-direction: column;
gap: 8px;
}
.upload-file-list {
max-height: 80px;
overflow-y: auto;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 4px 8px;
}
.file-item {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
padding: 2px 0;
}
.file-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.preview-container {
text-align: center;
}
.preview-unsupported {
padding: 40px;
color: #909399;
}
.required-star {
color: #f56c6c;
margin-left: 2px;
}
</style>
<template>
<el-dialog
v-model="dialogVisible"
:title="displayFileName"
width="70%"
:close-on-click-modal="false"
destroy-on-close
@close="handleClose"
>
<div class="preview-container">
<!-- 图片预览 -->
<div v-if="previewType === 'image'" class="preview-image-wrapper">
<img :src="previewUrl" class="preview-image" alt="预览图片" />
</div>
<!-- PDF 预览 -->
<iframe
v-else-if="previewType === 'pdf'"
:src="previewUrl"
class="preview-pdf"
frameborder="0"
></iframe>
<!-- 不支持预览 -->
<div v-else-if="previewType === 'unsupported'" class="preview-unsupported">
<el-icon :size="48" color="#909399"><Document /></el-icon>
<p>暂不支持预览此类型文件</p>
<el-button type="primary" @click="handleClose">关闭</el-button>
</div>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { Document } from '@element-plus/icons-vue'
// --- Props 定义 ---
interface Props {
modelValue: boolean // 控制弹窗显示/隐藏(支持 v-model)
fileUrl: string // 文件 URL 或 Blob URL
fileName?: string // 文件名(用于弹窗标题及类型推断)
fileType?: 'image' | 'pdf' | 'unsupported' // 手动指定文件类型(可选)
}
const props = withDefaults(defineProps<Props>(), {
fileName: '文件预览',
fileType: undefined
})
// --- Emits 定义 ---
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
(e: 'close'): void
}>()
// --- 内部状态 ---
// 用于实际绑定到 img/iframe 的 URL,关闭时会清空
const previewUrl = ref('')
// 弹窗显示状态(双向绑定)
const dialogVisible = computed({
get: () => props.modelValue,
set: val => emit('update:modelValue', val)
})
// 最终显示的标题
const displayFileName = computed(() => props.fileName || '文件预览')
// --- 文件类型推断 ---
const previewType = computed<'image' | 'pdf' | 'unsupported'>(() => {
// 1. 若外部显式传入 fileType,优先使用
if (props.fileType) {
return props.fileType
}
// 2. 根据 fileUrl 或 fileName 的后缀名自动判断
const url = props.fileUrl || ''
const name = props.fileName || ''
const lowerUrl = url.toLowerCase()
const lowerName = name.toLowerCase()
const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg']
const isImage = (str: string) => imageExts.some(ext => str.endsWith(ext))
const isPdf = (str: string) => str.endsWith('.pdf')
if (isImage(lowerUrl) || isImage(lowerName)) return 'image'
if (isPdf(lowerUrl) || isPdf(lowerName)) return 'pdf'
return 'unsupported'
})
// --- 同步外部 URL 到预览 URL ---
// 当弹窗打开时,将 fileUrl 赋值给 previewUrl
// 当弹窗关闭时,清空 previewUrl,释放资源
watch(
() => props.modelValue,
visible => {
if (visible && props.fileUrl) {
previewUrl.value = props.fileUrl
} else if (!visible) {
previewUrl.value = ''
}
},
{ immediate: true }
)
// 如果弹窗已打开但 fileUrl 发生变化(例如父组件动态修改),同步更新预览内容
watch(
() => props.fileUrl,
newUrl => {
if (dialogVisible.value && newUrl) {
previewUrl.value = newUrl
}
}
)
// --- 关闭处理 ---
const handleClose = () => {
dialogVisible.value = false // 关闭弹窗
previewUrl.value = '' // 清空预览地址
emit('close') // 通知父组件
}
</script>
<style scoped>
.preview-container {
width: 100%;
min-height: 400px;
display: flex;
justify-content: center;
align-items: center;
}
.preview-image-wrapper {
width: 100%;
text-align: center;
}
.preview-image {
max-width: 100%;
max-height: 70vh;
object-fit: contain;
}
.preview-pdf {
width: 100%;
height: 70vh;
}
.preview-unsupported {
text-align: center;
padding: 40px;
}
.preview-unsupported p {
margin: 20px 0;
color: #909399;
}
</style>
......@@ -597,7 +597,42 @@ export const validateIdCardSimple = (rule, value, callback) => {
callback()
}
/**
* 英文姓名校验规则
*/
export const validateEnglish2 = (value) => {
if (!value) {
// 如果值为空且字段不是必填的,直接通过校验
return
}
// 1. 基本字符检查
if (!/^[A-Za-z\s\-'.]+$/.test(value)) {
return "包含非法字符,只允许英文字母、空格、-、'和."
}
// 2. 首字母大写检查
// if (!/^[A-Z]/.test(value)) {
// return callback(new Error('名字应以大写字母开头'))
// }
// 3. 长度检查
if (value.length < 2) {
return '名字至少需要2个字符'
}
// 4. 连续特殊字符检查
if (/[\s\-'.]{2,}/.test(value)) {
return '不能连续使用特殊字符'
}
// 5. 开头或结尾不能是特殊字符
if (/^[\s\-'.]|[\s\-'.]$/.test(value.trim())) {
return '名字不能以特殊字符开头或结尾'
}
return ''
}
// 将身份证验证方法添加到默认导出对象中
export default {
validateEnglish,
......@@ -605,5 +640,6 @@ export default {
validatePhone,
validateBMI,
validateIdCard,
validateIdCardSimple
validateIdCardSimple,
validateEnglish2
}
......@@ -15,28 +15,49 @@
<template #searchForm>
<SearchForm ref="searchFormRef" :config="searchConfig" />
</template>
1111
<!-- 列表区域 -->
<template #table>
<!-- 统计信息卡片 v-if="statisticsData.totalInAmount > 0"-->
<div class="statistics-container">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="入账金额" :value="statisticsData.totalInAmount" :formatter="value=>formatCurrency(value)" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="入账金额"
:value="statisticsData.totalInAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保单数" :value="statisticsData.totalPolicyCount" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保单数" :value="statisticsData.totalPolicyCount" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保费(HKD)" :value="statisticsData.totalPremium" :formatter="value=>formatCurrency(value)" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="总保费(HKD)"
:value="statisticsData.totalPremium"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="待出账金额" :value="statisticsData.pendingOutAmount" :formatter="value=>formatCurrency(value)" />
<el-statistic
title="待出账金额"
:value="statisticsData.pendingOutAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="可出账金额" :value="statisticsData.availableOutAmount" :formatter="value=>formatCurrency(value)" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="可出账金额"
:value="statisticsData.availableOutAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="差额(估)" :value="statisticsData.differenceAmount" :formatter="value=>formatCurrency(value)" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="差额(估)"
:value="statisticsData.differenceAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
</el-row>
</div>
......@@ -50,6 +71,8 @@
v-loading="loading"
>
<el-table-column type="selection" width="40" />
<el-table-column prop="policyNo" label="业务编号" width="120" sortable />
<el-table-column prop="isPart" label="是否拆分出账" width="120" />
<el-table-column prop="fortuneBizType" label="应付单类型" width="120" sortable>
<template #default="{ row }">
{{ getFortuneBizTypeLabel(row.fortuneBizType) }}
......@@ -62,14 +85,21 @@
label="累积已入账金额"
width="120"
sortable
:formatter="row=>formatCurrency(row.commissionPaidAmount || 0)"
:formatter="row => formatCurrency(row.commissionPaidAmount || 0)"
/>
<el-table-column
<!-- <el-table-column
prop="commissionPaidRatio"
label="累积实佣率"
width="120"
sortable
:formatter="row => `${row.commissionPaidRatio ? row.commissionPaidRatio : 0}%`"
/> -->
<el-table-column
prop="commissionPaidRatio"
label="达成率缺口"
width="120"
sortable
:formatter="row => `${row.commissionPaidRatio ? row.commissionPaidRatio : 0}%`"
/>
<el-table-column prop="fortuneName" label="出账项目" width="130" sortable />
<el-table-column prop="fortunePeriod" label="出账期数" width="130" sortable />
......@@ -77,17 +107,35 @@
<el-table-column prop="broker" label="转介人" width="130" sortable />
<el-table-column prop="team" label="所属团队" width="120" sortable />
<el-table-column prop="exchangeRate" label="结算汇率(估)" width="140" sortable />
<el-table-column prop="hkdAmount" label="应出账金额(HKD)" width="160" sortable :formatter="row=>formatCurrency(row.hkdAmount || 0)" />
<el-table-column
prop="hkdAmount"
label="应出账金额(HKD)"
width="160"
sortable
:formatter="row => formatCurrency(row.hkdAmount || 0)"
/>
<el-table-column prop="currency" label="出账币种" width="130" sortable />
<el-table-column prop="fortunePaidAmount" label="已出账金额" width="160" sortable :formatter="row=>formatCurrency(row.fortunePaidAmount || 0)"/>
<el-table-column prop="fortuneUnpaidAmount" label="剩余出账金额" width="160" sortable :formatter="row=>formatCurrency(row.fortuneUnpaidAmount || 0)"/>
<el-table-column
prop="fortunePaidAmount"
label="已出账金额"
width="160"
sortable
:formatter="row => formatCurrency(row.fortunePaidAmount || 0)"
/>
<el-table-column
prop="fortuneUnpaidAmount"
label="剩余出账金额"
width="160"
sortable
:formatter="row => formatCurrency(row.fortuneUnpaidAmount || 0)"
/>
<!-- <el-table-column prop="currentPaymentAmount" label="本期出账金额" width="120" sortable/> -->
<el-table-column
prop="currentPaymentHkdAmount"
label="本期出账金额(HKD)"
width="160"
sortable
:formatter="row=>formatCurrency(row.currentPaymentHkdAmount || 0)"
:formatter="row => formatCurrency(row.currentPaymentHkdAmount || 0)"
/>
<el-table-column
prop="fortuneUnpaidRatio"
......@@ -101,7 +149,13 @@
{{ selectDictLabel(csf_fortune_status, row.status) }}
</template>
</el-table-column>
<el-table-column prop="premium" label="期交保费" width="120" sortable :formatter="row=>formatCurrency(row.premium || 0)"/>
<el-table-column
prop="premium"
label="期交保费"
width="120"
sortable
:formatter="row => formatCurrency(row.premium || 0)"
/>
<el-table-column prop="policyCurrency" label="保单币种" width="120" sortable />
<el-table-column prop="payoutDate" label="出账日(估)" width="120" sortable />
<el-table-column prop="actualPayoutDate" label="出账日(实)" width="120" sortable />
......@@ -152,6 +206,7 @@
ref="addCheckRecordFormRef"
:config="addCheckRecordConfig"
v-model="addCheckRecordFormModel"
@inputChange="(prop, value, item) => handleInputChange('addCheckRecord', prop, value, item)"
/>
</CommonDialog>
<!-- 导入出账检核页面 -->
......@@ -187,26 +242,96 @@
v-model="setPayoutAmountFormModel"
/>
</CommonDialog>
<!-- 分期出账弹窗 -->
<CommonDialog
dialogTitle="分期出账"
dialogWidth="80%"
confirmText="提交"
:openDialog="installmentsBillFlag"
:showAction="true"
:showClose="true"
@close="installmentsBillFlag = false"
@confirm="handleSpiltSubmit"
>
<editTable
ref="editTableRef"
addBtnTxt="添加出账"
v-model:table-data="splitTableData"
:columns="splitTableColumns"
row-id-key="id"
show-operation
@delete="handleSpiltDelete"
@add="addSpiltRecord"
@cell-change="handleSplitCellChange"
/>
</CommonDialog>
<!-- 设置出账年月弹窗 -->
<CommonDialog
dialogTitle="设置本期出账金额"
dialogWidth="80%"
:openDialog="settingBillYearMonthFlag"
:showAction="true"
:showClose="true"
@close="settingBillYearMonthFlag = false"
@confirm="submitSettingBillYearMonth"
>
<SearchForm
ref="settingBillYearMonthRef"
:config="settingBillTimeConfig"
v-model="settingBillTimeForm"
/>
</CommonDialog>
<!-- 修改结算汇率 -->
<CommonDialog
dialogTitle="修改结算汇率"
dialogWidth="80%"
:openDialog="rateExchangeFlag"
:showAction="true"
:showClose="true"
@close="rateExchangeFlag = false"
@confirm="confirmRateExchange"
>
<SearchForm
ref="rateExchangeFormRef"
:config="rateExchangeConfig"
v-model="rateExchangeForm"
@inputChange="(prop, value, item) => handleInputChange('rateExchange', prop, value, item)"
/>
</CommonDialog>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { round } from 'lodash-es' // 或者自己实现小数保留
import { ref, reactive, nextTick } from 'vue'
import CommonPage from '@/components/commonPage'
import editTable from '@/components/Table/editTable.vue'
import CommonDialog from '@/components/commonDialog'
import SearchForm from '@/components/SearchForm/SearchForm.vue'
import { ElMessage } from 'element-plus'
import { formatCurrency } from '@/utils/number'
import { Select } from '@element-plus/icons-vue'
// 接口
import {
getPolicyFortuneList,
addCheckRecordaddBatch,
updatePayoutAmount,
downloadPolicyFortuneAccount,
billSplitApi,
commissionExchangeRateApi,
actualPayoutDateApi,
editExchangeRateApi
} from '@/api/financial/commission'
import useUserStore from '@/store/modules/user'
const rateExchangeFormRef = ref(null)
const rateExchangeFlag = ref(false)
const rateExchangeForm = ref({})
const billTimeType = ref('single')
const settingBillTimeForm = ref({})
const splitTableData = ref([])
const installmentsBillFlag = ref(false)
const settingBillYearMonthFlag = ref(false)
const { proxy } = getCurrentInstance()
const { csf_fortune_status } = proxy.useDict('csf_fortune_status')
......@@ -219,6 +344,13 @@ const loading = ref(false)
const selectedRow = ref(null)
const searchFormRef = ref(null)
const searchParams = ref({})
// 表格操作菜单
const dropdownItems = [
// { label: '设置本期出账金额', value: 'setPayRoll' }
{ label: '分期出账', value: 'billInstallments' },
{ label: '设置出账年月(实)', value: 'settingBillYearMonth' },
{ label: '修改结算汇率', value: 'editExchangeRate' }
]
const searchConfig = ref([
{
type: 'input',
......@@ -226,6 +358,11 @@ const searchConfig = ref([
label: '保单号'
},
{
type: 'input',
prop: 'broker',
label: '转介人'
},
{
type: 'select',
prop: 'statusList',
label: '出账状态',
......@@ -287,17 +424,271 @@ const searchConfig = ref([
dictType: 'csf_expected_commission_status'
}
])
// 表格操作菜单
const dropdownItems = [
{ label: '设置本期出账金额', value: 'setPayRoll' }
// { label: '更新', value: 'editRecord' },
// { label: '查看记录', value: 'viewRecord' }
const editTableRef = ref(null)
const splitTableColumns = ref([
{
prop: 'splitRatio',
label: '出账比例',
editType: 'input',
inputType: 'decimal', // integer / decimalNumber / decimal
decimalDigits: 2, // 小数位数,默认2
required: true,
placeholder: '请输入金额'
},
{
prop: 'amount',
label: '原币种金额',
editType: 'input',
inputType: 'decimalNumber', // integer / decimalNumber / decimal
decimalDigits: 2, // 小数位数,默认2
required: true
},
{
prop: 'originalAmount',
label: '本次出账原币种金额',
editType: 'input',
inputType: 'decimalNumber', // integer / decimalNumber / decimal
decimalDigits: 2, // 小数位数,默认2
required: true
},
{
prop: 'exchangeRate',
label: '结算汇率',
editType: 'input',
inputType: 'decimal', // integer / decimalNumber / decimal
decimalDigits: 2, // 小数位数,默认2
required: true
},
{
prop: 'hkdAmount',
label: '港币出账金额',
editType: 'input',
inputType: 'decimalNumber', // integer / decimalNumber / decimal
decimalDigits: 2, // 小数位数,默认2
required: true
},
{
prop: 'payoutYearMonth',
label: '出账年月(估)',
editType: 'month',
required: true
},
{
prop: 'remark',
label: '备注',
editType: 'input',
inputType: 'textarea',
required: false
}
// {
// prop: 'userId',
// label: '负责人',
// editType: 'select',
// api: '/insurance/base/api/insuranceCompany/page', // 接口地址
// valueKey: 'insuranceCompanyBizId', // 选项 value 字段名
// labelKey: 'fullName', // 选项 label 字段名
// preload: true, // 初始化时自动加载
// requestParams: { pageNo: 1, pageSize: 20 }, // 固定参数
// // 远程搜索配置
// filterable: true,
// keywordField: 'keyword',
// debounceWait: 300
// },
// {
// prop: 'status',
// label: '状态',
// editType: 'select',
// dictType: 'sys_status', // 系统字典类型
// required: true
// }
])
const settingBillTimeConfig = ref([
{
type: 'month',
prop: 'actualPayoutDate',
label: '出账年月(实)',
placeholder: '出账年月(实)',
rules: [{ required: true, message: '请选择出账年月(实)', trigger: 'blur' }]
}
])
const rateExchangeConfig = [
{
type: 'input',
prop: 'originalAmount',
label: '本期出账原币种金额',
inputType: 'decimal',
rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
},
{
type: 'input',
prop: 'exchangeRate',
label: '结算汇率',
inputType: 'decimal',
rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
// defaultValue: 1
},
{
type: 'input',
prop: 'hkdAmount',
label: '港币出账金额',
inputType: 'decimal',
rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
}
]
// 应收单类型
const fortuneBizTypeOptions = [
{ value: 'R', label: '关联保单应付单' },
{ value: 'U', label: '非关联保单应付单' }
]
const confirmRateExchange = async () => {
let formData = await rateExchangeFormRef.value.validate()
formData.fortuneBizId = selectedRow.value.fortuneBizId
try {
//等待后端接口
const res = await editExchangeRateApi(formData)
ElMessage.success('结算汇率修改成功')
rateExchangeFlag.value = false
loadTableData()
} catch (error) {
ElMessage.success('结算汇率修改失败')
rateExchangeFlag.value = true
if (error.message && error.message.includes('Validation')) {
ElMessage.error('必填项不能为空')
}
}
}
const handleInputChange = async (formType, prop, value, item) => {
if (formType == 'rateExchange') {
if (prop === 'exchangeRate' && value && rateExchangeForm.value.originalAmount) {
// 计算港币金额
const originalAmount = rateExchangeForm.value.originalAmount
const exchangeRate = value
const hkdAmount = multiply(originalAmount, exchangeRate, 2)
await nextTick()
rateExchangeForm.value.hkdAmount = hkdAmount
} else if (prop === 'originalAmount' && value && rateExchangeForm.value.exchangeRate) {
// 计算港币金额
const originalAmount = rateExchangeForm.value.originalAmount
const exchangeRate = value
const hkdAmount = multiply(originalAmount, exchangeRate, 2)
await nextTick()
rateExchangeForm.value.hkdAmount = hkdAmount
} else if ((prop === 'exchangeRate' || prop === 'originalAmount') && !value) {
await nextTick()
rateExchangeForm.value.hkdAmount = ''
}
} else if (formType == 'addCheckRecord') {
await nextTick()
let policyNo = addCheckRecordFormModel.value.policyNo
let fortunePeriod = addCheckRecordFormModel.value.fortunePeriod
if ((prop == 'policyNo' || prop == 'fortunePeriod') && fortunePeriod && policyNo) {
//等待后端接口
const res = await commissionExchangeRateApi({
policyNo: policyNo,
commissionPeriod: fortunePeriod
})
if (res.code == 200) {
addCheckRecordFormModel.value.exchangeRate = res.data
} else {
ElMessage.error('查询结算汇率失败')
}
}
}
}
// 精确乘法并保留小数位数
const multiply = (a, b, digits = 2) => {
if (a == null || b == null) return null
const numA = parseFloat(a)
const numB = parseFloat(b)
if (isNaN(numA) || isNaN(numB)) return null
return round(numA * numB, digits)
}
const handleSplitCellChange = ({ row, col, newValue, oldValue, rowIndex, prop }) => {
// 避免同一字段重复触发(如修改 chuAmount 会触发 hkdAmount 计算,该计算会再次触发 cell-change)
// 通过比较新旧值跳过无实际变化的更新
// if (newValue === oldValue) return
const currentRow = splitTableData.value[rowIndex]
if (!currentRow) return
let needUpdate = false
const updatedRow = { ...currentRow }
// 联动规则1:出账比例 或 原币种金额 变化 → 重算 本次出账原币种金额
if (prop === 'splitRatio' || prop === 'amount') {
const ratio = prop === 'splitRatio' ? newValue : currentRow.splitRatio
const amount = prop === 'amount' ? newValue : currentRow.amount
const newChuAmount = multiply(amount, ratio, 2)
updatedRow.originalAmount = newChuAmount
needUpdate = true
}
// 联动规则2:汇率 或 本次出账原币种金额 变化 → 重算 港币金额
if (prop === 'exchangeRate' || prop === 'originalAmount') {
const rate = prop === 'exchangeRate' ? newValue : currentRow.exchangeRate
const chuAmt = prop === 'originalAmount' ? newValue : currentRow.originalAmount
const newHkdAmount = multiply(chuAmt, rate, 2)
updatedRow.hkdAmount = newHkdAmount
needUpdate = true
console.log('updatedRow.hkdAmount', updatedRow.hkdAmount)
}
// 如果用户手动修改了 hkdAmount,不做反向联动(符合需求)
if (needUpdate) {
// 更新表格数据(整体替换,触发视图更新)
const newData = [...splitTableData.value]
newData[rowIndex] = updatedRow
splitTableData.value = newData
}
}
const handleSpiltSubmit = async () => {
// 调用组件的校验方法
const isValid = await editTableRef.value.validateAndShow()
if (isValid) {
try {
let params = {
fortuneBizId: selectedRow.value.fortuneBizId,
fortuneSplitDtoList: splitTableData.value
}
console.log('分期出账参数', params)
const res = await billSplitApi(params)
console.log('分期出账结果', res)
installmentsBillFlag.value = false
ElMessage.success('分期出账已保存')
loadTableData()
} catch (error) {
installmentsBillFlag.value = true
console.log('分期出账错误', error)
ElMessage.error('分期出账失败')
}
}
}
const handleSpiltDelete = row => {
let index = splitTableData.value.findIndex(item => item.id == row.id)
splitTableData.value.splice(index, 1)
}
// 生成临时唯一ID
let nextId = 1
const generateId = () => Date.now() + nextId++
// 添加出账
const addSpiltRecord = () => {
const newRow = {
id: generateId(), // 必须有 rowIdKey 对应的字段(默认 'id')
splitRatio: '', // 出账比例
amount: selectedRow.value.amount, // 原币种金额
exchangeRate: '', // 结算汇率
hkdAmount: '', // 港币出账金额
payoutYearMonth: '', // 出账年月(估)
originalAmount: '', //本次出账原币种金额
remark: '' // 备注
}
// 插入到表格数据最前面
splitTableData.value.push(newRow)
}
// 应付单类型通过value转成label
const getFortuneBizTypeLabel = value => {
const item = fortuneBizTypeOptions.find(item => item.value === value)
......@@ -383,12 +774,19 @@ const addCheckRecordConfig = [
onChangeExtraFields: {
broker: 'realName', // 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code',
team:'deptName',
teamBizId:'deptBizId',
team: 'deptName',
teamBizId: 'deptBizId'
},
transform: res => {
return res?.data.records || []
}
},
{
type: 'input',
prop: 'exchangeRate',
label: '结算汇率',
inputType: 'decimal',
decimalDigits: 2
}
]
......@@ -517,6 +915,16 @@ const handleSelect = (e, row) => {
selectedRow.value = row
if (e === 'setPayRoll') {
setPayoutAmountDialogFlag.value = true
} else if (e == 'billInstallments') {
splitTableData.value = []
installmentsBillFlag.value = true
} else if (e == 'settingBillYearMonth') {
billTimeType.value = 'single'
settingBillTimeForm.value = {}
settingBillYearMonthFlag.value = true
} else if (e == 'editExchangeRate') {
rateExchangeForm.value = { originalAmount: row.amount }
rateExchangeFlag.value = true
}
}
const addCheckRecordaddBatchapi = async data => {
......@@ -568,22 +976,55 @@ const handleSelectionChange = 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('完成检核,等待关账')
const submitSettingBillYearMonth = async () => {
if (billTimeType.value == 'multiple') {
//等后端接口
// 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 || '检核失败')
// }
} else if (billTimeType.value == 'single') {
const params = {
fortuneBizId: selectedRow.value.fortuneBizId,
actualPayoutDate: settingBillTimeForm.value.actualPayoutDate
}
try {
const singleRes = await actualPayoutDateApi(params)
ElMessage.success('设置出账年月成功')
settingBillYearMonthFlag.value = false
loadTableData()
} catch (error) {
ElMessage.error('设置出账年月失败')
settingBillYearMonthFlag.value = true
}
} catch (error) {
console.error('检核失败:', error)
ElMessage.error(error.response?.data?.msg || '检核失败')
}
}
const downloadPolicyFortuneAccountapi = async data => {
console.log('生成出账清单:', data)
settingBillYearMonthFlag.value = true
settingBillTimeForm.value = {}
billTimeType.value = 'multiple'
// 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)
......
<template>
<div class='container'>
<CommonPage :operationBtnList='operationBtnList' :visibleDefaultButtons="visibleDefaultButtons"
:showSearchForm='true' :show-pagination='true' :total='pageTotal' :current-page='currentPage'
:page-size='pageSize' @size-change='handleSizeChange' @current-change='handleCurrentChange'>
<div class="container">
<CommonPage
:operationBtnList="operationBtnList"
:visibleDefaultButtons="visibleDefaultButtons"
:showSearchForm="true"
:show-pagination="true"
:total="pageTotal"
:current-page="currentPage"
:page-size="pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<!-- 搜索区域 -->
<template #searchForm>
<SearchForm ref="searchFormRef" :config="searchConfig" />
......@@ -12,47 +20,87 @@
<!-- 统计信息卡片 -->
<div class="statistics-container" v-if="statisticsData.totalPolicyCount > 0">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保单数" :value="statisticsData.totalPolicyCount" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保单数" :value="statisticsData.totalPolicyCount" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保费(HKD)" :value="statisticsData.totalPremium" :formatter="value=>formatCurrency(value)" />
<el-statistic
title="总保费(HKD)"
:value="statisticsData.totalPremium"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="对账公司数" :value="statisticsData.reconciliationCompanyCount" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="对账公司数" :value="statisticsData.reconciliationCompanyCount" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="比对记录总条数" :value="statisticsData.totalCompareCommissionCount" />
<el-statistic
title="比对记录总条数"
:value="statisticsData.totalCompareCommissionCount"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="比对成功数" :value="statisticsData.successCompareCommissionCount" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="比对成功数"
:value="statisticsData.successCompareCommissionCount"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="比对失败数" :value="statisticsData.failedCompareCommissionCount" />
<el-statistic
title="比对失败数"
:value="statisticsData.failedCompareCommissionCount"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="入账金额(实)" :value="statisticsData.totalPaidAmount" :formatter="value=>formatCurrency(value)" />
<el-statistic
title="入账金额(实)"
:value="statisticsData.totalPaidAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="入账金额(估)" :value="statisticsData.expectePaidAmount" :formatter="value=>formatCurrency(value)" />
<el-statistic
title="入账金额(估)"
:value="statisticsData.expectePaidAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="差额(估)" :value="statisticsData.differenceAmount" :formatter="value=>formatCurrency(value)" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="差额(估)"
:value="statisticsData.differenceAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
</el-row>
</div>
<el-row>
<el-col :span="6">
<el-text tag="mark" class="mx-1">实佣率=实际入账金额/结算汇率/每期保费</el-text>
<el-text tag="mark" class="mx-1">实佣率=实际入账金额/结算汇率/每期保费</el-text>
</el-col>
<el-col :span="6">
<el-text tag="mark" class="mx-1"> 达成率缺口= 1 - 本期实佣率/产品本期来佣率</el-text>
<el-text tag="mark" class="mx-1"> 达成率缺口= 1 - 本期实佣率/保单本期来佣率 </el-text>
</el-col>
</el-row>
<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
: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" :selectable="selectableFn" />
<el-table-column prop="commissionStatus" label="比对状态" width="120" sortable fixed="left">
<el-table-column
prop="commissionStatus"
label="比对状态"
width="120"
sortable
fixed="left"
>
<template #default="{ row }">
{{ selectDictLabel(csf_commission_status, row.commissionStatus) }}
</template>
......@@ -71,17 +119,53 @@
{{ selectDictLabel(csf_expected_commission_status, row.commissionExpectedStatus) }}
</template>
</el-table-column>
<el-table-column prop="currentCommissionRatio" label="本次实佣率" width="130" sortable :formatter="formatRatio" />
<el-table-column prop="paidRatio" label="累积实佣率" width="130" sortable :formatter="formatRatio" />
<el-table-column prop="pendingRatio" label="达成率缺口" width="120" sortable :formatter="formatRatio" />
<el-table-column prop="amount" label="本次入账金额" width="130" sortable :formatter="formatCurrencyUtil" />
<el-table-column
prop="currentCommissionRatio"
label="本次实佣率"
width="130"
sortable
:formatter="formatRatio"
/>
<el-table-column
prop="paidRatio"
label="累积实佣率"
width="130"
sortable
:formatter="formatRatio"
/>
<el-table-column
prop="pendingRatio"
label="达成率缺口"
width="120"
sortable
:formatter="formatRatio"
/>
<el-table-column
prop="amount"
label="本次入账金额"
width="130"
sortable
:formatter="formatCurrencyUtil"
/>
<el-table-column prop="currency" label="入账币种" width="120" sortable />
<el-table-column prop="exchangeRate" 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 :formatter="formatCurrencyUtil" />
<el-table-column
prop="premium"
label="期交保费"
width="120"
sortable
:formatter="formatCurrencyUtil"
/>
<el-table-column prop="policyCurrency" label="保单币种" width="120" sortable />
<el-table-column prop="policyCurrency" label="产品名称" width="120" />
<el-table-column prop="policyCurrency" label="保单本期来佣率" width="120" />
<el-table-column prop="policyCurrency" label="投保人" width="120" />
<el-table-column prop="policyCurrency" label="投保人(英文)" width="120" />
<el-table-column prop="policyCurrency" label="转介人" width="120" />
<el-table-column prop="policyCurrency" label="保险公司" width="120" />
<el-table-column prop="remark" label="备注" width="120" sortable />
<el-table-column prop="isDeleted" label="记录状态" width="120" sortable>
<template #default="{ row }">
......@@ -99,9 +183,17 @@
<el-menu @select="handleSelect($event, row)" popper-class="custom-menu">
<el-menu-item :index="item.value" v-for="item in dropdownItems" :key="item.value">
<el-text class="mx-1" v-if="!item.confirm">{{ item.label }}</el-text>
<el-popconfirm v-if="item.confirm" confirm-button-text="Yes" cancel-button-text="No"
:icon="InfoFilled" icon-color="#626AEF" :title="item.confirm"
@confirm="handleMenuConfirm(item.value, row)" width="300" placement="left-end">
<el-popconfirm
v-if="item.confirm"
confirm-button-text="Yes"
cancel-button-text="No"
:icon="InfoFilled"
icon-color="#626AEF"
:title="item.confirm"
@confirm="handleMenuConfirm(item.value, row)"
width="300"
placement="left-end"
>
<template #reference>
<el-text class="mx-1">{{ item.label }}</el-text>
</template>
......@@ -119,17 +211,27 @@
<el-text type="danger">*非关联保单应收单以及比对失败的不可生成可出账记录</el-text>
</el-col>
<el-col :span="4">
<el-button type="primary" :icon="Select" :disabled="updatePayRollStatusDisable"
@click="generateCommissionRecordapi">{{ multipleSelection.length }} 条数据,生成可出账记录</el-button>
<el-button
type="primary"
:icon="Select"
:disabled="updatePayRollStatusDisable"
@click="generateCommissionRecordapi"
>{{ multipleSelection.length }} 条数据,生成可出账记录</el-button
>
</el-col>
</el-row>
</div>
</template>
</CommonPage>
<!-- 开始检核弹窗-->
<CommonDialog dialogTitle='开始检核' dialogWidth='80%' :openDialog=dialogFlag :showAction='false' :showClose='true'
@close="closthDialog()">
<CommonDialog
dialogTitle="开始检核"
dialogWidth="80%"
:openDialog="dialogFlag"
:showAction="false"
:showClose="true"
@close="closthDialog()"
>
<el-row>
<el-col :xs="24" :sm="24" :md="24" :lg="24">
<SearchForm ref="checkFormRef" :config="checkConfig" v-model="checkFormData" />
......@@ -137,12 +239,22 @@
</el-row>
<el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="3" :lg="3">
<el-button type="primary" :icon="Plus" :disabled="!checkFormData?.reconciliationYearMonth"
@click="handleAddCheckList()">新增</el-button>
<el-button
type="primary"
:icon="Plus"
:disabled="!checkFormData?.reconciliationYearMonth"
@click="handleAddCheckList()"
>新增</el-button
>
</el-col>
<el-col :xs="24" :sm="24" :md="3" :lg="3">
<el-button type="primary" :icon="Upload" :disabled="!checkFormData?.reconciliationYearMonth"
@click="fileUploadDialogFlag = true;">导入</el-button>
<el-button
type="primary"
:icon="Upload"
:disabled="!checkFormData?.reconciliationYearMonth"
@click="fileUploadDialogFlag = true"
>导入</el-button
>
</el-col>
<el-col :xs="24" :sm="24" :md="3" :lg="3">
<el-button type="primary" link @click="downloadTemplate">下载模板</el-button>
......@@ -180,11 +292,16 @@
<el-button type="primary" text size="small" @click="checkRecordEdit(row)">
修改
</el-button>
<el-popconfirm confirm-button-text="Yes" cancel-button-text="No" :icon="InfoFilled" icon-color="#626AEF"
title="确认要删除吗?" @confirm="deletePolicyCommissionApi(row)">
<el-popconfirm
confirm-button-text="Yes"
cancel-button-text="No"
:icon="InfoFilled"
icon-color="#626AEF"
title="确认要删除吗?"
@confirm="deletePolicyCommissionApi(row)"
>
<template #reference>
<el-button text type="danger" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
......@@ -195,53 +312,121 @@
<el-row :gutter="10">
<el-col :xs="24" :sm="24" :md="24" :lg="24">
<div class="pagination-container">
<el-pagination v-model:current-page="checkRecordPageInfo.currentPage"
v-model:page-size="checkRecordPageInfo.pageSize" :page-sizes="[50, 100, 200, 300]"
:total="checkRecordPageInfo.total" @size-change="checkRecordPageHandleSizeChange"
@current-change="checkRecordPageHandleCurrentChange" layout="total, sizes, prev, pager, next, jumper" />
<el-pagination
v-model:current-page="checkRecordPageInfo.currentPage"
v-model:page-size="checkRecordPageInfo.pageSize"
:page-sizes="[50, 100, 200, 300]"
:total="checkRecordPageInfo.total"
@size-change="checkRecordPageHandleSizeChange"
@current-change="checkRecordPageHandleCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-col>
</el-row>
</CommonDialog>
<!-- 新增检核记录弹窗 -->
<CommonDialog :dialogTitle='editStatus.value == "add" ? "新增检核记录" : "修改检核记录"' dialogWidth='80%'
:openDialog=addCheckRecordDialogFlag :showAction='true' :showClose='true' @close='closeDialog()'
@confirm='handleAddCheckRecord()'>
<CommonDialog
:dialogTitle="editStatus.value == 'add' ? '新增检核记录' : '修改检核记录'"
dialogWidth="80%"
:openDialog="addCheckRecordDialogFlag"
:showAction="true"
:showClose="true"
@close="closeDialog()"
@confirm="handleAddCheckRecord()"
>
<el-row>
<el-col :xs="24" :sm="24" :md="24" :lg="24">
<SearchForm ref="addCheckRecordFormRef" :config="addCheckRecordConfig" v-model="addReceivablesFormModel" />
<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 = ""'
@confirm='fileUploadDialogFlag = false; files = ""'>
<FileUpload v-model="files" :data="{ reconciliationYearMonth: checkFormData.reconciliationYearMonth }"
:file-type="['xlsx', 'xls']" :action="'/csf/api/commission/upload/excel'" :limit="1" :fileSize="15"
@uploadEnd="handleUploadEnd" />
<CommonDialog
dialogTitle="文件导入"
dialogWidth="80%"
:openDialog="fileUploadDialogFlag"
:showAction="true"
:showClose="true"
@close="((fileUploadDialogFlag = false), (files = ''))"
@confirm="((fileUploadDialogFlag = false), (files = ''))"
>
<FileUpload
v-model="files"
:data="{ reconciliationYearMonth: checkFormData.reconciliationYearMonth }"
:file-type="['xlsx', 'xls']"
:action="'/csf/api/commission/upload/excel'"
:limit="1"
:fileSize="15"
@uploadEnd="handleUploadEnd"
/>
</CommonDialog>
<!-- 设置比对状态 -->
<CommonDialog dialogTitle='设置比对状态' dialogWidth='80%' :openDialog=setCompareStatusDialogFlag :showAction='true'
:showClose='true' @close='setCompareStatusDialogFlag = false;' @confirm='setCompareStatusapi'>
<SearchForm ref="setCompareStatusFormRef" :config="setCompareStatusConfig" v-model="setCompareStatusFormModel" />
<CommonDialog
dialogTitle="设置比对状态"
dialogWidth="80%"
:openDialog="setCompareStatusDialogFlag"
:showAction="true"
: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;' @confirm='updateDataapi'>
<SearchForm ref="updateDataFormRef" :config="updateDataConfig" v-model="updateDataFormModel" />
<CommonDialog
dialogTitle="更新数据"
dialogWidth="80%"
:openDialog="updateDataDialogFlag"
:showAction="true"
:showClose="true"
@close="updateDataDialogFlag = false"
@confirm="updateDataapi"
>
<SearchForm
ref="updateDataFormRef"
:config="updateDataConfig"
v-model="updateDataFormModel"
/>
</CommonDialog>
<!-- 查看记录 -->
<CommonDialog dialogTitle='查看记录' dialogWidth='80%' :openDialog=viewRecordDialogFlag :showAction='true'
:showClose='true' @close='viewRecordDialogFlag = false;'>
<CommonDialog
dialogTitle="查看记录"
dialogWidth="80%"
:openDialog="viewRecordDialogFlag"
:showAction="true"
:showClose="true"
@close="viewRecordDialogFlag = false"
>
</CommonDialog>
<!-- 更新数据 -->
<CommonDialog
dialogTitle="修改检核"
dialogWidth="80%"
:openDialog="recordCheckDialogFlag"
:showAction="true"
:showClose="true"
@close="recordCheckDialogFlag = false"
@confirm="confirmRecordCheck"
>
<SearchForm
ref="updateDataFormRef"
:config="updateDataConfig"
v-model="updateDataFormModel"
/>
</CommonDialog>
</div>
</template>
......@@ -255,8 +440,15 @@ import { ElMessage } from 'element-plus'
import { formatCurrency } from '@/utils/number'
// 接口
import {
getPolicyCommissionList, generateCommissionRecord,
addPayrollCheckRecord, commissionExpectedRecord, updateCompareStatus, updateCommissionRecord, deletePolicyCommission, syncExpectedCommission, compareCommissionEntry
getPolicyCommissionList,
generateCommissionRecord,
addPayrollCheckRecord,
commissionExpectedRecord,
updateCompareStatus,
updateCommissionRecord,
deletePolicyCommission,
syncExpectedCommission,
compareCommissionEntry
} from '@/api/financial/commission'
import { InfoFilled, Select, Upload, Plus } from '@element-plus/icons-vue'
......@@ -266,7 +458,9 @@ import { loadDicts, getDictLabel } from '@/utils/useDict'
const { proxy } = getCurrentInstance()
const { csf_expected_commission_status } = proxy.useDict('csf_expected_commission_status')
const { csf_commission_status } = proxy.useDict('csf_commission_status')
const downloadTemplateUrl = 'https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/xlsx/2026/01/26/33f0637762144d58a256f5d8d3c2d2b6.xlsx'
const downloadTemplateUrl =
'https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/xlsx/2026/01/26/33f0637762144d58a256f5d8d3c2d2b6.xlsx'
const recordCheckDialogFlag = ref(false)
const downloadTemplate = () => {
const link = document.createElement('a')
link.href = downloadTemplateUrl // 注意:以 / 开头,指向 public 下的文件
......@@ -275,7 +469,7 @@ const downloadTemplate = () => {
}
const formatRatio = (row, column, cellValue, index) => {
if (cellValue == null || cellValue == '' && cellValue !== 0) {
if (cellValue == null || (cellValue == '' && cellValue !== 0)) {
return '-'
}
return cellValue + '%'
......@@ -298,11 +492,11 @@ const checkRecordPageInfo = reactive({
pageSize: 10,
total: 0
})
const checkRecordPageHandleSizeChange = (val) => {
const checkRecordPageHandleSizeChange = val => {
checkRecordPageInfo.pageSize = val
checkRecordQuery()
}
const checkRecordPageHandleCurrentChange = (val) => {
const checkRecordPageHandleCurrentChange = val => {
checkRecordPageInfo.currentPage = val
checkRecordQuery()
}
......@@ -313,13 +507,15 @@ const searchConfig = ref([
type: 'input',
prop: 'policyNo',
label: '保单号'
}, {
},
{
type: 'select',
prop: 'statusList',
label: '比对状态',
multiple: true,
dictType: 'csf_commission_status'
}, {
},
{
type: 'select',
prop: 'reconciliationCompanyBizIdList',
label: '对账公司',
......@@ -331,22 +527,25 @@ const searchConfig = ref([
multiple: true,
valueKey: 'reconciliationCompanyBizId',
labelKey: 'name',
transform: (res) => {
transform: res => {
console.log(res)
return res?.data.records || []
}
}, {
},
{
type: 'date',
prop: 'expectedDate',
label: '入账日(估)',
placeholder: '请选择'
}, {
},
{
type: 'daterange',
prop: 'commissionDate',
label: '入账日(实)',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间'
}, {
},
{
type: 'select',
prop: 'insuranceCompanyBizIdList',
label: '保险公司',
......@@ -358,15 +557,16 @@ const searchConfig = ref([
multiple: true,
valueKey: 'insuranceCompanyBizId',
labelKey: 'fullName',
transform: (res) => {
transform: res => {
console.log(res)
return res?.data.records || []
}
}, {
},
{
type: 'month',
prop: 'reconciliationYearMonth',
label: '检核年月',
placeholder: '检核年月',
placeholder: '检核年月'
}
])
const selectedRow = ref(null)
......@@ -385,7 +585,7 @@ const setCompareStatusFormModel = reactive({
status: ''
})
// 当比对状态为成功时可以选择
const selectableFn = (row) => row.commissionStatus == '1' && row.commissionBizType == 'R'
const selectableFn = row => row.commissionStatus == '1' && row.commissionBizType == 'R'
// 设置编辑状态,是新增还是修改
const editStatus = ref('add')
// 更新数据
......@@ -393,8 +593,6 @@ const updateDataDialogFlag = ref(false)
// 查看记录
const viewRecordDialogFlag = ref(false)
const fileUploadDialogFlag = ref(false)
const checkRecordTableData = ref([])
......@@ -415,7 +613,7 @@ const checkConfig = ref([
placeholder: '保单号'
}
])
const checkRecordEdit = (row) => {
const checkRecordEdit = row => {
console.log('查看记录', row)
selectedRowCheck.value = { ...row }
editStatus.value = 'edit'
......@@ -431,7 +629,7 @@ const checkFormData = ref({
// 监听 checkFormData 变化,自动重新查询
watch(
() => ({ ...checkFormData.value }), // 深度监听对象内容
(newVal) => {
newVal => {
// console.log('搜索条件变化:', newVal)
// ✅ 在这里调用查询接口 或 触发列表刷新
......@@ -441,9 +639,12 @@ watch(
},
{ immediate: true } // 首次加载也执行(可选)
)
// 确认修改检核
const confirmRecordCheck = () => {
//等待后端接口
}
// 删除检核记录
const deletePolicyCommissionApi = async (row) => {
const deletePolicyCommissionApi = async row => {
console.log(row)
try {
const res = await deletePolicyCommission({ commissionBizId: row.commissionBizId })
......@@ -464,9 +665,7 @@ const deletePolicyCommissionApi = async (row) => {
// 生成可出账记录按钮
const updatePayRollStatusDisable = ref(true)
onMounted(async () => {
})
onMounted(async () => {})
// 应收单类型
const commissionBizTypeOptions = [
......@@ -474,7 +673,7 @@ const commissionBizTypeOptions = [
{ value: 'U', label: '非关联保单应收单' }
]
// 应收单类型通过value转成label
const getCommissionBizTypeLabel = (value) => {
const getCommissionBizTypeLabel = value => {
const item = commissionBizTypeOptions.find(item => item.value === value)
return item?.label || ''
}
......@@ -489,48 +688,43 @@ const addCheckRecordConfig = ref([
label: '应收单类型',
placeholder: '应收单类型',
options: commissionBizTypeOptions,
rules: [
{ required: true, message: '请选择应收单类型', trigger: 'blur' },
]
rules: [{ required: true, message: '请选择应收单类型', trigger: 'blur' }]
},
{
type: 'input',
prop: 'policyNo',
label: '保单号',
visible: (formData) => formData.commissionBizType == 'R',
rules: [
{ required: true, message: '请输入保单号', trigger: 'blur' },
]
visible: formData => formData.commissionBizType == 'R',
rules: [{ required: true, message: '请输入保单号', trigger: 'blur' }]
},
{
type: 'input',
prop: 'commissionPeriod',
label: '佣金期数',
inputType: 'decimal',
visible: (formData) => formData.commissionBizType == 'R',
visible: formData => formData.commissionBizType == 'R',
rules: [
{ required: true, message: '请输入佣金期数', trigger: 'blur' },
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
}, {
},
{
type: 'input',
prop: 'totalPeriod',
label: '总期数',
inputType: 'decimal',
visible: (formData) => formData.commissionBizType == 'R',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
}, {
visible: formData => formData.commissionBizType == 'R',
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
},
{
type: 'date',
prop: 'commissionDate',
label: '入账日(实)',
placeholder: '请选择',
maxDate: 'today',
rules: [
{ required: true, message: '请选择入账日(实)', trigger: 'blur' },
]
}, {
rules: [{ required: true, message: '请选择入账日(实)', trigger: 'blur' }]
},
{
type: 'input',
prop: 'amount',
label: '入账金额',
......@@ -540,26 +734,25 @@ const addCheckRecordConfig = ref([
{ required: true, message: '请输入金额', trigger: 'blur' },
{ pattern: /^-?\d+(\.\d+)?$/, message: '请输入有效的数字', trigger: 'blur' }
]
}, {
},
{
type: 'select',
prop: 'currency',
label: '入账币种',
dictType: 'bx_currency_type',
rules: [
{ required: true, message: '请选择入账币种', trigger: 'blur' },
]
}, {
rules: [{ required: true, message: '请选择入账币种', trigger: 'blur' }]
},
{
type: 'select',
prop: 'commissionType',
label: '入账项目',
dictType: 'csf_commission_type',
rules: [
{ required: true, message: '请选择入账项目', trigger: 'blur' },
],
rules: [{ required: true, message: '请选择入账项目', trigger: 'blur' }],
onChangeExtraFields: {
commissionName: 'itemLabel'
},
}, {
}
},
{
type: 'select',
prop: 'reconciliationCompanyBizId',
label: '对账公司',
......@@ -569,19 +762,18 @@ const addCheckRecordConfig = ref([
placeholder: '输入对账公司名称搜索',
debounceWait: 500, // 自定义防抖时间
onChangeExtraFields: {
reconciliationCompany: 'name',// 自动同步 raw.name 到 reconciliationCompany
reconciliationCompany: 'name', // 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code'
},
valueKey: 'reconciliationCompanyBizId',
labelKey: 'name',
transform: (res) => {
transform: res => {
console.log(res)
return res?.data.records || []
},
rules: [
{ required: true, message: '请选择对账公司', trigger: 'blur' },
]
}, {
rules: [{ required: true, message: '请选择对账公司', trigger: 'blur' }]
},
{
type: 'input',
prop: 'exchangeRate',
label: '结算汇率(实)',
......@@ -591,11 +783,10 @@ const addCheckRecordConfig = ref([
{ required: true, message: '请输入金额', trigger: 'blur' },
{ pattern: /^\d+(\.\d{1,4})?$/, message: '最多四位小数', trigger: 'blur' }
]
},
}
])
const addCheckRecordDialogFlag = ref(false)
const handleAddCheckRecord = async () => {
try {
// ✅ 统一从子组件获取完整表单数据(含 extra 字段)
await nextTick() // 确保子组件已同步
......@@ -625,14 +816,12 @@ const handleAddCheckRecord = async () => {
ElMessage.error('操作失败')
}
}
const clearForm = (type) => {
const clearForm = type => {
if (type === 'addReceivablesFormModel')
if (addCheckRecordFormRef.value) {
addReceivablesFormModel.value = {}
addCheckRecordFormRef.value.resetForm()
}
}
const closeDialog = () => {
......@@ -655,13 +844,12 @@ const generateCommissionRecordapi = async () => {
console.error('生成可出账记录失败', error)
ElMessage.error('生成可出账记录失败')
}
}
// 表格操作菜单
const dropdownItems = [
{ label: '设置比对状态', value: 'setStatus', confirm: '' },
{ label: '同步到应收款管理', value: 'syncToReceivable', confirm: '确认要同步到应收款管理吗?' },
// { label: '更新检核数据', value: 'editRecord' },
{ label: '修改检核记录', value: 'editCheckRecord' }
// { label: '查看记录', value: 'viewRecord' }
]
const handleMenuConfirm = async (action, row) => {
......@@ -718,7 +906,7 @@ const checkRecordQuery = async () => {
try {
const params = {
reconciliationYearMonth: checkFormData.value.reconciliationYearMonth,
policyNo:checkFormData.value.policyNo,
policyNo: checkFormData.value.policyNo,
pageNo: checkRecordPageInfo.currentPage,
pageSize: checkRecordPageInfo.pageSize
}
......@@ -731,11 +919,10 @@ const checkRecordQuery = async () => {
console.error('查询检核记录失败', error)
ElMessage.error('查询检核记录失败')
}
}
const multipleSelection = ref([])
const handleSelectionChange = (val) => {
const handleSelectionChange = val => {
multipleSelection.value = val
console.log('全选:', val)
// 生成可出账记录按钮是否禁用
......@@ -794,11 +981,11 @@ const loadTableData = async (searchParams = {}) => {
}
loadTableData()
// 分页事件
const handleSizeChange = (val) => {
const handleSizeChange = val => {
pageSize.value = val
loadTableData()
}
const handleCurrentChange = (val) => {
const handleCurrentChange = val => {
currentPage.value = val
loadTableData()
}
......@@ -818,7 +1005,9 @@ const handleSelect = (e, row) => {
viewRecordDialogFlag.value = true
return
} else if (e === 'syncToReceivable') {
return
} else if (e === 'editCheckRecord') {
recordCheckDialogFlag.value = true
return
}
}
......@@ -877,7 +1066,7 @@ const updateDataapi = async () => {
ElMessage.error('更新数据失败')
}
}
const handleUploadEnd = (code) => {
const handleUploadEnd = code => {
if (code === 200) {
ElMessage.success('上传文件成功')
fileUploadDialogFlag.value = false
......@@ -897,7 +1086,7 @@ const closthDialog = () => {
}
// 重新比对
const compareCommissionEntryapi = async (row) => {
const compareCommissionEntryapi = async row => {
try {
const res = await compareCommissionEntry(row.commissionBizId)
if (res.code === 200) {
......@@ -922,4 +1111,4 @@ const compareCommissionEntryapi = async (row) => {
.el-col {
margin-bottom: 5px;
}
</style>
\ No newline at end of file
</style>
......@@ -42,6 +42,7 @@
:border="true"
>
<el-table-column type="selection" width="55" :reserve-selection="true" />
<el-table-column prop="businessNo" label="业务编号" min-width="120" />
<el-table-column prop="fortuneAccountBizId" label="业务ID" min-width="120" sortable />
<el-table-column prop="broker" label="转介人" min-width="120" sortable />
<el-table-column prop="team" label="所属团队" min-width="120" sortable />
......@@ -62,11 +63,17 @@
</el-tag>
</template>
</el-table-column>
<el-table-column
<!-- <el-table-column
prop="fortuneAccountDate"
label="出账日"
min-width="150"
show-overflow-tooltip
/> -->
<el-table-column
prop="fortuneAccountMonth"
label="出账月"
min-width="150"
show-overflow-tooltip
/>
<!-- <el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip /> -->
<el-table-column fixed="right" label="操作" min-width="120">
......@@ -174,8 +181,23 @@
</template>
</el-table-column>
<el-table-column label="出账机构" prop="billOrg" width="150">
<template #default="scope">
<!-- <template #default="scope">
<el-input v-model="scope.row.billOrg" placeholder="请输入" clearable />
</template> -->
<template #default="scope">
<el-select
v-model="scope.row.billOrg"
style="width: 100%"
placeholder="请选择"
clearable
>
<el-option
v-for="item in csf_bill_org"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="原币种金额" prop="fromAmount" width="150">
......@@ -303,9 +325,9 @@
:showAction="true"
:showClose="true"
@close="showSalarySetting = false"
@confirm = "salaryDataSetting"
@confirm="salaryDataSetting"
>
<el-date-picker
<el-date-picker
v-model="fortuneAccountDate"
type="date"
placeholder="选择薪资日"
......@@ -341,7 +363,7 @@ import { debounce } from 'lodash-es'
const amountInput = usePositiveDecimal(4) // 默认2位小数
const { proxy } = getCurrentInstance()
const { bx_currency_type } = proxy.useDict('bx_currency_type')
const { bx_currency_type, csf_bill_org } = proxy.useDict('bx_currency_type', 'csf_bill_org')
const searchFormRef = ref(null)
const searchParams = ref({})
const searchConfig = ref([
......@@ -421,7 +443,7 @@ const debounceChangeToAmountMap = new WeakMap()
// 表格操作菜单
const dropdownItems = [
{ label: '拆分出账', value: 'splitBilling' },
{ label: '设置出账日', value: 'settingSalaryDate' },
{ label: '设置出账日', value: 'settingSalaryDate' }
// { label: '查看记录', value: 'viewRecord' }
]
//=============拆分出账开始================
......@@ -684,30 +706,29 @@ const handleSelect = (e, row) => {
billCurrentPage.value = 1
billTableList.value = []
getSplitTableList()
}else if(e==='settingSalaryDate'){
} else if (e === 'settingSalaryDate') {
console.log('更新薪资数据')
fortuneAccountDate.value= currentRow.value.fortuneAccountDate || ''
showSalarySetting.value = true;
fortuneAccountDate.value = currentRow.value.fortuneAccountDate || ''
showSalarySetting.value = true
}
}
const salaryDataSetting = async (e)=>{
try{
const params = {
fortuneAccountBizId : currentRow.value.fortuneAccountBizId,
fortuneAccountDate:fortuneAccountDate.value
};
const response = await updatePolicyFortuneAccount(params)
if(response.code==200){
showSalarySetting.value = false;
ElMessage.success('修改成功')
getList()
}
}catch (error) {
const salaryDataSetting = async e => {
try {
const params = {
fortuneAccountBizId: currentRow.value.fortuneAccountBizId,
fortuneAccountDate: fortuneAccountDate.value
}
const response = await updatePolicyFortuneAccount(params)
if (response.code == 200) {
showSalarySetting.value = false
ElMessage.success('修改成功')
getList()
}
} catch (error) {
console.error('获取数据失败:', error)
ElMessage.error('修改失败')
}
}
// 分页事件
const handleSizeChange = val => {
......
<template>
<div>
<CommonPage :operationBtnList="operationBtnList" :visibleDefaultButtons="visibleDefaultButtons"
:showSearchForm="true" :show-pagination="true" :total="pageTotal" :current-page="currentPage"
:page-size="pageSize" @size-change="handleSizeChange" @current-change="handleCurrentChange">
<CommonPage
:operationBtnList="operationBtnList"
:visibleDefaultButtons="visibleDefaultButtons"
:showSearchForm="true"
:show-pagination="true"
:total="pageTotal"
:current-page="currentPage"
:page-size="pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<!-- 搜索区域 -->
<template #searchForm>
<SearchForm ref="searchFormRef" :config="searchConfig" />
......@@ -12,30 +20,64 @@
<!-- 统计信息卡片 -->
<div class="statistics-container" v-if="statisticsData.totalPolicyCount > 0">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="应出账总金额" :value="statisticsData.totalExpectedAmount" :formatter="value=>formatCurrency(value)" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="应出账总金额"
:value="statisticsData.totalExpectedAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="已出账金额" :value="statisticsData.totalPaidAmount" :formatter="value=>formatCurrency(value)" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="已出账金额"
:value="statisticsData.totalPaidAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="待出账金额" :value="statisticsData.totalUnpaidAmount" :formatter="value=>formatCurrency(value)" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="待出账金额"
:value="statisticsData.totalUnpaidAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="已出账比例" :value="statisticsData.paidAmountRatio" :formatter="value=>formatCurrency(value,'',2)+'%'" />
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="已出账比例"
:value="statisticsData.paidAmountRatio"
:formatter="value => formatCurrency(value, '', 2) + '%'"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保单数" :value="statisticsData.totalPolicyCount"/>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保单数" :value="statisticsData.totalPolicyCount" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保费" :value="statisticsData.totalPremiumAmount" :formatter="value=>formatCurrency(value)" />
<el-statistic
title="总保费"
:value="statisticsData.totalPremiumAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
</el-row>
</div>
<!-- 应付款管理列表 -->
<el-table :data="tableData" height="400" border highlight-current-row style="width: 100%" v-loading="loading">
<el-table-column v-for="(column, index) in payableReportTableColumns" :key="index" :prop="column.prop"
:label="column.label" :width="column.width" :sortable="column.sortable" :formatter="column.formatter" />
<el-table
:data="tableData"
height="400"
border
highlight-current-row
style="width: 100%"
v-loading="loading"
>
<el-table-column
v-for="(column, index) in payableReportTableColumns"
:key="index"
:prop="column.prop"
:label="column.label"
:width="column.width"
:sortable="column.sortable"
:formatter="column.formatter"
/>
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="scope">
<el-button link type="primary" size="small" @click="viewDetail(scope.row)">
......@@ -47,33 +89,66 @@
</template>
</CommonPage>
<!-- 查看明细列表 -->
<CommonDialog dialogTitle="应付明细" dialogWidth="80%" :openDialog="detailDialogVisible" :showAction="true"
:showClose="true" @close="detailDialogVisible = false" @confirm="detailDialogVisible = false">
<CommonDialog
dialogTitle="应付明细"
dialogWidth="80%"
:openDialog="detailDialogVisible"
:showAction="true"
:showClose="true"
@close="detailDialogVisible = false"
@confirm="detailDialogVisible = false"
>
<div class="statistics-container" v-if="detailRecordStatistics.totalPolicyCount > 0">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="应出账总金额" :value="detailRecordStatistics.totalExpectedAmount" :formatter="value=>formatCurrency(value)" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="已出账金额" :value="detailRecordStatistics.totalPaidAmount" :formatter="value=>formatCurrency(value)" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="待出账金额" :value="detailRecordStatistics.totalUnpaidAmount" :formatter="value=>formatCurrency(value)" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="已出账比例" :value="detailRecordStatistics.paidAmountRatio" :formatter="value=>formatCurrency(value,'',2)+'%'" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保单数" :value="detailRecordStatistics.totalPolicyCount"/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保费" :value="detailRecordStatistics.totalPremiumAmount" :formatter="value=>formatCurrency(value)" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="应出账总金额"
:value="detailRecordStatistics.totalExpectedAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="已出账金额"
:value="detailRecordStatistics.totalPaidAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="待出账金额"
:value="detailRecordStatistics.totalUnpaidAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="已出账比例"
:value="detailRecordStatistics.paidAmountRatio"
:formatter="value => formatCurrency(value, '', 2) + '%'"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保单数" :value="detailRecordStatistics.totalPolicyCount" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
title="总保费"
:value="detailRecordStatistics.totalPremiumAmount"
:formatter="value => formatCurrency(value)"
/>
</el-col>
</el-row>
</div>
<el-table :data="payableReportTableData" border style="width: 100%;margin-bottom: 10px;">
<el-table-column v-for="item in payableReportListTableColumns" :key="item.property" :property="item.property"
:label="item.label" :width="item.width" :formatter="item.formatter" />
<el-table :data="payableReportTableData" border style="width: 100%; margin-bottom: 10px">
<el-table-column
v-for="item in payableReportListTableColumns"
:key="item.property"
:property="item.property"
:label="item.label"
:width="item.width"
:formatter="item.formatter"
/>
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="{ row }">
<el-popover placement="right" :width="200" trigger="click">
......@@ -91,66 +166,204 @@
</template>
</el-table-column>
</el-table>
<el-pagination v-model:current-page="detailPageInfo.currentPage" v-model:page-size="detailPageInfo.pageSize"
:page-sizes="[50, 100, 200, 300]" size="default" layout="total, sizes, prev, pager, next, jumper"
:total="detailPageInfo.total" @size-change="handleSizeChangeDetailRecord"
@current-change="handleCurrentChangeDetailRecord" />
<el-pagination
v-model:current-page="detailPageInfo.currentPage"
v-model:page-size="detailPageInfo.pageSize"
:page-sizes="[50, 100, 200, 300]"
size="default"
layout="total, sizes, prev, pager, next, jumper"
:total="detailPageInfo.total"
@size-change="handleSizeChangeDetailRecord"
@current-change="handleCurrentChangeDetailRecord"
/>
</CommonDialog>
<!-- 出账记录表格弹窗-->
<CommonDialog dialogTitle="出账记录" dialogWidth="80%" :openDialog="payRecordDialogTableVisible" :showAction="true"
:showClose="true" @close="payRecordDialogTableVisible = false" @confirm="payRecordDialogTableVisible = false">
<CommonDialog
dialogTitle="出账记录"
dialogWidth="80%"
:openDialog="payRecordDialogTableVisible"
:showAction="true"
:showClose="true"
@close="payRecordDialogTableVisible = false"
@confirm="payRecordDialogTableVisible = false"
>
<el-table :data="payRecordDialogTableData" border style="width: 100%">
<el-table-column v-for="item in payRecordDialogTableColumns" :key="item.property" :property="item.property"
:label="item.label" :width="item.width" :formatter="item.formatter" />
<el-table-column
v-for="item in payRecordDialogTableColumns"
:key="item.property"
:property="item.property"
:label="item.label"
:width="item.width"
:formatter="item.formatter"
/>
</el-table>
</CommonDialog>
<!-- 新增出账记录 -->
<CommonDialog :dialogTitle="editStatus === 'add' ? '新增出账记录' : '修改出账记录'" dialogWidth="80%"
:openDialog="addPayRecordDialogVisible" :showAction="true" :showClose="true" @close="resetAddPayRecordForm"
@confirm="handleConfirmAddPayRecord">
<SearchForm ref="addPayRecordFormRef" :config="addPayRecordFormConfig" v-model="addPayRecordFormModel" />
<CommonDialog
:dialogTitle="editStatus === 'add' ? '新增出账记录' : '修改出账记录'"
dialogWidth="80%"
:openDialog="addPayRecordDialogVisible"
:showAction="true"
:showClose="true"
@close="resetAddPayRecordForm"
@confirm="handleConfirmAddPayRecord"
>
<SearchForm
ref="addPayRecordFormRef"
:config="addPayRecordFormConfig"
v-model="addPayRecordFormModel"
@inputChange="(prop, value, item) => handleInputChange('addPayRecord', prop, value, item)"
/>
</CommonDialog>
<!-- 设置出账状态 -->
<CommonDialog dialogTitle="设置出账状态" dialogWidth="80%" :openDialog="setPayRecordStatusDialogVisible"
:showAction="true" :showClose="true" @close="setPayRecordStatusDialogVisible = false"
@confirm="handleConfirmSetPayRecordStatus">
<CommonDialog
dialogTitle="设置出账状态"
dialogWidth="80%"
:openDialog="setPayRecordStatusDialogVisible"
:showAction="true"
:showClose="true"
@close="setPayRecordStatusDialogVisible = false"
@confirm="handleConfirmSetPayRecordStatus"
>
<SearchForm ref="setPayRecordStatusFormRef" :config="setPayRecordStatusFormConfig" />
</CommonDialog>
</div>
</template>
<script setup name="Payables">
import CommonPage from '@/components/commonPage'
import { ref, reactive,nextTick } from 'vue'
import { ref, reactive, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { formatCurrency } from '@/utils/number'
import { expectedFortuneList, payRecordList, addPayRecord, updatePayRecord, exportPayRecord, payableReport } from '@/api/financial/commission'
import {
expectedFortuneList,
payRecordList,
addPayRecord,
updatePayRecord,
exportPayRecord,
payableReport
} from '@/api/financial/commission'
import SearchForm from '@/components/SearchForm/SearchForm.vue'
import CommonDialog from '@/components/commonDialog'
import { loadDicts, getDictLabel } from '@/utils/useDict'
import useUserStore from '@/store/modules/user'
import { safeDownload } from '@/utils/safeDownload'
const payableReportTableData = ref([])
const payableReportTableColumns = ref([
{ prop: 'policyNo', label: '保单号', sortable: true, width: '150', formatter: (row) => row.policyNo || '-' },
{ prop: 'broker', label: '转介人(主)', sortable: true, width: '150', formatter: (row) => row.broker || '-' },
{ prop: 'brokerGradeName', label: '职级', sortable: true, width: '80', formatter: (row) => row.brokerGradeName || '-' },
{ prop: 'fortunePeriod', label: '出账期数', sortable: true, width: '120', formatter: (row) => row.fortunePeriod || '-' },
{ prop: 'fortuneTotalPeriod', label: '出账总期数', sortable: true, width: '120', formatter: (row) => row.fortuneTotalPeriod || '-' },
{ prop: 'payoutDate', label: '出账日(估)', sortable: true, width: '130', },
{ prop: 'paidRatio', label: '已出账比例', sortable: true, width: '120', formatter: (row) => (row.paidRatio || 0) + '%' || '-' },
{ prop: 'unpaidRatio', label: '待出账比例', sortable: true, width: '120', formatter: (row) => (row.unpaidRatio || 0) + '%' || '-' },
{ prop: 'paidAmount', label: '已出账金额', sortable: true, width: '120', formatter: (row) => formatCurrency(row.paidAmount || 0) },
{ prop: 'unpaidAmount', label: '待出账金额', sortable: true, width: '120', formatter: (row) => formatCurrency(row.unpaidAmount || 0) },
{ prop: 'hkdAmount', label: '应出账金额(估)', sortable: true, width: '120', formatter: (row) => formatCurrency(row.hkdAmount || 0) },
{ prop: 'currency', label: '出账币种', sortable: true, width: '120', formatter: (row) => row.currency || '-' },
{ prop: 'premium', label: '期交保费', sortable: true, width: '120', formatter: (row) => formatCurrency(row.premium || 0) },
{ prop: 'insuranceCompany', label: '保险公司', sortable: true, width: '120', formatter: (row) => row.insuranceCompany || '-' },
{ prop: 'productName', label: '产品计划', sortable: true, width: '120', formatter: (row) => row.productName || '-' },
{ prop: 'policyCurrency', label: '保单币种', sortable: true, width: '120', formatter: (row) => row.policyCurrency || '-' }
{
prop: 'policyNo',
label: '保单号',
sortable: true,
width: '150',
formatter: row => row.policyNo || '-'
},
{
prop: 'policyHolder',
label: '投保人',
sortable: true,
width: '120',
formatter: row => row.policyHolder || '-'
},
{
prop: 'broker',
label: '转介人(主)',
sortable: true,
width: '150',
formatter: row => row.broker || '-'
},
{
prop: 'brokerGradeName',
label: '职级',
sortable: true,
width: '80',
formatter: row => row.brokerGradeName || '-'
},
{
prop: 'fortunePeriod',
label: '出账期数',
sortable: true,
width: '120',
formatter: row => row.fortunePeriod || '-'
},
{
prop: 'fortuneTotalPeriod',
label: '出账总期数',
sortable: true,
width: '120',
formatter: row => row.fortuneTotalPeriod || '-'
},
{ prop: 'payoutDate', label: '出账日(估)', sortable: true, width: '130' },
{
prop: 'paidRatio',
label: '已出账比例',
sortable: true,
width: '120',
formatter: row => (row.paidRatio || 0) + '%' || '-'
},
{
prop: 'unpaidRatio',
label: '待出账比例',
sortable: true,
width: '120',
formatter: row => (row.unpaidRatio || 0) + '%' || '-'
},
{
prop: 'paidAmount',
label: '已出账金额',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.paidAmount || 0)
},
{
prop: 'unpaidAmount',
label: '待出账金额',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.unpaidAmount || 0)
},
{
prop: 'hkdAmount',
label: '应出账金额(估)',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.hkdAmount || 0)
},
{
prop: 'currency',
label: '出账币种',
sortable: true,
width: '120',
formatter: row => row.currency || '-'
},
{
prop: 'premium',
label: '期交保费',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.premium || 0)
},
{
prop: 'insuranceCompany',
label: '保险公司',
sortable: true,
width: '120',
formatter: row => row.insuranceCompany || '-'
},
{
prop: 'productName',
label: '产品计划',
sortable: true,
width: '120',
formatter: row => row.productName || '-'
},
{
prop: 'policyCurrency',
label: '保单币种',
sortable: true,
width: '120',
formatter: row => row.policyCurrency || '-'
}
])
const detailDialogVisible = ref(false)
......@@ -158,33 +371,170 @@ const detailDialogVisible = ref(false)
const payableReportListTableColumns = ref([
// { prop: 'fortuneBizType', label: '应付款类型', sortable: true, width: '120', formatter: (row) => getFortuneBizTypeLabel(row.fortuneBizType) || '-' },
// { prop: 'payableNo', label: '应付账款编号', sortable: true, width: '120', formatter: (row) => row.payableNo || '-' },
{ prop: 'policyNo', label: '保单号', sortable: true, width: '130', formatter: (row) => row.policyNo || '-' },
{ prop: 'broker', label: '转介人', sortable: true, width: '120', formatter: (row) => row.broker || '-' },
{ prop: 'brokerGradeName', label: '职级', sortable: true, width: '120', formatter: (row) => row.brokerGradeName || '-' },
{ prop: 'fortuneName', label: '出账项目', sortable: true, width: '120', formatter: (row) => row.fortuneName || '-' },
{ prop: 'status', label: '出账状态', sortable: true, width: '120', formatter: (row) => getDictLabel('csf_expected_fortune_status', row.status) || '-' },
{ prop: 'currencyName', label: '出账币种', sortable: true, width: '100', formatter: (row) => row.currencyName || '-' },
{ prop: 'fortunePeriod', label: '出账期数', sortable: true, width: '100', formatter: (row) => row.fortunePeriod || '-' },
{ prop: 'fortuneTotalPeriod', label: '出账总期数', sortable: true, width: '100', formatter: (row) => row.fortuneTotalPeriod || '-' },
{ prop: 'payoutDate', label: '出账日(估)', sortable: true, width: '120', formatter: (row) => row.payoutDate || '-' },
{ prop: 'actualPayoutDate', label: '出账日(实)', sortable: true, width: '120', formatter: (row) => row.actualPayoutDate || '-' },
{
prop: 'policyNo',
label: '保单号',
sortable: true,
width: '130',
formatter: row => row.policyNo || '-'
},
{
prop: 'broker',
label: '转介人',
sortable: true,
width: '120',
formatter: row => row.broker || '-'
},
{
prop: 'brokerGradeName',
label: '职级',
sortable: true,
width: '120',
formatter: row => row.brokerGradeName || '-'
},
{
prop: 'fortuneName',
label: '出账项目',
sortable: true,
width: '120',
formatter: row => row.fortuneName || '-'
},
{
prop: 'status',
label: '出账状态',
sortable: true,
width: '120',
formatter: row => getDictLabel('csf_expected_fortune_status', row.status) || '-'
},
{
prop: 'currencyName',
label: '出账币种',
sortable: true,
width: '100',
formatter: row => row.currencyName || '-'
},
{
prop: 'fortunePeriod',
label: '出账期数',
sortable: true,
width: '100',
formatter: row => row.fortunePeriod || '-'
},
{
prop: 'fortuneTotalPeriod',
label: '出账总期数',
sortable: true,
width: '100',
formatter: row => row.fortuneTotalPeriod || '-'
},
{
prop: 'payoutDate',
label: '出账日(估)',
sortable: true,
width: '120',
formatter: row => row.payoutDate || '-'
},
{
prop: 'actualPayoutDate',
label: '出账日(实)',
sortable: true,
width: '120',
formatter: row => row.actualPayoutDate || '-'
},
// { prop: 'commissionRatio', label: '职级对应积分比例', sortable: true, width: '120', formatter: (row) => (row.commissionRatio || 0) || '-' },
{ prop: 'hkdAmount', label: '应出账金额(估)', sortable: true, width: '120', formatter: (row) => formatCurrency(row.hkdAmount || 0) },
{ prop: 'paidRatio', label: '已出账比例', sortable: true, width: '120', formatter: (row) => (row.paidRatio || 0) + '%' || '-' },
{ prop: 'paidAmount', label: '已出账金额', sortable: true, width: '120', formatter: (row) => formatCurrency(row.paidAmount || 0) },
{ prop: 'unpaidRatio', label: '待出账比例', sortable: true, width: '120', formatter: (row) => (row.unpaidRatio || 0) + '%' || '-' },
{ prop: 'unpaidAmount', label: '待出账金额(估)', sortable: true, width: '120', formatter: (row) => formatCurrency(row.unpaidAmount || 0) },
{ prop: 'brokerRatio', label: '持有比例', sortable: true, width: '100', formatter: (row) => (row.brokerRatio || 0) + '%' || '-' },
{ prop: 'premium', label: '期交保费', sortable: true, width: '120', formatter: (row) => formatCurrency(row.premium || 0) },
{ prop: 'insuranceCompany', label: '保险公司', sortable: true, width: '120', formatter: (row) => row.insuranceCompany || '-' },
{ prop: 'productName', label: '产品计划', sortable: true, width: '120', formatter: (row) => row.productName || '-' },
{ prop: 'statusDesc', label: '修改理由', sortable: true, width: '120', formatter: (row) => row.statusDesc || '-' },
{ prop: 'remark', label: '备注', sortable: true, width: '120', formatter: (row) => row.remark || '-' },
{
prop: 'hkdAmount',
label: '应出账金额(估)',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.hkdAmount || 0)
},
{
prop: 'paidRatio',
label: '已出账比例',
sortable: true,
width: '120',
formatter: row => (row.paidRatio || 0) + '%' || '-'
},
{
prop: 'paidAmount',
label: '已出账金额',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.paidAmount || 0)
},
{
prop: 'unpaidRatio',
label: '待出账比例',
sortable: true,
width: '120',
formatter: row => (row.unpaidRatio || 0) + '%' || '-'
},
{
prop: 'unpaidAmount',
label: '待出账金额(估)',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.unpaidAmount || 0)
},
{
prop: 'brokerRatio',
label: '持有比例',
sortable: true,
width: '100',
formatter: row => (row.brokerRatio || 0) + '%' || '-'
},
{
prop: 'premium',
label: '期交保费',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.premium || 0)
},
{
prop: 'insuranceCompany',
label: '保险公司',
sortable: true,
width: '120',
formatter: row => row.insuranceCompany || '-'
},
{
prop: 'productName',
label: '产品计划',
sortable: true,
width: '120',
formatter: row => row.productName || '-'
},
{
prop: 'statusDesc',
label: '修改理由',
sortable: true,
width: '120',
formatter: row => row.statusDesc || '-'
},
{
prop: 'creatorName',
label: '操作人',
sortable: true,
width: '120',
formatter: row => row.creatorName || '-'
},
{
prop: 'updateTime',
label: '操作时间',
sortable: true,
width: '180',
formatter: row => row.updateTime || '-'
},
{
prop: 'remark',
label: '备注',
sortable: true,
width: '120',
formatter: row => row.remark || '-'
}
])
// 设置出账状态
const setPayRecordStatusDialogVisible = ref(false)
const selectedRow = ref(null)
......@@ -195,15 +545,14 @@ const setPayRecordStatusFormConfig = ref([
prop: 'status',
label: '出账状态',
dictType: 'csf_expected_fortune_status'
}, {
},
{
type: 'textarea',
prop: 'statusDesc',
label: '修改理由',
},
])
const visibleDefaultButtons = ref([
'add', 'export', 'reset', 'query'
label: '修改理由'
}
])
const visibleDefaultButtons = ref(['add', 'export', 'reset', 'query'])
const userStore = useUserStore()
// 应收单类型
const fortuneBizTypeOptions = [
......@@ -211,7 +560,7 @@ const fortuneBizTypeOptions = [
{ value: 'U', label: '非关联保单应付单' }
]
// 应付单类型通过value转成label
const getFortuneBizTypeLabel = (value) => {
const getFortuneBizTypeLabel = value => {
const item = fortuneBizTypeOptions.find(item => item.value === value)
return item?.label || ''
}
......@@ -224,6 +573,11 @@ const searchConfig = ref([
label: '保单号'
},
{
type: 'input',
prop: 'policyHolder',
label: '投保人'
},
{
type: 'daterange',
prop: 'payoutDate',
label: '出账日(估)',
......@@ -242,9 +596,7 @@ const searchConfig = ref([
prop: 'fortunePeriod',
label: '出账期数',
inputType: 'decimal',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
},
{
type: 'select',
......@@ -264,11 +616,12 @@ const searchConfig = ref([
valueKey: 'insuranceCompanyBizId',
labelKey: 'fullName',
multiple: true,
transform: (res) => {
transform: res => {
console.log(res)
return res?.data.records || []
}
}, {
},
{
type: 'select',
prop: 'productLaunchBizIdList',
label: '产品计划',
......@@ -276,17 +629,22 @@ const searchConfig = ref([
keywordField: 'productName',
requestParams: {
tenantBizId: userStore.projectInfo.tenantBizId || '',
projectBizId: userStore.projectInfo.projectBizId || '', fieldBizId: 'field_olk1qZe81qHHKXbw', fieldValueBizId: 'field_value_uOfJH5ucA2YwJpbn', pageNo: 1, pageSize: 20
projectBizId: userStore.projectInfo.projectBizId || '',
fieldBizId: 'field_olk1qZe81qHHKXbw',
fieldValueBizId: 'field_value_uOfJH5ucA2YwJpbn',
pageNo: 1,
pageSize: 20
},
placeholder: '输入产品计划名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'productLaunchBizId',
labelKey: 'productName',
multiple: true,
transform: (res) => {
transform: res => {
return res?.data.records || []
}
}, {
},
{
type: 'select',
prop: 'fortuneBizType',
label: '应付单类型',
......@@ -294,7 +652,8 @@ const searchConfig = ref([
{ value: 'R', label: '关联保单应付单' },
{ value: 'U', label: '非关联保单应付单' }
]
}, {
},
{
type: 'select',
prop: 'teamBizIdList',
label: '出单团队',
......@@ -306,17 +665,16 @@ const searchConfig = ref([
multiple: true,
valueKey: 'teamBizId',
labelKey: 'teamName',
transform: (res) => {
transform: res => {
return res?.data.records || []
}
},
}
])
const payRecordDialogTableVisible = ref(false)
// 新增出账记录
const addPayRecordFormModel = ref({
fortuneBizType: 'U',
fortuneBizType: 'U'
})
const addPayRecordDialogVisible = ref(false)
const addPayRecordFormRef = ref()
......@@ -329,58 +687,61 @@ const addPayRecordFormConfig = [
{ value: 'R', label: '关联保单应付单' },
{ value: 'U', label: '非关联保单应付单' }
]
}, {
},
{
type: 'input',
prop: 'policyNo',
label: '关联保单号',
visible: (formData) => formData.fortuneBizType === 'R',
}, {
visible: formData => formData.fortuneBizType === 'R'
},
{
type: 'input',
prop: 'fortunePeriod',
label: '佣金期数',
inputType: 'decimal',
visible: (formData) => formData.fortuneBizType === 'R',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
}, {
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' }
]
}, {
visible: formData => formData.fortuneBizType === 'R',
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
},
{
type: 'date',
prop: 'payoutDate',
label: '出账日(估)',
placeholder: '请选择'
}, {
},
{
type: 'date',
prop: 'actualPayoutDate',
label: '出账日(实)',
placeholder: '请选择',
maxDate: 'today'
}, {
},
{
type: 'input',
prop: 'hkdAmount',
label: '出账金额',
rules: [
{ pattern: /^-?\d+(\.\d{1,2})?$/, message: '小数(最多两位)', trigger: 'blur' }
]
}, {
rules: [{ pattern: /^-?\d+(\.\d{1,2})?$/, 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: '转介人',
......@@ -392,19 +753,52 @@ const addPayRecordFormConfig = [
valueKey: 'clientUserBizId',
labelKey: 'realName',
onChangeExtraFields: {
broker: 'realName',// 自动同步 raw.name 到 reconciliationCompany
broker: 'realName', // 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code'
},
transform: (res) => {
transform: res => {
return res?.data.records || []
}
}, {
},
{
type: 'select',
prop: 'status',
label: '出账状态',
dictType: 'csf_expected_fortune_status'
},
{
type: 'input',
prop: 'exchangeRate',
label: '结算汇率',
inputType: 'decimal',
rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
// defaultValue: 1
},
{
type: 'input',
prop: 'remark',
label: '备注'
}
]
const handleInputChange = async (formType, prop, value, item) => {
if (formType == 'addPayRecord') {
await nextTick()
let policyNo = addPayRecordFormModel.value.policyNo
let fortunePeriod = addPayRecordFormModel.value.fortunePeriod
if ((prop == 'policyNo' || prop == 'fortunePeriod') && fortunePeriod && policyNo) {
//等待后端接口
const res = await commissionExchangeRateApi({
policyNo: policyNo,
commissionPeriod: fortunePeriod
})
if (res.code == 200) {
addPayRecordFormModel.value.exchangeRate = res.data
} else {
ElMessage.error('查询结算汇率失败')
}
}
}
}
const handleConfirmAddPayRecord = async () => {
if (editStatus.value === 'add') {
const handleConfirmAddPayRecordparams = addPayRecordFormRef.value.getFormData()
......@@ -427,14 +821,13 @@ const handleConfirmAddPayRecord = async () => {
ElMessage.success('更新出账记录成功')
addPayRecordDialogVisible.value = false
addPayRecordFormRef.value.resetForm()
editStatus.value = 'add';
editStatus.value = 'add'
loadPayRecordTableData(selectedRow.value.expectedFortuneBizId)
expectedFortuneListData()
} catch (error) {
ElMessage.error(error.message)
}
}
}
// 弹窗表单重置
const resetAddPayRecordForm = () => {
......@@ -443,7 +836,6 @@ const resetAddPayRecordForm = () => {
addPayRecordDialogVisible.value = false
}
const editStatus = ref('add')
// 出账记录
const payRecordDialogTableData = ref([])
......@@ -452,14 +844,29 @@ const handleSelect = async (e, row) => {
selectedRow.value = { ...row }
if (e === 'payRecord') {
payRecordDialogTableVisible.value = true
loadPayRecordTableData(selectedRow.value.expectedFortuneBizId);
loadPayRecordTableData(selectedRow.value.expectedFortuneBizId)
payRecordDialogTableColumns.value = [
{ property: 'broker', label: '转介人', width: '100', },
{ property: 'broker', label: '转介人', width: '100' },
{ property: 'fortuneName', label: '出账项目', width: '150' },
{ property: 'currentPaymentHkdAmount', label: '出账金额', width: '150',formatter:(row)=> formatCurrency(row.currentPaymentHkdAmount || 0)},
{
property: 'currentPaymentHkdAmount',
label: '出账金额',
width: '150',
formatter: row => formatCurrency(row.currentPaymentHkdAmount || 0)
},
{ property: 'currency', label: '出账币种', width: '150' },
{ property: 'currentPaymentRatio', label: '出账比例', width: '150', formatter: (row) => `${row.currentPaymentRatio}%` },
{ property: 'fortuneUnpaidRatio', label: '待出账比例', width: '150', formatter: (row) => `${row.fortuneUnpaidRatio}%` },
{
property: 'currentPaymentRatio',
label: '出账比例',
width: '150',
formatter: row => `${row.currentPaymentRatio}%`
},
{
property: 'fortuneUnpaidRatio',
label: '待出账比例',
width: '150',
formatter: row => `${row.fortuneUnpaidRatio}%`
},
{ property: 'fortunePeriod', label: '佣金期数', width: '150' },
{ property: 'fortuneTotalPeriod', label: '总期数', width: '150' },
{ property: 'reconciliationOperator', label: '操作人', width: '150' },
......@@ -474,29 +881,28 @@ const handleSelect = async (e, row) => {
label: '出账状态',
dictType: 'csf_expected_fortune_status',
defaultValue: selectedRow.value.status || ''
}, {
},
{
type: 'textarea',
prop: 'statusDesc',
label: '修改理由',
defaultValue: selectedRow.value.statusDesc || ''
},
}
]
} else if (e === 'updateData') {
editStatus.value = 'edit'
addPayRecordDialogVisible.value = true
// 2. 使用 nextTick 等待 DOM 更新
nextTick(() => {
// 3. 此时 addRecordRef.value 一定存在了
if (addPayRecordFormRef.value && selectedRow.value) {
addPayRecordFormModel.value = { ...selectedRow.value };
}
});
// 2. 使用 nextTick 等待 DOM 更新
nextTick(() => {
// 3. 此时 addRecordRef.value 一定存在了
if (addPayRecordFormRef.value && selectedRow.value) {
addPayRecordFormModel.value = { ...selectedRow.value }
}
})
console.log(addPayRecordFormModel.value)
}
}
// 分页相关
const currentPage = ref(1)
const pageSize = ref(10)
......@@ -531,11 +937,7 @@ const handleExport = async () => {
const response = await exportPayRecord(params)
// 文件名设置为应收款导出_yyyy-MM-dd hh:mm:ss.xlsx,不需要-,用字符串
const fileName = `应付款导出_${new Date().toLocaleString().replace(/\//g, '').replace(/:/g, '').replace(/\s/g, '')}.xlsx`
await safeDownload(
response,
fileName,
'application/vnd.ms-excel;charset=utf-8'
)
await safeDownload(response, fileName, 'application/vnd.ms-excel;charset=utf-8')
}
const handleReset = () => {
......@@ -581,12 +983,12 @@ const operationBtnList = ref([
])
// 分页事件
const handleSizeChange = (val) => {
const handleSizeChange = val => {
pageSize.value = val
loadTableData()
}
const handleCurrentChange = (val) => {
const handleCurrentChange = val => {
currentPage.value = val
loadTableData()
}
......@@ -618,7 +1020,6 @@ const loadTableData = async () => {
totalPolicyCount: response.data.statisticsVO.totalPolicyCount,
totalPremiumAmount: response.data.statisticsVO.totalPremiumAmount
}
} catch (error) {
console.error('加载数据失败:', error)
// ElMessage.error('加载数据失败')
......@@ -628,7 +1029,7 @@ const loadTableData = async () => {
}
// 出账记录查询
const loadPayRecordTableData = async (expectedFortuneBizId) => {
const loadPayRecordTableData = async expectedFortuneBizId => {
loading.value = true
try {
const params = {
......@@ -636,7 +1037,6 @@ const loadPayRecordTableData = async (expectedFortuneBizId) => {
}
const response = await payRecordList(params)
payRecordDialogTableData.value = response.data.records
} catch (error) {
console.error('加载数据失败:', error)
// ElMessage.error('加载数据失败')
......@@ -726,23 +1126,22 @@ const expectedFortuneListData = async () => {
}
}
const viewDetail = (row) => {
const viewDetail = row => {
selectedDetailRecordRow.value = row
detailDialogVisible.value = true
expectedFortuneListData()
}
// 分页事件
const handleSizeChangeDetailRecord = (val) => {
const handleSizeChangeDetailRecord = val => {
detailPageInfo.value.pageSize = val
expectedFortuneListData()
}
// 分页事件
const handleCurrentChangeDetailRecord = (val) => {
const handleCurrentChangeDetailRecord = val => {
detailPageInfo.value.currentPage = val
expectedFortuneListData()
}
</script>
<style scoped lang="scss">
......@@ -759,5 +1158,4 @@ const handleCurrentChangeDetailRecord = (val) => {
border-top: 1px solid #ebeef5;
text-align: right;
}
</style>
\ No newline at end of file
</style>
......@@ -18,7 +18,7 @@
</template>
</CommonPage>
</div>
</template>
......@@ -62,7 +62,8 @@ const searchConfig = ref([
])
const tableColumns = ref([
{ prop: 'salarySplitNo', label: '发放编号', sortable: true, width: '150',fixed:'left'},
{ prop: 'salarySplitNo', label: '发放编号', sortable: true, width: '150', fixed: 'left' },
{ prop: 'businessNo', label: '业务编号' , width: '150',fixed:'left'},
{ prop: 'brokerName', label: '转介人', sortable: true, width: '150',fixed:'left'},
{ prop: 'internalNumber', label: '内部编号', sortable: true, width: '150'},
{ prop: 'team', label: '所属团队', sortable: true, width: '150'},
......@@ -151,7 +152,7 @@ const handleExport = async () => {
startMonth:params.payoutMonth ? params.payoutMonth[0] : '',
endMonth:params.payoutMonth ? params.payoutMonth[1] :'',
payoutMonth:undefined,
}
const response = await exportPayRoll(params)
if(response.data && response.data.url){
......@@ -185,4 +186,4 @@ const operationBtnList = ref([
</script>
<style scoped></style>
\ No newline at end of file
<style scoped></style>
<template>
<div>
<CommonPage :operationBtnList="operationBtnList" :visibleDefaultButtons="visibleDefaultButtons"
:showSearchForm="true" :show-pagination="true" :total="pageTotal" :current-page="currentPage"
:page-size="pageSize" @size-change="handleSizeChange" @current-change="handleCurrentChange">
<!-- 搜索区域 -->
<template #searchForm>
<SearchForm ref="searchFormRef" :config="searchConfig" />
</template>
<div>
<CommonPage
:operationBtnList="operationBtnList"
:visibleDefaultButtons="visibleDefaultButtons"
:showSearchForm="true"
:show-pagination="true"
:total="pageTotal"
:current-page="currentPage"
:page-size="pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<!-- 搜索区域 -->
<template #searchForm>
<SearchForm ref="searchFormRef" :config="searchConfig" />
</template>
<!-- 列表区域 -->
<template #table>
<div class="statistics-container" v-if="statisticsData.totalPolicyCount > 0">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="statisticsData.totalAmount" :formatter="value=>formatCurrency(value)">
<template #title>
<div style="display: inline-flex; align-items: center">
应收款总金额
</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="statisticsData.totalPaidAmount" :formatter="value=>formatCurrency(value)">
<template #title>
<div style="display: inline-flex; align-items: center">
已入账金额
</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="statisticsData.pendingPaidAmount" :formatter="value=>formatCurrency(value)">
<template #title>
<div style="display: inline-flex; align-items: center">
待入账金额(估)
</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="statisticsData.paidAmountRatio" :formatter="value=>formatCurrency(value,'',2)+'%'">
<template #title>
<div style="display: inline-flex; align-items: center">
已入账比例
</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保单数" :value="statisticsData.totalPolicyCount" />
</el-col>
</el-row>
</div>
<!-- 应收款管理列表 -->
<el-table :data="tableData" height="400" border highlight-current-row style="width: 100%"
v-loading="loading">
<el-table-column v-for="(column, index) in receivableReportTableColumns" :key="index" :fixed="column.fixed"
:prop="column.prop" :label="column.label" :width="column.width" :sortable="column.sortable"
:formatter="column.formatter" />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="scope">
<el-button link type="primary" size="small" @click="viewDetail(scope.row)">
查看明细
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 列表区域 -->
<template #table>
<div class="statistics-container" v-if="statisticsData.totalPolicyCount > 0">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
:value="statisticsData.totalAmount"
:formatter="value => formatCurrency(value)"
>
<template #title>
<div style="display: inline-flex; align-items: center">应收款总金额</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
:value="statisticsData.totalPaidAmount"
:formatter="value => formatCurrency(value)"
>
<template #title>
<div style="display: inline-flex; align-items: center">已入账金额</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
:value="statisticsData.pendingPaidAmount"
:formatter="value => formatCurrency(value)"
>
<template #title>
<div style="display: inline-flex; align-items: center">待入账金额(估)</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
:value="statisticsData.paidAmountRatio"
:formatter="value => formatCurrency(value, '', 2) + '%'"
>
<template #title>
<div style="display: inline-flex; align-items: center">已入账比例</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保单数" :value="statisticsData.totalPolicyCount" />
</el-col>
</el-row>
</div>
<!-- 应收款管理列表 -->
<el-table
:data="tableData"
height="400"
border
highlight-current-row
style="width: 100%"
v-loading="loading"
>
<el-table-column
v-for="(column, index) in receivableReportTableColumns"
:key="index"
:fixed="column.fixed"
:prop="column.prop"
:label="column.label"
:width="column.width"
:sortable="column.sortable"
:formatter="column.formatter"
/>
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="scope">
<el-button link type="primary" size="small" @click="viewDetail(scope.row)">
查看明细
</el-button>
</template>
</CommonPage>
<CommonDialog dialogTitle="应收明细" dialogWidth="80%" :openDialog="detailDialogVisible" :showAction="false"
:showClose="true" @close="detailDialogVisible = false">
<div class="statistics-container" v-if="detailRecordStatistics.totalPolicyCount > 0">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="detailRecordStatistics.totalAmount" :formatter="value=>formatCurrency(value)">
<template #title>
<div style="display: inline-flex; align-items: center">
应入账总金额
</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="detailRecordStatistics.totalPaidAmount" :formatter="value=>formatCurrency(value)">
<template #title>
<div style="display: inline-flex; align-items: center">
已入账金额
</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="detailRecordStatistics.totalUnpaidAmount" :formatter="value=>formatCurrency(value)">
<template #title>
<div style="display: inline-flex; align-items: center">
待入账金额
</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="detailRecordStatistics.paidAmountRatio" :formatter="value=>formatCurrency(value,'',2)+'%'">
<template #title>
<div style="display: inline-flex; align-items: center">
已入账比例
</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="detailRecordStatistics.totalPolicyCount" >
<template #title>
<div style="display: inline-flex; align-items: center">
总保单数
</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="detailRecordStatistics.totalPremium" :formatter="value=>formatCurrency(value)">
<template #title>
<div style="display: inline-flex; align-items: center">
总保费
</div>
</template>
</el-statistic>
</el-col>
</el-row>
</div>
<el-table :data="receivableReportTableData" border style="width: 100%;margin-bottom: 10px;min-height: 300px;">
<el-table-column v-for="item in receivableReportItemTableColumns" :key="item.property" :prop="item.prop"
:label="item.label" :width="item.width" :formatter="item.formatter" />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="{ row }">
<el-popover placement="right" :width="200" trigger="click">
<template #reference>
<el-icon>
<MoreFilled />
</el-icon>
</template>
<el-menu @select="handleSelect($event, row)" popper-class="custom-menu">
<el-menu-item :index="item.value" v-for="item in dropdownItems" :key="item.value">
{{ item.label }}
</el-menu-item>
</el-menu>
</el-popover>
</template>
</el-table-column>
</el-table>
<el-pagination v-model:current-page="detailPageInfo.currentPage" v-model:page-size="detailPageInfo.pageSize"
:page-sizes="[50, 100, 200, 300]" size="default" layout="total, sizes, prev, pager, next, jumper"
:total="detailPageInfo.total" @size-change="handleSizeChangeDetailRecord"
@current-change="handleCurrentChangeDetailRecord" />
</CommonDialog>
<CommonDialog dialogTitle="入账记录" dialogWidth="80%" :openDialog="entryRecordDialogTableVisible"
:showAction="false" :showClose="true" @close="entryRecordDialogTableVisible = false">
<el-table :data="entryRecordDialogTableData" border style="width: 100%">
<el-table-column v-for="item in entryRecordDialogTableColumns" :key="item.property"
:prop="item.property" :label="item.label" :width="item.width" :formatter="item.formatter" />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="scope">
<el-button link type="primary" size="small" @click="handleClick(scope.row)">
查看比对记录
</el-button>
</template>
</el-table-column>
</el-table>
</CommonDialog>
<CommonDialog dialogTitle="操作记录" dialogWidth="80%" :openDialog="actionRecordsDialogVisible" :showAction="false"
:showClose="true" @close="actionRecordsDialogVisible = false">
<el-table :data="actionRecordsDialogTableData" border style="width: 100%">
<el-table-column v-for="item in actionRecordsDialogTableColumns" :key="item.property"
:prop="item.property" :label="item.label" :width="item.width"/>
</el-table>
</CommonDialog>
<CommonDialog dialogTitle="设置入账状态" dialogWidth="80%" :openDialog="setStatusDialogTableVisible"
@close="setStatusDialogTableVisible = false" @confirm="handleConfirmSetStatus">
<SearchForm ref="setCommissionRecordStatusFormRef" :config="setCommissionRecordStatusFormConfig" />
</CommonDialog>
<!-- 新增应收款管理 -->
<CommonDialog :dialogTitle="editStatus === 'add' ? '新增应收款' : '更新应收款'" dialogWidth="80%"
:openDialog="addReceivablesDialogVisible"
@close="addReceivablesDialogVisible = false; resetAddReceivablesForm()"
@confirm="handleConfirmAddReceivables">
<SearchForm ref="addRecordRef" :config="addReceivablesFormConfig" v-model="addReceivablesFormModel" />
</CommonDialog>
</div>
</el-table-column>
</el-table>
</template>
</CommonPage>
<CommonDialog
dialogTitle="应收明细"
dialogWidth="80%"
:openDialog="detailDialogVisible"
:showAction="false"
:showClose="true"
@close="detailDialogVisible = false"
>
<div class="statistics-container" v-if="detailRecordStatistics.totalPolicyCount > 0">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
:value="detailRecordStatistics.totalAmount"
:formatter="value => formatCurrency(value)"
>
<template #title>
<div style="display: inline-flex; align-items: center">应入账总金额</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
:value="detailRecordStatistics.totalPaidAmount"
:formatter="value => formatCurrency(value)"
>
<template #title>
<div style="display: inline-flex; align-items: center">已入账金额</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
:value="detailRecordStatistics.totalUnpaidAmount"
:formatter="value => formatCurrency(value)"
>
<template #title>
<div style="display: inline-flex; align-items: center">待入账金额</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
:value="detailRecordStatistics.paidAmountRatio"
:formatter="value => formatCurrency(value, '', 2) + '%'"
>
<template #title>
<div style="display: inline-flex; align-items: center">已入账比例</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="detailRecordStatistics.totalPolicyCount">
<template #title>
<div style="display: inline-flex; align-items: center">总保单数</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic
:value="detailRecordStatistics.totalPremium"
:formatter="value => formatCurrency(value)"
>
<template #title>
<div style="display: inline-flex; align-items: center">总保费</div>
</template>
</el-statistic>
</el-col>
</el-row>
</div>
<el-table
:data="receivableReportTableData"
border
style="width: 100%; margin-bottom: 10px; min-height: 300px"
>
<el-table-column
v-for="item in receivableReportItemTableColumns"
:key="item.property"
:prop="item.prop"
:label="item.label"
:width="item.width"
:formatter="item.formatter"
/>
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="{ row }">
<el-popover placement="right" :width="200" trigger="click">
<template #reference>
<el-icon>
<MoreFilled />
</el-icon>
</template>
<el-menu @select="handleSelect($event, row)" popper-class="custom-menu">
<el-menu-item :index="item.value" v-for="item in dropdownItems" :key="item.value">
{{ item.label }}
</el-menu-item>
</el-menu>
</el-popover>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:current-page="detailPageInfo.currentPage"
v-model:page-size="detailPageInfo.pageSize"
:page-sizes="[50, 100, 200, 300]"
size="default"
layout="total, sizes, prev, pager, next, jumper"
:total="detailPageInfo.total"
@size-change="handleSizeChangeDetailRecord"
@current-change="handleCurrentChangeDetailRecord"
/>
</CommonDialog>
<CommonDialog
dialogTitle="入账记录"
dialogWidth="80%"
:openDialog="entryRecordDialogTableVisible"
:showAction="false"
:showClose="true"
@close="entryRecordDialogTableVisible = false"
>
<el-table :data="entryRecordDialogTableData" border style="width: 100%">
<el-table-column
v-for="item in entryRecordDialogTableColumns"
:key="item.property"
:prop="item.property"
:label="item.label"
:width="item.width"
:formatter="item.formatter"
/>
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="scope">
<el-button link type="primary" size="small" @click="handleClick(scope.row)">
查看比对记录
</el-button>
</template>
</el-table-column>
</el-table>
</CommonDialog>
<CommonDialog
dialogTitle="操作记录"
dialogWidth="80%"
:openDialog="actionRecordsDialogVisible"
:showAction="false"
:showClose="true"
@close="actionRecordsDialogVisible = false"
>
<el-table :data="actionRecordsDialogTableData" border style="width: 100%">
<el-table-column
v-for="item in actionRecordsDialogTableColumns"
:key="item.property"
:prop="item.property"
:label="item.label"
:width="item.width"
/>
</el-table>
</CommonDialog>
<CommonDialog
dialogTitle="设置入账状态"
dialogWidth="80%"
:openDialog="setStatusDialogTableVisible"
@close="setStatusDialogTableVisible = false"
@confirm="handleConfirmSetStatus"
>
<SearchForm
ref="setCommissionRecordStatusFormRef"
:config="setCommissionRecordStatusFormConfig"
/>
</CommonDialog>
<!-- 新增应收款管理 -->
<CommonDialog
:dialogTitle="editStatus === 'add' ? '新增应收款' : '更新应收款'"
dialogWidth="80%"
:openDialog="addReceivablesDialogVisible"
@close="((addReceivablesDialogVisible = false), resetAddReceivablesForm())"
@confirm="handleConfirmAddReceivables"
>
<SearchForm
ref="addRecordRef"
:config="addReceivablesFormConfig"
v-model="addReceivablesFormModel"
@inputChange="(prop, value, item) => handleInputChange('addReceivables', prop, value, item)"
/>
</CommonDialog>
</div>
</template>
<script setup name="Receivables">
import { validateEnglish2 } from '@/utils/validate'
import CommonPage from '@/components/commonPage'
import CommonDialog from '@/components/commonDialog'
import { ref, reactive, onMounted, computed,nextTick } from 'vue'
import { ref, reactive, onMounted, computed, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { MoreFilled } from '@element-plus/icons-vue'
import {
receivedFortuneList, updateCommissionExpected, commissionEntryEditRecords,
exportReceivedFortune, commissionExpectedRecord, addReceivedFortune, receivableReport,CommissionExpectedChangeStatus
receivedFortuneList,
updateCommissionExpected,
commissionEntryEditRecords,
exportReceivedFortune,
commissionExpectedRecord,
addReceivedFortune,
receivableReport,
CommissionExpectedChangeStatus,
commissionExchangeRateApi
} from '@/api/financial/commission'
import { numberWithCommas } from '@/utils/index'
import SearchForm from '@/components/SearchForm/SearchForm.vue'
......@@ -213,277 +310,304 @@ import { formatCurrency } from '@/utils/number'
const userStore = useUserStore()
// 应收单类型
const commissionBizTypeOptions = [
{ value: 'R', label: '关联保单应收单' },
{ value: 'U', label: '非关联保单应收单' }
{ value: 'R', label: '关联保单应收单' },
{ value: 'U', label: '非关联保单应收单' }
]
const editStatus = ref('add')
// 新增应收款管理
const addReceivablesFormModel = ref({
commissionBizType: 'U',
commissionBizType: 'U'
})
const addReceivablesDialogVisible = ref(false)
const addRecordRef = ref(null)
const addReceivablesFormConfig = [
{
type: 'select',
prop: 'commissionBizType',
label: '应收单类型',
placeholder: '应收单类型',
options: commissionBizTypeOptions
},
{
type: 'input',
prop: 'policyNo',
label: '保单号',
visible: (formData) => formData.commissionBizType == 'R'
}, {
type: 'input',
prop: 'commissionPeriod',
label: '佣金期数',
inputType: 'decimal',
visible: (formData) => formData.commissionBizType === 'R',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
},
{
type: 'input',
prop: 'totalPeriod',
label: '总期数',
inputType: 'decimal',
visible: (formData) => formData.commissionBizType === 'R',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
}, {
type: 'date',
prop: 'commissionDate',
label: '入账日(估)',
placeholder: '请选择'
},
// {
// type: 'date',
// prop: 'actualCommissionDate',
// label: '入账日(实)',
// placeholder: '请选择',
// maxDate: 'today'
// },
{
type: 'input',
prop: 'amount',
label: '入账金额',
inputType: 'decimal',
decimalDigits: 4,
visible: (formData) => formData.commissionBizType === 'U',
rules: [
{ required: true, message: '请输入金额', trigger: 'blur' },
{ pattern: /^\d+(\.\d{1,4})?$/, message: '最多四位小数', trigger: 'blur' }
]
}, {
type: 'input',
prop: 'commissionRatio',
label: '入账比例(%)',
visible: (formData) => formData.commissionBizType === 'R',
rules: [
{ required: true, message: '请输入入账比例', trigger: 'blur' },
{ pattern: /^-?\d+(\.\d{1,4})?$/, message: '最多四位小数', trigger: 'blur' }
]
}, {
type: 'select',
prop: 'currency',
label: '入账币种',
dictType: 'bx_currency_type'
}, {
type: 'select',
prop: 'commissionType',
label: '入账项目',
dictType: 'csf_commission_type',
onChangeExtraFields: {
commissionName: 'itemLabel',
},
}, {
type: 'select',
prop: 'reconciliationCompanyBizId',
label: '对账公司',
api: '/insurance/base/api/insuranceReconciliationCompany/page',
keywordField: 'name',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入对账公司名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'reconciliationCompanyBizId',
labelKey: 'name',
onChangeExtraFields: {
reconciliationCompany: 'name',// 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code'
},
transform: (res) => {
console.log('对账公司', res)
return res.data.records || []
}
{
type: 'select',
prop: 'commissionBizType',
label: '应收单类型',
placeholder: '应收单类型',
options: commissionBizTypeOptions
},
{
type: 'input',
prop: 'policyNo',
label: '保单号',
visible: formData => formData.commissionBizType == 'R'
},
{
type: 'input',
prop: 'commissionPeriod',
label: '佣金期数',
inputType: 'decimal',
visible: formData => formData.commissionBizType === 'R',
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
},
{
type: 'input',
prop: 'totalPeriod',
label: '总期数',
inputType: 'decimal',
visible: formData => formData.commissionBizType === 'R',
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
},
{
type: 'date',
prop: 'commissionDate',
label: '入账日(估)',
placeholder: '请选择'
},
// {
// type: 'date',
// prop: 'actualCommissionDate',
// label: '入账日(实)',
// placeholder: '请选择',
// maxDate: 'today'
// },
{
type: 'input',
prop: 'amount',
label: '入账金额',
inputType: 'decimal',
decimalDigits: 4,
visible: formData => formData.commissionBizType === 'U',
rules: [
{ required: true, message: '请输入金额', trigger: 'blur' },
{ pattern: /^\d+(\.\d{1,4})?$/, message: '最多四位小数', trigger: 'blur' }
]
},
{
type: 'input',
prop: 'commissionRatio',
label: '入账比例(%)',
visible: formData => formData.commissionBizType === 'R',
rules: [
{ required: true, message: '请输入入账比例', trigger: 'blur' },
{ pattern: /^-?\d+(\.\d{1,4})?$/, message: '最多四位小数', trigger: 'blur' }
]
},
{
type: 'select',
prop: 'currency',
label: '入账币种',
dictType: 'bx_currency_type'
},
{
type: 'select',
prop: 'commissionType',
label: '入账项目',
dictType: 'csf_commission_type',
onChangeExtraFields: {
commissionName: 'itemLabel'
}
},
{
type: 'select',
prop: 'reconciliationCompanyBizId',
label: '对账公司',
api: '/insurance/base/api/insuranceReconciliationCompany/page',
keywordField: 'name',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入对账公司名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'reconciliationCompanyBizId',
labelKey: 'name',
onChangeExtraFields: {
reconciliationCompany: 'name', // 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code'
},
{
type: 'input',
prop: 'remark',
label: '备注',
transform: res => {
console.log('对账公司', res)
return res.data.records || []
}
},
{
type: 'input',
prop: 'exchangeRate',
label: '结算汇率',
inputType: 'decimal',
rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
// defaultValue: 1
},
{
type: 'input',
prop: 'remark',
label: '备注'
}
]
// 弹窗表单重置
const resetAddReceivablesForm = () => {
addRecordRef.value.resetForm()
editStatus.value = 'add'
addRecordRef.value.resetForm()
editStatus.value = 'add'
}
const handleConfirmAddReceivables = async () => {
if (editStatus.value === 'add') {
const p = addRecordRef.value.getFormData()
try {
await addReceivedFortune({
commissionExpectedAddDtoList: [p]
})
ElMessage.success('新增应收款成功')
addReceivablesDialogVisible.value = false
resetAddReceivablesForm()
handleQuery()
} catch (error) {
ElMessage.error(error.message)
}
} else {
try {
const res = await updateCommissionExpected({
commissionExpectedBizId: selectedRow.value.commissionExpectedBizId,
...addReceivablesFormModel.value
})
if (res.code === 200) {
ElMessage.success('应收款修改成功')
addReceivablesDialogVisible.value = false
resetAddReceivablesForm()
loadTableData() // 重新加载表格
} else {
ElMessage.error(res.msg || '应收款修改失败')
}
} catch (error) {
console.error('修改应收款失败:', error)
ElMessage.error('修改应收款失败')
}
}
if (editStatus.value === 'add') {
const p = addRecordRef.value.getFormData()
try {
await addReceivedFortune({
commissionExpectedAddDtoList: [p]
})
ElMessage.success('新增应收款成功')
addReceivablesDialogVisible.value = false
resetAddReceivablesForm()
handleQuery()
} catch (error) {
ElMessage.error(error.message)
}
} else {
try {
const res = await updateCommissionExpected({
commissionExpectedBizId: selectedRow.value.commissionExpectedBizId,
...addReceivablesFormModel.value
})
if (res.code === 200) {
ElMessage.success('应收款修改成功')
addReceivablesDialogVisible.value = false
resetAddReceivablesForm()
loadTableData() // 重新加载表格
} else {
ElMessage.error(res.msg || '应收款修改失败')
}
} catch (error) {
console.error('修改应收款失败:', error)
ElMessage.error('修改应收款失败')
}
}
}
const searchFormRef = ref(null)
const searchParams = ref({})
const searchConfig = ref([
{
type: 'input',
prop: 'policyNo',
label: '保单号'
},
{
type: 'daterange',
prop: 'entryDate',
label: '入账日(估)',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间'
},
{
type: 'select',
prop: 'statusList',
label: '入账状态',
multiple: true,
dictType: 'csf_expected_commission_status'
},
{
type: 'input',
prop: 'commissionPeriod',
label: '入账期数',
inputType: 'decimal',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
},
{
type: 'select',
prop: 'fortuneName',
label: '入账项目',
dictType: 'csf_commission_type'
},
{
type: 'select',
prop: 'reconciliationCompanyBizIdList',
label: '对账公司',
api: '/insurance/base/api/insuranceReconciliationCompany/page',
keywordField: 'name',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入对账公司名称搜索',
debounceWait: 500, // 自定义防抖时间
multiple: true,
valueKey: 'reconciliationCompanyBizId',
labelKey: 'name',
transform: (res) => {
console.log(res)
return res?.data.records || []
}
},
{
type: 'select',
prop: 'insurerCompanyBizIdList',
label: '保险公司',
api: '/insurance/base/api/insuranceCompany/page',
keywordField: 'queryContent',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入保险公司名称搜索',
debounceWait: 500, // 自定义防抖时间
multiple: true,
valueKey: 'insuranceCompanyBizId',
labelKey: 'fullName',
transform: (res) => {
return res?.data.records || []
}
}, {
type: 'select',
prop: 'productLaunchBizId',
label: '产品计划',
api: '/product/api/relProjectProductLaunch/parameter/page',
keywordField: 'productName',
requestParams: {
tenantBizId: userStore.projectInfo.tenantBizId || '',
projectBizId: userStore.projectInfo.projectBizId || '',
fieldBizId: 'field_olk1qZe81qHHKXbw',
fieldValueBizId: 'field_value_uOfJH5ucA2YwJpbn',
pageNo: 1,
pageSize: 20
},
placeholder: '输入产品计划名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'productLaunchBizId',
labelKey: 'productName',
transform: (res) => {
return res?.data.records || []
}
}, {
type: 'select',
prop: 'commissionBizType',
label: '应收单类型',
options: commissionBizTypeOptions,
}, {
type: 'select',
prop: 'teamBizId',
label: '出单团队',
api: '/csf/api/team/page',
keywordField: 'teamName',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入出单团队名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'teamBizId',
labelKey: 'teamName',
transform: (res) => {
return res?.data.records || []
}
{
type: 'input',
prop: 'policyNo',
label: '保单号'
},
{
type: 'daterange',
prop: 'entryDate',
label: '入账日(估)',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间'
},
{
type: 'select',
prop: 'statusList',
label: '入账状态',
multiple: true,
dictType: 'csf_expected_commission_status'
},
{
type: 'input',
prop: 'commissionPeriod',
label: '入账期数',
inputType: 'decimal',
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
},
{
type: 'select',
prop: 'fortuneName',
label: '入账项目',
dictType: 'csf_commission_type'
},
{
type: 'select',
prop: 'reconciliationCompanyBizIdList',
label: '对账公司',
api: '/insurance/base/api/insuranceReconciliationCompany/page',
keywordField: 'name',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入对账公司名称搜索',
debounceWait: 500, // 自定义防抖时间
multiple: true,
valueKey: 'reconciliationCompanyBizId',
labelKey: 'name',
transform: res => {
console.log(res)
return res?.data.records || []
}
},
{
type: 'select',
prop: 'insurerCompanyBizIdList',
label: '保险公司',
api: '/insurance/base/api/insuranceCompany/page',
keywordField: 'queryContent',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入保险公司名称搜索',
debounceWait: 500, // 自定义防抖时间
multiple: true,
valueKey: 'insuranceCompanyBizId',
labelKey: 'fullName',
transform: res => {
return res?.data.records || []
}
},
{
type: 'select',
prop: 'productLaunchBizId',
label: '产品计划',
api: '/product/api/relProjectProductLaunch/parameter/page',
keywordField: 'productName',
requestParams: {
tenantBizId: userStore.projectInfo.tenantBizId || '',
projectBizId: userStore.projectInfo.projectBizId || '',
fieldBizId: 'field_olk1qZe81qHHKXbw',
fieldValueBizId: 'field_value_uOfJH5ucA2YwJpbn',
pageNo: 1,
pageSize: 20
},
placeholder: '输入产品计划名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'productLaunchBizId',
labelKey: 'productName',
transform: res => {
return res?.data.records || []
}
},
{
type: 'select',
prop: 'commissionBizType',
label: '应收单类型',
options: commissionBizTypeOptions
},
{
type: 'select',
prop: 'teamBizId',
label: '出单团队',
api: '/csf/api/team/page',
keywordField: 'teamName',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入出单团队名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'teamBizId',
labelKey: 'teamName',
transform: res => {
return res?.data.records || []
}
},
{
type: 'input',
prop: 'policyHolder',
label: '投保人(中文/英文)'
},
{
type: 'input',
prop: 'insured',
label: '受保人(中文/英文)'
}
// {
// type: 'input',
// prop: 'signer',
// label: '签单员'
// },
// {
// type: 'input',
// prop: 'brokerName',
// label: '转介人(主)'
// }
])
// 分页相关
......@@ -492,18 +616,17 @@ const pageSize = ref(10)
const pageTotal = ref(0)
const loading = ref(false)
// 应收单类型通过value转成label
const getCommissionBizTypeLabel = (value) => {
const item = commissionBizTypeOptions.find(item => item.value === value)
return item?.label || ''
const getCommissionBizTypeLabel = value => {
const item = commissionBizTypeOptions.find(item => item.value === value)
return item?.label || ''
}
// 表格操作菜单
const dropdownItems = [
{ label: '入账记录', value: 'entryRecord' },
{ label: '设置状态', value: 'setStatus' },
{ label: '更新数据', value: 'updateData' },
{ label: '入账记录', value: 'entryRecord' },
{ label: '设置状态', value: 'setStatus' },
{ label: '更新数据', value: 'updateData' }
]
// 弹窗状态
......@@ -527,360 +650,625 @@ const tableData = ref([])
// 统计信息
const statisticsData = ref({
totalAmount: 0,
totalPaidAmount: 0,
pendingPaidAmount: 0,
paidAmountRatio: 0,
totalPolicyCount: 0
totalAmount: 0,
totalPaidAmount: 0,
pendingPaidAmount: 0,
paidAmountRatio: 0,
totalPolicyCount: 0
})
const handleInputChange = async (formType, prop, value, item) => {
if (formType == 'addReceivables') {
await nextTick()
let policyNo = addReceivablesFormModel.value.policyNo
let commissionPeriod = addReceivablesFormModel.value.commissionPeriod
if ((prop == 'policyNo' || prop == 'commissionPeriod') && commissionPeriod && policyNo) {
//等待后端接口
const res = await commissionExchangeRateApi({
policyNo: policyNo,
commissionPeriod: commissionPeriod
})
if (res.code == 200) {
addReceivablesFormModel.value.exchangeRate = res.data
} else {
ElMessage.error('查询结算汇率失败')
}
}
}
}
// 按钮事件处理
const handleAdd = () => {
addReceivablesDialogVisible.value = true
if (addRecordRef.value) {
addRecordRef.value.resetForm()
}
addReceivablesFormModel.value = {}
addReceivablesDialogVisible.value = true
if (addRecordRef.value) {
addRecordRef.value.resetForm()
}
addReceivablesFormModel.value = {}
}
const handleImport = () => ElMessage.info('点击导入按钮')
const handleExport = async () => {
// 获取搜索参数
const params = searchFormRef.value?.getFormData() || {}
const response = await exportReceivedFortune(params)
// 文件名设置为应收款导出_yyyy-MM-dd hh:mm:ss.xlsx,不需要-,用字符串
const fileName = `应收款导出_${new Date().toLocaleString().replace(/\//g, '').replace(/:/g, '').replace(/\s/g, '')}.xlsx`
await safeDownload(
response,
fileName,
'application/vnd.ms-excel;charset=utf-8'
)
// 获取搜索参数
const params = searchFormRef.value?.getFormData() || {}
const response = await exportReceivedFortune(params)
// 文件名设置为应收款导出_yyyy-MM-dd hh:mm:ss.xlsx,不需要-,用字符串
const fileName = `应收款导出_${new Date().toLocaleString().replace(/\//g, '').replace(/:/g, '').replace(/\s/g, '')}.xlsx`
await safeDownload(response, fileName, 'application/vnd.ms-excel;charset=utf-8')
}
const handleReset = () => {
// 重置搜索表单
searchFormRef.value.resetForm()
searchParams.value = {}
console.log('表单已重置')
loadTableData()
// 重置搜索表单
searchFormRef.value.resetForm()
searchParams.value = {}
console.log('表单已重置')
loadTableData()
}
const handleQuery = () => {
loadTableData()
loadTableData()
}
const visibleDefaultButtons = ref([
'add', 'export', 'reset', 'query'
])
const visibleDefaultButtons = ref(['add', 'export', 'reset', 'query'])
// 按钮配置
const operationBtnList = ref([
{ key: 'add', direction: 'left', click: handleAdd },
// { key: 'import', direction: 'left', click: handleImport },
{ key: 'export', direction: 'right', click: handleExport },
{ key: 'reset', direction: 'right', click: handleReset },
{ key: 'query', direction: 'right', click: handleQuery }
{ key: 'add', direction: 'left', click: handleAdd },
// { key: 'import', direction: 'left', click: handleImport },
{ key: 'export', direction: 'right', click: handleExport },
{ key: 'reset', direction: 'right', click: handleReset },
{ key: 'query', direction: 'right', click: handleQuery }
])
// 分页事件
const handleSizeChange = (val) => {
pageSize.value = val
loadTableData()
const handleSizeChange = val => {
pageSize.value = val
loadTableData()
}
const handleCurrentChange = (val) => {
currentPage.value = val
loadTableData()
const handleCurrentChange = val => {
currentPage.value = val
loadTableData()
}
// 加载表格数据
const loadTableData = async () => {
const searchParams = searchFormRef.value.getFormData() || {}
loading.value = true
try {
const params = {
...searchParams,
commissionDateStart: searchParams?.entryDate?.[0] || undefined,
commissionDateEnd: searchParams?.entryDate?.[1] || undefined,
entryDate: undefined,
pageNo: currentPage.value,
pageSize: pageSize.value
}
const response = await receivableReport(params)
tableData.value = response.data.page.records || []
pageTotal.value = response.data.page.total || 0
pageSize.value = response.data.page.size || 50
// 统计信息
statisticsData.value = {
totalAmount: response.data.statisticsVO.totalAmount,
totalPaidAmount: response.data.statisticsVO.totalPaidAmount,
pendingPaidAmount: response.data.statisticsVO.pendingPaidAmount,
paidAmountRatio: response.data.statisticsVO.paidAmountRatio,
totalPolicyCount: response.data.statisticsVO.totalPolicyCount
}
} catch (error) {
console.error('加载数据失败:', error)
ElMessage.error('加载数据失败')
} finally {
loading.value = false
const searchParams = searchFormRef.value.getFormData() || {}
// let msg = validateEnglish2(searchParams.eng)
// if (searchParams.eng && msg) {
// ElMessage.error(`投保人(英文):${msg}`)
// return
// }
loading.value = true
try {
const params = {
...searchParams,
commissionDateStart: searchParams?.entryDate?.[0] || undefined,
commissionDateEnd: searchParams?.entryDate?.[1] || undefined,
entryDate: undefined,
pageNo: currentPage.value,
pageSize: pageSize.value
}
const response = await receivableReport(params)
tableData.value = response.data.page.records || []
pageTotal.value = response.data.page.total || 0
pageSize.value = response.data.page.size || 50
// 统计信息
statisticsData.value = {
totalAmount: response.data.statisticsVO.totalAmount,
totalPaidAmount: response.data.statisticsVO.totalPaidAmount,
pendingPaidAmount: response.data.statisticsVO.pendingPaidAmount,
paidAmountRatio: response.data.statisticsVO.paidAmountRatio,
totalPolicyCount: response.data.statisticsVO.totalPolicyCount
}
} catch (error) {
console.error('加载数据失败:', error)
ElMessage.error('加载数据失败')
} finally {
loading.value = false
}
}
// 入账记录查询
const loadEntryRecordData = async (cbd) => {
loading.value = true
try {
const params = { commissionExpectedBizId: cbd }
const response = await commissionExpectedRecord(params)
return response.data.records || []
} catch (error) {
console.error('加载入账记录失败:', error)
ElMessage.error('加载入账记录失败')
return []
} finally {
loading.value = false
}
const loadEntryRecordData = async cbd => {
loading.value = true
try {
const params = { commissionExpectedBizId: cbd }
const response = await commissionExpectedRecord(params)
return response.data.records || []
} catch (error) {
console.error('加载入账记录失败:', error)
ElMessage.error('加载入账记录失败')
return []
} finally {
loading.value = false
}
}
// 入账操作记录查询
const loadEntryEditRecordData = async (cbd) => {
loading.value = true
try {
const params = { commissionBizId: cbd }
const response = await commissionEntryEditRecords(params)
return response.data.records || []
} catch (error) {
console.error('加载操作记录失败:', error)
ElMessage.error('加载操作记录失败')
return []
} finally {
loading.value = false
}
const loadEntryEditRecordData = async cbd => {
loading.value = true
try {
const params = { commissionBizId: cbd }
const response = await commissionEntryEditRecords(params)
return response.data.records || []
} catch (error) {
console.error('加载操作记录失败:', error)
ElMessage.error('加载操作记录失败')
return []
} finally {
loading.value = false
}
}
// 操作菜单选择事件
const handleSelect = async (e, row) => {
selectedRow.value = { ...row }
if (e === 'entryRecord') {
entryRecordDialogTableVisible.value = true
entryRecordDialogTableColumns.value = [
{ property: 'reconciliationYearMonth', label: '检核年月', width: '100' },
{ property: 'commissionPeriod', label: '佣金期数', width: '100' },
{ property: 'totalPeriod', label: '总期数', width: '150' },
{ property: 'exchangeRate', label: '结算汇率(实)', width: '150' },
{ property: 'currency', label: '入账币种', width: '150' },
{ property: 'amount', label: '入账金额', width: '150',formatter:(row)=> formatCurrency(row.amount || 0) },
{ property: 'currentCommissionRatio', label: '入账比例', width: '150', formatter: (row) => `${row.currentCommissionRatio || ''}%` },
{ property: 'commissionDate', label: '入账日', width: '150' },
{ property: 'commissionStatusName', label: '入账状态', width: '150' }
]
// 加载真实数据
const records = await loadEntryRecordData(row.commissionExpectedBizId)
entryRecordDialogTableData.value = records.length ? records : []
} else if (e === 'setStatus') {
console.log(selectedRow.value.status)
setCommissionRecordStatusFormConfig.value = [
{
type: 'select',
prop: 'status',
label: '入账状态',
dictType: 'csf_expected_commission_status',
defaultValue: selectedRow.value.status || ''
}, {
type: 'textarea',
prop: 'statusDesc',
label: '修改理由',
defaultValue: selectedRow.value.statusDesc || ''
},
]
setStatusDialogTableVisible.value = true
} else if (e === 'updateData') {
editStatus.value = 'update'
addReceivablesDialogVisible.value = true
// 2. 使用 nextTick 等待 DOM 更新
nextTick(() => {
// 3. 此时 addRecordRef.value 一定存在了
if (addRecordRef.value && selectedRow.value) {
addReceivablesFormModel.value = { ...selectedRow.value };
console.log('赋值成功:', addReceivablesFormModel.value);
}
});
console.log('更新数据', selectedRow.value)
}
selectedRow.value = { ...row }
if (e === 'entryRecord') {
entryRecordDialogTableVisible.value = true
entryRecordDialogTableColumns.value = [
{ property: 'reconciliationYearMonth', label: '检核年月', width: '100' },
{ property: 'commissionPeriod', label: '佣金期数', width: '100' },
{ property: 'totalPeriod', label: '总期数', width: '150' },
{ property: 'exchangeRate', label: '结算汇率(实)', width: '150' },
{ property: 'currency', label: '入账币种', width: '150' },
{
property: 'amount',
label: '入账金额',
width: '150',
formatter: row => formatCurrency(row.amount || 0)
},
{
property: 'currentCommissionRatio',
label: '入账比例',
width: '150',
formatter: row => `${row.currentCommissionRatio || ''}%`
},
{ property: 'commissionDate', label: '入账日', width: '150' },
{ property: 'commissionStatusName', label: '入账状态', width: '150' }
]
// 加载真实数据
const records = await loadEntryRecordData(row.commissionExpectedBizId)
entryRecordDialogTableData.value = records.length ? records : []
} else if (e === 'setStatus') {
console.log(selectedRow.value.status)
setCommissionRecordStatusFormConfig.value = [
{
type: 'select',
prop: 'status',
label: '入账状态',
dictType: 'csf_expected_commission_status',
defaultValue: selectedRow.value.status || ''
},
{
type: 'textarea',
prop: 'statusDesc',
label: '修改理由',
defaultValue: selectedRow.value.statusDesc || ''
}
]
setStatusDialogTableVisible.value = true
} else if (e === 'updateData') {
editStatus.value = 'update'
addReceivablesDialogVisible.value = true
// 2. 使用 nextTick 等待 DOM 更新
nextTick(() => {
// 3. 此时 addRecordRef.value 一定存在了
if (addRecordRef.value && selectedRow.value) {
addReceivablesFormModel.value = { ...selectedRow.value }
console.log('赋值成功:', addReceivablesFormModel.value)
}
})
console.log('更新数据', selectedRow.value)
}
}
// 查看比对记录
const handleClick = async (row) => {
actionRecordsDialogVisible.value = true
actionRecordsDialogTableColumns.value = [
{ property: 'checkMonth', label: '检核年月', width: '100' },
{ property: 'compareStatus', label: '比对状态', width: '150' },
{ property: 'entryRatio', label: '入账比例', width: '150' },
{ property: 'shouldEntryRatio', label: '应入账比例', width: '150' },
{ property: 'reconciliationCompany', label: '对账公司', width: '150' },
{ property: 'createTime', label: '比对时间', width: '150' },
{ property: 'userName', label: '比对人', width: '150' }
]
// 加载真实数据
const records = await loadEntryEditRecordData(row.commissionBizId)
actionRecordsDialogTableData.value = records.length ? records : []
const handleClick = async row => {
actionRecordsDialogVisible.value = true
actionRecordsDialogTableColumns.value = [
{ property: 'checkMonth', label: '检核年月', width: '100' },
{ property: 'compareStatus', label: '比对状态', width: '150' },
{ property: 'entryRatio', label: '入账比例', width: '150' },
{ property: 'shouldEntryRatio', label: '应入账比例', width: '150' },
{ property: 'reconciliationCompany', label: '对账公司', width: '150' },
{ property: 'createTime', label: '比对时间', width: '150' },
{ property: 'userName', label: '比对人', width: '150' }
]
// 加载真实数据
const records = await loadEntryEditRecordData(row.commissionBizId)
actionRecordsDialogTableData.value = records.length ? records : []
}
// 新增:设置状态确认事件
const handleConfirmSetStatus = () => {
const formData = setCommissionRecordStatusFormRef.value.getFormData()
if (!formData.status) {
return ElMessage.warning('请选择入账状态')
}
if (!formData.statusDesc) {
return ElMessage.warning('请输入修改理由')
}
// 调用接口修改状态(示例)
ElMessageBox.confirm('确定要修改入账状态吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
const res = await CommissionExpectedChangeStatus({
commissionExpectedBizId: selectedRow.value.commissionExpectedBizId,
...formData
})
if (res.code === 200) {
ElMessage.success('状态修改成功')
setStatusDialogTableVisible.value = false
loadTableData() // 重新加载表格
} else {
ElMessage.error(res.msg || '状态修改失败')
}
} catch (error) {
console.error('修改状态失败:', error)
ElMessage.error('修改状态失败')
const formData = setCommissionRecordStatusFormRef.value.getFormData()
if (!formData.status) {
return ElMessage.warning('请选择入账状态')
}
if (!formData.statusDesc) {
return ElMessage.warning('请输入修改理由')
}
// 调用接口修改状态(示例)
ElMessageBox.confirm('确定要修改入账状态吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(async () => {
try {
const res = await CommissionExpectedChangeStatus({
commissionExpectedBizId: selectedRow.value.commissionExpectedBizId,
...formData
})
if (res.code === 200) {
ElMessage.success('状态修改成功')
setStatusDialogTableVisible.value = false
loadTableData() // 重新加载表格
} else {
ElMessage.error(res.msg || '状态修改失败')
}
}).catch(() => {
ElMessage.info('已取消修改')
} catch (error) {
console.error('修改状态失败:', error)
ElMessage.error('修改状态失败')
}
})
.catch(() => {
ElMessage.info('已取消修改')
})
}
// 获取入账状态,字典值转化方法
onMounted(async () => {
try {
await loadDicts(['csf_expected_commission_status'])
loadTableData()
} catch (error) {
console.error('字典加载失败', error)
} finally {
loading.value = false
}
try {
await loadDicts(['csf_expected_commission_status'])
loadTableData()
} catch (error) {
console.error('字典加载失败', error)
} finally {
loading.value = false
}
})
// 格式化函数(每次渲染都会调用,所以能拿到最新字典)
const formatStatus = (row,column) => {
return getDictLabel('csf_expected_commission_status', row.status) // 实时查缓存
const formatStatus = (row, column) => {
return getDictLabel('csf_expected_commission_status', row.status) // 实时查缓存
}
const detailDialogVisible = ref(false)
const receivableReportTableData = ref([])
const receivableReportTableColumns = ref([
{ prop: 'policyNo', label: '保单号', sortable: true, width: '150',fixed:'left', formatter: (row) => row.policyNo || '-' },
{ prop: 'reconciliationCompany', label: '对账公司', sortable: true, width: '150', formatter: (row) => row.reconciliationCompany || '-' },
{ prop: 'commissionPeriod', label: '入账期数', sortable: true, width: '100', formatter: (row) => row.commissionPeriod || '-' },
{ prop: 'totalPeriod', label: '入账总期数', sortable: true, width: '100', formatter: (row) => row.totalPeriod || '-' },
{ prop: 'commissionDate', label: '入账日(估)', sortable: true, width: '130', },
{ prop: 'commissionRatio', label: '产品对应来佣率', sortable: true, width: '120', formatter: (row) => (row.commissionRatio || 0) + '%' || '-' },
{ prop: 'hkdAmount', label: '预估入账金额HKD', sortable: true, width: '120', formatter: (row) => formatCurrency(row.hkdAmount || 0) },
{ prop: 'paidRatio', label: '已入账比例', sortable: true, width: '120', formatter: (row) => (row.paidRatio || 0) + '%' || '-' },
{ prop: 'paidAmount', label: '已入账金额HKD', sortable: true, width: '120', formatter: (row) => formatCurrency(row.paidAmount || 0) },
{ prop: 'unpaidRatio', label: '待入账比例', sortable: true, width: '120', formatter: (row) => (row.unpaidRatio || 0) + '%' || '-' },
{ prop: 'unpaidAmount', label: '待入账金额HKD', sortable: true, width: '120', formatter: (row) => formatCurrency(row.unpaidAmount || 0) },
{ prop: 'exchangeRate', label: '结算汇率(估)', sortable: true, width: '120'},
{ prop: 'insuranceCompany', label: '保险公司', sortable: true, width: '120', formatter: (row) => row.insuranceCompany || '-' },
{ prop: 'productName', label: '产品计划', sortable: true, width: '120', formatter: (row) => row.productName || '-' },
{ prop: 'premium', label: '期交保费', sortable: true, width: '120', formatter: (row) => formatCurrency(row.premium || 0) },
{ prop: 'policyCurrency', label: '保单币种', sortable: true, width: '120', formatter: (row) => row.policyCurrency || '-' },
{
prop: 'policyNo',
label: '保单号',
sortable: true,
width: '150',
fixed: 'left',
formatter: row => row.policyNo || '-'
},
{
prop: 'reconciliationCompany',
label: '对账公司',
sortable: true,
width: '150',
formatter: row => row.reconciliationCompany || '-'
},
{
prop: 'commissionPeriod',
label: '入账期数',
sortable: true,
width: '100',
formatter: row => row.commissionPeriod || '-'
},
{
prop: 'totalPeriod',
label: '入账总期数',
sortable: true,
width: '100',
formatter: row => row.totalPeriod || '-'
},
{ prop: 'commissionDate', label: '入账年月(估)', sortable: true, width: '130' },
{
prop: 'commissionRatio',
label: '产品对应来佣率',
sortable: true,
width: '120',
formatter: row => (row.commissionRatio || 0) + '%' || '-'
},
{
prop: 'hkdAmount',
label: '预估入账金额HKD',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.hkdAmount || 0)
},
{
prop: 'paidRatio',
label: '已入账比例',
sortable: true,
width: '120',
formatter: row => (row.paidRatio || 0) + '%' || '-'
},
{
prop: 'paidAmount',
label: '已入账金额HKD',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.paidAmount || 0)
},
{
prop: 'unpaidRatio',
label: '待入账比例',
sortable: true,
width: '120',
formatter: row => (row.unpaidRatio || 0) + '%' || '-'
},
{
prop: 'unpaidAmount',
label: '待入账金额HKD',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.unpaidAmount || 0)
},
{ prop: 'exchangeRate', label: '结算汇率(估)', sortable: true, width: '120' },
{
prop: 'insuranceCompany',
label: '保险公司',
sortable: true,
width: '120',
formatter: row => row.insuranceCompany || '-'
},
{
prop: 'policyHolder',
label: '投保人',
sortable: true,
width: '120',
formatter: row => row.policyHolder || '-'
},
{
prop: 'policyHolderEn',
label: '投保人(英文)',
sortable: true,
width: '120',
formatter: row => row.policyHolderEn || '-'
},
{
prop: 'remark',
label: '备注',
width: '150',
formatter: row => row.remark || '-'
},
{
prop: 'productName',
label: '产品计划',
sortable: true,
width: '120',
formatter: row => row.productName || '-'
},
{
prop: 'premium',
label: '期交保费',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.premium || 0)
},
{
prop: 'policyCurrency',
label: '保单币种',
sortable: true,
width: '120',
formatter: row => row.policyCurrency || '-'
}
])
// 应收明细
const receivableReportItemTableColumns = ref([
{ prop: 'commissionBizType', label: '应收单类型', sortable: true, width: '150', formatter: (row) => getCommissionBizTypeLabel(row.commissionBizType) || '-' },
{ prop: 'receivableNo', label: '应收款编号', sortable: true, width: '150', formatter: (row) => row.receivableNo || '-' },
{ prop: 'policyNo', label: '保单号', sortable: true, width: '150', formatter: (row) => row.policyNo || '-' },
{ prop: 'reconciliationCompany', label: '对账公司', sortable: true, width: '150', formatter: (row) => row.reconciliationCompany || '-' },
{ prop: 'status', label: '入账状态', sortable: true, width: '120', formatter: (row) => formatStatus(row) || '-' },
{ prop: 'commissionPeriod', label: '入账期数', sortable: true, width: '120', formatter: (row) => row.commissionPeriod || '-' },
{ prop: 'totalPeriod', label: '入账总期数', sortable: true, width: '120', formatter: (row) => row.totalPeriod || '-' },
{ prop: 'commissionName', label: '入账项目', sortable: true, width: '130', formatter: (row) => row.commissionName || '-' },
{ prop: 'commissionDate', label: '入账日(估)', sortable: true, width: '130', formatter: (row) => row.commissionDate || '-' },
{ prop: 'commissionRatio', label: '产品对应来佣率', sortable: true, width: '120', formatter: (row) => (row.commissionRatio || 0) + '%' || '-' },
{ prop: 'expectedAmount', label: '预估入账金额', sortable: true, width: '120', formatter: (row) => formatCurrency(row.expectedAmount || 0) },
{ prop: 'paidRatio', label: '实佣率', sortable: true, width: '120', formatter: (row) => (row.paidRatio || 0) + '%' || '-' },
{ prop: 'paidAmount', label: '已入账金额', sortable: true, width: '120', formatter: (row) => formatCurrency(row.paidAmount || 0) },
{ prop: 'pendingRatio', label: '实佣率缺口', sortable: true, width: '120', formatter: (row) => (row.pendingRatio || 0) + '%' || '-' },
{ prop: 'pendingAmount', label: '待入账金额', sortable: true, width: '120', formatter: (row) => formatCurrency(row.pendingAmount || 0) },
{ prop: 'defaultExchangeRate', label: '结算汇率(估)', sortable: true, width: '120'},
{ prop: 'insuranceCompany', label: '保险公司', sortable: true, width: '120', formatter: (row) => row.insuranceCompany || '-' },
{ prop: 'productName', label: '产品计划', sortable: true, width: '120', formatter: (row) => row.productName || '-' },
{ prop: 'premium', label: '期交保费', sortable: true, width: '120', formatter: (row) => formatCurrency(row.premium || 0) },
{ prop: 'policyCurrency', label: '保单币种', sortable: true, width: '120', formatter: (row) => row.policyCurrency || '-' },
{ prop: 'currency', label: '入账币种', sortable: true, width: '120', formatter: (row) => row.currency || '-' },
{ prop: 'statusDesc', label: '入账状态修改理由', sortable: true, width: '120', formatter: (row) => row.statusDesc || '-' },
{ prop: 'remark', label: '备注', sortable: true, width: '120', formatter: (row) => row.remark || '-' },
{
prop: 'commissionBizType',
label: '应收单类型',
sortable: true,
width: '150',
formatter: row => getCommissionBizTypeLabel(row.commissionBizType) || '-'
},
{
prop: 'receivableNo',
label: '应收款编号',
sortable: true,
width: '150',
formatter: row => row.receivableNo || '-'
},
{
prop: 'policyNo',
label: '保单号',
sortable: true,
width: '150',
formatter: row => row.policyNo || '-'
},
{
prop: 'reconciliationCompany',
label: '对账公司',
sortable: true,
width: '150',
formatter: row => row.reconciliationCompany || '-'
},
{
prop: 'status',
label: '入账状态',
sortable: true,
width: '120',
formatter: row => formatStatus(row) || '-'
},
{
prop: 'commissionPeriod',
label: '入账期数',
sortable: true,
width: '120',
formatter: row => row.commissionPeriod || '-'
},
{
prop: 'totalPeriod',
label: '入账总期数',
sortable: true,
width: '120',
formatter: row => row.totalPeriod || '-'
},
{
prop: 'commissionName',
label: '入账项目',
sortable: true,
width: '130',
formatter: row => row.commissionName || '-'
},
{
prop: 'commissionDate',
label: '入账日(估)',
sortable: true,
width: '130',
formatter: row => row.commissionDate || '-'
},
{
prop: 'commissionRatio',
label: '产品对应来佣率',
sortable: true,
width: '120',
formatter: row => (row.commissionRatio || 0) + '%' || '-'
},
{
prop: 'expectedAmount',
label: '预估入账金额',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.expectedAmount || 0)
},
{
prop: 'paidRatio',
label: '实佣率',
sortable: true,
width: '120',
formatter: row => (row.paidRatio || 0) + '%' || '-'
},
{
prop: 'paidAmount',
label: '已入账金额',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.paidAmount || 0)
},
{
prop: 'pendingRatio',
label: '实佣率缺口',
sortable: true,
width: '120',
formatter: row => (row.pendingRatio || 0) + '%' || '-'
},
{
prop: 'pendingAmount',
label: '待入账金额',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.pendingAmount || 0)
},
{ prop: 'defaultExchangeRate', label: '结算汇率(估)', sortable: true, width: '120' },
{
prop: 'insuranceCompany',
label: '保险公司',
sortable: true,
width: '120',
formatter: row => row.insuranceCompany || '-'
},
{
prop: 'productName',
label: '产品计划',
sortable: true,
width: '120',
formatter: row => row.productName || '-'
},
{
prop: 'premium',
label: '期交保费',
sortable: true,
width: '120',
formatter: row => formatCurrency(row.premium || 0)
},
{
prop: 'policyCurrency',
label: '保单币种',
sortable: true,
width: '120',
formatter: row => row.policyCurrency || '-'
},
{
prop: 'currency',
label: '入账币种',
sortable: true,
width: '120',
formatter: row => row.currency || '-'
},
{
prop: 'statusDesc',
label: '入账状态修改理由',
sortable: true,
width: '120',
formatter: row => row.statusDesc || '-'
},
{
prop: 'remark',
label: '备注',
sortable: true,
width: '120',
formatter: row => row.remark || '-'
}
])
const detailRecordStatistics = ref({})
const detailPageInfo = ref({
pageNo: 1,
pageSize: 10,
currentPage: 1,
total: 0
pageNo: 1,
pageSize: 10,
currentPage: 1,
total: 0
})
// 获取入账报告
const receivedFortuneListData = async () => {
loading.value = true
try {
const params = {
policyNo: selectedDetailRecordRow.value.policyNo,
commissionPeriod: selectedDetailRecordRow.value.commissionPeriod,
receivableNo: selectedDetailRecordRow.value.receivableNo,
pageNo: detailPageInfo.value.currentPage,
pageSize: detailPageInfo.value.pageSize
}
const response = await receivedFortuneList(params)
receivableReportTableData.value = response.data.page.records || []
detailPageInfo.value.total = response.data.page.total || 0
detailPageInfo.value.pageSize = response.data.page.size || 50
// 统计信息
detailRecordStatistics.value = {
totalAmount: response.data.expectedStatisticsVO.totalAmount,
totalPaidAmount: response.data.expectedStatisticsVO.totalPaidAmount,
pendingPaidAmount: response.data.expectedStatisticsVO.pendingPaidAmount,
paidAmountRatio: response.data.expectedStatisticsVO.paidAmountRatio,
totalPolicyCount: response.data.expectedStatisticsVO.totalPolicyCount,
totalPremium: response.data.expectedStatisticsVO.totalPremium
}
} catch (error) {
console.error('加载数据失败:', error)
ElMessage.error('加载数据失败')
} finally {
loading.value = false
loading.value = true
try {
const params = {
policyNo: selectedDetailRecordRow.value.policyNo,
commissionPeriod: selectedDetailRecordRow.value.commissionPeriod,
receivableNo: selectedDetailRecordRow.value.receivableNo,
pageNo: detailPageInfo.value.currentPage,
pageSize: detailPageInfo.value.pageSize
}
const response = await receivedFortuneList(params)
receivableReportTableData.value = response.data.page.records || []
detailPageInfo.value.total = response.data.page.total || 0
detailPageInfo.value.pageSize = response.data.page.size || 50
// 统计信息
detailRecordStatistics.value = {
totalAmount: response.data.expectedStatisticsVO.totalAmount,
totalPaidAmount: response.data.expectedStatisticsVO.totalPaidAmount,
pendingPaidAmount: response.data.expectedStatisticsVO.pendingPaidAmount,
paidAmountRatio: response.data.expectedStatisticsVO.paidAmountRatio,
totalPolicyCount: response.data.expectedStatisticsVO.totalPolicyCount,
totalPremium: response.data.expectedStatisticsVO.totalPremium
}
} catch (error) {
console.error('加载数据失败:', error)
ElMessage.error('加载数据失败')
} finally {
loading.value = false
}
}
const selectedDetailRecordRow = ref({})
const viewDetail = (row) => {
selectedDetailRecordRow.value = row
detailDialogVisible.value = true
receivedFortuneListData()
const viewDetail = row => {
selectedDetailRecordRow.value = row
detailDialogVisible.value = true
receivedFortuneListData()
}
// 分页事件
const handleSizeChangeDetailRecord = (val) => {
detailPageInfo.value.pageSize = val
receivedFortuneListData()
const handleSizeChangeDetailRecord = val => {
detailPageInfo.value.pageSize = val
receivedFortuneListData()
}
// 分页事件
const handleCurrentChangeDetailRecord = (val) => {
detailPageInfo.value.currentPage = val
receivedFortuneListData()
const handleCurrentChangeDetailRecord = val => {
detailPageInfo.value.currentPage = val
receivedFortuneListData()
}
</script>
<style scoped lang="scss"></style>
\ No newline at end of file
<style scoped lang="scss"></style>
......@@ -12,7 +12,7 @@
<span class="iconfont icon-yanqiweiwancheng"></span>
<span
>{{ parseTime(processInfo.createTime) }}{{
processInfo.userBizId || '--'
processInfo.creatorName || '--'
}}创建</span
>
</div>
......@@ -161,7 +161,7 @@ const processInfo = ref({
fnaNo: '--',
status: '未保存',
createTime: proxy.parseTime(new Date()),
customerName: userStore.name
creatorName: userStore.name
}) // 流程详情信息
const updateStatus = ref(false)
const dictTypeLists = ref([])
......@@ -321,7 +321,7 @@ const getDictsData = async () => {
projectBizId: userStore.projectInfo.projectBizId,
tenantBizId: userStore.projectInfo.tenantBizId,
fieldBizId: 'field_olk1qZe81qHHKXbw',
fieldValueBizId: 'field_value_yXzTigvgUdRMFpoR',
fieldValueBizId: 'field_value_yXzTigvgUdRMFpoR'
}
const response6 = await secondAdditonalList(params6)
if (response6.code == 200) {
......@@ -399,6 +399,9 @@ function getProcessInfo(fnaBizId, changeTab, currentTab) {
getProcessDetail(fnaBizId).then(async res => {
if (res.code == 200) {
processInfo.value = res.data
if (!processInfo.value.creatorName) {
processInfo.value.creatorName = userStore.name
}
tabsList.value.forEach(item => {
if (res.data[item.key] && item.status) {
item.status = '1'
......
......@@ -61,6 +61,16 @@
</el-select>
</el-form-item>
</el-col>
<el-col :sm="12" :lg="6" :xs="24">
<el-form-item label="创建人" prop="creatorName">
<el-input
v-model="queryParams.creatorName"
placeholder="请输入创建人"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
......@@ -76,22 +86,26 @@
max-height="380px"
>
<el-table-column type="index" width="100" label="序号" />
<el-table-column label="客户姓名" align="center" prop="customerName" width="180" >
<template #default="scope">
<el-table-column label="客户姓名" align="center" prop="customerName" width="180">
<template #default="scope">
<el-button link size="default">
<span @click="copyToClipboard(scope.row.customerName,'客户姓名')">{{ scope.row.customerName }}</span>
<span @click="copyToClipboard(scope.row.customerName, '客户姓名')">{{
scope.row.customerName
}}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="中文姓名" align="center" prop="customerNameCn" width="180" >
<template #default="scope">
<el-button link size="default">
<span @click="copyToClipboard(scope.row.customerNameCn,'中文姓名')">{{ scope.row.customerNameCn }}</span>
</el-button>
</template>
</el-table-column>
</template>
</el-table-column>
<el-table-column label="中文姓名" align="center" prop="customerNameCn" width="180">
<template #default="scope">
<el-button link size="default">
<span @click="copyToClipboard(scope.row.customerNameCn, '中文姓名')">{{
scope.row.customerNameCn
}}</span>
</el-button>
</template>
</el-table-column>
<!-- <el-table-column label="状态" align="center" prop="status" width="100" :formatter="getDictLabel('csf_fna_status')"/> -->
<el-table-column label="流程状态" sortable align="center" prop="status" width="200">
<el-table-column label="流程状态" sortable align="center" prop="status" width="200">
<template #default="scope">
<span>{{ selectDictLabel(csf_fna_status, scope.row.status) }}</span>
</template>
......@@ -101,13 +115,16 @@
align="center"
prop="fnaNo"
width="240"
show-overflow-tooltip>
<template #default="scope">
show-overflow-tooltip
>
<template #default="scope">
<el-button link size="default">
<span @click="copyToClipboard(scope.row.fnaNo,'流程编号')">{{ scope.row.fnaNo }}</span>
<span @click="copyToClipboard(scope.row.fnaNo, '流程编号')">{{
scope.row.fnaNo
}}</span>
</el-button>
</template>
</el-table-column>
</template>
</el-table-column>
<el-table-column
label="预约编号"
align="center"
......@@ -117,12 +134,14 @@
>
<template #default="scope">
<el-button link size="default">
<span @click="copyToClipboard(scope.row.appointmentNo,'预约编号')">{{ scope.row.appointmentNo }}</span>
<span @click="copyToClipboard(scope.row.appointmentNo, '预约编号')">{{
scope.row.appointmentNo
}}</span>
</el-button>
</template>
</el-table-column>
<!-- <el-table-column label="新单编号" align="center" prop="policyId" /> -->
</template>
</el-table-column>
<el-table-column label="保单号" align="center" prop="policyNo" width="180" />
<el-table-column label="创建人" align="center" prop="creatorName" width="180" />
<el-table-column label="创建时间" sortable align="center" prop="createTime" width="200">
<template #default="scope">
......@@ -181,7 +200,7 @@ import { getFnaList, deleteFna, subProcess } from '@/api/sign/fna'
import useUserStore from '@/store/modules/user'
import { MoreFilled, InfoFilled } from '@element-plus/icons-vue'
import useDictStore from '@/store/modules/dict'
import copyToClipboard from '@/utils/copyToClipboard';
import copyToClipboard from '@/utils/copyToClipboard'
const dictStore = useDictStore()
const userStore = useUserStore()
const router = useRouter()
......@@ -395,7 +414,6 @@ function handleUpdate(row) {
}
getList()
</script>
<style lang="scss" scoped>
.bottomBtn {
......
......@@ -98,7 +98,9 @@
</el-table-column>
<el-table-column prop="payer" label="付款人" width="150" />
<el-table-column prop="policyHolder" label="投保人" width="150" />
<el-table-column prop="policyHolderEn" label="投保人(英文)" width="120" />
<el-table-column prop="insured" label="受保人" width="150" />
<el-table-column prop="insuredEn" label="受保人(英文)" width="150" />
<el-table-column prop="brokerName" label="转介人" width="150" />
<el-table-column prop="createTime" label="创建时间" width="150">
<template #default="{ row }">
......@@ -244,9 +246,10 @@
:config="remittanceConfig"
v-model="remittanceFormModel"
@uploadSuccess="handleUploadSuccess"
@select-change="onSelectChange"
/>
<div
v-if="remittanceFormModel.apiPremiumRemittanceFileDtoList?.length"
v-if="remittanceFormModel.apiPremiumRemittanceFileDtoList.length > 0"
class="otherFileBox"
>
<el-table border :data="tempOtherFileList" size="small">
......@@ -260,6 +263,7 @@
</el-table-column>
<el-table-column label="操作" width="100">
<template #default="{ row, $index }">
<el-button type="primary" link @click="viewFile(row, $index)"> 查看 </el-button>
<el-button type="danger" link @click="removeOtherFile(row, $index)">
删除
</el-button>
......@@ -270,13 +274,21 @@
</div>
</el-scrollbar>
</CommonDialog>
<filePreviewDialog
v-model="previewVisible"
:file-url="currentOtherFile.fileUrl"
:file-name="currentOtherFile.fileName"
ref="filePreviewDialogRef"
/>
</div>
</template>
<script setup>
import { validateEnglish2 } from '@/utils/validate'
import { ref, reactive, watch } from 'vue'
import CommonPage from '@/components/commonPage'
import CommonDialog from '@/components/commonDialog'
import filePreviewDialog from '@/components/commonDialog/filePreviewDialog.vue'
import SearchForm from '@/components/SearchForm/SearchForm.vue'
import { ElMessage } from 'element-plus'
import { formatCurrency } from '@/utils/number'
......@@ -296,13 +308,19 @@ import {
editPremiumReconciliation,
getRemainingUnpaidAmount,
submitResult,
addSinglePremiumRemittance
addSinglePremiumRemittance,
policyDetail
} from '@/api/sign/policy'
import { getBankList } from '@/api/common'
import useUserStore from '@/store/modules/user'
import { loadDicts, getDictLabel } from '@/utils/useDict'
import { getToken } from '@/utils/auth'
import { getNowTime, formatToDate, formatToDateTime } from '@/utils/date'
const previewVisible = ref(false)
const currentFileUrl = ref('')
const currentFileName = ref('')
const currentOtherFile = ref({ fileUrl: '', fileName: '' })
const { proxy } = getCurrentInstance()
const userStore = useUserStore()
// 分页相关
......@@ -374,7 +392,7 @@ const searchConfig = ref([
{
type: 'input',
prop: 'policyHolder',
label: '投保人'
label: '投保人(中文/英文)'
},
{
type: 'select',
......@@ -384,6 +402,11 @@ const searchConfig = ref([
dictType: 'csf_ap_first_issue'
},
{
type: 'input',
prop: 'insured',
label: '受保人(中文/英文)'
},
{
type: 'select',
prop: 'policyFollowStatus',
label: '新单状态',
......@@ -408,10 +431,15 @@ const searchConfig = ref([
type: 'date',
prop: 'paymentDate',
label: '付款日期',
maxDate: 'today'
}
// {
// type: 'input',
// prop: 'eng',
// label: '英文名字'
// }
])
// 动态生成操作菜单项
const getMenuItems = row => {
const items = [{ label: '编辑', value: 'editRecord' }]
......@@ -426,6 +454,13 @@ const getMenuItems = row => {
return items
}
const viewFile = row => {
console.log('====================================')
console.log('其他附件', row)
console.log('====================================')
previewVisible.value = true
currentOtherFile.value = JSON.parse(JSON.stringify(row))
}
//获取远程搜索下拉框数据,回显数据用
const getSelectOptions = async () => {
const params4 = {
......@@ -748,13 +783,16 @@ const remittanceConfig = [
multiple: true,
limit: 10,
maxSize: 10 * 1024 * 1024, // 10MB
accept: '.png,.jpg,.jpeg,.webp,.pdf,.doc,.docx,.xls,.xlsx,.txt', // ✅ 改成扩展名!
// accept: '.png,.jpg,.jpeg,.webp,.pdf,.doc,.docx,.xls,.xlsx,.txt', // ✅ 改成扩展名!
accept: '.png,.jpg,.jpeg,.webp,.pdf', // ✅ 改成扩展名!
action: import.meta.env.VITE_APP_BASE_API + '/oss/api/oss/upload',
headers: { Authorization: 'Bearer ' + getToken() },
listType: 'text',
defaultValue: [],
span: 24,
rules: [{ required: true, message: '请上传支付凭证', trigger: 'blur' }]
rules: [{ required: true, message: '请上传支付凭证', trigger: 'blur' }],
customFileList: true, // ✅ 开启自定义列表
customUploadButtonText: '点击上传文件' // 可选,默认“点击上传文件”
},
{
type: 'upload',
......@@ -764,13 +802,15 @@ const remittanceConfig = [
multiple: true,
limit: 10,
maxSize: 10 * 1024 * 1024, // 10MB
accept: '.png,.jpg,.jpeg,.webp,.pdf,.doc,.docx,.xls,.xlsx,.txt', // ✅ 改成扩展名!
accept: '.png,.jpg,.jpeg,.webp,.pdf', // ✅ 改成扩展名!
action: import.meta.env.VITE_APP_BASE_API + '/oss/api/oss/upload',
headers: { Authorization: 'Bearer ' + getToken() },
listType: 'text',
defaultValue: [],
span: 24,
rules: [{ required: true, message: '请上传账户证明', trigger: 'blur' }]
rules: [{ required: true, message: '请上传账户证明', trigger: 'blur' }],
customFileList: true, // ✅ 开启自定义列表
customUploadButtonText: '点击上传文件' // 可选,默认“点击上传文件”
},
{
type: 'upload',
......@@ -780,7 +820,7 @@ const remittanceConfig = [
multiple: true,
limit: 10,
maxSize: 10 * 1024 * 1024, // 5MB
accept: '.png,.jpg,.jpeg,.webp,.pdf,.doc,.docx,.xls,.xlsx,.txt', // ✅ 支持多种格式
accept: '.png,.jpg,.jpeg,.webp,.pdf', // ✅ 支持多种格式
action: import.meta.env.VITE_APP_BASE_API + '/oss/api/oss/upload',
headers: { Authorization: 'Bearer ' + getToken() },
listType: 'text', // ← 按钮样式(非图片卡片)
......@@ -809,12 +849,43 @@ const handleUploadSuccess = (prop, Model) => {
}
}
// 下拉框改变
function onSelectChange(prop, value, item) {
async function onSelectChange(prop, value, item) {
console.log('【Select 变更】字段:', prop, '新值:', value, '配置:', item)
// 示例:你可以在这里做任何事
if (prop === 'policyNo') {
checkPolicyNo(value)
} else if (prop == 'paymentRel') {
if (value !== 'TBR' && value !== 'SBR') {
await nextTick() // 等待 Vue 完成本轮 DOM 更新
remittanceFormModel.value.payer = ''
}
if (value == 'TBR' || value == 'SBR') {
getPolicyInfo(currentRow.value.policyNo, 'remittance', value)
}
}
}
// 检查此保单号是否已经提交过保费对账
const getPolicyInfo = async (policyNo, type, valueType) => {
try {
const res = await policyDetail(policyNo)
console.log('姓名', res)
if (type == 'remittance') {
if (valueType == 'TBR') {
remittanceFormModel.value.payer = res.data.policyHolder
} else if (valueType == 'SBR') {
remittanceFormModel.value.payer = res.data.insured
}
} else if (type == 'affirm') {
affirmFormModel.value.effectiveDate = res.data.effectiveDate
affirmFormModel.value.underwritingDate = res.data.underwritingDate
affirmFormModel.value.status = res.data.status
affirmFormModel.value.policyExpirationDate = res.data.policyExpirationDate
affirmFormModel.value.coolingOffEndDate = res.data.coolingOffEndDate
}
} catch (error) {
console.error('加载数据失败:', error)
ElMessage.error(error.message || '加载数据失败')
}
}
// 新增汇款
......@@ -908,6 +979,7 @@ const confirmRemittance = async data => {
const formData = await remittanceFormRef.value.validate()
let newFormData = JSON.parse(JSON.stringify(formData))
// 因为汇款记录回显了保费对账的一些字段,但提交汇款记录的时候这些回显的字段是不能提交的,所以在这里做删除
for (const key in newFormData) {
if (key == deleteObjkeys[key]) delete newFormData[key]
// 文件上传的数据格式在这里统一处理
......@@ -934,6 +1006,9 @@ const confirmRemittance = async data => {
}
}
let res = {}
console.log('====================================')
console.log('newFormData', newFormData)
console.log('====================================')
if (currentRow.value.premiumReconciliationBizId && !formData.premiumRemittanceBizId) {
//编辑保费对账状态下新增汇款记录
const params = {
......@@ -986,8 +1061,11 @@ const confirmRemittance = async data => {
}
}
// 修改汇款记录
const editRemittance = row => {
const editRemittance = async row => {
let newObj = JSON.parse(JSON.stringify(row))
console.log('====================================')
console.log('修改', row)
console.log('====================================')
// 添加保费对账弹窗得form表单便于汇款回显
newObj.reconciliationType = addCheckRecordFormModel.value.reconciliationType
newObj.applicant = addCheckRecordFormModel.value.applicant
......@@ -997,28 +1075,47 @@ const editRemittance = row => {
// 上传回显得格式是[{url:''}]所以要处理一下
for (const key in newObj) {
if (
(key == 'paymentVoucherList' ||
key == 'accountVerificationList' ||
key == 'apiPremiumRemittanceFileDtoList') &&
newObj[key] &&
newObj[key].length > 0
key == 'accountVerificationDtoList' ||
(key == 'paymentVoucherDtoList' && newObj[key] && newObj[key].length > 0)
) {
console.log('key', key)
newObj[key] = newObj[key].map(item => {
return {
url: item.fileUrl,
name: item.fileName
}
})
}
if (key == 'apiPremiumRemittanceFileDtoList' && newObj[key] && newObj[key].length > 0) {
newObj[key] = newObj[key].map(item => {
if (key == 'apiPremiumRemittanceFileDtoList') {
return {
...item,
url: item.fileUrl
}
} else {
return {
url: item
}
}
// else {
// return {
// url: item.fileUrl,
// name: item.fileName
// }
// }
})
}
}
remittanceFormModel.value = newObj
console.log('remittanceFormModel.value', remittanceFormModel.value)
if (newObj.accountVerificationDtoList && newObj.accountVerificationDtoList.length > 0) {
remittanceFormModel.value.accountVerificationList = newObj.accountVerificationDtoList
}
if (newObj.paymentVoucherDtoList && newObj.paymentVoucherDtoList.length > 0) {
remittanceFormModel.value.paymentVoucherList = newObj.paymentVoucherDtoList
}
if (remittanceFormModel.value.apiPremiumRemittanceFileDtoList.length > 0) {
tempOtherFileList.value = remittanceFormModel.value.apiPremiumRemittanceFileDtoList
}
showRemittance.value = true
}
......@@ -1148,7 +1245,11 @@ const handleReset = () => {
const handleQuery = async () => {
const params = searchFormRef.value.getFormData()
console.log('params', params)
// let msg = validateEnglish2(params.eng)
// if (params.eng && msg) {
// ElMessage.error(`英文名字:${msg}`)
// return
// }
loadTableData(params)
}
const visibleDefaultButtons = ref(['add', 'reset', 'query'])
......@@ -1222,8 +1323,10 @@ const handleSelect = (command, row) => {
settingAffirmLoading.value = false
showAffirm.value = true
currentRow.value = JSON.parse(JSON.stringify(row))
getPolicyInfo(row.policyNo, 'affirm')
}
}
const addReceipts = async () => {
try {
// ✅ 正确:await validate(),成功时返回表单数据
......
<template>
<div>
<CommonPage :operationBtnList='operationBtnList' :visibleDefaultButtons="visibleDefaultButtons" v-loading.fullscreen.lock="statusLoading"
:showSearchForm='true' :show-pagination='true' :total='pageTotal' :current-page='currentPage'
:page-size='pageSize' @size-change='handleSizeChange' @current-change='handleCurrentChange'>
<CommonPage
:operationBtnList="operationBtnList"
:visibleDefaultButtons="visibleDefaultButtons"
v-loading.fullscreen.lock="statusLoading"
:showSearchForm="true"
:show-pagination="true"
:total="pageTotal"
:current-page="currentPage"
:page-size="pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<!-- 搜索区域 -->
<template #searchForm>
<SearchForm ref="searchFormRef" :config="searchConfig" />
</template>
<!-- 列表区域 -->
<template #table>
<el-table :data="tableData" height="500" border highlight-current-row style="width: 100%" v-loading="loading">
<el-table
:data="tableData"
height="500"
border
highlight-current-row
style="width: 100%"
v-loading="loading"
>
<el-table-column prop="policyNo" label="保单号" width="200" sortable />
<el-table-column prop="status" label="新单状态" width="120" sortable>
<template #default="{ row }">
......@@ -25,6 +41,10 @@
<el-table-column prop="insuranceCompany" label="保险公司" width="200" sortable />
<el-table-column prop="productName" label="产品计划" width="200" sortable />
<el-table-column prop="issueNumber" label="缴费年期" width="120" sortable />
<el-table-column prop="brokerName" label="转介人(主)" width="120" />
<el-table-column prop="eachIssuePremium" label="保费" width="120" />
<el-table-column prop="signer" label="签单员" width="120" />
<el-table-column prop="coolingOffEndDate" label="冷静期结束日" width="150" sortable />
<el-table-column fixed="right" label="操作" min-width="80">
<template #default="{ row }">
<el-popover placement="right" :width="200" trigger="click">
......@@ -34,9 +54,12 @@
</el-icon>
</template>
<el-menu @select="handleSelect($event, row)" popper-class="custom-menu">
<el-menu-item :index="item.value" v-for="item in dropdownItems" :key="item.value">{{
item.label
}}</el-menu-item>
<el-menu-item
:index="item.value"
v-for="item in dropdownItems"
:key="item.value"
>{{ item.label }}</el-menu-item
>
</el-menu>
</el-popover>
</template>
......@@ -45,14 +68,31 @@
</template>
</CommonPage>
<!-- 弹窗-->
<CommonDialog dialogTitle='修改状态' dialogWidth='80%' :openDialog=editStatusDialogFlag :showAction='true'
:showClose='true' @close='editStatusDialogFlag = false' @confirm='handleEditStatusSubmit'>
<SearchForm ref="editStatusFormRef" :config="editStatusFormConfig" v-model="editStatusFormData" />
<CommonDialog
dialogTitle="修改状态"
dialogWidth="80%"
:openDialog="editStatusDialogFlag"
:showAction="true"
:showClose="true"
@close="editStatusDialogFlag = false"
@confirm="handleEditStatusSubmit"
>
<SearchForm
ref="editStatusFormRef"
:config="editStatusFormConfig"
v-model="editStatusFormData"
/>
</CommonDialog>
<!-- 查看关联记录 -->
<CommonDialog dialogTitle='查看关联记录' dialogWidth='80%' :openDialog=viewRelatedDialogFlag :showAction='false'
:showClose='true' @close='viewRelatedDialogFlag = false'>
<CommonDialog
dialogTitle="查看关联记录"
dialogWidth="80%"
:openDialog="viewRelatedDialogFlag"
:showAction="false"
:showClose="true"
@close="viewRelatedDialogFlag = false"
>
<el-table :data="relateRecordTableData" style="width: 100%">
<el-table-column fixed prop="followDate" label="流程编号" width="150" />
<el-table-column prop="name" label="客户姓名" width="120" />
......@@ -69,21 +109,31 @@
</CommonDialog>
<!-- 查看详情、更新数据 -->
<CommonDialog :dialogTitle='mode === "viewDetail" ? "查看详情" : "更新数据"' dialogWidth='80%'
:openDialog=viewDetailDialogFlag :showAction='false' :showClose='true' @close='viewDetailDialogFlag=false'
@confirm='handleUpdateSubmit'>
<PolicyDetail v-model="policyDetailFormData" :policyBizId="selectedRow.policyBizId" :mode="mode"
ref="policyDetailFormRef" @submit="onSubmit" @cancel="viewDetailDialogFlag = false"
v-if="viewDetailDialogFlag" />
<CommonDialog
:dialogTitle="mode === 'viewDetail' ? '查看详情' : '更新数据'"
dialogWidth="80%"
:openDialog="viewDetailDialogFlag"
:showAction="false"
:showClose="true"
@close="viewDetailDialogFlag = false"
@confirm="handleUpdateSubmit"
>
<PolicyDetail
v-model="policyDetailFormData"
:policyBizId="selectedRow.policyBizId"
:mode="mode"
ref="policyDetailFormRef"
@submit="onSubmit"
@cancel="viewDetailDialogFlag = false"
v-if="viewDetailDialogFlag"
/>
</CommonDialog>
</div>
</template>
<script setup>
import { ref, reactive, computed, watch, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import CommonPage from '@/components/commonPage'
import SearchForm from '@/components/SearchForm/SearchForm.vue'
import CommonDialog from '@/components/commonDialog'
......@@ -95,9 +145,13 @@ import {
getExpectedCommissionList,
changePolicyStatus,
policyFollowReport,
saveMailingInfo, updatePolicyfollow, batchSaveBrokers, saveInitialPayment,updatePolicyProduct
saveMailingInfo,
updatePolicyfollow,
batchSaveBrokers,
saveInitialPayment,
updatePolicyProduct
} from '@/api/sign/underwritingMain'
import {copyToClipboard} from '@/utils/copyToClipboard'
import { copyToClipboard } from '@/utils/copyToClipboard'
import PolicyDetail from './policyDetail.vue'
const policyDetailFormRef = ref(null)
const policyDetailFormData = ref({})
......@@ -108,7 +162,7 @@ const pageSize = ref(10)
const pageTotal = ref(0)
const loading = ref(false)
const statusLoading = ref(false)
// 搜索表单数据
// 搜索表单数据
const searchFormData = reactive({})
const selectedRow = ref(null)
// 弹窗相关
......@@ -123,26 +177,27 @@ const editStatusFormConfig = ref([
prop: 'status',
label: '新单状态',
dictType: 'csf_policy_follow_status_new'
},
},
{
type: 'date',
prop: 'effectiveDate',
label: '保单生效日',
}, {
label: '保单生效日'
},
{
type: 'date',
prop: 'underwritingDate',
label: '保单核保日',
},
label: '保单核保日'
}
])
// 查看关联记录
const viewRelatedDialogFlag = ref(false)
const relateRecordTableData = ref([])
const viewRelatedDetail = (row) => {
const viewRelatedDetail = row => {
ElMessage.info(`查看关联记录详情:${JSON.stringify(row)}`)
}
// 提交修改状态
const handleEditStatusSubmit = async () => {
statusLoading.value = true;
statusLoading.value = true
try {
await editStatusFormRef.value.validate()
const res = await changePolicyStatus({
......@@ -150,16 +205,16 @@ const handleEditStatusSubmit = async () => {
...editStatusFormData.value
})
if (res.code === 200) {
statusLoading.value = false;
statusLoading.value = false
ElMessage.success('修改状态成功')
editStatusDialogFlag.value = false
loadTableData()
} else {
statusLoading.value = false;
statusLoading.value = false
ElMessage.error(res.msg || '修改状态失败')
}
} catch (error) {
statusLoading.value = false;
statusLoading.value = false
console.error('修改状态失败', error)
}
}
......@@ -167,7 +222,7 @@ const handleEditStatusSubmit = async () => {
// 查看详情、更新数据
const viewDetailDialogFlag = ref(false)
const onSubmit = async (data) => {
const onSubmit = async data => {
console.log('提交的数据:', data)
// 调用 API 保存
// alert('提交成功!')
......@@ -193,24 +248,26 @@ const onSubmit = async (data) => {
const res = await updatePolicyfollow(params)
if (res.code === 200) {
ElMessage.success(data.activeTab === 'basic' ? '保存基本信息成功' : '保存产品计划成功')
loadTableData()
loadTableData()
// viewDetailDialogFlag.value = false
} else {
ElMessage.error(res.msg || (data.activeTab === 'basic' ? '保存基本信息失败' : '保存产品计划失败'))
ElMessage.error(
res.msg || (data.activeTab === 'basic' ? '保存基本信息失败' : '保存产品计划失败')
)
}
} else if (data.activeTab === 'productPlan') {
params = {
policyBizId: selectedRow.value.policyBizId,
apiProductPlanMainInfoDto:{...data},
apiProductPlanMainInfoDto: { ...data },
activeTab: undefined
}
const res = await updatePolicyProduct(params)
if (res.code === 200) {
ElMessage.success('保存产品计划成功')
loadTableData()
loadTableData()
// viewDetailDialogFlag.value = false
} else {
ElMessage.error(res.msg || ('保存产品计划失败'))
ElMessage.error(res.msg || '保存产品计划失败')
}
} else if (data.activeTab === 'introducer') {
params = {
......@@ -225,8 +282,7 @@ const onSubmit = async (data) => {
} else {
ElMessage.error(res.msg || '保存转介人失败')
}
} else if (data.activeTab === 'firstPayment') {
} else if (data.activeTab === 'firstPayment') {
try {
params = {
policyBizId: selectedRow.value.policyBizId,
......@@ -244,15 +300,13 @@ const onSubmit = async (data) => {
console.error('首期缴费表单验证失败', error)
return
}
}else if(data.activeTab === 'attachment'){
viewDetailDialogFlag.value = false
} else if (data.activeTab === 'attachment') {
viewDetailDialogFlag.value = false
}
}
// 处理转介人数据格式
const normalizeIntroducerData = (data) => {
const normalizeIntroducerData = data => {
if (Array.isArray(data)) return data
if (data && typeof data === 'object') {
const keys = Object.keys(data).filter(k => /^\d+$/.test(k))
......@@ -310,11 +364,11 @@ const operationBtnList = ref([
])
// 分页事件
const handleSizeChange = (val) => {
const handleSizeChange = val => {
pageSize.value = val
loadTableData()
}
const handleCurrentChange = (val) => {
const handleCurrentChange = val => {
currentPage.value = val
loadTableData()
}
......@@ -338,7 +392,8 @@ const searchConfig = ref([
type: 'input',
prop: 'appointmentNo',
label: '预约编号'
}, {
},
{
type: 'daterange',
prop: 'signDate',
label: '签单日',
......@@ -364,10 +419,11 @@ const searchConfig = ref([
valueKey: 'insuranceCompanyBizId',
labelKey: 'fullName',
multiple: true,
transform: (res) => {
transform: res => {
return res?.data.records || []
}
}, {
},
{
type: 'select',
prop: 'productLaunchBizIdList',
label: '产品计划',
......@@ -375,28 +431,50 @@ const searchConfig = ref([
keywordField: 'productName',
requestParams: {
tenantBizId: userStore.projectInfo.tenantBizId || '',
projectBizId: userStore.projectInfo.projectBizId || '', fieldBizId: 'field_olk1qZe81qHHKXbw', fieldValueBizId: 'field_value_uOfJH5ucA2YwJpbn', pageNo: 1, pageSize: 20
projectBizId: userStore.projectInfo.projectBizId || '',
fieldBizId: 'field_olk1qZe81qHHKXbw',
fieldValueBizId: 'field_value_uOfJH5ucA2YwJpbn',
pageNo: 1,
pageSize: 20
},
placeholder: '输入产品计划名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'productLaunchBizId',
labelKey: 'productName',
multiple: true,
transform: (res) => {
transform: res => {
return res?.data.records || []
}
}, {
},
{
type: 'input',
prop: 'issueNumber',
label: '缴费年期',
inputType: 'decimal',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
},
{
type: 'input',
prop: 'policyHolder',
label: '投保人'
},
{
type: 'input',
prop: 'signer',
label: '签单员'
},
{
type: 'input',
prop: 'brokerName',
label: '转介人'
},
{
type: 'input',
prop: 'insured',
label: '受保人'
}
])
const loadTableData = async () => {
loading.value = true
searchParams.value = searchFormRef.value.getFormData() || {}
......@@ -421,13 +499,12 @@ const loadTableData = async () => {
}
}
// 表格操作菜单
const dropdownItems = [
{ label: '查看保单详情', value: 'viewDetail' },
{ label: '补充保单信息', value: 'updateData' },
{ label: '生成签单报告', value: 'generateReport' },
{ label: '设置新单状态', value: 'setNewSingleStatus' },
{ label: '设置新单状态', value: 'setNewSingleStatus' }
// { label: '查看关联', value: 'viewRelated' },
// { label: '查看记录', value: 'viewRecord' },
]
......@@ -449,15 +526,15 @@ const handleSelect = async (e, row) => {
viewDetailDialogFlag.value = true
break
case 'setNewSingleStatus':
if (selectedRow.value.status === '生效') {
if (selectedRow.value.status === '生效') {
ElMessage.warning('保单已生效,不能更新新单状态')
return
}
editStatusDialogFlag.value = true
editStatusFormData.value = {
status: row.status,
effectiveDate:row.effectiveDate,
underwritingDate:row.underwritingDate
effectiveDate: row.effectiveDate,
underwritingDate: row.underwritingDate
}
break
case 'viewRelated':
......@@ -474,7 +551,7 @@ const handleSelect = async (e, row) => {
}
}
const generateReport = async (row) => {
const generateReport = async row => {
if (!selectedRow.value) {
ElMessage.warning('请选择要生成报告的新单')
return
......@@ -483,14 +560,10 @@ const generateReport = async (row) => {
const response = await policyFollowReport(row.policyBizId)
// 文件名设置为应收款导出_yyyy-MM-dd hh:mm:ss.xlsx,不需要-,用字符串
const fileName = `签单报告_${new Date().toLocaleString().replace(/\//g, '').replace(/:/g, '').replace(/\s/g, '')}.pdf`
await safeDownload(
response,
fileName,
'application/pdf;charset=utf-8'
)
await safeDownload(response, fileName, 'application/pdf;charset=utf-8')
if (res.code === 200) {
ElMessage.success('报告生成成功')
}else{
} else {
ElMessage.error(res.msg || '报告生成失败')
}
......@@ -529,7 +602,7 @@ const saveMailingInfoapi = async () => {
mailingMethod: postalFormData.value.mailingMethod,
deliveryNo: postalFormData.value.deliveryNo,
brokerSignDate: postalFormData.value.brokerSignDate,
customerSignDate: postalFormData.value.customerSignDate,
customerSignDate: postalFormData.value.customerSignDate
}
const res = await savePostalInfo(params)
if (res.code === 200) {
......@@ -540,16 +613,8 @@ const saveMailingInfoapi = async () => {
} catch (error) {
console.error('保存邮寄信息失败', error)
} finally {
}
}
</script>
<style scoped></style>
\ No newline at end of file
<style scoped></style>
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