Commit 769c83d5 by yuzhenWang

Merge branch 'test' into 'dev'

Test

See merge request !90
parents daf22e2d cda44161
......@@ -5,7 +5,8 @@ VITE_APP_TITLE = CSF-CORE
VITE_APP_ENV = 'production'
# 若依管理系统/生产环境
VITE_APP_BASE_API = '/prod-api'
VITE_APP_BASE_API = '/api'
VITE_APP_BASE_API1 = 'https://center.supguard.cn'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip
......@@ -9,7 +9,7 @@
"dev": "vite",
"build": "vite build",
"build:dev": "vite build --mode=development",
"build:prod": "vite build",
"build:prod": "vite build --mode=production",
"build:stage": "vite build --mode staging",
"preview": "vite preview"
},
......@@ -33,6 +33,7 @@
"jsencrypt": "3.3.2",
"nprogress": "0.2.0",
"pinia": "3.0.2",
"spark-md5": "^3.0.2",
"splitpanes": "^4.0.4",
"vue": "3.5.16",
"vue-cropper": "1.1.1",
......
......@@ -197,14 +197,7 @@ export function incomeEditRecords(data) {
data: data
})
}
// 入账管理比对记录列表
export function incomeCompareRecords(data) {
return request({
url: '/csf/api/commission/compare/records',
method: 'post',
data: data
})
}
// 保单号列表
export function policyData(data) {
return request({
......
<template>
<div class="upload-container">
<h2>多文件分片上传 (Vue3 + Element Plus)</h2>
<el-card shadow="hover">
<el-upload
ref="uploadRef"
class="multi-uploader"
action="#"
:auto-upload="false"
multiple
:limit="10"
:on-change="handleFileChange"
:on-remove="handleRemove"
:file-list="fileList"
>
<template #trigger>
<el-button type="primary">选择文件</el-button>
</template>
<!-- 自定义文件列表项 -->
<template #file="{ file }">
<div class="el-upload-list__item-custom">
<div class="file-info">
<el-icon><Document /></el-icon>
<span class="file-name">{{ file.name }}</span>
<span class="file-size">{{ formatSize(file.size) }}</span>
</div>
<!-- 状态与进度区域 -->
<div class="file-action-area">
<!-- 等待上传状态 -->
<div v-if="getTaskStatus(file.uid)?.status === 'idle' || !getTaskStatus(file.uid)" class="status-wait">
<el-tag size="small">待上传</el-tag>
<el-button link type="primary" @click="startSingleUpload(file)">开始</el-button>
</div>
<!-- 进行中/暂停/合并状态 -->
<div v-else-if="['checking', 'uploading', 'merging', 'paused'].includes(getTaskStatus(file.uid)?.status)" class="status-progress">
<el-progress
:percentage="getTaskStatus(file.uid)?.progress || 0"
:status="getTaskStatus(file.uid)?.status === 'paused' ? 'exception' : undefined"
:format="(percent) => getTaskStatus(file.uid)?.message || `${percent}%`"
/>
<div class="btn-group">
<el-button
v-if="getTaskStatus(file.uid)?.status !== 'paused'"
link type="warning"
@click="handlePause(file.uid)"
>暂停</el-button>
<el-button
v-else
link type="success"
@click="handleResume(file.uid)"
>继续</el-button>
<el-button link type="danger" @click="handleCancel(file.uid)">取消</el-button>
</div>
</div>
<!-- 成功状态 -->
<div v-else-if="getTaskStatus(file.uid)?.status === 'success'" class="status-success">
<el-tag type="success" effect="plain">完成</el-tag>
<span class="success-text">{{ getTaskStatus(file.uid)?.message }}</span>
</div>
<!-- 失败状态 -->
<div v-else-if="getTaskStatus(file.uid)?.status === 'error'" class="status-error">
<el-tag type="danger" effect="plain">失败</el-tag>
<span class="error-text">{{ getTaskStatus(file.uid)?.message }}</span>
<el-button link type="primary" @click="startSingleUpload(file)">重试</el-button>
</div>
</div>
</div>
</template>
</el-upload>
<div class="global-actions" v-if="fileList.length > 0">
<el-button type="success" @click="startAllUploads" :disabled="isUploadingAny">全部开始</el-button>
<el-button type="info" @click="clearAll">清空列表</el-button>
</div>
</el-card>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { Document } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { useMultiFileUpload } from '@/composables/useMultiFileUpload';
const uploadRef = ref(null);
const fileList = ref([]); // Element Plus 管理的文件列表
// 引入我们的逻辑钩子
const { startUpload, pauseFile, resumeFile, cancelFile, getTaskStatus } = useMultiFileUpload();
// 格式化大小
const formatSize = (bytes) => {
if (!bytes) return '';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
// 当用户选择文件时
const handleFileChange = (uploadFile, uploadFiles) => {
fileList.value = uploadFiles;
// 初始化状态为 idle (实际上 useMultiFileUpload 会在 start 时初始化)
};
// 移除文件
const handleRemove = (uploadFile, uploadFiles) => {
fileList.value = uploadFiles;
cancelFile(uploadFile.uid); // 如果正在传,先取消
};
// 单个开始上传
const startSingleUpload = (file) => {
// 这里的 file 是 element-plus 的 UploadFile 对象,raw 才是原生 File 对象
if (!file.raw) return;
// 定义回调,用于强制刷新视图(虽然 uploadTasks 是响应式的,但有时 EL 的 slot 更新不及时,可加 forceUpdate 逻辑,此处依赖响应式)
const updateCallback = (uid, status, progress, msg) => {
// 如果需要更激进的 UI 更新,可以在这里操作 fileList 中的对应项
// 但通常响应式对象足够
};
startUpload(file.raw, updateCallback);
};
// 全部开始 (简单实现:遍历所有未成功的文件)
const startAllUploads = () => {
fileList.value.forEach(file => {
const status = getTaskStatus(file.uid)?.status;
if (!status || status === 'error' || status === 'idle' || status === 'paused') {
// 如果是 paused,调用 resume 逻辑可能需要特殊处理,这里简化为重新触发 start (我们的 logic 支持断点)
if (status === 'paused') {
resumeFile(file.uid);
} else {
startSingleUpload(file);
}
}
});
};
const handlePause = (uid) => pauseFile(uid);
const handleResume = (uid) => resumeFile(uid);
const handleCancel = (uid) => {
cancelFile(uid);
// 从列表中移除视觉上的文件(可选,看需求是保留记录还是直接删掉)
// fileList.value = fileList.value.filter(f => f.uid !== uid);
};
const clearAll = () => {
fileList.value = [];
// 清理内部状态逻辑略
};
const isUploadingAny = computed(() => {
return fileList.value.some(f => {
const s = getTaskStatus(f.uid)?.status;
return s === 'uploading' || s === 'checking' || s === 'merging';
});
});
</script>
<style scoped>
.upload-container {
max-width: 800px;
margin: 20px auto;
}
.el-upload-list__item-custom {
display: flex;
flex-direction: column;
padding: 10px;
border-bottom: 1px solid #ebeef5;
transition: background-color 0.2s;
}
.el-upload-list__item-custom:hover {
background-color: #f5f7fa;
}
.file-info {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.file-info .el-icon {
margin-right: 8px;
color: #409eff;
font-size: 20px;
}
.file-name {
font-weight: bold;
color: #606266;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 10px;
}
.file-size {
font-size: 12px;
color: #909399;
}
.file-action-area {
display: flex;
align-items: center;
justify-content: space-between;
}
.status-wait, .status-success, .status-error {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
}
.status-progress {
width: 100%;
display: flex;
align-items: center;
gap: 15px;
}
.status-progress .el-progress {
flex: 1;
}
.btn-group {
display: flex;
gap: 5px;
}
.global-actions {
margin-top: 20px;
text-align: right;
border-top: 1px solid #eee;
padding-top: 15px;
}
</style>
\ No newline at end of file
<template>
<el-form ref="formRef" :model="localModel" :rules="formRules" label-width="auto" v-bind="$attrs"
:validate-on-rule-change="false">
<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-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)" />
<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 => (item.inputType ? `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',') : value)
"
: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)">
<el-option v-for="opt in getSelectOptions(item)" :key="opt.value" :label="opt.label" :value="opt.value" />
<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-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)" />
<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)" />
<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="开始日期" end-placeholder="结束日期"
:value-format="item.valueFormat || 'YYYY-MM-DD'" :disabled="item.disabled"
:disabled-date="getDisabledDateFn(item)" style="width: 100%"
@change="val => handleModelChange(val, item)" />
<el-date-picker
v-else-if="item.type === 'daterange'"
v-model="localModel[item.prop]"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:value-format="item.valueFormat || 'YYYY-MM-DD'"
: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-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)" />
<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"
<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)">
: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
v-else
size="small"
type="primary"
:link="item.link"
:disabled="item.disabled"
>
{{ '点击上传文件' }}
</el-button>
<template #tip v-if="item.maxSize || item.accept">
......@@ -579,7 +663,7 @@ async function loadRemoteOptionsForInit(item) {
// ==================== 加载远程 API 选项(首次 focus 时加载,无搜索词) ====================
async function loadRemoteOptions(item) {
const { prop, api, requestParams, keywordField, debounceWait, ...rest } = item
if (!api ) return
if (!api) return
try {
remoteLoading.value[prop] = true
......@@ -660,6 +744,46 @@ function handleFilterChange(keyword, item) {
}
// ==================== 数字输入处理 ====================
// function handleNumberInput(value, item) {
// const { inputType = 'text', decimalDigits = 2, prop } = item
// if (!prop) return
// let result = String(value ?? '').trim()
// if (inputType === 'integer') {
// // 只保留数字
// result = result.replace(/[^\d]/g, '')
// } else if (inputType === 'decimal') {
// // 1. 只保留数字和小数点
// result = result.replace(/[^\d.]/g, '')
// // 2. 去掉开头的小数点(不允许 ".5" → 改为 "0.5" 更好,但这里先简单处理)
// if (result.startsWith('.')) {
// result = '0.' + result.slice(1)
// }
// // 3. 保证最多一个小数点
// const parts = result.split('.')
// if (parts.length > 2) {
// result = parts[0] + '.' + parts.slice(1).join('')
// }
// // 4. 限制小数位数(但保留结尾的小数点!)
// if (result.includes('.')) {
// const [intPart, decPart] = result.split('.')
// // 如果小数部分超过限制,截断
// if (decPart.length > decimalDigits) {
// result = intPart + '.' + decPart.slice(0, decimalDigits)
// }
// // ✅ 不再删除结尾的 '.'
// }
// }
// // 防止重复赋值(可选优化)
// if (localModel.value[prop] !== result) {
// localModel.value = { ...localModel.value, [prop]: result }
// }
// }
function handleNumberInput(value, item) {
const { inputType = 'text', decimalDigits = 2, prop } = item
if (!prop) return
......@@ -667,9 +791,52 @@ function handleNumberInput(value, item) {
let result = String(value ?? '').trim()
if (inputType === 'integer') {
// 只保留数字
result = result.replace(/[^\d]/g, '')
// 只保留数字和负号
result = result.replace(/[^-\d]/g, '')
// 如果有多个负号或者负号不在开头,则移除多余的负号
if ((result.match(/-/g) || []).length > 1) {
result = result.replace(/-/g, '').replace(/^/, '-') // 仅保留一个负号在最前面
}
} else if (inputType === 'decimalNumber') {
// 可以输入正数,负数,小数
// 1. 只保留数字、小数点和负号
result = result.replace(/[^-\d.]/g, '')
// 2. 处理负号:确保最多只有一个负号且必须在开头
if ((result.match(/-/g) || []).length > 1) {
result = result.replace(/-/g, '') // 移除所有负号
if (result.startsWith('-')) {
result = '-' + result.slice(1) // 确保负号在最前面
} else {
result = '-' + result // 如果原本没有负号但需要保留数值,可以省略此步骤
}
}
// 3. 去掉开头的小数点(不允许 ".5" → 改为 "0.5" 更好)
if (result.startsWith('.')) {
result = '0' + result
} else if (result.startsWith('-.')) {
result = '-0' + result.slice(2)
}
// 4. 保证最多一个小数点
const parts = result.split('.')
if (parts.length > 2) {
result = parts[0] + '.' + parts.slice(1).join('')
}
// 5. 限制小数位数(但保留结尾的小数点!)
if (result.includes('.')) {
const [intPart, decPart] = result.split('.')
// 如果小数部分超过限制,截断
if (decPart.length > decimalDigits) {
result = intPart + '.' + decPart.slice(0, decimalDigits)
}
// ✅ 不再删除结尾的 '.'
}
} else if (inputType === 'decimal') {
// 可以输入正整数和小数
// 1. 只保留数字和小数点
result = result.replace(/[^\d.]/g, '')
......@@ -730,7 +897,7 @@ defineExpose({
nextTick(() => formRef.value?.clearValidate())
},
// ✅ 新增:强制刷新某个字段的远程选项
async refreshRemoteOptions(targetProp) {
async refreshRemoteOptions(targetProp) {
console.log(`[SearchForm] 收到刷新请求: ${targetProp}`)
// 1. 查找配置项
......
// composables/useMultiFileUpload.js
import { ref, reactive } from 'vue';
import SparkMD5 from 'spark-md5';
const CHUNK_SIZE = 2 * 1024 * 1024; // 2MB
const FILE_CONCURRENT_LIMIT = 3; // 同时上传几个文件(宏观并发)
const CHUNK_CONCURRENT_LIMIT = 3; // 每个文件内部同时传几片(微观并发)
export function useMultiFileUpload() {
// 存储所有文件的上传任务状态
// key: file.uid (element-plus生成的唯一ID), value: taskObject
const uploadTasks = reactive({});
const calculateFileMD5 = (file) => {
return new Promise((resolve, reject) => {
const spark = new SparkMD5.ArrayBuffer();
const fileReader = new FileReader();
let currentChunk = 0;
const chunks = Math.ceil(file.size / CHUNK_SIZE);
fileReader.onload = (e) => {
spark.append(e.target.result);
currentChunk++;
if (currentChunk < chunks) {
loadNext();
} else {
resolve(spark.end());
}
};
fileReader.onerror = () => reject(new Error('文件读取失败'));
const loadNext = () => {
const start = currentChunk * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, file.size);
fileReader.readAsArrayBuffer(file.slice(start, end));
};
loadNext();
});
};
const uploadChunkWithRetry = async (formData, chunkIndex, maxRetries = 3) => {
let retryCount = 0;
while (retryCount < maxRetries) {
try {
const response = await fetch('/upload/chunk', { method: 'POST', body: formData });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (error) {
retryCount++;
if (retryCount === maxRetries) throw error;
await new Promise((r) => setTimeout(r, 1000 * retryCount));
}
}
};
// 单个文件的上传主逻辑
const processFile = async (rawFile, updateCallback) => {
const uid = rawFile.uid;
// 初始化任务状态
uploadTasks[uid] = {
status: 'checking', // checking, uploading, merging, success, error, paused
progress: 0,
message: '正在检查文件...',
cancelFlag: false,
pauseFlag: false
};
const updateState = (status, progress, message) => {
if (uploadTasks[uid]) {
uploadTasks[uid].status = status;
uploadTasks[uid].progress = progress;
uploadTasks[uid].message = message;
}
// 通知组件更新 UI
if (updateCallback) updateCallback(uid, status, progress, message);
};
try {
// 1. 计算 MD5
const fileMd5 = await calculateFileMD5(rawFile);
// 2. 检查秒传/断点
updateState('checking', 5, '正在检查服务器记录...');
const checkResult = await fetch(
`/upload/check?fileName=${encodeURIComponent(rawFile.name)}&fileMd5=${fileMd5}&fileSize=${rawFile.size}`
).then(res => res.json());
if (checkResult.uploaded) {
updateState('success', 100, '秒传成功');
return { success: true, message: '秒传' };
}
const uploadedChunks = new Set(checkResult.uploadedChunks || []);
const totalChunks = Math.ceil(rawFile.size / CHUNK_SIZE);
const initialProgress = Math.round((uploadedChunks.size / totalChunks) * 100);
updateState('uploading', initialProgress, `准备上传 (已存在 ${uploadedChunks.size}/${totalChunks} 片)`);
// 3. 准备分片队列
const pendingChunks = [];
for (let i = 0; i < totalChunks; i++) {
if (!uploadedChunks.has(i)) pendingChunks.push(i);
}
// 4. 并发上传分片
const runChunkTask = async () => {
while (pendingChunks.length > 0 && !uploadTasks[uid].cancelFlag) {
// 处理暂停
while (uploadTasks[uid].pauseFlag && !uploadTasks[uid].cancelFlag) {
await new Promise(r => setTimeout(r, 500));
}
if (uploadTasks[uid].cancelFlag) break;
const chunkIndex = pendingChunks.shift();
const start = chunkIndex * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, rawFile.size);
const chunk = rawFile.slice(start, end);
const formData = new FormData();
formData.append('file', chunk);
formData.append('chunkIndex', chunkIndex);
formData.append('totalChunks', totalChunks);
formData.append('fileName', rawFile.name);
formData.append('fileMd5', fileMd5);
await uploadChunkWithRetry(formData, chunkIndex);
// 更新进度
const currentUploaded = totalChunks - pendingChunks.length + uploadedChunks.size;
const percent = Math.min(99, Math.round((currentUploaded / totalChunks) * 100));
updateState('uploading', percent, `上传中 ${percent}%`);
}
};
// 启动微观并发
const workers = Array.from({ length: Math.min(CHUNK_CONCURRENT_LIMIT, pendingChunks.length) }, () => runChunkTask());
await Promise.all(workers);
if (uploadTasks[uid].cancelFlag) {
updateState('error', 0, '用户取消');
return { success: false, message: 'Cancelled' };
}
// 5. 合并
updateState('merging', 99, '正在合并文件...');
await fetch('/upload/merge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ fileName: rawFile.name, fileMd5: fileMd5, totalChunks })
}).then(res => res.json());
updateState('success', 100, '上传完成');
return { success: true, message: 'Success' };
} catch (error) {
if (!uploadTasks[uid].cancelFlag) {
updateState('error', 0, error.message || '上传失败');
}
return { success: false, error };
}
};
// 对外暴露的方法
const startUpload = (rawFile, updateCallback) => {
if (!rawFile) return;
// 如果该文件已经在上传,先忽略或提示
if (uploadTasks[rawFile.uid]?.status === 'uploading') return;
processFile(rawFile, updateCallback);
};
const pauseFile = (uid) => {
if (uploadTasks[uid]) uploadTasks[uid].pauseFlag = true;
};
const resumeFile = (uid) => {
if (uploadTasks[uid]) uploadTasks[uid].pauseFlag = false;
};
const cancelFile = (uid) => {
if (uploadTasks[uid]) {
uploadTasks[uid].cancelFlag = true;
uploadTasks[uid].pauseFlag = false;
uploadTasks[uid].status = 'error';
uploadTasks[uid].message = '已取消';
}
};
const getTaskStatus = (uid) => uploadTasks[uid] || null;
return {
startUpload,
pauseFile,
resumeFile,
cancelFile,
getTaskStatus,
uploadTasks // 响应式对象,可直接在模板中使用
};
}
\ No newline at end of file
......@@ -108,6 +108,7 @@ const beneficiary = [
unit: '历史客户',
unitColor: 'rgba(0, 82, 217, 1)',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 15,
disabled: false,
......@@ -124,6 +125,7 @@ const beneficiary = [
key: 'namePyEn',
domType: 'Input',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 30,
disabled: false,
......@@ -153,6 +155,7 @@ const beneficiary = [
key: 'idNumber',
domType: 'Input',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 20,
disabled: false,
......@@ -209,7 +212,10 @@ const beneficiary = [
label: '受益比例',
key: 'benefitRatio',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......
......@@ -54,7 +54,8 @@ const customer = [
prop: 'idNumber',
type: 'input',
placeholder: '请输入证件号码',
required: true
required: true,
isFormatter: false //是否需要数字分位符
},
{
label: '是否长期有效',
......@@ -91,8 +92,8 @@ const customer = [
unit: '历史客户',
unitColor: 'rgba(0, 82, 217, 1)',
inputType: 'text',
required: true,
maxLength: 15,
required: false,
maxLength: 30,
disabled: false,
placeholder: '请输入2~6位汉字',
show: true,
......@@ -108,7 +109,7 @@ const customer = [
domType: 'Input',
inputType: 'text',
required: true,
maxLength: 30,
maxLength: 100,
disabled: false,
placeholder: '请输入',
show: true,
......@@ -118,36 +119,6 @@ const customer = [
lg: 8 //栅格布局份数
},
// {
// label: '证件类型',
// key: 'documentType',
// domType: 'Select',
// required: true,
// disabled: false,
// placeholder: '请选择',
// dictType: 'csf_id_type',
// show: true,
// labelPosition: 'top', //标签的位置
// labelWidth: '120px', //标签宽度
// sm: 12, //栅格布局份数
// lg: 8 //栅格布局份数
// },
// {
// label: '证件号码',
// key: 'idNumber',
// domType: 'Input',
// inputType: 'text',
// required: true,
// maxLength: 20,
// disabled: false,
// placeholder: '请输入',
// show: true,
// labelPosition: 'top', //标签的位置
// labelWidth: '120px', //标签宽度
// sm: 12, //栅格布局份数
// lg: 8 //栅格布局份数
// },
{
label: '性别',
key: 'gender',
......@@ -179,7 +150,9 @@ const customer = [
label: '年龄',
key: 'age',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
isFormatter: false, //是否需要数字分位符
maxLength: 30,
required: true,
disabled: true,
......@@ -294,7 +267,9 @@ const customer = [
label: '退休年龄',
key: 'retirementAge',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
isFormatter: false, //是否需要数字分位符
maxLength: 30,
required: false,
disabled: false,
......@@ -324,7 +299,8 @@ const customer = [
label: '体重(KG)',
key: 'weight',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
required: false,
maxLength: 300,
disabled: false,
......@@ -339,7 +315,8 @@ const customer = [
label: '身高(CM)',
key: 'height',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
required: false,
maxLength: 300,
disabled: false,
......@@ -369,7 +346,8 @@ const customer = [
label: '受供养人数目',
key: 'dependentsNum',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'integer', //输入值的格式
required: false,
maxLength: 300,
disabled: false,
......@@ -402,13 +380,15 @@ const customer = [
label: '税务国家',
prop: 'taxCountry',
type: 'Input',
placeholder: '请输入税务国家'
placeholder: '请输入税务国家',
isFormatter: false //是否需要数字分位符
},
{
label: '税务编号',
prop: 'taxId',
type: 'Input',
placeholder: '请税务编号'
placeholder: '请税务编号',
isFormatter: false //是否需要数字分位符
}
],
data: [
......@@ -468,7 +448,8 @@ const customer = [
label: '固定电话',
key: 'landline',
domType: 'Input',
inputType: 'number',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false,
disabled: false,
placeholder: '请填写',
......@@ -484,6 +465,7 @@ const customer = [
key: 'email',
domType: 'Input',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
maxLength: 30,
required: false,
disabled: false,
......@@ -529,6 +511,8 @@ const customer = [
label: '住宅地址',
key: 'residentialAddress',
domType: 'arrowRight',
customLabel: true, //自定义label
customAddress: true,
required: false,
disabled: false,
placeholder: '请填写',
......@@ -545,6 +529,7 @@ const customer = [
key: 'mailingAddressCode',
domType: 'Input',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......@@ -586,6 +571,7 @@ const customer = [
key: 'csName',
domType: 'Input',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......@@ -601,6 +587,7 @@ const customer = [
key: 'industry',
domType: 'Input',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
maxLength: 300,
required: false,
disabled: false,
......@@ -616,7 +603,10 @@ const customer = [
key: 'currentMonthlyIncome',
domType: 'Input',
unit: 'HKD',
inputType: 'number',
inputType: 'text', //使用千位符,type为text
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......@@ -631,7 +621,9 @@ const customer = [
label: '总工作年期',
key: 'totalWorkingYears',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......@@ -646,7 +638,9 @@ const customer = [
label: '受雇于现职年期',
key: 'currentTenure',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......@@ -662,6 +656,7 @@ const customer = [
key: 'position',
domType: 'Input',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......@@ -713,8 +708,9 @@ const customer = [
key: 'companyAddressCode',
domType: 'Input',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 20,
maxLength: 50,
disabled: false,
placeholder: '请输入',
show: true,
......@@ -739,7 +735,10 @@ const customer = [
key: 'monthIncome',
domType: 'Input',
unit: 'HKD',
inputType: 'number',
inputType: 'text', //使用千位符,type为text
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......@@ -755,7 +754,10 @@ const customer = [
key: 'monthExpenditure',
domType: 'Input',
unit: 'HKD',
inputType: 'number',
inputType: 'text', //使用千位符,type为text
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......@@ -771,7 +773,10 @@ const customer = [
key: 'totalCurrentAssets',
domType: 'Input',
unit: 'HKD',
inputType: 'number',
inputType: 'text', //使用千位符,type为text
valueType: 'decimalNumber', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......@@ -787,7 +792,10 @@ const customer = [
key: 'totalDebt',
domType: 'Input',
unit: 'HKD',
inputType: 'number',
inputType: 'text', //使用千位符,type为text
valueType: 'decimalNumber', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......@@ -814,6 +822,7 @@ const customer = [
key: 'travel',
domType: 'Input',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......@@ -872,6 +881,7 @@ const customer = [
key: 'delicacy',
domType: 'Input',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......
const fnaForm = [
// 介绍人信息
{
fatherTitle: '转介人信息',
keyType: 'Array',
key: 'brokerList',
anchorKey: 'brokerList',
moudleType: 'brokerList',
dataLength: 1,
showMoudle: false, //是否展示此模块
showTable: true,
addChildren: true,
addChildrenTxt: '转介人',
fatherRequired: false,
isOpen: false,
// 新增表格列配置
columns: [
{
label: '姓名',
prop: 'brokerName',
type: 'remoteSelect',
searchType: 'brokerName',
placeholder: '请输入关键词搜索',
required: false
},
{
label: '性别',
prop: 'brokerGender',
type: 'select',
dictType: 'sys_gender',
placeholder: '请选择',
required: false
},
{
label: '内部编号',
prop: 'brokerNumber',
type: 'input',
placeholder: '请输入',
required: false
},
{
label: '所属团队',
prop: 'brokerTeam',
type: 'remoteSelect',
searchType: 'brokerTeam',
placeholder: '请输入关键词搜索',
required: false
},
{
label: '分配比例',
prop: 'brokerRatio',
type: 'inputNumber',
placeholder: '请输入',
required: true
},
{
label: '备注',
prop: 'remark',
type: 'input',
placeholder: '请输入',
required: false
}
],
data: [
// {
// fatherTitle: '转介人信息',
// keyType: 'Array',
// key: 'brokerList',
// anchorKey: 'brokerList',
// moudleType: 'brokerList',
// dataLength: 1,
// showMoudle: true,
// showTable: true,
// addChildren: true,
// addChildrenTxt: '转介人',
// fatherRequired: false,
// isOpen: false,
// // 新增表格列配置
// columns: [
// {
// label: '姓名',
// prop: 'brokerName',
// type: 'remoteSelect',
// searchType: 'brokerName',
// placeholder: '请输入关键词搜索',
// required: false
// },
// {
// label: '性别',
// prop: 'brokerGender',
// type: 'select',
// dictType: 'sys_gender',
// placeholder: '请选择',
// required: false
// },
// {
// label: '内部编号',
// prop: 'brokerNumber',
// type: 'input',
// placeholder: '请输入',
// required: false
// },
// {
// label: '所属团队',
// prop: 'brokerTeam',
// type: 'remoteSelect',
// searchType: 'brokerTeam',
// placeholder: '请输入关键词搜索',
// required: false
// },
// {
// label: '分配比例',
// prop: 'brokerRatio',
// type: 'inputNumber',
// placeholder: '请输入',
// required: true
// },
// {
// label: '备注',
// prop: 'remark',
// type: 'input',
// placeholder: '请输入',
// required: false
// brokerName: '',
// brokerGender: '',
// brokerNumber: '',
// brokerTeam: '',
// brokerRatio: '',
// remark: ''
// }
// ],
// data: [
// // {
// // brokerName: '',
// // brokerGender: '',
// // brokerNumber: '',
// // brokerTeam: '',
// // brokerRatio: '',
// // remark: ''
// // }
// ]
// },
]
},
// 受供养人信息
{
fatherTitle: '受供养人信息',
......@@ -101,7 +101,9 @@ const fnaForm = [
prop: 'dependantAge',
type: 'inputNumber',
placeholder: '请输入年龄',
required: false
required: false,
valueType: 'decimal', //输入值的格式
isFormatter: false //是否需要数字分位符
}
],
data: [
......@@ -164,7 +166,10 @@ const fnaForm = [
prop: 'sumInsured',
type: 'inputNumber',
placeholder: '请输入保额',
required: true
required: true,
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true //是否需要数字分位符
}
],
data: [
......@@ -213,7 +218,10 @@ const fnaForm = [
prop: 'marketValue',
type: 'inputNumber',
placeholder: '请输入市场价值',
required: false
required: false,
decimalDigits: 2, //小数点位数
valueType: 'decimalNumber', //输入值的格式
isFormatter: true //是否需要数字分位符
},
{
label: '币种',
......@@ -250,7 +258,10 @@ const fnaForm = [
domType: 'Input',
unit: 'HKD',
showDes: true, //是否展示描述
inputType: 'number',
inputType: 'text', //使用千位符,type为text
valueType: 'decimal', //输入值的格式
isFormatter: true, //是否需要数字分位符
decimalDigits: 2, //小数点位数
required: true,
maxLength: 300,
disabled: false,
......@@ -270,7 +281,10 @@ const fnaForm = [
domType: 'Input',
unit: 'HKD',
showDes: true, //是否展示描述
inputType: 'number',
inputType: 'text', //使用千位符,type为text
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: true,
maxLength: 300,
disabled: false,
......@@ -288,7 +302,10 @@ const fnaForm = [
value: '',
domType: 'Input',
unit: 'HKD',
inputType: 'number',
inputType: 'text', //使用千位符,type为text
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: true,
maxLength: 300,
disabled: false,
......
......@@ -41,7 +41,6 @@ const productPlan = [
key: 'insuranceTypeName',
domType: 'SearchSelect',
required: true,
maxLength: 30,
disabled: false,
placeholder: '请输入',
......@@ -69,9 +68,12 @@ const productPlan = [
label: '供款期数',
key: 'issueNumber',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 0, //小数点位数
isFormatter: true, //是否需要数字分位符
required: true,
placeholder: '请选择',
placeholder: '请输入',
dictType: 'issueNumber',
show: true,
labelPosition: 'top', //标签的位置
......@@ -83,7 +85,10 @@ const productPlan = [
label: '保障年期',
key: 'guaranteePeriod',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 0, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false,
maxLength: 20,
disabled: false,
......@@ -127,7 +132,10 @@ const productPlan = [
label: '每期保费',
key: 'eachIssuePremium',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: true,
maxLength: 20,
disabled: false,
......@@ -156,7 +164,10 @@ const productPlan = [
label: '保单征费',
key: 'policyLevy',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: true,
maxLength: 20,
disabled: false,
......@@ -185,7 +196,10 @@ const productPlan = [
label: '预缴年期',
key: 'prepaymentPeriod',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false,
maxLength: 20,
disabled: false,
......@@ -270,7 +284,10 @@ const productPlan = [
label: '保单额度(重疾)',
key: 'sumInsured',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false,
maxLength: 20,
disabled: false,
......
......@@ -57,6 +57,7 @@ const secondHolder = [
label: '名字(中文)',
key: 'nameCn',
domType: 'Input',
isFormatter: false, //是否需要数字分位符
unit: '历史客户',
unitColor: 'rgba(0, 82, 217, 1)',
inputType: 'text',
......@@ -76,6 +77,7 @@ const secondHolder = [
key: 'namePyEn',
domType: 'Input',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 30,
disabled: false,
......@@ -105,6 +107,7 @@ const secondHolder = [
key: 'idNumber',
domType: 'Input',
inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false,
maxLength: 20,
disabled: false,
......@@ -148,7 +151,10 @@ const secondHolder = [
label: '受益比例',
key: 'benefitRatio',
domType: 'Input',
inputType: 'number',
inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false,
maxLength: 300,
disabled: false,
......
......@@ -9,7 +9,7 @@ import useSettingsStore from '@/store/modules/settings' // 使用命名导入
import usePermissionStore from '@/store/modules/permission' // 使用命名导入
import { getToken, setToken, removeToken } from '@/utils/auth'
import { de } from 'element-plus/es/locales.mjs'
console.log('权限11111')
NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/register', '/workbench']
......
// 格式化金额为货币格式
export function formatCurrency(value,currency='') {
export function formatCurrency(value, currency = '',fixedDigits = 2) {
if (value === undefined || value === null) return currency + '0.00'
return currency + value.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,')
return currency + value.toFixed(fixedDigits).replace(/\d(?=(\d{3})+\.)/g, '$&,')
}
// 数字格式化
export function numberFormat(value, item) {
const { valueType = 'text', decimalDigits = 2 } = item
// if (!value) return
let result = String(value ?? '').trim()
if (valueType === 'integer') {
// 只保留数字和负号
result = result.replace(/[^-\d]/g, '')
// 如果有多个负号或者负号不在开头,则移除多余的负号
if ((result.match(/-/g) || []).length > 1) {
result = result.replace(/-/g, '').replace(/^/, '-') // 仅保留一个负号在最前面
}
} else if (valueType === 'decimalNumber') {
// 可以输入正数,负数,小数
// 1. 只保留数字、小数点和负号
result = result.replace(/[^-\d.]/g, '')
// 2. 处理负号:确保最多只有一个负号且必须在开头
if ((result.match(/-/g) || []).length > 1) {
result = result.replace(/-/g, '') // 移除所有负号
if (result.startsWith('-')) {
result = '-' + result.slice(1) // 确保负号在最前面
} else {
result = '-' + result // 如果原本没有负号但需要保留数值,可以省略此步骤
}
}
// 3. 去掉开头的小数点(不允许 ".5" → 改为 "0.5" 更好)
if (result.startsWith('.')) {
result = '0' + result
} else if (result.startsWith('-.')) {
result = '-0' + result.slice(2)
}
// 4. 保证最多一个小数点
const parts = result.split('.')
if (parts.length > 2) {
result = parts[0] + '.' + parts.slice(1).join('')
}
// 5. 限制小数位数(但保留结尾的小数点!)
if (result.includes('.')) {
const [intPart, decPart] = result.split('.')
// 如果小数部分超过限制,截断
if (decPart.length > decimalDigits) {
result = intPart + '.' + decPart.slice(0, decimalDigits)
}
// ✅ 不再删除结尾的 '.'
}
} else if (valueType === 'decimal') {
// 可以输入正整数和小数
// 1. 只保留数字和小数点
result = result.replace(/[^\d.]/g, '')
// 2. 去掉开头的小数点(不允许 ".5" → 改为 "0.5" 更好,但这里先简单处理)
if (result.startsWith('.')) {
result = '0.' + result.slice(1)
}
// 3. 保证最多一个小数点
const parts = result.split('.')
if (parts.length > 2) {
result = parts[0] + '.' + parts.slice(1).join('')
}
// 4. 限制小数位数(但保留结尾的小数点!)
if (result.includes('.')) {
const [intPart, decPart] = result.split('.')
// 如果小数部分超过限制,截断
if (decPart.length > decimalDigits) {
result = intPart + '.' + decPart.slice(0, decimalDigits)
}
}
}
return result
}
/**
* 为数字添加千位分隔符(分位符)
* @param {number|string} value - 要格式化的数字
* @param {number} decimalDigits - 小数位数,默认2位
* @param {boolean} keepNegative - 是否保留负号,默认true
* @returns {string} 格式化后的数字字符串
* @example
* formatThousands(1234.56) => '1,234.56'
* formatThousands(-1234.56) => '-1,234.56'
* formatThousands(1000) => '1,000.00'
* formatThousands('1234567.89') => '1,234,567.89'
*/
export function formatThousands(value, decimalDigits = 2, keepNegative = true) {
// 处理空值
if (value === undefined || value === null || value === '') {
return '0.00'
}
// 转换为字符串并去除已有的逗号
let numStr = String(value).replace(/,/g, '').trim()
// 如果字符串为空,返回默认值
if (numStr === '') {
return '0.00'
}
// 检查是否为负数
const isNegative = keepNegative && numStr.startsWith('-')
// 移除负号以便处理数字部分
let absNumStr = isNegative ? numStr.slice(1) : numStr
// 如果移除负号后为空,返回默认值
if (absNumStr === '') {
return isNegative ? '-0.00' : '0.00'
}
// 分割整数和小数部分
let [intPart, decPart] = absNumStr.split('.')
// 处理整数部分:如果为空,设为'0'
intPart = intPart || '0'
// 确保整数部分只包含数字
intPart = intPart.replace(/[^\d]/g, '')
if (intPart === '') {
intPart = '0'
}
// 处理小数部分
if (decPart !== undefined) {
// 只保留数字
decPart = decPart.replace(/[^\d]/g, '')
// 如果小数部分超过指定长度,截断;如果不足,补零
if (decPart.length > decimalDigits) {
decPart = decPart.slice(0, decimalDigits)
} else if (decPart.length < decimalDigits) {
decPart = decPart.padEnd(decimalDigits, '0')
}
} else {
// 如果没有小数部分,创建指定长度的零
decPart = '0'.repeat(decimalDigits)
}
// 为整数部分添加千位分隔符
intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
// 组合结果
let result = decimalDigits > 0 ? `${intPart}.${decPart}` : intPart
// 添加负号
if (isNegative && parseFloat(intPart.replace(/,/g, '')) > 0) {
result = '-' + result
}
return result
}
/**
* 格式化数字为带千位分隔符的字符串(不强制小数位数)
* @param {number|string} value - 要格式化的数字
* @returns {string} 格式化后的数字字符串
* @example
* formatThousandsSimple(1234567.89) => '1,234,567.89'
* formatThousandsSimple(-1234.5) => '-1,234.5'
*/
export function formatThousandsSimple(value) {
if (value === undefined || value === null || value === '') {
return ''
}
let numStr = String(value).replace(/,/g, '').trim()
if (numStr === '') {
return ''
}
// 检查是否为负数
const isNegative = numStr.startsWith('-')
let absNumStr = isNegative ? numStr.slice(1) : numStr
// 分割整数和小数部分
const [intPart, decPart] = absNumStr.split('.')
// 为整数部分添加千位分隔符
const formattedInt = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
// 组合结果
let result = decPart !== undefined ? `${formattedInt}.${decPart}` : formattedInt
// 添加负号
if (isNegative) {
result = '-' + result
}
return result
}
export default {
formatCurrency
formatCurrency,
numberFormat,
formatThousands,
formatThousandsSimple
}
......@@ -6,7 +6,6 @@ import { tansParams, blobValidate } from '@/utils/ruoyi'
import cache from '@/plugins/cache'
import { saveAs } from 'file-saver'
import useUserStore from '@/store/modules/user'
let downloadLoadingInstance
// 是否显示重新登录
export let isRelogin = { show: false }
......@@ -84,8 +83,10 @@ service.interceptors.request.use(
// 响应拦截器
service.interceptors.response.use(
res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
......@@ -140,6 +141,31 @@ service.interceptors.response.use(
} else if (message.includes('timeout')) {
message = '系统接口请求超时'
} else if (message.includes('Request failed with status code')) {
console.log('打印系统', message.substr(message.length - 3))
if (message.substr(message.length - 3) == '401' && !isRelogin.show) {
isRelogin.show = true
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
isRelogin.show = false
useUserStore()
.logOut()
.then(() => {
// location.href = '/index'
//token过期,跳转回中台的登录页
window.open(`${import.meta.env.VITE_APP_BASE_API1}/login?redirect=/workbench`)
// 然后立刻关闭自己
window.close()
})
})
.catch(() => {
isRelogin.show = false
})
}
message = '系统接口' + message.substr(message.length - 3) + '异常'
}
ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
......
<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,58 +20,35 @@
<!-- 统计信息卡片 v-if="statisticsData.totalInAmount > 0"-->
<div class="statistics-container">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">入账金额</div>
<div class="card-value">{{ statisticsData.totalInAmount ? formatCurrency(statisticsData.totalInAmount) : 0 }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保单数</div>
<div class="card-value">{{ statisticsData.totalPolicyCount ? statisticsData.totalPolicyCount : 0 }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保费(HKD)</div>
<div class="card-value">{{ statisticsData.totalPremium ? formatCurrency(statisticsData.totalPremium) : 0 }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">待出账金额</div>
<div class="card-value">{{ statisticsData.pendingOutAmount ? formatCurrency(statisticsData.pendingOutAmount) : 0 }}</div>
</div>
</el-card>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="待出账金额" :value="statisticsData.pendingOutAmount" :formatter="value=>formatCurrency(value)" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">可出账金额</div>
<div class="card-value">{{ statisticsData.availableOutAmount ? formatCurrency(statisticsData.availableOutAmount) : 0 }}</div>
</div>
</el-card>
<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>
<!-- <el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">差额(估)</div>
<div class="card-value">{{ statisticsData.differenceAmount ? formatCurrency(statisticsData.differenceAmount) : 0 }}</div>
</div>
</el-card>
</el-col> -->
</el-row>
</div>
<el-table :data="tableData" @selection-change="handleSelectionChange" height="400" border highlight-current-row
style="width: 100%" v-loading="loading">
<el-table
:data="tableData"
@selection-change="handleSelectionChange"
height="400"
border
highlight-current-row
style="width: 100%"
v-loading="loading"
>
<el-table-column type="selection" width="40" />
<el-table-column prop="fortuneBizType" label="应付单类型" width="120" sortable>
<template #default="{ row }">
......@@ -72,27 +57,51 @@
</el-table-column>
<el-table-column prop="policyNo" label="保单号" width="120" sortable />
<el-table-column prop="insuranceCompany" label="保险公司" width="120" sortable />
<el-table-column prop="commissionPaidAmount" label="累积已入账金额" width="120" sortable />
<el-table-column prop="commissionPaidRatio" label="累积已入账比例" width="120" sortable :formatter="(row) => `${row.commissionPaidRatio ? row.commissionPaidRatio : 0 }%`" />
<el-table-column
prop="commissionPaidAmount"
label="累积已入账金额"
width="120"
sortable
:formatter="row=>formatCurrency(row.commissionPaidAmount || 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 />
<el-table-column prop="fortuneTotalPeriod" label="总期数" width="120" sortable />
<el-table-column prop="broker" label="转介人" width="130" sortable />
<el-table-column prop="team" label="所属团队" width="120" sortable />
<el-table-column prop="exchangeRate" label="结算汇率(估)" width="140" sortable />
<el-table-column prop="hkdAmount" label="应出账金额(HKD)" width="160" sortable/>
<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 />
<el-table-column prop="fortuneUnpaidAmount" label="剩余出账金额" width="160" 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="currentPaymentAmount" label="本期出账金额" width="120" sortable/> -->
<el-table-column prop="currentPaymentHkdAmount" label="本期出账金额(HKD)" width="160" sortable/>
<el-table-column prop="fortuneUnpaidRatio" label="剩余出账比例" width="120" sortable :formatter="(row) => `${row.fortuneUnpaidRatio }%`" />
<el-table-column
prop="currentPaymentHkdAmount"
label="本期出账金额(HKD)"
width="160"
sortable
:formatter="row=>formatCurrency(row.currentPaymentHkdAmount || 0)"
/>
<el-table-column
prop="fortuneUnpaidRatio"
label="剩余出账比例"
width="120"
sortable
:formatter="row => `${row.fortuneUnpaidRatio}%`"
/>
<el-table-column prop="status" label="出账状态" width="160" sortable>
<template #default="{ row }">
{{ selectDictLabel(csf_fortune_status, row.status) }}
</template>
</el-table-column>
<el-table-column prop="premium" label="期交保费" width="120" sortable />
<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 />
......@@ -106,9 +115,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>
......@@ -116,26 +128,64 @@
</el-table>
<!-- 表格操作菜单 -->
<div class="tableOptionContainer">
<el-button type="primary" :icon="Select" :disabled="updatePayRollStatusDisable"
@click="downloadPolicyFortuneAccountapi">完成检核</el-button>
<el-button
type="primary"
:icon="Select"
:disabled="updatePayRollStatusDisable"
@click="downloadPolicyFortuneAccountapi"
>完成检核</el-button
>
</div>
</template>
</CommonPage>
<!-- 新增出账检核页面-->
<CommonDialog dialogTitle='新增出账检核' dialogWidth='80%' :openDialog='addCheckRecordFormDialogFlag' :showAction='true'
:showClose='true' @close='addCheckRecordFormDialogFlag = false' @confirm='addCheckRecordaddBatchapi'>
<SearchForm ref="addCheckRecordFormRef" :config="addCheckRecordConfig" v-model="addCheckRecordFormModel" />
<CommonDialog
dialogTitle="新增出账检核"
dialogWidth="80%"
:openDialog="addCheckRecordFormDialogFlag"
:showAction="true"
:showClose="true"
@close="addCheckRecordFormDialogFlag = false"
@confirm="addCheckRecordaddBatchapi"
>
<SearchForm
ref="addCheckRecordFormRef"
:config="addCheckRecordConfig"
v-model="addCheckRecordFormModel"
/>
</CommonDialog>
<!-- 导入出账检核页面 -->
<CommonDialog dialogTitle='新增出账检核' dialogWidth='80%' :openDialog='importCheckRecordFlag' :showAction='true'
:showClose='true' @close='importCheckRecordFlag = false'>
<FileUploadPreview :header-row="0" :required-fields="'amount,exchangeRate'" :use-chinese-header="true"
:transform-submit-data="transformToBackend" @submit="onSubmit" />
<CommonDialog
dialogTitle="新增出账检核"
dialogWidth="80%"
:openDialog="importCheckRecordFlag"
:showAction="true"
:showClose="true"
@close="importCheckRecordFlag = false"
>
<FileUploadPreview
:header-row="0"
:required-fields="'amount,exchangeRate'"
:use-chinese-header="true"
:transform-submit-data="transformToBackend"
@submit="onSubmit"
/>
</CommonDialog>
<!-- 设置本期出账金额 -->
<CommonDialog dialogTitle='设置本期出账金额' dialogWidth='80%' :openDialog='setPayoutAmountDialogFlag' :showAction='true'
:showClose='true' @close='setPayoutAmountDialogFlag = false' @confirm='updatePayoutAmountapi'>
<SearchForm ref="setPayoutAmountFormRef" :config="setPayoutAmountConfig" v-model="setPayoutAmountFormModel" />
<CommonDialog
dialogTitle="设置本期出账金额"
dialogWidth="80%"
:openDialog="setPayoutAmountDialogFlag"
:showAction="true"
:showClose="true"
@close="setPayoutAmountDialogFlag = false"
@confirm="updatePayoutAmountapi"
>
<SearchForm
ref="setPayoutAmountFormRef"
:config="setPayoutAmountConfig"
v-model="setPayoutAmountFormModel"
/>
</CommonDialog>
</div>
</template>
......@@ -149,13 +199,17 @@ import { ElMessage } from 'element-plus'
import { formatCurrency } from '@/utils/number'
import { Select } from '@element-plus/icons-vue'
// 接口
import { getPolicyFortuneList, addCheckRecordaddBatch, updatePayoutAmount, downloadPolicyFortuneAccount } from '@/api/financial/commission'
import {
getPolicyFortuneList,
addCheckRecordaddBatch,
updatePayoutAmount,
downloadPolicyFortuneAccount
} from '@/api/financial/commission'
import useUserStore from '@/store/modules/user'
const { proxy } = getCurrentInstance()
const { csf_fortune_status } = proxy.useDict('csf_fortune_status')
const userStore = useUserStore()
// 分页相关
const currentPage = ref(1)
......@@ -177,7 +231,8 @@ const searchConfig = ref([
label: '出账状态',
multiple: true,
dictType: 'csf_fortune_status'
}, {
},
{
type: 'select',
prop: 'insuranceCompanyBizIdList',
label: '保险公司',
......@@ -189,11 +244,12 @@ const searchConfig = ref([
multiple: true,
valueKey: 'insuranceCompanyBizId',
labelKey: 'fullName',
transform: (res) => {
transform: res => {
console.log(res)
return res?.data.records || []
}
}, {
},
{
type: 'select',
prop: 'productLaunchBizIdList',
label: '产品计划',
......@@ -201,17 +257,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, // 自定义防抖时间
multiple: true,
valueKey: 'productLaunchBizId',
labelKey: 'productName',
transform: (res) => {
transform: res => {
return res?.data.records || []
}
}, {
},
{
type: 'daterange',
prop: 'payoutDate',
label: '出账日(估)',
......@@ -224,11 +285,11 @@ const searchConfig = ref([
label: '入账状态',
multiple: true,
dictType: 'csf_expected_commission_status'
},
}
])
// 表格操作菜单
const dropdownItems = [
{ label: '设置本期出账金额', value: 'setPayRoll' },
{ label: '设置本期出账金额', value: 'setPayRoll' }
// { label: '更新', value: 'editRecord' },
// { label: '查看记录', value: 'viewRecord' }
]
......@@ -238,7 +299,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 || ''
}
......@@ -252,59 +313,63 @@ const addCheckRecordConfig = [
prop: 'fortuneBizType',
label: '应付单类型',
options: fortuneBizTypeOptions
}, {
},
{
type: 'select',
prop: 'status',
label: '出账状态',
dictType: 'csf_expected_fortune_status'
}, {
},
{
type: 'input',
prop: 'policyNo',
label: '关联保单号',
visible: (formData) => formData.fortuneBizType === 'R',
}, {
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: 'actualPayoutDate',
label: '出账日(实)',
placeholder: '请选择',
maxDate: 'today'
}, {
},
{
type: 'input',
prop: 'amount',
label: '出账金额',
inputType: 'decimal',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
}, {
inputType: 'decimalNumber',
decimalDigits: 4,
rules: [{ pattern: /^-?\d+(\.\d+)?$/, message: '请输入有效的数字', trigger: 'blur' }]
},
{
type: 'select',
prop: 'currency',
label: '出账币种',
dictType: 'bx_currency_type'
}, {
},
{
type: 'select',
prop: 'fortuneType',
label: '出账项目',
dictType: 'csf_fortune_type'
}, {
},
{
type: 'select',
prop: 'brokerBizId',
label: '转介人',
......@@ -316,16 +381,15 @@ const addCheckRecordConfig = [
valueKey: 'userSaleBizId',
labelKey: 'realName',
onChangeExtraFields: {
broker: 'realName',// 自动同步 raw.name 到 reconciliationCompany
broker: 'realName', // 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code'
},
transform: (res) => {
transform: res => {
return res?.data.records || []
}
}
]
// 设置本期出账金额
const setPayoutAmountFormModel = ref({})
const setPayoutAmountFormRef = ref(null)
......@@ -336,9 +400,7 @@ const setPayoutAmountConfig = [
prop: 'currentPaymentHkdAmount',
label: '本期出账金额(HKD)',
inputType: 'decimal',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
},
// {
// type: 'select',
......@@ -351,7 +413,7 @@ const setPayoutAmountConfig = [
prop: 'status',
label: '出账状态',
dictType: 'csf_expected_fortune_status'
},
}
// {
// type: 'input',
// prop: 'exchangeRate',
......@@ -360,9 +422,6 @@ const setPayoutAmountConfig = [
// }
]
// 统计信息
const statisticsData = ref({})
// 弹窗相关
......@@ -440,11 +499,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()
}
......@@ -457,14 +516,11 @@ const handleSelect = (e, row) => {
if (e === 'setPayRoll') {
setPayoutAmountDialogFlag.value = true
}
}
const addCheckRecordaddBatchapi = async (data) => {
const addCheckRecordaddBatchapi = async data => {
const formData = addCheckRecordFormRef.value.getFormData()
console.log('新增出账检核记录:', formData)
const params = [
{ ...formData }
]
const params = [{ ...formData }]
try {
const res = await addCheckRecordaddBatch(params)
if (res.code === 200) {
......@@ -482,7 +538,7 @@ const addCheckRecordaddBatchapi = async (data) => {
}
// 设置本期出账金额
const updatePayoutAmountapi = async (data) => {
const updatePayoutAmountapi = async data => {
console.log('设置本期出账金额:', data)
const params = {
...setPayoutAmountFormRef.value.getFormData(),
......@@ -504,14 +560,14 @@ const updatePayoutAmountapi = async (data) => {
}
const updatePayRollStatusDisable = ref(true)
const multipleSelection = ref([])
const handleSelectionChange = (val) => {
const handleSelectionChange = val => {
multipleSelection.value = val
console.log('全选:', val)
// 完成检核按钮是否禁用
updatePayRollStatusDisable.value = val.length === 0
}
const downloadPolicyFortuneAccountapi = async (data) => {
const downloadPolicyFortuneAccountapi = async data => {
console.log('生成出账清单:', data)
try {
const res = await downloadPolicyFortuneAccount({
......@@ -530,7 +586,7 @@ import FileUploadPreview from '@/components/fileUploadPreview/fileUploadPreview.
const importCheckRecordFlag = ref(false)
// 如果后端接收的就是当前格式,可直接透传
const formatForBackend = (rows) => {
const formatForBackend = rows => {
return rows.map(row => ({
...row,
amount: Number(row.amount) || 0,
......@@ -538,15 +594,12 @@ const formatForBackend = (rows) => {
}))
}
const onSubmit = (data) => {
const onSubmit = data => {
console.log('提交给后端的数据:', data)
// 调用保存 API
}
onMounted(async () => {
})
onMounted(async () => {})
</script>
<style scoped>
......
......@@ -12,80 +12,43 @@
<!-- 统计信息卡片 -->
<div class="statistics-container" v-if="statisticsData.totalPolicyCount > 0">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保单数</div>
<div class="card-value">{{ statisticsData.totalPolicyCount }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保费(HKD)</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalPremium) }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">对账公司数</div>
<div class="card-value">{{ statisticsData.reconciliationCompanyCount }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">比对记录总条数</div>
<div class="card-value">{{ statisticsData.totalCompareCommissionCount }}</div>
</div>
</el-card>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="比对记录总条数" :value="statisticsData.totalCompareCommissionCount" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">比对成功数</div>
<div class="card-value">{{ statisticsData.successCompareCommissionCount }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">比对失败数</div>
<div class="card-value">{{ statisticsData.failedCompareCommissionCount }}</div>
</div>
</el-card>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="比对失败数" :value="statisticsData.failedCompareCommissionCount" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">入账金额(实)</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalPaidAmount) }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">入账金额(估)</div>
<div class="card-value">{{ formatCurrency(statisticsData.expectePaidAmount) }}</div>
</div>
</el-card>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="入账金额(估)" :value="statisticsData.expectePaidAmount" :formatter="value=>formatCurrency(value)" />
</el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">差额(估)</div>
<div class="card-value">{{ formatCurrency(statisticsData.differenceAmount) }}</div>
</div>
</el-card>
<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="4">
<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-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-column type="selection" width="40" :selectable="selectableFn" />
......@@ -108,9 +71,9 @@
{{ 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="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 />
......@@ -310,12 +273,14 @@ const downloadTemplate = () => {
link.download = '入账检核导入模板.xlsx' // 设置下载文件名
link.click() // 自动触发点击,无需 append 到 DOM(现代浏览器支持)
}
const formatRatio = (row, column, cellValue, index) => {
if (cellValue == null || cellValue == '' || cellValue == 0) {
if (cellValue == null || cellValue == '' && cellValue !== 0) {
return '-'
}
return cellValue + '%'
}
const formatCurrencyUtil = (row, column, cellValue, index) => {
if (cellValue == null || cellValue == '' || cellValue == 0) {
return '-'
......
......@@ -97,7 +97,7 @@ const searchConfig = ref([
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入转介人名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'userSaleBizId',
valueKey: 'clientUserBizId',
labelKey: 'realName',
multiple: true,
transform: (res) => {
......
......@@ -12,53 +12,23 @@
<!-- 统计信息卡片 -->
<div class="statistics-container" v-if="statisticsData.totalPolicyCount > 0">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">应出账总金额</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalExpectedAmount) }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">已出账金额</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalPaidAmount) }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">待出账金额</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalUnpaidAmount) }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">已出账比例</div>
<div class="card-value">{{ statisticsData.paidAmountRatio }}%</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保单数</div>
<div class="card-value">{{ statisticsData.totalPolicyCount }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保费</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalPremiumAmount) }}</div>
</div>
</el-card>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic title="总保费" :value="statisticsData.totalPremiumAmount" :formatter="value=>formatCurrency(value)" />
</el-col>
</el-row>
</div>
......@@ -78,56 +48,26 @@
</CommonPage>
<!-- 查看明细列表 -->
<CommonDialog dialogTitle="应付明细" dialogWidth="80%" :openDialog="detailDialogVisible" :showAction="true"
:showClose="true" @close="detailDialogVisible = false">
: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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">应出账总金额</div>
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalExpectedAmount) }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">已出账金额</div>
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalPaidAmount) }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">待出账金额</div>
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalUnpaidAmount) }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">已出账比例</div>
<div class="card-value">{{ detailRecordStatistics.paidAmountRatio }}%</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保单数</div>
<div class="card-value">{{ detailRecordStatistics.totalPolicyCount }}</div>
</div>
</el-card>
<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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保费</div>
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalPremiumAmount) }}</div>
</div>
</el-card>
<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>
......@@ -158,7 +98,7 @@
</CommonDialog>
<!-- 出账记录表格弹窗-->
<CommonDialog dialogTitle="出账记录" dialogWidth="80%" :openDialog="payRecordDialogTableVisible" :showAction="true"
:showClose="true" @close="payRecordDialogTableVisible = false">
: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" />
......@@ -306,13 +246,13 @@ const searchConfig = ref([
},
{
type: 'select',
prop: 'fortuneName',
prop: 'fortuneType',
label: '出账项目',
dictType: 'csf_fortune_type'
},
{
type: 'select',
prop: 'insurerBizId',
prop: 'insuranceCompanyBizIdList',
label: '保险公司',
api: '/insurance/base/api/insuranceCompany/page',
keywordField: 'queryContent',
......@@ -321,13 +261,14 @@ const searchConfig = ref([
debounceWait: 500, // 自定义防抖时间
valueKey: 'insuranceCompanyBizId',
labelKey: 'fullName',
multiple: true,
transform: (res) => {
console.log(res)
return res?.data.records || []
}
}, {
type: 'select',
prop: 'productLaunchBizId',
prop: 'productLaunchBizIdList',
label: '产品计划',
api: '/product/api/relProjectProductLaunch/parameter/page',
keywordField: 'productName',
......@@ -339,6 +280,7 @@ const searchConfig = ref([
debounceWait: 500, // 自定义防抖时间
valueKey: 'productLaunchBizId',
labelKey: 'productName',
multiple: true,
transform: (res) => {
return res?.data.records || []
}
......@@ -352,13 +294,14 @@ const searchConfig = ref([
]
}, {
type: 'select',
prop: 'teamBizId',
prop: 'teamBizIdList',
label: '出单团队',
api: '/csf/api/team/page',
keywordField: 'teamName',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入出单团队名称搜索',
debounceWait: 500, // 自定义防抖时间
multiple: true,
valueKey: 'teamBizId',
labelKey: 'teamName',
transform: (res) => {
......@@ -509,7 +452,7 @@ const handleSelect = async (e, row) => {
payRecordDialogTableColumns.value = [
{ property: 'broker', label: '转介人', width: '100', },
{ property: 'fortuneName', label: '出账项目', width: '150' },
{ property: 'currentPaymentAmount', label: '出账金额', width: '150' },
{ 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}%` },
......
......@@ -13,16 +13,40 @@
<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.totalAmount" />
<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 title="已入账金额" :value="statisticsData.totalPaidAmount" />
<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 title="待入账金额(估)" :value="statisticsData.pendingPaidAmount" />
<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 title="已入账比例" :value="statisticsData.paidAmountRatio" />
<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" />
......@@ -50,57 +74,59 @@
: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" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">应出账总金额</div>
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalExpectedAmount) }}
</div>
<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>
</el-card>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">已出账金额</div>
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalPaidAmount) }}
</div>
<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>
</el-card>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">待出账金额</div>
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalUnpaidAmount) }}
</div>
<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>
</el-card>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">已出账比例</div>
<div class="card-value">{{ detailRecordStatistics.paidAmountRatio }}%</div>
<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>
</el-card>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保单数</div>
<div class="card-value">{{ detailRecordStatistics.totalPolicyCount }}</div>
<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>
</el-card>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保费</div>
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalPremiumAmount) }}
</div>
<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>
</el-card>
</template>
</el-statistic>
</el-col>
</el-row>
</div>
......@@ -133,10 +159,10 @@
: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" />
:prop="item.property" :label="item.label" :width="item.width" :formatter="item.formatter" />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default>
<el-button link type="primary" size="small" @click="handleClick">
<template #default="scope">
<el-button link type="primary" size="small" @click="handleClick(scope.row)">
查看比对记录
</el-button>
</template>
......@@ -148,7 +174,7 @@
: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" />
:prop="item.property" :label="item.label" :width="item.width"/>
</el-table>
</CommonDialog>
......@@ -642,10 +668,10 @@ const handleSelect = async (e, row) => {
{ property: 'totalPeriod', label: '总期数', width: '150' },
{ property: 'exchangeRate', label: '结算汇率(实)', width: '150' },
{ property: 'currency', label: '入账币种', width: '150' },
{ property: 'amount', label: '入账金额', width: '150' },
{ property: 'currentCommissionRatio', 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: 'commissionStatus', label: '入账状态', width: '150' }
{ property: 'commissionStatusName', label: '入账状态', width: '150' }
]
// 加载真实数据
const records = await loadEntryRecordData(row.commissionExpectedBizId)
......@@ -681,7 +707,7 @@ const handleSelect = async (e, row) => {
// 查看比对记录
const handleClick = async () => {
const handleClick = async (row) => {
actionRecordsDialogVisible.value = true
actionRecordsDialogTableColumns.value = [
{ property: 'checkMonth', label: '检核年月', width: '100' },
......@@ -693,7 +719,7 @@ const handleClick = async () => {
{ property: 'userName', label: '比对人', width: '150' }
]
// 加载真实数据
const records = await loadEntryEditRecordData(selectedRow.value.commissionExpectedBizId)
const records = await loadEntryEditRecordData(row.commissionBizId)
actionRecordsDialogTableData.value = records.length ? records : []
}
......@@ -764,7 +790,7 @@ const receivableReportTableColumns = ref([
{ 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) => row.premium || '-' },
{ 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([
......@@ -822,7 +848,8 @@ const receivedFortuneListData = async () => {
totalPaidAmount: response.data.expectedStatisticsVO.totalPaidAmount,
pendingPaidAmount: response.data.expectedStatisticsVO.pendingPaidAmount,
paidAmountRatio: response.data.expectedStatisticsVO.paidAmountRatio,
totalPolicyCount: response.data.expectedStatisticsVO.totalPolicyCount
totalPolicyCount: response.data.expectedStatisticsVO.totalPolicyCount,
totalPremium: response.data.expectedStatisticsVO.totalPremium
}
} catch (error) {
console.error('加载数据失败:', error)
......
......@@ -35,27 +35,18 @@
<el-col :sm="child.sm" :lg="child.lg" class="formItem" v-if="child.show">
<div>
<el-form-item
:label="
child.key === 'residentialAddress' && child.domType === 'arrowRight'
? ''
: child.label
"
:label="child.customLabel ? '' : child.label"
:prop="child.key"
:key="child.key"
:label-width="child.labelWidth"
:label-position="child.labelPosition"
>
<!-- 自定义 label 插槽 -->
<template
v-if="
child.key === 'residentialAddress' &&
child.domType === 'arrowRight'
"
#label
>
<template v-if="child.customLabel" #label>
<div class="custom-label">
<span>{{ child.label }}</span>
<el-checkbox-group
v-if="child.customAddress"
v-model="form.isCopyAddress"
size="small"
style="margin-left: 8px"
......@@ -70,10 +61,17 @@
</template>
<el-input
v-if="child.domType === 'Input'"
:type="child.inputType"
v-model="form[child.key]"
:type="child.inputType"
:placeholder="child.placeholder"
maxlength="30"
:maxlength="child.maxlength"
:formatter="
value =>
child.isFormatter
? `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
: value
"
:parser="value => value.replace(/\$\s?|(,*)/g, '')"
:disabled="
child.key == 'age' ||
child.key == 'bmi' ||
......@@ -82,6 +80,7 @@
: editStatus
"
@blur="handleInputBlur(child)"
@input="val => handleInputChange(val, child)"
>
<template #append v-if="child.unit">
<span
......@@ -231,30 +230,6 @@
style="width: calc(50% - 10px)"
/>
</div>
<!-- <el-date-picker
v-else-if="column.type === 'datePicker'"
v-model="scope.row[column.prop]"
:type="getDatePickerType(scope.row, column)"
:placeholder="column.placeholder"
:disabled="editStatus"
unlink-panels
style="width: 100%"
range-separator="至"
start-placeholder="开始"
end-placeholder="结束"
@change="changeDatePicker(father, scope.row, column.prop, $event)"
/> -->
<el-input
v-else-if="column.type === 'inputNumber'"
v-model.number="scope.row[column.prop]"
:placeholder="column.placeholder"
:disabled="editStatus"
type="number"
>
<template v-if="column.unit" #append>
<span>{{ column.unit }}</span>
</template>
</el-input>
<el-radio-group
v-model="scope.row[column.prop]"
v-else-if="column.type === 'radioGroup'"
......@@ -273,6 +248,13 @@
:placeholder="column.placeholder"
:disabled="editStatus"
@blur="tableInputBlur(father, scope.row, column.prop, $event)"
:formatter="
value =>
column.isFormatter
? `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
: value
"
:parser="value => value.replace(/\$\s?|(,*)/g, '')"
/>
<!-- 满足一定条件展示得控件 -->
</template>
......@@ -442,6 +424,7 @@ import CommonForm from '@/views/components/commonForm'
import CommonDialog from '@/components/commonDialog'
import CommonPage from '@/components/commonPage'
import CardOne from '@/components/formCard/cardOne'
import { numberFormat } from '@/utils/number'
import { watch } from 'vue'
import {
addCustomer,
......@@ -485,7 +468,7 @@ const tableLoading = ref(false)
const tableData = ref([])
const total = ref(0)
//计算默认显示的日期(18年前的今天)
const defaultDisplayDate = ref(dayjs().subtract(19, 'year').toDate())
const defaultDisplayDate = ref()
// 地址组件菜单数据
const addressMenuList = ref([
......@@ -640,6 +623,11 @@ const handleAppendInput = child => {
customerList()
}
}
//处理数据
const handleInputChange = (val, child) => {
let newVal = numberFormat(val, child)
form.value[child.key] = newVal
}
const handleInputBlur = child => {
if (
(child.key == 'weight' || child.key == 'height') &&
......@@ -773,13 +761,11 @@ const handleDateChange = child => {
}
}
const disabledDate = (time, child) => {
if (child.key == 'birthday') {
if (child.key == 'birthday' && props.activeName !== 'insurantInfo') {
// 计算18年前的今天
const eighteenYearsAgo = dayjs().subtract(19, 'year')
// 禁用今天之后的日期和18年前的今天之后的日期
return time.getTime() > Date.now() || time.getTime() > eighteenYearsAgo.valueOf()
} else {
return time.getTime() > Date.now()
}
}
const exportInfo = () => {
......@@ -1606,6 +1592,9 @@ watch(
(newVal, oldVal) => {
editStatus.value = false
customerRightRef.value = null
if (newVal === 'customer' || newVal === 'policyholder') {
defaultDisplayDate.value = dayjs().subtract(19, 'year').toDate()
}
if (newVal === 'customer') {
processFormData()
// if (JSON.stringify(customerForm.value) != '{}') {
......@@ -1634,6 +1623,7 @@ watch(
processFormData()
}
} else if (newVal === 'insurantInfo') {
defaultDisplayDate.value = dayjs().startOf('day')
editStatus.value = props.fatherEditStatus
if (JSON.stringify(insuredForm.value) != '{}') {
form.value = JSON.parse(JSON.stringify(insuredForm.value))
......@@ -1718,6 +1708,12 @@ defineExpose({
width: 100%;
display: flex;
align-items: center;
.numberTxt {
font-size: 14px;
font-weight: normal;
margin-left: 5px;
color: #a8abb2;
}
}
.topBtn {
width: 100%;
......
......@@ -84,8 +84,15 @@
:type="child.inputType"
v-model="form[father.key][child.key]"
:placeholder="child.placeholder"
maxlength="30"
:maxlength="child.maxlength"
:disabled="editStatus"
:formatter="
value =>
child.isFormatter
? `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
: value
"
:parser="value => value.replace(/\$\s?|(,*)/g, '')"
>
<template #append v-if="child.unit">
<span>{{ child.unit }}</span>
......@@ -249,32 +256,43 @@
v-else-if="
column.type === 'inputNumber' && !!scope.row.showSumInsured
"
v-model.number="scope.row[column.prop]"
v-model="scope.row[column.prop]"
:placeholder="column.placeholder"
:disabled="editStatus"
type="number"
@input="val => (scope.row[column.prop] = numberFormat(val, column))"
:formatter="value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')"
:parser="value => value.replace(/\$\s?|(,*)/g, '')"
>
<template v-if="column.unit" #append>
<span>{{ column.unit }}</span>
</template>
</el-input>
<el-input
<!-- <el-input
v-else-if="column.type === 'inputNumber' && scope.row.showSumInsured"
v-model.number="scope.row[column.prop]"
v-model="scope.row[column.prop]"
:placeholder="column.placeholder"
:disabled="editStatus"
type="number"
:formatter="value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')"
:parser="value => value.replace(/\$\s?|(,*)/g, '')"
>
<template v-if="column.unit" #append>
<span>{{ column.unit }}</span>
</template>
</el-input>
</el-input> -->
<!-- 普通输入框(默认) -->
<el-input
v-else-if="column.prop !== 'sumInsured'"
v-model="scope.row[column.prop]"
:placeholder="column.placeholder"
:disabled="editStatus"
@input="val => (scope.row[column.prop] = numberFormat(val, column))"
:formatter="
value =>
column.isFormatter
? `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
: value
"
:parser="value => value.replace(/\$\s?|(,*)/g, '')"
/>
</template>
</el-table-column>
......@@ -346,6 +364,7 @@ import useDictStore from '@/store/modules/dict'
import fanFormDomData from '@/formJson/fnaForm'
import { watch, nextTick } from 'vue'
import { addfanForm, getfanFormDetail, editFanForm, getCustomerList } from '@/api/sign/fna'
import { numberFormat } from '@/utils/number'
import {
getUserSaleExpandList,
getAllTeam,
......@@ -399,6 +418,7 @@ const {
tempFanFormValue,
tempFanFormData
} = toRefs(data)
const handleRemoteSelectChange = async (row, column, father) => {
console.log(row, column)
// 通过保险公司查保险种类 row中收集了insurer的值
......@@ -803,15 +823,7 @@ const getInvalidFields = fields => {
}
return errors
}
// 判断是否为数组
const isArray = value => {
return Array.isArray(value)
}
// 判断是否为对象
const isObject = value => {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
// 表单提交
const submitForm = saveType => {
......
......@@ -36,6 +36,7 @@
type="warning"
:icon="Edit"
@click="handleCancel('appoint')"
:disabled="submitLoading"
>取消预约</el-button
>
</div>
......@@ -48,14 +49,21 @@
>编辑</el-button
>
<el-button type="primary" v-if="!editStatus" @click="handleSubmit('editStorage')"
<!-- <el-button
type="primary"
v-if="!editStatus"
@click="handleSubmit('editStorage')"
:disabled="submitLoading"
:loading="submitLoading"
>暂存</el-button
>
> -->
<el-button
v-if="!editStatus"
type="success"
:icon="Check"
@click="handleSubmit('submit')"
:disabled="submitLoading"
:loading="submitLoading"
>提交</el-button
>
<el-button
......@@ -67,6 +75,7 @@
type="warning"
:icon="Edit"
@click="handleCancel('apply')"
:disabled="submitLoading"
>取消申请</el-button
>
</div>
......@@ -107,15 +116,19 @@
type="success"
:icon="Check"
@click="handleSubmit('submit')"
:disabled="submitLoading"
:loading="submitLoading"
>提交</el-button
>
<el-button
<!-- <el-button
type="primary"
plain
v-if="!idsObj.appointmentBizId && source == 'fnaList'"
@click="handleSubmit('storage')"
:disabled="submitLoading"
:loading="submitLoading"
>暂存</el-button
>
> -->
<el-button
style="margin-left: 10px"
v-if="idsObj.appointmentBizId"
......@@ -445,7 +458,7 @@ const props = defineProps({
policyDetailInfo: { type: Object, default: () => ({}) }, //新单跟进传递关于保单的详情信息
policyId: { type: Object, default: () => ({}) } //新单跟进传递的Id
})
const submitLoading = ref(false)
const userStore = useUserStore()
const dictStore = useDictStore()
const showStorage = ref(false)
......@@ -960,6 +973,7 @@ const handleSubmit = async type => {
return
}
}
// 获取投保人form
if (policyHolderInfoRef.value) {
submitAppointmentObj.value.apiPolicyholderInfoDto =
......@@ -1045,15 +1059,19 @@ const handleSubmit = async type => {
console.log('大提交', submitAppointmentObj.value)
// return
if (type == 'submit') {
submitLoading.value = true
// 新增预约单
addAppointment(submitAppointmentObj.value).then(res => {
if (res.code == 200) {
submitLoading.value = false
proxy.$message.success('预约成功')
editStatus.value = false
emit('handleSuccess', {
tab: 'appointment',
type: 'add'
})
} else {
submitLoading.value = false
}
})
}
......@@ -1064,12 +1082,16 @@ const handleSubmit = async type => {
if (route.query.appointmentType == '2') {
submitAppointmentObj.value.source = 2
}
submitLoading.value = true
// 编辑预约单
editAppointmentDetail(submitAppointmentObj.value).then(res => {
if (res.code == 200) {
submitLoading.value = false
editStatus.value = true
getAppointmentInfo(idsObj.value.appointmentBizId)
proxy.$message.success('修改预约单成功')
} else {
submitLoading.value = false
}
})
}
......@@ -1079,22 +1101,30 @@ const handleSubmit = async type => {
props.processDetail.customerBizId
submitAppointmentObj.value.apiAppointmentInfoDto.fnaBizId = props.processDetail.fnaBizId
submitAppointmentObj.value.apiAppointmentInfoDto.fnaNo = props.processDetail.fnaNo
submitLoading.value = true
// 暂存预约单
storageAppointment(submitAppointmentObj.value).then(res => {
if (res.code == 200) {
submitLoading.value = false
proxy.$message.success('预约暂存成功')
router.push({ path: '/sign/appointment' })
} else {
submitLoading.value = false
}
})
}
// 编辑状态下预约暂存
if (type == 'editStorage') {
submitLoading.value = true
// 暂存预约单
appointmentEditStorage(submitAppointmentObj.value).then(res => {
if (res.code == 200) {
submitLoading.value = false
proxy.$message.success('预约暂存提交成功')
editStatus.value = true
getAppointmentInfo(idsObj.value.appointmentBizId)
} else {
submitLoading.value = false
}
})
}
......
......@@ -73,7 +73,7 @@
:type="child.inputType"
v-model="form[child.key]"
:placeholder="child.placeholder"
maxlength="30"
:maxlength="child.maxlength || 50"
:rows="child.rows"
:disabled="editStatus"
/>
......@@ -964,7 +964,7 @@ const searchSelectList = async (query, key) => {
const params5 = {
pageNo: 1,
pageSize: 10,
queryContent: queryString
realName: queryString
}
const response5 = await getUserSaleExpandList(params5)
if (response5.code == 200) {
......@@ -998,7 +998,7 @@ const searchSelectList = async (query, key) => {
const params5 = {
pageNo: 1,
pageSize: 10,
realName: queryString
name: queryString
}
const response5 = await insuranceReconciliationCompany(params5)
if (response5.code == 200) {
......
......@@ -28,7 +28,7 @@
:type="child.inputType"
v-model="form[child.key]"
:placeholder="child.placeholder"
maxlength="30"
:maxlength="child.maxlength || 50"
:disabled="editStatus"
:style="{ width: child.inputWidth ? child.inputWidth : '100%' }"
/>
......@@ -177,8 +177,16 @@
:type="child.inputType"
v-model="dialogForm[child.key]"
:placeholder="child.placeholder"
maxlength="30"
@blur="handleInputBlur(child)"
@input="val => (dialogForm[child.key] = numberFormat(val, child))"
:maxlength="child.maxlength"
:formatter="
value =>
child.isFormatter
? `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
: value
"
:parser="value => value.replace(/\$\s?|(,*)/g, '')"
/>
<el-select
......@@ -257,10 +265,8 @@ import Phone from '@/views/components/phone'
import Address from '@/views/components/address'
import { watch, nextTick } from 'vue'
import { updatePolicyBeneficiary, delPolicyBeneficiary } from '@/api/sign/underwritingMain'
import { editBeneficiaryInfo, delBeneficiary } from '@/api/sign/appointment'
import useDictStore from '@/store/modules/dict'
import { numberFormat } from '@/utils/number'
const dictStore = useDictStore() //获取字典数据
const props = defineProps({
activeName: { type: String, default: '' }, //tab名称
......@@ -392,6 +398,7 @@ const {
tempBeneficiaryDomData,
dialogForm
} = toRefs(data)
const deleteChildren = (father, index) => {
if (father.key == 'apiBeneficiaryInfoDtoList') {
father.data.splice(index, 1)
......
......@@ -38,9 +38,17 @@
:type="child.inputType"
v-model="form[father.key][child.key]"
:placeholder="child.placeholder"
maxlength="30"
:maxlength="child.maxlength|| 50"
:disabled="editStatus"
:style="{ width: child.inputWidth ? child.inputWidth : '100%' }"
@input="val => handleInputChange(val, child, father)"
:formatter="
value =>
child.isFormatter
? `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
: value
"
:parser="value => value.replace(/\$\s?|(,*)/g, '')"
/>
<el-select
v-if="child.domType === 'Select'"
......@@ -255,6 +263,7 @@ import {
import { editProductPlanInfo, delAdditional } from '@/api/sign/appointment'
import { updatePolicyProduct, delPolicyAdditional } from '@/api/sign/underwritingMain'
import useUserStore from '@/store/modules/user'
import { numberFormat } from '@/utils/number'
const userStore = useUserStore()
const dictStore = useDictStore() //获取字典数据
const props = defineProps({
......@@ -307,6 +316,11 @@ const data = reactive({
})
const { form, rules, processedProductData, queryParams, oldProductDomData, tempForm, dialogForm } =
toRefs(data)
//处理数据
const handleInputChange = (val, child, father) => {
let newVal = numberFormat(val, child)
form.value[father.key][child.key] = newVal
}
const deleteChildren = (father, index) => {
if (father.key == 'apiProductPlanAdditionalInfoDtoList') {
father.data.splice(index, 1)
......@@ -442,7 +456,7 @@ const handleSearchSelectChange = (father, key) => {
if (key == 'insuranceTypeName') {
searchOptions.value['insuranceTypeName'].forEach(item => {
if (item.value == form.value['apiProductPlanMainInfoDto']['insuranceTypeName']) {
if (item.code == 'CI') {
if (item.code == 'CATEGORY5934890141') {
resetShow('sumInsured', true)
} else {
resetShow('sumInsured', false)
......
......@@ -28,7 +28,15 @@
:type="child.inputType"
v-model="form[child.key]"
:placeholder="child.placeholder"
maxlength="30"
@input="val => (form[child.key] = numberFormat(val, child))"
:maxlength="child.maxlength || 50"
:formatter="
value =>
child.isFormatter
? `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
: value
"
:parser="value => value.replace(/\$\s?|(,*)/g, '')"
:disabled="editStatus"
:style="{ width: child.inputWidth ? child.inputWidth : '100%' }"
/>
......@@ -227,10 +235,8 @@ import Address from '@/views/components/address'
import { watch, nextTick } from 'vue'
import { getCustomerList } from '@/api/sign/fna'
import { editSecondHolderInfo } from '@/api/sign/appointment'
import { updatePolicySecondHolder } from '@/api/sign/underwritingMain'
import useDictStore from '@/store/modules/dict'
import { numberFormat } from '@/utils/number'
const dictStore = useDictStore() //获取字典数据
const props = defineProps({
activeName: { type: String, default: '' }, //tab名称
......
......@@ -16,7 +16,7 @@
<el-form :model="queryParams" ref="queryRef" label-width="100px" label-position="top">
<el-row :gutter="30">
<el-col :sm="12" :lg="8" :xs="24">
<el-form-item label="确认预约时间">
<el-form-item label="签单日">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
......@@ -26,6 +26,23 @@
end-placeholder="结束日期"
@clear="clearDate"
></el-date-picker>
<!-- <div class="timeBox">
<el-date-picker
v-model="queryParams.date1"
type="date"
aria-label="Pick a date"
placeholder="开始日期"
style="width: 47%"
/>
<span style="margin: 10px">-</span>
<el-date-picker
v-model="queryParams.date1"
type="date"
aria-label="Pick a date"
placeholder="结束日期"
style="width: 47%"
/>
</div> -->
</el-form-item>
</el-col>
<el-col :sm="12" :lg="8" :xs="24">
......@@ -73,7 +90,9 @@
>
<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>
......@@ -213,7 +232,7 @@ import {
getItineraryExprot
} from '@/api/sign/appointment'
import useUserStore from '@/store/modules/user'
import {copyToClipboard} from '@/utils/copyToClipboard'
import { copyToClipboard } from '@/utils/copyToClipboard'
const dictStore = useDictStore()
const userStore = useUserStore()
const router = useRouter()
......@@ -588,7 +607,6 @@ function handleUpdate(row, type) {
}
getList()
</script>
<style lang="scss" scoped>
.app-container {
......@@ -646,5 +664,12 @@ getList()
align-items: center;
justify-content: center;
}
.timeBox {
display: flex;
width: 100%;
box-sizing: border-box;
align-items: center;
font-size: 0; /* 移除空格影响 */
}
}
</style>
......@@ -225,8 +225,6 @@ const onSubmit = async (data) => {
ElMessage.error(res.msg || '保存转介人失败')
}
} else if (data.activeTab === 'attachment') {
} else if (data.activeTab === 'firstPayment') {
try {
params = {
......@@ -246,6 +244,8 @@ const onSubmit = async (data) => {
return
}
}else if(data.activeTab === 'attachment'){
viewDetailDialogFlag.value = false
}
}
......@@ -386,7 +386,7 @@ const searchConfig = ref([
}
}, {
type: 'input',
prop: 'paymentTerm',
prop: 'issueNumber',
label: '缴费年期',
inputType: 'decimal',
rules: [
......
......@@ -156,25 +156,20 @@
:formatter="column.formatter"
/>
<el-table-column fixed="right" label="操作" min-width="120">
<template #default>
<template #default="scope">
<el-popconfirm title="确定删除吗?" @confirm="deleteFile(scope.row)">
<template #reference>
<el-button
link
type="primary"
size="small"
@click="handleClick"
:disabled="props.mode === 'viewDetail'"
>
修改 </el-button
><el-button
link
type="danger"
size="small"
@click="handleClick"
:disabled="props.mode === 'viewDetail'"
>
删除
</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
......@@ -199,6 +194,7 @@
@close="((fileUploadDialogFlag = false), (files = ''))"
@confirm="((fileUploadDialogFlag = false), (files = ''))"
>
<!-- <FileUploader/> -->
<FileUpload
v-model="files"
:data="{ obiectTableName: 'policy_follow', objectBizId: props.policyBizId }"
......@@ -220,7 +216,7 @@ import CommonDialog from '@/components/commonDialog'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Delete, Edit, Search, Share, Upload } from '@element-plus/icons-vue'
import { getPolicyfollow, getProductList } from '@/api/sign/underwritingMain'
import { uploadOssFileList } from '@/api/common'
import { uploadOssFileList,delUploadFile } from '@/api/common'
import { getProcessDetail } from '@/api/sign/fna'
import { premiumReconciliationList } from '@/api/sign/policy'
import { loadDicts, getDictLabel } from '@/utils/useDict'
......@@ -228,6 +224,8 @@ import { getNowTime, formatToDate, formatToDateTime } from '@/utils/date'
import EditableTable from '@/components/csf-common/EditableTable.vue'
import useUserStore from '@/store/modules/user'
import FileUploader from '@/components/LargeFileUploader/index.vue';
const { proxy } = getCurrentInstance()
proxy.useDictLists([
'sys_no_yes',
......@@ -287,7 +285,8 @@ const introducerConfig = [
brokerName: 'realName',
internalCode: 'internalNumber',
team: 'deptName',
phone: 'phone'
phone: 'phone',
teamBizId:'deptBizId'
},
transform: res => {
return (res?.data.records || []).map(item => ({
......@@ -297,7 +296,7 @@ const introducerConfig = [
}))
}
},
{
{
type: 'input',
prop: 'brokerName',
label: '转介人姓名',
......@@ -501,10 +500,9 @@ const policyInfoFormConfig = ref([
},
onChangeExtraFields: {
reconciliationCompany: 'name',
reconciliationCode:'code',
reconciliationCode: 'code'
},
rules: [{ required: true, message: '请选择出单经纪公司', trigger: 'blur' }],
rules: [{ required: true, message: '请选择出单经纪公司', trigger: 'blur' }]
},
{
type: 'input',
......@@ -525,11 +523,11 @@ const policyInfoFormConfig = ref([
},
{
type: 'select',
prop: 'policyTpye',
prop: 'policyType',
label: '保单类型',
options:[
{label:'电子保单',value:'1'},
{label:'纸质保单',value:'2'}
options: [
{ label: '电子保单', value: '1' },
{ label: '纸质保单', value: '2' }
]
},
{
......@@ -677,7 +675,7 @@ const basicPlanFormConfig = ref([
{
type: 'input',
prop: 'issueNumber',
label: '供款期数',
label: '供款期数'
},
{
type: 'input',
......@@ -685,9 +683,7 @@ const basicPlanFormConfig = ref([
label: '保障年期',
inputType: 'decimal',
decimalDigits: 4,
rules: [
{ pattern: /^\d+(\.\d{1,4})?$/, message: '最多四位小数', trigger: 'blur' }
]
rules: [{ pattern: /^\d+(\.\d{1,4})?$/, message: '最多四位小数', trigger: 'blur' }]
},
{
type: 'input',
......@@ -695,9 +691,7 @@ const basicPlanFormConfig = ref([
label: '保额(重疾险)',
inputType: 'decimal',
decimalDigits: 4,
rules: [
{ pattern: /^\d+(\.\d{1,4})?$/, message: '最多四位小数', trigger: 'blur' }
]
rules: [{ pattern: /^\d+(\.\d{1,4})?$/, message: '最多四位小数', trigger: 'blur' }]
},
{
type: 'select',
......@@ -1004,6 +998,7 @@ const activeTab = ref('basic')
// ========================
const handleBeforeLeave = async (newTabName, oldTabName) => {
console.log('切换前确认-----------------------', newTabName, oldTabName)
console.log(tabDirty.value)
if (tabDirty.value[oldTabName]) {
try {
await ElMessageBox.confirm(`“${getTabLabel(oldTabName)}” 未提交,确定要切换吗?`, '提示', {
......@@ -1063,25 +1058,36 @@ const handleSubmit = () => {
if (valid) {
console.log('提交数据', introducerTableData.value)
if (activeTab.value === 'postal') {
tabDirty.value.postal = false
emit('submit', { ...postalFormData.value, activeTab: activeTab.value })
} else if (activeTab.value === 'firstPayment') {
tabDirty.value.firstPayment = false
emit('submit', { ...firstPremiumFormData.value, activeTab: activeTab.value })
} else if (activeTab.value === 'attachment') {
emit('submit', { activeTab: activeTab.value })
tabDirty.value.attachment = false
} else if (activeTab.value === 'introducer') {
emit('submit', { ...introducerTableData.value, activeTab: activeTab.value })
tabDirty.value.introducer = false
} else if (activeTab.value === 'basic') {
tabDirty.value.basic = false
emit('submit', {
...basicInfoFormData.value,
activeTab: activeTab.value,
...policyInfoFormData.value
})
} else if (activeTab.value === 'productPlan') {
tabDirty.value.productPlan = false
emit('submit', {
...basicPlanFormData.value,
activeTab: activeTab.value,
...localData.additionalPlans
})
}else if (activeTab.value === 'attachment'){
tabDirty.value.attachment = false
emit('submit', {
activeTab: activeTab.value
})
}
}
})
......@@ -1090,9 +1096,19 @@ const handleSubmit = () => {
const handleCancel = () => {
emit('cancel')
}
// 删除附件
const deleteFile = (row)=>{
console.log(row)
const fileBizId = row.fileBizId || '';
delUploadFile(fileBizId).then(res=>{
console.log(res)
if(res.code===200){
getAttachmentListDetail(props.policyBizId)
}else{
ElMessage.error('删除文件失败')
}
const handleClick = tab => {
activeTab.value = tab.name
})
}
// 如果外部 modelValue 更新(比如重新加载数据),同步到 localData
......@@ -1121,6 +1137,9 @@ const getPolicyfollowDetail = () => {
firstPremiumFormData.value = {
...transformToFormData(res.data, firstPremiumFormConfig.value)
}
postalFormData.value = {
...transformToFormData(res.data, postalFormConfig.value)
}
introducerTableData.value = res.data.brokerList || []
console.log('====父组件在赋值basicPlanFormData后', basicPlanFormData.value)
}
......@@ -1140,7 +1159,6 @@ const getAttachmentListDetail = policyBizId => {
uploadOssFileList(params).then(res => {
if (res.code === 200) {
attachmentTableData.value = res.data || []
console.log('attachmentTableData', res.data)
}
})
}
......@@ -1257,7 +1275,6 @@ const handleSelectChange = async (prop, value, item, type) => {
console.log('✅ 产品列表刷新指令执行完毕')
// ElMessage.success('产品列表已更新')
} catch (err) {
console.error('💥 刷新产品列表出错:', err)
ElMessage.error('产品列表更新失败,请重试')
......
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