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