84 changed files with 4707 additions and 322 deletions
@ -1,15 +1,30 @@ |
|||
package irbs.cust.rating.controller; |
|||
|
|||
import io.sc.platform.mvc.controller.support.RestCrudController; |
|||
import io.sc.platform.orm.service.support.QueryParameter; |
|||
import irbs.cust.rating.jpa.entity.FinanceReportDetail; |
|||
import irbs.cust.rating.jpa.repository.FinanceReportDetailRepository; |
|||
import irbs.cust.rating.jpa.vo.FinanceReportDetailVo; |
|||
import irbs.cust.rating.jpa.vo.FinanceReportDetailsVo; |
|||
import irbs.cust.rating.jpa.vo.FinanceReportVo; |
|||
import irbs.cust.rating.service.FinanceReportDetailService; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import java.util.List; |
|||
|
|||
@RestController |
|||
@RequestMapping("/api/irbs/financeReportDetail") |
|||
public class FinanceReportDetailController extends RestCrudController<FinanceReportDetailVo, FinanceReportDetail, String, FinanceReportDetailRepository, FinanceReportDetailService> { |
|||
|
|||
/** |
|||
* 根据客户ID获取最新多期财报(企业类获取2期,事业类获取3期) |
|||
* @param custId |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
@GetMapping("query/{custId}") |
|||
@ResponseBody |
|||
public List<FinanceReportDetailsVo> queryDetail(@PathVariable(name="custId") String custId, QueryParameter queryParameter) throws Exception { |
|||
return service.queryDetail(custId, queryParameter); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,99 @@ |
|||
package irbs.cust.rating.jpa.vo; |
|||
|
|||
import io.sc.platform.orm.api.vo.BaseVo; |
|||
|
|||
import java.math.BigDecimal; |
|||
|
|||
/** |
|||
* 多期财报科目详情Vo |
|||
*/ |
|||
public class FinanceReportDetailsVo extends BaseVo { |
|||
|
|||
private String id; |
|||
|
|||
/** |
|||
* 科目编码 |
|||
*/ |
|||
private String code; |
|||
|
|||
/** |
|||
* 科目名称 |
|||
*/ |
|||
private String name; |
|||
|
|||
/** |
|||
* 科目本期值 |
|||
*/ |
|||
private BigDecimal value; |
|||
|
|||
/** |
|||
* 科目上期值 |
|||
*/ |
|||
private BigDecimal previousPeriodvalue; |
|||
|
|||
/** |
|||
* 科目上2期值 |
|||
*/ |
|||
private BigDecimal previousPeriod2value; |
|||
|
|||
/** |
|||
* 科目上3期值 |
|||
*/ |
|||
private BigDecimal previousPeriod3value; |
|||
|
|||
public String getId() { |
|||
return id; |
|||
} |
|||
|
|||
public void setId(String id) { |
|||
this.id = id; |
|||
} |
|||
|
|||
public String getCode() { |
|||
return code; |
|||
} |
|||
|
|||
public void setCode(String code) { |
|||
this.code = code; |
|||
} |
|||
|
|||
public String getName() { |
|||
return name; |
|||
} |
|||
|
|||
public void setName(String name) { |
|||
this.name = name; |
|||
} |
|||
|
|||
public BigDecimal getValue() { |
|||
return value; |
|||
} |
|||
|
|||
public void setValue(BigDecimal value) { |
|||
this.value = value; |
|||
} |
|||
|
|||
public BigDecimal getPreviousPeriodvalue() { |
|||
return previousPeriodvalue; |
|||
} |
|||
|
|||
public void setPreviousPeriodvalue(BigDecimal previousPeriodvalue) { |
|||
this.previousPeriodvalue = previousPeriodvalue; |
|||
} |
|||
|
|||
public BigDecimal getPreviousPeriod2value() { |
|||
return previousPeriod2value; |
|||
} |
|||
|
|||
public void setPreviousPeriod2value(BigDecimal previousPeriod2value) { |
|||
this.previousPeriod2value = previousPeriod2value; |
|||
} |
|||
|
|||
public BigDecimal getPreviousPeriod3value() { |
|||
return previousPeriod3value; |
|||
} |
|||
|
|||
public void setPreviousPeriod3value(BigDecimal previousPeriod3value) { |
|||
this.previousPeriod3value = previousPeriod3value; |
|||
} |
|||
} |
@ -1,9 +1,14 @@ |
|||
package irbs.cust.rating.service; |
|||
|
|||
import io.sc.platform.orm.service.DaoService; |
|||
import io.sc.platform.orm.service.support.QueryParameter; |
|||
import irbs.cust.rating.jpa.entity.FinanceReportDetail; |
|||
import irbs.cust.rating.jpa.repository.FinanceReportDetailRepository; |
|||
import irbs.cust.rating.jpa.vo.FinanceReportDetailsVo; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface FinanceReportDetailService extends DaoService<FinanceReportDetail, String, FinanceReportDetailRepository> { |
|||
|
|||
public List<FinanceReportDetailsVo> queryDetail(String custId, QueryParameter queryParameter) throws Exception; |
|||
} |
|||
|
@ -1,13 +1,133 @@ |
|||
package irbs.cust.rating.service.impl; |
|||
|
|||
import io.sc.platform.lcdp.frontend.component.support.CriteriaHandler; |
|||
import io.sc.platform.orm.service.impl.DaoServiceImpl; |
|||
import io.sc.platform.orm.service.support.OperatorType; |
|||
import io.sc.platform.orm.service.support.QueryParameter; |
|||
import io.sc.platform.orm.service.support.criteria.Criteria; |
|||
import io.sc.platform.orm.service.support.criteria.impl.InSet; |
|||
import irbs.cust.rating.jpa.entity.FinanceReport; |
|||
import irbs.cust.rating.jpa.entity.FinanceReportDetail; |
|||
import irbs.cust.rating.jpa.entity.RatingCompanyCustomer; |
|||
import irbs.cust.rating.jpa.repository.FinanceReportDetailRepository; |
|||
import irbs.cust.rating.jpa.repository.FinanceReportRepository; |
|||
import irbs.cust.rating.jpa.vo.FinanceReportDetailsVo; |
|||
import irbs.cust.rating.service.FinanceReportDetailService; |
|||
import irbs.cust.rating.service.RatingCompanyCustomerService; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@Service("financeReportDetailService") |
|||
public class FinanceReportDetailServiceImpl extends DaoServiceImpl<FinanceReportDetail, String, FinanceReportDetailRepository> implements FinanceReportDetailService { |
|||
|
|||
@Autowired |
|||
private RatingCompanyCustomerService ratingCompanyCustomerService; |
|||
@Autowired |
|||
private FinanceReportRepository financeReportRepository; |
|||
|
|||
@Override |
|||
public List<FinanceReportDetailsVo> queryDetail(String custId, QueryParameter queryParameter) throws Exception { |
|||
List<String> reportIds = getReportIds(custId); |
|||
addCriteria(reportIds, queryParameter); |
|||
List<FinanceReportDetail> list = this.list(queryParameter); |
|||
List<FinanceReportDetailsVo> result = new ArrayList<>(); |
|||
resultHandler(reportIds, list, result); |
|||
return result; |
|||
} |
|||
|
|||
private void resultHandler(List<String> reportIds, List<FinanceReportDetail> list, List<FinanceReportDetailsVo> result) throws Exception { |
|||
if (list!=null && list.size()>0) { |
|||
Map<String, List<FinanceReportDetail>> map = list.stream().collect(Collectors.groupingBy(FinanceReportDetail::getReportId)); |
|||
//以本期值为基础
|
|||
List<FinanceReportDetail> currList = map.get(reportIds.get(0)); |
|||
// 上期值
|
|||
List<FinanceReportDetail> previousPeriodList = new ArrayList<>(); |
|||
if (reportIds.size() > 1 && map.containsKey(reportIds.get(1))) { |
|||
previousPeriodList = map.get(reportIds.get(1)); |
|||
} |
|||
// 上2期值
|
|||
List<FinanceReportDetail> previousPeriod2List = new ArrayList<>(); |
|||
if (reportIds.size() > 2 && map.containsKey(reportIds.get(2))) { |
|||
previousPeriod2List = map.get(reportIds.get(2)); |
|||
} |
|||
// 上3期值
|
|||
List<FinanceReportDetail> previousPeriod3List = new ArrayList<>(); |
|||
if (reportIds.size() > 3 && map.containsKey(reportIds.get(3))) { |
|||
previousPeriod3List = map.get(reportIds.get(3)); |
|||
} |
|||
list2ListVo(currList, result, previousPeriodList, previousPeriod2List, previousPeriod3List); |
|||
} |
|||
} |
|||
|
|||
private void list2ListVo( |
|||
List<FinanceReportDetail> list, |
|||
List<FinanceReportDetailsVo> listVo, |
|||
List<FinanceReportDetail> previousPeriodList, |
|||
List<FinanceReportDetail> previousPeriod2List, |
|||
List<FinanceReportDetail> previousPeriod3List |
|||
) { |
|||
for(FinanceReportDetail frd: list) { |
|||
FinanceReportDetailsVo vo = new FinanceReportDetailsVo(); |
|||
vo.setId(frd.getId()); |
|||
vo.setCode(frd.getProjectCode()); |
|||
vo.setName(frd.getProjectName()); |
|||
vo.setValue(frd.getProjectValue()); |
|||
if (previousPeriodList.size() > 0) { |
|||
FinanceReportDetail tmp = previousPeriodList.stream().filter(entity -> entity.getProjectCode().equals(frd.getProjectCode())).findFirst().get(); |
|||
vo.setPreviousPeriodvalue(tmp.getProjectValue()); |
|||
} |
|||
if (previousPeriod2List.size() > 0) { |
|||
FinanceReportDetail tmp = previousPeriod2List.stream().filter(entity -> entity.getProjectCode().equals(frd.getProjectCode())).findFirst().get(); |
|||
vo.setPreviousPeriod2value(tmp.getProjectValue()); |
|||
} |
|||
if (previousPeriod3List.size() > 0) { |
|||
FinanceReportDetail tmp = previousPeriod3List.stream().filter(entity -> entity.getProjectCode().equals(frd.getProjectCode())).findFirst().get(); |
|||
vo.setPreviousPeriod3value(tmp.getProjectValue()); |
|||
} |
|||
listVo.add(vo); |
|||
} |
|||
} |
|||
|
|||
private void addCriteria(List<String> reportIds, QueryParameter queryParameter) throws Exception { |
|||
InSet inSet = new InSet(); |
|||
inSet.setFieldName("reportId"); |
|||
inSet.setOperator(OperatorType.inSet); |
|||
if (reportIds != null && reportIds.size() > 0) { |
|||
inSet.setValue((String[])reportIds.toArray(new String[0])); |
|||
} else { |
|||
inSet.setValue(new String[]{Long.toString(System.currentTimeMillis())}); |
|||
} |
|||
queryParameter.addCriteria(inSet); |
|||
} |
|||
|
|||
/** |
|||
* 根据客户ID,查询最新多期报表ID(企业类3期,其他4期) |
|||
* @param custId |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
private List<String> getReportIds(String custId) throws Exception { |
|||
RatingCompanyCustomer customer = ratingCompanyCustomerService.findById(custId); |
|||
Integer periodNum = 3; |
|||
if (!customer.getCustomerType().equals("1")) { |
|||
periodNum = 4; |
|||
} |
|||
List<FinanceReport> reports = financeReportRepository.findByCustIdOrderByEndDateDesc(custId); |
|||
List<String> reportIds = new ArrayList<>(); |
|||
if (reports!=null && reports.size() > 0) { |
|||
if (reports.size() <= periodNum) { |
|||
reportIds = reports.stream().map(FinanceReport::getId).collect(Collectors.toList()); |
|||
} else { |
|||
for(int i=0;i<periodNum-1;i++) { |
|||
reportIds.add(reports.get(i).getId()); |
|||
} |
|||
} |
|||
} |
|||
return reportIds; |
|||
} |
|||
} |
|||
|
@ -0,0 +1,36 @@ |
|||
<template> |
|||
<q-dialog v-model="alert" no-esc-dismiss no-backdrop-dismiss> |
|||
<q-card style="width: 300px; height: 200px"> |
|||
<q-card-section class="w-full h-full"> |
|||
<div style="display: flex; justify-content: center; align-items: center; height: 100%; flex-direction: column"> |
|||
<div> |
|||
<q-spinner-gears color="primary" size="5.5em" /> |
|||
</div> |
|||
<div> |
|||
{{ msg }} |
|||
</div> |
|||
</div> |
|||
</q-card-section> |
|||
</q-card> |
|||
</q-dialog> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
|
|||
const alert = ref(false); |
|||
const msg = ref(''); |
|||
|
|||
const show = (msg_: string = '正在处理,请稍等...') => { |
|||
msg.value = msg_; |
|||
alert.value = true; |
|||
}; |
|||
const hide = () => { |
|||
alert.value = false; |
|||
}; |
|||
|
|||
defineExpose({ |
|||
show, |
|||
hide, |
|||
}); |
|||
</script> |
@ -0,0 +1,61 @@ |
|||
<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> |
@ -0,0 +1,69 @@ |
|||
<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> |
@ -0,0 +1,13 @@ |
|||
<template> |
|||
<div style="height: 100%"> |
|||
<RatingManager :show-rating-dialog="true" :cust-no="custNo"></RatingManager> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import RatingManager from './RatingManager.vue'; |
|||
import { useRoute } from 'vue-router'; |
|||
|
|||
const route = useRoute(); |
|||
const custNo = route?.query?.custNo; |
|||
</script> |
@ -0,0 +1,37 @@ |
|||
<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> |
@ -0,0 +1,54 @@ |
|||
<template> |
|||
<w-dialog |
|||
ref="dialogRef" |
|||
:title="state.dialogTitle" |
|||
width="80%" |
|||
height="80%" |
|||
:buttons="[ |
|||
{ |
|||
label: '确定', |
|||
icon: 'beenhere', |
|||
click: () => { |
|||
submit(); |
|||
}, |
|||
}, |
|||
]" |
|||
> |
|||
<CustSelectGrid ref="customerGridRef"></CustSelectGrid> |
|||
</w-dialog> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { NotifyManager } from 'platform-core'; |
|||
import { reactive, ref } from 'vue'; |
|||
import CustSelectGrid from './CustSelectGrid.vue'; |
|||
|
|||
const dialogRef = ref(); |
|||
const customerGridRef = ref(); |
|||
const emit = defineEmits(['refresh', 'submitHandler']); |
|||
|
|||
const state = reactive({ |
|||
dialogTitle: '选择客户', |
|||
}); |
|||
|
|||
const submit = () => { |
|||
const rows = customerGridRef.value.getRef().getSelectedRows(); |
|||
if (rows.length === 0) { |
|||
NotifyManager.warn('请选择客户'); |
|||
return; |
|||
} |
|||
emit('submitHandler', rows[0]); |
|||
}; |
|||
|
|||
const show = () => { |
|||
dialogRef.value.show(); |
|||
}; |
|||
const hide = () => { |
|||
dialogRef.value.hide(); |
|||
}; |
|||
|
|||
defineExpose({ |
|||
show, |
|||
hide, |
|||
}); |
|||
</script> |
@ -0,0 +1,57 @@ |
|||
<template> |
|||
<div class="h-full"> |
|||
<w-grid |
|||
ref="customerGridRef" |
|||
title="客户列表" |
|||
:fetch-data-url="Environment.apiContextPath('api/irbs/companyCustomer/query')" |
|||
:sort-no="true" |
|||
:checkbox-selection="false" |
|||
:config-button="false" |
|||
:toolbar-actions="props.toolbarActions" |
|||
:query-criteria="{ |
|||
fieldName: 'mgerNo', |
|||
operator: 'equals', |
|||
value: SessionManager.getUser().loginName, |
|||
}" |
|||
:sort-by="['-lastModifyDate']" |
|||
:query-form-cols-num="3" |
|||
:query-form-fields="[ |
|||
{ label: '客户号', name: 'custNo', type: 'w-text' }, |
|||
{ label: '客户名称', name: 'custName', type: 'w-text' }, |
|||
{ label: '企业规模', name: 'corpSizeCd', type: 'w-select', options: Options.dictionary(dictionaryArr['CustomerSizeCd']) }, |
|||
]" |
|||
:columns="[ |
|||
{ name: 'custNo', label: '客户号' }, |
|||
{ name: 'custName', label: '客户名称' }, |
|||
{ name: 'induSortName', label: '行业类型' }, |
|||
{ name: 'custTypeCd', label: '客户类型', format: Formater.dictionary(dictionaryArr['CustomerTypeCd']) }, |
|||
{ name: 'buildDt', label: '成立时间' }, |
|||
{ name: 'corpSizeCd', label: '企业规模', format: Formater.dictionary(dictionaryArr['CustomerSizeCd']) }, |
|||
]" |
|||
></w-grid> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { DictionaryTools, Environment, Formater, Options, SessionManager } from 'platform-core'; |
|||
import { ref } from 'vue'; |
|||
|
|||
const customerGridRef = ref(); |
|||
const props = defineProps({ |
|||
toolbarActions: { |
|||
type: Array, |
|||
default: () => { |
|||
return ['query', 'reset']; |
|||
}, |
|||
}, |
|||
}); |
|||
const getRef = () => { |
|||
return customerGridRef.value; |
|||
}; |
|||
|
|||
defineExpose({ |
|||
getRef, |
|||
}); |
|||
|
|||
const dictionaryArr = await DictionaryTools.fetch(['CustomerSizeCd', 'CustomerTypeCd']); |
|||
</script> |
@ -0,0 +1,21 @@ |
|||
<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> |
@ -0,0 +1,93 @@ |
|||
<template> |
|||
<w-dialog ref="dialogRef" width="95%" height="95%" body-padding="0px 0px 0px 0px" @hide="hideEvent"> |
|||
<template #title> |
|||
<div> |
|||
<span class="text-xl">客户评级:</span> |
|||
<span class="text-3xl"> |
|||
{{ custInfoData['custName'] }} |
|||
<q-badge color="orange" align="top">客户号:{{ custInfoData['custNo'] }}</q-badge |
|||
> <q-badge color="green" align="top">上市</q-badge> |
|||
<q-badge color="red" align="top">{{ Formater.dictionary(dictionary.customerSize)(custInfoData['customerSize']) }}</q-badge |
|||
> <q-badge align="top">{{ Formater.dictionary(dictionary.registeredType)(custInfoData['registeredType']) }}</q-badge |
|||
> <q-badge color="secondary" align="top">{{ custInfoData['groupCustInd'] === '1' ? '集团客户' : '非集团客户' }}</q-badge |
|||
> |
|||
<q-badge v-if="custInfoData['groupCustInd'] === '1'" align="top" |
|||
>集团成员类别:{{ Formater.dictionary(dictionary.memberTypeCd)(custInfoData['memberType']) }}</q-badge |
|||
><template v-if="custInfoData['groupCustInd'] === '1'"> </template> |
|||
<q-badge color="blue" align="top">行业:{{ custInfoData['industryTypeName'] }}</q-badge |
|||
> <q-badge color="brown" align="top">成立日期:{{ custInfoData['buildDate'] }}</q-badge |
|||
> |
|||
</span> |
|||
</div> |
|||
</template> |
|||
<RatingPage :rating-data="props.ratingData" :read-mode="props.readMode" @after-complete="afterComplete"></RatingPage> |
|||
</w-dialog> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref, computed, nextTick } from 'vue'; |
|||
import { axios, Environment, Formater } from 'platform-core'; |
|||
import RatingPage from './RatingPage.vue'; |
|||
import { Dictionary } from './ts/Dictionary'; |
|||
|
|||
const dialogRef = ref(); |
|||
const custInfoData = ref({}); |
|||
const dictionary = new Dictionary(); |
|||
|
|||
const props = defineProps({ |
|||
ratingData: { |
|||
type: Object, |
|||
default: () => { |
|||
return {}; |
|||
}, |
|||
}, |
|||
readMode: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
}); |
|||
const emit = defineEmits(['refresh']); |
|||
|
|||
const titleComputed = computed(() => { |
|||
return `公司客户评级 客户:` + props.ratingData['custName'] + ` 评级模型:` + (props.ratingData['modelName'] || ''); |
|||
}); |
|||
|
|||
/** |
|||
* 显示评级窗口 |
|||
*/ |
|||
const show = () => { |
|||
dialogRef.value.show(); |
|||
nextTick(() => { |
|||
const custId = props.ratingData['custId']; |
|||
axios.get(Environment.apiContextPath('api/irbs/ratingCompanyCustomer/' + custId)).then((resp) => { |
|||
if (resp && resp.data) { |
|||
custInfoData.value = resp.data; |
|||
} |
|||
}); |
|||
}); |
|||
}; |
|||
/** |
|||
* 隐藏评级窗口 |
|||
*/ |
|||
const hide = () => { |
|||
dialogRef.value.hide(); |
|||
}; |
|||
/** |
|||
* 隐藏窗口事件 |
|||
*/ |
|||
const hideEvent = () => { |
|||
afterComplete(); |
|||
}; |
|||
|
|||
const afterComplete = () => { |
|||
// 完成后关闭窗口,刷新表格 |
|||
hide(); |
|||
emit('refresh'); |
|||
}; |
|||
|
|||
defineExpose({ |
|||
show, |
|||
hide, |
|||
}); |
|||
|
|||
await dictionary.load(); |
|||
</script> |
@ -0,0 +1,9 @@ |
|||
<template> |
|||
<div style="height: 100%"> |
|||
<RatingManager :show-rating-dialog="false"></RatingManager> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import RatingManager from './RatingManager.vue'; |
|||
</script> |
@ -0,0 +1,287 @@ |
|||
<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" |
|||
:toolbar-actions="[ |
|||
'query', |
|||
'reset', |
|||
'separator', |
|||
{ |
|||
extend: 'add', |
|||
label: '发起评级', |
|||
click: () => { |
|||
custSelectDialogRef.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) => { |
|||
ratingData = args.selected; |
|||
readMode = false; |
|||
ratingDialogRef.show(); |
|||
}, |
|||
}, |
|||
// { |
|||
// extend: 'remove', |
|||
// label: '撤销', |
|||
// click: () => {}, |
|||
// }, |
|||
'separator', |
|||
{ |
|||
extend: 'view', |
|||
click: (args) => { |
|||
ratingData = args.selected; |
|||
readMode = true; |
|||
ratingDialogRef.show(); |
|||
}, |
|||
}, |
|||
'export', |
|||
'separator', |
|||
[ |
|||
{ |
|||
name: 'otherOp', |
|||
label: '其他操作', |
|||
icon: 'device_hub', |
|||
}, |
|||
{ |
|||
name: 'viewGray', |
|||
extend: 'view', |
|||
label: '查看灰度模型结果', |
|||
click: (args) => { |
|||
grayDialogRef.show(args.selected); |
|||
}, |
|||
}, |
|||
'separator', |
|||
{ |
|||
extend: 'remove', |
|||
name: 'removeRatingAndProcess', |
|||
label: '强制删除', |
|||
click: (args: any) => { |
|||
removeRatingAndProcess(args); |
|||
}, |
|||
}, |
|||
], |
|||
'separator', |
|||
]" |
|||
:query-criteria="{ |
|||
operator: 'and', |
|||
criteria: [ |
|||
{ |
|||
fieldName: 'triggerType', |
|||
operator: 'equals', |
|||
value: 'INDEPENDENT', |
|||
}, |
|||
{ |
|||
fieldName: 'launchUser', |
|||
operator: 'equals', |
|||
value: SessionManager.getUser().loginName, |
|||
}, |
|||
], |
|||
}" |
|||
:sort-by="['-createDate']" |
|||
:query-form-fields="[ |
|||
{ 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: '当前处理人' }, |
|||
]" |
|||
></w-grid> |
|||
<CustSelectDialog ref="custSelectDialogRef" @refresh="refreshTable" @submit-handler="selectCustSubmit"></CustSelectDialog> |
|||
<RatingDialog ref="ratingDialogRef" :rating-data="ratingData" :read-mode="readMode" @refresh="refreshTable"></RatingDialog> |
|||
<GrayModelDialog ref="grayDialogRef" @refresh="refreshTable"></GrayModelDialog> |
|||
<LoadingDialog ref="loadingDialogRef"></LoadingDialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, nextTick, onMounted } from 'vue'; |
|||
import { useQuasar } from 'quasar'; |
|||
import { axios, EnumTools, Environment, Formater, noErrorAxios, NotifyManager, Options, SessionManager, Tools, ServerExceptionHandler } from 'platform-core'; |
|||
import { RatingLevelOptions, RatingProcessStatus, Round } from '../CustRating.ts'; |
|||
import GrayModelDialog from '../GrayModelDialog.vue'; |
|||
import RatingDialog from './RatingDialog.vue'; |
|||
import RatingLevel from '../RatingLevel.vue'; |
|||
import CustSelectDialog from './CustSelectDialog.vue'; |
|||
import LoadingDialog from '@/views/components/LoadingDialog.vue'; |
|||
|
|||
const $q = useQuasar(); |
|||
const companyRatingGridRef = ref(); |
|||
const custSelectDialogRef = ref(); |
|||
const loadingDialogRef = ref(); |
|||
const ratingDialogRef = ref(); |
|||
const grayDialogRef = ref(); |
|||
const ratingData = ref({}); |
|||
const readMode = ref(false); |
|||
|
|||
const enums = await EnumTools.fetch(['irbs.cust.rating.enums.RatingStatus', 'irbs.cust.rating.enums.RatingProcessStatus']); |
|||
const RatingStatusEnum = enums['RatingStatus']; |
|||
const RatingProcessStatusEnum = enums['RatingProcessStatus']; |
|||
|
|||
const props = defineProps({ |
|||
// 直接显示评级窗口(对于其他系统点击评级跳转过来的请求,页面完成后直接打开评级窗口) |
|||
showRatingDialog: { type: Boolean, default: false }, |
|||
// 客户号 |
|||
custNo: { type: String, default: undefined }, |
|||
}); |
|||
|
|||
const removeRatingAndProcess = (args: any) => { |
|||
$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('删除失败'); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const showLoading = (msg: string = '正在处理,请稍等...') => { |
|||
loadingDialogRef.value.show(msg); |
|||
}; |
|||
const hideLoading = () => { |
|||
loadingDialogRef.value.hide(); |
|||
}; |
|||
|
|||
/** |
|||
* 选择客户提交处理方法 |
|||
* @param data 选择的客户数据 |
|||
*/ |
|||
const selectCustSubmit = async (data: any) => { |
|||
// 判断客户是否已达当月评级次数上限 |
|||
const monthLimit = await axios.post(Environment.apiContextPath('api/irbs/companyRating/getMonthRatingLimit/' + data['custNo'])); |
|||
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(data); |
|||
}); |
|||
} else { |
|||
generateRating(data); |
|||
} |
|||
}; |
|||
|
|||
// 生成评级记录及流程 |
|||
const generateRating = (data: any) => { |
|||
showLoading(); |
|||
const requestParams = { |
|||
method: 'POST', |
|||
headers: { 'content-type': 'application/json;charset=utf-8;' }, |
|||
url: Environment.apiContextPath('api/irbs/companyRating/generateRating/' + data['custNo']), |
|||
}; |
|||
noErrorAxios(requestParams) |
|||
.then((resp: any) => { |
|||
hideLoading(); |
|||
if (resp?.code === 200) { |
|||
refreshTable(); |
|||
custSelectDialogRef.value.hide(); |
|||
// 成功发起以后直接打开相关窗口进入评级页面 |
|||
if (resp.data) { |
|||
ratingData.value = resp.data; |
|||
readMode.value = false; |
|||
ratingDialogRef.value.show(); |
|||
} |
|||
} |
|||
}) |
|||
.catch((error) => { |
|||
hideLoading(); |
|||
if (error?.code === 1001) { |
|||
// 验证错误 |
|||
NotifyManager.error('验证错误'); |
|||
} else { |
|||
console.info('error=========', error); |
|||
ServerExceptionHandler.handle(error); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
const refreshTable = () => { |
|||
companyRatingGridRef.value.refresh(); |
|||
}; |
|||
|
|||
onMounted(async () => { |
|||
if (props.showRatingDialog && props.custNo) { |
|||
await selectCustSubmit({ custNo: props.custNo }); |
|||
} |
|||
}); |
|||
</script> |
@ -0,0 +1,71 @@ |
|||
<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> |
@ -0,0 +1,58 @@ |
|||
<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> |
@ -0,0 +1,73 @@ |
|||
<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> |
@ -0,0 +1,48 @@ |
|||
<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> |
@ -0,0 +1,18 @@ |
|||
<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> |
@ -0,0 +1,68 @@ |
|||
<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> |
@ -0,0 +1,36 @@ |
|||
<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> |
@ -0,0 +1,38 @@ |
|||
<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> |
@ -0,0 +1,29 @@ |
|||
<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> |
@ -0,0 +1,61 @@ |
|||
<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> |
@ -0,0 +1,46 @@ |
|||
<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> |
@ -0,0 +1,45 @@ |
|||
<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> |
@ -0,0 +1,49 @@ |
|||
<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> |
@ -0,0 +1,74 @@ |
|||
<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> |
@ -0,0 +1,146 @@ |
|||
<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> |
@ -0,0 +1,39 @@ |
|||
<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> |
@ -0,0 +1,29 @@ |
|||
<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> |
@ -0,0 +1,51 @@ |
|||
<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> |
@ -0,0 +1,33 @@ |
|||
<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> |
@ -0,0 +1,40 @@ |
|||
<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> |
@ -0,0 +1,44 @@ |
|||
<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> |
@ -0,0 +1,77 @@ |
|||
<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> |
@ -0,0 +1,106 @@ |
|||
<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> |
@ -0,0 +1,51 @@ |
|||
<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> |
@ -0,0 +1,20 @@ |
|||
<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> |
@ -0,0 +1,36 @@ |
|||
<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> |
@ -0,0 +1,34 @@ |
|||
<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> |
@ -0,0 +1,157 @@ |
|||
<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> |
@ -0,0 +1,82 @@ |
|||
<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> |
@ -0,0 +1,37 @@ |
|||
import { computed } from 'vue'; |
|||
import { Rating } from './Rating'; |
|||
import { Step } from './Step'; |
|||
import { Constant } from './Constant'; |
|||
|
|||
/** |
|||
* 计算属性管理器 |
|||
*/ |
|||
export class ComputedManager { |
|||
rating: Rating; |
|||
|
|||
constructor(rating_: Rating) { |
|||
this.rating = rating_; |
|||
} |
|||
|
|||
/** |
|||
* 当前是否为发起人可以办理的流程状态 |
|||
*/ |
|||
isApplyUserProcessStatus = computed(() => { |
|||
const processStatus = this.rating.ratingData.value['processStatus']; |
|||
const showProcessStatus = [Constant.RATING_PROCESS_STATUS.AWAIT_SUBMIT, Constant.RATING_PROCESS_STATUS.BACK]; |
|||
if (showProcessStatus.includes(processStatus)) { |
|||
return true; |
|||
} |
|||
return false; |
|||
}); |
|||
|
|||
/** |
|||
* 当前是否为申请人发起流程待提交状态 |
|||
*/ |
|||
isAwaitSubmitProcessStatus = computed(() => { |
|||
if (this.rating.ratingData.value['processStatus'] === Constant.RATING_PROCESS_STATUS.AWAIT_SUBMIT) { |
|||
return true; |
|||
} |
|||
return false; |
|||
}); |
|||
} |
@ -0,0 +1,76 @@ |
|||
class RatingProcessStatus { |
|||
/** |
|||
* 待评级 |
|||
*/ |
|||
static AWAIT_RATING = 'AWAIT_RATING'; |
|||
/** |
|||
* 待提交 |
|||
*/ |
|||
static AWAIT_SUBMIT = 'AWAIT_SUBMIT'; |
|||
/** |
|||
* 退回 |
|||
*/ |
|||
static BACK = 'BACK'; |
|||
/** |
|||
* 审批中 |
|||
*/ |
|||
static APPROVALING = 'APPROVALING'; |
|||
/** |
|||
* 通过 |
|||
*/ |
|||
static PASS = 'PASS'; |
|||
/** |
|||
* 否决 |
|||
*/ |
|||
static NEGATIVED = 'NEGATIVED'; |
|||
/** |
|||
* 已结束 |
|||
*/ |
|||
static END = 'END'; |
|||
} |
|||
|
|||
class PageType { |
|||
static APPLY = 'apply'; |
|||
static AUDIT = 'audit'; |
|||
static DETAIL = 'detail'; |
|||
} |
|||
|
|||
class QualSaveOperator { |
|||
/** |
|||
* 试算 |
|||
*/ |
|||
static TEST = 'test'; |
|||
/** |
|||
* 正常点击下一步 |
|||
*/ |
|||
static NEXT = 'next'; |
|||
} |
|||
|
|||
class DefaultProcessStatus { |
|||
/** |
|||
* 通过 |
|||
*/ |
|||
static PASS = '03'; |
|||
} |
|||
|
|||
/** |
|||
* 评级功能常量类 |
|||
*/ |
|||
export class Constant { |
|||
/** |
|||
* 评级流程状态 |
|||
*/ |
|||
static RATING_PROCESS_STATUS = RatingProcessStatus; |
|||
/** |
|||
* 页面类型 |
|||
*/ |
|||
static PAGE_TYPE = PageType; |
|||
/** |
|||
* 定性选项保存操作 |
|||
*/ |
|||
static QUAL_SAVE_OPERATOR = QualSaveOperator; |
|||
/** |
|||
* 违约认定流程状态 |
|||
*/ |
|||
static DEFAULT_PROCESS_STATUS = DefaultProcessStatus; |
|||
} |
@ -0,0 +1,90 @@ |
|||
import { DictionaryTools } from 'platform-core'; |
|||
|
|||
/** |
|||
* 评级使用到的数据字典 |
|||
*/ |
|||
export class Dictionary { |
|||
/** |
|||
* 推翻类型 |
|||
*/ |
|||
overturnType: any; |
|||
/** |
|||
* 企业规模 |
|||
*/ |
|||
customerSize: any; |
|||
/** |
|||
* 企业类型 |
|||
*/ |
|||
registeredType: any; |
|||
/** |
|||
* 融资平台类型 |
|||
*/ |
|||
goverFinanceType: any; |
|||
/** |
|||
* 融资平台标志 |
|||
*/ |
|||
goverFinanceSign: any; |
|||
/** |
|||
* 注册所在地 |
|||
*/ |
|||
registrationCd: any; |
|||
/** |
|||
* 所在国家地区 |
|||
*/ |
|||
nationCd: any; |
|||
/** |
|||
* 成员类别 |
|||
*/ |
|||
memberTypeCd: any; |
|||
/** |
|||
* 报表类型 |
|||
*/ |
|||
financeTypeCd: any; |
|||
/** |
|||
* 报表类别 |
|||
*/ |
|||
financeSortTypeCd: any; |
|||
/** |
|||
* 是否审计 |
|||
*/ |
|||
financeStatusCd: any; |
|||
/** |
|||
* 报表口径 |
|||
*/ |
|||
caliberCd: any; |
|||
/** |
|||
* 报表币种 |
|||
*/ |
|||
currencyTypeCd: any; |
|||
|
|||
async load() { |
|||
const arr = await DictionaryTools.fetch([ |
|||
'OVERTURN_TYPE', |
|||
'CustomerSizeCd', |
|||
'REGISTERED_TYPE', |
|||
'GOVER_FINANCE_TYPE', |
|||
'GOVER_FINANCE_SIGN', |
|||
'REGISTRATION_CD', |
|||
'NATION_CD', |
|||
'MEMBER_TYPE_CD', |
|||
'FinanceTypeCd', |
|||
'FinanceSortTypeCd', |
|||
'FinanceStatusCd', |
|||
'CaliberCd', |
|||
'CurrencyTypeCd', |
|||
]); |
|||
this.overturnType = arr['OVERTURN_TYPE']; |
|||
this.customerSize = arr['CustomerSizeCd']; |
|||
this.registeredType = arr['REGISTERED_TYPE']; |
|||
this.goverFinanceType = arr['GOVER_FINANCE_TYPE']; |
|||
this.goverFinanceSign = arr['GOVER_FINANCE_SIGN']; |
|||
this.registrationCd = arr['REGISTRATION_CD']; |
|||
this.nationCd = arr['NATION_CD']; |
|||
this.memberTypeCd = arr['MEMBER_TYPE_CD']; |
|||
this.financeTypeCd = arr['FinanceTypeCd']; |
|||
this.financeSortTypeCd = arr['FinanceSortTypeCd']; |
|||
this.financeStatusCd = arr['FinanceStatusCd']; |
|||
this.caliberCd = arr['CaliberCd']; |
|||
this.currencyTypeCd = arr['CurrencyTypeCd']; |
|||
} |
|||
} |
@ -0,0 +1,21 @@ |
|||
import { EnumTools } from 'platform-core'; |
|||
|
|||
/** |
|||
* 评级中用到的枚举 |
|||
*/ |
|||
export class Enum { |
|||
/** |
|||
* 评级状态 |
|||
*/ |
|||
ratingStatus: any; |
|||
/** |
|||
* 评级流程状态 |
|||
*/ |
|||
ratingProcessStatus: any; |
|||
|
|||
async load() { |
|||
const arr = await EnumTools.fetch(['irbs.cust.rating.enums.RatingStatus', 'irbs.cust.rating.enums.RatingProcessStatus']); |
|||
this.ratingStatus = arr['RatingStatus']; |
|||
this.ratingProcessStatus = arr['RatingProcessStatus']; |
|||
} |
|||
} |
@ -0,0 +1,79 @@ |
|||
import { Ref, ref, computed } from 'vue'; |
|||
|
|||
type DetailType = { |
|||
label: string; |
|||
value: string; |
|||
icon: string; |
|||
order: number; |
|||
}; |
|||
|
|||
/** |
|||
* 常量 |
|||
*/ |
|||
export class Constant { |
|||
/** |
|||
* 报表类型-企业类 |
|||
*/ |
|||
static REPORT_TYPE_COMPANY = '1'; |
|||
/** |
|||
* 报表类型-事业类 |
|||
*/ |
|||
static REPORT_TYPE_PUBLIC_INSTITUTION = '2'; |
|||
} |
|||
|
|||
export class FinanceReport { |
|||
/** |
|||
* 报表详情 |
|||
*/ |
|||
static reportDetail = { |
|||
/** |
|||
* 企业类资产负债表 |
|||
*/ |
|||
c_balance_sheet: <DetailType>{ label: '企业类资产负债表', value: '01', icon: 'currency_yen', order: 2 }, |
|||
/** |
|||
* 企业类现金流表 |
|||
*/ |
|||
c_statement_of_cash_flows: <DetailType>{ label: '企业类现金流表', value: '02', icon: 'money', order: 3 }, |
|||
/** |
|||
* 企业类损益表 |
|||
*/ |
|||
c_income_statement: <DetailType>{ label: '企业类损益表', value: '03', icon: 'remove_circle_outline', order: 4 }, |
|||
/** |
|||
* 事业类资产负债表 |
|||
*/ |
|||
p_balance_sheet: <DetailType>{ label: '事业类资产负债表', value: '11', icon: 'currency_yen', order: 5 }, |
|||
/** |
|||
* 事业类收入支出表 |
|||
*/ |
|||
p_income_statement: <DetailType>{ label: '事业类资产负债表', value: '12', icon: 'price_change', order: 6 }, |
|||
}; |
|||
|
|||
/** |
|||
* 当前报表类型 |
|||
*/ |
|||
reportType: Ref = ref(Constant.REPORT_TYPE_COMPANY); |
|||
/** |
|||
* 当前查看的财报ID |
|||
*/ |
|||
reportId: string = ''; |
|||
/** |
|||
* 基本信息 |
|||
*/ |
|||
otherInfo = <any>[]; |
|||
|
|||
/** |
|||
* tabs计算属性 |
|||
*/ |
|||
tabsComputed = computed(() => { |
|||
const result = <any>[]; |
|||
if (this.reportType.value === Constant.REPORT_TYPE_COMPANY) { |
|||
result.push(FinanceReport.reportDetail.c_balance_sheet); |
|||
result.push(FinanceReport.reportDetail.c_statement_of_cash_flows); |
|||
result.push(FinanceReport.reportDetail.c_income_statement); |
|||
} else { |
|||
result.push(FinanceReport.reportDetail.p_balance_sheet); |
|||
result.push(FinanceReport.reportDetail.p_income_statement); |
|||
} |
|||
return result; |
|||
}); |
|||
} |
@ -0,0 +1,31 @@ |
|||
type HistGridType = { |
|||
label: string; |
|||
value: string; |
|||
icon: string; |
|||
order: number; |
|||
}; |
|||
|
|||
export class HistGird { |
|||
/** |
|||
* 历史过往记录类型 |
|||
*/ |
|||
static type = { |
|||
/** |
|||
* 客户过往评级记录 |
|||
*/ |
|||
histRating: <HistGridType>{ label: '客户历史评级', value: 'histRating', icon: 'hotel_class', order: 1 }, |
|||
/** |
|||
* 违约认定情况 |
|||
*/ |
|||
defaultCognizance: <HistGridType>{ label: '违约认定历史', value: 'defaultCognizance', icon: 'pan_tool_alt', order: 2 }, |
|||
/** |
|||
* 违约重生情况 |
|||
*/ |
|||
defaultRebirth: <HistGridType>{ label: '违约重生历史', value: 'defaultRebirth', icon: 'auto_mode', order: 3 }, |
|||
}; |
|||
|
|||
/** |
|||
* tabs |
|||
*/ |
|||
static tabs = Object.values(HistGird.type).sort((a, b) => a.order - b.order); |
|||
} |
@ -0,0 +1,179 @@ |
|||
import { Ref, ref } from 'vue'; |
|||
import { Loading } from 'quasar'; |
|||
import { Step } from './Step'; |
|||
import { Dictionary } from './Dictionary'; |
|||
import { Enum } from './Enum'; |
|||
import { Refs } from './Refs'; |
|||
import { RequestApi } from './RequestApi'; |
|||
import { SystemParameter } from './SystemParameter'; |
|||
import { ComputedManager } from './ComputedManager'; |
|||
|
|||
/** |
|||
* 评级类 |
|||
*/ |
|||
export class Rating { |
|||
/** |
|||
* 评级页面自身实例 |
|||
*/ |
|||
instance: any; |
|||
/** |
|||
* 当前页面是否为查看模式 |
|||
*/ |
|||
readMode: boolean = false; |
|||
/** |
|||
* 评级页面整体是否使用紧凑布局 |
|||
*/ |
|||
dense: boolean = true; |
|||
/** |
|||
* 评级页面切分线颜色 |
|||
*/ |
|||
separatorColor: string = 'orange'; |
|||
/** |
|||
* 重要的显示评级等级的大小 |
|||
*/ |
|||
mainLevelSize: string = '60px'; |
|||
/** |
|||
* 评级页面中表格通用高度 |
|||
*/ |
|||
gridHeight: number = 200; |
|||
/** |
|||
* 评级页面用到的数据字典 |
|||
*/ |
|||
dictionary: Dictionary; |
|||
/** |
|||
* 评级页面用到的枚举 |
|||
*/ |
|||
enum: Enum; |
|||
/** |
|||
* 评级步骤 |
|||
*/ |
|||
step: Step; |
|||
/** |
|||
* 计算属性管理器 |
|||
*/ |
|||
cm: ComputedManager; |
|||
/** |
|||
* 当前评级数据 |
|||
*/ |
|||
ratingData: Ref = ref({}); |
|||
/** |
|||
* 获取页面组件ref对象的函数管理器 |
|||
*/ |
|||
refs: Refs; |
|||
/** |
|||
* 请求后台API |
|||
*/ |
|||
api: RequestApi; |
|||
/** |
|||
* 系统配置参数 |
|||
*/ |
|||
systemParameter: SystemParameter; |
|||
/** |
|||
* 客户信息数据 |
|||
*/ |
|||
custInfoData: Ref = ref({}); |
|||
/** |
|||
* 定量得分数据 |
|||
*/ |
|||
quanData: Ref = ref([]); |
|||
/** |
|||
* 定量得分详情数据 |
|||
*/ |
|||
quanScoreDtlData: Ref = ref({}); |
|||
/** |
|||
* 定性选项表单数据 |
|||
*/ |
|||
qualFormData: Ref = ref([]); |
|||
/** |
|||
* 只包含值本身选项的表单数据 |
|||
*/ |
|||
qualSimpleOptionData: Ref = ref([]); |
|||
/** |
|||
* 定性选项表单对象 |
|||
*/ |
|||
qualFormObj: Ref = ref({}); |
|||
/** |
|||
* 定性得分数据 |
|||
*/ |
|||
qualData: Ref = ref([]); |
|||
/** |
|||
* 定性得分详情数据 |
|||
*/ |
|||
qualScoreDtlData: Ref = ref([]); |
|||
/** |
|||
* 初评结果 |
|||
*/ |
|||
firstRatingData: Ref = ref([]); |
|||
/** |
|||
* 评级调整型得分数据 |
|||
*/ |
|||
adjustItemScoreDtlData: Ref = ref([]); |
|||
/** |
|||
* 评级调整项指标详情 |
|||
*/ |
|||
adjustItemData: Ref = ref([]); |
|||
/** |
|||
* 评级调整项对象 |
|||
*/ |
|||
adjustItemObj: Ref = ref({}); |
|||
/** |
|||
* 报告页面客户评级主要信息 |
|||
*/ |
|||
reportRatingMainData: Ref = ref([]); |
|||
/** |
|||
* 报告页面客户评级数据 |
|||
*/ |
|||
reportCustRatingData: Ref = ref([]); |
|||
/** |
|||
* 评级推翻提示数据 |
|||
*/ |
|||
overturnTipData: Ref = ref([]); |
|||
/** |
|||
* 签署意见数据 |
|||
*/ |
|||
opinionData: Ref = ref({}); |
|||
/** |
|||
* 财报信息 |
|||
*/ |
|||
financeReport: Ref = ref([]); |
|||
/** |
|||
* 待选择的字段集合(用于定性分析问答模式填报) |
|||
*/ |
|||
waitSelectFields: Ref = ref([]); |
|||
/** |
|||
* 报告页面显示所有评级信息模型值 |
|||
*/ |
|||
showAllRatingInfoModel: Ref = ref(false); |
|||
|
|||
/** |
|||
* 评级类构造函数 |
|||
* @param ratingData_ 评级数据 |
|||
* @param readMode_ 是否阅读模式 |
|||
*/ |
|||
constructor(ratingData_: any, readMode_: boolean) { |
|||
this.readMode = readMode_; |
|||
this.ratingData.value = ratingData_; |
|||
this.refs = new Refs(); |
|||
this.dictionary = new Dictionary(); |
|||
this.enum = new Enum(); |
|||
this.cm = new ComputedManager(this); |
|||
this.systemParameter = new SystemParameter(); |
|||
this.step = new Step(this); |
|||
this.api = new RequestApi(this); |
|||
} |
|||
|
|||
showLoading(msg: string = '正在获取数据,请稍等...') { |
|||
Loading.show({ |
|||
message: msg, |
|||
boxClass: 'bg-grey-2 text-grey-9', |
|||
spinnerColor: 'primary', |
|||
}); |
|||
} |
|||
hideLoading() { |
|||
Loading.hide(); |
|||
} |
|||
|
|||
setInstance(instance_: any) { |
|||
this.instance = instance_; |
|||
} |
|||
} |
@ -0,0 +1,52 @@ |
|||
export class Refs { |
|||
/** |
|||
* 获取财报列表ref对象函数 |
|||
*/ |
|||
financeReportGridRefFunction: any; |
|||
/** |
|||
* 获取征信报告列表ref对象函数 |
|||
*/ |
|||
creditReportGridRefFunction: any; |
|||
/** |
|||
* 获取客户补录信息表单ref对象函数 |
|||
*/ |
|||
custFormRefFunction: any; |
|||
/** |
|||
* 获取定性选项表单ref对象函数 |
|||
*/ |
|||
qualitativeFormRefFunction: any; |
|||
/** |
|||
* 评级等级格式化函数 |
|||
*/ |
|||
ratingLevelFormatFunction: any; |
|||
/** |
|||
* 获取评级调整项列表ref对象函数 |
|||
*/ |
|||
adjustItemGridRefFunction: any; |
|||
/** |
|||
* 获取签署意见表单ref对象函数 |
|||
*/ |
|||
overturnFormRefFunction: any; |
|||
|
|||
setFinanceReportGridRefFunction(fun: () => void) { |
|||
this.financeReportGridRefFunction = fun; |
|||
} |
|||
setCreditReportGridRefFunction(fun: () => void) { |
|||
this.creditReportGridRefFunction = fun; |
|||
} |
|||
setCustFormRefFunction(fun: () => void) { |
|||
this.custFormRefFunction = fun; |
|||
} |
|||
setQualitativeFormRefFunction(fun: () => void) { |
|||
this.qualitativeFormRefFunction = fun; |
|||
} |
|||
setRatingLevelFormatFunction(fun: (modelLevel: any, size?: string) => any) { |
|||
this.ratingLevelFormatFunction = fun; |
|||
} |
|||
setAdjustItemGridRefFunction(fun: () => void) { |
|||
this.adjustItemGridRefFunction = fun; |
|||
} |
|||
setOverturnFormRefFunction(fun: () => void) { |
|||
this.overturnFormRefFunction = fun; |
|||
} |
|||
} |
@ -0,0 +1,466 @@ |
|||
import { axios, Environment, Tools, NotifyManager, Formater } from 'platform-core'; |
|||
import { Rating } from './Rating'; |
|||
import { Constant } from './Constant'; |
|||
import { round, groupBy } from './Utils'; |
|||
import { Step } from './Step'; |
|||
import { StepType } from './type/StepType'; |
|||
|
|||
/** |
|||
* 请求后台api |
|||
*/ |
|||
export class RequestApi { |
|||
rating: Rating; |
|||
|
|||
constructor(rating_: Rating) { |
|||
this.rating = rating_; |
|||
} |
|||
|
|||
/** |
|||
* 加载客户信息面板数据 |
|||
*/ |
|||
async loadCustInfo() { |
|||
const custId = this.rating.ratingData.value['custId']; |
|||
const custNo = this.rating.ratingData.value['custNo']; |
|||
await axios.get(Environment.apiContextPath('api/irbs/ratingCompanyCustomer/' + custId)).then((resp) => { |
|||
if (resp && resp.data) { |
|||
this.rating.custInfoData.value = resp.data; |
|||
} |
|||
}); |
|||
if (this.rating.refs.financeReportGridRefFunction) { |
|||
// 加载财报信息
|
|||
const reportGridRef = this.rating.refs.financeReportGridRefFunction(); |
|||
if (reportGridRef) { |
|||
reportGridRef.setFetchDataUrl( |
|||
Environment.apiContextPath('api/irbs/financeReport/getReport?custId=' + custId + '&sort=' + this.rating.custInfoData.value['customerType']), |
|||
); |
|||
reportGridRef.refresh(); |
|||
// 加载征信报告
|
|||
const creditGridRef = this.rating.refs.creditReportGridRefFunction(); |
|||
creditGridRef.setQueryCriteriaFieldValue('custNo', custNo); |
|||
creditGridRef.refresh(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 加载定量定性面板数据 |
|||
*/ |
|||
async loadQuanQualInfo() { |
|||
const stepQuanUrl = Environment.apiContextPath('api/irbs/companyRating/stepQuan'); |
|||
let actuCtrlYears = null; |
|||
if (this.rating.refs.custFormRefFunction) { |
|||
actuCtrlYears = this.rating.refs.custFormRefFunction()?.getFieldValue('actuCtrlYears'); |
|||
} |
|||
const stepQuanParams = { |
|||
// 评级ID
|
|||
ratingId: this.rating.ratingData.value['id'], |
|||
// 补录信息-实际控制人从业年限
|
|||
actuCtrlYears: actuCtrlYears, |
|||
// 页面类型
|
|||
page: this.rating.readMode ? Constant.PAGE_TYPE.DETAIL : Constant.PAGE_TYPE.APPLY, |
|||
}; |
|||
await axios |
|||
.get(stepQuanUrl, { params: stepQuanParams }) |
|||
.then((resp) => { |
|||
this.rating.hideLoading(); |
|||
if (resp && resp.data && resp.data.quanScore) { |
|||
this.rating.quanData.value = []; |
|||
this.rating.quanData.value.push({ label: '定量得分', value: round(resp.data.quanScore, 2) }); |
|||
} |
|||
}) |
|||
.catch((error) => { |
|||
this.rating.hideLoading(); |
|||
console.info('error====', error); |
|||
}); |
|||
if (this.rating.systemParameter.showIndex.value) { |
|||
await this.getScoreDetail(); |
|||
} |
|||
const stepQualUrl = Environment.apiContextPath('api/irbs/companyRating/stepQual'); |
|||
await axios |
|||
.get(stepQualUrl, { params: stepQuanParams }) |
|||
.then((resp) => { |
|||
this.rating.hideLoading(); |
|||
if (resp && resp.data && resp.data.indices) { |
|||
this.rating.qualFormData.value = []; |
|||
this.rating.qualSimpleOptionData.value = []; |
|||
this.rating.waitSelectFields.value = []; |
|||
this.rating.qualFormObj.value = {}; |
|||
// 将指标进行分组
|
|||
const groupMap = groupBy(resp.data.indices, 'indexCategory'); |
|||
Object.keys(groupMap).forEach((key: any) => { |
|||
const fields = this.buildFormQualIndex(groupMap[key]); |
|||
const group = { label: key, type: 'w-form-group', mode: 'card', fields: fields }; |
|||
const simpleFields = this.buildFormQualIndex(groupMap[key], false); |
|||
const simpleGroup = { label: key, type: 'w-form-group', mode: 'card', fields: simpleFields }; |
|||
this.rating.qualFormData.value.push(group); |
|||
this.rating.qualSimpleOptionData.value.push(simpleGroup); |
|||
}); |
|||
} |
|||
}) |
|||
.catch((error) => { |
|||
this.rating.hideLoading(); |
|||
console.info('error====', error); |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 构建添加到form表单中得定性指标字段 |
|||
* @param arr |
|||
* @returns |
|||
*/ |
|||
private buildFormQualIndex(arr: Array<any>, allOptions: boolean = true) { |
|||
const resultArr = <any>[]; |
|||
for (let i = 0; i < arr.length; i++) { |
|||
const index = arr[i]; |
|||
const indexOptions = <any>[]; |
|||
if (index.options && index.options.length > 0) { |
|||
index.options.forEach((item, index_) => { |
|||
const option = { label: item.text, value: item.disVal }; |
|||
if (index_ === 0) { |
|||
option['desc'] = { label: '选项说明' }; |
|||
} |
|||
if (allOptions || (!Tools.isEmpty(index.indexValue) && index.indexValue === item.disVal)) { |
|||
indexOptions.push(option); |
|||
} |
|||
}); |
|||
} |
|||
const radio = { |
|||
label: i + 1 + '、' + index.indexName, |
|||
name: index.indexCode, |
|||
type: 'w-ext-radio', |
|||
dense: true, |
|||
options: indexOptions, |
|||
selectedColor: 'blue', |
|||
selectedLabelColor: '#2196f3', |
|||
}; |
|||
if (i !== 0) { |
|||
radio['class'] = 'pt-1'; |
|||
} |
|||
if (!Tools.isEmpty(index.indexValue)) { |
|||
radio['defaultValue'] = index.indexValue; |
|||
} |
|||
resultArr.push(radio); |
|||
if (allOptions) { |
|||
this.rating.waitSelectFields.value.push({ group: index.indexCategory, field: radio }); |
|||
this.rating.qualFormObj.value[index.indexCode] = [i + 1 + '、' + index.indexName, index]; |
|||
} |
|||
} |
|||
return resultArr; |
|||
} |
|||
|
|||
/** |
|||
* 加载评级调整项面板信息 |
|||
*/ |
|||
async loadAdjustItemInfo() { |
|||
this.loadFirstResult(); |
|||
this.loadAdjustItem(); |
|||
} |
|||
|
|||
/** |
|||
* 加载得分详情 |
|||
*/ |
|||
async getScoreDetail() { |
|||
// 加载得分详情
|
|||
const urlSearchParams = new URLSearchParams({ sortBy: ['INDEX_TYPE', 'INDEX_CATEGORY'] }); |
|||
urlSearchParams.append('pageable', 'false'); |
|||
const criteria = { |
|||
fieldName: 'RATING_ID', |
|||
operator: 'equals', |
|||
value: this.rating.ratingData.value['id'], |
|||
}; |
|||
urlSearchParams.append('criteria', JSON.stringify(criteria)); |
|||
const url = Environment.apiContextPath('api/irbs/ratingIndex/getScoreDetail'); |
|||
await axios |
|||
.get(url, { params: urlSearchParams }) |
|||
.then((resp) => { |
|||
if (resp?.data?.content) { |
|||
const groupMap = groupBy(resp.data.content, 'INDEX_TYPE'); |
|||
this.rating.quanScoreDtlData.value = groupBy(groupMap['QUANTITATIVE'], 'INDEX_CATEGORY'); |
|||
this.rating.qualScoreDtlData.value = groupBy(groupMap[Step.type.quanQualAnalysis.value], 'INDEX_CATEGORY'); |
|||
if (groupMap[Step.type.adjustItem.value] && groupMap[Step.type.adjustItem.value].length > 0) { |
|||
this.rating.adjustItemScoreDtlData.value = groupBy( |
|||
groupMap[Step.type.adjustItem.value].filter((item) => { |
|||
return item['INDEX_VALUE'] !== '0'; |
|||
}), |
|||
'INDEX_CATEGORY', |
|||
); |
|||
} |
|||
} |
|||
}) |
|||
.catch((error) => { |
|||
this.rating.hideLoading(); |
|||
console.info('error====', error); |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 保存评级调整项 |
|||
* @returns |
|||
*/ |
|||
async adjustItemSave() { |
|||
let result = false; |
|||
const submitData = <any>[]; |
|||
const rows = this.rating.refs.adjustItemGridRefFunction().getRows(); |
|||
rows.forEach((item, index) => { |
|||
this.rating.adjustItemObj.value[item.indexCode][1].indexValue = item.value |
|||
? this.rating.adjustItemObj.value[item.indexCode][1]['options'].find((opt) => { |
|||
return opt['disVal'] !== '0'; |
|||
})['disVal'] |
|||
: '0'; |
|||
submitData.push(this.rating.adjustItemObj.value[item.indexCode][1]); |
|||
}); |
|||
this.rating.showLoading('正在重新计算得分,请稍等...'); |
|||
const resp = await axios.post(Environment.apiContextPath('api/irbs/companyRating/saveIndices'), submitData).catch((error) => { |
|||
this.rating.hideLoading(); |
|||
NotifyManager.error('计算出错,请联系管理员'); |
|||
console.info('error====', error); |
|||
}); |
|||
if (resp['code'] === 200) { |
|||
result = true; |
|||
} else { |
|||
this.rating.hideLoading(); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 保存定性选项指标 |
|||
* @param nextStep 下一步步骤 |
|||
* @param op 操作类型(分为试算与正式提交) |
|||
* @param submitData 提交的数据集合 |
|||
*/ |
|||
async qualSaveIndices(nextStep: StepType, op: string, submitData: Array<any>) { |
|||
this.rating.showLoading('正在计算定性得分,请稍等...'); |
|||
const url = Environment.apiContextPath('api/irbs/companyRating/qualSaveIndices/' + op); |
|||
const resp = await axios.post(url, submitData).catch((error) => { |
|||
this.rating.hideLoading(); |
|||
NotifyManager.error('计算出错,请联系管理员'); |
|||
console.info('error====', error); |
|||
}); |
|||
if (resp?.data?.success === true) { |
|||
this.loadFirstResult(nextStep); |
|||
this.loadAdjustItem(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 加载初评结果 |
|||
* @param nextStep |
|||
*/ |
|||
async loadFirstResult(nextStep?: StepType) { |
|||
this.rating.firstRatingData.value = []; |
|||
const url = Environment.apiContextPath('api/irbs/companyRating/stepInitRating'); |
|||
const params = { |
|||
params: { |
|||
ratingId: this.rating.ratingData.value['id'], |
|||
page: this.rating.readMode ? Constant.PAGE_TYPE.DETAIL : Constant.PAGE_TYPE.APPLY, |
|||
}, |
|||
}; |
|||
const res = await axios.get(url, params).catch((error) => { |
|||
this.rating.hideLoading(); |
|||
console.info('error====', error); |
|||
}); |
|||
if (nextStep) { |
|||
this.rating.step.next(nextStep); |
|||
this.rating.hideLoading(); |
|||
} |
|||
this.rating.firstRatingData.value.push({ |
|||
label: '等级', |
|||
value: res?.data?.modelLevel, |
|||
format: () => { |
|||
return this.rating.refs.ratingLevelFormatFunction(res?.data?.modelLevel); |
|||
}, |
|||
}); |
|||
if (this.rating.systemParameter.showTotalScore.value) { |
|||
this.rating.firstRatingData.value.push({ label: '得分', value: res?.data?.modelScore ? round(res.data.modelScore, 2) : '' }); |
|||
this.rating.firstRatingData.value.push({ label: '定量得分', value: res?.data?.quanScore ? round(res.data.quanScore, 2) : '' }); |
|||
this.rating.firstRatingData.value.push({ label: '定性得分', value: res?.data?.qualScore ? round(res.data.qualScore, 2) : '' }); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 加载评级调整项 |
|||
*/ |
|||
loadAdjustItem() { |
|||
const url = Environment.apiContextPath('api/irbs/companyRating/stepAdj'); |
|||
const params = { |
|||
params: { |
|||
ratingId: this.rating.ratingData.value['id'], |
|||
page: this.rating.readMode ? Constant.PAGE_TYPE.DETAIL : Constant.PAGE_TYPE.APPLY, |
|||
}, |
|||
}; |
|||
axios |
|||
.get(url, params) |
|||
.then((resp) => { |
|||
if (resp && resp.data && resp.data.indices) { |
|||
this.rating.adjustItemData.value = []; |
|||
for (let i = 0; i < resp.data.indices.length; i++) { |
|||
const index = resp.data.indices[i]; |
|||
this.rating.adjustItemObj.value[index.indexCode] = [i + 1 + '、' + index.indexName, index]; |
|||
this.rating.adjustItemData.value.push({ |
|||
value: index.indexValue !== '0' && !Tools.isEmpty(index.indexValue) ? true : false, |
|||
indexCode: index.indexCode, |
|||
indexCategory: index.indexCategory, |
|||
adjustItemName: index.indexName, |
|||
}); |
|||
} |
|||
this.rating.refs.adjustItemGridRefFunction().setLocalData(this.rating.adjustItemData.value); |
|||
} |
|||
}) |
|||
.catch((error) => { |
|||
console.info('error====', error); |
|||
}); |
|||
} |
|||
|
|||
async loadRatingReport() { |
|||
this.rating.reportCustRatingData.value = []; |
|||
this.rating.reportRatingMainData.value = []; |
|||
const resp = await axios |
|||
.get(Environment.apiContextPath('api/irbs/companyRating/stepRatingReport'), { params: { ratingId: this.rating.ratingData.value['id'] } }) |
|||
.catch((error) => { |
|||
this.rating.hideLoading(); |
|||
console.info('error====', error); |
|||
}); |
|||
this.rating.hideLoading(); |
|||
this.rating.ratingData.value = resp?.data; |
|||
// this.rating.reportRatingMainData.value.push({ label: '模型名称', value: this.rating.ratingData.value['modelName'] });
|
|||
// this.rating.reportCustRatingData.value.push({ label: '模型名称', value: this.rating.ratingData.value['modelName'] });
|
|||
if (this.rating.systemParameter.showTotalScore.value) { |
|||
this.rating.reportCustRatingData.value.push({ label: '定量得分', value: round(this.rating.ratingData.value['quanScore'], 2) }); |
|||
this.rating.reportCustRatingData.value.push({ label: '定性得分', value: round(this.rating.ratingData.value['qualScore'], 2) }); |
|||
this.rating.reportCustRatingData.value.push({ label: '模型得分', value: round(this.rating.ratingData.value['modelScore'], 2) }); |
|||
} |
|||
this.rating.reportRatingMainData.value.push({ |
|||
label: '模型级别', |
|||
value: this.rating.ratingData.value['modelLevel'], |
|||
// format: () => {
|
|||
// return this.rating.refs.ratingLevelFormatFunction(this.rating.ratingData.value['modelLevel'], this.rating.mainLevelSize);
|
|||
// },
|
|||
}); |
|||
// this.rating.reportCustRatingData.value.push({
|
|||
// label: '模型级别',
|
|||
// value: this.rating.ratingData.value['modelLevel'],
|
|||
// format: () => {
|
|||
// return this.rating.refs.ratingLevelFormatFunction(this.rating.ratingData.value['modelLevel']);
|
|||
// },
|
|||
// });
|
|||
this.rating.reportRatingMainData.value.push({ |
|||
label: '调整项级别', |
|||
value: this.rating.ratingData.value['adjLevel'], |
|||
// format: () => {
|
|||
// return this.rating.refs.ratingLevelFormatFunction(this.rating.ratingData.value['adjLevel'], this.rating.mainLevelSize);
|
|||
// },
|
|||
}); |
|||
// this.rating.reportCustRatingData.value.push({
|
|||
// label: '调整项级别',
|
|||
// value: this.rating.ratingData.value['adjLevel'],
|
|||
// format: () => {
|
|||
// return this.rating.refs.ratingLevelFormatFunction(this.rating.ratingData.value['adjLevel']);
|
|||
// },
|
|||
// });
|
|||
this.rating.reportCustRatingData.value.push({ |
|||
label: '初始级别', |
|||
value: this.rating.ratingData.value['initLevel'], |
|||
// format: () => {
|
|||
// return this.rating.refs.ratingLevelFormatFunction(this.rating.ratingData.value['initLevel']);
|
|||
// },
|
|||
}); |
|||
this.rating.reportCustRatingData.value.push({ label: '上一次建议级别', value: this.rating.ratingData.value['spLevel'] }); |
|||
this.rating.reportRatingMainData.value.push({ |
|||
label: '最终认定级别', |
|||
value: this.rating.ratingData.value['finalLevel'], |
|||
// format: () => {
|
|||
// return this.rating.refs.ratingLevelFormatFunction(this.rating.ratingData.value['finalLevel'], this.rating.mainLevelSize);
|
|||
// },
|
|||
}); |
|||
// this.rating.reportCustRatingData.value.push({
|
|||
// label: '最终认定级别',
|
|||
// value: this.rating.ratingData.value['finalLevel'],
|
|||
// format: () => {
|
|||
// return this.rating.refs.ratingLevelFormatFunction(this.rating.ratingData.value['finalLevel']);
|
|||
// },
|
|||
// });
|
|||
// this.rating.reportCustRatingData.value.push({
|
|||
// label: '违约概率',
|
|||
// value: this.rating.ratingData.value['pd'] ? this.rating.ratingData.value['pd'] + '%' : '',
|
|||
// });
|
|||
// this.rating.reportCustRatingData.value.push({ label: '客户经理', value: this.rating.ratingData.value['managerName'] });
|
|||
this.rating.reportCustRatingData.value.push({ label: '评级发起日期', value: this.rating.ratingData.value['startTime'] }); |
|||
this.rating.reportCustRatingData.value.push({ label: '评级生效日期', value: this.rating.ratingData.value['effectiveTime'] }); |
|||
this.rating.reportCustRatingData.value.push({ label: '评级到期日期', value: this.rating.ratingData.value['matureTime'] }); |
|||
this.rating.reportCustRatingData.value.push({ |
|||
label: '评级状态', |
|||
value: this.rating.ratingData.value['ratingStatus'], |
|||
format: Formater.enum(this.rating.enum.ratingStatus), |
|||
}); |
|||
this.rating.reportCustRatingData.value.push({ |
|||
label: '流程状态', |
|||
value: this.rating.ratingData.value['processStatus'], |
|||
format: Formater.enum(this.rating.enum.ratingProcessStatus), |
|||
}); |
|||
if (this.rating.systemParameter.reportShowScoreDtl.value) { |
|||
this.getScoreDetail(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 加载评级推翻需要的数据 |
|||
*/ |
|||
async loadOverturnInfo() { |
|||
const params = { |
|||
params: { |
|||
ratingId: this.rating.ratingData.value['id'], |
|||
page: this.rating.readMode ? Constant.PAGE_TYPE.DETAIL : Constant.PAGE_TYPE.APPLY, |
|||
}, |
|||
}; |
|||
axios |
|||
.get(Environment.apiContextPath('api/irbs/companyRating/stepRatingOverturn'), params) |
|||
.then((res) => { |
|||
this.rating.hideLoading(); |
|||
if (res && res.data) { |
|||
this.rating.overturnTipData.value = []; |
|||
if (this.rating.systemParameter.showTotalScore.value) { |
|||
this.rating.overturnTipData.value.push({ label: '定量得分', value: round(res.data.quanScore, 2) }); |
|||
this.rating.overturnTipData.value.push({ label: '定性得分', value: round(res.data.qualScore, 2) }); |
|||
this.rating.overturnTipData.value.push({ label: '模型得分', value: round(res.data.modelScore, 2) }); |
|||
} |
|||
this.rating.overturnTipData.value.push({ |
|||
label: '模型等级', |
|||
value: res.data.modelLevel, |
|||
// format: () => {
|
|||
// return this.rating.refs.ratingLevelFormatFunction(res.data.modelLevel);
|
|||
// },
|
|||
}); |
|||
this.rating.overturnTipData.value.push({ |
|||
label: '调整后等级', |
|||
value: res.data.adjLevel, |
|||
// format: () => {
|
|||
// return this.rating.refs.ratingLevelFormatFunction(res.data.adjLevel);
|
|||
// },
|
|||
}); |
|||
this.rating.overturnTipData.value.push({ |
|||
label: '准入建议', |
|||
value: res.data.accessLevel, |
|||
}); |
|||
} |
|||
}) |
|||
.catch((error) => { |
|||
this.rating.hideLoading(); |
|||
console.info('error====', error); |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 加载财报信息 |
|||
*/ |
|||
loadFinanceReport() { |
|||
const custId = this.rating.ratingData.value['custId']; |
|||
const customerType = '1'; |
|||
const url = Environment.apiContextPath('api/irbs/financeReport/getReport?custId=' + custId + '&sort=' + customerType); |
|||
axios.get(url).then((resp) => { |
|||
if (resp?.data) { |
|||
this.rating.financeReport.value = resp.data; |
|||
} |
|||
}); |
|||
} |
|||
} |
@ -0,0 +1,325 @@ |
|||
import { nextTick, Ref, ref } from 'vue'; |
|||
import { Dialog } from 'quasar'; |
|||
import { Tools, DialogManager, $t } from 'platform-core'; |
|||
import { Constant } from './Constant'; |
|||
import type { StepType } from './type/StepType'; |
|||
import { Rating } from './Rating'; |
|||
|
|||
// 步骤类
|
|||
export class Step { |
|||
/** |
|||
* 步骤类型对象 |
|||
*/ |
|||
static type = { |
|||
/** |
|||
* 客户信息 |
|||
*/ |
|||
custInfo: <StepType>{ |
|||
label: '客户信息', |
|||
tooltip: '确认评分卡及客户信息是否有误', |
|||
value: 'CUST_INFO', |
|||
icon: 'bi-1-circle-fill', |
|||
order: 1, |
|||
disable: false, |
|||
}, |
|||
/** |
|||
* 定量定性分析 |
|||
*/ |
|||
quanQualAnalysis: <StepType>{ |
|||
label: '定性分析', |
|||
tooltip: '定性指标填报', |
|||
value: 'QUALITATIVE_EDIT', |
|||
icon: 'bi-2-circle-fill', |
|||
order: 2, |
|||
disable: false, |
|||
}, |
|||
/** |
|||
* 评级调整项 |
|||
*/ |
|||
adjustItem: <StepType>{ |
|||
label: '评级调整项', |
|||
tooltip: '查看初评结果并勾选调整项', |
|||
value: 'ADJUST_ITEM', |
|||
icon: 'bi-3-circle-fill', |
|||
order: 3, |
|||
disable: false, |
|||
}, |
|||
/** |
|||
* 评级报告 |
|||
*/ |
|||
reportInfo: <StepType>{ |
|||
label: '评级报告', |
|||
tooltip: '确认评级指标及评级结果', |
|||
value: 'REPORT_INFO', |
|||
icon: 'bi-4-circle-fill', |
|||
order: 4, |
|||
disable: false, |
|||
}, |
|||
/** |
|||
* 签署意见 |
|||
*/ |
|||
opinion: <StepType>{ |
|||
label: '签署意见', |
|||
tooltip: '评级推翻及提交审核', |
|||
value: 'OTHER', |
|||
icon: 'bi-5-circle-fill', |
|||
order: 5, |
|||
disable: false, |
|||
}, |
|||
}; |
|||
|
|||
/** |
|||
* 评级类 |
|||
*/ |
|||
rating: Rating; |
|||
/** |
|||
* 步骤集合 |
|||
*/ |
|||
steps: Ref = ref([]); |
|||
|
|||
/** |
|||
* 当前步骤 |
|||
*/ |
|||
currStep: Ref = ref(Step.type.reportInfo.value); |
|||
|
|||
constructor(rating_: Rating) { |
|||
this.rating = rating_; |
|||
this.initSteps(); |
|||
this.click = this.click.bind(this); |
|||
this.nextClick = this.nextClick.bind(this); |
|||
} |
|||
|
|||
/** |
|||
* 步骤点击事件 |
|||
*/ |
|||
click(value: string) { |
|||
this.setCurrStep(value); |
|||
} |
|||
|
|||
/** |
|||
* 执行下一步 |
|||
* @param nextStep |
|||
*/ |
|||
next(nextStep: StepType) { |
|||
// 将下一步禁用状态改为可用
|
|||
this.setDisable(nextStep, false); |
|||
// 将当前步骤修改为下一步
|
|||
this.setCurrStep(nextStep.value); |
|||
} |
|||
|
|||
/** |
|||
* 下一步按钮点击事件 |
|||
*/ |
|||
nextClick() { |
|||
const nextStep = this.findNextStep(this.currStep.value); |
|||
switch (nextStep.value) { |
|||
case Step.type.quanQualAnalysis.value: |
|||
this.nextQuanQual(nextStep); |
|||
break; |
|||
case Step.type.adjustItem.value: |
|||
this.nextAdjustItem(nextStep); |
|||
break; |
|||
case Step.type.reportInfo.value: |
|||
this.nextReport(nextStep); |
|||
break; |
|||
case Step.type.opinion.value: |
|||
this.nextOpinion(nextStep); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 下一步为定量定性分析 |
|||
* @param nextStep |
|||
*/ |
|||
async nextQuanQual(nextStep: StepType) { |
|||
this.rating.showLoading('正在获取定量得分,请稍等...'); |
|||
// await this.rating.api.loadQuanQualInfo();
|
|||
this.next(nextStep); |
|||
} |
|||
|
|||
/** |
|||
* 下一步为评级调整项步骤 |
|||
*/ |
|||
async nextAdjustItem(nextStep: StepType) { |
|||
const result = this.qualFormValidate(); |
|||
if (!result['result']) { |
|||
return; |
|||
} |
|||
DialogManager.yesnoDialog({ |
|||
title: $t('confirm') || '', |
|||
message: '请检查所选数据的正确性,提交并计算结果后无法再次修改,确定要提交吗?', |
|||
yesCallback: () => { |
|||
this.rating.showLoading('正在计算定性得分,请稍等...'); |
|||
this.rating.api.qualSaveIndices(nextStep, Constant.QUAL_SAVE_OPERATOR.NEXT, result['submitData']); |
|||
}, |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 下一步为评级报告 |
|||
* @param nextStep |
|||
*/ |
|||
async nextReport(nextStep: StepType) { |
|||
const result = await this.rating.api.adjustItemSave(); |
|||
if (result) { |
|||
this.next(nextStep); |
|||
// this.rating.api.loadCustInfo();
|
|||
// this.rating.api.loadQuanQualInfo();
|
|||
// this.rating.api.loadRatingReport();
|
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 下一步为签署意见 |
|||
* @param nextStep |
|||
*/ |
|||
async nextOpinion(nextStep: StepType) { |
|||
// await this.rating.api.loadOverturnInfo();
|
|||
this.next(nextStep); |
|||
} |
|||
|
|||
/** |
|||
* 定性选项验证 |
|||
* @returns |
|||
*/ |
|||
qualFormValidate() { |
|||
// 判断是否存在未选择的选项
|
|||
const formData = this.rating.refs.qualitativeFormRefFunction().getData(); |
|||
const submitData = <any>[]; |
|||
let result = true; |
|||
const errorRows = <any>[]; |
|||
const keys = Object.keys(formData); |
|||
for (let i = 0; i < keys.length; i++) { |
|||
if (Tools.isEmpty(formData[keys[i]])) { |
|||
result = false; |
|||
errorRows.push({ |
|||
label: this.rating.qualFormObj.value[keys[i]][0], |
|||
value: keys[i], |
|||
checkedIcon: 'cancel', |
|||
uncheckedIcon: 'cancel', |
|||
color: 'red', |
|||
keepColor: true, |
|||
disable: true, |
|||
dense: true, |
|||
}); |
|||
} else { |
|||
this.rating.qualFormObj.value[keys[i]][1].indexValue = formData[keys[i]]; |
|||
submitData.push(this.rating.qualFormObj.value[keys[i]][1]); |
|||
} |
|||
} |
|||
if (!result) { |
|||
Dialog.create({ |
|||
title: '提示', |
|||
message: '以下定性选项为空,请选择后执行该操作!', |
|||
persistent: true, |
|||
options: { |
|||
type: 'checkbox', |
|||
model: [], |
|||
items: errorRows, |
|||
}, |
|||
}); |
|||
} |
|||
return { |
|||
result, |
|||
submitData, |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* 根据 value 返回 步骤信息 |
|||
* @param value |
|||
*/ |
|||
findByValue(value: string): StepType { |
|||
return this.steps.value.find((item) => item.value === value); |
|||
} |
|||
|
|||
/** |
|||
* 根据 value 返回 下一步的步骤信息 |
|||
* @param value |
|||
*/ |
|||
findNextStep(value: string): StepType { |
|||
const currStep_ = this.findByValue(value); |
|||
if (!currStep_) { |
|||
throw new Error('[step.ts] Step information cannot be found based on the current value(' + value + ').'); |
|||
} |
|||
return this.steps.value.find((item) => item.order === currStep_.order + 1); |
|||
} |
|||
|
|||
/** |
|||
* 设置步骤禁用状态 |
|||
* @param step_ 步骤对象 |
|||
* @param value 是否禁用 |
|||
*/ |
|||
setDisable(step_: StepType, value: boolean) { |
|||
step_.disable = value; |
|||
} |
|||
|
|||
/** |
|||
* 设置当前步骤 |
|||
* @param step_ |
|||
*/ |
|||
setCurrStep(value: string) { |
|||
// TODO 如果状态为初始化完成,直接使用改造过后的前端步骤-客户信息
|
|||
const value_ = value === 'INIT_FINISH' ? Step.type.custInfo.value : value; |
|||
if (value_) { |
|||
this.currStep.value = value_; |
|||
nextTick(() => { |
|||
switch (value_) { |
|||
case Step.type.custInfo.value: |
|||
this.rating.api.loadCustInfo(); |
|||
break; |
|||
case Step.type.quanQualAnalysis.value: |
|||
this.rating.api.loadQuanQualInfo(); |
|||
break; |
|||
case Step.type.adjustItem.value: |
|||
this.rating.api.loadAdjustItemInfo(); |
|||
break; |
|||
case Step.type.reportInfo.value: |
|||
this.rating.api.loadCustInfo(); |
|||
this.rating.api.loadQuanQualInfo(); |
|||
this.rating.api.loadRatingReport(); |
|||
break; |
|||
case Step.type.opinion.value: |
|||
this.rating.api.loadOverturnInfo(); |
|||
break; |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 初始化评级步骤 |
|||
* @param |
|||
*/ |
|||
private initSteps() { |
|||
if (this.rating.cm.isAwaitSubmitProcessStatus.value) { |
|||
// 发起评级页面
|
|||
const currStep_ = Object.values(Step.type).find( |
|||
(item) => |
|||
item.value === |
|||
(this.rating.ratingData.value['currentStep'] === 'INIT_FINISH' ? Step.type.custInfo.value : this.rating.ratingData.value['currentStep']), |
|||
); |
|||
const result: Array<StepType> = []; |
|||
Object.values(Step.type) |
|||
.sort((a, b) => a.order - b.order) |
|||
.forEach((step: StepType) => { |
|||
if (step.order > Step.type.custInfo.order) { |
|||
// 默认将顺序大于`客户信息`的步骤全部置为不可用状态。
|
|||
step.disable = true; |
|||
} |
|||
if (currStep_ && step.order <= currStep_.order) { |
|||
// 将顺序 <= 当前评级表中存储的步骤 的状态置为可用
|
|||
step.disable = false; |
|||
} |
|||
result.push(step); |
|||
}); |
|||
this.steps.value = result; |
|||
} else { |
|||
// 审批流程页面签署意见需要单独与业务信息分成左右两边布局放置,不需要【签署意见】步骤。
|
|||
this.steps.value = Object.values(Step.type) |
|||
.filter((item) => item.value !== Step.type.opinion.value) |
|||
.sort((a, b) => a.order - b.order); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,67 @@ |
|||
import { axios, Environment } from 'platform-core'; |
|||
import { Ref, ref } from 'vue'; |
|||
|
|||
/** |
|||
* 系统参数 |
|||
*/ |
|||
export class SystemParameter { |
|||
/** |
|||
* 评级向上推翻几级需总行审批 |
|||
*/ |
|||
overturnLevel: Ref = ref(3); |
|||
/** |
|||
* 是否展示定量定性总分 |
|||
*/ |
|||
showTotalScore: Ref = ref(false); |
|||
/** |
|||
* 是否展示指标 |
|||
*/ |
|||
showIndex: Ref = ref(false); |
|||
/** |
|||
* 是否展示定量定性指标得分 |
|||
*/ |
|||
showIndexScore: Ref = ref(false); |
|||
/** |
|||
* 评级报告是否展示得分详情 |
|||
*/ |
|||
reportShowScoreDtl: Ref = ref(false); |
|||
/** |
|||
* 评级报告是否展示定量定性指标得分 |
|||
*/ |
|||
reportShowIndexScore: Ref = ref(false); |
|||
/** |
|||
* 定性试算次数 |
|||
*/ |
|||
qualTestCalcNum: Ref = ref(5); |
|||
/** |
|||
* 定性选项启用问答模式填报 |
|||
*/ |
|||
qualQaMode: Ref = ref(true); |
|||
|
|||
async load() { |
|||
// 获取当前参数配置中评级页面相关的配置
|
|||
const parameters = [ |
|||
'parameter.irbs.params.overturnLevel', |
|||
'parameter.irbs.params.ratingUI.totalScore', |
|||
'parameter.irbs.params.ratingUI.index', |
|||
'parameter.irbs.params.ratingUI.indexScore', |
|||
'parameter.irbs.params.ratingUI.report.indexScore', |
|||
'parameter.irbs.params.qual.calc.num', |
|||
]; |
|||
// 判断当前用户在评级报告中是否能看到得分详情
|
|||
const authResult = await axios.get(Environment.apiContextPath('api/irbs/rating/report/showScoreRole/findShowScoreAuth')).catch((error) => { |
|||
console.info('error====', error); |
|||
}); |
|||
this.reportShowScoreDtl.value = authResult['data']; |
|||
const res: any = await axios.post(Environment.apiContextPath('api/irbs/companyRating/getSystemParameters'), parameters).catch((error) => { |
|||
console.info('error====', error); |
|||
}); |
|||
this.overturnLevel.value = parseInt(res.data['parameter.irbs.params.overturnLevel']); |
|||
this.showTotalScore.value = res.data['parameter.irbs.params.ratingUI.totalScore'] === 'show' ? true : false; |
|||
this.showIndex.value = res.data['parameter.irbs.params.ratingUI.index'] === 'show' ? true : false; |
|||
this.showIndexScore.value = res.data['parameter.irbs.params.ratingUI.indexScore'] === 'show' ? true : false; |
|||
this.reportShowIndexScore.value = |
|||
res.data['parameter.irbs.params.ratingUI.report.indexScore'] === 'show' && this.reportShowScoreDtl.value === true ? true : false; |
|||
this.qualTestCalcNum.value = parseInt(res.data['parameter.irbs.params.qual.calc.num']); |
|||
} |
|||
} |
@ -0,0 +1,41 @@ |
|||
export const round = (val: any, precision: any) => { |
|||
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 groupBy = (arr, prop) => |
|||
arr.reduce((acc, item) => { |
|||
const key = item[prop]; |
|||
if (!acc[key]) acc[key] = []; |
|||
acc[key].push(item); |
|||
return acc; |
|||
}, {}); |
|||
|
|||
export const formatAmt = (num: any) => { |
|||
if (num) { |
|||
// 强制转换为Number类型(自动解析科学计数法)
|
|||
const numberVal = Number(num); |
|||
|
|||
// 常规处理流程
|
|||
const fixedNum = numberVal.toFixed(2); |
|||
const [integer, decimal] = fixedNum.split('.'); |
|||
return integer.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') + '.' + decimal; |
|||
} |
|||
return num; |
|||
}; |
@ -0,0 +1,27 @@ |
|||
// 步骤类型
|
|||
export type StepType = { |
|||
/** |
|||
* 步骤名称 |
|||
*/ |
|||
label: string; |
|||
/** |
|||
* 步骤码值 |
|||
*/ |
|||
value: string; |
|||
/** |
|||
* 步骤使用的图标 |
|||
*/ |
|||
icon: string; |
|||
/** |
|||
* 步骤顺序 |
|||
*/ |
|||
order: number; |
|||
/** |
|||
* 步骤当前是否禁用 |
|||
*/ |
|||
disable: boolean; |
|||
/** |
|||
* 步骤悬浮提示 |
|||
*/ |
|||
tooltip: string; |
|||
}; |
@ -0,0 +1,50 @@ |
|||
<template> |
|||
<div class="h-full"> |
|||
<q-splitter v-model="tabsSplitterModel" horizontal class="h-full" unit="px" disable> |
|||
<template #before> |
|||
<q-tabs v-model="tabsModel" dense no-caps inline-label indicator-color="amber" active-color="amber" align="right"> |
|||
<q-tab name="mockApply" icon="arrow_outward" label="模拟发起评级直接打开评级窗口" /> |
|||
<q-tab name="mockTask" icon="assignment" label="模拟处理任务直接打开任务窗口" /> |
|||
</q-tabs> |
|||
</template> |
|||
<template #after> |
|||
<!-- 切分窗口 --> |
|||
<q-tab-panels v-model="tabsModel" class="p-0 h-full" animated> |
|||
<q-tab-panel name="mockApply" class="p-0 h-full"> |
|||
<CustSelectGrid |
|||
ref="customerGridRef" |
|||
:toolbar-actions="[ |
|||
'query', |
|||
'reset', |
|||
'separator', |
|||
{ |
|||
name: 'mock', |
|||
label: '模拟发起评级直接打开评级窗口', |
|||
icon: 'add', |
|||
enableIf: (args) => { |
|||
return args.selected; |
|||
}, |
|||
click: mockApply, |
|||
}, |
|||
]" |
|||
></CustSelectGrid> |
|||
</q-tab-panel> |
|||
<q-tab-panel name="mockTask" class="p-0 h-full"> 模拟处理任务直接打开任务窗口 </q-tab-panel> |
|||
</q-tab-panels> |
|||
</template> |
|||
</q-splitter> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
import CustSelectGrid from '../company/CustSelectGrid.vue'; |
|||
|
|||
const tabsSplitterModel = ref(36); |
|||
const customerGridRef = ref(); |
|||
const tabsModel = ref('mockApply'); |
|||
const mockApply = (args) => { |
|||
const custNo = args.selected['custNo']; |
|||
window.open('http://localhost:3000/#/irbs/rating/apply?custNo=' + custNo, '_blank'); |
|||
}; |
|||
</script> |
Loading…
Reference in new issue