89 changed files with 622 additions and 5349 deletions
File diff suppressed because one or more lines are too long
@ -1,167 +0,0 @@ |
|||||
/** |
|
||||
* 评级等级下拉选项数组 |
|
||||
*/ |
|
||||
export const RatingLevelOptions = [ |
|
||||
{ label: 'AAA+', value: 'AAA+', numberValue: 15, color: 'green', denseLabel: false }, |
|
||||
{ label: 'AAA', value: 'AAA', numberValue: 14, color: 'green', denseLabel: false }, |
|
||||
{ label: 'AA+', value: 'AA+', numberValue: 13, color: 'green', denseLabel: false }, |
|
||||
{ label: 'AA', value: 'AA', numberValue: 12, color: 'green', denseLabel: false }, |
|
||||
{ label: 'AA-', value: 'AA-', numberValue: 11, color: 'green', denseLabel: false }, |
|
||||
{ label: 'A+', value: 'A+', numberValue: 10, color: 'green', denseLabel: false }, |
|
||||
{ label: 'A', value: 'A', numberValue: 9, color: 'green', denseLabel: false }, |
|
||||
{ label: 'A-', value: 'A-', numberValue: 8, color: 'green', denseLabel: false }, |
|
||||
{ label: 'BBB', value: 'BBB', numberValue: 7, color: 'green', denseLabel: false }, |
|
||||
{ label: 'BB', value: 'BB', numberValue: 6, color: 'red', denseLabel: false }, |
|
||||
{ label: 'B', value: 'B', numberValue: 5, color: 'red', denseLabel: false }, |
|
||||
{ label: 'CCC', value: 'CCC', numberValue: 4, color: 'red', denseLabel: false }, |
|
||||
{ label: 'CC', value: 'CC', numberValue: 3, color: 'red', denseLabel: false }, |
|
||||
{ label: 'C', value: 'C', numberValue: 2, color: 'red', denseLabel: false }, |
|
||||
{ label: 'D', value: 'D', numberValue: 1, color: 'red', denseLabel: false }, |
|
||||
]; |
|
||||
|
|
||||
/** |
|
||||
* 评级步骤对象 |
|
||||
*/ |
|
||||
export const RatingStep = { |
|
||||
CSWC: 'INIT_FINISH', // 初始化完成
|
|
||||
KHXX: 'CUST_INFO', // 客户信息
|
|
||||
DLFX: 'QUANTITATIVE', // 定量分析
|
|
||||
DXFX: 'QUALITATIVE_EDIT', // 定性分析
|
|
||||
CPJG: 'QUALITATIVE_SHOW', // 初评结果
|
|
||||
PJTZX: 'ADJUST_ITEM', // 评级调整项
|
|
||||
QSYJ: 'OTHER', // 签署意见
|
|
||||
PJBG: 'REPORT_INFO', // 评级报告
|
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 评级流程状态对象 |
|
||||
*/ |
|
||||
export const RatingProcessStatus = { |
|
||||
AWAIT_RATING: 'AWAIT_RATING', // 待评级
|
|
||||
AWAIT_SUBMIT: 'AWAIT_SUBMIT', // 待提交
|
|
||||
BACK: 'BACK', // 退回
|
|
||||
APPROVALING: 'APPROVALING', // 审批中
|
|
||||
PASS: 'PASS', // 通过
|
|
||||
NEGATIVED: 'NEGATIVED', // 否决
|
|
||||
END: 'END', // 已结束
|
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 评级流程操作 |
|
||||
*/ |
|
||||
export const RatingProcessOperationStatus = [ |
|
||||
{ |
|
||||
label: '提交', |
|
||||
value: '10', |
|
||||
}, |
|
||||
{ |
|
||||
label: '否决', |
|
||||
value: '20', |
|
||||
}, |
|
||||
{ |
|
||||
label: '退回', |
|
||||
value: '30', |
|
||||
}, |
|
||||
{ |
|
||||
label: '撤销', |
|
||||
value: '40', |
|
||||
}, |
|
||||
{ |
|
||||
label: '批准', |
|
||||
value: '50', |
|
||||
}, |
|
||||
{ |
|
||||
label: '同意', |
|
||||
value: '60', |
|
||||
}, |
|
||||
{ |
|
||||
label: '不同意', |
|
||||
value: '70', |
|
||||
}, |
|
||||
]; |
|
||||
|
|
||||
/** |
|
||||
* 评级流程操作标识 |
|
||||
*/ |
|
||||
export const RatingProcessOperation = { |
|
||||
// 通过
|
|
||||
APPROVE: 1, |
|
||||
// 否决
|
|
||||
REJECT: 2, |
|
||||
// 同意
|
|
||||
AGREE: 4, |
|
||||
// 不同意
|
|
||||
DISAGREE: 5, |
|
||||
// 提交下一步
|
|
||||
SUBMIT: 6, |
|
||||
// 提交到其他上级(信审会)
|
|
||||
UPPER_1: 8, |
|
||||
// 提交到上级
|
|
||||
UPPER: 9, |
|
||||
//退回起始步骤
|
|
||||
BACK_START: -1, |
|
||||
//退回到某步骤
|
|
||||
BACK: -2, |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 违约认定流程状态对象 |
|
||||
*/ |
|
||||
export const DefaultProcessStatus = { |
|
||||
PASS: '03', // 通过
|
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 选择客户标记 |
|
||||
*/ |
|
||||
export const SelectCustomerFlag = { |
|
||||
RATING: 'rating', // 发起评级
|
|
||||
COGNIZANCE: 'cognizance', // 违约认定申请
|
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 财报类型对象 |
|
||||
*/ |
|
||||
export const FinanceReportType = { |
|
||||
QYL: '1', // 企业类
|
|
||||
SYL: '2', // 事业类
|
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* 财报科目类型对象 |
|
||||
*/ |
|
||||
export const FinanceReportProjectType = { |
|
||||
QYLZCFZ: '01', // 企业类资产负债
|
|
||||
QYLXJL: '02', // 企业类现金流
|
|
||||
QYLSY: '03', // 企业类损益
|
|
||||
SYLZCFZ: '11', // 事业类资产负债
|
|
||||
SYLSRZC: '12', // 事业类收入支出表
|
|
||||
}; |
|
||||
|
|
||||
export const Round = (val: any, precision) => { |
|
||||
if (typeof precision !== 'number' || precision <= 0) return val; |
|
||||
let result = val; |
|
||||
if (val && typeof val === 'string') { |
|
||||
const patt = /^-?[0-9]+.?[0-9]*/; |
|
||||
if (patt.test(val)) { |
|
||||
result = parseFloat(val); |
|
||||
} |
|
||||
} |
|
||||
if (result && typeof result === 'number' && String(result).indexOf('.') > -1) { |
|
||||
let scale = '1'; |
|
||||
for (let i = 0; i < precision; i++) { |
|
||||
scale += 0; |
|
||||
} |
|
||||
const pre = 'e' + precision; |
|
||||
const post = 'e-' + precision; |
|
||||
return (+(Math.round(result + pre) + post)).toFixed(precision); |
|
||||
} |
|
||||
return result; |
|
||||
}; |
|
||||
|
|
||||
export const RatioFormat = (val) => { |
|
||||
if (val && typeof val === 'number') { |
|
||||
return Round(val * 100, 2) + '%'; |
|
||||
} |
|
||||
return val; |
|
||||
}; |
|
@ -1,218 +0,0 @@ |
|||||
<template> |
|
||||
<div style="height: 100%"> |
|
||||
<w-grid |
|
||||
ref="companyRatingGridRef" |
|
||||
title="客户评级列表" |
|
||||
:data-url="Environment.apiContextPath('api/irbs/companyRating')" |
|
||||
:fetch-data-url="Environment.apiContextPath('api/irbs/companyRating/query')" |
|
||||
:sort-no="true" |
|
||||
group-mode="alone" |
|
||||
:checkbox-selection="false" |
|
||||
:query-form-cols-num="4" |
|
||||
:query-form-fields="companyRatingGrid.queryFormFields" |
|
||||
:columns="companyRatingGrid.columns" |
|
||||
:toolbar-actions="companyRatingGrid.buttons" |
|
||||
:query-criteria="{ |
|
||||
operator: 'and', |
|
||||
criteria: [ |
|
||||
{ |
|
||||
fieldName: 'triggerType', |
|
||||
operator: 'equals', |
|
||||
value: 'INDEPENDENT', |
|
||||
}, |
|
||||
{ |
|
||||
fieldName: 'launchUser', |
|
||||
operator: 'equals', |
|
||||
value: SessionManager.getUser().loginName, |
|
||||
}, |
|
||||
], |
|
||||
}" |
|
||||
:sort-by="['-createDate']" |
|
||||
></w-grid> |
|
||||
<LaunchRatingDialog ref="launchRatingdialogRef" :dictionary="dictionary" @refresh="refreshTable"></LaunchRatingDialog> |
|
||||
<RatingDialog ref="ratingdialogRef" @refresh="refreshTable"></RatingDialog> |
|
||||
<GrayModelDialog ref="grayDialogRef" @refresh="refreshTable"></GrayModelDialog> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"> |
|
||||
import { ref, shallowRef } from 'vue'; |
|
||||
import { useQuasar } from 'quasar'; |
|
||||
import { Environment, EnumTools, DictionaryTools, Options, Formater, axios, NotifyManager, Tools, SessionManager } from 'platform-core'; |
|
||||
import LaunchRatingDialog from './LaunchRatingDialog.vue'; |
|
||||
import RatingDialog from './RatingDialog.vue'; |
|
||||
import GrayModelDialog from './GrayModelDialog.vue'; |
|
||||
import { RatingLevelOptions, RatingProcessStatus, Round } from './CustRating.ts'; |
|
||||
import RatingLevel from './RatingLevel.vue'; |
|
||||
|
|
||||
const $q = useQuasar(); |
|
||||
const companyRatingGridRef = ref(); |
|
||||
const launchRatingdialogRef = ref(); |
|
||||
const ratingdialogRef = ref(); |
|
||||
const grayDialogRef = ref(); |
|
||||
|
|
||||
const RatingStatusEnum = await EnumTools.fetch('irbs.cust.rating.enums.RatingStatus'); |
|
||||
const RatingProcessStatusEnum = await EnumTools.fetch('irbs.cust.rating.enums.RatingProcessStatus'); |
|
||||
const dictCustomerTypeCd = await DictionaryTools.fetch('CustomerTypeCd'); |
|
||||
const optionsCustomerTypeCd = Options.dictionary(dictCustomerTypeCd); |
|
||||
const dictCustomerSizeCd = await DictionaryTools.fetch('CustomerSizeCd'); |
|
||||
const optionsCustomerSizeCd = Options.dictionary(dictCustomerSizeCd); |
|
||||
|
|
||||
const dictionary = { |
|
||||
dictCustomerTypeCd: dictCustomerTypeCd, |
|
||||
optionsCustomerTypeCd: optionsCustomerTypeCd, |
|
||||
dictCustomerSizeCd: dictCustomerSizeCd, |
|
||||
optionsCustomerSizeCd: optionsCustomerSizeCd, |
|
||||
}; |
|
||||
|
|
||||
const companyRatingGrid = { |
|
||||
buttons: [ |
|
||||
'query', |
|
||||
'reset', |
|
||||
'separator', |
|
||||
{ |
|
||||
extend: 'add', |
|
||||
label: '发起评级', |
|
||||
click: () => { |
|
||||
launchRatingdialogRef.value.show(); |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
extend: 'edit', |
|
||||
label: '评级', |
|
||||
enableIf: (args) => { |
|
||||
if ( |
|
||||
args.selected && |
|
||||
(args.selected['processStatus'] === RatingProcessStatus.AWAIT_RATING || |
|
||||
args.selected['processStatus'] === RatingProcessStatus.AWAIT_SUBMIT || |
|
||||
args.selected['processStatus'] === RatingProcessStatus.BACK) |
|
||||
) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}, |
|
||||
click: (args) => { |
|
||||
ratingdialogRef.value.show(args.selected); |
|
||||
}, |
|
||||
}, |
|
||||
// { |
|
||||
// extend: 'remove', |
|
||||
// label: '撤销', |
|
||||
// click: () => {}, |
|
||||
// }, |
|
||||
'separator', |
|
||||
{ |
|
||||
extend: 'view', |
|
||||
click: (args) => { |
|
||||
ratingdialogRef.value.show(args.selected, true); |
|
||||
}, |
|
||||
}, |
|
||||
'export', |
|
||||
'separator', |
|
||||
[ |
|
||||
{ |
|
||||
name: 'otherOp', |
|
||||
label: '其他操作', |
|
||||
icon: 'device_hub', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'viewGray', |
|
||||
extend: 'view', |
|
||||
label: '查看灰度模型结果', |
|
||||
click: (args) => { |
|
||||
grayDialogRef.value.show(args.selected); |
|
||||
}, |
|
||||
}, |
|
||||
'separator', |
|
||||
{ |
|
||||
extend: 'remove', |
|
||||
name: 'delete', |
|
||||
label: '强制删除', |
|
||||
click: (args) => { |
|
||||
// args._click(); |
|
||||
$q.dialog({ |
|
||||
title: '询问', |
|
||||
message: '是否确认删除已生成评级信息及流程信息?', |
|
||||
cancel: { noCaps: true }, |
|
||||
ok: { noCaps: true }, |
|
||||
persistent: true, |
|
||||
}).onOk(() => { |
|
||||
axios |
|
||||
.post(Environment.apiContextPath('api/irbs/companyRating/removeRatingAndProcess/' + args.selected['id'])) |
|
||||
.then((resp) => { |
|
||||
if (resp && resp.data) { |
|
||||
NotifyManager.info('删除成功'); |
|
||||
companyRatingGridRef.value.refresh(); |
|
||||
} |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
console.info('error====', error); |
|
||||
NotifyManager.warn('删除失败'); |
|
||||
}); |
|
||||
}); |
|
||||
}, |
|
||||
}, |
|
||||
], |
|
||||
'separator', |
|
||||
], |
|
||||
queryFormFields: [ |
|
||||
{ label: '申请编号', name: 'id', type: 'w-text' }, |
|
||||
{ label: '客户号', name: 'custNo', type: 'w-text' }, |
|
||||
{ label: '客户名称', name: 'custName', type: 'w-text' }, |
|
||||
{ |
|
||||
label: '认定等级', |
|
||||
name: 'finalLevel', |
|
||||
type: 'w-select', |
|
||||
options: RatingLevelOptions, |
|
||||
}, |
|
||||
{ label: '流程状态', name: 'processStatus', type: 'w-select', options: Options.enum(RatingProcessStatusEnum) }, |
|
||||
{ label: '评级状态', name: 'ratingStatus', type: 'w-select', options: Options.enum(RatingStatusEnum) }, |
|
||||
], |
|
||||
columns: [ |
|
||||
{ name: 'id', label: '申请编号', align: 'center' }, |
|
||||
{ name: 'custNo', label: '客户号', align: 'center' }, |
|
||||
{ name: 'custName', label: '客户名称', width: 150 }, |
|
||||
{ name: 'industryTypeName', label: '行业类型' }, |
|
||||
{ name: 'modelScore', label: '模型得分' }, |
|
||||
{ name: 'modelLevel', label: '模型等级' }, |
|
||||
{ name: 'adjLevel', label: '调整后等级' }, |
|
||||
{ name: 'initLevel', label: '初评等级' }, |
|
||||
{ |
|
||||
name: 'finalLevel', |
|
||||
label: '认定等级', |
|
||||
format: (val) => { |
|
||||
if (!Tools.isEmpty(val)) { |
|
||||
return { |
|
||||
componentType: RatingLevel, |
|
||||
attrs: { |
|
||||
level: val, |
|
||||
dense: true, |
|
||||
}, |
|
||||
}; |
|
||||
} |
|
||||
return val; |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
name: 'pd', |
|
||||
label: '违约概率', |
|
||||
format: (val) => { |
|
||||
if (val && typeof val === 'number') { |
|
||||
return Round(val * 100, 2) + '%'; |
|
||||
} |
|
||||
return val; |
|
||||
}, |
|
||||
}, |
|
||||
{ name: 'effectiveTime', label: '评级生效日', format: Formater.dateOnly() }, |
|
||||
{ name: 'matureTime', label: '评级失效日', format: Formater.dateOnly() }, |
|
||||
{ name: 'ratingStatus', label: '评级状态', format: Formater.enum(RatingStatusEnum) }, |
|
||||
{ name: 'launchUser', label: '发起人' }, |
|
||||
{ name: 'processStatus', label: '流程状态', format: Formater.enum(RatingProcessStatusEnum) }, |
|
||||
{ name: 'currentAssignee', label: '当前处理人' }, |
|
||||
], |
|
||||
}; |
|
||||
|
|
||||
const refreshTable = () => { |
|
||||
companyRatingGridRef.value.refresh(); |
|
||||
}; |
|
||||
</script> |
|
@ -1,5 +0,0 @@ |
|||||
<template> |
|
||||
<div>111</div> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"></script> |
|
@ -1,5 +0,0 @@ |
|||||
<template> |
|
||||
<div>111</div> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"></script> |
|
@ -1,349 +0,0 @@ |
|||||
<template> |
|
||||
<w-dialog ref="dialogRef" :title="state.dialogTitle" width="80%" height="80%" body-padding="0px 0px 0px 0px" :maximized="true" :buttons="[]"> |
|
||||
<div> |
|
||||
<div class="p-[10px]"> |
|
||||
<span class="text-3xl text-amber-600" |
|
||||
>{{ state.custInfoObj['custName'] }}<q-badge v-if="state.custInfoObj['marketEnterprisesInd'] === '1'" color="red" align="top">上市</q-badge></span |
|
||||
> |
|
||||
<div class="flex pt-2"> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>客户号</div> |
|
||||
<div class="text-blue-500"> |
|
||||
{{ state.custInfoObj['custNo'] }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>企业规模</div> |
|
||||
<div class="text-blue-500"> |
|
||||
{{ Formater.dictionary(dictCustomerSize)(state.custInfoObj['customerSize']) }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>企业类型</div> |
|
||||
<div class="text-blue-500"> |
|
||||
{{ Formater.dictionary(dictRegisteredType)(state.custInfoObj['registeredType']) }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>融资平台标志</div> |
|
||||
<div class="text-blue-500"> |
|
||||
{{ Formater.dictionary(dictGoverFinanceSign)(state.custInfoObj['goverFinanceSign']) }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>融资平台类型</div> |
|
||||
<div class="text-blue-500"> |
|
||||
{{ Formater.dictionary(dictGoverFinanceType)(state.custInfoObj['goverFinanceType']) }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>所在国家地区</div> |
|
||||
<div class="text-blue-500"> |
|
||||
{{ Formater.dictionary(dictNationCd)(state.custInfoObj['nation']) }} |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="pt-3"></div> |
|
||||
<div class="flex"> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>注册所在地</div> |
|
||||
<div class="text-blue-500"> |
|
||||
{{ Formater.dictionary(dictRegistrationCd)(state.custInfoObj['registration']) }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>是否集团客户</div> |
|
||||
<div class="text-blue-500"> |
|
||||
{{ state.custInfoObj['groupCustInd'] === '1' ? '是' : '否' }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>成员类别</div> |
|
||||
<div class="text-blue-500"> |
|
||||
{{ Formater.dictionary(dictMemberTypeCd)(state.custInfoObj['memberType']) }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>行业类型(国标)</div> |
|
||||
<div class="text-blue-500"> |
|
||||
{{ state.custInfoObj['industryTypeName'] }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>成立日期</div> |
|
||||
<div class="text-blue-500"> |
|
||||
{{ state.custInfoObj['buildDate'] }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"></div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<q-separator /> |
|
||||
<div class="pt-[10px]"> |
|
||||
<q-card flat bordered> |
|
||||
<q-item dense class="pl-[8px]"> |
|
||||
<q-item-section> |
|
||||
<q-item-label>灰度模型结果</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-separator /> |
|
||||
<q-card-section class="p-[10px]"> |
|
||||
<div class="pt-[8px]"> |
|
||||
<w-info-panel :dense="state.dense" :info="state.reportCustRatingInfo" :column-num="3"></w-info-panel> |
|
||||
</div> |
|
||||
</q-card-section> |
|
||||
</q-card> |
|
||||
<div class="pt-[8px]"> |
|
||||
<q-list bordered class="rounded-borders"> |
|
||||
<q-expansion-item default-opened expand-separator icon="bubble_chart" label="得分详情"> |
|
||||
<q-separator /> |
|
||||
<q-splitter v-model="state.splitterModel"> |
|
||||
<template #before> |
|
||||
<div> |
|
||||
<div class="text-center"> |
|
||||
<q-chip outline color="orange" text-color="white" :clickable="false" :ripple="false"> 定量得分详情 </q-chip> |
|
||||
</div> |
|
||||
<q-list v-if="state.reportQuantitativeScoreDtl" padding> |
|
||||
<template v-for="(values, key, a) in state.reportQuantitativeScoreDtl" :key="a"> |
|
||||
<q-item-label header>{{ key }}</q-item-label> |
|
||||
|
|
||||
<template v-for="(dtl, index) in values" :key="index"> |
|
||||
<q-item> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ dtl['INDEX_NAME'] }}</q-item-label> |
|
||||
<q-item-label caption> {{ Round(dtl['INDEX_VALUE'], 2) }} </q-item-label> |
|
||||
</q-item-section> |
|
||||
<q-item-section v-if="!Tools.isEmpty(dtl['INDEX_SCORE_G'])" side> |
|
||||
<q-chip :clickable="false" :ripple="false" color="green" text-color="white"> {{ Round(dtl['INDEX_SCORE_G'], 2) }} </q-chip> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</template> |
|
||||
<q-separator /> |
|
||||
</template> |
|
||||
</q-list> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<template #after> |
|
||||
<q-splitter v-model="state.afterSplitterModel"> |
|
||||
<template #before> |
|
||||
<div> |
|
||||
<div class="text-center"> |
|
||||
<q-chip outline color="light-blue" text-color="white" :clickable="false" :ripple="false"> 定性得分详情 </q-chip> |
|
||||
</div> |
|
||||
<q-list v-if="state.reportQualitativeScoreDtl" padding> |
|
||||
<template v-for="(values, key, a) in state.reportQualitativeScoreDtl" :key="a"> |
|
||||
<q-item-label header>{{ key }}</q-item-label> |
|
||||
|
|
||||
<template v-for="(dtl, index) in values" :key="index"> |
|
||||
<q-item> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ dtl['INDEX_NAME'] }}</q-item-label> |
|
||||
<q-item-label caption> {{ dtl['TEXT'] }} </q-item-label> |
|
||||
</q-item-section> |
|
||||
<q-item-section v-if="!Tools.isEmpty(dtl['INDEX_SCORE_G'])" side> |
|
||||
<q-chip :clickable="false" :ripple="false" color="green" text-color="white"> {{ Round(dtl['INDEX_SCORE_G'], 2) }} </q-chip> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</template> |
|
||||
<q-separator /> |
|
||||
</template> |
|
||||
</q-list> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<template #after> |
|
||||
<div> |
|
||||
<div class="text-center"> |
|
||||
<q-chip outline color="lime" text-color="white" :clickable="false" :ripple="false"> 评级调整项详情 </q-chip> |
|
||||
</div> |
|
||||
<q-list v-if="state.reportAdjustScoreDtl" padding> |
|
||||
<template v-for="(values, key, a) in state.reportAdjustScoreDtl" :key="a"> |
|
||||
<q-item-label header>{{ key }}</q-item-label> |
|
||||
|
|
||||
<template v-for="(dtl, index) in values" :key="index"> |
|
||||
<q-item> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ dtl['INDEX_NAME'] }}</q-item-label> |
|
||||
<q-item-label caption> {{ dtl['TEXT'] }} </q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</template> |
|
||||
<q-separator /> |
|
||||
</template> |
|
||||
</q-list> |
|
||||
</div> |
|
||||
</template> |
|
||||
</q-splitter> |
|
||||
</template> |
|
||||
</q-splitter> |
|
||||
</q-expansion-item> |
|
||||
</q-list> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</w-dialog> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"> |
|
||||
import { DictionaryTools, Environment, Formater, axios, Tools } from 'platform-core'; |
|
||||
import { nextTick, reactive, ref } from 'vue'; |
|
||||
import { RatingStep, Round } from './CustRating.ts'; |
|
||||
import RatingLevel from './RatingLevel.vue'; |
|
||||
|
|
||||
const dialogRef = ref(); |
|
||||
|
|
||||
const state = reactive({ |
|
||||
splitterModel: 30, |
|
||||
afterSplitterModel: 50, |
|
||||
rating: {}, |
|
||||
dialogTitle: '灰度模型结果', |
|
||||
dense: false, |
|
||||
custInfoObj: {}, |
|
||||
reportCustRatingInfo: <any>[], |
|
||||
reportQuantitativeScoreDtl: <any>null, |
|
||||
reportQualitativeScoreDtl: <any>null, |
|
||||
reportAdjustScoreDtl: <any>null, |
|
||||
}); |
|
||||
|
|
||||
// 加载客户信息 |
|
||||
const loadCustInfo = async () => { |
|
||||
// 加载客户基本信息 |
|
||||
let customerType = ''; |
|
||||
await axios.get(Environment.apiContextPath('api/irbs/ratingCompanyCustomer/' + state.rating['custId'])).then((resp) => { |
|
||||
if (resp && resp.data) { |
|
||||
customerType = resp.data['customerType']; |
|
||||
state.custInfoObj = {}; |
|
||||
state.custInfoObj['custNo'] = resp.data['custNo']; |
|
||||
state.custInfoObj['custName'] = resp.data['custName']; |
|
||||
state.custInfoObj['certificateType'] = resp.data['certificateType']; |
|
||||
state.custInfoObj['certificateNum'] = resp.data['certificateNum']; |
|
||||
state.custInfoObj['customerType'] = resp.data['customerType']; |
|
||||
state.custInfoObj['customerSize'] = resp.data['customerSize']; |
|
||||
state.custInfoObj['registeredType'] = resp.data['registeredType']; |
|
||||
state.custInfoObj['goverFinanceSign'] = resp.data['goverFinanceSign']; |
|
||||
state.custInfoObj['goverFinanceType'] = resp.data['goverFinanceType']; |
|
||||
state.custInfoObj['registration'] = resp.data['registration']; |
|
||||
state.custInfoObj['nation'] = resp.data['nation']; |
|
||||
state.custInfoObj['memberType'] = resp.data['memberType']; |
|
||||
state.custInfoObj['groupCustInd'] = resp.data['groupCustInd']; |
|
||||
state.custInfoObj['marketEnterprisesInd'] = resp.data['marketEnterprisesInd']; |
|
||||
state.custInfoObj['industryType'] = resp.data['industryType']; |
|
||||
state.custInfoObj['industryTypeName'] = resp.data['industryTypeName']; |
|
||||
state.custInfoObj['buildDate'] = resp.data['buildDate']; |
|
||||
state.custInfoObj['customerManagerNo'] = resp.data['customerManagerNo']; |
|
||||
state.custInfoObj['customerManagerName'] = resp.data['customerManagerName']; |
|
||||
state.custInfoObj['handleOrgId'] = resp.data['handleOrgId']; |
|
||||
state.custInfoObj['handleTime'] = resp.data['handleTime']; |
|
||||
} |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
const groupByProperties = (list, propName) => { |
|
||||
const map = {}; |
|
||||
list.forEach((item, index, arr) => { |
|
||||
if (!map[item[propName]]) { |
|
||||
map[item[propName]] = arr.filter((a) => a[propName] == item[propName]); |
|
||||
} |
|
||||
}); |
|
||||
return map; |
|
||||
}; |
|
||||
|
|
||||
const loadRatingReport = async () => { |
|
||||
// 加载评级信息 |
|
||||
state.reportCustRatingInfo = []; |
|
||||
await axios |
|
||||
.get(Environment.apiContextPath('api/irbs/companyRating/stepRatingReport'), { params: { ratingId: state.rating['id'] } }) |
|
||||
.then((resp) => { |
|
||||
if (resp && resp.data) { |
|
||||
state.rating = resp.data; |
|
||||
state.reportCustRatingInfo.push({ label: '模型名称', value: resp.data.modelNameGray }); |
|
||||
state.reportCustRatingInfo.push({ label: '定量得分', value: Round(resp.data.quanScoreGray, 2) }); |
|
||||
state.reportCustRatingInfo.push({ label: '定性得分', value: Round(resp.data.qualScoreGray, 2) }); |
|
||||
state.reportCustRatingInfo.push({ label: '模型得分', value: Round(resp.data.modelScoreGray, 2) }); |
|
||||
state.reportCustRatingInfo.push({ |
|
||||
label: '模型级别', |
|
||||
value: resp.data.modelLevelGray, |
|
||||
format: () => { |
|
||||
return { |
|
||||
componentType: RatingLevel, |
|
||||
attrs: { |
|
||||
level: resp.data.modelLevelGray, |
|
||||
dense: true, |
|
||||
}, |
|
||||
}; |
|
||||
}, |
|
||||
}); |
|
||||
state.reportCustRatingInfo.push({ |
|
||||
label: '调整项级别', |
|
||||
value: resp.data.adjLevelGray, |
|
||||
format: () => { |
|
||||
return { |
|
||||
componentType: RatingLevel, |
|
||||
attrs: { |
|
||||
level: resp.data.adjLevelGray, |
|
||||
dense: true, |
|
||||
}, |
|
||||
}; |
|
||||
}, |
|
||||
}); |
|
||||
} |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
console.info('error====', error); |
|
||||
}); |
|
||||
// 加载得分详情 |
|
||||
const urlSearchParams = new URLSearchParams({ sortBy: ['INDEX_TYPE', 'INDEX_CATEGORY'] }); |
|
||||
urlSearchParams.append('pageable', 'false'); |
|
||||
const criteria = { |
|
||||
fieldName: 'RATING_ID', |
|
||||
operator: 'equals', |
|
||||
value: state.rating['id'], |
|
||||
}; |
|
||||
urlSearchParams.append('criteria', JSON.stringify(criteria)); |
|
||||
await axios |
|
||||
.get(Environment.apiContextPath('api/irbs/ratingIndex/getGrayScoreDetail'), { params: urlSearchParams }) |
|
||||
.then((resp) => { |
|
||||
if (resp?.data?.content) { |
|
||||
const groupMap = groupByProperties(resp.data.content, 'INDEX_TYPE'); |
|
||||
state.reportQuantitativeScoreDtl = groupByProperties(groupMap[RatingStep.DLFX], 'INDEX_CATEGORY'); |
|
||||
state.reportQualitativeScoreDtl = groupByProperties(groupMap[RatingStep.DXFX], 'INDEX_CATEGORY'); |
|
||||
state.reportAdjustScoreDtl = groupByProperties( |
|
||||
groupMap[RatingStep.PJTZX].filter((item) => { |
|
||||
return item['INDEX_VALUE'] !== '0'; |
|
||||
}), |
|
||||
'INDEX_CATEGORY', |
|
||||
); |
|
||||
} |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
console.info('error====', error); |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
const show = async (data: any) => { |
|
||||
dialogRef.value.show(); |
|
||||
state.rating = data; |
|
||||
state.dialogTitle = `公司客户灰度评级结果(客户:` + data['custName'] + `)`; |
|
||||
loadCustInfo(); |
|
||||
await loadRatingReport(); |
|
||||
}; |
|
||||
const hide = () => { |
|
||||
dialogRef.value.hide(); |
|
||||
}; |
|
||||
|
|
||||
defineExpose({ |
|
||||
show, |
|
||||
hide, |
|
||||
}); |
|
||||
|
|
||||
const dictCustomerSize = await DictionaryTools.fetch('CustomerSizeCd'); |
|
||||
const dictRegisteredType = await DictionaryTools.fetch('REGISTERED_TYPE'); |
|
||||
const dictGoverFinanceType = await DictionaryTools.fetch('GOVER_FINANCE_TYPE'); |
|
||||
const dictGoverFinanceSign = await DictionaryTools.fetch('GOVER_FINANCE_SIGN'); |
|
||||
const dictRegistrationCd = await DictionaryTools.fetch('REGISTRATION_CD'); |
|
||||
const dictNationCd = await DictionaryTools.fetch('NATION_CD'); |
|
||||
const dictMemberTypeCd = await DictionaryTools.fetch('MEMBER_TYPE_CD'); |
|
||||
</script> |
|
||||
|
|
||||
<style scoped lang="css"></style> |
|
@ -1,186 +0,0 @@ |
|||||
<template> |
|
||||
<w-dialog ref="dialogRef" :title="state.dialogTitle" width="80%" height="80%" :buttons="dialogButtons"> |
|
||||
<w-grid |
|
||||
ref="customerGridRef" |
|
||||
title="客户列表" |
|
||||
:fetch-data-url="Environment.apiContextPath('api/irbs/companyCustomer/query')" |
|
||||
:sort-no="true" |
|
||||
:checkbox-selection="false" |
|
||||
:config-button="false" |
|
||||
:query-form-cols-num="3" |
|
||||
:query-form-fields="customerGrid.queryFormFields" |
|
||||
:columns="customerGrid.columns" |
|
||||
:toolbar-actions="customerGrid.buttons" |
|
||||
:sort-by="['-lastModifyDate']" |
|
||||
:query-criteria="{ |
|
||||
fieldName: 'mgerNo', |
|
||||
operator: 'equals', |
|
||||
value: SessionManager.getUser().loginName, |
|
||||
}" |
|
||||
></w-grid> |
|
||||
</w-dialog> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"> |
|
||||
import { ref, reactive } from 'vue'; |
|
||||
import { useQuasar } from 'quasar'; |
|
||||
import { useI18n } from 'vue-i18n'; |
|
||||
import { axios, noErrorAxios, Environment, NotifyManager, Formater, Tools, SessionManager } from 'platform-core'; |
|
||||
import { SelectCustomerFlag } from './CustRating.ts'; |
|
||||
|
|
||||
const { t } = useI18n(); |
|
||||
const $q = useQuasar(); |
|
||||
const dialogRef = ref(); |
|
||||
const customerGridRef = ref(); |
|
||||
const props = defineProps({ |
|
||||
dictionary: { |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
}); |
|
||||
const emit = defineEmits(['refresh']); |
|
||||
|
|
||||
const state = reactive({ |
|
||||
dialogTitle: '选择客户', |
|
||||
flag: SelectCustomerFlag.RATING, // 发起评级或发起违约认定标记 |
|
||||
}); |
|
||||
|
|
||||
const generateRating = (rows) => { |
|
||||
showLoading(); |
|
||||
const requestParams = { |
|
||||
method: 'POST', |
|
||||
headers: { 'content-type': 'application/json;charset=utf-8;' }, |
|
||||
url: Environment.apiContextPath('api/irbs/companyRating/generateRating/' + rows[0]['custNo']), |
|
||||
}; |
|
||||
noErrorAxios(requestParams) |
|
||||
.then((resp) => { |
|
||||
hideLoading(); |
|
||||
emit('refresh'); |
|
||||
dialogRef.value.hide(); |
|
||||
NotifyManager.info('发起成功'); |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
const response = error?.response; |
|
||||
const status = response?.status; |
|
||||
const data = response?.data; |
|
||||
//其他错误 |
|
||||
if (status === 500 && data.exception === 'java.lang.RuntimeException' && !Tools.isEmpty(data.errorMessage)) { |
|
||||
NotifyManager.error(data.errorMessage); |
|
||||
} else if (status === 500) { |
|
||||
NotifyManager.error(t(data?.errorMessageI18nKey)); |
|
||||
} else { |
|
||||
NotifyManager.error(t(status)); |
|
||||
} |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
const dialogButtons = [ |
|
||||
{ |
|
||||
label: '确定', |
|
||||
icon: 'beenhere', |
|
||||
click: async () => { |
|
||||
const rows = customerGridRef.value.getSelectedRows(); |
|
||||
if (rows.length === 0) { |
|
||||
NotifyManager.warn('请选择客户'); |
|
||||
return; |
|
||||
} |
|
||||
if (state.flag === SelectCustomerFlag.RATING) { |
|
||||
// 判断客户是否已达当月评级次数上限 |
|
||||
const monthLimit = await axios.post(Environment.apiContextPath('api/irbs/companyRating/getMonthRatingLimit/' + rows[0]['custNo'])); |
|
||||
console.info('monthLimit====', monthLimit); |
|
||||
if (monthLimit?.data?.result === true && monthLimit?.data?.tactics === '1') { |
|
||||
NotifyManager.warn('该客户当月已达评级次数上限(' + monthLimit?.data?.num + '次),无法再次发起评级'); |
|
||||
return; |
|
||||
} else if (monthLimit?.data?.result === true && monthLimit?.data?.tactics === '2') { |
|
||||
$q.dialog({ |
|
||||
title: '询问', |
|
||||
message: '该客户当月已发起评级次数超过:' + monthLimit?.data?.num + '次,是否继续发起?', |
|
||||
cancel: { noCaps: true }, |
|
||||
ok: { noCaps: true }, |
|
||||
persistent: true, |
|
||||
}).onOk(() => { |
|
||||
generateRating(rows); |
|
||||
}); |
|
||||
} else { |
|
||||
generateRating(rows); |
|
||||
} |
|
||||
} else if (state.flag === SelectCustomerFlag.COGNIZANCE) { |
|
||||
// 查询是否有在途流程 |
|
||||
const resp = await axios |
|
||||
.get(Environment.apiContextPath('api/irbs/defaultCognizance/queryDefaCustNo'), { params: { custNo: rows[0]['custNo'] } }) |
|
||||
.catch((error) => { |
|
||||
console.info('error-------------', error); |
|
||||
}); |
|
||||
if (resp && resp.data === '1') { |
|
||||
NotifyManager.warn('当前客户存在进行中违约认定流程'); |
|
||||
return; |
|
||||
} else if (resp && resp.data === '2') { |
|
||||
NotifyManager.warn('当前客户存在已人工认定违约认定结果,无法发起新的人工违约认定流程'); |
|
||||
return; |
|
||||
} else if (resp && resp.data === '0') { |
|
||||
// 发起 |
|
||||
showLoading(); |
|
||||
axios |
|
||||
.post(Environment.apiContextPath('api/irbs/defaultManager/cognizanceApply'), { custNo: rows[0]['custNo'], type: 'F' }) |
|
||||
.then((resp) => { |
|
||||
hideLoading(); |
|
||||
if (resp && resp['code'] === 200 && resp['data']) { |
|
||||
emit('refresh'); |
|
||||
dialogRef.value.hide(); |
|
||||
} |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
hideLoading(); |
|
||||
console.info('error-------------', error); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
]; |
|
||||
|
|
||||
const customerGrid = { |
|
||||
queryFormFields: [ |
|
||||
{ label: '客户号', name: 'custNo', type: 'w-text' }, //defaultValue: '2100601306' |
|
||||
{ label: '客户名称', name: 'custName', type: 'w-text' }, |
|
||||
{ label: '企业规模', name: 'corpSizeCd', type: 'w-select', options: props.dictionary.optionsCustomerSizeCd }, |
|
||||
], |
|
||||
buttons: ['query', 'reset'], |
|
||||
columns: [ |
|
||||
{ name: 'custNo', label: '客户号' }, |
|
||||
{ name: 'custName', label: '客户名称' }, |
|
||||
{ name: 'induSortName', label: '行业类型' }, |
|
||||
{ name: 'custTypeCd', label: '客户类型', format: Formater.dictionary(props.dictionary.dictCustomerTypeCd) }, |
|
||||
{ name: 'buildDt', label: '成立时间' }, |
|
||||
{ name: 'corpSizeCd', label: '企业规模', format: Formater.dictionary(props.dictionary.dictCustomerSizeCd) }, |
|
||||
], |
|
||||
}; |
|
||||
|
|
||||
const show = (flag) => { |
|
||||
if (flag) { |
|
||||
state.flag = flag; |
|
||||
} |
|
||||
dialogRef.value.show(); |
|
||||
}; |
|
||||
const hide = () => { |
|
||||
dialogRef.value.hide(); |
|
||||
}; |
|
||||
|
|
||||
const showLoading = (msg: string = '正在处理,请稍等...') => { |
|
||||
$q.loading.show({ |
|
||||
message: msg, |
|
||||
boxClass: 'bg-grey-2 text-grey-9', |
|
||||
spinnerColor: 'primary', |
|
||||
}); |
|
||||
}; |
|
||||
const hideLoading = () => { |
|
||||
$q.loading.hide(); |
|
||||
}; |
|
||||
|
|
||||
defineExpose({ |
|
||||
show, |
|
||||
hide, |
|
||||
}); |
|
||||
</script> |
|
File diff suppressed because it is too large
@ -1,31 +0,0 @@ |
|||||
<template> |
|
||||
<div> |
|
||||
<q-knob v-model="value" :min="1" :max="10" readonly show-value :size="size" :color="color" track-color="grey-3" v-bind="attrs"> |
|
||||
<span>{{ props.level }}</span> |
|
||||
</q-knob> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"> |
|
||||
import { ref, useAttrs } from 'vue'; |
|
||||
import { RatingLevelOptions } from './CustRating.ts'; |
|
||||
|
|
||||
const attrs = useAttrs(); |
|
||||
const props = defineProps({ |
|
||||
level: { type: String, default: '' }, |
|
||||
size: { type: String, default: 'md' }, |
|
||||
}); |
|
||||
|
|
||||
const value = ref(0); |
|
||||
const color = ref('teal'); |
|
||||
|
|
||||
const ratingLevel = RatingLevelOptions.find((item) => { |
|
||||
return item['value'] === props.level; |
|
||||
}); |
|
||||
if (ratingLevel) { |
|
||||
value.value = ratingLevel.numberValue; |
|
||||
color.value = ratingLevel.color; |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped lang="css"></style> |
|
@ -1,61 +0,0 @@ |
|||||
<template> |
|
||||
<q-slider |
|
||||
v-model="ratingLevel" |
|
||||
dense |
|
||||
readonly |
|
||||
color="blue" |
|
||||
:label-always="labelAlwaysComputed" |
|
||||
:label-value="labelValueComputed" |
|
||||
:marker-labels="labelsComputed" |
|
||||
:min="minLevel" |
|
||||
:max="maxLevel" |
|
||||
style="width: 95%" |
|
||||
/> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, computed } from 'vue'; |
|
||||
import { RatingLevelOptions } from './CustRating'; |
|
||||
|
|
||||
const modelValue = defineModel<string>(); |
|
||||
const minLevel = ref(RatingLevelOptions.find((item) => item.value === 'D')?.numberValue); |
|
||||
const maxLevel = ref(RatingLevelOptions.find((item) => item.value === 'AAA+')?.numberValue); |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
// marker-labels 精简模式 |
|
||||
denseLabel: { type: Boolean, default: false }, |
|
||||
}); |
|
||||
|
|
||||
const ratingLevel = computed(() => { |
|
||||
const ratingLevel_ = RatingLevelOptions.find((item) => { |
|
||||
return item['value'] === modelValue.value; |
|
||||
}); |
|
||||
if (ratingLevel_) { |
|
||||
return ratingLevel_.numberValue; |
|
||||
} |
|
||||
return 1; |
|
||||
}); |
|
||||
|
|
||||
const labelsComputed = computed(() => { |
|
||||
const result = <any>[]; |
|
||||
RatingLevelOptions.forEach((item) => { |
|
||||
if (props.denseLabel && item.denseLabel) { |
|
||||
result.push({ |
|
||||
label: item.label, |
|
||||
value: item.numberValue, |
|
||||
}); |
|
||||
} else if (!props.denseLabel) { |
|
||||
result.push({ |
|
||||
label: item.label, |
|
||||
value: item.numberValue, |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
return result; |
|
||||
}); |
|
||||
const labelAlwaysComputed = computed(() => { |
|
||||
return props.denseLabel; |
|
||||
}); |
|
||||
const labelValueComputed = computed(() => { |
|
||||
return modelValue.value; |
|
||||
}); |
|
||||
</script> |
|
@ -1,69 +0,0 @@ |
|||||
<template> |
|
||||
<div> |
|
||||
<q-btn |
|
||||
v-if="showComputed" |
|
||||
icon="mark_chat_unread" |
|
||||
color="primary" |
|
||||
label="批注" |
|
||||
@click=" |
|
||||
() => { |
|
||||
addAnnotationRef.show(); |
|
||||
} |
|
||||
" |
|
||||
/> |
|
||||
<w-drawer ref="addAnnotationRef" title="批注"> |
|
||||
<div class="p-2 h-full"> |
|
||||
<w-splitter :size="205" horizontal unit="px" disable :show-separator="false"> |
|
||||
<template #before> |
|
||||
<w-form ref="formRef" :cols-num="1" :fields="[{ label: '批注内容', name: 'annotation', type: 'w-textarea' }]"></w-form> |
|
||||
<br /> |
|
||||
<q-btn |
|
||||
icon="add" |
|
||||
color="primary" |
|
||||
label="添加" |
|
||||
@click=" |
|
||||
() => { |
|
||||
annotationGridRef.addLocalData({ nr: formRef.getData()['annotation'], sj: currDate() }); |
|
||||
} |
|
||||
" |
|
||||
/> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<div class="h-full"> |
|
||||
<w-grid |
|
||||
ref="annotationGridRef" |
|
||||
:auto-fetch-data="false" |
|
||||
:config-button="false" |
|
||||
:toolbar-actions="['refresh', 'remove']" |
|
||||
:columns="[ |
|
||||
{ label: '批注内容', name: 'nr' }, |
|
||||
{ label: '批注时间', name: 'sj', width: 200 }, |
|
||||
]" |
|
||||
> |
|
||||
</w-grid> |
|
||||
</div> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</div> |
|
||||
</w-drawer> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, inject, computed } from 'vue'; |
|
||||
import { Rating } from './ts/Rating'; |
|
||||
import { Step } from './ts/Step'; |
|
||||
import { Constant } from './ts/Constant'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const addAnnotationRef = ref(); |
|
||||
const annotationGridRef = ref(); |
|
||||
const formRef = ref(); |
|
||||
const currDate = () => { |
|
||||
const now = new Date(); |
|
||||
const formattedTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`; |
|
||||
return formattedTime; |
|
||||
}; |
|
||||
const showComputed = computed(() => { |
|
||||
return true; |
|
||||
}); |
|
||||
</script> |
|
@ -1,37 +0,0 @@ |
|||||
<template> |
|
||||
<CustInfo v-if="custComputed"></CustInfo> |
|
||||
<QuanQualAnalysis v-else-if="qqaComputed"></QuanQualAnalysis> |
|
||||
<AdjustItem v-else-if="aiComputed"></AdjustItem> |
|
||||
<ReportInfo v-else-if="riComputed"></ReportInfo> |
|
||||
<Opinion v-else-if="opinionComputed"></Opinion> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, PropType, computed } from 'vue'; |
|
||||
import type { StepType } from './ts/type/StepType'; |
|
||||
import { Step } from './ts/Step'; |
|
||||
import CustInfo from './content/CustInfo.vue'; |
|
||||
import QuanQualAnalysis from './content/QuanQualAnalysis.vue'; |
|
||||
import AdjustItem from './content/AdjustItem.vue'; |
|
||||
import ReportInfo from './content/ReportInfo.vue'; |
|
||||
import Opinion from './content/Opinion.vue'; |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
step: { type: Object as PropType<StepType>, default: undefined }, |
|
||||
}); |
|
||||
|
|
||||
const custComputed = computed(() => { |
|
||||
return props.step?.value === Step.type.custInfo.value; |
|
||||
}); |
|
||||
const qqaComputed = computed(() => { |
|
||||
return props.step?.value === Step.type.quanQualAnalysis.value; |
|
||||
}); |
|
||||
const aiComputed = computed(() => { |
|
||||
return props.step?.value === Step.type.adjustItem.value; |
|
||||
}); |
|
||||
const riComputed = computed(() => { |
|
||||
return props.step?.value === Step.type.reportInfo.value; |
|
||||
}); |
|
||||
const opinionComputed = computed(() => { |
|
||||
return props.step?.value === Step.type.opinion.value; |
|
||||
}); |
|
||||
</script> |
|
@ -1,21 +0,0 @@ |
|||||
<template> |
|
||||
<q-btn v-if="showComputed" icon="next_plan" color="primary" label="下一步" @click="rating.step.nextClick" /> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, computed } from 'vue'; |
|
||||
import { Rating } from './ts/Rating'; |
|
||||
import { Step } from './ts/Step'; |
|
||||
import { Constant } from './ts/Constant'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const showComputed = computed(() => { |
|
||||
if (rating.ratingData.value['processStatus'] === Constant.RATING_PROCESS_STATUS.BACK && rating.step.currStep.value === Step.type.reportInfo.value) { |
|
||||
return false; |
|
||||
} else { |
|
||||
if (rating.cm.isApplyUserProcessStatus.value && rating.step.currStep.value !== Step.type.opinion.value) { |
|
||||
return true; |
|
||||
} |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
</script> |
|
@ -1,71 +0,0 @@ |
|||||
<template> |
|
||||
<w-splitter :size="46" horizontal disable reverse unit="px" style="height: 100%"> |
|
||||
<template #before> |
|
||||
<Step ref="stepRef" class="h-full"></Step> |
|
||||
</template> |
|
||||
|
|
||||
<template #after> |
|
||||
<div class="p-1 flex justify-end gap-x-4"> |
|
||||
<AnnotationButton></AnnotationButton> |
|
||||
<!-- <TestCalcButton></TestCalcButton> --> |
|
||||
<NextButton></NextButton> |
|
||||
<WorkflowButton></WorkflowButton> |
|
||||
</div> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { VueTools } from 'platform-core'; |
|
||||
import { getCurrentInstance, onMounted, provide, ref, shallowRef } from 'vue'; |
|
||||
import RatingLevel from '../RatingLevel.vue'; |
|
||||
import NextButton from './NextButton.vue'; |
|
||||
import Step from './Step.vue'; |
|
||||
import TestCalcButton from './TestCalcButton.vue'; |
|
||||
import { Rating } from './ts/Rating'; |
|
||||
import WorkflowButton from './WorkflowButton.vue'; |
|
||||
import AnnotationButton from './AnnotationButton.vue'; |
|
||||
|
|
||||
const RatingLevelRef = shallowRef(RatingLevel); |
|
||||
const props = defineProps({ |
|
||||
ratingData: { |
|
||||
type: Object, |
|
||||
default: () => { |
|
||||
return {}; |
|
||||
}, |
|
||||
}, |
|
||||
readMode: { |
|
||||
type: Boolean, |
|
||||
default: false, |
|
||||
}, |
|
||||
}); |
|
||||
const emit = defineEmits(['afterComplete']); |
|
||||
|
|
||||
const rating = new Rating(props.ratingData, props.readMode); |
|
||||
await rating.dictionary.load(); |
|
||||
await rating.enum.load(); |
|
||||
await rating.systemParameter.load(); |
|
||||
const ratingLevelFormat = (modelLevel: any, size: string) => { |
|
||||
return { |
|
||||
componentType: RatingLevelRef, |
|
||||
attrs: { |
|
||||
level: modelLevel, |
|
||||
dense: true, |
|
||||
size: size, |
|
||||
}, |
|
||||
}; |
|
||||
}; |
|
||||
rating.refs.setRatingLevelFormatFunction(ratingLevelFormat); |
|
||||
|
|
||||
onMounted(() => { |
|
||||
rating.step.setCurrStep(rating.ratingData.value['currentStep']); |
|
||||
}); |
|
||||
|
|
||||
// 获得自身实例 |
|
||||
const instance = getCurrentInstance(); |
|
||||
// 将对外暴露API添加至自身实例中 |
|
||||
VueTools.expose2Instance(instance); |
|
||||
// 设置到评级类中 |
|
||||
rating.setInstance(instance); |
|
||||
|
|
||||
provide('rating', rating); |
|
||||
</script> |
|
@ -1,58 +0,0 @@ |
|||||
<template> |
|
||||
<w-splitter :size="75" class="h-full" unit="px" disable> |
|
||||
<template #before> |
|
||||
<div class="h-full" style="display: flex; align-items: center"> |
|
||||
<q-tabs |
|
||||
v-model="rating.step.currStep.value" |
|
||||
vertical |
|
||||
indicator-color="amber" |
|
||||
active-color="amber" |
|
||||
align="left" |
|
||||
:dense="rating.cm.isAwaitSubmitProcessStatus.value" |
|
||||
style="height: auto" |
|
||||
@update:model-value="rating.step.click" |
|
||||
> |
|
||||
<template v-for="(step, index) in rating.step.steps.value" :key="step.value"> |
|
||||
<q-tab :name="step.value" :icon="step.icon" :label="step.label" :disable="step.disable" style="padding: 2px"> |
|
||||
<q-tooltip v-if="rating.cm.isAwaitSubmitProcessStatus.value" transition-show="rotate" transition-hide="rotate">{{ step.tooltip }}</q-tooltip> |
|
||||
</q-tab> |
|
||||
<div v-if="index !== rating.step.steps.value.length - 1 && rating.cm.isAwaitSubmitProcessStatus.value" style="width: 100%; text-align: center"> |
|
||||
<q-icon name="bi-arrow-down-short" size="sm"></q-icon> |
|
||||
</div> |
|
||||
</template> |
|
||||
</q-tabs> |
|
||||
</div> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<!-- 切分窗口 --> |
|
||||
<w-splitter v-if="!rating.cm.isAwaitSubmitProcessStatus.value" :size="77" :separator-color="rating.separatorColor" class="h-full"> |
|
||||
<template #before> |
|
||||
<q-tab-panels v-model="rating.step.currStep.value" class="p-0 h-full"> |
|
||||
<q-tab-panel v-for="step in rating.step.steps.value" :key="step.value" :name="step.value" class="p-0 h-full"> |
|
||||
<Content :step="step"></Content> |
|
||||
</q-tab-panel> |
|
||||
</q-tab-panels> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<Opinion></Opinion> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
<!-- 不切分 --> |
|
||||
<q-tab-panels v-else v-model="rating.step.currStep.value" class="p-0 h-full"> |
|
||||
<q-tab-panel v-for="step in rating.step.steps.value" :key="step.value" :name="step.value" class="p-0 h-full"> |
|
||||
<Content :step="step"></Content> |
|
||||
</q-tab-panel> |
|
||||
</q-tab-panels> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"> |
|
||||
import { inject, ref, onMounted } from 'vue'; |
|
||||
import { Rating } from './ts/Rating'; |
|
||||
import Content from './Content.vue'; |
|
||||
import { Constant } from './ts/Constant'; |
|
||||
import Opinion from './content/Opinion.vue'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
</script> |
|
@ -1,73 +0,0 @@ |
|||||
<template> |
|
||||
<q-btn v-if="showComputed" icon="calculate" color="primary" label="试算" :disable="testCalcNum < 1" @click="click"> |
|
||||
<q-badge :color="testCalcNum > 3 ? 'green' : 'red'" floating |
|
||||
>{{ testCalcNum }} |
|
||||
<q-tooltip> 剩余可试算次数 </q-tooltip> |
|
||||
</q-badge> |
|
||||
</q-btn> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, inject, computed, onBeforeMount } from 'vue'; |
|
||||
import { useQuasar } from 'quasar'; |
|
||||
import { axios, Environment, NotifyManager, Tools } from 'platform-core'; |
|
||||
import { Rating } from './ts/Rating'; |
|
||||
import { Step } from './ts/Step'; |
|
||||
import { Constant } from './ts/Constant'; |
|
||||
import { round } from './ts/Utils'; |
|
||||
|
|
||||
const $q = useQuasar(); |
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const testCalcNum = ref(0); |
|
||||
|
|
||||
const getTestCalcNum = async () => { |
|
||||
const resp = await axios.get(Environment.apiContextPath('api/irbs/companyRating/getQualTestCalcNum'), { |
|
||||
params: { ratingId: rating.ratingData.value['id'] }, |
|
||||
}); |
|
||||
if (resp?.data) { |
|
||||
testCalcNum.value = resp.data; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const click = async () => { |
|
||||
const result = rating.step.qualFormValidate(); |
|
||||
if (!result['result']) { |
|
||||
return; |
|
||||
} |
|
||||
rating.showLoading('正在计算定性得分,请稍等...'); |
|
||||
const url = Environment.apiContextPath('api/irbs/companyRating/qualSaveIndices/' + Constant.QUAL_SAVE_OPERATOR.TEST); |
|
||||
const resp = await axios.post(url, result['submitData']).catch((error) => { |
|
||||
rating.hideLoading(); |
|
||||
NotifyManager.error('计算出错,请联系管理员'); |
|
||||
console.info('error====', error); |
|
||||
}); |
|
||||
rating.hideLoading(); |
|
||||
if (resp?.data?.success === true) { |
|
||||
rating.ratingData.value['lastQualTestCalcScore'] = round(resp.data.qualScore, 2); |
|
||||
$q.dialog({ |
|
||||
title: '消息', |
|
||||
message: '本次试算定性得分为:' + round(resp.data.qualScore, 2), |
|
||||
cancel: false, |
|
||||
persistent: true, |
|
||||
}); |
|
||||
// 获取最新试算次数 |
|
||||
getTestCalcNum(); |
|
||||
} else if (resp?.data?.success === false && !Tools.isEmpty(resp.data.errorMsg)) { |
|
||||
if (rating.ratingData.value['lastQualTestCalcScore']) { |
|
||||
NotifyManager.error(resp.data.errorMsg + ',与最后一次试算结果相同,得分为:' + rating.ratingData.value['lastQualTestCalcScore']); |
|
||||
} else { |
|
||||
NotifyManager.error(resp.data.errorMsg); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const showComputed = computed(() => { |
|
||||
if (rating.cm.isApplyUserProcessStatus.value && rating.step.currStep.value === Step.type.quanQualAnalysis.value) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
|
|
||||
onBeforeMount(() => { |
|
||||
getTestCalcNum(); |
|
||||
}); |
|
||||
</script> |
|
@ -1,48 +0,0 @@ |
|||||
<template> |
|
||||
<w-workflow-action |
|
||||
v-if="showComputed" |
|
||||
:task-id="rating.ratingData.value['taskId']" |
|
||||
:data="rating.opinionData.value" |
|
||||
:default-submit-button="false" |
|
||||
:action-url="Environment.apiContextPath('/api/irbs/companyRatingProcess/submit')" |
|
||||
@before-submit=" |
|
||||
async (action, callback) => { |
|
||||
rating.opinionData.value = { |
|
||||
transientVariables: { |
|
||||
opaVal: action.transientVariables.goback, |
|
||||
desc: '客户名称:' + rating.ratingData.value['custName'], |
|
||||
}, |
|
||||
data: rating.refs.overturnFormRefFunction().getData(), |
|
||||
}; |
|
||||
const validateResult = await rating.refs.overturnFormRefFunction().validate(); |
|
||||
if (validateResult) { |
|
||||
callback(true); |
|
||||
} else { |
|
||||
callback(false); |
|
||||
} |
|
||||
} |
|
||||
" |
|
||||
@after-submit=" |
|
||||
() => { |
|
||||
rating.instance.emit('afterComplete'); |
|
||||
} |
|
||||
" |
|
||||
> |
|
||||
</w-workflow-action> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, computed } from 'vue'; |
|
||||
import { Environment } from 'platform-core'; |
|
||||
import { Rating } from './ts/Rating'; |
|
||||
import { Step } from './ts/Step'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const showComputed = computed(() => { |
|
||||
if (rating.cm.isAwaitSubmitProcessStatus.value) { |
|
||||
return rating.step.currStep.value === Step.type.opinion.value; |
|
||||
} else if (rating.ratingData.value['taskId']) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
</script> |
|
@ -1,18 +0,0 @@ |
|||||
<template> |
|
||||
<w-splitter :size="45" unit="px" horizontal disable> |
|
||||
<template #before> |
|
||||
<FirstRatingResult></FirstRatingResult> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<AdjustItemGrid class="px-1"></AdjustItemGrid> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, computed } from 'vue'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import FirstRatingResult from './FirstRatingResult.vue'; |
|
||||
import AdjustItemGrid from './AdjustItemGrid.vue'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
</script> |
|
@ -1,68 +0,0 @@ |
|||||
<template> |
|
||||
<w-grid |
|
||||
ref="adjustItemGridRef" |
|
||||
title="评级调整项列表" |
|
||||
:dense="rating.dense" |
|
||||
:pageable="false" |
|
||||
:hide-bottom="true" |
|
||||
:auto-fetch-data="false" |
|
||||
:checkbox-selection="false" |
|
||||
group-mode="alone" |
|
||||
group-by-field="indexCategory" |
|
||||
:stripe="true" |
|
||||
:sort-no="false" |
|
||||
:config-button="false" |
|
||||
:toolbar-actions="['expand']" |
|
||||
:columns="[ |
|
||||
{ |
|
||||
name: 'value', |
|
||||
label: '符合', |
|
||||
width: 80, |
|
||||
align: 'center', |
|
||||
sortable: false, |
|
||||
format: (val) => { |
|
||||
return { |
|
||||
componentType: 'w-checkbox', |
|
||||
bindModelValue: true, |
|
||||
attrs: { |
|
||||
dense: true, |
|
||||
disableIf: () => { |
|
||||
// if (showNextBtnComputed) { |
|
||||
// return false; |
|
||||
// } |
|
||||
return false; |
|
||||
}, |
|
||||
}, |
|
||||
}; |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
name: 'indexCategory', |
|
||||
label: '调整项分类', |
|
||||
showIf: false, |
|
||||
}, |
|
||||
{ |
|
||||
name: 'indexCode', |
|
||||
label: '调整项编码', |
|
||||
sortable: false, |
|
||||
}, |
|
||||
{ |
|
||||
name: 'adjustItemName', |
|
||||
label: '调整项(<span class=\'text-red-500\'>若客户符合调整项所描述情况,请在【符合】列打勾</span>)', |
|
||||
sortable: false, |
|
||||
}, |
|
||||
]" |
|
||||
@row-db-click="() => {}" |
|
||||
></w-grid> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, ref } from 'vue'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const adjustItemGridRef = ref(); |
|
||||
const getAdjustItemGridRef = () => { |
|
||||
return adjustItemGridRef.value; |
|
||||
}; |
|
||||
rating.refs.setAdjustItemGridRefFunction(getAdjustItemGridRef); |
|
||||
</script> |
|
@ -1,36 +0,0 @@ |
|||||
<template> |
|
||||
<w-splitter :size="34" horizontal unit="px" disable class="h-full"> |
|
||||
<template #before> |
|
||||
<w-card-panel |
|
||||
label="评级调整项详情" |
|
||||
:bordered="false" |
|
||||
:title-mode="true" |
|
||||
icon="build_circle" |
|
||||
:icon-attrs="{ color: 'yellow' }" |
|
||||
color="yellow" |
|
||||
></w-card-panel> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<q-list v-if="rating.adjustItemScoreDtlData.value" padding> |
|
||||
<template v-for="(values, key, a) in rating.adjustItemScoreDtlData.value" :key="a"> |
|
||||
<q-item-label header>{{ key }}</q-item-label> |
|
||||
|
|
||||
<template v-for="(dtl, index) in values" :key="index"> |
|
||||
<q-item> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ index + 1 }}、{{ dtl['INDEX_NAME'] }}</q-item-label> |
|
||||
<!-- <q-item-label caption> {{ dtl['TEXT'] }} </q-item-label> --> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</template> |
|
||||
<!-- <q-separator /> --> |
|
||||
</template> |
|
||||
</q-list> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject } from 'vue'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
const rating = <Rating>inject('rating'); |
|
||||
</script> |
|
@ -1,38 +0,0 @@ |
|||||
<template> |
|
||||
<w-grid |
|
||||
ref="creditReportGridRef" |
|
||||
title="征信报告" |
|
||||
:dense="rating.dense" |
|
||||
:pageable="false" |
|
||||
:hide-bottom="true" |
|
||||
:auto-fetch-data="false" |
|
||||
:fetch-data-url="Environment.apiContextPath('api/irbs/creditReport')" |
|
||||
:checkbox-selection="false" |
|
||||
:config-button="false" |
|
||||
:columns="[ |
|
||||
{ name: 'id', label: '报告号' }, |
|
||||
{ name: 'custNo', label: '客户号' }, |
|
||||
{ name: 'entName', label: '客户名称' }, |
|
||||
{ name: 'reportDate', label: '报告日期', format: Formater.dateOnly() }, |
|
||||
{ name: 'expiryDate', label: '征信有效期', format: Formater.dateOnly() }, |
|
||||
{ name: 'blankInd', label: '是否白户', slot: 'isValid' }, |
|
||||
]" |
|
||||
:query-criteria="{ |
|
||||
fieldName: 'custNo', |
|
||||
operator: 'equals', |
|
||||
value: '', |
|
||||
}" |
|
||||
></w-grid> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, inject } from 'vue'; |
|
||||
import { Formater, Environment } from 'platform-core'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const creditReportGridRef = ref(); |
|
||||
const getCreditReportGridRef = () => { |
|
||||
return creditReportGridRef.value; |
|
||||
}; |
|
||||
rating.refs.setCreditReportGridRefFunction(getCreditReportGridRef); |
|
||||
</script> |
|
@ -1,29 +0,0 @@ |
|||||
<template> |
|
||||
<q-card flat bordered> |
|
||||
<q-card-section> |
|
||||
<span class="text-3xl"> |
|
||||
{{ rating.custInfoData.value['custName'] }} |
|
||||
<!-- <q-badge v-if="rating.custInfoData.value['marketEnterprisesInd'] === '1'" color="red" align="top">上市</q-badge> --> |
|
||||
<q-badge color="orange" align="top">客户号:{{ rating.custInfoData.value['custNo'] }}</q-badge |
|
||||
> <q-badge color="green" align="top">上市</q-badge> |
|
||||
<q-badge color="red" align="top">{{ Formater.dictionary(rating.dictionary.customerSize)(rating.custInfoData.value['customerSize']) }}</q-badge |
|
||||
> <q-badge align="top">{{ Formater.dictionary(rating.dictionary.registeredType)(rating.custInfoData.value['registeredType']) }}</q-badge |
|
||||
> <q-badge color="secondary" align="top">{{ rating.custInfoData.value['groupCustInd'] === '1' ? '集团客户' : '非集团客户' }}</q-badge |
|
||||
> |
|
||||
<q-badge v-if="rating.custInfoData.value['groupCustInd'] === '1'" align="top" |
|
||||
>集团成员类别:{{ Formater.dictionary(rating.dictionary.memberTypeCd)(rating.custInfoData.value['memberType']) }}</q-badge |
|
||||
><template v-if="rating.custInfoData.value['groupCustInd'] === '1'"> </template> |
|
||||
<q-badge color="blue" align="top">行业:{{ rating.custInfoData.value['industryTypeName'] }}</q-badge |
|
||||
> <q-badge color="brown" align="top">成立日期:{{ rating.custInfoData.value['buildDate'] }}</q-badge |
|
||||
> |
|
||||
</span> |
|
||||
</q-card-section> |
|
||||
</q-card> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { Formater } from 'platform-core'; |
|
||||
import { inject } from 'vue'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
</script> |
|
@ -1,61 +0,0 @@ |
|||||
<template> |
|
||||
<w-splitter :size="48" unit="px" horizontal disable> |
|
||||
<template #before> |
|
||||
<div class="py-1 pl-1 flex flex-nowrap items-start gap-x-2"> |
|
||||
<div class="flex-1"> |
|
||||
<w-form |
|
||||
:cols-num="3" |
|
||||
:fields="[ |
|
||||
{ |
|
||||
label: '评分卡', |
|
||||
name: 'modelName', |
|
||||
type: 'w-select', |
|
||||
options: ['A评分卡', 'B评分卡', 'K评分卡'], |
|
||||
defaultValue: rating.ratingData.value['modelName'], |
|
||||
}, |
|
||||
{ |
|
||||
label: '更改评分卡说明', |
|
||||
name: 'updateModelDesc', |
|
||||
type: 'w-text', |
|
||||
colSpan: 2, |
|
||||
}, |
|
||||
]" |
|
||||
> |
|
||||
</w-form> |
|
||||
</div> |
|
||||
<div class="flex-none pr-4"> |
|
||||
<q-btn icon="task_alt" color="primary" label="更改" @click="click" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<w-splitter :size="70" :limits="[30, 95]" horizontal :separator-color="rating.separatorColor"> |
|
||||
<template #before> |
|
||||
<FinanceReportTabs></FinanceReportTabs> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<CreditReportGrid class="px-1"></CreditReportGrid> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, inject } from 'vue'; |
|
||||
import { DialogManager } from 'platform-core'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import FinanceReportGrid from './FinanceReportGrid.vue'; |
|
||||
import CreditReportGrid from './CreditReportGrid.vue'; |
|
||||
import CustBaseInfo from './CustBaseInfo.vue'; |
|
||||
import FinanceReportTabs from './FinanceReportTabs.vue'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const custFormRef = ref(); |
|
||||
const getCustFormRef = () => { |
|
||||
return custFormRef.value; |
|
||||
}; |
|
||||
rating.refs.setCustFormRefFunction(getCustFormRef); |
|
||||
const click = () => { |
|
||||
DialogManager.confirm('更改评分卡后流程会经总行进行审批,确认要更改评分卡吗', () => {}); |
|
||||
}; |
|
||||
</script> |
|
@ -1,46 +0,0 @@ |
|||||
<template> |
|
||||
<w-grid |
|
||||
ref="defaultCognizanceGridRef" |
|
||||
:dense="rating.dense" |
|
||||
:pageable="false" |
|
||||
:hide-bottom="true" |
|
||||
:auto-fetch-data="true" |
|
||||
:toolbar-actions="[]" |
|
||||
:fetch-data-url="Environment.apiContextPath('api/irbs/defaultCognizance')" |
|
||||
:checkbox-selection="false" |
|
||||
:config-button="false" |
|
||||
:columns="[ |
|
||||
{ name: 'custNo', label: '客户号' }, |
|
||||
{ name: 'custName', label: '客户名称' }, |
|
||||
{ name: 'levelHis', label: '违约时评级' }, |
|
||||
{ name: 'effectiveDate', label: '违约发起时间' }, |
|
||||
{ name: 'creator', label: '违约发起人' }, |
|
||||
{ name: 'defaultProcessStatus', label: '违约流程状态' }, |
|
||||
{ name: 'defalutType', label: '违约认定类型' }, |
|
||||
]" |
|
||||
:query-criteria="{ |
|
||||
operator: 'and', |
|
||||
criteria: [ |
|
||||
{ |
|
||||
fieldName: 'custNo', |
|
||||
operator: 'equals', |
|
||||
value: rating.ratingData.value['custNo'], |
|
||||
}, |
|
||||
{ |
|
||||
fieldName: 'status', |
|
||||
operator: 'equals', |
|
||||
value: Constant.DEFAULT_PROCESS_STATUS.PASS, |
|
||||
}, |
|
||||
], |
|
||||
}" |
|
||||
:sort-by="['-effectiveDate']" |
|
||||
></w-grid> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { Environment } from 'platform-core'; |
|
||||
import { inject } from 'vue'; |
|
||||
import { Constant } from '../ts/Constant'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
</script> |
|
@ -1,45 +0,0 @@ |
|||||
<template> |
|
||||
<w-grid |
|
||||
ref="defaultRebirthGridRef" |
|
||||
:dense="rating.dense" |
|
||||
:pageable="false" |
|
||||
:hide-bottom="true" |
|
||||
:auto-fetch-data="true" |
|
||||
:toolbar-actions="[]" |
|
||||
:fetch-data-url="Environment.apiContextPath('api/irbs/defaultRebirth')" |
|
||||
:checkbox-selection="false" |
|
||||
:config-button="false" |
|
||||
:columns="[ |
|
||||
{ name: 'custNo', label: '客户号' }, |
|
||||
{ name: 'custName', label: '客户名称' }, |
|
||||
{ name: 'rebirthEffectiveDate', label: '重生生效时间' }, |
|
||||
{ name: 'creator', label: '重生发起人' }, |
|
||||
{ name: 'rebirthProcessStatus', label: '重生流程状态' }, |
|
||||
{ name: 'defalutRebornType', label: '违约重生类型' }, |
|
||||
]" |
|
||||
:query-criteria="{ |
|
||||
operator: 'and', |
|
||||
criteria: [ |
|
||||
{ |
|
||||
fieldName: 'custNo', |
|
||||
operator: 'equals', |
|
||||
value: rating.ratingData.value['custNo'], |
|
||||
}, |
|
||||
{ |
|
||||
fieldName: 'rebirthProcessStatus', |
|
||||
operator: 'equals', |
|
||||
value: Constant.DEFAULT_PROCESS_STATUS.PASS, |
|
||||
}, |
|
||||
], |
|
||||
}" |
|
||||
:sort-by="['-rebirthEffectiveDate']" |
|
||||
></w-grid> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { Environment } from 'platform-core'; |
|
||||
import { inject } from 'vue'; |
|
||||
import { Constant } from '../ts/Constant'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
</script> |
|
@ -1,49 +0,0 @@ |
|||||
<template> |
|
||||
<div v-if="!Tools.isEmpty(valueComputed)">{{ formatAmt(valueComputed) }} <q-icon :name="iconComputed?.name" :color="iconComputed?.color"></q-icon></div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { computed } from 'vue'; |
|
||||
import { Tools } from 'platform-core'; |
|
||||
import { formatAmt } from '../ts/Utils'; |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
// 本期值 |
|
||||
value: { type: Number, default: undefined }, |
|
||||
// 上期值 |
|
||||
previousPeriod: { type: Number, default: undefined }, |
|
||||
}); |
|
||||
|
|
||||
const icon = { |
|
||||
equals: { |
|
||||
name: 'horizontal_rule', |
|
||||
color: 'green', |
|
||||
}, |
|
||||
up: { |
|
||||
name: 'arrow_upward', |
|
||||
color: 'blue', |
|
||||
}, |
|
||||
down: { |
|
||||
name: 'arrow_downward', |
|
||||
color: 'orange', |
|
||||
}, |
|
||||
}; |
|
||||
|
|
||||
const valueComputed = computed(() => { |
|
||||
if (!Tools.isEmpty(props.value) && !Tools.isEmpty(props.previousPeriod)) { |
|
||||
const result = props.value - props.previousPeriod; |
|
||||
return result; |
|
||||
} |
|
||||
return undefined; |
|
||||
}); |
|
||||
|
|
||||
const iconComputed = computed(() => { |
|
||||
if (!Tools.isEmpty(valueComputed.value) && valueComputed.value === 0) { |
|
||||
return icon.equals; |
|
||||
} else if (!Tools.isEmpty(valueComputed.value) && valueComputed.value > 0) { |
|
||||
return icon.up; |
|
||||
} else if (!Tools.isEmpty(valueComputed.value) && valueComputed.value < 0) { |
|
||||
return icon.down; |
|
||||
} |
|
||||
return undefined; |
|
||||
}); |
|
||||
</script> |
|
@ -1,74 +0,0 @@ |
|||||
<template> |
|
||||
<w-grid |
|
||||
:dense-body="true" |
|
||||
:pageable="false" |
|
||||
:hide-bottom="true" |
|
||||
:auto-fetch-data="true" |
|
||||
:dense="true" |
|
||||
:fetch-data-url="Environment.apiContextPath('api/irbs/financeReportDetail/query/' + rating.ratingData.value['custId'])" |
|
||||
:checkbox-selection="false" |
|
||||
:sort-no="true" |
|
||||
:config-button="false" |
|
||||
:columns="columnsComputed" |
|
||||
:query-criteria="props.queryCriteria" |
|
||||
:sort-by="['projectCode']" |
|
||||
></w-grid> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, onMounted, computed } from 'vue'; |
|
||||
import { Environment } from 'platform-core'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import FinanceProjectCompare from './FinanceProjectCompare.vue'; |
|
||||
import { formatAmt } from '../ts/Utils'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const props = defineProps({ |
|
||||
queryCriteria: { type: Object, default: undefined }, |
|
||||
}); |
|
||||
|
|
||||
const columnsComputed = computed(() => { |
|
||||
const columns = <any>[ |
|
||||
{ name: 'code', label: '科目编码' }, |
|
||||
{ name: 'name', label: '科目名称' }, |
|
||||
]; |
|
||||
if (rating.financeReport.value.length > 0) { |
|
||||
const columnNames = ['value', 'previousPeriodvalue', 'previousPeriod2value', 'previousPeriod3value']; |
|
||||
rating.financeReport.value.forEach((report: any, index: number) => { |
|
||||
const year = report['endDate'].substring(0, 4); |
|
||||
columns.push({ |
|
||||
name: year, |
|
||||
label: year, |
|
||||
columns: [ |
|
||||
{ |
|
||||
name: columnNames[index], |
|
||||
label: '本期值', |
|
||||
align: 'right', |
|
||||
format: (val, row) => { |
|
||||
return formatAmt(val); |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
name: year + 'comparePreviousPeriod', |
|
||||
label: '比上期', |
|
||||
align: 'right', |
|
||||
format: (val, row) => { |
|
||||
return { |
|
||||
componentType: FinanceProjectCompare, |
|
||||
attrs: { |
|
||||
value: row[columnNames[index]], |
|
||||
previousPeriod: index === columnNames.length - 1 ? undefined : row[columnNames[index + 1]], |
|
||||
}, |
|
||||
}; |
|
||||
}, |
|
||||
}, |
|
||||
], |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
return columns; |
|
||||
}); |
|
||||
|
|
||||
onMounted(() => { |
|
||||
rating.api.loadFinanceReport(); |
|
||||
}); |
|
||||
</script> |
|
@ -1,146 +0,0 @@ |
|||||
<template> |
|
||||
<div> |
|
||||
<w-grid |
|
||||
ref="financeReportGridRef" |
|
||||
title="财务报表" |
|
||||
:dense="rating.dense" |
|
||||
:height="150" |
|
||||
:pageable="false" |
|
||||
:hide-bottom="true" |
|
||||
:auto-fetch-data="false" |
|
||||
:fetch-data-url="Environment.apiContextPath('api/irbs/financeReport/getReport')" |
|
||||
:checkbox-selection="false" |
|
||||
:config-button="false" |
|
||||
:columns="[ |
|
||||
{ name: 'endDate', label: '财报日期', format: Formater.dateOnly() }, |
|
||||
// { name: 'type', label: '报表类型', format: Formater.dictionary(rating.dictionary.financeTypeCd) }, |
|
||||
{ |
|
||||
name: 'type', |
|
||||
label: '报表类型', |
|
||||
format: Formater.dictionary(rating.dictionary.financeTypeCd), |
|
||||
}, |
|
||||
{ name: 'sort', label: '报表类别', format: Formater.dictionary(rating.dictionary.financeSortTypeCd) }, |
|
||||
{ name: 'auditedInd', label: '是否审计', format: Formater.dictionary(rating.dictionary.financeStatusCd) }, |
|
||||
{ name: 'caliber', label: '报表口径', format: Formater.dictionary(rating.dictionary.caliberCd) }, |
|
||||
{ name: 'currency', label: '报表币种', format: Formater.dictionary(rating.dictionary.currencyTypeCd) }, |
|
||||
{ name: 'userNo', label: '经办人' }, |
|
||||
{ name: 'remarks', label: '备注' }, |
|
||||
{ |
|
||||
name: 'op', |
|
||||
label: '操作', |
|
||||
format: opFormat, |
|
||||
}, |
|
||||
]" |
|
||||
></w-grid> |
|
||||
<w-dialog ref="financeReportDetailDialogRef" title="财报详情" width="80%" height="80%" body-padding="0px 0px 0px 0px"> |
|
||||
<w-splitter :size="60" horizontal unit="px" disable class="h-full"> |
|
||||
<template #before> |
|
||||
<div class="p-1"> |
|
||||
<w-info-panel :info="financeReport.otherInfo" :column-num="5" :label-width="150"></w-info-panel> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<template #after> |
|
||||
<div class="flex flex-nowrap items-start h-full"> |
|
||||
<div class="flex-none h-full"> |
|
||||
<q-tabs v-model="detailTab" vertical indicator-color="amber" active-color="amber"> |
|
||||
<template v-for="report in financeReport.tabsComputed.value" :key="report.value"> |
|
||||
<q-tab :name="report.value" :icon="report.icon" :label="report.label" /> |
|
||||
</template> |
|
||||
</q-tabs> |
|
||||
</div> |
|
||||
<q-separator vertical /> |
|
||||
<div class="flex-1 h-full"> |
|
||||
<q-tab-panels |
|
||||
v-model="detailTab" |
|
||||
:keep-alive="true" |
|
||||
animated |
|
||||
swipeable |
|
||||
vertical |
|
||||
transition-prev="jump-up" |
|
||||
transition-next="jump-up" |
|
||||
class="h-full" |
|
||||
> |
|
||||
<template v-for="report in financeReport.tabsComputed.value" :key="report.value"> |
|
||||
<q-tab-panel :name="report.value" class="h-full p-1"> |
|
||||
<FinanceProjectGrid |
|
||||
:query-criteria="{ |
|
||||
operator: 'and', |
|
||||
criteria: [ |
|
||||
{ |
|
||||
fieldName: 'reportId', |
|
||||
operator: 'equals', |
|
||||
value: financeReport.reportId, |
|
||||
}, |
|
||||
{ |
|
||||
fieldName: 'projectType', |
|
||||
operator: 'equals', |
|
||||
value: report.value, |
|
||||
}, |
|
||||
], |
|
||||
}" |
|
||||
></FinanceProjectGrid> |
|
||||
</q-tab-panel> |
|
||||
</template> |
|
||||
</q-tab-panels> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</w-dialog> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, nextTick, reactive, inject } from 'vue'; |
|
||||
import { Formater, Environment } from 'platform-core'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import FinanceProjectGrid from './FinanceProjectGrid.vue'; |
|
||||
import { FinanceReport, Constant } from '../ts/FinanceReport'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const financeReportGridRef = ref(); |
|
||||
const getFinanceReportGridRef = () => { |
|
||||
return financeReportGridRef.value; |
|
||||
}; |
|
||||
rating.refs.setFinanceReportGridRefFunction(getFinanceReportGridRef); |
|
||||
const financeReportDetailDialogRef = ref(); |
|
||||
const financeReport = new FinanceReport(); |
|
||||
|
|
||||
const splitterModel = ref(60); |
|
||||
const detailTab = ref(''); |
|
||||
|
|
||||
const opFormat = (value, row) => { |
|
||||
if (row && row['sort']) { |
|
||||
return { |
|
||||
componentType: 'q-chip', |
|
||||
attrs: { |
|
||||
dense: true, |
|
||||
color: 'primary', |
|
||||
icon: 'visibility', |
|
||||
textColor: 'white', |
|
||||
square: true, |
|
||||
size: rating.dense ? 'xs' : 'md', |
|
||||
label: '查 看', |
|
||||
onclick: () => { |
|
||||
financeReport.reportType.value = row['sort']; |
|
||||
if (financeReport.reportType.value === Constant.REPORT_TYPE_COMPANY) { |
|
||||
detailTab.value = FinanceReport.reportDetail.c_balance_sheet.value; |
|
||||
} else { |
|
||||
detailTab.value = FinanceReport.reportDetail.p_balance_sheet.value; |
|
||||
} |
|
||||
financeReport.reportId = row['id']; |
|
||||
financeReportDetailDialogRef.value.show(); |
|
||||
financeReport.otherInfo = []; |
|
||||
financeReport.otherInfo.push({ label: '财务报表截至日期', value: row['endDate'], format: Formater.dateOnly() }); |
|
||||
financeReport.otherInfo.push({ label: '是否经过审计', value: row['auditedInd'], format: Formater.dictionary(rating.dictionary.financeStatusCd) }); |
|
||||
financeReport.otherInfo.push({ label: '财务报表类别', value: row['sort'], format: Formater.dictionary(rating.dictionary.financeTypeCd) }); |
|
||||
financeReport.otherInfo.push({ label: '财务报表口径', value: row['caliber'], format: Formater.dictionary(rating.dictionary.caliberCd) }); |
|
||||
financeReport.otherInfo.push({ label: '财务报表币种', value: row['currency'], format: Formater.dictionary(rating.dictionary.currencyTypeCd) }); |
|
||||
}, |
|
||||
}, |
|
||||
}; |
|
||||
} else { |
|
||||
return ''; |
|
||||
} |
|
||||
}; |
|
||||
</script> |
|
@ -1,39 +0,0 @@ |
|||||
<template> |
|
||||
<div class="h-full"> |
|
||||
<w-splitter :size="36" horizontal class="h-full" unit="px" disable> |
|
||||
<template #before> |
|
||||
<q-tabs v-model="tab" inline-label dense indicator-color="amber" align="right" active-color="amber"> |
|
||||
<template v-for="report in financeReport.tabsComputed.value" :key="report.value"> |
|
||||
<q-tab :name="report.value" :icon="report.icon" :label="report.label" /> |
|
||||
</template> |
|
||||
</q-tabs> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<q-tab-panels v-model="tab" :keep-alive="true" animated swipeable vertical transition-prev="jump-up" transition-next="jump-up" class="h-full"> |
|
||||
<template v-for="report in financeReport.tabsComputed.value" :key="report.value"> |
|
||||
<q-tab-panel :name="report.value" class="h-full p-1"> |
|
||||
<FinanceProjectGrid |
|
||||
:query-criteria="{ |
|
||||
fieldName: 'projectType', |
|
||||
operator: 'equals', |
|
||||
value: report.value, |
|
||||
}" |
|
||||
></FinanceProjectGrid> |
|
||||
</q-tab-panel> |
|
||||
</template> |
|
||||
</q-tab-panels> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, ref } from 'vue'; |
|
||||
import { Environment, Formater } from 'platform-core'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import { FinanceReport, Constant } from '../ts/FinanceReport'; |
|
||||
import FinanceProjectGrid from './FinanceProjectGrid.vue'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const financeReport = new FinanceReport(); |
|
||||
const tab = ref(FinanceReport.reportDetail.c_balance_sheet.value); |
|
||||
</script> |
|
@ -1,29 +0,0 @@ |
|||||
<template> |
|
||||
<div v-if="rating.systemParameter.showTotalScore.value"> |
|
||||
<span class="pl-[8px]">初评结果</span> |
|
||||
<w-info-panel :info="rating.firstRatingData.value" :column-num="4"></w-info-panel> |
|
||||
</div> |
|
||||
<div v-else> |
|
||||
<q-list dense> |
|
||||
<q-item> |
|
||||
<q-item-section avatar>初评结果</q-item-section> |
|
||||
<q-item-section> |
|
||||
<RatingLevelSlider v-model="firstRatingLevelComputed"></RatingLevelSlider> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</q-list> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, computed } from 'vue'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import RatingLevelSlider from '../../RatingLevelSlider.vue'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const firstRatingLevelComputed = computed(() => { |
|
||||
if (rating.firstRatingData.value && rating.firstRatingData.value.length > 0) { |
|
||||
return rating.firstRatingData.value[0]['value']; |
|
||||
} |
|
||||
return undefined; |
|
||||
}); |
|
||||
</script> |
|
@ -1,51 +0,0 @@ |
|||||
<template> |
|
||||
<w-grid |
|
||||
ref="custHistRatingGridRef" |
|
||||
:dense="rating.dense" |
|
||||
:pageable="false" |
|
||||
:hide-bottom="true" |
|
||||
:auto-fetch-data="true" |
|
||||
:toolbar-actions="[]" |
|
||||
:fetch-data-url="Environment.apiContextPath('api/irbs/companyRating')" |
|
||||
:checkbox-selection="false" |
|
||||
:config-button="false" |
|
||||
:columns="[ |
|
||||
{ name: 'id', label: '申请编号' }, |
|
||||
{ name: 'custNo', label: '客户号' }, |
|
||||
{ name: 'custName', label: '客户名称' }, |
|
||||
// { name: 'modelScore', label: '得分' }, |
|
||||
{ name: 'modelLevel', label: '系统评级等级' }, |
|
||||
{ name: 'adjLevel', label: '调整等级' }, |
|
||||
{ name: 'initLevel', label: '初评等级' }, |
|
||||
{ name: 'finalLevel', label: '最终等级' }, |
|
||||
{ name: 'effectiveTime', label: '生效日期', format: Formater.dateOnly() }, |
|
||||
{ name: 'matureTime', label: '到期日期', format: Formater.dateOnly() }, |
|
||||
{ name: 'ratingStatus', label: '评级状态', format: Formater.enum(rating.enum.ratingStatus) }, |
|
||||
{ name: 'launchUser', label: '发起人' }, |
|
||||
{ name: 'processStatus', label: '状态', format: Formater.enum(rating.enum.ratingProcessStatus) }, |
|
||||
]" |
|
||||
:query-criteria="{ |
|
||||
operator: 'and', |
|
||||
criteria: [ |
|
||||
{ |
|
||||
fieldName: 'custNo', |
|
||||
operator: 'equals', |
|
||||
value: rating.ratingData.value['custNo'], |
|
||||
}, |
|
||||
{ |
|
||||
fieldName: 'processStatus', |
|
||||
operator: 'equals', |
|
||||
value: Constant.DEFAULT_PROCESS_STATUS.PASS, |
|
||||
}, |
|
||||
], |
|
||||
}" |
|
||||
></w-grid> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { Environment, Formater } from 'platform-core'; |
|
||||
import { inject } from 'vue'; |
|
||||
import { Constant } from '../ts/Constant'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
</script> |
|
@ -1,33 +0,0 @@ |
|||||
<template> |
|
||||
<div class="h-full"> |
|
||||
<w-splitter :size="36" horizontal class="h-full" unit="px" disable> |
|
||||
<template #before> |
|
||||
<q-tabs v-model="tab" inline-label dense indicator-color="amber" align="left" active-color="amber"> |
|
||||
<template v-for="item in HistGird.tabs" :key="item.value"> |
|
||||
<q-tab :name="item.value" :icon="item.icon" :label="item.label" /> |
|
||||
</template> |
|
||||
</q-tabs> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<q-tab-panels v-model="tab" :keep-alive="true" animated swipeable vertical transition-prev="jump-up" transition-next="jump-up" class="h-full"> |
|
||||
<template v-for="item in HistGird.tabs" :key="item.value"> |
|
||||
<q-tab-panel :name="item.value" class="h-full p-1"> |
|
||||
<HistRatingGrid v-if="item.value === HistGird.type.histRating.value"></HistRatingGrid> |
|
||||
<DefaultCognizanceGrid v-else-if="item.value === HistGird.type.defaultCognizance.value"></DefaultCognizanceGrid> |
|
||||
<DefaultRebirthGrid v-else-if="item.value === HistGird.type.defaultRebirth.value"></DefaultRebirthGrid> |
|
||||
</q-tab-panel> |
|
||||
</template> |
|
||||
</q-tab-panels> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref } from 'vue'; |
|
||||
import { HistGird } from '../ts/HistGrid'; |
|
||||
import HistRatingGrid from './HistRatingGrid.vue'; |
|
||||
import DefaultCognizanceGrid from './DefaultCognizanceGrid.vue'; |
|
||||
import DefaultRebirthGrid from './DefaultRebirthGrid.vue'; |
|
||||
|
|
||||
const tab = ref(HistGird.type.histRating.value); |
|
||||
</script> |
|
@ -1,40 +0,0 @@ |
|||||
<template> |
|
||||
<div class="h-full pl-1"> |
|
||||
<w-splitter v-if="!rating.cm.isAwaitSubmitProcessStatus.value" :size="rating.readMode ? 100 : 50" horizontal class="h-full" disable :show-separator="false"> |
|
||||
<template #before> |
|
||||
<w-splitter :size="36" horizontal class="h-full" unit="px" disable :show-separator="false"> |
|
||||
<template #before> |
|
||||
<q-tabs v-model="histTab" dense no-caps inline-label align="right"> |
|
||||
<q-tab name="timeline" icon="query_builder" label="审批时间线" /> |
|
||||
<q-tab name="grid" icon="grid_on" label="审批列表" /> |
|
||||
</q-tabs> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<q-tab-panels v-model="histTab" class="p-0 h-full"> |
|
||||
<q-tab-panel name="timeline" class="p-0 pt-1 px-2 h-full"> |
|
||||
<Timeline></Timeline> |
|
||||
</q-tab-panel> |
|
||||
<q-tab-panel name="grid" class="p-0 pt-1 h-full"> |
|
||||
<OpinionGrid></OpinionGrid> |
|
||||
</q-tab-panel> |
|
||||
</q-tab-panels> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<Overturn></Overturn> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
<Overturn v-else class="p-1"></Overturn> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, ref } from 'vue'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import OpinionGrid from './OpinionGrid.vue'; |
|
||||
import Overturn from './Overturn.vue'; |
|
||||
import Timeline from './Timeline.vue'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const histTab = ref('timeline'); |
|
||||
</script> |
|
@ -1,44 +0,0 @@ |
|||||
<template> |
|
||||
<w-grid |
|
||||
ref="opinionGridRef" |
|
||||
:dense="rating.dense" |
|
||||
:pageable="false" |
|
||||
:hide-bottom="true" |
|
||||
:auto-fetch-data="true" |
|
||||
:fetch-data-url="histOpinionUrl" |
|
||||
:checkbox-selection="false" |
|
||||
:config-button="false" |
|
||||
:columns="[ |
|
||||
{ name: 'roleName', label: '岗位名称', width: 150 }, |
|
||||
{ name: 'userCode', label: '操作人工号', width: 100 }, |
|
||||
{ name: 'userName', label: '操作人名称', width: 100 }, |
|
||||
{ name: 'operationOpinion', label: '操作意见', width: 100, format: Formater.dictionary({ items: RatingProcessOperationStatus }) }, |
|
||||
{ name: 'adjReason', label: '意见说明', width: 200 }, |
|
||||
{ name: 'lastModifyDate', label: '操作时间', width: 150 }, |
|
||||
{ name: 'isOverturn', label: '是否推翻', width: 80, format: Formater.yesNo() }, |
|
||||
{ name: 'overturnType', label: '推翻类型', width: 100, format: Formater.dictionary(rating.dictionary.overturnType) }, |
|
||||
{ name: 'suggestLevel', label: '建议等级', width: 100 }, |
|
||||
{ name: 'fileCount', label: '附件列表', width: 100 }, |
|
||||
]" |
|
||||
:query-criteria="{ |
|
||||
fieldName: 'ratingId', |
|
||||
operator: 'equals', |
|
||||
value: rating.ratingData.value['id'], |
|
||||
}" |
|
||||
:sort-by="['-lastModifyDate']" |
|
||||
></w-grid> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, ref } from 'vue'; |
|
||||
import { Environment, Formater } from 'platform-core'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import { RatingProcessOperationStatus } from '../../CustRating'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const overturnFormRef = ref(); |
|
||||
const getOverturnFormRef = () => { |
|
||||
return overturnFormRef.value; |
|
||||
}; |
|
||||
rating.refs.setOverturnFormRefFunction(getOverturnFormRef); |
|
||||
const histOpinionUrl = Environment.apiContextPath('api/irbs/ratingOverturn'); |
|
||||
</script> |
|
@ -1,77 +0,0 @@ |
|||||
<template> |
|
||||
<w-card-panel v-bind="panelAttrsComputed"> |
|
||||
<div class="flex flex-row gap-4"> |
|
||||
<div v-if="rating.cm.isAwaitSubmitProcessStatus.value" class="basis-1/4"> |
|
||||
<w-info-panel :info="rating.overturnTipData.value" :column-num="1"></w-info-panel> |
|
||||
</div> |
|
||||
<div :class="rating.cm.isAwaitSubmitProcessStatus.value ? 'basis-2/3' : 'flex-1'"> |
|
||||
<w-form |
|
||||
ref="overturnFormRef" |
|
||||
:fields="[ |
|
||||
{ label: '附件', name: 'file', type: 'w-file' }, |
|
||||
{ label: '是否推翻', name: 'isOverturn', type: 'w-checkbox' }, |
|
||||
{ |
|
||||
label: '推翻类型', |
|
||||
name: 'overturnType', |
|
||||
type: 'w-select', |
|
||||
options: Options.dictionary(rating.dictionary.overturnType), |
|
||||
requiredIf: true, |
|
||||
showIf: (args) => { |
|
||||
if (args?.form && args.form.getFieldValue('isOverturn')) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
label: '建议等级', |
|
||||
name: 'suggestLevel', |
|
||||
type: 'w-select', |
|
||||
options: RatingLevelOptions, |
|
||||
requiredIf: true, |
|
||||
showIf: (args) => { |
|
||||
if (args?.form && args.form.getFieldValue('isOverturn')) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
label: '意见说明', |
|
||||
name: 'adjReason', |
|
||||
type: 'w-textarea', |
|
||||
requiredIf: true, |
|
||||
}, |
|
||||
]" |
|
||||
:cols-num="1" |
|
||||
></w-form> |
|
||||
</div> |
|
||||
</div> |
|
||||
</w-card-panel> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { Options } from 'platform-core'; |
|
||||
import { computed, ref, inject } from 'vue'; |
|
||||
import { RatingLevelOptions } from '../../CustRating'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const overturnFormRef = ref(); |
|
||||
const getOverturnFormRef = () => { |
|
||||
return overturnFormRef.value; |
|
||||
}; |
|
||||
rating.refs.setOverturnFormRefFunction(getOverturnFormRef); |
|
||||
const panelAttrsComputed = computed(() => { |
|
||||
if (rating.cm.isAwaitSubmitProcessStatus.value) { |
|
||||
return { |
|
||||
label: '评级推翻', |
|
||||
icon: 'model_training', |
|
||||
}; |
|
||||
} else { |
|
||||
return { |
|
||||
label: '审批意见', |
|
||||
icon: 'comment', |
|
||||
}; |
|
||||
} |
|
||||
}); |
|
||||
</script> |
|
@ -1,106 +0,0 @@ |
|||||
<template> |
|
||||
<w-splitter :size="titleSplitterModel" horizontal unit="px" disable class="h-full"> |
|
||||
<template #before> |
|
||||
<w-card-panel :bordered="false" :title-mode="true" icon="scatter_plot" :icon-attrs="{ color: 'green' }" color="green"> |
|
||||
<template #label> |
|
||||
<div class="flex justify-between"> |
|
||||
<div> |
|
||||
定性分析<q-badge v-if="existsWaitSelectComputed" color="red" floating transparent class="qa-badge">{{ |
|
||||
rating.waitSelectFields.value.length |
|
||||
}}</q-badge> |
|
||||
</div> |
|
||||
<q-toggle v-if="showToggleComputed" v-model="qaMode" color="blue" dense size="40px" icon="bi-quora" label="问卷模式" /> |
|
||||
<q-toggle v-if="props.readOnly" v-model="showOptionsModel" color="blue" dense size="40px" icon="hdr_auto" label="显示全部选项" /> |
|
||||
</div> |
|
||||
</template> |
|
||||
</w-card-panel> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<w-form |
|
||||
v-show="showQaPanelComputed" |
|
||||
ref="qa-qualFormRef" |
|
||||
:cols-num="1" |
|
||||
:fields="qaFormFieldComputed" |
|
||||
class="px-1 py-1" |
|
||||
@update-value="qaFormUpdateValue" |
|
||||
></w-form> |
|
||||
<w-form |
|
||||
v-show="!showQaPanelComputed" |
|
||||
ref="qualitativeFormRef" |
|
||||
:fields="qualFormFieldsComputed" |
|
||||
:cols-num="1" |
|
||||
:y-gap="10" |
|
||||
class="pl-1 py-1" |
|
||||
@update-value="formUpdateValue" |
|
||||
></w-form> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, ref, computed } from 'vue'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
|
|
||||
const qualitativeFormRef = ref(); |
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const titleSplitterModel = ref(34); |
|
||||
const getQualitativeFormRef = () => { |
|
||||
return qualitativeFormRef.value; |
|
||||
}; |
|
||||
rating.refs.setQualitativeFormRefFunction(getQualitativeFormRef); |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
// 问答模式 |
|
||||
qualQaMode: { type: Boolean, default: true }, |
|
||||
// 只读模式 |
|
||||
readOnly: { type: Boolean, default: false }, |
|
||||
// 显示所有选项 |
|
||||
showOptions: { type: Boolean, default: false }, |
|
||||
}); |
|
||||
const qaMode = ref(props.readOnly ? false : props.qualQaMode); |
|
||||
const showOptionsModel = ref(props.showOptions); |
|
||||
|
|
||||
const qaFormFieldComputed = computed(() => { |
|
||||
const result = <any>[]; |
|
||||
if (rating.waitSelectFields.value?.length > 0) { |
|
||||
result.push({ |
|
||||
type: 'w-form-group', |
|
||||
label: rating.waitSelectFields.value[0]['group'], |
|
||||
mode: 'card', |
|
||||
fields: [rating.waitSelectFields.value[0]['field']], |
|
||||
}); |
|
||||
} |
|
||||
return result; |
|
||||
}); |
|
||||
const showQaPanelComputed = computed(() => { |
|
||||
return showToggleComputed.value && qaMode.value; |
|
||||
}); |
|
||||
const showToggleComputed = computed(() => { |
|
||||
return props.qualQaMode && existsWaitSelectComputed.value; |
|
||||
}); |
|
||||
const existsWaitSelectComputed = computed(() => { |
|
||||
return rating.waitSelectFields.value.length > 0 && !props.readOnly; |
|
||||
}); |
|
||||
const qualFormFieldsComputed = computed(() => { |
|
||||
if (showOptionsModel.value) { |
|
||||
return rating.qualFormData.value; |
|
||||
} |
|
||||
return rating.qualSimpleOptionData.value; |
|
||||
}); |
|
||||
|
|
||||
const qaFormUpdateValue = (args) => { |
|
||||
qualitativeFormRef.value.setFieldValue(args.fieldName, args.fieldValue); |
|
||||
rating.waitSelectFields.value = rating.waitSelectFields.value.slice(1); |
|
||||
}; |
|
||||
const formUpdateValue = (args) => { |
|
||||
const result = rating.waitSelectFields.value.filter((field) => field.field['name'] !== args.fieldName); |
|
||||
rating.waitSelectFields.value = result; |
|
||||
}; |
|
||||
</script> |
|
||||
<style lang="css"> |
|
||||
.qa-badge { |
|
||||
position: relative; |
|
||||
top: -7px; |
|
||||
right: -2px; |
|
||||
cursor: inherit; |
|
||||
} |
|
||||
</style> |
|
@ -1,51 +0,0 @@ |
|||||
<template> |
|
||||
<w-splitter :size="34" horizontal unit="px" disable class="h-full"> |
|
||||
<template #before> |
|
||||
<w-card-panel label="定量分析" :bordered="false" :title-mode="true" icon="bar_chart" :icon-attrs="{ color: 'teal' }" color="teal"></w-card-panel> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<div v-if="rating.systemParameter.showTotalScore.value" class="p-1"> |
|
||||
<w-card-panel :info="rating.quanData.value"></w-card-panel> |
|
||||
</div> |
|
||||
<div v-if="rating.systemParameter.showIndex.value" class="rating-quan"> |
|
||||
<q-list> |
|
||||
<template v-for="(values, key, a) in rating.quanScoreDtlData.value" :key="a"> |
|
||||
<!-- <q-item-label header><q-icon name="toc" size="25px" />定量指标详情</q-item-label> --> |
|
||||
<template v-for="(dtl, index) in values" :key="index"> |
|
||||
<q-item> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ dtl['INDEX_NAME'] }}</q-item-label> |
|
||||
<q-item-label caption> |
|
||||
{{ round(dtl['INDEX_VALUE'], 2) }} |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
<q-item-section v-if="rating.systemParameter.showIndexScore.value && !Tools.isEmpty(dtl['INDEX_SCORE'])" side> |
|
||||
<q-chip :clickable="false" :ripple="false" color="green" text-color="white"> |
|
||||
{{ round(dtl['INDEX_SCORE'], 2) }} |
|
||||
</q-chip> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</template> |
|
||||
</template> |
|
||||
</q-list> |
|
||||
</div> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { Tools } from 'platform-core'; |
|
||||
import { inject } from 'vue'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import { round } from '../ts/Utils'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
</script> |
|
||||
<style lang="css"> |
|
||||
.rating-quan { |
|
||||
padding-top: 10px; |
|
||||
} |
|
||||
.rating-quan .text-caption { |
|
||||
font-size: 1rem; |
|
||||
color: #2196f3; |
|
||||
} |
|
||||
</style> |
|
@ -1,20 +0,0 @@ |
|||||
<template> |
|
||||
<div class="h-full"> |
|
||||
<w-splitter :size="25" :separator-color="rating.separatorColor" class="h-full"> |
|
||||
<template #before> |
|
||||
<QuanAnalysis></QuanAnalysis> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<QualAnalysis :qual-qa-mode="rating.systemParameter.qualQaMode.value" :show-options="true"></QualAnalysis> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject } from 'vue'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import QuanAnalysis from './QuanAnalysis.vue'; |
|
||||
import QualAnalysis from './QualAnalysis.vue'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
</script> |
|
@ -1,36 +0,0 @@ |
|||||
<template> |
|
||||
<div v-if="rating.systemParameter.reportShowScoreDtl.value" class="h-full"> |
|
||||
<w-splitter :size="25" :separator-color="rating.separatorColor"> |
|
||||
<template #before> |
|
||||
<QuanAnalysis></QuanAnalysis> |
|
||||
</template> |
|
||||
|
|
||||
<template #after> |
|
||||
<w-splitter v-if="splitComputed" :size="65" class="h-full" :separator-color="rating.separatorColor"> |
|
||||
<template #before> |
|
||||
<QualAnalysis :qual-qa-mode="false" :read-only="true"></QualAnalysis> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<AdjustItemIndex></AdjustItemIndex> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
<QualAnalysis v-else :qual-qa-mode="false" :read-only="true"></QualAnalysis> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { inject, computed } from 'vue'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import QuanAnalysis from './QuanAnalysis.vue'; |
|
||||
import QualAnalysis from './QualAnalysis.vue'; |
|
||||
import AdjustItemIndex from './AdjustItemIndex.vue'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const splitComputed = computed(() => { |
|
||||
if (rating.adjustItemScoreDtlData.value && Object.keys(rating.adjustItemScoreDtlData.value).length > 0) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
</script> |
|
@ -1,34 +0,0 @@ |
|||||
<template> |
|
||||
<div class="h-full"> |
|
||||
<w-splitter :size="splitterSizeComputed" :limits="[0, Infinity]" horizontal unit="px" disable class="h-full"> |
|
||||
<template #before> |
|
||||
<ReportRating></ReportRating> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<w-splitter :size="70" horizontal :separator-color="rating.separatorColor"> |
|
||||
<template #before> |
|
||||
<ReportIndex></ReportIndex> |
|
||||
</template> |
|
||||
<template #after> |
|
||||
<HistTab></HistTab> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</template> |
|
||||
</w-splitter> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { computed, inject } from 'vue'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import HistTab from './HistTab.vue'; |
|
||||
import ReportIndex from './ReportIndex.vue'; |
|
||||
import ReportRating from './ReportRating.vue'; |
|
||||
|
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const splitterSizeComputed = computed(() => { |
|
||||
if (rating.showAllRatingInfoModel.value) { |
|
||||
return 125; |
|
||||
} |
|
||||
return 40; |
|
||||
}); |
|
||||
</script> |
|
@ -1,157 +0,0 @@ |
|||||
<template> |
|
||||
<!-- <w-card-panel label="评级结果" :bordered="false"> |
|
||||
<div class="flex"> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>模型名称</div> |
|
||||
<div :class="valueClassComputed"> |
|
||||
<q-chip outline square size="md" :clickable="false" :ripple="false" :label="rating.ratingData.value['modelName']" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>模型级别</div> |
|
||||
<div :class="valueClassComputed"> |
|
||||
<q-chip outline square size="md" :clickable="false" :ripple="false" :label="rating.ratingData.value['modelLevel']" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>调整后级别</div> |
|
||||
<div v-if="rating.ratingData.value['adjLevel']" :class="valueClassComputed"> |
|
||||
<q-chip outline square size="md" :clickable="false" :ripple="false" :label="rating.ratingData.value['adjLevel']" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>推翻级别</div> |
|
||||
<div v-if="rating.ratingData.value['tf']" :class="valueClassComputed"> |
|
||||
<q-chip outline square size="md" :clickable="false" :ripple="false" :label="rating.ratingData.value['tf']" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>最终认定级别</div> |
|
||||
<div v-if="rating.ratingData.value['finalLevel']" :class="valueClassComputed"> |
|
||||
<q-chip outline square size="md" :clickable="false" :ripple="false" :label="rating.ratingData.value['finalLevel']" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-1 text-center"> |
|
||||
<div>客户经理</div> |
|
||||
<div v-if="rating.ratingData.value['managerName']" :class="valueClassComputed"> |
|
||||
<q-chip outline square size="md" :clickable="false" :ripple="false" :label="rating.ratingData.value['managerName']" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</w-card-panel> --> |
|
||||
<div class="flex"> |
|
||||
<div class="flex-1"> |
|
||||
<div :class="classComputed"> |
|
||||
<div :class="itemClassComputed"> |
|
||||
<div :class="labelClassComputed">模型名称:</div> |
|
||||
<div :class="valueClassComputed">{{ rating.ratingData.value['modelName'] }}</div> |
|
||||
</div> |
|
||||
<div :class="itemClassComputed"> |
|
||||
<div :class="labelClassComputed">模型评级等级:</div> |
|
||||
<div :class="valueClassComputed" @mouseover="handleHover('.rr-modelLevel', rating.ratingData.value['modelLevel'])" @mouseout="handleOut"> |
|
||||
<span class="rr-modelLevel underline">{{ rating.ratingData.value['modelLevel'] }}</span> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div :class="itemClassComputed"> |
|
||||
<div :class="labelClassComputed">调整后级别:</div> |
|
||||
<div :class="valueClassComputed" @mouseover="handleHover('.rr-adjLevel', rating.ratingData.value['adjLevel'])" @mouseout="handleOut"> |
|
||||
<span class="rr-adjLevel underline">{{ rating.ratingData.value['adjLevel'] }}</span> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div :class="itemClassComputed"> |
|
||||
<div :class="labelClassComputed">最终认定级别:</div> |
|
||||
<div :class="valueClassComputed" @mouseover="handleHover('.rr-finalLevel', rating.ratingData.value['finalLevel'])" @mouseout="handleOut"> |
|
||||
<span class="rr-finalLevel underline">{{ rating.ratingData.value['finalLevel'] }}</span> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div :class="itemClassComputed"> |
|
||||
<div :class="labelClassComputed">客户经理:</div> |
|
||||
<div :class="valueClassComputed">{{ rating.ratingData.value['managerName'] }}</div> |
|
||||
</div> |
|
||||
<template v-if="rating.showAllRatingInfoModel.value"> |
|
||||
<div :class="itemClassComputed"> |
|
||||
<div :class="labelClassComputed">初始级别:</div> |
|
||||
<div :class="valueClassComputed" @mouseover="handleHover('.rr-initLevel', rating.ratingData.value['initLevel'])" @mouseout="handleOut"> |
|
||||
<span class="rr-initLevel underline">{{ rating.ratingData.value['initLevel'] }}</span> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div :class="itemClassComputed"> |
|
||||
<div :class="labelClassComputed">上一次建议级别:</div> |
|
||||
<div :class="valueClassComputed" @mouseover="handleHover('.rr-spLevel', rating.ratingData.value['spLevel'])" @mouseout="handleOut"> |
|
||||
<span class="rr-spLevel underline">{{ rating.ratingData.value['spLevel'] }}</span> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div :class="itemClassComputed"> |
|
||||
<div :class="labelClassComputed">评级发起日期:</div> |
|
||||
<div :class="valueClassComputed" v-html="Formater.dateOnly()(rating.ratingData.value['startTime'])"></div> |
|
||||
</div> |
|
||||
<div :class="itemClassComputed"> |
|
||||
<div :class="labelClassComputed">评级生效日期:</div> |
|
||||
<div :class="valueClassComputed" v-html="Formater.dateOnly()(rating.ratingData.value['effectiveTime'])"></div> |
|
||||
</div> |
|
||||
<div :class="itemClassComputed"> |
|
||||
<div :class="labelClassComputed">评级到期日期:</div> |
|
||||
<div :class="valueClassComputed" v-html="Formater.dateOnly()(rating.ratingData.value['matureTime'])"></div> |
|
||||
</div> |
|
||||
<div :class="itemClassComputed"> |
|
||||
<div :class="labelClassComputed">评级状态:</div> |
|
||||
<div :class="valueClassComputed">{{ Formater.enum(rating.enum.ratingStatus)(rating.ratingData.value['ratingStatus']) }}</div> |
|
||||
</div> |
|
||||
<div :class="itemClassComputed"> |
|
||||
<div :class="labelClassComputed">流程状态:</div> |
|
||||
<div :class="valueClassComputed">{{ Formater.enum(rating.enum.ratingProcessStatus)(rating.ratingData.value['processStatus']) }}</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="flex-none pr-1 pt-1"> |
|
||||
<q-btn |
|
||||
flat |
|
||||
round |
|
||||
color="primary" |
|
||||
size="sm" |
|
||||
:icon="rating.showAllRatingInfoModel.value ? 'bi-arrow-up-circle-fill' : 'bi-arrow-down-circle-fill'" |
|
||||
@click="rating.showAllRatingInfoModel.value = !rating.showAllRatingInfoModel.value" |
|
||||
/> |
|
||||
</div> |
|
||||
<q-popup-proxy v-model="ratingLevelShowModel" :target="ratingLevelTarget" no-parent-event> |
|
||||
<div style="height: 80px; width: 650px; padding: 20px 10px 10px 20px"> |
|
||||
<RatingLevelSlider v-model="ratingLevel"></RatingLevelSlider> |
|
||||
</div> |
|
||||
</q-popup-proxy> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, inject, computed } from 'vue'; |
|
||||
import { Formater } from 'platform-core'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import RatingLevelSlider from '../../RatingLevelSlider.vue'; |
|
||||
|
|
||||
const ratingLevelShowModel = ref(false); |
|
||||
const ratingLevel = ref(''); |
|
||||
const ratingLevelTarget = ref(''); |
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const itemClassComputed = computed(() => { |
|
||||
return 'flex items-center'; |
|
||||
}); |
|
||||
const labelClassComputed = computed(() => { |
|
||||
return 'flex-none text-gray-500'; |
|
||||
}); |
|
||||
const valueClassComputed = computed(() => { |
|
||||
return 'flex-1 text-2xl'; |
|
||||
}); |
|
||||
const classComputed = computed(() => { |
|
||||
if (rating.showAllRatingInfoModel) { |
|
||||
return 'grid grid-cols-5 gap-3 pl-3 pt-1'; |
|
||||
} |
|
||||
return 'grid grid-cols-5 gap-x-3 pl-3 pt-1'; |
|
||||
}); |
|
||||
const handleHover = (target_, ratingLevel_: any) => { |
|
||||
ratingLevel.value = ratingLevel_; |
|
||||
ratingLevelTarget.value = target_; |
|
||||
ratingLevelShowModel.value = true; |
|
||||
}; |
|
||||
const handleOut = () => { |
|
||||
ratingLevelShowModel.value = false; |
|
||||
}; |
|
||||
</script> |
|
@ -1,82 +0,0 @@ |
|||||
<template> |
|
||||
<div class="rating-timeline"> |
|
||||
<div v-if="timelineData.length < 1" style="display: flex; justify-content: center; align-items: center; height: 100%"> |
|
||||
<q-icon size="2em" name="info" />{{ $t('tip.noData') }} |
|
||||
</div> |
|
||||
<q-timeline v-else layout="dense" side="right" color="secondary"> |
|
||||
<template v-for="(opinion, index) in timelineData" :key="index"> |
|
||||
<q-timeline-entry side="left" :color="timelineEntryColor(opinion)"> |
|
||||
<div style="background-color: #f9f9f9"> |
|
||||
{{ opinion['adjReason'] }} |
|
||||
</div> |
|
||||
<template #subtitle> |
|
||||
<div class="flex justify-between"> |
|
||||
<span class="text-sm">{{ timelineTitleFormat(opinion) }}</span> |
|
||||
<div class="flex gap-x-2"> |
|
||||
<span>{{ opinion['userName'] }}</span> |
|
||||
<span class="text-blue-grey-6">7分钟以前</span> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
</q-timeline-entry> |
|
||||
</template> |
|
||||
</q-timeline> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, onMounted, inject } from 'vue'; |
|
||||
import { $t, axios, Environment } from 'platform-core'; |
|
||||
import { Rating } from '../ts/Rating'; |
|
||||
import { RatingProcessOperationStatus } from '../../CustRating'; |
|
||||
|
|
||||
const timelineData = ref([]); |
|
||||
const rating = <Rating>inject('rating'); |
|
||||
const histOpinionUrl = Environment.apiContextPath('api/irbs/ratingOverturn'); |
|
||||
const timelineTitleFormat = (opinion: any) => { |
|
||||
const findResult = RatingProcessOperationStatus.find((item) => item['value'] === opinion['operationOpinion']); |
|
||||
if (findResult) { |
|
||||
return findResult['label']; |
|
||||
} |
|
||||
return ''; |
|
||||
}; |
|
||||
const timelineSubTitleFormat = (opinion: any) => { |
|
||||
return opinion['userName'] + ' ' + timelineTitleFormat(opinion) + ' ' + '7分钟以前'; |
|
||||
}; |
|
||||
const timelineEntryColor = (opinion: any) => { |
|
||||
const operator = opinion['operationOpinion']; |
|
||||
if (operator === '20') { |
|
||||
return 'red'; |
|
||||
} else if (operator === '30' || operator === '40') { |
|
||||
return 'orange'; |
|
||||
} |
|
||||
return 'secondary'; |
|
||||
}; |
|
||||
|
|
||||
onMounted(() => { |
|
||||
const urlSearchParams = new URLSearchParams({ sortBy: ['-lastModifyDate'] }); |
|
||||
urlSearchParams.append('pageable', 'false'); |
|
||||
const criteria = { |
|
||||
fieldName: 'ratingId', |
|
||||
operator: 'equals', |
|
||||
value: rating.ratingData.value['id'], |
|
||||
}; |
|
||||
urlSearchParams.append('criteria', JSON.stringify(criteria)); |
|
||||
axios.get(histOpinionUrl, { params: urlSearchParams }).then((resp) => { |
|
||||
if (resp['code'] === 200) { |
|
||||
timelineData.value = resp.data.content; |
|
||||
} |
|
||||
}); |
|
||||
}); |
|
||||
</script> |
|
||||
<style lang="css"> |
|
||||
.rating-timeline .q-timeline--dense--right .q-timeline__entry { |
|
||||
padding-left: 25px; |
|
||||
} |
|
||||
.rating-timeline .q-timeline__title { |
|
||||
margin-top: 0; |
|
||||
margin-bottom: 0px; |
|
||||
} |
|
||||
.rating-timeline .q-timeline__content { |
|
||||
padding-bottom: 10px; |
|
||||
} |
|
||||
</style> |
|
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue