84 changed files with 4707 additions and 322 deletions
@ -1,15 +1,30 @@ |
|||||
package irbs.cust.rating.controller; |
package irbs.cust.rating.controller; |
||||
|
|
||||
import io.sc.platform.mvc.controller.support.RestCrudController; |
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.entity.FinanceReportDetail; |
||||
import irbs.cust.rating.jpa.repository.FinanceReportDetailRepository; |
import irbs.cust.rating.jpa.repository.FinanceReportDetailRepository; |
||||
import irbs.cust.rating.jpa.vo.FinanceReportDetailVo; |
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 irbs.cust.rating.service.FinanceReportDetailService; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
import org.springframework.web.bind.annotation.*; |
||||
import org.springframework.web.bind.annotation.RestController; |
|
||||
|
import java.util.List; |
||||
|
|
||||
@RestController |
@RestController |
||||
@RequestMapping("/api/irbs/financeReportDetail") |
@RequestMapping("/api/irbs/financeReportDetail") |
||||
public class FinanceReportDetailController extends RestCrudController<FinanceReportDetailVo, FinanceReportDetail, String, FinanceReportDetailRepository, FinanceReportDetailService> { |
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; |
package irbs.cust.rating.service; |
||||
|
|
||||
import io.sc.platform.orm.service.DaoService; |
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.entity.FinanceReportDetail; |
||||
import irbs.cust.rating.jpa.repository.FinanceReportDetailRepository; |
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 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; |
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.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.FinanceReportDetail; |
||||
|
import irbs.cust.rating.jpa.entity.RatingCompanyCustomer; |
||||
import irbs.cust.rating.jpa.repository.FinanceReportDetailRepository; |
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.FinanceReportDetailService; |
||||
|
import irbs.cust.rating.service.RatingCompanyCustomerService; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Service; |
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
@Service("financeReportDetailService") |
@Service("financeReportDetailService") |
||||
public class FinanceReportDetailServiceImpl extends DaoServiceImpl<FinanceReportDetail, String, FinanceReportDetailRepository> implements 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