Commit 8ad5356f authored by 沈翠玲's avatar 沈翠玲

案件分配和稽核审批

parent 1222e845
import request from '@/utils/http/index';
// 获取未分的资产统计
// 获取未分的资产统计
export const getLoanDistribute = (params) => {
return request.get('/LoanDistribute/totalLoan', params);
};
......@@ -11,19 +11,19 @@ export const getLoantotal = (params) => {
export const getLoanpage = (params) => {
return request.get('/LoanDistribute/page', params);
};
// 分到调节中心
// 分到调节中心
export const distributeLoan = (tenantId, data) => {
return request.post(`/LoanDistribute/distribute?toTenantId=${tenantId}`, data);
};
// 手动调整分CPE,确认
// 手动调整分CPE,确认
export const confirm = (data) => {
return request.post(`/LoanDistribute/confirm`, data);
};
// 系统自动分配; 分配到CPE
// 系统自动分派; 分派到CPE
export const distributeCpe = (data) => {
return request.post(`/LoanDistribute/distributeCpe?type=${data.type}`, data);
};
// 手动调整分CPE
// 手动调整分CPE
export const changeDistributeCpe = (data) => {
return request.post(`/LoanDistribute/changeDistributeCpe`, data);
};
......
......@@ -105,7 +105,7 @@
"component": "/property/case-allocation/index",
"meta": {
"icon": "",
"title": "案件分",
"title": "案件分",
"isLink": "",
"isHide": false,
"isFull": false,
......
<template>
<el-drawer
v-model="showModal"
title="拨打跟进记录"
:size="850"
@close="showModal = false"
direction="rtl"
:before-close="onHide"
>
<div class="h-full flex-col flex mydrawer pb-2">
<div>
<p class="font-bold mb-2">联系人信息:</p>
<div class="flex justify-between">
<div>
<span class="text-gray-400">姓名:</span>
<span>{{ currentInfo.guarantor.name }}</span>
</div>
<div>
<span class="text-gray-400">与案人关系:</span>
<span>{{ currentInfo.guarantor.kinship }}</span>
</div>
<div>
<span class="text-gray-400">联系号码:</span>
<span>{{ currentInfo.guarantor.phone }}</span>
</div>
<div>
<span class="text-gray-400">号码状态:</span>
<span>
<el-radio-group v-model="currentInfo.guarantor.status" disabled>
<el-radio value="N">无效</el-radio>
<el-radio value="Y">有效</el-radio>
</el-radio-group>
</span>
</div>
</div>
</div>
<div>
<ProTable :config="config" :data="tabledata" :showPagination="false" :showToolBar="false">
<template #table_top>
<p class="font-bold">关联案件:</p>
</template>
</ProTable>
</div>
<div class="flex">
<div class="mt-3 pr-8 w-1/2">
<p class="font-bold mb-2">跟进信息:</p>
<el-form
ref="formRef"
inline
:model="form"
:rules="rules"
label-width="110px"
label-position="left"
>
<el-row>
<el-col :span="24">
<el-form-item class="w-full" label="跟进时间:" prop="name">
<el-date-picker
v-model="form.trackTime"
disabled
class="w-full"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
type="datetime"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item class="w-full" label="下次跟进时间:" prop="code">
<div>
<el-date-picker
v-model="form.nextTime"
disabled
class="w-full"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
type="datetime"
/>
<div>
<el-button size="small" disabled @click="changeLastTime('tomorrow')"
>明天</el-button
>
<el-button size="small" disabled @click="changeLastTime('tomorrowDay')"
>后天</el-button
>
<el-button size="small" disabled @click="changeLastTime('tomorrow2Day')"
>2天后</el-button
>
</div>
</div>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item
class="w-full"
style="display: block"
label="跟进结果:"
prop="name"
label-position="top"
>
<el-tabs v-model="resuleObj.label" class="w-full">
<el-tab-pane
:label="item.label"
:name="item.label"
v-for="(item, index) in tabObj"
:key="`tab${index}`"
>
<el-button
size="small"
:type="resuleObj.childrenlabel === item1.value ? 'primary' : null"
plain
v-for="(item1, index1) in item.children"
:key="`tabchild${index1}`"
disabled
@click="resuleObj.childrenlabel = item1.value"
>{{ item1.label }}</el-button
>
</el-tab-pane>
</el-tabs>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item class="w-full" style="display: block" label="跟进状态:" prop="code">
<el-button
size="small"
style="margin-left: 0"
class="mr-2 mb-2"
disabled
:type="form.phoneResultStatus === item1.value ? 'primary' : null"
plain
v-for="(item1, index1) in statusArr"
:key="`btn${index1}`"
@click="form.phoneResultStatus = item1.value"
>{{ item1.label }}</el-button
>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item class="w-full" label="跟进备注:" prop="code" style="display: block">
<el-input
v-model="form.remark"
class="w-full"
disabled
:rows="2"
type="textarea"
placeholder="请输入"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<div class="w-1/2">
<p class="font-bold mb-2 mt-2">跟进附件:</p>
<el-form inline :model="form" :rules="rules" label-width="110px" label-position="left">
<el-form-item class="w-full" label="通话录音:" prop="code">
<el-icon>
<Download
v-for="(item, index) in form.voices"
:key="index"
@click="download(item)"
/>
</el-icon>
<el-upload
class="avatar-uploader"
:action="url"
:on-success="handleFileSuccess"
:on-remove="handleRemove"
:disabled="props.mode !== 'handle'"
:auto-upload="true"
>
<el-button type="primary" plain :icon="Upload" :disabled="props.mode !== 'handle'"
>上传录音文件</el-button
>
</el-upload>
<!-- 进度条 -->
<!-- <el-progress v-if="progressFlag" :percentage="loadProgress" /> -->
</el-form-item>
<el-form-item label="微信图片附件:" prop="code" class="w-full">
<el-upload
:action="url"
list-type="picture-card"
v-model:file-list="form.images"
class="mypicture"
:on-preview="handlePictureCardPreview"
:on-success="handleFileSuccess1"
:disabled="props.mode !== 'handle'"
:on-remove="handleRemove1"
>
<div class="text-center">
<el-icon><Plus /></el-icon>
<div>微信相关附件</div>
</div>
</el-upload>
</el-form-item>
<el-form-item label="短信图片附件:" prop="code" class="w-full">
<el-upload
:action="url"
list-type="picture-card"
class="mypicture"
v-model:file-list="form.notes"
:disabled="props.mode !== 'handle'"
:on-preview="handlePictureCardPreview"
:on-success="handleFileSuccess2"
:on-remove="handleRemove2"
>
<div class="text-center">
<el-icon><Plus /></el-icon>
<div>短信相关附件</div>
</div>
</el-upload>
</el-form-item>
<el-form-item label="其他图片附件:" prop="code" class="w-full">
<el-upload
:action="url"
list-type="picture-card"
v-model:file-list="form.others"
:disabled="props.mode !== 'handle'"
class="mypicture"
:on-preview="handlePictureCardPreview"
:on-success="handleFileSuccess3"
:on-remove="handleRemove3"
>
<div class="text-center">
<el-icon><Plus /></el-icon>
<div>其他相关附件</div>
</div>
</el-upload>
</el-form-item>
</el-form>
</div>
</div>
</div>
<template #footer>
<div style="flex: auto">
<el-button @click="showModal = false">取消</el-button>
<el-button type="primary" @click="submitForm" v-if="props.mode === 'handle'"
>确定</el-button
>
<template v-else>
<el-button type="primary" @click="submitForm('passed')">通过</el-button>
<el-button type="danger" @click="submitForm('rejected')">拒绝</el-button>
</template>
</div>
</template>
<el-dialog v-model="dialogVisible">
<img w-full :src="dialogImageUrl" alt="Preview Image" />
</el-dialog>
</el-drawer>
</template>
<script setup lang="jsx" name="reduceDrawer">
import dayjs from 'dayjs';
import { computed, inject } from 'vue';
import { reactive, ref } from 'vue';
import { ElInputNumber, ElMessage } from 'element-plus';
import { Upload, Download } from '@element-plus/icons-vue';
import { getAppEnvConfig } from '@/utils/env';
import { saveTrackRecord } from '@/api/property';
import { auditAudit } from '@/api/audit';
const envs = getAppEnvConfig();
const downloadfile = inject('download');
const url = envs.VITE_GLOB_API_URL_PREFIX + '/sys/upload';
const showModal = ref(false);
const editFirst = ref(false);
const tabledata = ref([]);
const progressFlag = ref(false);
const loadProgress = ref(0);
const dialogImageUrl = ref('');
const dialogVisible = ref(false);
const props = defineProps({
mode: String,
});
const emits = defineEmits(['success']);
const tabObj = [
{
label: '本人可联',
children: [
{ label: '接通后挂断', value: 'hang_up' },
{ label: '接通有实质进展', value: 'progress' },
],
},
{
label: '联系人可联',
children: [
{ label: '接通有效转告', value: 'pass_on' },
{ label: '接通拒绝转告', value: 'no_pass' },
{ label: '接听后挂断', value: 'Hang_up_after_answering' },
{ label: '接通无应答', value: 'No_response_when_connected' },
{ label: '称与债人无关', value: 'Claims_unrelated_to_creditors' },
{ label: '称非本人', value: 'Claiming_not_to_be_myself' },
],
},
{
label: '未接听',
children: [
{ label: '无人接听', value: 'no_answer' },
{ label: '关机', value: 'Shutdown' },
{ label: '空号', value: 'dead_number' },
{ label: '占线/忙音/正在通话中', value: 'Busy' },
{ label: '停机', value: 'closing_down' },
{ label: '机器人回复', value: 'Robot_reply' },
],
},
{
label: '非电联',
children: [
{ label: '微信', value: 'WeChat' },
{ label: '短信', value: 'short_message' },
{ label: 'QQ', value: 'QQ' },
{ label: '飞书', value: 'fly' },
{ label: '钉钉', value: 'DING' },
],
},
];
const resuleObj = reactive({
label: '本人可联',
childrenlabel: null,
});
const form = reactive({
sum: 0,
images: [],
notes: [],
phoneResultStatus: '',
others: [],
voices: [],
remark: '',
trackTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
nextTime: null,
});
const currentInfo = ref({ name: 0, kinship: 0, phone: 0, status: '' });
const onHide = (done) => {
done();
};
const statusArr = [
{
label: '后续再跟进',
value: 'later',
},
{
label: '承诺还款',
value: 'Promise_Repayment',
},
{
label: '暂无还款意愿',
value: 'No_Repay',
},
{
label: '要求停催',
value: 'Stop_Urging',
},
{
label: '情绪激动抗拒',
value: 'resistance',
},
{
label: '拒绝还款',
value: 'Refuse_Repayment',
},
{
label: '已还款',
value: 'Repaired',
},
{
label: '代履行还款',
value: 'repayment_others',
},
];
const selectdList = ref([]);
const onCheckboxChange = (row) => {
selectdList.value = row.records;
};
const handleRemove = (uploadFile, uploadFiles) => {
const index = form.voices.findIndex((v) => v.name === uploadFile.name);
form.voices.splice(index, 1);
};
const handlePictureCardPreview = (uploadFile) => {
dialogImageUrl.value = uploadFile.url;
dialogVisible.value = true;
};
const changeLastTime = (day) => {
if (day === 'tomorrow') {
form.nextTime = dayjs().add(1, 'day').format('YYYY-MM-DD HH:mm:ss');
} else if (day === 'tomorrowDay') {
form.nextTime = dayjs().add(2, 'day').format('YYYY-MM-DD HH:mm:ss');
} else {
form.nextTime = dayjs().add(3, 'day').format('YYYY-MM-DD HH:mm:ss');
}
};
const handleFileSuccess = (response, file, fileList) => {
if (file.uid) {
const item = form.voices.find((v) => v.uid === file.uid);
item.url = envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/' + response.message;
}
};
const handleFileSuccess1 = (response, file, fileList) => {
if (file.uid) {
const item = form.images.find((v) => v.uid === file.uid);
item.url = envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/' + response.message;
}
};
const handleRemove1 = (uploadFile, uploadFiles) => {
const index = form.images.findIndex((v) => v.name === uploadFile.name);
form.images.splice(index, 1);
};
const handleFileSuccess2 = (response, file, fileList) => {
// console.log(response, file);
if (file.uid) {
const item = form.notes.find((v) => v.uid === file.uid);
item.url = envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/' + response.message;
}
};
const handleRemove2 = (uploadFile, uploadFiles) => {
const index = form.notes.findIndex((v) => v.name === uploadFile.name);
form.notes.splice(index, 1);
};
const handleFileSuccess3 = (response, file, fileList) => {
if (file.uid) {
const item = form.others.find((v) => v.uid === file.uid);
item.url = envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/' + response.message;
}
};
const download = (item) => {
const name = item.slice(item.lastIndexOf('/') + 1, item.length);
downloadfile(envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/' + item, {}, name);
};
const handleRemove3 = (uploadFile, uploadFiles) => {
const index = form.others.findIndex((v) => v.name === uploadFile.name);
form.others.splice(index, 1);
};
const config = reactive({
minHeight: 200,
columns: [
// { type: 'checkbox', width: 50 },
{
field: 'caseId',
title: '案件ID',
showOverflow: 'tooltip',
},
{
field: 'product',
showOverflow: 'tooltip',
title: '产品',
},
{
field: 'loanPlatform.name',
showOverflow: 'tooltip',
title: '借款机构',
},
{
field: 'commissionAmount',
showOverflow: 'tooltip',
title: '委案金额',
},
{
field: 'sumReductionAmount',
showOverflow: 'tooltip',
title: '累计减免金额',
},
{
field: 'sumRepayAmount',
showOverflow: 'tooltip',
title: '累计还款金额',
},
{
field: 'remainingAmount',
showOverflow: 'tooltip',
title: '剩余待还金额',
},
],
onCheckboxChange: onCheckboxChange,
toolbarConfig: { enabled: false },
});
const openModal = (info) => {
showModal.value = true;
currentInfo.value = info;
form.trackTime = info.trackTime;
form.remark = info.remark;
form.phoneResultStatus = info.phoneResultStatus;
resuleObj.childrenlabel = info.followStatus;
form.nextTime = info.nextTime;
form.notes = info.notes
? info.notes.map((v) => ({
name: v.slice(v.lastIndexOf('/') + 1, v.length),
url: envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/' + v,
}))
: [];
form.voices = info.voices
? info.voices.map((v) => ({
name: v.slice(v.lastIndexOf('/') + 1, v.length),
url: envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/' + v,
}))
: [];
form.images = info.images
? info.images.map((v) => ({
name: v.slice(v.lastIndexOf('/') + 1, v.length),
url: envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/' + v,
}))
: [];
form.others = info.others
? info.others.map((v) => ({
name: v.slice(v.lastIndexOf('/') + 1, v.length),
url: envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/' + v,
}))
: [];
tabledata.value = info.loans;
console.log('formform', form);
};
const submitForm = (type) => {
let voices = JSON.parse(JSON.stringify(form.voices.map((v) => v.url)));
let images = JSON.parse(JSON.stringify(form.images.map((v) => v.url)));
let notes = JSON.parse(JSON.stringify(form.notes.map((v) => v.url)));
let others = JSON.parse(JSON.stringify(form.others.map((v) => v.url)));
voices = voices.map((v) => {
return v.replace(envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/', '');
});
images = images.map((v) => {
return v.replace(envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/', '');
});
notes = notes.map((v) => {
return v.replace(envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/', '');
});
others = others.map((v) => {
return v.replace(envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/', '');
});
saveTrackRecord({
loans: tabledata.value,
id: currentInfo.value.id,
guarantor: currentInfo.value.guarantor,
trackTime: form.trackTime,
nextTime: form.nextTime,
phoneResultStatus: form.phoneResultStatus,
auditStatus: type === 'rejected' ? 'rejected' : type === 'passed' ? 'passed' : 'complete',
followStatus: resuleObj.childrenlabel,
remark: form.remark,
voices: voices.length > 0 ? voices : null,
images: images.length > 0 ? images : null,
notes: notes.length > 0 ? notes : null,
others: others.length > 0 ? others : null,
}).then((res) => {
if (res.success) {
ElMessage.success({
message: '保存成功',
plain: true,
});
showModal.value = false;
emits('success');
}
});
};
defineExpose({
openModal,
});
</script>
<style lang="scss" scoped>
.mydrawer {
:deep(.card) {
padding: 0;
border: none;
}
:deep(.el-date-editor) {
width: 100%;
}
.el-form-item {
margin-right: 0;
}
}
.mypicture {
:deep(.el-upload--picture-card) {
width: 80px;
height: 80px;
}
:deep(.el-upload-list__item) {
width: 80px;
height: 80px;
}
:deep(.el-upload--picture-card) {
font-size: 12px;
}
}
</style>
<template>
<div class="table-box">
<ProTable :config="config" ref="ProTableRef" :api="getTrackRecord"
:paramCallback="paramCallback">
</ProTable>
<callDrawer ref="callDrawerRef" @success="query()" :mode="callMode"></callDrawer>
<el-image-viewer :url-list="srcList" v-if="RefImage" @close="RefImage = false" />
</div>
</template>
<script setup name="systemLog" lang="jsx">
import { computed } from 'vue';
import { reactive, ref } from 'vue';
import { getTrackRecord } from '@/api/property';
import { onMounted } from 'vue';
import { getAppEnvConfig } from '@/utils/env';
import { ElMessageBox, ElMessage, ElButton, ElTag } from 'element-plus';
import callDrawer from './components/callDrawer.vue';
import { Download } from '@element-plus/icons-vue';
import { inject } from 'vue';
const envs = getAppEnvConfig();
const downloadfile = inject('download');
const ProTableRef = ref();
const callMode = ref('');
const showModal = ref(false);
const selectdList = ref([]);
const onCheckboxChange = (row) => {
console.log('row', row);
selectdList.value = row.records;
};
const callDrawerRef = ref();
const srcList = ref([]);
const RefImage = ref(false);
const options = [
{
value: 2,
label: '2',
},
{
value: 3,
label: '3',
},
{
value: 4,
label: '4',
},
{
value: 5,
label: '5',
},
{
value: 6,
label: '6',
},
];
const preview = (item, type) => {
const list = [];
item[type].forEach((v) => {
list.push(envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/' + v);
});
RefImage.value = true;
srcList.value = list;
};
const download = (item) => {
const name = item.slice(item.lastIndexOf('/') + 1, item.length);
downloadfile(envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/' + item, {}, name);
};
const auditStatusOpt = [
{ label: '未稽核', value: 'un_audit' },
{ label: '稽核中', value: 'audit' },
{ label: '已拒绝', value: 'rejected' },
{ label: '已完成', value: 'complete' },
{ label: '稽核通过', value: 'passed' },
];
const changeStatus = async () => {
showModal.value = true;
};
const submitForm = async (type) => {
const ids = selectdList.value.map((v) => v.id).join(',');
await byStagesflowStatusByIds({
ids: ids,
flowStatus: type,
});
ElMessage.success({
message: '审核成功',
plain: true,
});
showModal.value = false;
query();
};
const callTelephone = (row, type) => {
callMode.value = type;
callDrawerRef.value.openModal(JSON.parse(JSON.stringify(row)));
};
const followStatusOpt = [
{ label: '接通后挂断', value: 'hang_up' },
{ label: '接通有实质进展', value: 'progress' },
{ label: '接通有效转告', value: 'pass_on' },
{ label: '接通拒绝转告', value: 'no_pass' },
{ label: '接听后挂断', value: 'Hang_up_after_answering' },
{ label: '接通无应答', value: 'No_response_when_connected' },
{ label: '称与债人无关', value: 'Claims_unrelated_to_creditors' },
{ label: '称非本人', value: 'Claiming_not_to_be_myself' },
{ label: '无人接听', value: 'no_answer' },
{ label: '关机', value: 'Shutdown' },
{ label: '空号', value: 'dead_number' },
{ label: '占线/忙音/正在通话中', value: 'Busy' },
{ label: '停机', value: 'closing_down' },
{ label: '机器人回复', value: 'Robot_reply' },
{ label: '微信', value: 'WeChat' },
{ label: '短信', value: 'short_message' },
{ label: 'QQ', value: 'QQ' },
{ label: '飞书', value: 'fly' },
{ label: '钉钉', value: 'DING' },
];
const phoneResultStatusOpt = [
{ label: '后续再跟进', value: 'later' },
{ label: '承诺还款', value: 'Promise_Repayment' },
{ label: '暂无还款意愿', value: 'No_Repay' },
{ label: '要求停催', value: 'Stop_Urging' },
{ label: '情绪激动抗拒', value: 'resistance' },
{ label: '拒绝还款', value: 'Refuse_Repayment' },
{ label: '已还款', value: 'Repaired' },
{ label: '代履行还款', value: 'repayment_others' },
];
const caseStatusOpt = [
{ label: '正常', value: 'normal' },
{ label: '撤案', value: 'withdraw' },
{ label: '留案', value: 'stay' },
];
const paramCallback = (param) => {
const obj = JSON.parse(JSON.stringify(param));
obj['auditStatus'] = 'complete';
if (obj['trackTime']) {
if (obj['trackTime'][0]) obj['trackTimeBegin'] = obj['trackTime'][0];
if (obj['trackTime'][1]) obj['trackTimeEnd'] = obj['trackTime'][1];
delete obj['trackTime'];
}
if (obj['nextTime']) {
if (obj['nextTime'][0]) obj['nextTrackTimeBegin'] = obj['nextTime'][0];
if (obj['nextTime'][1]) obj['nextTrackTimeEnd'] = obj['nextTime'][1];
delete obj['nextTime'];
}
// obj['voices'] = 'Y'
// obj['images'] = 'Y'
// obj['notes'] = 'Y'
// obj['others'] = 'Y'
return obj;
};
const config = computed(() => {
return {
rowStyle ({ row }) {
if (row.auditDesc === 'time_out') {
return {
backgroundColor: 'rgb(242,217,217)'
}
}
},
columns: [
{ field: 'id', title: '跟进记录ID', showOverflow: 'tooltip', width: 90 },
{
field: 'caseId',
title: '案件ID',
showOverflow: 'tooltip',
width: 80,
search: { el: 'input', props: { clearable: true }, labelWidth: 78 },
slots: {
default: ({ row }) => {
return <>{row.loans.map((v) => v.caseId).join(',')}</>;
},
},
},
{
field: 'trackTime',
title: '跟进日期',
showOverflow: 'tooltip',
width: 100,
search: {
el: 'date-picker',
props: { type: 'daterange', valueFormat: 'YYYY-MM-DD' },
labelWidth: 78,
},
},
{
field: 'nextTime',
title: '下次跟进时间',
showOverflow: 'tooltip',
width: 110,
search: {
el: 'date-picker',
props: { type: 'daterange', valueFormat: 'YYYY-MM-DD' },
labelWidth: 78,
},
},
{
field: 'guarantor.name',
title: '联系人姓名',
showOverflow: 'tooltip',
width: 110,
search: { el: 'input', props: { clearable: true }, key: 'guarantorName', labelWidth: 78 },
},
{
field: 'guarantor.phone',
title: '联系人号码',
showOverflow: 'tooltip',
width: 110,
search: {
el: 'input',
props: { clearable: true },
key: 'guarantorPhone',
labelWidth: 78,
},
labelWidth: 78,
},
{
field: 'followStatus',
title: '拨打跟进结果',
showOverflow: 'tooltip',
width: 120,
enum: followStatusOpt,
search: {
el: 'select',
props: { filterable: true, multiple: true, 'collapse-tags': true },
labelWidth: 78,
defaultValue: [
'progress',
'pass_on',
'no_pass',
'Claims_unrelated_to_creditors',
'Claiming_not_to_be_myself',
],
},
fieldNames: { label: 'label', value: 'value' },
slots: {
default: ({ row }) => {
return (
<>
{row.followStatus
? followStatusOpt.find((v) => v.value === row.followStatus).label
: ''}
</>
);
},
},
},
{
field: 'phoneResultStatus',
title: '拨打处置状态',
showOverflow: 'tooltip',
width: 120,
enum: phoneResultStatusOpt,
search: { el: 'select', props: { filterable: true }, labelWidth: 78 },
fieldNames: { label: 'label', value: 'value' },
slots: {
default: ({ row }) => {
return (
<>
{row.phoneResultStatus
? phoneResultStatusOpt.find((v) => v.value === row.phoneResultStatus).label
: ''}
</>
);
},
},
},
{
field: 'trackRecord.auditStatus',
title: '稽核状态',
showOverflow: 'tooltip',
width: 80,
slots: {
default: ({ row }) => {
return (
<>
{row.auditStatus
? auditStatusOpt.find((v) => v.value === row.auditStatus).label
: ''}
</>
);
},
},
},
{
field: 'remark',
title: '备注',
showOverflow: 'tooltip',
width: 80,
search: { el: 'input', props: { clearable: true }, labelWidth: 78 },
},
{
field: 'voices',
title: '通话录音',
showOverflow: 'tooltip',
width: 80,
slots: {
default: ({ row, rowIndex }) => {
if (row.voices && row.voices.length > 0) {
return (
<>
{row.voices.map((item, index) => (
<el-icon>
<Download onClick={() => download(row)} />
</el-icon>
))}
</>
);
}
},
},
},
{
field: 'status',
title: '超时状态',
width: 100,
search: { el: 'select', props: { clearable: true } },
slots: {
default: ({ row }) => {
return (
<ElTag type={row.auditDesc == 'time_out' ? 'danger' : 'primary'}>
{row.auditDesc == 'time_out' ? '已超时' : '正常'}
</ElTag>
);
},
},
},
{
field: 'code',
title: '微信图片附件',
showOverflow: 'tooltip',
width: 100,
slots: {
default: ({ row, rowIndex }) => {
if (row.images && row.images.length > 0) {
return (
<>
<div className="flex justify-center">
<ElButton onClick={() => preview(row, 'images')} link type="primary">
查看
</ElButton>
</div>
</>
);
}
},
},
},
{
field: 'code',
title: '短信图片附件',
showOverflow: 'tooltip',
width: 100,
slots: {
default: ({ row, rowIndex }) => {
if (row.notes && row.notes.length > 0) {
return (
<>
<div className="flex justify-center">
<ElButton onClick={() => preview(row, 'notes')} link type="primary">
查看
</ElButton>
</div>
</>
);
}
},
},
},
{
field: 'code',
title: '其他图片附件',
showOverflow: 'tooltip',
width: 100,
slots: {
default: ({ row, rowIndex }) => {
if (row.others && row.others.length > 0) {
return (
<>
<div className="flex justify-center">
<ElButton onClick={() => preview(row, 'others')} link type="primary">
查看
</ElButton>
</div>
</>
);
}
},
},
},
{
field: 'code',
title: '操作',
width: 80,
slots: {
default: ({ row, rowIndex }) => {
return (
<>
<ElButton type="primary" onClick={() => callTelephone(row, 'audit')}>
审核
</ElButton>
</>
);
},
},
},
],
onCheckboxChange: onCheckboxChange,
};
});
const query = () => ProTableRef.value?.search();
onMounted(() => {
query();
});
</script>
<style lang="scss" scoped>
.expand-box {
background: #fff;
padding: 10px;
.expand-table {
color: #000;
table {
width: 100%;
margin-top: 10px;
border-collapse: collapse;
}
td {
border: 1px solid rgba(5, 5, 5, 0.06);
background: #fff;
padding: 8px;
width: 240px;
&.label {
background: #f5f7f9;
}
}
}
}
</style>
......@@ -51,7 +51,7 @@
<td>{{ item?.cpe.username }}</td>
</tr>
<tr>
<td class="label">CPE日期:</td>
<td class="label">CPE日期:</td>
<td>{{ item?.cpeDate }}</td>
<td class="label">本金余额:</td>
<td>{{ item?.principalBalance }}</td>
......
......@@ -47,7 +47,7 @@
<tr>
<td class="label">CPE:</td>
<td>{{ item?.loan?.cpe.username }}</td>
<td class="label">CPE日期:</td>
<td class="label">CPE日期:</td>
<td>{{ item?.loan?.cpeDate }}</td>
<td class="label">本金余额:</td>
<td>{{ item?.loan?.principalBalance }}</td>
......
......@@ -576,5 +576,8 @@
width: 80px;
height: 80px;
}
:deep(.el-upload--picture-card) {
font-size: 12px;
}
}
</style>
......@@ -40,7 +40,7 @@
import { inject } from 'vue';
import { computed } from 'vue';
import callDrawer from './components/callDrawer.vue';
import { ElMessageBox, ElButton } from 'element-plus';
import { ElMessageBox, ElButton, ElTag } from 'element-plus';
import { onMounted } from 'vue';
import { getAppEnvConfig } from '@/utils/env';
import { getTrackRecord } from '@/api/property';
......@@ -129,6 +129,13 @@
};
const config = computed(() => {
return {
rowStyle ({ row }) {
if (row.auditDesc === 'time_out') {
return {
backgroundColor: 'rgb(242,217,217)'
}
}
},
columns: [
{ type: 'checkbox', title: '', width: 40 },
{ field: 'id', title: '跟进记录ID', showOverflow: 'tooltip', width: 90 },
......@@ -303,6 +310,21 @@
},
},
},
{
field: 'status',
title: '超时状态',
width: 100,
search: { el: 'select', props: { clearable: true } },
slots: {
default: ({ row }) => {
return (
<ElTag type={row.auditDesc == 'time_out' ? 'danger' : 'primary'}>
{row.auditDesc == 'time_out' ? '已超时' : '正常'}
</ElTag>
);
},
},
},
{
field: 'code',
title: '短信图片附件',
......
<template>
<vxe-modal
v-model="showModal"
title="案件分"
title="案件分"
@hide="onHide"
height="582"
width="1003"
......@@ -10,19 +10,55 @@
>
<div class="allocation-wrap">
<div class="Content">
<div class="top">
<div class="number-box">
<div class="number-left">
<div class="icon-wrapper">
<el-icon><UserFilled size="850" /></el-icon>
</div>
</div>
<div class="number-right">
<p>待分派客户数量</p>
<p>{{ statisis.unCustomerNum }}</p>
</div>
</div>
<div class="number-box">
<div class="number-left">
<div class="icon-wrapper">
<el-icon><List /></el-icon>
</div>
</div>
<div class="number-right">
<p>待分派案件数量</p>
<p>{{ statisis.unCaseNum }}</p>
</div>
</div>
<div class="number-box">
<div class="number-left">
<div class="icon-wrapper">
<el-icon><UserFilled size="850" /></el-icon>
</div>
</div>
<div class="number-right">
<p>待分派金额</p>
<p>{{ statisis.unAmount }}</p>
</div>
</div>
</div>
<div style="margin-top: 10px; margin-bottom: 10px">
<el-radio-group v-model="radio" @change="changeRadio">
<el-radio :value="0">到调解中心</el-radio>
<el-radio :value="1">到CPE</el-radio>
<el-radio :value="0">到调解中心</el-radio>
<el-radio :value="1">到CPE</el-radio>
</el-radio-group>
</div>
<template v-if="radio === 0 || radio === 1">
<template v-if="radio === 0 || (radio === 1 && !step)">
<div class="mainContent">
<div class="flex items-center my-3">
<span class="mr-2">
{{ radio === 0 ? '调解中心' : '分配到调解中心的cpe' }}
<span class="mr-2" v-if="radio === 0">
调解中心
</span>
<el-select
v-if="radio === 0"
v-model="currentTenant"
placeholder="请选择调解中心"
style="width: 210px"
......@@ -35,54 +71,59 @@
:key="index"
/>
</el-select>
<div v-else class="flex">
<div class="tree-cpe">
<p class="bb">选择CPE</p>
<div class="flex">
<div class="tree-wrapper" style="border-right: none">
<el-tree :data="data" highlight-current :props="defaultProps" @node-click="handleNodeClick" />
</div>
<!-- <el-transfer
v-model="value1"
:data="data1"
:titles="['CPE', '已选CPE']"
ref="mytransfer"
@left-check-change="leftChange"
/> -->
<div class="tree-wrapper">
<div class="flex items-center h-10 border-b">
<div class=" px-3 border-r mr-auto h-full flex items-center justify-center">
<el-checkbox v-model="checked1" label="" @change="changeAll"/>
</div>
</template>
<div class="top" v-if="radio === 0 || (radio === 1 && currentTenant)">
<div class="number-box">
<div class="number-left">
<div class="icon-wrapper">
<el-icon><UserFilled size="850" /></el-icon>
<div class="flex-1 text-center">CPE</div></div>
<el-tree :data="alltabledata" highlight-current ref="allTreeRef" node-key="id" show-checkbox :props="{
children: 'children',
label: 'username',
}" @check-change="checkChange"/>
</div>
</div>
<div class="number-right">
<p>待分配客户数量</p>
<p>{{ statisis.unCustomerNum }}</p>
</div>
<div class="ml-3">
<p class="bb">已选CPE</p>
<div class="flex items-center h-10 border border-b-0">
<div class=" w-10 border-r mr-auto h-full flex items-center justify-center">
序号
</div>
<div class="number-box">
<div class="number-left">
<div class="icon-wrapper">
<el-icon><List /></el-icon>
<div class="flex-1 text-center">CPE</div>
</div>
<div class="tree-wrapper" style="height: calc(100% - 3.8rem);">
<div v-for="(item, index) in tabledata" :key="index" class="flex">
<div class=" w-10 mr-auto h-full flex items-center justify-center">
{{index+1}}
</div>
<div class="number-right">
<p>待分配案件数量</p>
<p>{{ statisis.unCaseNum }}</p>
<div class="flex-1 text-left pl-3">{{ item.cpe.username }}</div>
</div>
</div>
<div class="number-box">
<div class="number-left">
<div class="icon-wrapper">
<el-icon><UserFilled size="850" /></el-icon>
</div>
</div>
<div class="number-right">
<p>待分配金额</p>
<p>{{ statisis.unAmount }}</p>
</div>
</div>
<!-- <el-transfer
v-model="value1"
:data="data1"
:titles="['CPE', '已选CPE']"
ref="mytransfer"
@left-check-change="leftChange"
/> -->
</div>
<template v-if="radio === 1 && currentTenant">
<p class="my-2">分配方案</p>
</template>
<template v-if="radio === 1 && currentTenant && step">
<p class="my-2">分派方案</p>
<div class="idea-box">
<div class="left-idea">
<div class="idea-btn">
......@@ -97,7 +138,7 @@
</div>
<div class="reset-btn">
<el-button type="primary" link :icon="RefreshRight" @click="resetAllocation"
>重新分</el-button
>重新分</el-button
>
</div>
</div>
......@@ -115,16 +156,19 @@
</div>
</div>
<template #footer>
<el-button type="primary" @click="saveFrom" :disabled="!currentTenant" v-if="radio === 1"
<el-button @click="backform" v-if="step" style="float: left">上一步</el-button>
<el-button type="primary" @click="saveFrom" :disabled="!currentTenant" v-if="radio === 1 && step"
>保存</el-button
>
<el-button type="default" @click="showModal = false">取消</el-button>
<el-button
type="primary"
@click="submitForm"
v-if="radio ===0||step"
:disabled="!currentTenant || (radio === 1 && !SaveBol)"
>确认提交</el-button
>
<el-button type="primary" @click="submitForm" v-if="!step && radio === 1" :disabled="!currentTenant">下一步</el-button>
</template>
</vxe-modal>
</template>
......@@ -142,17 +186,21 @@
} from '@/api/allcation';
import { getTenantPage } from '@/api/tenant';
import { computed } from 'vue';
import { reactive, ref } from 'vue';
import { reactive, ref, nextTick } from 'vue';
const emits = defineEmits(['success']);
const data = ref([]);
const mytransfer = ref();
const tabledata = ref([]);
const alltabledata = ref([]);
const lonsArr = ref([]);
const SaveBol = ref(false);
const showModal = ref(false);
const currentTenant = ref();
const allTreeRef = ref();
const statisis = ref({});
const currentAllBtn = ref();
const checked1 = ref(false)
const editRowIndex = ref(-1);
const pageParams = ref({});
const radio = ref(null);
......@@ -185,15 +233,23 @@
const chooseIdea = (type) => {
editRowIndex.value = -1;
currentAllBtn.value = type;
distributeCpe({
const param = {
type: currentAllBtn.value,
borrowerName: pageParams.value.borrowerName,
borrowerIdCard: pageParams.value.idCard,
borrowerPhone: pageParams.value.borrowerPhone,
commissionAmount: pageParams.value.commissionAmount,
remainingAmount: pageParams.value.remainingAmount,
tenantId: currentTenant.value,
}).then((res) => {
}
if (tabledata.value && tabledata.value.length) {
param['cpeIds'] = tabledata.value.map(v => v.cpe.id)
}
if (lonsArr.value && lonsArr.value.length) {
param['loanIds'] = lonsArr.value.map(v => v.id)
} else {
param['borrowerName'] = pageParams.value.borrowerName
param['borrowerIdCard'] = pageParams.value.idCard
param['borrowerPhone'] = pageParams.value.borrowerPhone
param['commissionAmount'] = pageParams.value.commissionAmount
param['remainingAmount'] = pageParams.value.remainingAmount
}
distributeCpe(param).then((res) => {
if (res.success) {
tabledata.value = res.result;
}
......@@ -201,18 +257,32 @@
};
const changeRadio = () => {
radio.value === 0 && getStatisis(pageParams.value);
step.value = 0
currentTenant.value = null;
};
const openModal = (param) => {
const openModal = (param, select) => {
console.log('select', select)
lonsArr.value = select
showModal.value = true;
pageParams.value = param;
currentAllBtn.value = null;
getTree();
step.value = 0;
radio.value = 0
SaveBol.value = false;
currentTenant.value = null;
editRowIndex.value = -1;
tabledata.value = []
alltabledata.value = []
checked1.value = false
};
const changeAll = () => {
if (checked1.value) {
allTreeRef.value.setCheckedNodes(alltabledata.value)
} else {
allTreeRef.value.setCheckedKeys([])
}
}
const backform = () => {
step.value = 0;
};
......@@ -221,14 +291,14 @@
const sum = tabledata.value.reduce((pre, cur) => pre + cur.caseNum, 0);
if (Number(sum) !== Number(statisis.value.unCaseNum))
return ElMessage.warning({
message: '总数与待分案件数不一致',
message: '总数与待分案件数不一致',
plain: true,
});
} else if (currentAllBtn.value === 'BORROWER') {
const sum = tabledata.value.reduce((pre, cur) => pre + cur.borrowerNum, 0);
if (Number(sum) !== Number(statisis.value.unCustomerNum))
return ElMessage.warning({
message: '总数与待分案人数不一致',
message: '总数与待分案人数不一致',
plain: true,
});
}
......@@ -247,6 +317,7 @@
};
const submitForm = () => {
if (radio.value === 1) {
if (step.value){
if (!SaveBol.value)
ElMessage.warning({
message: '要先保存',
......@@ -258,13 +329,17 @@
}).then((res) => {
if (res.success) {
ElMessage.success({
message: '分配成功',
message: '分派成功',
plain: true,
});
showModal.value = false;
emits('success');
}
});
} else {
step.value = 1;
}
} else if (radio.value === 0) {
// chooseIdea('BORROWER');
distributeLoan(currentTenant.value, {
......@@ -277,7 +352,7 @@
}).then((res) => {
if (res.success) {
ElMessage.success({
message: '分成功',
message: '分成功',
plain: true,
});
showModal.value = false;
......@@ -286,23 +361,48 @@
});
}
};
const handleNodeClick = () => {
const item = data.value.find((v) => v.id === currentTenant.value);
if (radio.value === 0) {
const handleNodeClick = (data1) => {
if(typeof data1 === 'object') {
currentTenant.value = data1.id
let param = {}
if (lonsArr.value && lonsArr.value.length) {
param['loanIds'] = lonsArr.value.map(v => v.id)
} else {
getStatisis(pageParams.value, currentTenant.value);
tabledata.value = item.users.map((v) => ({
param = {...pageParams.value}
}
getStatisis(param, currentTenant.value);
tabledata.value = data1.users.map((v) => ({
cpe: v,
borrowerNum: null,
caseNum: null,
amount: null,
}));
alltabledata.value = data1.users
checked1.value = true
console.log('allTreeRef', data1.users)
console.log('allTreeRef.value', allTreeRef.value)
nextTick(() => {
setTimeout(() => {
allTreeRef.value.setCheckedNodes(data1.users)
});
});
}
};
const resetAllocation = () => {
chooseIdea(currentAllBtn.value);
};
const checkChange = (dara, ddd, aaa) => {
nextTick(() => {
const arr = allTreeRef.value.getCheckedNodes()
tabledata.value = arr.map((v) => ({
cpe: v,
borrowerNum: null,
caseNum: null,
amount: null,
}));
});
}
const changeNum = (type, row, index) => {
// editRowIndex.value = index;
console.log('changeNum', type, row, index);
......@@ -336,7 +436,7 @@
},
{
field: 'borrowerNum',
title: '分案人数',
title: '分案人数',
slots: {
default: ({ row, rowIndex }) => {
if (currentAllBtn.value === 'BORROWER') {
......@@ -357,7 +457,7 @@
},
{
field: 'caseNum',
title: '分案件数',
title: '分案件数',
slots: {
default: ({ row, rowIndex }) => {
if (currentAllBtn.value === 'CASE') {
......@@ -378,7 +478,7 @@
},
{
field: 'amount',
title: '分委案金额',
title: '分委案金额',
// slots: {
// default: ({ row, rowIndex }) => {
// // if (currentAllBtn.value === 3) {
......@@ -495,16 +595,18 @@
}
.mainContent {
display: flex;
:deep(.el-transfer-panel .el-transfer-panel__header) {
border-top-left-radius: 0;
background: #fff;
border-top-right-radius: 0;
.bb {
padding-left: 14px;
border: 1px solid #ebeef5;
border-bottom: none;
}
:deep(.el-transfer__buttons) {
padding: 0 5px;
.tree-wrapper {
border: 1px solid #ebeef5;
width: 290px;
height: 318px;
}
:deep(.el-transfer-panel) {
width: 250px;
.bc {
display: flex;
}
}
</style>
......@@ -5,23 +5,23 @@
<template #table_top>
<div class="flex">
<div class="mr-4"
>未分案件数:<span class="text-blue-500">{{ statisis.caseNum }}</span
>未分案件数:<span class="text-blue-500">{{ statisis.caseNum }}</span
></div
>
<div class="mr-4"
>未分CPE数:<span class="text-blue-500">{{ statisis.cpeNum }}</span
>未分CPE数:<span class="text-blue-500">{{ statisis.cpeNum }}</span
></div
>
<div class="mr-4"
>未分总金额数:<span class="text-blue-500">{{ statisis.amount }}</span
>未分总金额数:<span class="text-blue-500">{{ statisis.amount }}</span
></div
>
</div>
</template>
<!-- 表格 header 按钮 -->
<template #left_buttons>
<el-button type="primary" @click="allocation"> </el-button>
<el-button
<el-button type="primary" @click="allocation"> </el-button>
<!-- <el-button
type="primary"
:disabled="!selectdList || selectdList.length < 1"
@click="backCase"
......@@ -29,7 +29,7 @@
</el-button>
<el-button type="primary" :disabled="!selectdList || selectdList.length < 1"
>撤案
</el-button>
</el-button> -->
</template>
</ProTable>
</div>
......@@ -99,10 +99,17 @@
return obj;
};
const distributeStatusOpt = [
{ label: '未分', value: 'undistributed' },
{ label: '分到调解中心', value: 'tenant' },
{ label: '分到CPE', value: 'CPE' },
{ label: '未分', value: 'undistributed' },
{ label: '分到调解中心', value: 'tenant' },
{ label: '分到CPE', value: 'CPE' },
];
const onCheckboxAll =(flag)=> {
if (flag.checked) {
selectdList.value = flag.records;
} else {
selectdList.value = [];
}
}
console.log('citydatacitydata', citydata);
const config = reactive({
columns: [
......@@ -148,7 +155,7 @@
},
{
field: 'distributeStatus',
title: '分状态',
title: '分状态',
width: 80,
enum: distributeStatusOpt,
search: { el: 'select', props: { filterable: true }, labelWidth: 78 },
......@@ -184,7 +191,7 @@
},
{
field: 'tenantTime',
title: '分中心日期',
title: '分中心日期',
width: 130,
search: {
el: 'date-picker',
......@@ -200,7 +207,7 @@
},
{
field: 'cpeDate',
title: '分CPE日期',
title: '分CPE日期',
width: 130,
search: {
el: 'date-picker',
......@@ -250,10 +257,11 @@
},
],
onCheckboxChange: onCheckboxChange,
onCheckboxAll: onCheckboxAll
});
const allocation = (row) => {
allocationModalRef.value.openModal(JSON.parse(JSON.stringify(curParam.value)));
allocationModalRef.value.openModal(JSON.parse(JSON.stringify(curParam.value)), JSON.parse(JSON.stringify(selectdList.value)));
};
const query = () => caseLRef.value?.search();
......
......@@ -273,6 +273,7 @@
},
];
const submitForm = () => {
console.log('currentInfo.value', currentInfo.value)
formRef.value.validate((valid) => {
if (valid) {
const param = {
......
......@@ -48,8 +48,8 @@
<template v-if="!step">
<div style="margin-top: 10px; margin-bottom: 10px">
<el-radio-group v-model="radio">
<el-radio :value="0">到调解中心</el-radio>
<el-radio :value="1">到CPE</el-radio>
<el-radio :value="0">到调解中心</el-radio>
<el-radio :value="1">到CPE</el-radio>
</el-radio-group>
</div>
<div class="mainContent">
......@@ -91,7 +91,7 @@
</div>
<div class="reset-btn">
<el-button type="primary" link :icon="RefreshRight" @click="resetAllocation"
>重新分</el-button
>重新分</el-button
>
</div>
</div>
......
......@@ -176,7 +176,7 @@
},
{
field: 'cpeDate',
title: '分CPE日期',
title: '分CPE日期',
width: 130,
search: {
el: 'date-picker',
......
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