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 ...@@ -5,7 +5,8 @@ VITE_APP_TITLE = CSF-CORE
VITE_APP_ENV = 'production' 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 # 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip VITE_BUILD_COMPRESS = gzip
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"build:dev": "vite build --mode=development", "build:dev": "vite build --mode=development",
"build:prod": "vite build", "build:prod": "vite build --mode=production",
"build:stage": "vite build --mode staging", "build:stage": "vite build --mode staging",
"preview": "vite preview" "preview": "vite preview"
}, },
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
"jsencrypt": "3.3.2", "jsencrypt": "3.3.2",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"pinia": "3.0.2", "pinia": "3.0.2",
"spark-md5": "^3.0.2",
"splitpanes": "^4.0.4", "splitpanes": "^4.0.4",
"vue": "3.5.16", "vue": "3.5.16",
"vue-cropper": "1.1.1", "vue-cropper": "1.1.1",
......
...@@ -197,14 +197,7 @@ export function incomeEditRecords(data) { ...@@ -197,14 +197,7 @@ export function incomeEditRecords(data) {
data: data data: data
}) })
} }
// 入账管理比对记录列表
export function incomeCompareRecords(data) {
return request({
url: '/csf/api/commission/compare/records',
method: 'post',
data: data
})
}
// 保单号列表 // 保单号列表
export function policyData(data) { export function policyData(data) {
return request({ 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
// 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 = [ ...@@ -108,6 +108,7 @@ const beneficiary = [
unit: '历史客户', unit: '历史客户',
unitColor: 'rgba(0, 82, 217, 1)', unitColor: 'rgba(0, 82, 217, 1)',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 15, maxLength: 15,
disabled: false, disabled: false,
...@@ -124,6 +125,7 @@ const beneficiary = [ ...@@ -124,6 +125,7 @@ const beneficiary = [
key: 'namePyEn', key: 'namePyEn',
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 30, maxLength: 30,
disabled: false, disabled: false,
...@@ -153,6 +155,7 @@ const beneficiary = [ ...@@ -153,6 +155,7 @@ const beneficiary = [
key: 'idNumber', key: 'idNumber',
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 20, maxLength: 20,
disabled: false, disabled: false,
...@@ -209,7 +212,10 @@ const beneficiary = [ ...@@ -209,7 +212,10 @@ const beneficiary = [
label: '受益比例', label: '受益比例',
key: 'benefitRatio', key: 'benefitRatio',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
......
...@@ -54,7 +54,8 @@ const customer = [ ...@@ -54,7 +54,8 @@ const customer = [
prop: 'idNumber', prop: 'idNumber',
type: 'input', type: 'input',
placeholder: '请输入证件号码', placeholder: '请输入证件号码',
required: true required: true,
isFormatter: false //是否需要数字分位符
}, },
{ {
label: '是否长期有效', label: '是否长期有效',
...@@ -91,8 +92,8 @@ const customer = [ ...@@ -91,8 +92,8 @@ const customer = [
unit: '历史客户', unit: '历史客户',
unitColor: 'rgba(0, 82, 217, 1)', unitColor: 'rgba(0, 82, 217, 1)',
inputType: 'text', inputType: 'text',
required: true, required: false,
maxLength: 15, maxLength: 30,
disabled: false, disabled: false,
placeholder: '请输入2~6位汉字', placeholder: '请输入2~6位汉字',
show: true, show: true,
...@@ -108,7 +109,7 @@ const customer = [ ...@@ -108,7 +109,7 @@ const customer = [
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
required: true, required: true,
maxLength: 30, maxLength: 100,
disabled: false, disabled: false,
placeholder: '请输入', placeholder: '请输入',
show: true, show: true,
...@@ -118,36 +119,6 @@ const customer = [ ...@@ -118,36 +119,6 @@ const customer = [
lg: 8 //栅格布局份数 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: '性别', label: '性别',
key: 'gender', key: 'gender',
...@@ -179,7 +150,9 @@ const customer = [ ...@@ -179,7 +150,9 @@ const customer = [
label: '年龄', label: '年龄',
key: 'age', key: 'age',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
isFormatter: false, //是否需要数字分位符
maxLength: 30, maxLength: 30,
required: true, required: true,
disabled: true, disabled: true,
...@@ -294,7 +267,9 @@ const customer = [ ...@@ -294,7 +267,9 @@ const customer = [
label: '退休年龄', label: '退休年龄',
key: 'retirementAge', key: 'retirementAge',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
isFormatter: false, //是否需要数字分位符
maxLength: 30, maxLength: 30,
required: false, required: false,
disabled: false, disabled: false,
...@@ -324,7 +299,8 @@ const customer = [ ...@@ -324,7 +299,8 @@ const customer = [
label: '体重(KG)', label: '体重(KG)',
key: 'weight', key: 'weight',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -339,7 +315,8 @@ const customer = [ ...@@ -339,7 +315,8 @@ const customer = [
label: '身高(CM)', label: '身高(CM)',
key: 'height', key: 'height',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -369,7 +346,8 @@ const customer = [ ...@@ -369,7 +346,8 @@ const customer = [
label: '受供养人数目', label: '受供养人数目',
key: 'dependentsNum', key: 'dependentsNum',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'integer', //输入值的格式
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -402,13 +380,15 @@ const customer = [ ...@@ -402,13 +380,15 @@ const customer = [
label: '税务国家', label: '税务国家',
prop: 'taxCountry', prop: 'taxCountry',
type: 'Input', type: 'Input',
placeholder: '请输入税务国家' placeholder: '请输入税务国家',
isFormatter: false //是否需要数字分位符
}, },
{ {
label: '税务编号', label: '税务编号',
prop: 'taxId', prop: 'taxId',
type: 'Input', type: 'Input',
placeholder: '请税务编号' placeholder: '请税务编号',
isFormatter: false //是否需要数字分位符
} }
], ],
data: [ data: [
...@@ -468,7 +448,8 @@ const customer = [ ...@@ -468,7 +448,8 @@ const customer = [
label: '固定电话', label: '固定电话',
key: 'landline', key: 'landline',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false, required: false,
disabled: false, disabled: false,
placeholder: '请填写', placeholder: '请填写',
...@@ -484,6 +465,7 @@ const customer = [ ...@@ -484,6 +465,7 @@ const customer = [
key: 'email', key: 'email',
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
maxLength: 30, maxLength: 30,
required: false, required: false,
disabled: false, disabled: false,
...@@ -529,6 +511,8 @@ const customer = [ ...@@ -529,6 +511,8 @@ const customer = [
label: '住宅地址', label: '住宅地址',
key: 'residentialAddress', key: 'residentialAddress',
domType: 'arrowRight', domType: 'arrowRight',
customLabel: true, //自定义label
customAddress: true,
required: false, required: false,
disabled: false, disabled: false,
placeholder: '请填写', placeholder: '请填写',
...@@ -545,6 +529,7 @@ const customer = [ ...@@ -545,6 +529,7 @@ const customer = [
key: 'mailingAddressCode', key: 'mailingAddressCode',
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -586,6 +571,7 @@ const customer = [ ...@@ -586,6 +571,7 @@ const customer = [
key: 'csName', key: 'csName',
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -601,6 +587,7 @@ const customer = [ ...@@ -601,6 +587,7 @@ const customer = [
key: 'industry', key: 'industry',
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
maxLength: 300, maxLength: 300,
required: false, required: false,
disabled: false, disabled: false,
...@@ -616,7 +603,10 @@ const customer = [ ...@@ -616,7 +603,10 @@ const customer = [
key: 'currentMonthlyIncome', key: 'currentMonthlyIncome',
domType: 'Input', domType: 'Input',
unit: 'HKD', unit: 'HKD',
inputType: 'number', inputType: 'text', //使用千位符,type为text
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -631,7 +621,9 @@ const customer = [ ...@@ -631,7 +621,9 @@ const customer = [
label: '总工作年期', label: '总工作年期',
key: 'totalWorkingYears', key: 'totalWorkingYears',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -646,7 +638,9 @@ const customer = [ ...@@ -646,7 +638,9 @@ const customer = [
label: '受雇于现职年期', label: '受雇于现职年期',
key: 'currentTenure', key: 'currentTenure',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -662,6 +656,7 @@ const customer = [ ...@@ -662,6 +656,7 @@ const customer = [
key: 'position', key: 'position',
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -713,8 +708,9 @@ const customer = [ ...@@ -713,8 +708,9 @@ const customer = [
key: 'companyAddressCode', key: 'companyAddressCode',
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 20, maxLength: 50,
disabled: false, disabled: false,
placeholder: '请输入', placeholder: '请输入',
show: true, show: true,
...@@ -739,7 +735,10 @@ const customer = [ ...@@ -739,7 +735,10 @@ const customer = [
key: 'monthIncome', key: 'monthIncome',
domType: 'Input', domType: 'Input',
unit: 'HKD', unit: 'HKD',
inputType: 'number', inputType: 'text', //使用千位符,type为text
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -755,7 +754,10 @@ const customer = [ ...@@ -755,7 +754,10 @@ const customer = [
key: 'monthExpenditure', key: 'monthExpenditure',
domType: 'Input', domType: 'Input',
unit: 'HKD', unit: 'HKD',
inputType: 'number', inputType: 'text', //使用千位符,type为text
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -771,7 +773,10 @@ const customer = [ ...@@ -771,7 +773,10 @@ const customer = [
key: 'totalCurrentAssets', key: 'totalCurrentAssets',
domType: 'Input', domType: 'Input',
unit: 'HKD', unit: 'HKD',
inputType: 'number', inputType: 'text', //使用千位符,type为text
valueType: 'decimalNumber', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -787,7 +792,10 @@ const customer = [ ...@@ -787,7 +792,10 @@ const customer = [
key: 'totalDebt', key: 'totalDebt',
domType: 'Input', domType: 'Input',
unit: 'HKD', unit: 'HKD',
inputType: 'number', inputType: 'text', //使用千位符,type为text
valueType: 'decimalNumber', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -814,6 +822,7 @@ const customer = [ ...@@ -814,6 +822,7 @@ const customer = [
key: 'travel', key: 'travel',
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -872,6 +881,7 @@ const customer = [ ...@@ -872,6 +881,7 @@ const customer = [
key: 'delicacy', key: 'delicacy',
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
......
const fnaForm = [ const fnaForm = [
// 介绍人信息 // 介绍人信息
// { {
// fatherTitle: '转介人信息', fatherTitle: '转介人信息',
// keyType: 'Array', keyType: 'Array',
// key: 'brokerList', key: 'brokerList',
// anchorKey: 'brokerList', anchorKey: 'brokerList',
// moudleType: 'brokerList', moudleType: 'brokerList',
// dataLength: 1, dataLength: 1,
// showMoudle: true, showMoudle: false, //是否展示此模块
// showTable: true, showTable: true,
// addChildren: true, addChildren: true,
// addChildrenTxt: '转介人', addChildrenTxt: '转介人',
// fatherRequired: false, fatherRequired: false,
// isOpen: false, isOpen: false,
// // 新增表格列配置 // 新增表格列配置
// columns: [ columns: [
// { {
// label: '姓名', label: '姓名',
// prop: 'brokerName', prop: 'brokerName',
// type: 'remoteSelect', type: 'remoteSelect',
// searchType: 'brokerName', searchType: 'brokerName',
// placeholder: '请输入关键词搜索', placeholder: '请输入关键词搜索',
// required: false required: false
// }, },
// { {
// label: '性别', label: '性别',
// prop: 'brokerGender', prop: 'brokerGender',
// type: 'select', type: 'select',
// dictType: 'sys_gender', dictType: 'sys_gender',
// placeholder: '请选择', placeholder: '请选择',
// required: false required: false
// }, },
// { {
// label: '内部编号', label: '内部编号',
// prop: 'brokerNumber', prop: 'brokerNumber',
// type: 'input', type: 'input',
// placeholder: '请输入', placeholder: '请输入',
// required: false required: false
// }, },
// { {
// label: '所属团队', label: '所属团队',
// prop: 'brokerTeam', prop: 'brokerTeam',
// type: 'remoteSelect', type: 'remoteSelect',
// searchType: 'brokerTeam', searchType: 'brokerTeam',
// placeholder: '请输入关键词搜索', placeholder: '请输入关键词搜索',
// required: false required: false
// }, },
// { {
// label: '分配比例', label: '分配比例',
// prop: 'brokerRatio', prop: 'brokerRatio',
// type: 'inputNumber', type: 'inputNumber',
// placeholder: '请输入', placeholder: '请输入',
// required: true required: true
// }, },
// { {
// label: '备注', label: '备注',
// prop: 'remark', prop: 'remark',
// type: 'input', type: 'input',
// placeholder: '请输入', placeholder: '请输入',
// required: false required: false
// } }
// ], ],
// data: [ data: [
// // { // {
// // brokerName: '', // brokerName: '',
// // brokerGender: '', // brokerGender: '',
// // brokerNumber: '', // brokerNumber: '',
// // brokerTeam: '', // brokerTeam: '',
// // brokerRatio: '', // brokerRatio: '',
// // remark: '' // remark: ''
// // } // }
// ] ]
// }, },
// 受供养人信息 // 受供养人信息
{ {
fatherTitle: '受供养人信息', fatherTitle: '受供养人信息',
...@@ -101,7 +101,9 @@ const fnaForm = [ ...@@ -101,7 +101,9 @@ const fnaForm = [
prop: 'dependantAge', prop: 'dependantAge',
type: 'inputNumber', type: 'inputNumber',
placeholder: '请输入年龄', placeholder: '请输入年龄',
required: false required: false,
valueType: 'decimal', //输入值的格式
isFormatter: false //是否需要数字分位符
} }
], ],
data: [ data: [
...@@ -164,7 +166,10 @@ const fnaForm = [ ...@@ -164,7 +166,10 @@ const fnaForm = [
prop: 'sumInsured', prop: 'sumInsured',
type: 'inputNumber', type: 'inputNumber',
placeholder: '请输入保额', placeholder: '请输入保额',
required: true required: true,
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true //是否需要数字分位符
} }
], ],
data: [ data: [
...@@ -213,7 +218,10 @@ const fnaForm = [ ...@@ -213,7 +218,10 @@ const fnaForm = [
prop: 'marketValue', prop: 'marketValue',
type: 'inputNumber', type: 'inputNumber',
placeholder: '请输入市场价值', placeholder: '请输入市场价值',
required: false required: false,
decimalDigits: 2, //小数点位数
valueType: 'decimalNumber', //输入值的格式
isFormatter: true //是否需要数字分位符
}, },
{ {
label: '币种', label: '币种',
...@@ -250,7 +258,10 @@ const fnaForm = [ ...@@ -250,7 +258,10 @@ const fnaForm = [
domType: 'Input', domType: 'Input',
unit: 'HKD', unit: 'HKD',
showDes: true, //是否展示描述 showDes: true, //是否展示描述
inputType: 'number', inputType: 'text', //使用千位符,type为text
valueType: 'decimal', //输入值的格式
isFormatter: true, //是否需要数字分位符
decimalDigits: 2, //小数点位数
required: true, required: true,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -270,7 +281,10 @@ const fnaForm = [ ...@@ -270,7 +281,10 @@ const fnaForm = [
domType: 'Input', domType: 'Input',
unit: 'HKD', unit: 'HKD',
showDes: true, //是否展示描述 showDes: true, //是否展示描述
inputType: 'number', inputType: 'text', //使用千位符,type为text
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: true, required: true,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
...@@ -288,7 +302,10 @@ const fnaForm = [ ...@@ -288,7 +302,10 @@ const fnaForm = [
value: '', value: '',
domType: 'Input', domType: 'Input',
unit: 'HKD', unit: 'HKD',
inputType: 'number', inputType: 'text', //使用千位符,type为text
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: true, required: true,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
......
...@@ -41,7 +41,6 @@ const productPlan = [ ...@@ -41,7 +41,6 @@ const productPlan = [
key: 'insuranceTypeName', key: 'insuranceTypeName',
domType: 'SearchSelect', domType: 'SearchSelect',
required: true, required: true,
maxLength: 30, maxLength: 30,
disabled: false, disabled: false,
placeholder: '请输入', placeholder: '请输入',
...@@ -69,9 +68,12 @@ const productPlan = [ ...@@ -69,9 +68,12 @@ const productPlan = [
label: '供款期数', label: '供款期数',
key: 'issueNumber', key: 'issueNumber',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 0, //小数点位数
isFormatter: true, //是否需要数字分位符
required: true, required: true,
placeholder: '请选择', placeholder: '请输入',
dictType: 'issueNumber', dictType: 'issueNumber',
show: true, show: true,
labelPosition: 'top', //标签的位置 labelPosition: 'top', //标签的位置
...@@ -83,7 +85,10 @@ const productPlan = [ ...@@ -83,7 +85,10 @@ const productPlan = [
label: '保障年期', label: '保障年期',
key: 'guaranteePeriod', key: 'guaranteePeriod',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 0, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false, required: false,
maxLength: 20, maxLength: 20,
disabled: false, disabled: false,
...@@ -127,7 +132,10 @@ const productPlan = [ ...@@ -127,7 +132,10 @@ const productPlan = [
label: '每期保费', label: '每期保费',
key: 'eachIssuePremium', key: 'eachIssuePremium',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: true, required: true,
maxLength: 20, maxLength: 20,
disabled: false, disabled: false,
...@@ -156,7 +164,10 @@ const productPlan = [ ...@@ -156,7 +164,10 @@ const productPlan = [
label: '保单征费', label: '保单征费',
key: 'policyLevy', key: 'policyLevy',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: true, required: true,
maxLength: 20, maxLength: 20,
disabled: false, disabled: false,
...@@ -185,7 +196,10 @@ const productPlan = [ ...@@ -185,7 +196,10 @@ const productPlan = [
label: '预缴年期', label: '预缴年期',
key: 'prepaymentPeriod', key: 'prepaymentPeriod',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false, required: false,
maxLength: 20, maxLength: 20,
disabled: false, disabled: false,
...@@ -270,7 +284,10 @@ const productPlan = [ ...@@ -270,7 +284,10 @@ const productPlan = [
label: '保单额度(重疾)', label: '保单额度(重疾)',
key: 'sumInsured', key: 'sumInsured',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false, required: false,
maxLength: 20, maxLength: 20,
disabled: false, disabled: false,
......
...@@ -57,6 +57,7 @@ const secondHolder = [ ...@@ -57,6 +57,7 @@ const secondHolder = [
label: '名字(中文)', label: '名字(中文)',
key: 'nameCn', key: 'nameCn',
domType: 'Input', domType: 'Input',
isFormatter: false, //是否需要数字分位符
unit: '历史客户', unit: '历史客户',
unitColor: 'rgba(0, 82, 217, 1)', unitColor: 'rgba(0, 82, 217, 1)',
inputType: 'text', inputType: 'text',
...@@ -76,6 +77,7 @@ const secondHolder = [ ...@@ -76,6 +77,7 @@ const secondHolder = [
key: 'namePyEn', key: 'namePyEn',
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 30, maxLength: 30,
disabled: false, disabled: false,
...@@ -105,6 +107,7 @@ const secondHolder = [ ...@@ -105,6 +107,7 @@ const secondHolder = [
key: 'idNumber', key: 'idNumber',
domType: 'Input', domType: 'Input',
inputType: 'text', inputType: 'text',
isFormatter: false, //是否需要数字分位符
required: false, required: false,
maxLength: 20, maxLength: 20,
disabled: false, disabled: false,
...@@ -148,7 +151,10 @@ const secondHolder = [ ...@@ -148,7 +151,10 @@ const secondHolder = [
label: '受益比例', label: '受益比例',
key: 'benefitRatio', key: 'benefitRatio',
domType: 'Input', domType: 'Input',
inputType: 'number', inputType: 'text',
valueType: 'decimal', //输入值的格式
decimalDigits: 2, //小数点位数
isFormatter: true, //是否需要数字分位符
required: false, required: false,
maxLength: 300, maxLength: 300,
disabled: false, disabled: false,
......
...@@ -9,7 +9,7 @@ import useSettingsStore from '@/store/modules/settings' // 使用命名导入 ...@@ -9,7 +9,7 @@ import useSettingsStore from '@/store/modules/settings' // 使用命名导入
import usePermissionStore from '@/store/modules/permission' // 使用命名导入 import usePermissionStore from '@/store/modules/permission' // 使用命名导入
import { getToken, setToken, removeToken } from '@/utils/auth' import { getToken, setToken, removeToken } from '@/utils/auth'
import { de } from 'element-plus/es/locales.mjs' import { de } from 'element-plus/es/locales.mjs'
console.log('权限11111')
NProgress.configure({ showSpinner: false }) NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/register', '/workbench'] 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' 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 { export default {
formatCurrency formatCurrency,
} numberFormat,
\ No newline at end of file formatThousands,
formatThousandsSimple
}
...@@ -6,7 +6,6 @@ import { tansParams, blobValidate } from '@/utils/ruoyi' ...@@ -6,7 +6,6 @@ import { tansParams, blobValidate } from '@/utils/ruoyi'
import cache from '@/plugins/cache' import cache from '@/plugins/cache'
import { saveAs } from 'file-saver' import { saveAs } from 'file-saver'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
let downloadLoadingInstance let downloadLoadingInstance
// 是否显示重新登录 // 是否显示重新登录
export let isRelogin = { show: false } export let isRelogin = { show: false }
...@@ -84,8 +83,10 @@ service.interceptors.request.use( ...@@ -84,8 +83,10 @@ service.interceptors.request.use(
// 响应拦截器 // 响应拦截器
service.interceptors.response.use( service.interceptors.response.use(
res => { res => {
// 未设置状态码则默认成功状态 // 未设置状态码则默认成功状态
const code = res.data.code || 200 const code = res.data.code || 200
// 获取错误信息 // 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default'] const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回 // 二进制数据则直接返回
...@@ -140,6 +141,31 @@ service.interceptors.response.use( ...@@ -140,6 +141,31 @@ service.interceptors.response.use(
} else if (message.includes('timeout')) { } else if (message.includes('timeout')) {
message = '系统接口请求超时' message = '系统接口请求超时'
} else if (message.includes('Request failed with status code')) { } 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) + '异常' message = '系统接口' + message.substr(message.length - 3) + '异常'
} }
ElMessage({ message: message, type: 'error', duration: 5 * 1000 }) ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
......
...@@ -12,80 +12,43 @@ ...@@ -12,80 +12,43 @@
<!-- 统计信息卡片 --> <!-- 统计信息卡片 -->
<div class="statistics-container" v-if="statisticsData.totalPolicyCount > 0"> <div class="statistics-container" v-if="statisticsData.totalPolicyCount > 0">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="4" :lg="4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-card shadow="hover" class="statistics-card"> <el-statistic title="总保单数" :value="statisticsData.totalPolicyCount" />
<div class="card-content">
<div class="card-label">总保单数</div>
<div class="card-value">{{ statisticsData.totalPolicyCount }}</div>
</div>
</el-card>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-card shadow="hover" class="statistics-card"> <el-statistic title="总保费(HKD)" :value="statisticsData.totalPremium" :formatter="value=>formatCurrency(value)" />
<div class="card-content">
<div class="card-label">总保费(HKD)</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalPremium) }}</div>
</div>
</el-card>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-card shadow="hover" class="statistics-card"> <el-statistic title="对账公司数" :value="statisticsData.reconciliationCompanyCount" />
<div class="card-content">
<div class="card-label">对账公司数</div>
<div class="card-value">{{ statisticsData.reconciliationCompanyCount }}</div>
</div>
</el-card>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-card shadow="hover" class="statistics-card"> <el-statistic title="比对记录总条数" :value="statisticsData.totalCompareCommissionCount" />
<div class="card-content">
<div class="card-label">比对记录总条数</div>
<div class="card-value">{{ statisticsData.totalCompareCommissionCount }}</div>
</div>
</el-card>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-card shadow="hover" class="statistics-card"> <el-statistic title="比对成功数" :value="statisticsData.successCompareCommissionCount" />
<div class="card-content">
<div class="card-label">比对成功数</div>
<div class="card-value">{{ statisticsData.successCompareCommissionCount }}</div>
</div>
</el-card>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-card shadow="hover" class="statistics-card"> <el-statistic title="比对失败数" :value="statisticsData.failedCompareCommissionCount" />
<div class="card-content">
<div class="card-label">比对失败数</div>
<div class="card-value">{{ statisticsData.failedCompareCommissionCount }}</div>
</div>
</el-card>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-card shadow="hover" class="statistics-card"> <el-statistic title="入账金额(实)" :value="statisticsData.totalPaidAmount" :formatter="value=>formatCurrency(value)" />
<div class="card-content">
<div class="card-label">入账金额(实)</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalPaidAmount) }}</div>
</div>
</el-card>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-card shadow="hover" class="statistics-card"> <el-statistic title="入账金额(估)" :value="statisticsData.expectePaidAmount" :formatter="value=>formatCurrency(value)" />
<div class="card-content">
<div class="card-label">入账金额(估)</div>
<div class="card-value">{{ formatCurrency(statisticsData.expectePaidAmount) }}</div>
</div>
</el-card>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="4" :lg="4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-card shadow="hover" class="statistics-card"> <el-statistic title="差额(估)" :value="statisticsData.differenceAmount" :formatter="value=>formatCurrency(value)" />
<div class="card-content">
<div class="card-label">差额(估)</div>
<div class="card-value">{{ formatCurrency(statisticsData.differenceAmount) }}</div>
</div>
</el-card>
</el-col> </el-col>
</el-row> </el-row>
</div> </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 <el-table :data="tableData" ref="multipleTableRef" height="400" row-key="id" border highlight-current-row
style="width: 100%" v-loading="loading" @selection-change="handleSelectionChange"> style="width: 100%" v-loading="loading" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="40" :selectable="selectableFn" /> <el-table-column type="selection" width="40" :selectable="selectableFn" />
...@@ -108,9 +71,9 @@ ...@@ -108,9 +71,9 @@
{{ selectDictLabel(csf_expected_commission_status, row.commissionExpectedStatus) }} {{ selectDictLabel(csf_expected_commission_status, row.commissionExpectedStatus) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="currentCommissionRatio" label="本次入账比例" width="130" 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="paidRatio" label="累积实佣率" width="130" sortable :formatter="formatRatio" />
<el-table-column prop="pendingRatio" label="待入账比例" width="120" 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="amount" label="本次入账金额" width="130" sortable :formatter="formatCurrencyUtil" />
<el-table-column prop="currency" label="入账币种" width="120" sortable /> <el-table-column prop="currency" label="入账币种" width="120" sortable />
<el-table-column prop="exchangeRate" label="结算汇率(实)" width="140" sortable /> <el-table-column prop="exchangeRate" label="结算汇率(实)" width="140" sortable />
...@@ -310,12 +273,14 @@ const downloadTemplate = () => { ...@@ -310,12 +273,14 @@ const downloadTemplate = () => {
link.download = '入账检核导入模板.xlsx' // 设置下载文件名 link.download = '入账检核导入模板.xlsx' // 设置下载文件名
link.click() // 自动触发点击,无需 append 到 DOM(现代浏览器支持) link.click() // 自动触发点击,无需 append 到 DOM(现代浏览器支持)
} }
const formatRatio = (row, column, cellValue, index) => { const formatRatio = (row, column, cellValue, index) => {
if (cellValue == null || cellValue == '' || cellValue == 0) { if (cellValue == null || cellValue == '' && cellValue !== 0) {
return '-' return '-'
} }
return cellValue + '%' return cellValue + '%'
} }
const formatCurrencyUtil = (row, column, cellValue, index) => { const formatCurrencyUtil = (row, column, cellValue, index) => {
if (cellValue == null || cellValue == '' || cellValue == 0) { if (cellValue == null || cellValue == '' || cellValue == 0) {
return '-' return '-'
......
...@@ -97,7 +97,7 @@ const searchConfig = ref([ ...@@ -97,7 +97,7 @@ const searchConfig = ref([
requestParams: { pageNo: 1, pageSize: 20 }, requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入转介人名称搜索', placeholder: '输入转介人名称搜索',
debounceWait: 500, // 自定义防抖时间 debounceWait: 500, // 自定义防抖时间
valueKey: 'userSaleBizId', valueKey: 'clientUserBizId',
labelKey: 'realName', labelKey: 'realName',
multiple: true, multiple: true,
transform: (res) => { transform: (res) => {
......
...@@ -35,27 +35,18 @@ ...@@ -35,27 +35,18 @@
<el-col :sm="child.sm" :lg="child.lg" class="formItem" v-if="child.show"> <el-col :sm="child.sm" :lg="child.lg" class="formItem" v-if="child.show">
<div> <div>
<el-form-item <el-form-item
:label=" :label="child.customLabel ? '' : child.label"
child.key === 'residentialAddress' && child.domType === 'arrowRight'
? ''
: child.label
"
:prop="child.key" :prop="child.key"
:key="child.key" :key="child.key"
:label-width="child.labelWidth" :label-width="child.labelWidth"
:label-position="child.labelPosition" :label-position="child.labelPosition"
> >
<!-- 自定义 label 插槽 --> <!-- 自定义 label 插槽 -->
<template <template v-if="child.customLabel" #label>
v-if="
child.key === 'residentialAddress' &&
child.domType === 'arrowRight'
"
#label
>
<div class="custom-label"> <div class="custom-label">
<span>{{ child.label }}</span> <span>{{ child.label }}</span>
<el-checkbox-group <el-checkbox-group
v-if="child.customAddress"
v-model="form.isCopyAddress" v-model="form.isCopyAddress"
size="small" size="small"
style="margin-left: 8px" style="margin-left: 8px"
...@@ -70,10 +61,17 @@ ...@@ -70,10 +61,17 @@
</template> </template>
<el-input <el-input
v-if="child.domType === 'Input'" v-if="child.domType === 'Input'"
:type="child.inputType"
v-model="form[child.key]" v-model="form[child.key]"
:type="child.inputType"
:placeholder="child.placeholder" :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=" :disabled="
child.key == 'age' || child.key == 'age' ||
child.key == 'bmi' || child.key == 'bmi' ||
...@@ -82,6 +80,7 @@ ...@@ -82,6 +80,7 @@
: editStatus : editStatus
" "
@blur="handleInputBlur(child)" @blur="handleInputBlur(child)"
@input="val => handleInputChange(val, child)"
> >
<template #append v-if="child.unit"> <template #append v-if="child.unit">
<span <span
...@@ -231,30 +230,6 @@ ...@@ -231,30 +230,6 @@
style="width: calc(50% - 10px)" style="width: calc(50% - 10px)"
/> />
</div> </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 <el-radio-group
v-model="scope.row[column.prop]" v-model="scope.row[column.prop]"
v-else-if="column.type === 'radioGroup'" v-else-if="column.type === 'radioGroup'"
...@@ -273,6 +248,13 @@ ...@@ -273,6 +248,13 @@
:placeholder="column.placeholder" :placeholder="column.placeholder"
:disabled="editStatus" :disabled="editStatus"
@blur="tableInputBlur(father, scope.row, column.prop, $event)" @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> </template>
...@@ -442,6 +424,7 @@ import CommonForm from '@/views/components/commonForm' ...@@ -442,6 +424,7 @@ import CommonForm from '@/views/components/commonForm'
import CommonDialog from '@/components/commonDialog' import CommonDialog from '@/components/commonDialog'
import CommonPage from '@/components/commonPage' import CommonPage from '@/components/commonPage'
import CardOne from '@/components/formCard/cardOne' import CardOne from '@/components/formCard/cardOne'
import { numberFormat } from '@/utils/number'
import { watch } from 'vue' import { watch } from 'vue'
import { import {
addCustomer, addCustomer,
...@@ -485,7 +468,7 @@ const tableLoading = ref(false) ...@@ -485,7 +468,7 @@ const tableLoading = ref(false)
const tableData = ref([]) const tableData = ref([])
const total = ref(0) const total = ref(0)
//计算默认显示的日期(18年前的今天) //计算默认显示的日期(18年前的今天)
const defaultDisplayDate = ref(dayjs().subtract(19, 'year').toDate()) const defaultDisplayDate = ref()
// 地址组件菜单数据 // 地址组件菜单数据
const addressMenuList = ref([ const addressMenuList = ref([
...@@ -640,6 +623,11 @@ const handleAppendInput = child => { ...@@ -640,6 +623,11 @@ const handleAppendInput = child => {
customerList() customerList()
} }
} }
//处理数据
const handleInputChange = (val, child) => {
let newVal = numberFormat(val, child)
form.value[child.key] = newVal
}
const handleInputBlur = child => { const handleInputBlur = child => {
if ( if (
(child.key == 'weight' || child.key == 'height') && (child.key == 'weight' || child.key == 'height') &&
...@@ -773,13 +761,11 @@ const handleDateChange = child => { ...@@ -773,13 +761,11 @@ const handleDateChange = child => {
} }
} }
const disabledDate = (time, child) => { const disabledDate = (time, child) => {
if (child.key == 'birthday') { if (child.key == 'birthday' && props.activeName !== 'insurantInfo') {
// 计算18年前的今天 // 计算18年前的今天
const eighteenYearsAgo = dayjs().subtract(19, 'year') const eighteenYearsAgo = dayjs().subtract(19, 'year')
// 禁用今天之后的日期和18年前的今天之后的日期 // 禁用今天之后的日期和18年前的今天之后的日期
return time.getTime() > Date.now() || time.getTime() > eighteenYearsAgo.valueOf() return time.getTime() > Date.now() || time.getTime() > eighteenYearsAgo.valueOf()
} else {
return time.getTime() > Date.now()
} }
} }
const exportInfo = () => { const exportInfo = () => {
...@@ -1606,6 +1592,9 @@ watch( ...@@ -1606,6 +1592,9 @@ watch(
(newVal, oldVal) => { (newVal, oldVal) => {
editStatus.value = false editStatus.value = false
customerRightRef.value = null customerRightRef.value = null
if (newVal === 'customer' || newVal === 'policyholder') {
defaultDisplayDate.value = dayjs().subtract(19, 'year').toDate()
}
if (newVal === 'customer') { if (newVal === 'customer') {
processFormData() processFormData()
// if (JSON.stringify(customerForm.value) != '{}') { // if (JSON.stringify(customerForm.value) != '{}') {
...@@ -1634,6 +1623,7 @@ watch( ...@@ -1634,6 +1623,7 @@ watch(
processFormData() processFormData()
} }
} else if (newVal === 'insurantInfo') { } else if (newVal === 'insurantInfo') {
defaultDisplayDate.value = dayjs().startOf('day')
editStatus.value = props.fatherEditStatus editStatus.value = props.fatherEditStatus
if (JSON.stringify(insuredForm.value) != '{}') { if (JSON.stringify(insuredForm.value) != '{}') {
form.value = JSON.parse(JSON.stringify(insuredForm.value)) form.value = JSON.parse(JSON.stringify(insuredForm.value))
...@@ -1718,6 +1708,12 @@ defineExpose({ ...@@ -1718,6 +1708,12 @@ defineExpose({
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
.numberTxt {
font-size: 14px;
font-weight: normal;
margin-left: 5px;
color: #a8abb2;
}
} }
.topBtn { .topBtn {
width: 100%; width: 100%;
......
...@@ -84,8 +84,15 @@ ...@@ -84,8 +84,15 @@
:type="child.inputType" :type="child.inputType"
v-model="form[father.key][child.key]" v-model="form[father.key][child.key]"
:placeholder="child.placeholder" :placeholder="child.placeholder"
maxlength="30" :maxlength="child.maxlength"
:disabled="editStatus" :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"> <template #append v-if="child.unit">
<span>{{ child.unit }}</span> <span>{{ child.unit }}</span>
...@@ -249,32 +256,43 @@ ...@@ -249,32 +256,43 @@
v-else-if=" v-else-if="
column.type === 'inputNumber' && !!scope.row.showSumInsured column.type === 'inputNumber' && !!scope.row.showSumInsured
" "
v-model.number="scope.row[column.prop]" v-model="scope.row[column.prop]"
:placeholder="column.placeholder" :placeholder="column.placeholder"
:disabled="editStatus" :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> <template v-if="column.unit" #append>
<span>{{ column.unit }}</span> <span>{{ column.unit }}</span>
</template> </template>
</el-input> </el-input>
<el-input <!-- <el-input
v-else-if="column.type === 'inputNumber' && scope.row.showSumInsured" 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" :placeholder="column.placeholder"
:disabled="editStatus" :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> <template v-if="column.unit" #append>
<span>{{ column.unit }}</span> <span>{{ column.unit }}</span>
</template> </template>
</el-input> </el-input> -->
<!-- 普通输入框(默认) --> <!-- 普通输入框(默认) -->
<el-input <el-input
v-else-if="column.prop !== 'sumInsured'" v-else-if="column.prop !== 'sumInsured'"
v-model="scope.row[column.prop]" v-model="scope.row[column.prop]"
:placeholder="column.placeholder" :placeholder="column.placeholder"
:disabled="editStatus" :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> </template>
</el-table-column> </el-table-column>
...@@ -346,6 +364,7 @@ import useDictStore from '@/store/modules/dict' ...@@ -346,6 +364,7 @@ import useDictStore from '@/store/modules/dict'
import fanFormDomData from '@/formJson/fnaForm' import fanFormDomData from '@/formJson/fnaForm'
import { watch, nextTick } from 'vue' import { watch, nextTick } from 'vue'
import { addfanForm, getfanFormDetail, editFanForm, getCustomerList } from '@/api/sign/fna' import { addfanForm, getfanFormDetail, editFanForm, getCustomerList } from '@/api/sign/fna'
import { numberFormat } from '@/utils/number'
import { import {
getUserSaleExpandList, getUserSaleExpandList,
getAllTeam, getAllTeam,
...@@ -399,6 +418,7 @@ const { ...@@ -399,6 +418,7 @@ const {
tempFanFormValue, tempFanFormValue,
tempFanFormData tempFanFormData
} = toRefs(data) } = toRefs(data)
const handleRemoteSelectChange = async (row, column, father) => { const handleRemoteSelectChange = async (row, column, father) => {
console.log(row, column) console.log(row, column)
// 通过保险公司查保险种类 row中收集了insurer的值 // 通过保险公司查保险种类 row中收集了insurer的值
...@@ -803,15 +823,7 @@ const getInvalidFields = fields => { ...@@ -803,15 +823,7 @@ const getInvalidFields = fields => {
} }
return errors return errors
} }
// 判断是否为数组
const isArray = value => {
return Array.isArray(value)
}
// 判断是否为对象
const isObject = value => {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
// 表单提交 // 表单提交
const submitForm = saveType => { const submitForm = saveType => {
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
type="warning" type="warning"
:icon="Edit" :icon="Edit"
@click="handleCancel('appoint')" @click="handleCancel('appoint')"
:disabled="submitLoading"
>取消预约</el-button >取消预约</el-button
> >
</div> </div>
...@@ -48,14 +49,21 @@ ...@@ -48,14 +49,21 @@
>编辑</el-button >编辑</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
> > -->
<el-button <el-button
v-if="!editStatus" v-if="!editStatus"
type="success" type="success"
:icon="Check" :icon="Check"
@click="handleSubmit('submit')" @click="handleSubmit('submit')"
:disabled="submitLoading"
:loading="submitLoading"
>提交</el-button >提交</el-button
> >
<el-button <el-button
...@@ -67,6 +75,7 @@ ...@@ -67,6 +75,7 @@
type="warning" type="warning"
:icon="Edit" :icon="Edit"
@click="handleCancel('apply')" @click="handleCancel('apply')"
:disabled="submitLoading"
>取消申请</el-button >取消申请</el-button
> >
</div> </div>
...@@ -107,15 +116,19 @@ ...@@ -107,15 +116,19 @@
type="success" type="success"
:icon="Check" :icon="Check"
@click="handleSubmit('submit')" @click="handleSubmit('submit')"
:disabled="submitLoading"
:loading="submitLoading"
>提交</el-button >提交</el-button
> >
<el-button <!-- <el-button
type="primary" type="primary"
plain plain
v-if="!idsObj.appointmentBizId && source == 'fnaList'" v-if="!idsObj.appointmentBizId && source == 'fnaList'"
@click="handleSubmit('storage')" @click="handleSubmit('storage')"
:disabled="submitLoading"
:loading="submitLoading"
>暂存</el-button >暂存</el-button
> > -->
<el-button <el-button
style="margin-left: 10px" style="margin-left: 10px"
v-if="idsObj.appointmentBizId" v-if="idsObj.appointmentBizId"
...@@ -445,7 +458,7 @@ const props = defineProps({ ...@@ -445,7 +458,7 @@ const props = defineProps({
policyDetailInfo: { type: Object, default: () => ({}) }, //新单跟进传递关于保单的详情信息 policyDetailInfo: { type: Object, default: () => ({}) }, //新单跟进传递关于保单的详情信息
policyId: { type: Object, default: () => ({}) } //新单跟进传递的Id policyId: { type: Object, default: () => ({}) } //新单跟进传递的Id
}) })
const submitLoading = ref(false)
const userStore = useUserStore() const userStore = useUserStore()
const dictStore = useDictStore() const dictStore = useDictStore()
const showStorage = ref(false) const showStorage = ref(false)
...@@ -960,6 +973,7 @@ const handleSubmit = async type => { ...@@ -960,6 +973,7 @@ const handleSubmit = async type => {
return return
} }
} }
// 获取投保人form // 获取投保人form
if (policyHolderInfoRef.value) { if (policyHolderInfoRef.value) {
submitAppointmentObj.value.apiPolicyholderInfoDto = submitAppointmentObj.value.apiPolicyholderInfoDto =
...@@ -1045,15 +1059,19 @@ const handleSubmit = async type => { ...@@ -1045,15 +1059,19 @@ const handleSubmit = async type => {
console.log('大提交', submitAppointmentObj.value) console.log('大提交', submitAppointmentObj.value)
// return // return
if (type == 'submit') { if (type == 'submit') {
submitLoading.value = true
// 新增预约单 // 新增预约单
addAppointment(submitAppointmentObj.value).then(res => { addAppointment(submitAppointmentObj.value).then(res => {
if (res.code == 200) { if (res.code == 200) {
submitLoading.value = false
proxy.$message.success('预约成功') proxy.$message.success('预约成功')
editStatus.value = false editStatus.value = false
emit('handleSuccess', { emit('handleSuccess', {
tab: 'appointment', tab: 'appointment',
type: 'add' type: 'add'
}) })
} else {
submitLoading.value = false
} }
}) })
} }
...@@ -1064,12 +1082,16 @@ const handleSubmit = async type => { ...@@ -1064,12 +1082,16 @@ const handleSubmit = async type => {
if (route.query.appointmentType == '2') { if (route.query.appointmentType == '2') {
submitAppointmentObj.value.source = 2 submitAppointmentObj.value.source = 2
} }
submitLoading.value = true
// 编辑预约单 // 编辑预约单
editAppointmentDetail(submitAppointmentObj.value).then(res => { editAppointmentDetail(submitAppointmentObj.value).then(res => {
if (res.code == 200) { if (res.code == 200) {
submitLoading.value = false
editStatus.value = true editStatus.value = true
getAppointmentInfo(idsObj.value.appointmentBizId) getAppointmentInfo(idsObj.value.appointmentBizId)
proxy.$message.success('修改预约单成功') proxy.$message.success('修改预约单成功')
} else {
submitLoading.value = false
} }
}) })
} }
...@@ -1079,22 +1101,30 @@ const handleSubmit = async type => { ...@@ -1079,22 +1101,30 @@ const handleSubmit = async type => {
props.processDetail.customerBizId props.processDetail.customerBizId
submitAppointmentObj.value.apiAppointmentInfoDto.fnaBizId = props.processDetail.fnaBizId submitAppointmentObj.value.apiAppointmentInfoDto.fnaBizId = props.processDetail.fnaBizId
submitAppointmentObj.value.apiAppointmentInfoDto.fnaNo = props.processDetail.fnaNo submitAppointmentObj.value.apiAppointmentInfoDto.fnaNo = props.processDetail.fnaNo
submitLoading.value = true
// 暂存预约单 // 暂存预约单
storageAppointment(submitAppointmentObj.value).then(res => { storageAppointment(submitAppointmentObj.value).then(res => {
if (res.code == 200) { if (res.code == 200) {
submitLoading.value = false
proxy.$message.success('预约暂存成功') proxy.$message.success('预约暂存成功')
router.push({ path: '/sign/appointment' }) router.push({ path: '/sign/appointment' })
} else {
submitLoading.value = false
} }
}) })
} }
// 编辑状态下预约暂存 // 编辑状态下预约暂存
if (type == 'editStorage') { if (type == 'editStorage') {
submitLoading.value = true
// 暂存预约单 // 暂存预约单
appointmentEditStorage(submitAppointmentObj.value).then(res => { appointmentEditStorage(submitAppointmentObj.value).then(res => {
if (res.code == 200) { if (res.code == 200) {
submitLoading.value = false
proxy.$message.success('预约暂存提交成功') proxy.$message.success('预约暂存提交成功')
editStatus.value = true editStatus.value = true
getAppointmentInfo(idsObj.value.appointmentBizId) getAppointmentInfo(idsObj.value.appointmentBizId)
} else {
submitLoading.value = false
} }
}) })
} }
......
...@@ -73,7 +73,7 @@ ...@@ -73,7 +73,7 @@
:type="child.inputType" :type="child.inputType"
v-model="form[child.key]" v-model="form[child.key]"
:placeholder="child.placeholder" :placeholder="child.placeholder"
maxlength="30" :maxlength="child.maxlength || 50"
:rows="child.rows" :rows="child.rows"
:disabled="editStatus" :disabled="editStatus"
/> />
...@@ -964,7 +964,7 @@ const searchSelectList = async (query, key) => { ...@@ -964,7 +964,7 @@ const searchSelectList = async (query, key) => {
const params5 = { const params5 = {
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
queryContent: queryString realName: queryString
} }
const response5 = await getUserSaleExpandList(params5) const response5 = await getUserSaleExpandList(params5)
if (response5.code == 200) { if (response5.code == 200) {
...@@ -998,7 +998,7 @@ const searchSelectList = async (query, key) => { ...@@ -998,7 +998,7 @@ const searchSelectList = async (query, key) => {
const params5 = { const params5 = {
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
realName: queryString name: queryString
} }
const response5 = await insuranceReconciliationCompany(params5) const response5 = await insuranceReconciliationCompany(params5)
if (response5.code == 200) { if (response5.code == 200) {
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
:type="child.inputType" :type="child.inputType"
v-model="form[child.key]" v-model="form[child.key]"
:placeholder="child.placeholder" :placeholder="child.placeholder"
maxlength="30" :maxlength="child.maxlength || 50"
:disabled="editStatus" :disabled="editStatus"
:style="{ width: child.inputWidth ? child.inputWidth : '100%' }" :style="{ width: child.inputWidth ? child.inputWidth : '100%' }"
/> />
...@@ -177,8 +177,16 @@ ...@@ -177,8 +177,16 @@
:type="child.inputType" :type="child.inputType"
v-model="dialogForm[child.key]" v-model="dialogForm[child.key]"
:placeholder="child.placeholder" :placeholder="child.placeholder"
maxlength="30"
@blur="handleInputBlur(child)" @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 <el-select
...@@ -257,10 +265,8 @@ import Phone from '@/views/components/phone' ...@@ -257,10 +265,8 @@ import Phone from '@/views/components/phone'
import Address from '@/views/components/address' import Address from '@/views/components/address'
import { watch, nextTick } from 'vue' 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 useDictStore from '@/store/modules/dict'
import { numberFormat } from '@/utils/number'
const dictStore = useDictStore() //获取字典数据 const dictStore = useDictStore() //获取字典数据
const props = defineProps({ const props = defineProps({
activeName: { type: String, default: '' }, //tab名称 activeName: { type: String, default: '' }, //tab名称
...@@ -392,6 +398,7 @@ const { ...@@ -392,6 +398,7 @@ const {
tempBeneficiaryDomData, tempBeneficiaryDomData,
dialogForm dialogForm
} = toRefs(data) } = toRefs(data)
const deleteChildren = (father, index) => { const deleteChildren = (father, index) => {
if (father.key == 'apiBeneficiaryInfoDtoList') { if (father.key == 'apiBeneficiaryInfoDtoList') {
father.data.splice(index, 1) father.data.splice(index, 1)
......
...@@ -38,9 +38,17 @@ ...@@ -38,9 +38,17 @@
:type="child.inputType" :type="child.inputType"
v-model="form[father.key][child.key]" v-model="form[father.key][child.key]"
:placeholder="child.placeholder" :placeholder="child.placeholder"
maxlength="30" :maxlength="child.maxlength|| 50"
:disabled="editStatus" :disabled="editStatus"
:style="{ width: child.inputWidth ? child.inputWidth : '100%' }" :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 <el-select
v-if="child.domType === 'Select'" v-if="child.domType === 'Select'"
...@@ -255,6 +263,7 @@ import { ...@@ -255,6 +263,7 @@ import {
import { editProductPlanInfo, delAdditional } from '@/api/sign/appointment' import { editProductPlanInfo, delAdditional } from '@/api/sign/appointment'
import { updatePolicyProduct, delPolicyAdditional } from '@/api/sign/underwritingMain' import { updatePolicyProduct, delPolicyAdditional } from '@/api/sign/underwritingMain'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import { numberFormat } from '@/utils/number'
const userStore = useUserStore() const userStore = useUserStore()
const dictStore = useDictStore() //获取字典数据 const dictStore = useDictStore() //获取字典数据
const props = defineProps({ const props = defineProps({
...@@ -307,6 +316,11 @@ const data = reactive({ ...@@ -307,6 +316,11 @@ const data = reactive({
}) })
const { form, rules, processedProductData, queryParams, oldProductDomData, tempForm, dialogForm } = const { form, rules, processedProductData, queryParams, oldProductDomData, tempForm, dialogForm } =
toRefs(data) toRefs(data)
//处理数据
const handleInputChange = (val, child, father) => {
let newVal = numberFormat(val, child)
form.value[father.key][child.key] = newVal
}
const deleteChildren = (father, index) => { const deleteChildren = (father, index) => {
if (father.key == 'apiProductPlanAdditionalInfoDtoList') { if (father.key == 'apiProductPlanAdditionalInfoDtoList') {
father.data.splice(index, 1) father.data.splice(index, 1)
...@@ -442,7 +456,7 @@ const handleSearchSelectChange = (father, key) => { ...@@ -442,7 +456,7 @@ const handleSearchSelectChange = (father, key) => {
if (key == 'insuranceTypeName') { if (key == 'insuranceTypeName') {
searchOptions.value['insuranceTypeName'].forEach(item => { searchOptions.value['insuranceTypeName'].forEach(item => {
if (item.value == form.value['apiProductPlanMainInfoDto']['insuranceTypeName']) { if (item.value == form.value['apiProductPlanMainInfoDto']['insuranceTypeName']) {
if (item.code == 'CI') { if (item.code == 'CATEGORY5934890141') {
resetShow('sumInsured', true) resetShow('sumInsured', true)
} else { } else {
resetShow('sumInsured', false) resetShow('sumInsured', false)
......
...@@ -28,7 +28,15 @@ ...@@ -28,7 +28,15 @@
:type="child.inputType" :type="child.inputType"
v-model="form[child.key]" v-model="form[child.key]"
:placeholder="child.placeholder" :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" :disabled="editStatus"
:style="{ width: child.inputWidth ? child.inputWidth : '100%' }" :style="{ width: child.inputWidth ? child.inputWidth : '100%' }"
/> />
...@@ -227,10 +235,8 @@ import Address from '@/views/components/address' ...@@ -227,10 +235,8 @@ import Address from '@/views/components/address'
import { watch, nextTick } from 'vue' import { watch, nextTick } from 'vue'
import { getCustomerList } from '@/api/sign/fna' 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 useDictStore from '@/store/modules/dict'
import { numberFormat } from '@/utils/number'
const dictStore = useDictStore() //获取字典数据 const dictStore = useDictStore() //获取字典数据
const props = defineProps({ const props = defineProps({
activeName: { type: String, default: '' }, //tab名称 activeName: { type: String, default: '' }, //tab名称
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
<el-form :model="queryParams" ref="queryRef" label-width="100px" label-position="top"> <el-form :model="queryParams" ref="queryRef" label-width="100px" label-position="top">
<el-row :gutter="30"> <el-row :gutter="30">
<el-col :sm="12" :lg="8" :xs="24"> <el-col :sm="12" :lg="8" :xs="24">
<el-form-item label="确认预约时间"> <el-form-item label="签单日">
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
...@@ -26,6 +26,23 @@ ...@@ -26,6 +26,23 @@
end-placeholder="结束日期" end-placeholder="结束日期"
@clear="clearDate" @clear="clearDate"
></el-date-picker> ></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-form-item>
</el-col> </el-col>
<el-col :sm="12" :lg="8" :xs="24"> <el-col :sm="12" :lg="8" :xs="24">
...@@ -71,11 +88,13 @@ ...@@ -71,11 +88,13 @@
width="200" width="200"
fixed="left" fixed="left"
> >
<template #default="scope"> <template #default="scope">
<el-button link size="default"> <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> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="预约状态" align="center" prop="status" fixed="left" width="100"> <el-table-column label="预约状态" align="center" prop="status" fixed="left" width="100">
<template #default="scope"> <template #default="scope">
...@@ -213,7 +232,7 @@ import { ...@@ -213,7 +232,7 @@ import {
getItineraryExprot getItineraryExprot
} from '@/api/sign/appointment' } from '@/api/sign/appointment'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import {copyToClipboard} from '@/utils/copyToClipboard' import { copyToClipboard } from '@/utils/copyToClipboard'
const dictStore = useDictStore() const dictStore = useDictStore()
const userStore = useUserStore() const userStore = useUserStore()
const router = useRouter() const router = useRouter()
...@@ -588,7 +607,6 @@ function handleUpdate(row, type) { ...@@ -588,7 +607,6 @@ function handleUpdate(row, type) {
} }
getList() getList()
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.app-container { .app-container {
...@@ -646,5 +664,12 @@ getList() ...@@ -646,5 +664,12 @@ getList()
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.timeBox {
display: flex;
width: 100%;
box-sizing: border-box;
align-items: center;
font-size: 0; /* 移除空格影响 */
}
} }
</style> </style>
...@@ -225,9 +225,7 @@ const onSubmit = async (data) => { ...@@ -225,9 +225,7 @@ const onSubmit = async (data) => {
ElMessage.error(res.msg || '保存转介人失败') ElMessage.error(res.msg || '保存转介人失败')
} }
} else if (data.activeTab === 'attachment') { } else if (data.activeTab === 'firstPayment') {
} else if (data.activeTab === 'firstPayment') {
try { try {
params = { params = {
policyBizId: selectedRow.value.policyBizId, policyBizId: selectedRow.value.policyBizId,
...@@ -246,6 +244,8 @@ const onSubmit = async (data) => { ...@@ -246,6 +244,8 @@ const onSubmit = async (data) => {
return return
} }
}else if(data.activeTab === 'attachment'){
viewDetailDialogFlag.value = false
} }
} }
...@@ -386,7 +386,7 @@ const searchConfig = ref([ ...@@ -386,7 +386,7 @@ const searchConfig = ref([
} }
}, { }, {
type: 'input', type: 'input',
prop: 'paymentTerm', prop: 'issueNumber',
label: '缴费年期', label: '缴费年期',
inputType: 'decimal', inputType: 'decimal',
rules: [ rules: [
......
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