迁移892-without-redis分支全量功能
This commit is contained in:
@@ -4,8 +4,9 @@ VUE_APP_TITLE = 若依管理系统
|
||||
# 开发环境配置
|
||||
ENV = 'development'
|
||||
|
||||
# 若依管理系统/开发环境
|
||||
VUE_APP_BASE_API = '/dev-api'
|
||||
|
||||
# 路由懒加载
|
||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||
# 若依管理系统/开发环境
|
||||
VUE_APP_BASE_API = '/dev-api'
|
||||
VUE_APP_PASSWORD_TRANSFER_KEY = '1234567890abcdef'
|
||||
|
||||
# 路由懒加载
|
||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||
|
||||
@@ -4,5 +4,6 @@ VUE_APP_TITLE = 若依管理系统
|
||||
# 生产环境配置
|
||||
ENV = 'production'
|
||||
|
||||
# 若依管理系统/生产环境
|
||||
VUE_APP_BASE_API = '/prod-api'
|
||||
# 若依管理系统/生产环境
|
||||
VUE_APP_BASE_API = '/prod-api'
|
||||
VUE_APP_PASSWORD_TRANSFER_KEY = '1234567890abcdef'
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"axios": "0.30.3",
|
||||
"clipboard": "2.0.8",
|
||||
"core-js": "3.37.1",
|
||||
"crypto-js": "4.2.0",
|
||||
"echarts": "5.4.0",
|
||||
"element-ui": "2.15.14",
|
||||
"file-saver": "2.0.5",
|
||||
@@ -54,6 +55,7 @@
|
||||
"chalk": "4.1.0",
|
||||
"compression-webpack-plugin": "6.1.2",
|
||||
"connect": "3.6.6",
|
||||
"html-webpack-plugin": "3.2.0",
|
||||
"sass": "1.32.13",
|
||||
"sass-loader": "10.1.1",
|
||||
"script-ext-html-webpack-plugin": "2.1.5",
|
||||
|
||||
45
ruoyi-ui/src/api/loanPricing/workflow.js
Normal file
45
ruoyi-ui/src/api/loanPricing/workflow.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询利率定价流程列表
|
||||
export function listWorkflow(query) {
|
||||
return request({
|
||||
url: '/loanPricing/workflow/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询利率定价流程详情
|
||||
export function getWorkflow(serialNum) {
|
||||
return request({
|
||||
url: '/loanPricing/workflow/' + serialNum,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 创建个人客户利率定价流程
|
||||
export function createPersonalWorkflow(data) {
|
||||
return request({
|
||||
url: '/loanPricing/workflow/create/personal',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 创建企业客户利率定价流程
|
||||
export function createCorporateWorkflow(data) {
|
||||
return request({
|
||||
url: '/loanPricing/workflow/create/corporate',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 设定执行利率
|
||||
export function setExecuteRate(serialNum, executeRate) {
|
||||
return request({
|
||||
url: '/loanPricing/workflow/' + serialNum + '/executeRate',
|
||||
method: 'put',
|
||||
data: { executeRate: executeRate }
|
||||
})
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 登录方法
|
||||
export function login(username, password, code, uuid) {
|
||||
const data = {
|
||||
username,
|
||||
password,
|
||||
code,
|
||||
uuid
|
||||
}
|
||||
return request({
|
||||
import request from '@/utils/request'
|
||||
import { encryptPasswordFields } from '@/utils/passwordTransfer'
|
||||
|
||||
// 登录方法
|
||||
export function login(username, password, code, uuid) {
|
||||
const data = encryptPasswordFields({
|
||||
username,
|
||||
password,
|
||||
code,
|
||||
uuid
|
||||
}, ['password'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
||||
return request({
|
||||
url: '/login',
|
||||
headers: {
|
||||
isToken: false,
|
||||
@@ -17,19 +18,20 @@ export function login(username, password, code, uuid) {
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 注册方法
|
||||
export function register(data) {
|
||||
return request({
|
||||
url: '/register',
|
||||
}
|
||||
|
||||
// 注册方法
|
||||
export function register(data) {
|
||||
const payload = encryptPasswordFields(data, ['password'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
||||
return request({
|
||||
url: '/register',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
},
|
||||
method: 'post',
|
||||
data: payload
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户详细信息
|
||||
export function getInfo() {
|
||||
@@ -66,4 +68,4 @@ export function getCodeImg() {
|
||||
method: 'get',
|
||||
timeout: 20000
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import request from '@/utils/request'
|
||||
import { parseStrEmpty } from "@/utils/ruoyi"
|
||||
import request from '@/utils/request'
|
||||
import { parseStrEmpty } from "@/utils/ruoyi"
|
||||
import { encryptPasswordFields } from '@/utils/passwordTransfer'
|
||||
|
||||
// 查询用户列表
|
||||
export function listUser(query) {
|
||||
@@ -19,13 +20,14 @@ export function getUser(userId) {
|
||||
}
|
||||
|
||||
// 新增用户
|
||||
export function addUser(data) {
|
||||
return request({
|
||||
url: '/system/user',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function addUser(data) {
|
||||
const payload = encryptPasswordFields(data, ['password'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
||||
return request({
|
||||
url: '/system/user',
|
||||
method: 'post',
|
||||
data: payload
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户
|
||||
export function updateUser(data) {
|
||||
@@ -45,12 +47,12 @@ export function delUser(userId) {
|
||||
}
|
||||
|
||||
// 用户密码重置
|
||||
export function resetUserPwd(userId, password) {
|
||||
const data = {
|
||||
userId,
|
||||
password
|
||||
}
|
||||
return request({
|
||||
export function resetUserPwd(userId, password) {
|
||||
const data = encryptPasswordFields({
|
||||
userId,
|
||||
password
|
||||
}, ['password'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
||||
return request({
|
||||
url: '/system/user/resetPwd',
|
||||
method: 'put',
|
||||
data: data
|
||||
@@ -88,12 +90,12 @@ export function updateUserProfile(data) {
|
||||
}
|
||||
|
||||
// 用户密码重置
|
||||
export function updateUserPwd(oldPassword, newPassword) {
|
||||
const data = {
|
||||
oldPassword,
|
||||
newPassword
|
||||
}
|
||||
return request({
|
||||
export function updateUserPwd(oldPassword, newPassword) {
|
||||
const data = encryptPasswordFields({
|
||||
oldPassword,
|
||||
newPassword
|
||||
}, ['oldPassword', 'newPassword'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
||||
return request({
|
||||
url: '/system/user/profile/updatePwd',
|
||||
method: 'put',
|
||||
data: data
|
||||
|
||||
14
ruoyi-ui/src/utils/passwordTransfer.js
Normal file
14
ruoyi-ui/src/utils/passwordTransfer.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import CryptoJS from 'crypto-js'
|
||||
|
||||
export function encryptPasswordFields(payload, fields, key) {
|
||||
const next = { ...payload }
|
||||
fields.forEach((field) => {
|
||||
if (next[field]) {
|
||||
next[field] = CryptoJS.AES.encrypt(next[field], CryptoJS.enc.Utf8.parse(key), {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
}).toString()
|
||||
}
|
||||
})
|
||||
return next
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<el-card class="bargaining-pool-card">
|
||||
<div slot="header" class="card-header">
|
||||
<span class="card-title">议价池</span>
|
||||
</div>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="网点议价池">
|
||||
{{ displayBranchPool }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="支行议价池">
|
||||
{{ displaySubBranchPool }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="私域池">
|
||||
{{ displayPrivateDomainPool }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="超利分成">
|
||||
{{ displayExcessProfitShare }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "BargainingPoolDisplay",
|
||||
props: {
|
||||
branchPool: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
subBranchPool: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
privateDomainPool: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
excessProfitShare: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
displayBranchPool() {
|
||||
const value = this.branchPool
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '0'
|
||||
}
|
||||
return value
|
||||
},
|
||||
displaySubBranchPool() {
|
||||
const value = this.subBranchPool
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '0'
|
||||
}
|
||||
return value
|
||||
},
|
||||
displayPrivateDomainPool() {
|
||||
const value = this.privateDomainPool
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '0'
|
||||
}
|
||||
return value
|
||||
},
|
||||
displayExcessProfitShare() {
|
||||
const value = this.excessProfitShare
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '0'
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bargaining-pool-card {
|
||||
::v-deep .el-card__header {
|
||||
padding: 16px 20px;
|
||||
background-color: #fafafa;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-card__body {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,303 @@
|
||||
<template>
|
||||
<el-dialog title="新增企业利率定价流程" :visible.sync="dialogVisible" width="900px" append-to-body
|
||||
@close="handleClose">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="140px" class="workflow-create-form">
|
||||
<!-- 基本信息 -->
|
||||
<el-divider content-position="left">基本信息</el-divider>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户内码" prop="custIsn">
|
||||
<el-input v-model="form.custIsn" placeholder="请输入客户内码"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户名称" prop="custName">
|
||||
<el-input v-model="form.custName" placeholder="请输入客户名称"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="证件类型" prop="idType">
|
||||
<el-select v-model="form.idType" placeholder="请选择证件类型" style="width: 100%">
|
||||
<el-option label="统一社会信用代码" value="统一社会信用代码"/>
|
||||
<el-option label="其他" value="其他"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="证件号码" prop="idNum">
|
||||
<el-input v-model="form.idNum" placeholder="请输入证件号码"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 贷款信息 -->
|
||||
<el-divider content-position="left">贷款信息</el-divider>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="担保方式" prop="guarType">
|
||||
<el-select v-model="form.guarType" placeholder="请选择担保方式" style="width: 100%">
|
||||
<el-option label="信用" value="信用"/>
|
||||
<el-option label="保证" value="保证"/>
|
||||
<el-option label="抵押" value="抵押"/>
|
||||
<el-option label="质押" value="质押"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="申请金额(元)" prop="applyAmt">
|
||||
<el-input v-model="form.applyAmt" placeholder="请输入申请金额"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="贷款期限(月)" prop="loanTerm">
|
||||
<el-input v-model.number="form.loanTerm" type="number" placeholder="请输入贷款期限"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 企业标识 -->
|
||||
<el-divider content-position="left">企业标识</el-divider>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="省农担担保贷款" prop="isAgriGuar">
|
||||
<el-switch v-model="form.isAgriGuar"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="绿色贷款" prop="isGreenLoan">
|
||||
<el-switch v-model="form.isGreenLoan"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="科技型企业" prop="isTechEnt">
|
||||
<el-switch v-model="form.isTechEnt"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="贸易和建筑业企业" prop="isTradeConstruction">
|
||||
<el-switch v-model="form.isTradeConstruction"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 抵质押信息 -->
|
||||
<el-divider content-position="left">抵质押信息</el-divider>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="抵质押类型" prop="collType">
|
||||
<el-select v-model="form.collType" placeholder="请选择抵质押类型" style="width: 100%">
|
||||
<el-option label="一线" value="一线"/>
|
||||
<el-option label="一类" value="一类"/>
|
||||
<el-option label="二类" value="二类"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="抵质押物三方所有" prop="collThirdParty">
|
||||
<el-switch v-model="form.collThirdParty"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" :loading="submitting" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createCorporateWorkflow} from "@/api/loanPricing/workflow"
|
||||
|
||||
export default {
|
||||
name: "CorporateCreateDialog",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
// 金额验证
|
||||
const validateApplyAmt = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请输入申请金额'))
|
||||
} else {
|
||||
const num = parseFloat(value)
|
||||
if (isNaN(num) || num <= 0) {
|
||||
callback(new Error('请输入有效的金额'))
|
||||
} else if (num > 999999999.99) {
|
||||
callback(new Error('金额不能超过 999999999.99'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 贷款期限验证
|
||||
const validateLoanTerm = (rule, value, callback) => {
|
||||
if (!value && value !== 0) {
|
||||
callback(new Error('请输入贷款期限'))
|
||||
} else {
|
||||
const num = parseInt(value)
|
||||
if (isNaN(num) || num <= 0) {
|
||||
callback(new Error('请输入有效的贷款期限'))
|
||||
} else if (num > 360) {
|
||||
callback(new Error('贷款期限不能超过 360 个月'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
submitting: false,
|
||||
form: {
|
||||
orgCode: '892000',
|
||||
runType: '1',
|
||||
custIsn: undefined,
|
||||
custName: undefined,
|
||||
idType: undefined,
|
||||
idNum: undefined,
|
||||
guarType: undefined,
|
||||
applyAmt: undefined,
|
||||
loanTerm: undefined,
|
||||
isAgriGuar: false,
|
||||
isGreenLoan: false,
|
||||
isTechEnt: false,
|
||||
isTradeConstruction: false,
|
||||
collType: undefined,
|
||||
collThirdParty: false
|
||||
},
|
||||
rules: {
|
||||
custIsn: [
|
||||
{required: true, message: "客户内码不能为空", trigger: "blur"},
|
||||
{min: 1, max: 50, message: "长度在 1 到 50 个字符", trigger: "blur"}
|
||||
],
|
||||
custName: [
|
||||
{required: true, message: "客户名称不能为空", trigger: "blur"},
|
||||
{min: 1, max: 100, message: "长度在 1 到 100 个字符", trigger: "blur"}
|
||||
],
|
||||
idType: [
|
||||
{required: true, message: "请选择证件类型", trigger: "change"}
|
||||
],
|
||||
idNum: [
|
||||
{required: true, message: "证件号码不能为空", trigger: "blur"}
|
||||
],
|
||||
guarType: [
|
||||
{required: true, message: "请选择担保方式", trigger: "change"}
|
||||
],
|
||||
applyAmt: [
|
||||
{required: true, validator: validateApplyAmt, trigger: "blur"}
|
||||
],
|
||||
loanTerm: [
|
||||
{required: true, validator: validateLoanTerm, trigger: "blur"}
|
||||
],
|
||||
collType: [
|
||||
{required: true, message: "请选择抵质押类型", trigger: "change"}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dialogVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:visible', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val) {
|
||||
this.reset()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
orgCode: '892000',
|
||||
runType: '1',
|
||||
custIsn: undefined,
|
||||
custName: undefined,
|
||||
idType: undefined,
|
||||
idNum: undefined,
|
||||
guarType: undefined,
|
||||
applyAmt: undefined,
|
||||
loanTerm: undefined,
|
||||
isAgriGuar: false,
|
||||
isGreenLoan: false,
|
||||
isTechEnt: false,
|
||||
isTradeConstruction: false,
|
||||
collType: undefined,
|
||||
collThirdParty: false
|
||||
}
|
||||
this.submitting = false
|
||||
this.resetForm("form")
|
||||
},
|
||||
/** 对话框关闭处理 */
|
||||
handleClose() {
|
||||
this.cancel()
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.dialogVisible = false
|
||||
this.reset()
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
this.submitting = true
|
||||
// 转换开关值为字符串
|
||||
const data = {
|
||||
...this.form,
|
||||
isAgriGuar: this.form.isAgriGuar ? 'true' : 'false',
|
||||
isGreenLoan: this.form.isGreenLoan ? 'true' : 'false',
|
||||
isTechEnt: this.form.isTechEnt ? 'true' : 'false',
|
||||
isTradeConstruction: this.form.isTradeConstruction ? 'true' : 'false',
|
||||
collThirdParty: this.form.collThirdParty ? 'true' : 'false'
|
||||
}
|
||||
|
||||
createCorporateWorkflow(data).then(response => {
|
||||
this.$modal.msgSuccess("新增成功")
|
||||
this.dialogVisible = false
|
||||
this.$emit('success')
|
||||
}).catch(error => {
|
||||
// 保留表单,显示错误信息
|
||||
console.error('创建失败:', error)
|
||||
}).finally(() => {
|
||||
this.submitting = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.workflow-create-form .el-divider {
|
||||
margin: 8px 0 16px 0;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.workflow-create-form .el-col {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
flex: 0 0 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,324 @@
|
||||
<template>
|
||||
<div class="corporate-workflow-detail" v-loading="loading">
|
||||
<!-- 两栏布局:左侧关键信息 + 右侧(流程详情+模型输出) -->
|
||||
<div v-if="!loading && detailData" class="detail-layout">
|
||||
<!-- 左侧关键信息卡片 -->
|
||||
<div class="left-panel">
|
||||
<el-card class="summary-card">
|
||||
<div slot="header" class="card-header">
|
||||
<span class="card-title">关键信息</span>
|
||||
</div>
|
||||
<el-descriptions :column="1" direction="vertical" border>
|
||||
<el-descriptions-item label="业务方流水号">{{ detailData.serialNum }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ detailData.custName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户类型">{{ detailData.custType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请金额">{{ detailData.applyAmt }} 元</el-descriptions-item>
|
||||
<el-descriptions-item label="基准利率">
|
||||
<span class="rate-value">{{ getBaseLoanRate() }}</span> %
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="浮动BP">
|
||||
<span class="total-bp-value">{{ getTotalBp() }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="测算利率">
|
||||
<span class="calculate-rate">{{ getCalculateRate() }}</span> %
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="执行利率">
|
||||
<div class="execute-rate-input-wrapper">
|
||||
<el-input
|
||||
v-model="executeRateInput"
|
||||
class="execute-rate-input"
|
||||
placeholder="请输入执行利率"
|
||||
>
|
||||
<template slot="append">%</template>
|
||||
</el-input>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleSetExecuteRate"
|
||||
>
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 右侧面板 -->
|
||||
<div class="right-panel">
|
||||
<!-- 模型输出卡片 -->
|
||||
<ModelOutputDisplay
|
||||
:cust-type="detailData.custType"
|
||||
:retail-output="null"
|
||||
:corp-output="corpOutput"
|
||||
/>
|
||||
|
||||
<!-- 流程详情卡片 -->
|
||||
<el-card class="detail-card">
|
||||
<div slot="header" class="card-header">
|
||||
<span class="card-title">流程详情</span>
|
||||
</div>
|
||||
|
||||
<!-- 基本信息组 -->
|
||||
<div class="info-section">
|
||||
<h4 class="section-title">基本信息</h4>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="机构编码">{{ detailData.orgCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="运行模式">{{ detailData.runType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户内码">{{ detailData.custIsn }}</el-descriptions-item>
|
||||
<el-descriptions-item label="证件类型">{{ detailData.idType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="证件号码">{{ detailData.idNum }}</el-descriptions-item>
|
||||
<el-descriptions-item label="贷款期限">{{ detailData.loanTerm || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ detailData.createTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建者">{{ detailData.createBy }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<!-- 业务信息组 -->
|
||||
<div class="info-section">
|
||||
<h4 class="section-title">业务信息</h4>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="担保方式">{{ detailData.guarType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请金额">{{ detailData.applyAmt }} 元</el-descriptions-item>
|
||||
<el-descriptions-item label="省农担担保贷款">{{
|
||||
formatBoolean(detailData.isAgriGuar)
|
||||
}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="绿色贷款">{{ formatBoolean(detailData.isGreenLoan) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="科技型企业">{{ formatBoolean(detailData.isTechEnt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="抵质押类型">{{ detailData.collType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="抵质押物是否三方所有">{{
|
||||
formatBoolean(detailData.collThirdParty)
|
||||
}}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 议价池卡片 -->
|
||||
<BargainingPoolDisplay
|
||||
v-if="bargainingPool"
|
||||
:branch-pool="bargainingPool.branchPool"
|
||||
:sub-branch-pool="bargainingPool.subBranchPool"
|
||||
:private-domain-pool="bargainingPool.privateDomainPool"
|
||||
:excess-profit-share="bargainingPool.excessProfitShare"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {setExecuteRate} from "@/api/loanPricing/workflow"
|
||||
import ModelOutputDisplay from "./ModelOutputDisplay.vue"
|
||||
import BargainingPoolDisplay from "./BargainingPoolDisplay.vue"
|
||||
|
||||
export default {
|
||||
name: "CorporateWorkflowDetail",
|
||||
components: {
|
||||
ModelOutputDisplay,
|
||||
BargainingPoolDisplay
|
||||
},
|
||||
props: {
|
||||
detailData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
corpOutput: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
bargainingPool: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
executeRateInput: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'detailData.executeRate': {
|
||||
handler(newVal) {
|
||||
this.executeRateInput = newVal || ''
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 格式化布尔值为中文 */
|
||||
formatBoolean(value) {
|
||||
if (value === 'true' || value === true || value === '1' || value === 1) return '是'
|
||||
if (value === 'false' || value === false || value === '0' || value === 0) return '否'
|
||||
return value || '-'
|
||||
},
|
||||
/** 获取基准利率 */
|
||||
getBaseLoanRate() {
|
||||
return this.corpOutput?.baseLoanRate || '-'
|
||||
},
|
||||
/** 获取浮动BP */
|
||||
getTotalBp() {
|
||||
return this.corpOutput?.totalBp || '-'
|
||||
},
|
||||
/** 获取测算利率 */
|
||||
getCalculateRate() {
|
||||
return this.corpOutput?.calculateRate || '-'
|
||||
},
|
||||
/** 设定执行利率 */
|
||||
handleSetExecuteRate() {
|
||||
const value = this.executeRateInput
|
||||
if (value === null || value === undefined || value === '') {
|
||||
this.$modal.msgError("请输入执行利率")
|
||||
return
|
||||
}
|
||||
|
||||
const numValue = parseFloat(value)
|
||||
if (isNaN(numValue)) {
|
||||
this.$modal.msgError("请输入有效的数字")
|
||||
return
|
||||
}
|
||||
if (numValue < 0 || numValue > 100) {
|
||||
this.$modal.msgError("执行利率必须在 0 到 100 之间")
|
||||
return
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
setExecuteRate(this.detailData.serialNum, value.toString()).then(() => {
|
||||
this.$modal.msgSuccess("执行利率设定成功")
|
||||
this.$emit('refresh')
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
this.$modal.msgError("设定失败:" + (error.msg || error.message || "未知错误"))
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.corporate-workflow-detail {
|
||||
.detail-layout {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: flex-start;
|
||||
|
||||
.left-panel {
|
||||
flex: 0 0 280px;
|
||||
max-width: 280px;
|
||||
|
||||
.summary-card {
|
||||
::v-deep .el-card__header {
|
||||
padding: 16px 20px;
|
||||
background-color: #fafafa;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-card__body {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.execute-rate-input-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
|
||||
.execute-rate-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.rate-value {
|
||||
color: #67c23a;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.total-bp-value {
|
||||
color: #e6a23c;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.calculate-rate {
|
||||
color: #f56c6c;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
|
||||
.detail-card {
|
||||
::v-deep .el-card__header {
|
||||
padding: 16px 20px;
|
||||
background-color: #fafafa;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-card__body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 992px) {
|
||||
.corporate-workflow-detail {
|
||||
.detail-layout {
|
||||
flex-direction: column;
|
||||
|
||||
.left-panel,
|
||||
.right-panel {
|
||||
flex: 1 1 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<el-dialog title="选择客户类型" :visible.sync="dialogVisible" width="500px" append-to-body
|
||||
:close-on-click-modal="false">
|
||||
<div class="customer-type-selector">
|
||||
<div class="type-card" @click="selectType('personal')">
|
||||
<div class="card-icon">
|
||||
<i class="el-icon-user"></i>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="card-title">个人客户</div>
|
||||
<div class="card-desc">个人客户利率定价</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="type-card" @click="selectType('corporate')">
|
||||
<div class="card-icon">
|
||||
<i class="el-icon-office-building"></i>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="card-title">企业客户</div>
|
||||
<div class="card-desc">企业客户利率定价</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CustomerTypeSelector",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
dialogVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:visible', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectType(type) {
|
||||
this.$emit('select', type)
|
||||
this.dialogVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.customer-type-selector {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.type-card {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 30px 20px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.type-card:hover {
|
||||
border-color: #409EFF;
|
||||
background-color: #f0f7ff;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 48px;
|
||||
color: #409EFF;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,311 @@
|
||||
<template>
|
||||
<el-card class="model-output-card" v-if="custType && (retailOutput || corpOutput)">
|
||||
<div slot="header" class="card-header">
|
||||
<span class="card-title">模型输出</span>
|
||||
</div>
|
||||
<el-tabs v-model="activeTab">
|
||||
<!-- 个人客户模型输出 -->
|
||||
<template v-if="custType === '个人' && retailOutput">
|
||||
<!-- 基本信息 -->
|
||||
<el-tab-pane label="基本信息" name="retail-basic">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="客户内码">{{ retailOutput.custIsn || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ retailOutput.custName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="证件类型">{{ retailOutput.idType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="证件号码">{{ retailOutput.idNum || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="基准利率"><span class="rate-value">{{ retailOutput.baseLoanRate || '-' }}</span> %</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 忠诚度分析 -->
|
||||
<el-tab-pane label="忠诚度分析" name="retail-loyalty">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="我行首贷客户">{{ formatBoolean(retailOutput.isFirstLoan) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用信天数">{{ retailOutput.faithDay || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户年龄">{{ retailOutput.custAge || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_首贷"><span class="bp-value">{{ retailOutput.bpFirstLoan || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="BP_贷龄"><span class="bp-value">{{ retailOutput.bpAgeLoan || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="BP_年龄"><span class="bp-value">{{ retailOutput.bpAge || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_忠诚度" :span="2"><span class="total-bp-value">{{ retailOutput.totalBpLoyalty || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 贡献度分析 -->
|
||||
<el-tab-pane label="贡献度分析" name="retail-contribution">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="存款年日均">{{ retailOutput.balanceAvg || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="贷款年日均">{{ retailOutput.loanAvg || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="派生率">{{ retailOutput.derivationRate || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_贡献度"><span class="total-bp-value">{{ retailOutput.totalBpContribution || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 关联度分析 -->
|
||||
<el-tab-pane label="关联度分析" name="retail-relevance">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="中间业务_个人_信用卡">{{ formatBoolean(retailOutput.midPerCard) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_个人_一码通">{{ formatBoolean(retailOutput.midPerPass) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_个人_丰收互联">{{ formatBoolean(retailOutput.midPerHarvest) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_个人_有效客户">{{ formatBoolean(retailOutput.midPerEffect) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_个人_快捷支付">{{ formatBoolean(retailOutput.midPerQuickPay) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_个人_电费代扣">{{ formatBoolean(retailOutput.midPerEleDdc) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_个人_水费代扣">{{ formatBoolean(retailOutput.midPerWaterDdc) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_个人_华数费代扣">{{ formatBoolean(retailOutput.midPerHuashuDdc) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_个人_煤气费代扣">{{ formatBoolean(retailOutput.MidPerGasDdc) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_个人_市民卡">{{ formatBoolean(retailOutput.midPerCitizencard) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_个人_理财业务">{{ formatBoolean(retailOutput.midPerFinMan) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_个人_etc">{{ formatBoolean(retailOutput.midPerEtc) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_中间业务"><span class="bp-value">{{ retailOutput.bpMid || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_关联度"><span class="total-bp-value">{{ retailOutput.totoalBpRelevance || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 贷款特征 -->
|
||||
<el-tab-pane label="贷款特征" name="retail-loan">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="申请金额">{{ retailOutput.applyAmt || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_贷款额度"><span class="bp-value">{{ retailOutput.bpLoanAmount || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="贷款用途">{{ formatLoanPurpose(retailOutput.loanPurpose) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否有经营佐证">{{ formatBoolean(retailOutput.bizProof) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_贷款用途"><span class="bp-value">{{ retailOutput.bpLoanUse || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="循环功能">{{ formatBoolean(retailOutput.loanLoop) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_循环功能"><span class="bp-value">{{ retailOutput.bpLoanLoop || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="抵质押类型">{{ retailOutput.collType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="抵质押物三方所有">{{ formatBoolean(retailOutput.collThirdParty) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_抵押物"><span class="bp-value">{{ retailOutput.bpCollateral || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 风险度分析 -->
|
||||
<el-tab-pane label="风险度分析" name="retail-risk">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="灰名单客户">{{ formatBoolean(retailOutput.greyCust) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="本金逾期">{{ formatBoolean(retailOutput.prinOverdue) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="利息逾期">{{ formatBoolean(retailOutput.interestOverdue) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="信用卡逾期">{{ formatBoolean(retailOutput.cardOverdue) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_灰名单与逾期"><span class="bp-value">{{ retailOutput.bpGreyOverdue || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_风险度"><span class="total-bp-value">{{ retailOutput.totoalBpRisk || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 测算结果 -->
|
||||
<el-tab-pane label="测算结果" name="retail-result">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="浮动BP"><span class="total-bp-value">{{ retailOutput.totalBp || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="测算利率"><span class="calculate-rate">{{ retailOutput.calculateRate || '-' }}</span> %</el-descriptions-item>
|
||||
<el-descriptions-item label="历史利率">{{ retailOutput.loanRateHistory || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品最低利率下限">{{ retailOutput.minRateProduct || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="平滑幅度">{{ retailOutput.smoothRange || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="最终测算利率"><span class="calculate-rate">{{ retailOutput.finalCalculateRate || '-' }}</span> %</el-descriptions-item>
|
||||
<el-descriptions-item label="参考利率"><span class="calculate-rate">{{ retailOutput.referenceRate || '-' }}</span> %</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</template>
|
||||
|
||||
<!-- 企业客户模型输出 -->
|
||||
<template v-else-if="custType === '企业' && corpOutput">
|
||||
<!-- 基本信息 -->
|
||||
<el-tab-pane label="基本信息" name="corp-basic">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="客户内码">{{ corpOutput.custIsn || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ corpOutput.custName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="证件类型">{{ corpOutput.idType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="证件号码">{{ corpOutput.idNum || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="基准利率"><span class="rate-value">{{ corpOutput.baseLoanRate || '-' }}</span> %</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 忠诚度分析 -->
|
||||
<el-tab-pane label="忠诚度分析" name="corp-loyalty">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="我行首贷客户">{{ formatBoolean(corpOutput.isFirstLoan) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用信天数">{{ corpOutput.faithDay || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_首贷"><span class="bp-value">{{ corpOutput.bpFirstLoan || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="BP_贷龄"><span class="bp-value">{{ corpOutput.bpAgeLoan || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_忠诚度" :span="2"><span class="total-bp-value">{{ corpOutput.totalBpLoyalty || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 贡献度分析 -->
|
||||
<el-tab-pane label="贡献度分析" name="corp-contribution">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="存款年日均">{{ corpOutput.balanceAvg || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="贷款年日均">{{ corpOutput.loanAvg || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="派生率">{{ corpOutput.derivationRate || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_贡献度"><span class="total-bp-value">{{ corpOutput.totalBpContribution || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 关联度分析 -->
|
||||
<el-tab-pane label="关联度分析" name="corp-relevance">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="中间业务_企业_企业互联">{{ formatBoolean(corpOutput.midEntConnect) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_企业_有效价值客户">{{ formatBoolean(corpOutput.midEntEffect) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_企业_国际业务">{{ formatBoolean(corpOutput.midEntInter) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_企业_承兑">{{ formatBoolean(corpOutput.midEntAccept) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_企业_贴现">{{ formatBoolean(corpOutput.midEntDiscount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_企业_电费代扣">{{ formatBoolean(corpOutput.midEntEleDdc) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_企业_水费代扣">{{ formatBoolean(corpOutput.midEntWaterDdc) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="中间业务_企业_税务代扣">{{ formatBoolean(corpOutput.midEntTax) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_中间业务"><span class="bp-value">{{ corpOutput.bpMid || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="代发工资户数">{{ corpOutput.payroll || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="存量贷款余额">{{ corpOutput.invLoanAmount || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_代发工资"><span class="bp-value">{{ corpOutput.bpPayroll || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_关联度"><span class="total-bp-value">{{ corpOutput.totoalBpRelevance || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 企业类别 -->
|
||||
<el-tab-pane label="企业类别" name="corp-category">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="净身企业">{{ formatBoolean(corpOutput.isCleanEnt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="开立基本结算账户">{{ formatBoolean(corpOutput.hasSettleAcct) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="省农担担保贷款">{{ formatBoolean(corpOutput.isAgriGuar) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="绿色贷款">{{ formatBoolean(corpOutput.isGreenLoan) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="科技型企业">{{ formatBoolean(corpOutput.isTechEnt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_企业客户类别"><span class="bp-value">{{ corpOutput.bpEntType || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 贷款特征 -->
|
||||
<el-tab-pane label="贷款特征" name="corp-loan">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="贷款期限">{{ corpOutput.loanTerm || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_贷款期限"><span class="bp-value">{{ corpOutput.bpLoanTerm || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="申请金额">{{ corpOutput.applyAmt || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_贷款额度"><span class="bp-value">{{ corpOutput.bpLoanAmount || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="抵质押类型">{{ corpOutput.collType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="抵质押物三方所有">{{ formatBoolean(corpOutput.collThirdParty) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_抵押物"><span class="bp-value">{{ corpOutput.bpCollateral || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 风险度分析 -->
|
||||
<el-tab-pane label="风险度分析" name="corp-risk">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="灰名单客户">{{ formatBoolean(corpOutput.greyCust) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="本金逾期">{{ formatBoolean(corpOutput.prinOverdue) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="利息逾期">{{ formatBoolean(corpOutput.interestOverdue) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="信用卡逾期">{{ formatBoolean(corpOutput.cardOverdue) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="BP_灰名单与逾期"><span class="bp-value">{{ corpOutput.bpGreyOverdue || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="TOTAL_BP_风险度"><span class="total-bp-value">{{ corpOutput.totoalBpRisk || '-' }}</span></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 测算结果 -->
|
||||
<el-tab-pane label="测算结果" name="corp-result">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="浮动BP"><span class="total-bp-value">{{ corpOutput.totalBp || '-' }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="测算利率"><span class="calculate-rate">{{ corpOutput.calculateRate || '-' }}</span> %</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</template>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ModelOutputDisplay",
|
||||
props: {
|
||||
custType: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
retailOutput: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
corpOutput: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
custType: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
// 根据客户类型设置默认 tab
|
||||
if (val === '个人') {
|
||||
this.activeTab = 'retail-basic'
|
||||
} else if (val === '企业') {
|
||||
this.activeTab = 'corp-basic'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 格式化布尔值为中文 */
|
||||
formatBoolean(value) {
|
||||
if (value === 'true' || value === true || value === '1' || value === 1) return '是'
|
||||
if (value === 'false' || value === false || value === '0' || value === 0) return '否'
|
||||
return value || '-'
|
||||
},
|
||||
/** 格式化贷款用途 */
|
||||
formatLoanPurpose(value) {
|
||||
if (value === 'consumer') return '消费贷款'
|
||||
if (value === 'business') return '经营贷款'
|
||||
return value || '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.model-output-card {
|
||||
::v-deep .el-card__header {
|
||||
padding: 16px 20px;
|
||||
background-color: #fafafa;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-card__body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
// BP 值样式
|
||||
.bp-value {
|
||||
color: #409eff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
// TOTAL_BP 样式
|
||||
.total-bp-value {
|
||||
color: #e6a23c;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
// 测算利率高亮样式
|
||||
.calculate-rate {
|
||||
color: #f56c6c;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
// 利率值样式
|
||||
.rate-value {
|
||||
color: #67c23a;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<el-dialog title="新增个人利率定价流程" :visible.sync="dialogVisible" width="900px" append-to-body
|
||||
@close="handleClose">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="140px" class="workflow-create-form">
|
||||
<!-- 基本信息 -->
|
||||
<el-divider content-position="left">基本信息</el-divider>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户内码" prop="custIsn">
|
||||
<el-input v-model="form.custIsn" placeholder="请输入客户内码"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户名称" prop="custName">
|
||||
<el-input v-model="form.custName" placeholder="请输入客户名称"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="证件类型" prop="idType">
|
||||
<el-select v-model="form.idType" placeholder="请选择证件类型" style="width: 100%">
|
||||
<el-option label="身份证" value="身份证"/>
|
||||
<el-option label="其他证件" value="其他证件"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="证件号码" prop="idNum">
|
||||
<el-input v-model="form.idNum" placeholder="请输入证件号码"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 贷款信息 -->
|
||||
<el-divider content-position="left">贷款信息</el-divider>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="担保方式" prop="guarType">
|
||||
<el-select v-model="form.guarType" placeholder="请选择担保方式" style="width: 100%">
|
||||
<el-option label="信用" value="信用"/>
|
||||
<el-option label="保证" value="保证"/>
|
||||
<el-option label="抵押" value="抵押"/>
|
||||
<el-option label="质押" value="质押"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="申请金额(元)" prop="applyAmt">
|
||||
<el-input v-model="form.applyAmt" placeholder="请输入申请金额"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="贷款用途" prop="loanPurpose">
|
||||
<el-select v-model="form.loanPurpose" placeholder="请选择贷款用途" style="width: 100%">
|
||||
<el-option label="消费" value="consumer"/>
|
||||
<el-option label="经营" value="business"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="借款期限(年)" prop="loanTerm">
|
||||
<el-select v-model="form.loanTerm" placeholder="请选择借款期限" style="width: 100%">
|
||||
<el-option v-for="item in loanTermOptions" :key="item" :label="item" :value="item"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否有经营佐证" prop="bizProof">
|
||||
<el-switch v-model="form.bizProof"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="循环功能" prop="loanLoop">
|
||||
<el-switch v-model="form.loanLoop"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 抵质押信息 -->
|
||||
<el-divider content-position="left">抵质押信息</el-divider>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="抵质押类型" prop="collType">
|
||||
<el-select v-model="form.collType" placeholder="请选择抵质押类型" style="width: 100%">
|
||||
<el-option label="一类" value="一类"/>
|
||||
<el-option label="二类" value="二类"/>
|
||||
<el-option label="三类" value="三类"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="抵质押物三方所有" prop="collThirdParty">
|
||||
<el-switch v-model="form.collThirdParty"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" :loading="submitting" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createPersonalWorkflow} from "@/api/loanPricing/workflow"
|
||||
|
||||
export default {
|
||||
name: "PersonalCreateDialog",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
// 金额验证
|
||||
const validateApplyAmt = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请输入申请金额'))
|
||||
} else {
|
||||
const num = parseFloat(value)
|
||||
if (isNaN(num) || num <= 0) {
|
||||
callback(new Error('请输入有效的金额'))
|
||||
} else if (num > 999999999.99) {
|
||||
callback(new Error('金额不能超过 999999999.99'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
loanTermOptions: [
|
||||
'1', '2', '3', '4', '5', '6'
|
||||
],
|
||||
submitting: false,
|
||||
form: {
|
||||
orgCode: '892000',
|
||||
runType: '1',
|
||||
custIsn: undefined,
|
||||
custName: undefined,
|
||||
idType: undefined,
|
||||
idNum: undefined,
|
||||
guarType: undefined,
|
||||
applyAmt: undefined,
|
||||
loanPurpose: undefined,
|
||||
loanTerm: undefined,
|
||||
bizProof: false,
|
||||
loanLoop: false,
|
||||
collType: undefined,
|
||||
collThirdParty: false
|
||||
},
|
||||
rules: {
|
||||
custIsn: [
|
||||
{required: true, message: "客户内码不能为空", trigger: "blur"},
|
||||
{min: 1, max: 50, message: "长度在 1 到 50 个字符", trigger: "blur"}
|
||||
],
|
||||
custName: [
|
||||
{required: true, message: "客户名称不能为空", trigger: "blur"},
|
||||
{min: 1, max: 100, message: "长度在 1 到 100 个字符", trigger: "blur"}
|
||||
],
|
||||
idType: [
|
||||
{required: true, message: "请选择证件类型", trigger: "change"}
|
||||
],
|
||||
idNum: [
|
||||
{required: true, message: "证件号码不能为空", trigger: "blur"}
|
||||
],
|
||||
guarType: [
|
||||
{required: true, message: "请选择担保方式", trigger: "change"}
|
||||
],
|
||||
applyAmt: [
|
||||
{required: true, validator: validateApplyAmt, trigger: "blur"}
|
||||
],
|
||||
loanPurpose: [
|
||||
{required: true, message: "请选择贷款用途", trigger: "change"}
|
||||
],
|
||||
loanTerm: [
|
||||
{required: true, message: "请选择借款期限", trigger: "change"}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dialogVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:visible', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val) {
|
||||
this.reset()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
orgCode: '892000',
|
||||
runType: '1',
|
||||
custIsn: undefined,
|
||||
custName: undefined,
|
||||
idType: undefined,
|
||||
idNum: undefined,
|
||||
guarType: undefined,
|
||||
applyAmt: undefined,
|
||||
loanPurpose: undefined,
|
||||
loanTerm: undefined,
|
||||
bizProof: false,
|
||||
loanLoop: false,
|
||||
collType: undefined,
|
||||
collThirdParty: false
|
||||
}
|
||||
this.submitting = false
|
||||
this.resetForm("form")
|
||||
},
|
||||
/** 对话框关闭处理 */
|
||||
handleClose() {
|
||||
this.cancel()
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.dialogVisible = false
|
||||
this.reset()
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
this.submitting = true
|
||||
// 转换开关值为字符串
|
||||
const data = {
|
||||
...this.form,
|
||||
bizProof: this.form.bizProof ? '1' : '0',
|
||||
loanLoop: this.form.loanLoop ? '1' : '0',
|
||||
collThirdParty: this.form.collThirdParty ? '1' : '0'
|
||||
}
|
||||
|
||||
createPersonalWorkflow(data).then(response => {
|
||||
this.$modal.msgSuccess("新增成功")
|
||||
this.dialogVisible = false
|
||||
this.$emit('success')
|
||||
}).catch(error => {
|
||||
// 保留表单,显示错误信息
|
||||
console.error('创建失败:', error)
|
||||
}).finally(() => {
|
||||
this.submitting = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.workflow-create-form .el-divider {
|
||||
margin: 8px 0 16px 0;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.workflow-create-form .el-col {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
flex: 0 0 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,330 @@
|
||||
<template>
|
||||
<div class="personal-workflow-detail" v-loading="loading">
|
||||
<!-- 两栏布局:左侧关键信息 + 右侧(流程详情+模型输出) -->
|
||||
<div v-if="!loading && detailData" class="detail-layout">
|
||||
<!-- 左侧关键信息卡片 -->
|
||||
<div class="left-panel">
|
||||
<el-card class="summary-card">
|
||||
<div slot="header" class="card-header">
|
||||
<span class="card-title">关键信息</span>
|
||||
</div>
|
||||
<el-descriptions :column="1" direction="vertical" border>
|
||||
<el-descriptions-item label="业务方流水号">{{ detailData.serialNum }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ detailData.custName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户类型">{{ detailData.custType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请金额">{{ detailData.applyAmt }} 元</el-descriptions-item>
|
||||
<el-descriptions-item label="基准利率">
|
||||
<span class="rate-value">{{ getBaseLoanRate() }}</span> %
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="浮动BP">
|
||||
<span class="total-bp-value">{{ getTotalBp() }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="最终测算利率">
|
||||
<span class="calculate-rate">{{ getCalculateRate() }}</span> %
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="执行利率">
|
||||
<div class="execute-rate-input-wrapper">
|
||||
<el-input
|
||||
v-model="executeRateInput"
|
||||
class="execute-rate-input"
|
||||
placeholder="请输入执行利率"
|
||||
>
|
||||
<template slot="append">%</template>
|
||||
</el-input>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleSetExecuteRate"
|
||||
>
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 右侧面板 -->
|
||||
<div class="right-panel">
|
||||
<!-- 模型输出卡片 -->
|
||||
<ModelOutputDisplay
|
||||
:cust-type="detailData.custType"
|
||||
:retail-output="retailOutput"
|
||||
:corp-output="null"
|
||||
/>
|
||||
|
||||
<!-- 流程详情卡片 -->
|
||||
<el-card class="detail-card">
|
||||
<div slot="header" class="card-header">
|
||||
<span class="card-title">流程详情</span>
|
||||
</div>
|
||||
|
||||
<!-- 基本信息组 -->
|
||||
<div class="info-section">
|
||||
<h4 class="section-title">基本信息</h4>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="机构编码">{{ detailData.orgCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="运行模式">{{ detailData.runType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户内码">{{ detailData.custIsn }}</el-descriptions-item>
|
||||
<el-descriptions-item label="证件类型">{{ detailData.idType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="证件号码">{{ detailData.idNum }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ detailData.createTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建者">{{ detailData.createBy }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<!-- 业务信息组 -->
|
||||
<div class="info-section">
|
||||
<h4 class="section-title">业务信息</h4>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="担保方式">{{ detailData.guarType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请金额">{{ detailData.applyAmt }} 元</el-descriptions-item>
|
||||
<el-descriptions-item label="贷款用途">{{ formatLoanPurpose(detailData.loanPurpose) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="借款期限">{{ detailData.loanTerm || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否有经营佐证">{{
|
||||
formatBoolean(detailData.bizProof)
|
||||
}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="循环功能">{{ formatBoolean(detailData.loanLoop) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="抵质押类型">{{ detailData.collType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="抵质押物是否三方所有">{{
|
||||
formatBoolean(detailData.collThirdParty)
|
||||
}}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 议价池卡片 -->
|
||||
<BargainingPoolDisplay
|
||||
v-if="bargainingPool"
|
||||
:branch-pool="bargainingPool.branchPool"
|
||||
:sub-branch-pool="bargainingPool.subBranchPool"
|
||||
:private-domain-pool="bargainingPool.privateDomainPool"
|
||||
:excess-profit-share="bargainingPool.excessProfitShare"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {setExecuteRate} from "@/api/loanPricing/workflow"
|
||||
import ModelOutputDisplay from "./ModelOutputDisplay.vue"
|
||||
import BargainingPoolDisplay from "./BargainingPoolDisplay.vue"
|
||||
|
||||
export default {
|
||||
name: "PersonalWorkflowDetail",
|
||||
components: {
|
||||
ModelOutputDisplay,
|
||||
BargainingPoolDisplay
|
||||
},
|
||||
props: {
|
||||
detailData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
retailOutput: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
bargainingPool: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
executeRateInput: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'detailData.executeRate': {
|
||||
handler(newVal) {
|
||||
this.executeRateInput = newVal || ''
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 格式化布尔值为中文 */
|
||||
formatBoolean(value) {
|
||||
if (value === 'true' || value === true || value === '1' || value === 1) return '是'
|
||||
if (value === 'false' || value === false || value === '0' || value === 0) return '否'
|
||||
return value || '-'
|
||||
},
|
||||
/** 格式化贷款用途 */
|
||||
formatLoanPurpose(value) {
|
||||
if (value === 'consumer') return '消费'
|
||||
if (value === 'business') return '经营'
|
||||
return value || '-'
|
||||
},
|
||||
/** 获取基准利率 */
|
||||
getBaseLoanRate() {
|
||||
return this.retailOutput?.baseLoanRate || '-'
|
||||
},
|
||||
/** 获取浮动BP */
|
||||
getTotalBp() {
|
||||
return this.retailOutput?.totalBp || '-'
|
||||
},
|
||||
/** 获取最终测算利率 */
|
||||
getCalculateRate() {
|
||||
return this.retailOutput?.finalCalculateRate || '-'
|
||||
},
|
||||
/** 设定执行利率 */
|
||||
handleSetExecuteRate() {
|
||||
const value = this.executeRateInput
|
||||
if (value === null || value === undefined || value === '') {
|
||||
this.$modal.msgError("请输入执行利率")
|
||||
return
|
||||
}
|
||||
|
||||
const numValue = parseFloat(value)
|
||||
if (isNaN(numValue)) {
|
||||
this.$modal.msgError("请输入有效的数字")
|
||||
return
|
||||
}
|
||||
if (numValue < 0 || numValue > 100) {
|
||||
this.$modal.msgError("执行利率必须在 0 到 100 之间")
|
||||
return
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
setExecuteRate(this.detailData.serialNum, value.toString()).then(() => {
|
||||
this.$modal.msgSuccess("执行利率设定成功")
|
||||
this.$emit('refresh')
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
this.$modal.msgError("设定失败:" + (error.msg || error.message || "未知错误"))
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.personal-workflow-detail {
|
||||
.detail-layout {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: flex-start;
|
||||
|
||||
.left-panel {
|
||||
flex: 0 0 280px;
|
||||
max-width: 280px;
|
||||
|
||||
.summary-card {
|
||||
::v-deep .el-card__header {
|
||||
padding: 16px 20px;
|
||||
background-color: #fafafa;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-card__body {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.execute-rate-input-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
|
||||
.execute-rate-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.rate-value {
|
||||
color: #67c23a;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.total-bp-value {
|
||||
color: #e6a23c;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.calculate-rate {
|
||||
color: #f56c6c;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
|
||||
.detail-card {
|
||||
::v-deep .el-card__header {
|
||||
padding: 16px 20px;
|
||||
background-color: #fafafa;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-card__body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 992px) {
|
||||
.personal-workflow-detail {
|
||||
.detail-layout {
|
||||
flex-direction: column;
|
||||
|
||||
.left-panel,
|
||||
.right-panel {
|
||||
flex: 1 1 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
97
ruoyi-ui/src/views/loanPricing/workflow/detail.vue
Normal file
97
ruoyi-ui/src/views/loanPricing/workflow/detail.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="app-container workflow-detail-container" v-loading="loading">
|
||||
<!-- 页面头部:标题和返回按钮 -->
|
||||
<div class="page-header">
|
||||
<h2 class="page-title">流程详情</h2>
|
||||
<el-button icon="el-icon-back" size="small" @click="goBack">返回</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 根据客户类型渲染对应的详情组件 -->
|
||||
<personal-workflow-detail
|
||||
v-if="!loading && workflowDetail && workflowDetail.custType === '个人'"
|
||||
:detail-data="workflowDetail"
|
||||
:retail-output="retailOutput"
|
||||
:bargaining-pool="bargainingPool"
|
||||
@refresh="getDetail"
|
||||
/>
|
||||
|
||||
<corporate-workflow-detail
|
||||
v-if="!loading && workflowDetail && workflowDetail.custType === '企业'"
|
||||
:detail-data="workflowDetail"
|
||||
:corp-output="corpOutput"
|
||||
:bargaining-pool="bargainingPool"
|
||||
@refresh="getDetail"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getWorkflow} from "@/api/loanPricing/workflow"
|
||||
import PersonalWorkflowDetail from "./components/PersonalWorkflowDetail.vue"
|
||||
import CorporateWorkflowDetail from "./components/CorporateWorkflowDetail.vue"
|
||||
|
||||
export default {
|
||||
name: "LoanPricingWorkflowDetail",
|
||||
components: {
|
||||
PersonalWorkflowDetail,
|
||||
CorporateWorkflowDetail
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
workflowDetail: null,
|
||||
retailOutput: null,
|
||||
corpOutput: null,
|
||||
bargainingPool: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getDetail()
|
||||
},
|
||||
methods: {
|
||||
/** 获取流程详情 */
|
||||
getDetail() {
|
||||
const serialNum = this.$route.params.serialNum
|
||||
if (!serialNum) {
|
||||
this.$modal.msgError("缺少业务方流水号参数")
|
||||
this.goBack()
|
||||
return
|
||||
}
|
||||
|
||||
getWorkflow(serialNum).then(response => {
|
||||
this.workflowDetail = response.data.loanPricingWorkflow
|
||||
this.retailOutput = response.data.modelRetailOutputFields
|
||||
this.corpOutput = response.data.modelCorpOutputFields
|
||||
this.bargainingPool = response.data.bargainingPool
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
this.$modal.msgError("获取流程详情失败:" + (error.message || "未知错误"))
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 返回上一页 */
|
||||
goBack() {
|
||||
this.$router.go(-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.workflow-detail-container {
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 0 4px;
|
||||
|
||||
.page-title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
182
ruoyi-ui/src/views/loanPricing/workflow/index.vue
Normal file
182
ruoyi-ui/src/views/loanPricing/workflow/index.vue
Normal file
@@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="客户内码" prop="custIsn">
|
||||
<el-input
|
||||
v-model="queryParams.custIsn"
|
||||
placeholder="请输入客户内码"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建者" prop="createBy">
|
||||
<el-input
|
||||
v-model="queryParams.createBy"
|
||||
placeholder="请输入创建者"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="机构号" prop="orgCode">
|
||||
<el-input
|
||||
v-model="queryParams.orgCode"
|
||||
placeholder="请输入机构号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="workflowList">
|
||||
<el-table-column label="业务方流水号" align="center" prop="serialNum" width="180" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="客户名称" align="center" prop="custName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="客户类型" align="center" prop="custType" width="100" />
|
||||
<el-table-column label="担保方式" align="center" prop="guarType" width="100" />
|
||||
<el-table-column label="申请金额(元)" align="center" prop="applyAmt" width="120" />
|
||||
<el-table-column label="测算利率(%)" align="center" prop="calculateRate" width="100" />
|
||||
<el-table-column label="执行利率(%)" align="center" prop="executeRate" width="100" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建者" align="center" prop="createBy" width="120" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-view"
|
||||
@click="handleView(scope.row)"
|
||||
>查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 客户类型选择对话框 -->
|
||||
<customer-type-selector :visible.sync="showTypeSelector" @select="handleSelectType"/>
|
||||
|
||||
<!-- 个人客户创建对话框 -->
|
||||
<personal-create-dialog :visible.sync="showPersonalDialog" @success="handleCreateSuccess"/>
|
||||
|
||||
<!-- 企业客户创建对话框 -->
|
||||
<corporate-create-dialog :visible.sync="showCorporateDialog" @success="handleCreateSuccess"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {listWorkflow} from "@/api/loanPricing/workflow"
|
||||
import CustomerTypeSelector from "./components/CustomerTypeSelector"
|
||||
import PersonalCreateDialog from "./components/PersonalCreateDialog"
|
||||
import CorporateCreateDialog from "./components/CorporateCreateDialog"
|
||||
|
||||
export default {
|
||||
name: "LoanPricingWorkflow",
|
||||
components: {
|
||||
CustomerTypeSelector,
|
||||
PersonalCreateDialog,
|
||||
CorporateCreateDialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 利率定价流程表格数据
|
||||
workflowList: [],
|
||||
// 是否显示客户类型选择弹出层
|
||||
showTypeSelector: false,
|
||||
// 是否显示个人客户创建弹出层
|
||||
showPersonalDialog: false,
|
||||
// 是否显示企业客户创建弹出层
|
||||
showCorporateDialog: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
custIsn: undefined,
|
||||
createBy: undefined,
|
||||
orgCode: undefined
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
activated() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
/** 查询利率定价流程列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
listWorkflow(this.queryParams).then(response => {
|
||||
this.workflowList = response.rows
|
||||
this.total = response.total
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm")
|
||||
this.handleQuery()
|
||||
},
|
||||
/** 查看详情操作 */
|
||||
handleView(row) {
|
||||
this.$router.push({
|
||||
name: 'LoanPricingWorkflowDetail',
|
||||
params: { serialNum: row.serialNum }
|
||||
})
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.showTypeSelector = true
|
||||
},
|
||||
/** 选择客户类型回调 */
|
||||
handleSelectType(type) {
|
||||
if (type === 'personal') {
|
||||
this.showPersonalDialog = true
|
||||
} else if (type === 'corporate') {
|
||||
this.showCorporateDialog = true
|
||||
}
|
||||
},
|
||||
/** 创建成功回调 */
|
||||
handleCreateSuccess() {
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -73,13 +73,13 @@ export default {
|
||||
return {
|
||||
title: process.env.VUE_APP_TITLE,
|
||||
footerContent: defaultSettings.footerContent,
|
||||
codeUrl: "",
|
||||
loginForm: {
|
||||
username: "admin",
|
||||
password: "admin123",
|
||||
rememberMe: false,
|
||||
code: "",
|
||||
uuid: ""
|
||||
codeUrl: "",
|
||||
loginForm: {
|
||||
username: "",
|
||||
password: "",
|
||||
rememberMe: false,
|
||||
code: "",
|
||||
uuid: ""
|
||||
},
|
||||
loginRules: {
|
||||
username: [
|
||||
|
||||
34
ruoyi-ui/tests/id-number-validation-removal.test.js
Normal file
34
ruoyi-ui/tests/id-number-validation-removal.test.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
function read(relativePath) {
|
||||
return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8')
|
||||
}
|
||||
|
||||
const personalCreateDialog = read('src/views/loanPricing/workflow/components/PersonalCreateDialog.vue')
|
||||
const corporateCreateDialog = read('src/views/loanPricing/workflow/components/CorporateCreateDialog.vue')
|
||||
|
||||
assert(
|
||||
!personalCreateDialog.includes('const validateIdNum ='),
|
||||
'个人新增弹窗仍包含证件号码格式校验函数'
|
||||
)
|
||||
|
||||
assert(
|
||||
!corporateCreateDialog.includes('const validateIdNum ='),
|
||||
'企业新增弹窗仍包含证件号码格式校验函数'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes("idNum: [") &&
|
||||
personalCreateDialog.includes('{required: true, message: "证件号码不能为空", trigger: "blur"}'),
|
||||
'个人新增弹窗证件号码规则应仅保留必填'
|
||||
)
|
||||
|
||||
assert(
|
||||
corporateCreateDialog.includes("idNum: [") &&
|
||||
corporateCreateDialog.includes('{required: true, message: "证件号码不能为空", trigger: "blur"}'),
|
||||
'企业新增弹窗证件号码规则应仅保留必填'
|
||||
)
|
||||
|
||||
console.log('id number validation removal assertions passed')
|
||||
30
ruoyi-ui/tests/login-default-credentials.test.js
Normal file
30
ruoyi-ui/tests/login-default-credentials.test.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
const loginViewSource = fs.readFileSync(
|
||||
path.join(__dirname, '../src/views/login.vue'),
|
||||
'utf8'
|
||||
)
|
||||
|
||||
assert(
|
||||
/loginForm:\s*\{[\s\S]*username:\s*""/.test(loginViewSource),
|
||||
'登录页默认用户名应为空字符串'
|
||||
)
|
||||
|
||||
assert(
|
||||
/loginForm:\s*\{[\s\S]*password:\s*""/.test(loginViewSource),
|
||||
'登录页默认密码应为空字符串'
|
||||
)
|
||||
|
||||
assert(
|
||||
/username:\s*username === undefined \? this\.loginForm\.username : username/.test(loginViewSource),
|
||||
'登录页应继续支持从 cookie 回填用户名'
|
||||
)
|
||||
|
||||
assert(
|
||||
/password:\s*password === undefined \? this\.loginForm\.password : decrypt\(password\)/.test(loginViewSource),
|
||||
'登录页应继续支持从 cookie 回填密码'
|
||||
)
|
||||
|
||||
console.log('login default credentials assertions passed')
|
||||
88
ruoyi-ui/tests/password-transfer-api.test.js
Normal file
88
ruoyi-ui/tests/password-transfer-api.test.js
Normal file
@@ -0,0 +1,88 @@
|
||||
const assert = require('assert')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const vm = require('vm')
|
||||
|
||||
function loadModule(filePath, stubs = {}) {
|
||||
const source = fs.readFileSync(filePath, 'utf8')
|
||||
const exportedNames = []
|
||||
const transformed = source
|
||||
.replace(/^import .*$/gm, '')
|
||||
.replace(/export function\s+([A-Za-z0-9_]+)\s*\(/g, (_, name) => {
|
||||
exportedNames.push(name)
|
||||
return `function ${name}(`
|
||||
})
|
||||
.replace(/export default\s+/g, 'module.exports = ')
|
||||
|
||||
const sandbox = {
|
||||
module: { exports: {} },
|
||||
exports: {},
|
||||
require,
|
||||
console,
|
||||
process: {
|
||||
env: {
|
||||
VUE_APP_PASSWORD_TRANSFER_KEY: '1234567890abcdef'
|
||||
}
|
||||
},
|
||||
...stubs
|
||||
}
|
||||
|
||||
vm.runInNewContext(
|
||||
`${transformed}\nmodule.exports = { ${exportedNames.join(', ')} };`,
|
||||
sandbox,
|
||||
{ filename: filePath }
|
||||
)
|
||||
|
||||
return sandbox.module.exports
|
||||
}
|
||||
|
||||
const passwordTransferModule = loadModule(
|
||||
path.resolve(__dirname, '../src/utils/passwordTransfer.js'),
|
||||
{ CryptoJS: require('crypto-js') }
|
||||
)
|
||||
|
||||
const { encryptPasswordFields } = passwordTransferModule
|
||||
|
||||
const encrypted = encryptPasswordFields(
|
||||
{ password: 'admin123', code: '8888' },
|
||||
['password'],
|
||||
'1234567890abcdef'
|
||||
)
|
||||
|
||||
assert.notStrictEqual(encrypted.password, 'admin123')
|
||||
assert.strictEqual(encrypted.code, '8888')
|
||||
|
||||
const request = config => config
|
||||
const loginModule = loadModule(
|
||||
path.resolve(__dirname, '../src/api/login.js'),
|
||||
{ request, encryptPasswordFields }
|
||||
)
|
||||
|
||||
const loginConfig = loginModule.login('admin', 'admin123', '8888', 'uuid-1')
|
||||
assert.notStrictEqual(loginConfig.data.password, 'admin123')
|
||||
assert.strictEqual(loginConfig.data.username, 'admin')
|
||||
|
||||
const registerConfig = loginModule.register({ username: 'u1', password: 'p1', confirmPassword: 'p1', code: '8888' })
|
||||
assert.notStrictEqual(registerConfig.data.password, 'p1')
|
||||
assert.strictEqual(registerConfig.data.confirmPassword, 'p1')
|
||||
|
||||
const userModule = loadModule(
|
||||
path.resolve(__dirname, '../src/api/system/user.js'),
|
||||
{
|
||||
request,
|
||||
encryptPasswordFields,
|
||||
parseStrEmpty: value => value
|
||||
}
|
||||
)
|
||||
|
||||
const updatePwdConfig = userModule.updateUserPwd('oldPwd', 'newPwd')
|
||||
assert.notStrictEqual(updatePwdConfig.data.oldPassword, 'oldPwd')
|
||||
assert.notStrictEqual(updatePwdConfig.data.newPassword, 'newPwd')
|
||||
|
||||
const addUserConfig = userModule.addUser({ userName: 'u1', password: 'initPwd', nickName: 'n1' })
|
||||
assert.notStrictEqual(addUserConfig.data.password, 'initPwd')
|
||||
|
||||
const resetUserPwdConfig = userModule.resetUserPwd(2, 'resetPwd')
|
||||
assert.notStrictEqual(resetUserPwdConfig.data.password, 'resetPwd')
|
||||
|
||||
console.log('password-transfer-api test passed')
|
||||
65
ruoyi-ui/tests/personal-create-input-params.test.js
Normal file
65
ruoyi-ui/tests/personal-create-input-params.test.js
Normal file
@@ -0,0 +1,65 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
function read(relativePath) {
|
||||
return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8')
|
||||
}
|
||||
|
||||
const personalCreateDialog = read('src/views/loanPricing/workflow/components/PersonalCreateDialog.vue')
|
||||
const personalDetail = read('src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue')
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes('label="贷款用途"') && personalCreateDialog.includes('prop="loanPurpose"'),
|
||||
'个人新增弹窗缺少贷款用途字段'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes('label="借款期限(年)"') && personalCreateDialog.includes('prop="loanTerm"'),
|
||||
'个人新增弹窗缺少借款期限字段'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes("value=\"consumer\"") && personalCreateDialog.includes("value=\"business\""),
|
||||
'个人新增弹窗缺少贷款用途选项'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes('loanTermOptions') &&
|
||||
personalCreateDialog.includes("'1'") &&
|
||||
personalCreateDialog.includes("'6'") &&
|
||||
!personalCreateDialog.includes("'7'"),
|
||||
'个人新增弹窗借款期限选项应限制为 1-6 年'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes('label="一类"') &&
|
||||
personalCreateDialog.includes('label="二类"') &&
|
||||
personalCreateDialog.includes('label="三类"') &&
|
||||
!personalCreateDialog.includes('label="一线"'),
|
||||
'个人新增弹窗抵质押类型选项未按 Excel 对齐'
|
||||
)
|
||||
|
||||
assert(
|
||||
!personalCreateDialog.includes('{required: true, message: "请选择抵质押类型", trigger: "change"}'),
|
||||
'个人新增弹窗仍将抵质押类型设为必填'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalCreateDialog.includes("bizProof: this.form.bizProof ? '1' : '0'") &&
|
||||
personalCreateDialog.includes("loanLoop: this.form.loanLoop ? '1' : '0'") &&
|
||||
personalCreateDialog.includes("collThirdParty: this.form.collThirdParty ? '1' : '0'"),
|
||||
'个人新增弹窗开关字段未按 1/0 提交'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalDetail.includes('label="贷款用途"') && personalDetail.includes('detailData.loanPurpose'),
|
||||
'个人详情页缺少贷款用途展示'
|
||||
)
|
||||
|
||||
assert(
|
||||
personalDetail.includes("value === '1'") && personalDetail.includes("value === '0'"),
|
||||
'个人详情页布尔格式化未兼容 1/0'
|
||||
)
|
||||
|
||||
console.log('personal create input params assertions passed')
|
||||
21
ruoyi-ui/tests/personal-final-calculate-rate-display.test.js
Normal file
21
ruoyi-ui/tests/personal-final-calculate-rate-display.test.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
function read(relativePath) {
|
||||
return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8')
|
||||
}
|
||||
|
||||
const personalDetail = read('src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue')
|
||||
|
||||
assert(
|
||||
/label="最终测算利率"/.test(personalDetail),
|
||||
'个人流程详情左侧缺少“最终测算利率”标签'
|
||||
)
|
||||
|
||||
assert(
|
||||
/return this\.retailOutput\?\.finalCalculateRate \|\| '-'/.test(personalDetail),
|
||||
'个人流程详情没有使用 finalCalculateRate 展示最终测算利率'
|
||||
)
|
||||
|
||||
console.log('personal final calculate rate display assertions passed')
|
||||
29
ruoyi-ui/tests/retail-display-fields.test.js
Normal file
29
ruoyi-ui/tests/retail-display-fields.test.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
function read(relativePath) {
|
||||
return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8')
|
||||
}
|
||||
|
||||
const personalDetail = read('src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue')
|
||||
const modelOutput = read('src/views/loanPricing/workflow/components/ModelOutputDisplay.vue')
|
||||
|
||||
assert(
|
||||
personalDetail.includes('label="借款期限"') && personalDetail.includes('detailData.loanTerm'),
|
||||
'个人详情页缺少借款期限展示'
|
||||
)
|
||||
|
||||
const requiredRetailFields = [
|
||||
'retailOutput.loanRateHistory',
|
||||
'retailOutput.minRateProduct',
|
||||
'retailOutput.smoothRange',
|
||||
'retailOutput.finalCalculateRate',
|
||||
'retailOutput.referenceRate'
|
||||
]
|
||||
|
||||
requiredRetailFields.forEach((field) => {
|
||||
assert(modelOutput.includes(field), `模型输出缺少字段展示: ${field}`)
|
||||
})
|
||||
|
||||
console.log('retail display fields assertions passed')
|
||||
27
ruoyi-ui/tests/workflow-detail-card-order.test.js
Normal file
27
ruoyi-ui/tests/workflow-detail-card-order.test.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
|
||||
function read(relativePath) {
|
||||
return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8')
|
||||
}
|
||||
|
||||
function assertModelOutputBeforeDetailCard(source, label) {
|
||||
const modelOutputIndex = source.indexOf('<ModelOutputDisplay')
|
||||
const detailCardIndex = source.indexOf('<el-card class="detail-card">')
|
||||
|
||||
assert(modelOutputIndex !== -1, `${label} 缺少模型输出卡片`)
|
||||
assert(detailCardIndex !== -1, `${label} 缺少流程详情卡片`)
|
||||
assert(
|
||||
modelOutputIndex < detailCardIndex,
|
||||
`${label} 的模型输出卡片应位于流程详情卡片上方`
|
||||
)
|
||||
}
|
||||
|
||||
const personalDetail = read('src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue')
|
||||
const corporateDetail = read('src/views/loanPricing/workflow/components/CorporateWorkflowDetail.vue')
|
||||
|
||||
assertModelOutputBeforeDetailCard(personalDetail, '个人流程详情')
|
||||
assertModelOutputBeforeDetailCard(corporateDetail, '企业流程详情')
|
||||
|
||||
console.log('workflow detail card order assertions passed')
|
||||
52
ruoyi-ui/tests/workflow-index-refresh.test.js
Normal file
52
ruoyi-ui/tests/workflow-index-refresh.test.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const assert = require('assert')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const vm = require('vm')
|
||||
|
||||
function loadComponentOptions(filePath) {
|
||||
const source = fs.readFileSync(filePath, 'utf8')
|
||||
const scriptMatch = source.match(/<script>([\s\S]*?)<\/script>/)
|
||||
|
||||
if (!scriptMatch) {
|
||||
throw new Error('未找到组件脚本内容')
|
||||
}
|
||||
|
||||
const importNames = []
|
||||
const importPattern = /^import\s+([A-Za-z0-9_]+)\s+from\s+.*$/gm
|
||||
let importMatch = importPattern.exec(scriptMatch[1])
|
||||
while (importMatch) {
|
||||
importNames.push(importMatch[1])
|
||||
importMatch = importPattern.exec(scriptMatch[1])
|
||||
}
|
||||
|
||||
const stubImports = importNames.map(name => `const ${name} = {};`).join('\n')
|
||||
const transformed = `${stubImports}\n${scriptMatch[1]}`
|
||||
.replace(/^import .*$/gm, '')
|
||||
.replace(/export default/, 'module.exports =')
|
||||
|
||||
const sandbox = {
|
||||
module: { exports: {} },
|
||||
exports: {},
|
||||
require,
|
||||
console
|
||||
}
|
||||
|
||||
vm.runInNewContext(transformed, sandbox, { filename: filePath })
|
||||
return sandbox.module.exports
|
||||
}
|
||||
|
||||
const filePath = path.resolve(__dirname, '../src/views/loanPricing/workflow/index.vue')
|
||||
const component = loadComponentOptions(filePath)
|
||||
|
||||
assert.strictEqual(typeof component.activated, 'function', '流程列表页应在激活时刷新数据')
|
||||
|
||||
let refreshCount = 0
|
||||
component.activated.call({
|
||||
getList() {
|
||||
refreshCount += 1
|
||||
}
|
||||
})
|
||||
|
||||
assert.strictEqual(refreshCount, 1, '流程列表页激活时应调用一次 getList')
|
||||
|
||||
console.log('workflow-index-refresh test passed')
|
||||
Reference in New Issue
Block a user