Commit 88683368 authored by 沈翠玲's avatar 沈翠玲

业务审批

parent 0ccbd1ba
......@@ -11,7 +11,7 @@
/>
<!-- 表格 -->
<div class="card flex-1">
<div class="card flex-1" :style="wrapperstyle">
<!-- 表格主体 -->
<div>
<slot name="table_top"></slot>
......@@ -63,6 +63,10 @@
type: Object,
required: true,
},
wrapperstyle: {
type: Object,
default: () => {}
},
selectdList: {
type: Array,
default: undefined,
......
<template>
<el-dialog v-model="dialogVisible" title="个人信息" width="500px" draggable>
<span>This is userInfo</span>
<div class="flex">
<span>用户名:{{username}}</span>
<span style="margin-left: 80px" v-if="username !== 'admin'">调解中心:{{currentTenant}}</span>
</div>
<div class="flex">
<span>手机号:{{userStore.userInfo.phone}}</span>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
......@@ -11,12 +17,25 @@
</template>
<script setup>
import { useUserStore } from '@/stores/modules/user';
import { computed } from 'vue';
import { ref } from 'vue';
const userStore = useUserStore();
const currentTenant = ref();
const dialogVisible = ref(false);
const openDialog = () => {
dialogVisible.value = true;
};
console.log('userStore', userStore.userInfo)
const username = computed(() => userStore.userInfo?.username);
if (userStore.userInfo?.tenants && userStore.userInfo?.tenants.length > 0) {
if (userStore.userInfo?.tenants.length < 2) {
currentTenant.value = userStore.userInfo?.tenants[0].name;
} else {
currentTenant.value = userStore.tenant.name;
}
}
defineExpose({ openDialog });
</script>
<template>
<vxe-modal
resize
v-model="showModal"
:title="currentType"
@hide="onHide"
height="482"
width="1003"
show-footer
esc-closable
>
<div class="w-full px-3 h-full overflow-auto flex-col flex mytable">
<template v-if="currentType === '结清减免还款' || currentType === '直接还款'">
<table style="margin-bottom: 10px" v-if="currentType === '结清减免还款'">
<tr>
<td class="label">减免申请ID</td>
<td>{{ currentInfo.id }}</td>
<td class="label">减免截止时间</td>
<td>{{ currentInfo.inEffectDate }}</td>
<td class="label">减免金额(总计)</td>
<td>{{ currentInfo.totalReduceAmount }}</td>
</tr>
<tr>
<td class="label">减免案件数</td>
<td>{{ currentInfo.totalNumber }}</td>
<td class="label">应还金额</td>
<td colspan="5">{{
Decimal(currentInfo.remainingAmount).sub(currentInfo.totalReduceAmount)
}}</td>
</tr>
<!-- Add more rows as needed -->
</table>
<table style="margin-bottom: 10px" v-else>
<tr>
<td class="label">案件ID</td>
<td>{{ currentInfo.caseId }}</td>
<td class="label">借款平台</td>
<td>{{ currentInfo.loanPlatform.name }}</td>
<td class="label">资管公司</td>
<td>{{ currentInfo.manageOrg.orgName }}</td>
</tr>
<tr>
<td class="label">委案金额</td>
<td>{{ currentInfo.commissionAmount }}</td>
<td class="label">累计减免金额</td>
<td>{{ currentInfo.sumReductionAmount }}</td>
<td class="label">剩余待还金额</td>
<td>{{ currentInfo.remainingAmount }}</td>
</tr>
<tr>
<td class="label">累计还款金额</td>
<td colspan="5">{{ currentInfo.sumRepayAmount }}</td>
</tr>
<!-- Add more rows as needed -->
</table>
<div>
<el-form ref="formRef" inline :model="form" :rules="rules" label-width="110px">
<el-row>
<el-col :span="8">
<el-form-item class="w-full" label="实际还款金额:" prop="realRepayAmount">
<div>
<el-input
v-model="form.realRepayAmount"
disabled
placeholder="请输入"
style="width: 100%"
/>
</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item class="w-full" label="实际还款时间:" prop="name">
<el-date-picker
v-model="form.realRepayTime"
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="8">
<el-form-item class="w-full" label="付款凭证:" prop="name">
<el-upload
v-model:file-list="form.images"
:action="url"
disabled
:headers="{ timeout: 180000 }"
list-type="picture-card"
class="mypicture"
:on-preview="handlePictureCardPreview"
:on-success="handleFileSuccess"
:on-remove="handleRemove"
>
<div class="text-center">
<el-icon><Plus /></el-icon>
<div>付款相关附件</div>
</div>
</el-upload>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</template>
<template v-if="currentType === '分期还款'">
<table style="margin-bottom: 10px">
<tr>
<td class="label">分期申请ID</td>
<td>{{ currentDetail.stages.id }}</td>
<td class="label">还款总额</td>
<td>{{ currentDetail.stages.totalRepayAmount }}</td>
<td class="label">分期申请时间</td>
<td>{{ currentDetail.stages.applyTime }}</td>
</tr>
<tr>
<td class="label">还款期数</td>
<td>{{ currentDetail.stages.totalPeriod }}</td>
<td class="label">首期还款日</td>
<td>{{ currentDetail.stages.firstApplyDate }}</td>
<td class="label">分期生效时间</td>
<td>{{ currentDetail.stages.effectiveTime }}</td>
</tr>
<tr>
<td class="label">期次</td>
<td>{{ currentInfo.period }}</td>
<td class="label">期次还款到期日</td>
<td colspan="3">{{ currentInfo.playApplyDate }}</td>
</tr>
<tr>
<td class="label">期次应还金额</td>
<td>{{ currentInfo.applyAmount }}</td>
<td class="label">期次还款状态</td>
<td colspan="3">{{
currentInfo.repayStatus
? currentInfo.repayStatus === 'over'
? '已还款'
: '待还款'
: ''
}}</td>
</tr>
</table>
<div class="flex">
<div class="w-2/5 pr-11">
<el-form ref="formRef" inline :model="form" :rules="rules" label-width="110px">
<el-row>
<el-col :span="24">
<el-form-item class="w-full" label="实际还款时间:" prop="realRepayTime">
<el-date-picker
v-model="form.realRepayTime"
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="realRepayAmount">
<div class="w-full">
<el-input
disabled
v-model="form.realRepayAmount"
placeholder="请输入"
class="w-full"
/>
</div>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item class="w-full" label="付款凭证:" prop="name">
<el-upload
v-model:file-list="form.images"
:action="url"
disabled
:headers="{ timeout: 180000 }"
list-type="picture-card"
class="mypicture"
:on-preview="handlePictureCardPreview"
:on-success="handleFileSuccess"
:on-remove="handleRemove"
>
<div class="text-center">
<el-icon><Plus /></el-icon>
<div>付款相关附件</div>
</div>
</el-upload>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<div class="w-3/5">
<ProTable
:config="config"
:data="tabledata"
:showPagination="false"
:showToolBar="false"
/>
</div>
</div>
</template>
</div>
<template #footer>
<el-button @click="showModal = false">取消</el-button>
<el-button
type="primary"
@click="submitForm('fail')"
v-if="
(currentDetail.flowStatus === 'pending' && authButtonListGet.includes('repayment_tenant_apply_btn')) ||
(currentDetail.flowStatus === 'in_review' && authButtonListGet.includes('repayment_apply_btn'))
"
>不通过</el-button
>
<el-button
type="primary"
@click="submitForm('pass')"
v-if="
(currentDetail.flowStatus === 'pending' && authButtonListGet.includes('repayment_tenant_apply_btn')) ||
(currentDetail.flowStatus === 'in_review' && authButtonListGet.includes('repayment_apply_btn'))
"
>通过</el-button
>
</template>
<el-dialog v-model="dialogVisible">
<img w-full :src="dialogImageUrl" alt="Preview Image" />
</el-dialog>
</vxe-modal>
</template>
<script setup lang="jsx" name="allocationModal">
import { computed } from 'vue';
import dayjs from 'dayjs';
import Decimal from 'decimal.js';
import { reactive, ref } from 'vue';
import { getAppEnvConfig } from '@/utils/env';
import { ElMessage, ElTag } from 'element-plus';
import { useDict } from '@/hooks/useDict';
const { PhoneResultStatus, RepayType, FlowStatus, FollowStatus, AuditStatus, CaseStatus } =
useDict(
'PhoneResultStatus',
'RepayType',
'FlowStatus',
'FollowStatus',
'AuditStatus',
'CaseStatus'
);
import { saveRepayRecord } from '@/api/property';
const envs = getAppEnvConfig();
import { useAuthStore } from '@/stores/modules/auth';
const { authButtonListGet } = useAuthStore(); // 获取用户权限列表
const data = ref([]);
const mytransfer = ref();
const tabledata = ref([]);
const url = envs.VITE_GLOB_API_URL_PREFIX + '/sys/upload';
const showModal = ref(false);
const currentType = ref('');
const formRef = ref();
const currentInfo = ref({});
const currentDetail = ref({});
const emits = defineEmits(['submitForm']);
const form = reactive({
realRepayAmount: '',
images: [],
realRepayTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
});
const dialogImageUrl = ref('');
const dialogVisible = ref(false);
const props = defineProps({
mergerCase: String
});
const radio = ref(0);
const validaterealRepayAmount = (rule, value, callback) => {
if (
currentType.value === '结清减免还款' &&
Number(value) !==
Number(Decimal(currentInfo.value.remainingAmount).sub(currentInfo.value.totalReduceAmount))
) {
callback(new Error('*金额必须等于应还金额'));
} else if (
currentType.value === '直接还款' &&
Number(value) > Number(currentInfo.value.remainingAmount)
) {
callback(new Error('*金额必须小于应还金额'));
} else if (
currentType.value === '分期还款' &&
Number(value) !== Number(currentInfo.value.applyAmount)
) {
callback(new Error('*金额必须等于应还金额'));
} else {
callback();
}
};
const rules = ref({
realRepayAmount: [{ validator: validaterealRepayAmount, trigger: 'blur' }],
});
const onHide = () => {
// currentInfo.value = {};
};
const openModal = (info, detail, type) => {
showModal.value = true;
form.realRepayAmount = detail.realRepayAmount;
form.images = detail.images
? detail.images.map((v) => ({
name: v.slice(v.lastIndexOf('/') + 1, v.length),
url: envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/' + v,
}))
: [];
form.realRepayTime = detail.realRepayTime;
currentInfo.value = info;
currentDetail.value = detail;
console.log('currentInfo.value', currentDetail.value)
currentType.value = type;
tabledata.value = detail?.stages?.loans;
};
const handleRemove = (uploadFile, uploadFiles) => {
const index = form.images.findIndex((v) => v.name === uploadFile.name);
form.images.splice(index, 1);
};
const handlePictureCardPreview = (uploadFile) => {
dialogImageUrl.value = uploadFile.url;
dialogVisible.value = true;
};
const submitForm = (type) => {
emits('submitForm', type)
showModal.value = false
};
const handleFileSuccess = (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 config = computed(() => {
return {
toolbarConfig: { enabled: false },
columns: [
{
field: 'caseId',
width: 230,
slots: {
default: ({ row, rowIndex }) => {
return (
<>
<div className="inline-block">{row.caseId}</div>
<Tag row={row} />
</>
);
},
},
title: '案件ID',
showOverflow: 'tooltip',
},
{
field: 'loanPlatform.name',
title: '借款平台',
showOverflow: 'tooltip',
},
{
field: 'remainingAmount',
title: '剩余待还金额',
showOverflow: 'tooltip',
},
],
};
});
defineExpose({
openModal,
});
</script>
<style lang="scss" scoped>
.box-title {
font-weight: 600;
font-size: 16px;
color: #000000;
line-height: 43px;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
margin-bottom: 10px;
}
.mytable {
.el-form-item {
margin-right: 0;
}
table {
width: 100%;
border-collapse: collapse;
}
td {
border: 1px solid rgba(5, 5, 5, 0.06);
background: #fff;
color: #606268;
width: 240px;
height: 40px;
font-size: 14px;
padding-left: 5px;
line-height: 11px;
&.label {
width: 160px;
text-align: center;
font-weight: bold;
background: #f6f8ff;
color: #909399;
}
}
}
.mypicture {
:deep(.el-upload--picture-card) {
width: 70px;
height: 70px;
}
:deep(.el-upload-list__item) {
width: 70px;
height: 70px;
}
:deep(.el-upload--picture-card) {
font-size: 11px;
}
}
</style>
\ No newline at end of file
......@@ -34,42 +34,16 @@
</el-button>
</template> -->
</ProTable>
<vxe-modal
resize
v-model="showModal"
title="审批提示"
height="282"
width="450"
show-footer
esc-closable
>
<div class="w-full px-3 h-full overflow-auto flex-col flex mytable">
<template v-if="currentRow">
是否审批通过{{
currentRow
? `借款人${currentRow?.borrower?.name}, 还款金额${currentRow?.realRepayAmount},实际还款时间${currentRow?.realRepayTime}`
: ''
}}的数据?
</template>
<template v-else>
是否审批通过以下数据:<br />
<div v-for="(item, index) in selectdList" :key="index">
{{
`借款人${item?.borrower?.name}, 还款金额${item?.realRepayAmount},实际还款时间${item?.realRepayTime}`
}}
</div>
</template>
</div>
<template #footer>
<el-button type="default" @click="showModal = false">取消</el-button>
<el-button type="danger" @click="submitForm('fail')">不通过</el-button>
<el-button type="primary" @click="submitForm('pass')">通过</el-button>
</template>
</vxe-modal>
<returnModal
ref="returnModalRef"
@submitForm="submitForm"
:mergerCase="detail?.mergerCase"
/>
</div>
</template>
<script setup name="repaymentApprove" lang="jsx">
import returnModal from './components/returnModal.vue';
import { computed } from 'vue';
import { reactive, ref } from 'vue';
import {
......@@ -83,6 +57,7 @@
import { getTenantPage } from '@/api/tenant';
import { useAuthStore } from '@/stores/modules/auth';
const { authButtonListGet } = useAuthStore(); // 获取用户权限列表
const returnModalRef = ref();
const { RepayType, FlowStatus } = useDict('RepayType', 'FlowStatus');
const ProTableRef = ref();
......@@ -145,12 +120,29 @@
return obj;
};
const changeStatus = async (row) => {
if (row.id) {
currentRow.value = row;
currentRow.value = row
const type = RepayType.value?.find((v) => v.value === row.repayType)?.label
if (type === '分期还款') {
returnModalRef.value.openModal(
JSON.parse(JSON.stringify(row.byStagesRecord)),
JSON.parse(JSON.stringify(row)),
type
);
} else if (type === '直接还款') {
returnModalRef.value.openModal(
JSON.parse(JSON.stringify(row.loan)),
JSON.parse(JSON.stringify(row)),
type
);
} else {
currentRow.value = null;
{
returnModalRef.value.openModal(
JSON.parse(JSON.stringify(row.reduce)),
JSON.parse(JSON.stringify(row)),
type
);
}
}
showModal.value = true;
};
const submitForm = async (type) => {
const ids = selectdList.value.map((v) => v.id).join(',');
......@@ -208,23 +200,7 @@
width: 80,
slots: {
default: ({ row }) => {
if (row.reduce && row.reduce.reduceRecords) {
return (
<>
{Array.from(
new Set(row.reduce.reduceRecords.map((v) => v.loan?.cpe?.username))
).join(',')}
</>
);
} else if (row.stages && row.stages.loans) {
return (
<>
{Array.from(new Set(row.stages.loans.map((v) => v?.cpe?.username))).join(',')}
</>
);
} else {
return <>{row.cpe?.username}</>;
}
return <>{row.cpe?.username}</>;
},
},
search: { el: 'input', props: { clearable: true }, key: 'cpeName', labelWidth: 105 },
......@@ -233,21 +209,7 @@
showOverflow: 'tooltip',
slots: {
default: ({ row }) => {
if (row.reduce && row.reduce.reduceRecords) {
return (
<>
{Array.from(
new Set(row.reduce.reduceRecords.map((v) => v.loan?.tenant?.name))
).join(',')}
</>
);
} else if (row.stages && row.stages.loans) {
return (
<>{Array.from(new Set(row.stages.loans.map((v) => v?.tenant?.name))).join(',')}</>
);
} else {
return <>{row.tenant?.name}</>;
}
return <>{row.tenant?.name}</>;
},
},
enum: () => getTenantPage({ current: 1, size: 999999999, status: 'enable' }),
......@@ -329,10 +291,6 @@
{
field: 'code',
title: '操作',
visible:
((activeName.value === 'pending' && authButtonListGet.includes('repayment_tenant_apply_btn')) || (activeName.value === 'in_review' && authButtonListGet.includes('repayment_apply_btn')))
? true
: false,
slots: {
default: ({ row, rowIndex }) => {
if (row.flowStatus === 'pending' && authButtonListGet.includes('repayment_tenant_apply_btn')) {
......@@ -351,6 +309,14 @@
</ElButton>
</>
);
} else {
return (
<>
<ElButton type="primary" onClick={() => changeStatus(row)}>
查看
</ElButton>
</>
);
}
},
},
......
......@@ -88,8 +88,10 @@
watch(
() => AuditStatus.value,
(newValue, oldValue) => {
activeName.value = AuditStatus.value[0].value
query()
if (AuditStatus.value[0]) {
activeName.value = AuditStatus.value[0].value
query()
}
}
);
const callMode = ref('');
......
......@@ -78,16 +78,16 @@
/>
</el-form-item>
</el-col>
<el-col :span="8" v-if="currentType === '结清减免还款'">
<el-col :span="8">
<el-form-item class="w-full" label="付款凭证:" prop="name">
<el-upload
v-model:file-list="form.files"
v-model:file-list="form.images"
:action="url"
:headers="{ timeout: 180000 }"
list-type="picture-card"
class="mypicture"
:on-preview="handlePictureCardPreview"
:on-success="handleFileSuccess"
:auto-upload="true"
:on-remove="handleRemove"
>
<div class="text-center">
......@@ -164,6 +164,25 @@
</div>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item class="w-full" label="付款凭证:" prop="name">
<el-upload
v-model:file-list="form.images"
:action="url"
:headers="{ timeout: 180000 }"
list-type="picture-card"
class="mypicture"
:on-preview="handlePictureCardPreview"
:on-success="handleFileSuccess"
:on-remove="handleRemove"
>
<div class="text-center">
<el-icon><Plus /></el-icon>
<div>付款相关附件</div>
</div>
</el-upload>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
......@@ -180,6 +199,7 @@
<div class="font-bold pl-3">还款记录</div>
<ProTable
:config="props.returnConfig"
:wrapperstyle="{'overflow-x':'clip'}"
:data="props.returnData"
:showPagination="false"
:showToolBar="false"
......@@ -189,6 +209,9 @@
<el-button type="default" @click="showModal = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</template>
<el-dialog v-model="dialogVisible">
<img w-full :src="dialogImageUrl" alt="Preview Image" />
</el-dialog>
</vxe-modal>
</template>
......@@ -223,12 +246,14 @@
const emits = defineEmits(['success']);
const form = reactive({
realRepayAmount: '',
files: [],
images: [],
realRepayTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
});
const dialogImageUrl = ref('');
const dialogVisible = ref(false);
const props = defineProps({
mergerCase: String,
returnConfig: Array,
returnConfig: Object,
returnData: Array,
});
const radio = ref(0);
......@@ -262,7 +287,7 @@
const openModal = (info, detail, type) => {
showModal.value = true;
form.realRepayAmount = '';
form.files = [];
form.images = [];
form.realRepayTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
currentInfo.value = info;
console.log('currentInfo', currentInfo.value);
......@@ -272,7 +297,8 @@
};
const handleRemove = (uploadFile, uploadFiles) => {
console.log(uploadFile, uploadFiles);
const index = form.images.findIndex((v) => v.name === uploadFile.name);
form.images.splice(index, 1);
};
const handlePictureCardPreview = (uploadFile) => {
......@@ -280,12 +306,16 @@
dialogVisible.value = true;
};
const submitForm = () => {
console.log('currentInfo.value', currentInfo.value, currentDetail.value, tabledata.value);
let images = JSON.parse(JSON.stringify(form.images.map((v) => v.url)));
images = images.map((v) => {
return v.replace(envs.VITE_GLOB_API_URL_PREFIX + '/sys/static/', '');
});
formRef.value.validate((valid) => {
if (valid) {
const param = {
repayType: RepayType.value.find((v) => v.label === currentType.value).value,
...form,
images,
repayAmount: form.realRepayAmount,
borrower: currentInfo.value.borrower,
flowStatus: 'pending',
......@@ -340,7 +370,10 @@
});
};
const handleFileSuccess = (response, file, fileList) => {
console.log('888877', 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 config = computed(() => {
return {
......
......@@ -13,11 +13,13 @@
type="primary"
@click="reduce"
:disabled="detail && detail.repayStatus && detail.repayStatus === 'over'"
v-permission="'case_handle'"
>减免申请</el-button
>
<el-button
type="primary"
@click="split"
v-permission="'case_handle'"
:disabled="detail && detail.repayStatus && detail.repayStatus === 'over'"
>分期申请</el-button
>
......@@ -908,6 +910,7 @@
detail.value.repayStatus &&
detail.value.repayStatus === 'over'
}
v-permission={'case_handle'}
icon={Phone}
link
></ElButton>
......@@ -1034,7 +1037,7 @@
{
field: 'code',
fixed: 'right',
title: '',
title: '操作',
slots: {
default: ({ row, rowIndex }) => {
return (
......@@ -1042,6 +1045,7 @@
<ElButton
type="primary"
onClick={() => returnCrash(row, '直接还款')}
v-permission={'case_handle'}
disabled={row.repayStatus == 'over'}
>
还款
......@@ -1213,6 +1217,7 @@
rowIndex !== splitCanIndex.value ||
(detail.value.repayStatus && detail.value.repayStatus === 'over')
}
v-permission={'case_handle'}
>
还款
</ElButton>
......@@ -1378,6 +1383,7 @@
row.flowStatus !== 'pass' ||
(detail.value.repayStatus && detail.value.repayStatus === 'over')
}
v-permission={'case_handle'}
>
还款
</ElButton>
......
......@@ -87,8 +87,10 @@
watch(
() => AuditStatus.value,
(newValue, oldValue) => {
activeName.value = AuditStatus.value[0].value
query()
if (AuditStatus.value[0]) {
activeName.value = AuditStatus.value[0].value
query()
}
}
);
const callMode = ref('');
......
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