Commit 9d0b34ab by yuzhenWang

Merge branch 'test' into 'feature-20250827wyz-写业务'

Test

See merge request !91
parents 66f2ec3f 330ef011
...@@ -21,17 +21,19 @@ ...@@ -21,17 +21,19 @@
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.1",
"@vueup/vue-quill": "1.2.0", "@vueup/vue-quill": "1.2.0",
"@vueuse/core": "13.3.0", "@vueuse/core": "13.3.0",
"ali-oss": "^6.23.0",
"axios": "1.9.0", "axios": "1.9.0",
"clipboard": "2.0.11", "clipboard": "2.0.11",
"dayjs": "^1.11.18", "dayjs": "^1.11.18",
"echarts": "5.6.0", "echarts": "5.6.0",
"element-plus": "^2.9.9", "element-plus": "^2.13.5",
"file-saver": "2.0.5", "file-saver": "2.0.5",
"fuse.js": "6.6.2", "fuse.js": "6.6.2",
"js-beautify": "1.14.11", "js-beautify": "1.14.11",
"js-cookie": "3.0.5", "js-cookie": "3.0.5",
"jsencrypt": "3.3.2", "jsencrypt": "3.3.2",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"p-limit": "^7.3.0",
"pinia": "3.0.2", "pinia": "3.0.2",
"spark-md5": "^3.0.2", "spark-md5": "^3.0.2",
"splitpanes": "^4.0.4", "splitpanes": "^4.0.4",
......
...@@ -221,3 +221,19 @@ export function insuranceReconciliationCompany(data) { ...@@ -221,3 +221,19 @@ export function insuranceReconciliationCompany(data) {
method: 'post' method: 'post'
}) })
} }
// 获取STS凭证
export function getStstoken(projectBizId) {
return request({
url: `/oss/api/sts/sts-token?projectBizId=${projectBizId}`,
method: 'get'
})
}
// 大文件分片上传接口
export function batchSaveFiles(data) {
return request({
url: '/oss/api/sts/batch/save/files',
data: data,
method: 'post'
})
}
...@@ -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> <template>
<div class="upload-container"> <div class="big-file-uploader">
<h2>多文件分片上传 (Vue3 + Element Plus)</h2> <!--
关键修改:
<el-card shadow="hover"> 1. 添加 ref="uploadRef" 以便直接访问内部文件列表
<el-upload 2. 监听 @change 获取原生文件
ref="uploadRef" -->
class="multi-uploader" <el-upload
action="#" ref="uploadRef"
:auto-upload="false" class="upload-demo"
multiple drag
:limit="10" multiple
:on-change="handleFileChange" :auto-upload="false"
:on-remove="handleRemove" :on-change="handleFileSelect"
:file-list="fileList" :file-list="uiFileList"
:disabled="isUploading"
:limit="100"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
拖拽文件到此处 或 <em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">
支持大文件切片上传,最大并发数:{{ uploadConcurrency }}
</div>
</template>
</el-upload>
<!-- 控制栏 -->
<div class="control-bar" v-if="fileList.length > 0">
<el-button
type="primary"
@click="startUpload"
:disabled="isUploading || !hasPendingFiles"
:loading="isUploading"
size="default"
>
{{ isUploading ? '正在上传...' : '开始上传' }}
</el-button>
<el-button @click="clearList" :disabled="isUploading" size="default">
清空列表
</el-button>
<div class="stats">
共 {{ fileList.length }} 个文件 |
成功: <span class="text-success">{{ successCount }}</span> |
失败: <span class="text-danger">{{ failCount }}</span>
</div>
</div>
<!-- 文件列表 -->
<div class="file-list-container">
<div
v-for="(item, index) in fileList"
:key="item.uid"
class="file-item"
:class="{ 'is-error': item.status === 'fail' }"
> >
<template #trigger> <div class="file-info">
<el-button type="primary">选择文件</el-button> <el-icon class="file-icon"><document /></el-icon>
</template> <div class="file-meta">
<div class="file-name" :title="item.name">{{ item.name }}</div>
<!-- 自定义文件列表项 --> <div class="file-size">{{ formatSize(item.size) }}</div>
<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> </div>
</template> </div>
</el-upload>
<div class="progress-wrapper">
<el-progress
:percentage="item.progress"
:status="getProgressStatus(item.status)"
:stroke-width="16"
>
<template v-if="item.status === 'uploading'">
<span class="speed-text">{{ item.speed }}</span>
</template>
<template v-else-if="item.status === 'success'">
<el-icon><check /></el-icon>
</template>
<template v-else-if="item.status === 'fail'">
<el-icon><close /></el-icon>
</template>
</el-progress>
</div>
<div class="global-actions" v-if="fileList.length > 0"> <div class="action-area">
<el-button type="success" @click="startAllUploads" :disabled="isUploadingAny">全部开始</el-button> <el-tag :type="getStatusType(item.status)" size="small">
<el-button type="info" @click="clearAll">清空列表</el-button> {{ statusMap[item.status] }}
</el-tag>
<el-button
v-if="item.status === 'fail'"
link
type="primary"
size="small"
@click="retryUpload(item)"
>
重试
</el-button>
<el-button
v-if="item.status === 'waiting'"
link
type="danger"
size="small"
@click="removeFile(item)"
>
移除
</el-button>
</div>
</div> </div>
</el-card> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, reactive } from 'vue'; import { ref, computed, nextTick } from 'vue';
import { Document } from '@element-plus/icons-vue'; import {
UploadFilled,
Document,
Check,
Close
} from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { useMultiFileUpload } from '@/composables/useMultiFileUpload'; import OSS from 'ali-oss';
import pLimit from 'p-limit';
// 请确保路径正确
import { getStstoken, batchSaveFiles } from '@/api/common';
const props = defineProps({
tenantBizId: { type: String, required: true,default: '' },
projectBizId: { type: String, required: true, default: '' },
objectBizId: { type: String, required: true, default: '' },
objectType: { type: String, default: '' },
objectTableName: { type: String, default: '' },
objectName: { type: String, default: '' },
uploadConcurrency: { type: Number, default: 3 },
partSize: { type: Number, default: 1 * 1024 * 1024 },
});
const uploadRef = ref(null); const uploadRef = ref(null);
const fileList = ref([]); // Element Plus 管理的文件列表 const fileList = ref([]);
const uiFileList = ref([]);
const isUploading = ref(false);
const ossClient = ref(null);
const statusMap = {
waiting: '等待上传',
uploading: '上传中',
success: '成功',
fail: '失败',
};
const hasPendingFiles = computed(() => {
return fileList.value.some(f => f.status === 'waiting' || f.status === 'fail');
});
// 引入我们的逻辑钩子 const successCount = computed(() => fileList.value.filter(f => f.status === 'success').length);
const { startUpload, pauseFile, resumeFile, cancelFile, getTaskStatus } = useMultiFileUpload(); const failCount = computed(() => fileList.value.filter(f => f.status === 'fail').length);
// 格式化大小
const formatSize = (bytes) => { const formatSize = (bytes) => {
if (!bytes) return ''; if (!bytes) return '0 B';
const k = 1024; const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB']; const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k)); const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}; };
// 当用户选择文件时 const getProgressStatus = (status) => {
const handleFileChange = (uploadFile, uploadFiles) => { if (status === 'success') return 'success';
fileList.value = uploadFiles; if (status === 'fail') return 'exception';
// 初始化状态为 idle (实际上 useMultiFileUpload 会在 start 时初始化) return undefined;
}; };
// 移除文件 const getStatusType = (status) => {
const handleRemove = (uploadFile, uploadFiles) => { const map = { waiting: 'info', uploading: 'warning', success: 'success', fail: 'danger' };
fileList.value = uploadFiles; return map[status] || 'info';
cancelFile(uploadFile.uid); // 如果正在传,先取消
}; };
// 单个开始上传 // 【优化】处理文件选择(增加关闭再打开时的去重清理)
const startSingleUpload = (file) => { const handleFileSelect = (rawFile, uploadFiles) => {
// 这里的 file 是 element-plus 的 UploadFile 对象,raw 才是原生 File 对象 // 如果是重新选择文件,建议先检查是否需要清理“失败”或“成功”的旧状态,视业务需求而定
if (!file.raw) return; // 这里保持原有逻辑,但增强了对 raw 对象的获取
const filesToProcess = Array.isArray(rawFile) ? rawFile : [rawFile];
// 定义回调,用于强制刷新视图(虽然 uploadTasks 是响应式的,但有时 EL 的 slot 更新不及时,可加 forceUpdate 逻辑,此处依赖响应式) filesToProcess.forEach(file => {
const updateCallback = (uid, status, progress, msg) => { let nativeFile = null;
// 如果需要更激进的 UI 更新,可以在这里操作 fileList 中的对应项
// 但通常响应式对象足够 // 优先从 file.raw 获取
}; if (file.raw && (file.raw instanceof File || file.raw instanceof Blob)) {
nativeFile = file.raw;
} else if (file instanceof File || file instanceof Blob) {
nativeFile = file;
} else {
// 尝试从 uploadRef 中找回原生对象(防止 element-plus 包装丢失)
if (uploadRef.value && uploadRef.value.files) {
const found = uploadRef.value.files.find(f => f.name === file.name && f.size === file.size);
if (found && found.raw) {
nativeFile = found.raw;
}
}
}
startUpload(file.raw, updateCallback); if (!nativeFile) {
}; console.warn('Skipping invalid file:', file.name);
return;
}
// 【关键去重逻辑】
// 如果列表中已存在同名同大小的文件,且状态不是 success(允许重新上传成功的?通常不允许),则跳过
// 如果你的需求是:关闭弹窗再打开,之前的列表应该被清空。请确保在弹窗关闭事件 (@close) 中调用了 clearList()
const exists = fileList.value.some(
f => f.name === nativeFile.name && f.size === nativeFile.size && f.status !== 'success'
);
if (!exists) {
const uid = nativeFile.uid || Date.now() + Math.random();
const newItem = {
uid,
name: nativeFile.name,
size: nativeFile.size,
raw: nativeFile,
status: 'waiting',
progress: 0,
speed: '0 KB/s',
url: '',
startTime: 0,
};
// 全部开始 (简单实现:遍历所有未成功的文件) fileList.value.push(newItem);
const startAllUploads = () => {
fileList.value.forEach(file => { // 同步到 uiFileList,确保 el-upload 显示
const status = getTaskStatus(file.uid)?.status; // 注意:不要直接 push 到 uploadRef.value.files,而是更新绑定的 uiFileList
if (!status || status === 'error' || status === 'idle' || status === 'paused') { const uiExists = uiFileList.value.some(f => f.uid === uid);
// 如果是 paused,调用 resume 逻辑可能需要特殊处理,这里简化为重新触发 start (我们的 logic 支持断点) if (!uiExists) {
if (status === 'paused') { uiFileList.value.push({
resumeFile(file.uid); uid,
} else { name: nativeFile.name,
startSingleUpload(file); size: nativeFile.size,
status: 'ready',
raw: nativeFile
});
} }
} }
}); });
}; };
const handlePause = (uid) => pauseFile(uid); // 【修复】移除单个文件方法
const handleResume = (uid) => resumeFile(uid); const removeFile = (item) => {
const handleCancel = (uid) => { if (isUploading.value && item.status === 'uploading') {
cancelFile(uid); ElMessage.warning('文件正在上传,无法移除');
// 从列表中移除视觉上的文件(可选,看需求是保留记录还是直接删掉) return;
// fileList.value = fileList.value.filter(f => f.uid !== uid); }
// 1. 从逻辑列表移除
fileList.value = fileList.value.filter(f => f.uid !== item.uid);
// 2. 从 UI 列表移除
uiFileList.value = uiFileList.value.filter(f => f.uid !== item.uid);
// 3. 同步清除 el-upload 内部对应的文件记录
if (uploadRef.value) {
// el-upload 的 clearFiles 通常清空所有,若要移除单个,可以直接操作 its internal files 数组
// 但最简单可靠的方法是:既然我们控制了 uiFileList,让 el-upload 依赖 uiFileList 即可
// 如果 UI 没刷新,可以强制触发一次 update
const uploadInstance = uploadRef.value;
if (uploadInstance.files) {
uploadInstance.files = uploadInstance.files.filter(f => f.uid !== item.uid);
}
}
}; };
// 【修复】清空列表方法
const clearList = () => {
if (isUploading.value) {
ElMessage.warning('上传进行中,无法清空');
return;
}
const clearAll = () => { // 1. 清空内部逻辑列表
fileList.value = []; fileList.value = [];
// 清理内部状态逻辑略
// 2. 清空 UI 绑定列表
uiFileList.value = [];
// 3. 【关键】调用 Element Plus 组件实例的方法清除内部状态
if (uploadRef.value) {
uploadRef.value.clearFiles();
}
// 4. 如果是在弹窗中使用,且关闭弹窗时调用此方法,确保父组件也能感知变化(如果需要)
// emit('update:modelValue', []); // 如果有双向绑定
}; };
const isUploadingAny = computed(() => { const initOssClient = async () => {
return fileList.value.some(f => { try {
const s = getTaskStatus(f.uid)?.status; const res = await getStstoken(props.projectBizId);
return s === 'uploading' || s === 'checking' || s === 'merging'; if (res.code !== 200 && res.data?.code !== 200) {
}); throw new Error(res.msg || res.data?.msg || '获取 STS 凭证失败');
}); }
</script>
<style scoped> const data = res.data || res;
.upload-container { const rawData = data.data || data;
max-width: 800px; const { region, accessKeyId, accessKeySecret, stsToken, bucket, endpoint } = rawData;
margin: 20px auto;
}
.el-upload-list__item-custom { if (!accessKeyId || !bucket) throw new Error('STS 凭证信息不完整');
display: flex;
flex-direction: column;
padding: 10px;
border-bottom: 1px solid #ebeef5;
transition: background-color 0.2s;
}
.el-upload-list__item-custom:hover { let ossEndpoint = endpoint;
background-color: #f5f7fa; if (!ossEndpoint) ossEndpoint = `oss-${region}.aliyuncs.com`;
}
.file-info { ossClient.value = new OSS({
display: flex; region,
align-items: center; accessKeyId,
margin-bottom: 8px; accessKeySecret,
} stsToken,
bucket,
endpoint: ossEndpoint,
secure: true,
timeout: 60000,
});
return ossClient.value;
} catch (error) {
console.error('Init OSS Error:', error);
throw error;
}
};
.file-info .el-icon { const uploadSingleFile = async (item) => {
margin-right: 8px; // 【核心修复】再次校验 raw 是否为原生 File
color: #409eff; const file = item.raw;
font-size: 20px;
} if (!(file instanceof File) && !(file instanceof Blob)) {
console.error('Invalid file object for upload:', file);
throw new Error('文件对象无效,必须是 File 或 Blob 类型');
}
.file-name { const objectName = `uploads/${props.projectBizId}/${Date.now()}_${item.uid}_${file.name}`;
font-weight: bold;
color: #606266; item.status = 'uploading';
flex: 1; item.startTime = Date.now();
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 10px;
}
.file-size { try {
font-size: 12px; const client = ossClient.value;
color: #909399;
} await client.multipartUpload(objectName, file, {
parallel: 4,
partSize: props.partSize,
progress: (p) => {
item.progress = Math.floor(p * 100);
const duration = (Date.now() - item.startTime) / 1000;
if (duration > 0 && p > 0) {
const speed = (file.size * p) / duration;
item.speed = `${formatSize(speed)}/s`;
}
},
});
.file-action-area { item.status = 'success';
display: flex; item.progress = 100;
align-items: center; item.speed = '-';
justify-content: space-between;
} // const endpoint = client.options.endpoint.replace(/^https?:\/\//, '');
const endpoint = client.options.endpoint.host;
item.url = `https://${client.options.bucket}.${endpoint}/${objectName}`;
.status-wait, .status-success, .status-error { return {
display: flex; fileName: item.name,
align-items: center; fileSize: item.size,
gap: 10px; fileUrl: item.url,
width: 100%; };
} } catch (error) {
console.error(`Upload failed: ${item.name}`, error);
item.status = 'fail';
item.speed = '-';
return null;
}
};
.status-progress { const startUpload = async () => {
width: 100%; if (!hasPendingFiles.value) return;
display: flex; isUploading.value = true;
align-items: center; const successResults = [];
gap: 15px;
}
.status-progress .el-progress { try {
flex: 1; await initOssClient();
} const pendingItems = fileList.value.filter(f => f.status === 'waiting' || f.status === 'fail');
.btn-group { if (pendingItems.length === 0) return;
display: flex;
gap: 5px; const limit = pLimit(props.uploadConcurrency);
} const tasks = pendingItems.map(item => limit(() => uploadSingleFile(item)));
const results = await Promise.all(tasks);
results.forEach(res => { if (res) successResults.push(res); });
if (successResults.length > 0) {
const payload = {
tenantBizId: props.tenantBizId,
projectBizId: props.projectBizId,
objectBizId: props.objectBizId,
objectType: props.objectType,
objectTableName: props.objectTableName,
objectName: props.objectName,
apiOssFileDtoList: successResults,
};
await batchSaveFiles(payload);
ElMessage.success(`上传完成!成功 ${successResults.length} 个文件`);
} else {
ElMessage.error('所有文件上传均失败');
}
} catch (globalError) {
console.error('Global Upload Error:', globalError);
ElMessage.error(`上传初始化失败: ${globalError.message}`);
} finally {
isUploading.value = false;
}
};
const retryUpload = async (item) => {
if (isUploading.value) return;
item.status = 'uploading';
item.progress = 0;
try {
if (!ossClient.value) await initOssClient();
await uploadSingleFile(item);
if (item.status === 'success') ElMessage.success(`${item.name} 重试成功`);
} catch (e) { /* ignored */ }
};
.global-actions { // 暴露方法给父组件调用
margin-top: 20px; defineExpose({
text-align: right; clearList,
border-top: 1px solid #eee; startUpload, // 如果父组件需要主动触发上传也可以暴露
padding-top: 15px; fileList // 如果需要访问文件列表也可以暴露
});
</script>
<style scoped>
/* 样式保持不变 */
.big-file-uploader { width: 100%; max-width: 900px; margin: 0 auto; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; }
.control-bar { display: flex; align-items: center; gap: 15px; margin: 20px 0; padding: 15px; background: #f5f7fa; border-radius: 8px; }
.stats { margin-left: auto; font-size: 14px; color: #606266; }
.text-success { color: #67c23a; font-weight: bold; }
.text-danger { color: #f56c6c; font-weight: bold; }
.file-list-container { display: flex; flex-direction: column; gap: 12px; }
.file-item { display: grid; grid-template-columns: 250px 1fr 120px; align-items: center; gap: 20px; padding: 15px; border: 1px solid #ebeef5; border-radius: 6px; background: #fff; transition: all 0.3s; }
.file-item.is-error { border-color: #fde2e2; background-color: #fef0f0; }
.file-info { display: flex; align-items: center; gap: 10px; overflow: hidden; }
.file-icon { font-size: 24px; color: #409eff; flex-shrink: 0; }
.file-meta { display: flex; flex-direction: column; overflow: hidden; }
.file-name { font-size: 14px; color: #303133; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 500; }
.file-size { font-size: 12px; color: #909399; margin-top: 2px; }
.progress-wrapper { width: 100%; }
.speed-text { font-size: 12px; color: #909399; }
.action-area { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; }
@media (max-width: 600px) {
.file-item { grid-template-columns: 1fr; gap: 10px; }
.action-area { flex-direction: row; justify-content: space-between; align-items: center; }
.control-bar { flex-direction: column; align-items: stretch; }
.stats { margin-left: 0; margin-top: 10px; }
} }
</style> </style>
\ No newline at end of file
// 格式化金额为货币格式 // 格式化金额为货币格式
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) { export function numberFormat(value, item) {
......
...@@ -20,78 +20,24 @@ ...@@ -20,78 +20,24 @@
<!-- 统计信息卡片 v-if="statisticsData.totalInAmount > 0"--> <!-- 统计信息卡片 v-if="statisticsData.totalInAmount > 0"-->
<div class="statistics-container"> <div class="statistics-container">
<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.totalInAmount" :formatter="value=>formatCurrency(value)" />
<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> </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.totalPolicyCount" />
<div class="card-content">
<div class="card-label">总保单数</div>
<div class="card-value">
{{ statisticsData.totalPolicyCount ? statisticsData.totalPolicyCount : 0 }}
</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">
{{
statisticsData.totalPremium ? formatCurrency(statisticsData.totalPremium) : 0
}}
</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.pendingOutAmount" :formatter="value=>formatCurrency(value)" />
<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> </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.availableOutAmount" :formatter="value=>formatCurrency(value)" />
<div class="card-content"> </el-col>
<div class="card-label">可出账金额</div> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<div class="card-value"> <el-statistic title="差额(估)" :value="statisticsData.differenceAmount" :formatter="value=>formatCurrency(value)" />
{{
statisticsData.availableOutAmount
? formatCurrency(statisticsData.availableOutAmount)
: 0
}}
</div>
</div>
</el-card>
</el-col> </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> </el-row>
</div> </div>
<el-table <el-table
...@@ -120,7 +66,7 @@ ...@@ -120,7 +66,7 @@
/> />
<el-table-column <el-table-column
prop="commissionPaidRatio" prop="commissionPaidRatio"
label="累积已入账比例" label="累积实佣率"
width="120" width="120"
sortable sortable
:formatter="row => `${row.commissionPaidRatio ? row.commissionPaidRatio : 0}%`" :formatter="row => `${row.commissionPaidRatio ? row.commissionPaidRatio : 0}%`"
......
...@@ -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 '-'
......
...@@ -12,53 +12,23 @@ ...@@ -12,53 +12,23 @@
<!-- 统计信息卡片 --> <!-- 统计信息卡片 -->
<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.totalExpectedAmount" :formatter="value=>formatCurrency(value)" />
<div class="card-content">
<div class="card-label">应出账总金额</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalExpectedAmount) }}</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.totalUnpaidAmount" :formatter="value=>formatCurrency(value)" />
<div class="card-content">
<div class="card-label">待出账金额</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalUnpaidAmount) }}</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.paidAmountRatio" :formatter="value=>formatCurrency(value,'',2)+'%'" />
<div class="card-content">
<div class="card-label">已出账比例</div>
<div class="card-value">{{ statisticsData.paidAmountRatio }}%</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.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="总保费" :value="statisticsData.totalPremiumAmount" :formatter="value=>formatCurrency(value)" />
<div class="card-content">
<div class="card-label">总保费</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalPremiumAmount) }}</div>
</div>
</el-card>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
...@@ -78,57 +48,27 @@ ...@@ -78,57 +48,27 @@
</CommonPage> </CommonPage>
<!-- 查看明细列表 --> <!-- 查看明细列表 -->
<CommonDialog dialogTitle="应付明细" dialogWidth="80%" :openDialog="detailDialogVisible" :showAction="true" <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"> <div class="statistics-container" v-if="detailRecordStatistics.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="detailRecordStatistics.totalExpectedAmount" :formatter="value=>formatCurrency(value)" />
<div class="card-content"> </el-col>
<div class="card-label">应出账总金额</div> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalExpectedAmount) }}</div> <el-statistic title="已出账金额" :value="detailRecordStatistics.totalPaidAmount" :formatter="value=>formatCurrency(value)" />
</div> </el-col>
</el-card> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
</el-col> <el-statistic title="待出账金额" :value="detailRecordStatistics.totalUnpaidAmount" :formatter="value=>formatCurrency(value)" />
<el-col :xs="24" :sm="12" :md="4" :lg="4"> </el-col>
<el-card shadow="hover" class="statistics-card"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<div class="card-content"> <el-statistic title="已出账比例" :value="detailRecordStatistics.paidAmountRatio" :formatter="value=>formatCurrency(value,'',2)+'%'" />
<div class="card-label">已出账金额</div> </el-col>
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalPaidAmount) }}</div> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
</div> <el-statistic title="总保单数" :value="detailRecordStatistics.totalPolicyCount"/>
</el-card> </el-col>
</el-col> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-col :xs="24" :sm="12" :md="4" :lg="4"> <el-statistic title="总保费" :value="detailRecordStatistics.totalPremiumAmount" :formatter="value=>formatCurrency(value)" />
<el-card shadow="hover" class="statistics-card"> </el-col>
<div class="card-content">
<div class="card-label">待出账金额</div>
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalUnpaidAmount) }}</div>
</div>
</el-card>
</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>
<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>
<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>
</el-row> </el-row>
</div> </div>
<el-table :data="payableReportTableData" border style="width: 100%;margin-bottom: 10px;"> <el-table :data="payableReportTableData" border style="width: 100%;margin-bottom: 10px;">
...@@ -158,7 +98,7 @@ ...@@ -158,7 +98,7 @@
</CommonDialog> </CommonDialog>
<!-- 出账记录表格弹窗--> <!-- 出账记录表格弹窗-->
<CommonDialog dialogTitle="出账记录" dialogWidth="80%" :openDialog="payRecordDialogTableVisible" :showAction="true" <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 :data="payRecordDialogTableData" border style="width: 100%">
<el-table-column v-for="item in payRecordDialogTableColumns" :key="item.property" :property="item.property" <el-table-column v-for="item in payRecordDialogTableColumns" :key="item.property" :property="item.property"
:label="item.label" :width="item.width" :formatter="item.formatter" /> :label="item.label" :width="item.width" :formatter="item.formatter" />
...@@ -306,13 +246,13 @@ const searchConfig = ref([ ...@@ -306,13 +246,13 @@ const searchConfig = ref([
}, },
{ {
type: 'select', type: 'select',
prop: 'fortuneName', prop: 'fortuneType',
label: '出账项目', label: '出账项目',
dictType: 'csf_fortune_type' dictType: 'csf_fortune_type'
}, },
{ {
type: 'select', type: 'select',
prop: 'insurerBizId', prop: 'insuranceCompanyBizIdList',
label: '保险公司', label: '保险公司',
api: '/insurance/base/api/insuranceCompany/page', api: '/insurance/base/api/insuranceCompany/page',
keywordField: 'queryContent', keywordField: 'queryContent',
...@@ -321,13 +261,14 @@ const searchConfig = ref([ ...@@ -321,13 +261,14 @@ const searchConfig = ref([
debounceWait: 500, // 自定义防抖时间 debounceWait: 500, // 自定义防抖时间
valueKey: 'insuranceCompanyBizId', valueKey: 'insuranceCompanyBizId',
labelKey: 'fullName', labelKey: 'fullName',
multiple: true,
transform: (res) => { transform: (res) => {
console.log(res) console.log(res)
return res?.data.records || [] return res?.data.records || []
} }
}, { }, {
type: 'select', type: 'select',
prop: 'productLaunchBizId', prop: 'productLaunchBizIdList',
label: '产品计划', label: '产品计划',
api: '/product/api/relProjectProductLaunch/parameter/page', api: '/product/api/relProjectProductLaunch/parameter/page',
keywordField: 'productName', keywordField: 'productName',
...@@ -339,6 +280,7 @@ const searchConfig = ref([ ...@@ -339,6 +280,7 @@ const searchConfig = ref([
debounceWait: 500, // 自定义防抖时间 debounceWait: 500, // 自定义防抖时间
valueKey: 'productLaunchBizId', valueKey: 'productLaunchBizId',
labelKey: 'productName', labelKey: 'productName',
multiple: true,
transform: (res) => { transform: (res) => {
return res?.data.records || [] return res?.data.records || []
} }
...@@ -352,13 +294,14 @@ const searchConfig = ref([ ...@@ -352,13 +294,14 @@ const searchConfig = ref([
] ]
}, { }, {
type: 'select', type: 'select',
prop: 'teamBizId', prop: 'teamBizIdList',
label: '出单团队', label: '出单团队',
api: '/csf/api/team/page', api: '/csf/api/team/page',
keywordField: 'teamName', keywordField: 'teamName',
requestParams: { pageNo: 1, pageSize: 20 }, requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入出单团队名称搜索', placeholder: '输入出单团队名称搜索',
debounceWait: 500, // 自定义防抖时间 debounceWait: 500, // 自定义防抖时间
multiple: true,
valueKey: 'teamBizId', valueKey: 'teamBizId',
labelKey: 'teamName', labelKey: 'teamName',
transform: (res) => { transform: (res) => {
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<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" class="text-center mb-4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="formatCurrency(statisticsData.totalAmount)"> <el-statistic :value="statisticsData.totalAmount" :formatter="value=>formatCurrency(value)">
<template #title> <template #title>
<div style="display: inline-flex; align-items: center"> <div style="display: inline-flex; align-items: center">
应收款总金额 应收款总金额
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
</el-statistic> </el-statistic>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="formatCurrency(statisticsData.totalPaidAmount)"> <el-statistic :value="statisticsData.totalPaidAmount" :formatter="value=>formatCurrency(value)">
<template #title> <template #title>
<div style="display: inline-flex; align-items: center"> <div style="display: inline-flex; align-items: center">
已入账金额 已入账金额
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
</el-statistic> </el-statistic>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="formatCurrency(statisticsData.pendingPaidAmount)"> <el-statistic :value="statisticsData.pendingPaidAmount" :formatter="value=>formatCurrency(value)">
<template #title> <template #title>
<div style="display: inline-flex; align-items: center"> <div style="display: inline-flex; align-items: center">
待入账金额(估) 待入账金额(估)
...@@ -40,13 +40,12 @@ ...@@ -40,13 +40,12 @@
</el-statistic> </el-statistic>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="formatCurrency(statisticsData.paidAmountRatio)"> <el-statistic :value="statisticsData.paidAmountRatio" :formatter="value=>formatCurrency(value,'',2)+'%'">
<template #title> <template #title>
<div style="display: inline-flex; align-items: center"> <div style="display: inline-flex; align-items: center">
已入账比例 已入账比例
</div> </div>
</template> </template>
<template #suffix>%</template>
</el-statistic> </el-statistic>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4"> <el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
...@@ -75,58 +74,60 @@ ...@@ -75,58 +74,60 @@
:showClose="true" @close="detailDialogVisible = false"> :showClose="true" @close="detailDialogVisible = false">
<div class="statistics-container" v-if="detailRecordStatistics.totalPolicyCount > 0"> <div class="statistics-container" v-if="detailRecordStatistics.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 :value="detailRecordStatistics.totalAmount" :formatter="value=>formatCurrency(value)">
<div class="card-content"> <template #title>
<div class="card-label">应入账总金额</div> <div style="display: inline-flex; align-items: center">
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalExpectedAmount) }} 应入账总金额
</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="detailRecordStatistics.totalPaidAmount" :formatter="value=>formatCurrency(value)">
<template #title>
<div style="display: inline-flex; align-items: center">
已入账金额
</div> </div>
</div> </template>
</el-card> </el-statistic>
</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 :value="detailRecordStatistics.totalUnpaidAmount" :formatter="value=>formatCurrency(value)">
<div class="card-content"> <template #title>
<div class="card-label">已入账金额</div> <div style="display: inline-flex; align-items: center">
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalPaidAmount) }} 待入账金额
</div>
</template>
</el-statistic>
</el-col>
<el-col :xs="24" :sm="12" :md="4" class="text-center mb-4">
<el-statistic :value="detailRecordStatistics.paidAmountRatio" :formatter="value=>formatCurrency(value,'',2)+'%'">
<template #title>
<div style="display: inline-flex; align-items: center">
已入账比例
</div> </div>
</div> </template>
</el-card> </el-statistic>
</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 :value="detailRecordStatistics.totalPolicyCount" >
<div class="card-content"> <template #title>
<div class="card-label">待入账金额</div> <div style="display: inline-flex; align-items: center">
<div class="card-value">{{ formatCurrency(detailRecordStatistics.totalUnpaidAmount) }} 总保单数
</div> </div>
</div> </template>
</el-card> </el-statistic>
</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 :value="detailRecordStatistics.totalPremium" :formatter="value=>formatCurrency(value)">
<div class="card-content"> <template #title>
<div class="card-label">已入账比例</div> <div style="display: inline-flex; align-items: center">
<div class="card-value">{{ detailRecordStatistics.paidAmountRatio }}%</div> 总保费
</div>
</el-card>
</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>
<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.totalPremium) }}
</div> </div>
</div> </template>
</el-card> </el-statistic>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<el-table :data="receivableReportTableData" border style="width: 100%;margin-bottom: 10px;min-height: 300px;"> <el-table :data="receivableReportTableData" border style="width: 100%;margin-bottom: 10px;min-height: 300px;">
...@@ -160,8 +161,8 @@ ...@@ -160,8 +161,8 @@
<el-table-column v-for="item in entryRecordDialogTableColumns" :key="item.property" <el-table-column v-for="item in entryRecordDialogTableColumns" :key="item.property"
:prop="item.property" :label="item.label" :width="item.width" :formatter="item.formatter" /> :prop="item.property" :label="item.label" :width="item.width" :formatter="item.formatter" />
<el-table-column fixed="right" label="操作" min-width="120"> <el-table-column fixed="right" label="操作" min-width="120">
<template #default> <template #default="scope">
<el-button link type="primary" size="small" @click="handleClick"> <el-button link type="primary" size="small" @click="handleClick(scope.row)">
查看比对记录 查看比对记录
</el-button> </el-button>
</template> </template>
...@@ -173,7 +174,7 @@ ...@@ -173,7 +174,7 @@
:showClose="true" @close="actionRecordsDialogVisible = false"> :showClose="true" @close="actionRecordsDialogVisible = false">
<el-table :data="actionRecordsDialogTableData" border style="width: 100%"> <el-table :data="actionRecordsDialogTableData" border style="width: 100%">
<el-table-column v-for="item in actionRecordsDialogTableColumns" :key="item.property" <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> </el-table>
</CommonDialog> </CommonDialog>
...@@ -706,7 +707,7 @@ const handleSelect = async (e, row) => { ...@@ -706,7 +707,7 @@ const handleSelect = async (e, row) => {
// 查看比对记录 // 查看比对记录
const handleClick = async () => { const handleClick = async (row) => {
actionRecordsDialogVisible.value = true actionRecordsDialogVisible.value = true
actionRecordsDialogTableColumns.value = [ actionRecordsDialogTableColumns.value = [
{ property: 'checkMonth', label: '检核年月', width: '100' }, { property: 'checkMonth', label: '检核年月', width: '100' },
...@@ -718,7 +719,7 @@ const handleClick = async () => { ...@@ -718,7 +719,7 @@ const handleClick = async () => {
{ property: 'userName', label: '比对人', width: '150' } { property: 'userName', label: '比对人', width: '150' }
] ]
// 加载真实数据 // 加载真实数据
const records = await loadEntryEditRecordData(selectedRow.value.commissionExpectedBizId) const records = await loadEntryEditRecordData(row.commissionBizId)
actionRecordsDialogTableData.value = records.length ? records : [] actionRecordsDialogTableData.value = records.length ? records : []
} }
...@@ -847,7 +848,8 @@ const receivedFortuneListData = async () => { ...@@ -847,7 +848,8 @@ const receivedFortuneListData = async () => {
totalPaidAmount: response.data.expectedStatisticsVO.totalPaidAmount, totalPaidAmount: response.data.expectedStatisticsVO.totalPaidAmount,
pendingPaidAmount: response.data.expectedStatisticsVO.pendingPaidAmount, pendingPaidAmount: response.data.expectedStatisticsVO.pendingPaidAmount,
paidAmountRatio: response.data.expectedStatisticsVO.paidAmountRatio, paidAmountRatio: response.data.expectedStatisticsVO.paidAmountRatio,
totalPolicyCount: response.data.expectedStatisticsVO.totalPolicyCount totalPolicyCount: response.data.expectedStatisticsVO.totalPolicyCount,
totalPremium: response.data.expectedStatisticsVO.totalPremium
} }
} catch (error) { } catch (error) {
console.error('加载数据失败:', error) console.error('加载数据失败:', error)
......
...@@ -192,10 +192,17 @@ ...@@ -192,10 +192,17 @@
:showAction="true" :showAction="true"
:showClose="true" :showClose="true"
@close="((fileUploadDialogFlag = false), (files = ''))" @close="((fileUploadDialogFlag = false), (files = ''))"
@confirm="((fileUploadDialogFlag = false), (files = ''))" @confirm="handleDialogClose"
> >
<!-- <FileUploader/> --> <FileUploader
<FileUpload :tenant-biz-id="userStore.projectInfo.tenantBizId"
:project-biz-id="userStore.projectInfo.projectBizId"
:object-biz-id="props.policyBizId"
:object-type="'DOCUMENT'"
:upload-concurrency="3"
ref="uploaderRef"
/>
<!-- <FileUpload
v-model="files" v-model="files"
:data="{ obiectTableName: 'policy_follow', objectBizId: props.policyBizId }" :data="{ obiectTableName: 'policy_follow', objectBizId: props.policyBizId }"
:file-type="['xlsx', 'xls', 'doc', 'docx', 'pdf', 'txt', 'jpg', 'jpeg', 'png', 'gif']" :file-type="['xlsx', 'xls', 'doc', 'docx', 'pdf', 'txt', 'jpg', 'jpeg', 'png', 'gif']"
...@@ -204,7 +211,7 @@ ...@@ -204,7 +211,7 @@
:fileSize="15" :fileSize="15"
:name="'files'" :name="'files'"
@uploadEnd="handleUploadEnd" @uploadEnd="handleUploadEnd"
/> /> -->
</CommonDialog> </CommonDialog>
</div> </div>
</template> </template>
...@@ -266,7 +273,17 @@ const props = defineProps({ ...@@ -266,7 +273,17 @@ const props = defineProps({
default: '' default: ''
} }
}) })
const uploaderRef = ref(null);
// 【关键】当弹窗关闭时,强制清空上传组件列表
const handleDialogClose = () => {
if (uploaderRef.value) {
// 调用子组件暴露的清空方法
// 你需要在子组件中使用 defineExpose 暴露 clearList 方法
uploaderRef.value.clearList();
fileUploadDialogFlag.value = false
getAttachmentListDetail(props.policyBizId)
}
};
const emit = defineEmits(['update:modelValue', 'submit', 'cancel', 'saveRow']) const emit = defineEmits(['update:modelValue', 'submit', 'cancel', 'saveRow'])
const introducerTableData = ref([]) const introducerTableData = ref([])
...@@ -896,7 +913,7 @@ const attachmentTableColumns = ref([ ...@@ -896,7 +913,7 @@ const attachmentTableColumns = ref([
prop: 'originalName', prop: 'originalName',
label: '文件名', label: '文件名',
sortable: true, sortable: true,
width: '150', width: '300',
formatter: row => row.originalName || '-' formatter: row => row.originalName || '-'
}, },
{ {
...@@ -910,8 +927,8 @@ const attachmentTableColumns = ref([ ...@@ -910,8 +927,8 @@ const attachmentTableColumns = ref([
prop: 'createTime', prop: 'createTime',
label: '上传时间', label: '上传时间',
sortable: true, sortable: true,
width: '150', width: '200',
formatter: row => row.createTime || '-' formatter: row => formatToDateTime(row.createTime || '-')
}, },
{ {
prop: 'creatorName', prop: 'creatorName',
......
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