Compare commits

...

21 Commits

Author SHA1 Message Date
hz
ddd1bee126 feat(underwriting): 新增待核保状态并优化路由跳转逻辑
- 在 ORDER_STATUS 常量中新增 pending(待核保)状态
- 修复 navigateRouter 中 routerInfo 被覆盖的问题,使用对象展开保留原有属性
- 在 OrderList 组件中引入 ORDER_STATUS 并优化 navigateDataCollection 方法支持传递订单状态
- 更新 SupplementaryInformation 页面结构,引入 Sticky 组件优化提示信息展示
- 在 UnderwritingDataCollection 页面根据订单状态动态显示不同操作按钮及文案
- 调整页面样式确保组件布局适配新功能需求
2025-12-12 17:21:15 +08:00
hz
0d5e2b24cb feat(underwriting): 添加预核保订单列表导航功能
- 引入 navigateRouter 方法用于页面跳转
- 新增 handleNavigateToList 方法跳转至订单列表页
- 为确定按钮添加点击事件触发导航跳转
2025-12-12 17:21:15 +08:00
hz
0caaf79345 feat(ebiz/underwriting): 实现订单列表数据获取功能
- 新增 LIST_TYPE 常量定义未审核和已审核状态
- 添加 list API 接口用于获取订单预核保列表数据
- 在 OrderList 组件中引入并使用新的 list 接口
- 使用 LIST_TYPE 替换原有的字符串类型标识
- 实现 fetchListData 方法异步获取列表数据
- 更新 tabs 组件绑定值为 LIST_TYPE 对象属性
- 移除旧的 branchType 相关逻辑
- 调整组件初始化时的数据加载时机到 data 函数中
2025-12-12 14:52:34 +08:00
hz
fac7ec78df feat(underwriting): 重构订单列表页面逻辑与组件
- 移除旧的订单列表获取逻辑,替换为静态数据展示
- 更新搜索框绑定字段从 searchName 到 searchText
- 引入 ORDER_STATUS 常量用于模拟订单状态显示
- 修改 OrderListCard 组件以支持接收 type 属性并据此渲染不同状态
- 调整样式类名和结构以优化界面布局
- 删除不再使用的数据处理函数及导入模块
- 简化分页加载逻辑,目前使用定时器模拟加载效果
- 更新模板中 van-list 的循环方式以适配新数据源
2025-12-12 14:52:34 +08:00
hz
a7c5ed519a style(ebiz/underwriting): 调整页面样式和布局
- 移除 PreliminaryUnderwritingContainer 中 router-view 的 mt10 类
- 为 SupplementaryInformation 页面容器添加 mt10 类
- UnderwritingDataCollection 页面容器增加内边距类 ph15
- 基本信息标题添加上边距类 pt20
- 优化表单字段布局结构
- 为 UnderwritingDataCollection 添加自定义样式
- 设置页面容器高度为 100vh 并添加白色背景
2025-12-12 14:52:33 +08:00
hz
5d084256af fix(underwriting): 移除冗余else分支并添加卡片圆角样式
- 移除了OrderList.vue中created钩子内的冗余else分支
- 为OrderListCard.vue添加了6px的圆角样式
- 优化了条件判断逻辑,提升代码可读性
2025-12-11 15:26:14 +08:00
hz
1ad3d29cd1 feat(ebiz): 添加承保结果页面UI展示
- 引入 vant 组件库的 Image 和 Button 组件
- 添加错误提示图片和文案显示
- 实现页面布局居中对齐
- 设置背景色为白色并占满全屏
- 添加确定按钮并设置样式
- 引入评估结果图片资源
2025-12-11 15:26:13 +08:00
hz
f3a852cbc2 style(underwriting): 调整提交按钮样式
- 将提交按钮的样式从单独的组件移至容器组件
- 统一设置按钮底部距离为10px
- 设置按钮宽度为90%
- 添加水平居中变换
- 移除重复的样式定义
- 优化样式作用域穿透写法
2025-12-11 15:26:13 +08:00
hz
42088c8e81 feat(underwriting): 新增预核保订单列表卡片组件
- 添加 ORDER_STATUS 常量定义,包含标准体、问题件、拒保和延期状态
- 创建 OrderListCard 组件用于展示预核保订单信息
- 在 OrderList.vue 中引入并使用 OrderListCard 组件
- 移除 OrderList.vue 中冗余的 getAgentInfo 方法调用
- 调整 PreliminaryUnderwritingContainer.vue 样式类名
- 新增 UnderwritingResult.vue 空页面组件
- 更新 SupplementaryInformation.vue 页面样式
- 在路由配置中添加预核保结果页路由
2025-12-11 15:26:13 +08:00
hz
a8d13be189 feat(underwriting): 新增路由跳转工具函数并优化跳转逻辑
- 引入 jump 工具函数并调整其内部路由调用方式
- 新增 navigateRouter 函数用于统一管理路由跳转
- 替换原有 add 方法为 navigateDataCollection 并使用新跳转逻辑
- 移除旧有的手动清理 localStorage 的逻辑
- 优化路由查找逻辑,支持多层级 children 路由匹配
- 添加 URL 参数处理函数 processJson 以适配查询字符串拼接
2025-12-11 15:26:13 +08:00
hz
5af2c7f6bf feat(underwriting): 新增字段选择组件并优化预核保流程
- 新增 FieldSelect 组件,支持证件类型和险种类型选择
- 在预核保数据收集页面集成 FieldSelect 组件
- 移除旧的字段选择弹窗逻辑
- 更新订单列表页面样式和结构
- 调整预核保容器背景色和提交按钮样式
- 添加 UNDERWRITING_STATUS 和 FIELD_SELECT_TYPE 常量定义
- 移除冗余的权限检查和短信验证相关代码
- 简化订单列表展示逻辑
- 优化组件导入和注册方式
2025-12-11 15:26:13 +08:00
hz
b8c99ffed1 feat(ebiz): 新增预核保订单列表功能
- 添加预核保路由配置及引入相关组件
- 实现订单列表展示、搜索、分页加载功能
- 支持未审核和已审核订单的标签切换
- 实现订单详情查看、编辑、删除操作
- 添加撤单、重新支付、修改卡号等功能按钮
- 集成短信验证码验证撤单流程
- 实现获取投保资料并发送至邮箱功能
- 添加权限校验及无权限提示页面
- 优化订单状态展示及双录提示逻辑
2025-12-11 15:25:26 +08:00
勾通
5859857ddc 【FIX】修改输入框校验规则,取消提交置灰更换为提示
(cherry picked from commit 370be6db5d)
2025-12-10 15:39:36 +08:00
hz
ea2b8984d2 Merge branch 'feature/FCRS-1048投保流程优化需求' into dev-new 2025-12-05 16:22:07 +08:00
hz
8f1f3c13fc feat(sale): 新增新产品流程判断逻辑
- 添加新产品流程标识判断
- 根据标识跳转至新投保告知页面或原有页面
- 更新附件管理页面跳转逻辑
- 优化订单状态为37时的页面跳转条件判断
2025-12-05 16:21:50 +08:00
xuxinxiang
ed565ffb80 Merge branch 'feature/FCRS-1048投保流程优化需求' into dev-new 2025-12-05 13:59:40 +08:00
xuxinxiang
b8ea811ad6 修改签名后页面样式问题 2025-12-05 13:58:14 +08:00
xuxinxiang
dfb9546422 Merge branch 'feature/FCRS-1048投保流程优化需求' into dev-new 2025-12-05 11:16:44 +08:00
xuxinxiang
0edabc67cf 抄录提交和签字确定回跳页面 2025-12-05 11:15:31 +08:00
xuxinxiang
5ca5d913d4 Merge branch 'feature/FCRS-1048投保流程优化需求' into dev-new 2025-12-05 09:43:23 +08:00
xuxinxiang
46ae4f590d 修改文件为已阅读状态后,二次阅读页面的展示问题。修改投被不同人被保人无法阅读文件的问题 2025-12-05 09:42:05 +08:00
24 changed files with 1279 additions and 54 deletions

View File

@@ -0,0 +1,11 @@
import request from '@/assets/js/utils/request'
import getUrl from '@/assets/js/utils/get-url'
// 获取培训系统中心页面
export function list(data) {
return request({
url: getUrl('/sale/orderPreUW/orderPreUWList', 1),
method: 'get',
data
})
}

View File

@@ -1,13 +1,17 @@
/* eslint-disable no-undef */
import router from '@/router'
export default function jump(options) {
// eslint-disable
if (window.WebViewJavascriptBridge && options.flag) {
if (options.flag == 'h5' ||
options.flag == 'service' ||
options.flag == 'home' ||
options.flag == 'mine' ||
options.flag == 'message' ||
options.flag == 'setting' ) {
if (
options.flag == 'h5' ||
options.flag == 'service' ||
options.flag == 'home' ||
options.flag == 'mine' ||
options.flag == 'message' ||
options.flag == 'setting'
) {
EWebBridge.webCallAppInJs('bridge', {
flag: options.flag,
extra: options.extra
@@ -23,11 +27,11 @@ export default function jump(options) {
} else {
// 1:replace 2:go 默认为push
if (options.routerInfo && options.routerInfo.type == '1') {
this.$router.replace(options.routerInfo)
router.replace(options.routerInfo)
} else if (options.routerInfo && options.routerInfo.type == '2') {
this.$router.go(options.routerInfo.index || -1)
router.go(options.routerInfo.index || -1)
} else if (options.routerInfo) {
this.$router.push(options.routerInfo)
router.push(options.routerInfo)
}
}
}

View File

@@ -148,6 +148,20 @@
flex-direction: column;
}
.column {
flex-direction: column;
}
.row {
flex-direction: row;
}
@for $i from 1 through 300 {
.gap-#{$i} {
gap: #{$i * 1px};
}
}
// 字体颜色
.green {
color: $green !important;
@@ -156,9 +170,11 @@
.red {
color: $red !important;
}
.red1 {
color: $red1 !important;
}
.yellow {
color: $yellow !important;
}
@@ -182,8 +198,9 @@
.black {
color: $black !important;
}
.ffcb6b{
color:#ffcb6b;
.ffcb6b {
color: #ffcb6b;
}
// 常用文字颜色
@@ -266,9 +283,11 @@
.bg-red {
background: $red !important;
}
.bg-red1 {
background: $red1 !important;
}
.bg-green-base {
background: #ddf2ef !important;
}
@@ -335,12 +354,15 @@
.van-checkbox__icon .van-icon {
border-color: #e9332e;
}
.van-checkbox__label {
color: #e9332e;
}
.van-radio__icon .van-icon {
border-color: #e9332e;
}
.van-radio__label {
color: #e9332e;
}
@@ -350,12 +372,15 @@
.van-checkbox__icon .van-icon {
border-color: #2E4591;
}
.van-checkbox__label {
color: #2E4591;
}
.van-radio__icon .van-icon {
border-color: #2E4591;
}
.van-radio__label {
color: #2E4591;
}

View File

@@ -16,38 +16,21 @@
</div>
</template>
<script>
import { SEX } from '@/views/ebiz/underwriting/js/const'
import { RadioGroup, Radio } from 'vant'
export default {
name: 'select-radio',
props: {
value: {
type: String | Boolean,
default: '0'
},
label: {
type: String,
default: ''
default: SEX.male
},
color: {
type: String,
default: ''
},
type: {
type: String,
default: 'danger'
},
radios: {
type: Array,
default: []
},
required: {
type: Boolean,
default: true
},
disabled: {
type: Boolean,
default: false
}
label: { type: String, default: '' },
color: { type: String, default: '' },
type: { type: String, default: 'danger' },
radios: { type: Array, default: () => [] },
required: { type: Boolean, default: true },
disabled: { type: Boolean, default: false }
},
data() {
return {}
@@ -76,7 +59,6 @@ export default {
}
}
</script>
<style lang="scss"></style>
<style lang="scss" scoped>
.sex-radio {
.radio-area {

View File

@@ -58,6 +58,7 @@ import saleFlowImprove from './saleFlowImprove'
// // 产品流程优化
import {improveProduct as productFlowImprove} from './productFlowImprove'
import { underwriting } from '@/router/ebiz/underwriting'
export default [
...proposal,
...sale,
@@ -99,5 +100,7 @@ export default [
...GBC,
...healthInsuranceRenewal,
...saleFlowImprove,
...productFlowImprove
...productFlowImprove,
// 预核保信息
...underwriting
] //根据需要进行删减

View File

@@ -0,0 +1,37 @@
/**
* 预核保路由信息
* @type {import("vue-router").RouteRecord[]}
* */
export const underwriting = [
{
path: '/underwriting',
// 布局组件
component: () => import('@/views/ebiz/underwriting/PreliminaryUnderwritingContainer.vue'),
children: [
{
path: 'list',
name: 'UnderwritingOrderList',
meta: { title: '预核保订单列表' },
component: () => import('@/views/ebiz/underwriting/OrderList.vue')
},
{
path: 'data-collection',
name: 'UnderwritingDataCollection',
meta: { title: '信息录入' },
component: () => import('@/views/ebiz/underwriting/UnderwritingDataCollection.vue')
},
{
path: 'supplementary-information',
name: 'UnderwritingSupplementaryInformation',
meta: { title: '补传资料' },
component: () => import('@/views/ebiz/underwriting/SupplementaryInformation.vue')
},
{
path: 'result',
name: 'UnderwritingResult',
meta: { title: '预核保结果' },
component: () => import('@/views/ebiz/underwriting/Result.vue')
}
]
}
]

View File

@@ -583,7 +583,8 @@ export default {
localStorage.orderNo = orderNo
localStorage.isFrom = 'sale'
localStorage.removeItem('changeCard')
// 是否进入新流程的标识
const recordedNewProd = order.orderInfoDTO.newSaleFlag === '0'
if (orderStatus == '01') {
//已签名待客户确认, 跳到签名确认页面
url = '/sale/SignatureConfirmation?edit=1&orderNo=' + orderNo
@@ -597,11 +598,11 @@ export default {
//被保险人保存成功, 跳到已选产品列表
url = processURL('/common/selectedProduct?edit=1&orderNo=' + orderNo)
} else if (orderStatus == '37') {
//受益人保存成功, 跳到告知信息--
url = '/sale/NotifyingMessage?edit=1&orderNo=' + orderNo
// 新流程的新产品检测, 如果是新产品,则跳转到新投保告知页面
url = (recordedNewProd ? '/flow-improve/sale/insureNotifyReplenishImag?orderNo=' : '/sale/newProduct?edit=1&orderNo=') + orderNo
} else if (orderStatus == '38') {
//账户信息保存成功, 跳到附件管理--
if (order.orderInfoDTO.activeType == 'KMH') {
if (order.orderInfoDTO.activeType === 'KMH') {
let params = {
orderNo: order.orderInfoDTO.orderNo
}
@@ -628,7 +629,7 @@ export default {
}
})
} else {
url = '/sale/AttachmentManagement?edit=1&orderNo=' + orderNo
url = processURL('/sale/AttachmentManagement?edit=1&orderNo=' + orderNo)
}
} else if (orderStatus == '39') {
//险种信息保存成功, 跳到已选产品列表

View File

@@ -92,14 +92,14 @@
class="budget-input"
>
</template>
<div class="fs12 c-gray-base">请填写0-10000之间的整数</div>
<div class="fs12 c-gray-base">请填写大于等于0的整数或保留两位小数点的非整数</div>
</p>
</div>
</div>
</div>
</div>
<div class="question-btn bottom-btn bg-white">
<van-button type="danger" size="large" :disabled="disabled" class="bottom-btn" @click="submitAnswer" v-no-more-click="1000">提交</van-button>
<van-button type="danger" size="large" class="bottom-btn" @click="submitAnswer" v-no-more-click="1000">提交</van-button>
</div>
<PopupQuesResult
@reTestHandler="reTest"
@@ -641,6 +641,9 @@ export default {
this.getDetailInfo()
},
submitAnswer(){
if( this.disabled ){
return this.$toast('请填写完整信息')
}
if(this.answerList.length !=this.questionList.length){
return this.$toast('请填写完整信息')
@@ -661,10 +664,10 @@ export default {
}
}
let regex = /^(0|[1-9]\d{0,3}|10000)$/;
let regex =/^(?!0\d)(?!.*\.$)(0|[1-9]\d{0,14})(?:\.\d{1,2})?$/;
if(Array.from(this.answerList[3]).some(item=>{return !regex.test(item)})
|| !regex.test(this.answerList[4][0])){
return this.$toast('请填写0-10000之间的整数')
return this.$toast('请填写大于等于0的整数或保留两位小数点的非整数')
}
this.checkDataChanged()
this.getEvaluateResult()

View File

@@ -1219,6 +1219,8 @@ export default {
}).then(() => {
this.isNoWXInsureReadClick(val)
})
} else {
this.isNoWXInsureReadClick(val)
}
// 若被保险人为未成年人时,非微信环境点击【立即阅读签名】按钮进行弹窗提示 结束
}

View File

@@ -12,7 +12,7 @@
<!--文件导航 结束-->
<!-- 顶部提示 开始 -->
<p v-if='branchType != "14"' class="xxx-notice-bar" ref="xxx2">文件滑动至底部完成阅读</p>
<p v-if='branchType != "14" && !isPreview' class="xxx-notice-bar" ref="xxx2">文件滑动至底部完成阅读</p>
<!-- 顶部提示 结束 -->
<!-- 一键置底 开始 -->
@@ -22,14 +22,14 @@
<!-- 一键置底 结束 -->
<!--文件内容 开始-->
<div id="pdf" ref="scrollableContent" @scroll="updateScrollProgress">
<div id="pdf" :class="[isPreview ? 'active' : '']" ref="scrollableContent" @scroll="updateScrollProgress">
<div ref="xxx8" id="pdfH5ID"></div>
<div ref="activeButtonEle"></div>
</div>
<!--文件内容 结束-->
<!--底部提示和按钮 开始-->
<div class="xxx-bottom">
<div class="xxx-bottom" v-if="!isPreview">
<p class="xxx-isread" ref="xxx3">
本人已阅读确认理解并同意<span>{{documentName}}</span>各项内容
</p>
@@ -47,7 +47,7 @@
</div>
<!--底部提示和按钮 结束-->
<!--关怀标准切换 开始-->
<oldVersionSwitch @onFloatBtnClicked="onFloatBtnClicked" />
<oldVersionSwitch v-if="!isPreview" @onFloatBtnClicked="onFloatBtnClicked" />
<!--关怀标准切换 结束-->
</div>
</template>
@@ -117,6 +117,7 @@
})
}, 100)
window.appCallBack = this.appCallBack
console.log(this.isPreview)
},
mounted() {
document.body.style.backgroundColor = '#fff'
@@ -288,6 +289,35 @@
]
}
}
/**
* @Author: LiuXiaoFeng
* @Description: documentCode为2 代表的是人身保险电子投保单
* 人身保险电子投保单文件需要抄录 保存时修改入参数据 copyValue是抄录的base64字符串 signOrRead需要变更为sign
* @Date: 2023/11/20
**/
if(this.fileList[this.current - 1].documentCode == '2' && this.signVal == '2' && (this.productType == '1' || this.productType == '2')){
window.localStorage.setItem('chaoluSubmitObj',JSON.stringify({
orderType: 'SIGN_COPY',
orderDTO: {
orderInfoDTO: {
orderNo: this.$route.query.orderNo
},
ebizSignDTOS: [
{
copyValue: '',
caType: 'copy',
signOrRead: 'sign',
signId: this.fileList[this.current - 1].signId,
orderNo: this.$route.query.orderNo,
documentCode: this.fileList[this.current - 1].documentCode,
documentStatus: '1',
documentType: this.fileList[this.current - 1].documentType,
signType: window.localStorage.getItem('sign-val')
}
]
}
}))
}
saveInformation(params).then(res=>{
if(res.result == 0) {
window.sessionStorage.removeItem('currentFile')
@@ -674,6 +704,10 @@
height: calc(100vh - 170px) !important;
position: relative;
top: 66px;
&.active{
height: 100vh !important;
top: 0;
}
//
}
.xxx-bottom{
@@ -711,6 +745,10 @@
#pdf{
height: calc(100vh - 240px) !important;
top: 84px;
&.active{
height: 100vh;
top: 0;
}
}
.xxx-isread{
font-size: 18px;

View File

@@ -1,6 +1,7 @@
<template>
<div class='insuranceInformation-container' :class="[isGuanhuaiBiaozhun === 0 ? '' : 'active']">
<div style="display: flex;align-items: center;font-weight: bold;" class="fs20 div1">
<div class="big-box">
<div style="display: flex;align-items: center;font-weight: bold;" class="fs20 div1">
<span class="span1" style="width: 3px;background: red;height: 25px;"></span>
<span v-if="signVal == '0' || signVal == '2'" style="margin-left: 5px;">
投保人{{signName}}
@@ -56,6 +57,7 @@
<div>
<img v-if="signstatus" :src="signImgUrl" style="height: 34px;margin-left: 20px;width: auto;"/>
</div>
</div>
<div class='bg-white bottom-btn'>
<van-button :disabled="nextDisabled" type='danger' size='normal' style="font-size: 16px;" block v-no-more-click='1000' @click="gonext">
完成阅读并签署
@@ -160,6 +162,9 @@
if(JSON.parse(window.sessionStorage.getItem('signH5Img')).type == '签名' && JSON.parse(window.sessionStorage.getItem('signH5Img')).val)
this.signstatus = true
this.signImgUrl = this.signImgUrl + JSON.parse(window.sessionStorage.getItem('signH5Img')).val
this.chaoLuObj.btnText = '抄录完成'
this.chaoLuObj.btnText1 = '已抄录'
}
if(window.sessionStorage.getItem('signH5Val')){
this.nextDisabled = false
@@ -231,12 +236,27 @@
signInfo.text = this.guardianName
}
window.sessionStorage.setItem('signInfo',JSON.stringify(signInfo))
window.location.href = this.$mainUrl + '/signH5/1.html'
window.location.href = this.$mainUrl + '/signH5/2.html'
// window.location.href = 'http://'+window.location.host + '/signH5/1.html'
}
},
gonext(){
// 抄录提交 开始
if ((this.signVal == '0' || this.signVal == '2') && (this.productType == '1' || this.productType == '2')) {
saveInformation(JSON.parse(window.localStorage.getItem('chaoluSubmitObj'))).then(res => {
if(res.result == '0') {
this.publicSubmit()
} else {
this.$toast(res.resultMessage)
}
})
} else {
this.publicSubmit()
}
// 抄录提交 结束
},
publicSubmit() {
let params = {
orderType: 'SIGN_MERGED_ORDER',
orderDTO: {
@@ -434,6 +454,9 @@
box-sizing: border-box;
width: 100%;
overflow: auto;
.big-box{
margin-bottom: 140px;
}
}
.insuranceInformation-container.active{
.div1{
@@ -459,7 +482,7 @@
}
}
.div4{
margin-bottom: 160px;
// margin-bottom: 160px;
span{
font-size: 20px !important;
}

View File

@@ -0,0 +1,132 @@
<script>
import { list } from '@/api/ebiz/underwriting'
import OrderListCard from '@/views/ebiz/underwriting/components/OrderList/OrderListCard.vue'
import { LIST_TYPE, ORDER_STATUS } from '@/views/ebiz/underwriting/js/const'
import { navigateRouter } from '@/views/ebiz/underwriting/js/navigate'
import { Search, Tabs, Tab, List, Sticky, Button } from 'vant'
export default {
name: 'saleList',
computed: {
ORDER_STATUS() {
return ORDER_STATUS
},
LIST_TYPE() {
return LIST_TYPE
}
},
components: {
OrderListCard,
Button,
VanSearch: Search,
VanTabs: Tabs,
VanTab: Tab,
VanList: List,
VanSticky: Sticky
},
data() {
this.fetchListData()
return {
showFlag: true,
searchText: '',
type: LIST_TYPE.unreviewed, //uncommit 表示未提交 commit表示已提交
saleList: [],
loading: false,
finished: false,
currentPage: 1, //当前页数
error: false,
finishedText: '没有更多了',
pageSize: 5, //每页数据条数
isSuccess: false,
types: [ORDER_STATUS.declined.value, ORDER_STATUS.postponed.value, ORDER_STATUS.standard.value, ORDER_STATUS.referred.value],
branchType: ''
}
},
methods: {
/**
* 初始化列表数据
* @returns {Promise<void>}
*/
async fetchListData() {
const params = {
pageNum: this.currentPage,
pageSize: this.pageSize,
orderType: this.type
}
const res = await list(params)
},
//分页用
loadMore() {
setTimeout(() => {
console.log(123)
this.loading = false
}, 1000)
},
tabChange(name) {
this.currentPage = 1
this.active = name
this.saleList = []
;[this.loading, this.finished] = [true, false]
this.finishedText = '正在加载...'
this.loadMore()
},
searchList() {
console.log(123)
this.currentPage = 1
this.saleList = []
;[this.loading, this.finished] = [true, false]
this.finishedText = '正在加载...'
this.loadMore()
},
//投保单详情
async goDetail() {},
//新增
navigateDataCollection(orderStatus) {
const pathName = orderStatus === ORDER_STATUS.referred.value ? 'UnderwritingSupplementaryInformation' : 'UnderwritingDataCollection'
navigateRouter({
name: pathName,
params: { orderStatus }
})
}
}
}
</script>
<template>
<div class="sale-list-container mb50">
<van-sticky>
<van-search v-model="searchText" placeholder="请输入投保人姓名" @search="searchList" />
<van-tabs v-model="type" :line-width="45" sticky @change="tabChange">
<van-tab :name="LIST_TYPE.unreviewed" title="未审核"></van-tab>
<van-tab :name="LIST_TYPE.reviewed" title="已审核"></van-tab>
</van-tabs>
</van-sticky>
<van-list
v-model="loading"
:error.sync="error"
:finished="finished"
:finished-text="finishedText"
:immediate-check="false"
error-text="请求失败点击重新加载"
@load="loadMore"
>
<OrderListCard
v-for="(_, index) in 10"
:key="index"
:type="type === LIST_TYPE.unreviewed ? ORDER_STATUS.pending.value : types[index % types.length]"
@click.native="navigateDataCollection(type === LIST_TYPE.unreviewed ? ORDER_STATUS.pending.value : types[index % types.length])"
></OrderListCard>
</van-list>
<Button v-no-more-click="1000" class="submit-btn bottom-btn mt20" type="danger" @click="navigateDataCollection()">点我新增</Button>
</div>
</template>
<style lang="scss" scoped>
.sale-list-container {
background-color: #f5f5f5;
}
</style>

View File

@@ -0,0 +1,26 @@
<template>
<div class="container">
<router-view />
</div>
</template>
<script>
export default {
name: 'PreliminaryUnderwritingContainer'
}
</script>
<style lang="scss" scoped>
.container {
width: 100vw;
height: 100vh;
background: #f5f5f5;
overflow-x: hidden;
::v-deep .submit-btn {
bottom: 10px;
width: 90%;
transform: translateX(5%);
}
}
</style>

View File

@@ -0,0 +1,43 @@
<script>
import { Image as VanImage, Button } from 'vant'
import { navigateRouter } from '@/views/ebiz/underwriting//js/navigate'
export default {
name: 'UnderwritingResult',
components: { Button, VanImage },
data() {
return {
errorMsg: '根据您的信息,不符合本产品承保要求',
image: require('@/assets/images/ebiz/evalImg.png')
}
},
methods: {
handleNavigateToList() {
navigateRouter({ name: 'UnderwritingOrderList', title: '预核保订单列表' })
}
}
}
</script>
<template>
<div class="underwriting-result-container flex justify-content-c align-items-c">
<div class="error-msg flex column align-items-c">
<VanImage :src="image" alt="error-result-image" width="80%" />
<div class="mt40">{{ errorMsg }}</div>
</div>
<Button class="submit-btn bottom-btn mt20" color="#E9322E" @click="handleNavigateToList">确定</Button>
</div>
</template>
<style lang="scss" scoped>
.underwriting-result-container {
background-color: #ffffff;
margin-top: 0 !important;
height: 100vh;
width: 100vw;
overflow: hidden;
.error-msg {
height: fit-content;
}
}
</style>

View File

@@ -0,0 +1,49 @@
<script>
import UnderwritingDataCollection from '@/views/ebiz/underwriting/UnderwritingDataCollection.vue'
import { Button, Sticky } from 'vant'
export default {
name: 'supplementaryInformation',
components: { UnderwritingDataCollection, Button, Sticky },
data() {
return {
/**
* 媒体信息列表
* @type {MediaDTO[]}
*/
mediaDTOS: []
}
}
}
</script>
<template>
<div class="supplementary-document-container ph20">
<Sticky>
<div aria-live="polite" class="alert alert-warning" role="alert">
请您补充关于处方明细的影像资料
</div>
</Sticky>
<UnderwritingDataCollection class="supplementary-document-content" />
<Button block class="submit-btn bottom-btn mt20" color="#E9322E">提交</Button>
</div>
</template>
<style lang="scss" scoped>
.supplementary-document-container {
background-color: #fff;
height: 100vh;
.alert {
padding: 10px 20px;
margin: 0 -20px;
}
.alert-warning {
background-color: #fef2d3;
}
.supplementary-document-content {
height: unset !important;
width: unset !important;
padding: unset !important;
}
}
</style>

View File

@@ -0,0 +1,160 @@
<script>
/**
* mediaDTO 媒体信息列表
* @typedef {Object} MediaDTO 媒体文件类型
* @property {string} businessNo - 业务编号
* @property {string} businessType - 业务类型(可能为空)
* @property {string} fileName - 文件名
* @property {string} imageInfoType - 图片信息类型("png" 等字符串形式)
* @property {string} rgssUrl - 加密或编码后的文件 URL可能经过 Base64 或其他编码)
* @property {string} subBusinessType - 子业务类型(如 "0"
*/
import filter from '@/filters'
import { getRelatedData } from '@/views/ebiz/agentEenter/js/methods'
import FieldSelect from '@/views/ebiz/underwriting/components/FieldSelect.vue'
import { FIELD_SELECT_TYPE, ORDER_STATUS, SEX } from '@/views/ebiz/underwriting/js/const'
import { navigateRouter } from '@/views/ebiz/underwriting/js/navigate'
import GField from './components/GField.vue'
import { Button } from 'vant'
import SexRadio from '@/views/ebiz/underwriting/components/SexRadio.vue'
import FieldPicker from '@/views/ebiz/underwriting/components/FieldPicker.vue'
import UnderwritingCollectionMedias from '@/views/ebiz/underwriting/components/UnderwritingCollection/UnderwritingCollectionMedias.vue'
export default {
name: 'CollectionUnderwritingData',
components: { FieldSelect, GField, SexRadio, FieldPicker, UnderwritingCollectionMedias, Button },
computed: {
FIELD_SELECT_TYPE: () => FIELD_SELECT_TYPE,
_idType: {
get: () => filter.idToText(this.data.user.idType, 'insuredIdType'),
set(val) {
// 如何 id 类型 改变,则清空 id 值
this.idNo = void 0
this.data.user.idType = val
}
},
executeBtn() {
const orderStatus = this.$route.query.orderStatus
const isShow = orderStatus !== ORDER_STATUS.pending.value
switch (orderStatus) {
case ORDER_STATUS.standard.value:
return {
label: '去投保',
fn: () => console.log('去投保'),
isShow
}
case ORDER_STATUS.declined.value:
case ORDER_STATUS.postponed.value: {
return {
label: '查看结果',
fn: () => console.log('查看结果'),
isShow
}
}
}
return {
label: '提交预核保',
fn: this.handleDataSubmit,
isShow
}
}
},
data() {
return {
// 险种名称
riskName: '',
// 用户相关信息
user: {
name: '',
idType: '',
idNo: '',
sex: SEX.male,
birthday: '',
riskCode: ''
},
/**
* 媒体信息列表
* @type {MediaDTO[]}
*/
mediaDTOS: []
}
},
methods: {
getRelatedData,
handleDataSubmit() {
this.navigateToList()
},
navigateToList() {
navigateRouter({
name: 'UnderwritingOrderList'
})
}
}
}
</script>
<template>
<div class="underwriting-data-collection-container ph15">
<h4 class="pt20">基本信息</h4>
<div>
<FieldSelect
v-validate="'required'"
:type="FIELD_SELECT_TYPE.riskType"
:value.sync="user.idType"
label="险种名称"
name="险种名称"
placeholder="请选择"
readonly
required
right-icon="arrow"
/>
<GField v-validate="'required'" :value="user.name" label="姓名" />
<SexRadio :sex.sync="user.sex" :value="user.sex"></SexRadio>
<FieldPicker
ref="birthday"
v-validate="'required'"
:flag="true"
:maxDate="new Date()"
:value.sync="user.birthday"
label="出生日期"
name="出生日期"
required
type="date"
@confirm="onDateConfirm($event, '2')"
/>
<FieldSelect
v-validate="'required'"
:type="FIELD_SELECT_TYPE.idType"
:value.sync="user.idType"
label="证件类型"
name="证件类型"
placeholder="请选择"
readonly
required
right-icon="arrow"
/>
<GField
v-model="user.idNo"
v-validate="'required'"
clearable
label="证件号码"
maxlength="18"
name="证件号码"
placeholder="手工录入或点击右侧证件扫描"
required
@blur="getRelatedData(user.idNo)"
/>
</div>
<UnderwritingCollectionMedias :files="mediaDTOS" class="medias" />
<Button v-if="executeBtn.isShow" block class="bottom-btn submit-btn mt20" color="#E9322E" @click="executeBtn.fn">{{ executeBtn.label }}</Button>
</div>
</template>
<style>
.underwriting-data-collection-container {
height: 100vh;
background-color: #fff;
}
</style>

View File

@@ -0,0 +1,197 @@
<template>
<div class="date-picter mb1" id="date-picter">
<div v-if="defaultStyle == 'style02'" @click="DatePickerShow(flag)" class="fs12 red ml15">
<span v-if="value">{{ value }} </span>
<span v-else>{{ placeholder }} </span>
<img src="@/assets/images/u79.png" alt class="absolute mt5 ml5" />
</div>
<div v-else-if="defaultStyle == 'style03'" @click="DatePickerShow(flag)" class="fs12 mr15">
<span v-if="value">{{ value }} </span>
<span v-else>{{ placeholder }} </span>
<img src="@/assets/images/u80.png" alt class="absolute ml5" />
</div>
<van-field
v-else
readonly
clickable
:label="label"
:value="date"
:placeholder="placeholder"
@click="DatePickerShow(flag)"
:required="required"
:name="label"
right-icon="arrow"
/>
<van-popup v-model="showDataPicker" position="bottom" style="z-index9999">
<van-datetime-picker :type="type" v-model="data" @confirm="onConfirmDate" @cancel="cancel" :max-date="maxDate" :min-date="minDate" />
</van-popup>
</div>
</template>
<script>
import { Field, Popup, DatetimePicker } from 'vant'
import utils from '@/assets/js/business-common'
import beforeDate from '@/assets/js/utils/getBeforeDate.js'
export default {
name: 'FieldDatePicter',
props: {
// defaultStyle :style02 时,显示红字红三角的样式,不写的话展示默认样式
defaultStyle: {
type: String,
default: ''
},
isDefault: {
type: Boolean,
default: false
},
maxDate: {
type: Date,
default: () => {
return beforeDate.getBeforeYear(-20)
}
},
minDate: {
type: Date,
default: () => {
return new Date('1900-01-01')
}
},
disabled: {
type: Boolean,
default: false
},
label: {
type: String,
default: '11'
},
value: {
type: String,
default: ''
},
placeholder: {
type: String,
default: '请选择'
},
type: {
type: String,
default: 'date'
},
defaultDate: {
type: Date,
default: () => {
return new Date()
}
},
defaulTime: {
type: String,
default: ''
},
required: {
type: Boolean,
default: false
},
flag: {
type: Boolean,
default: false
},
readonly: {
type: Boolean,
default: false
}
},
data() {
return {
currentDate: beforeDate.getBeforeYear(30), //当前时间的30年前
showDataPicker: false,
data: '', //时间插件绑定的值
date: '' //field显示的值
//minDate: new Date('1900-01-01') //因为VANT组件默认是十年前
}
},
components: {
[DatetimePicker.name]: DatetimePicker,
[Field.name]: () => import('@/views/ebiz/underwriting/components/GField.vue'),
[Popup.name]: Popup
},
created() {
// this.DatePickerShow(this.flag)
this.init()
// setTimeout(() => {
// this.showDataPicker = false
// }, 0)
},
mounted() {},
watch: {
value() {
this.init()
}
},
methods: {
/**
* 初始化
*/
init() {
if (this.type == 'time') {
this.data = this.value
} else if (this.value) {
this.data = new Date(this.value)
} else {
this.data = this.currentDate
}
if (this.required) {
this.rules = 'required'
}
if (this.isDefault) {
this.data = beforeDate.getBeforeYear(0)
}
this.date = this.value
},
onConfirmDate(value) {
let result = ''
if (this.type == 'time') {
result = value
} else {
let dateType = {
date: 'yyyy-MM-dd',
datetime: 'yyyy-MM-dd HH:mm:ss',
'year-month': 'yyyy-MM',
time: 'mm:ss'
}
result = utils.formatDate(value, dateType[this.type])
}
this.$emit('update:value', result)
this.date = result
this.$emit('confirm', result)
this.showDataPicker = false
},
cancel() {
this.showDataPicker = false
this.$emit('cancel', '')
},
DatePickerShow(flag) {
if (this.readonly || this.disabled) {
return
}
this.showDataPicker = flag
this.$emit('showUp', flag)
}
}
}
</script>
<style lang="scss" scoped>
.nav-bar {
.van-nav-bar {
&__text {
color: white;
}
&__title {
color: white;
}
}
.van-icon {
color: white;
}
}
</style>

View File

@@ -0,0 +1,50 @@
<script>
import DataDictionary from '@/assets/js/utils/data-dictionary'
import GField from '@/views/ebiz/underwriting/components/GField.vue'
import { FIELD_SELECT_TYPE } from '@/views/ebiz/underwriting/js/const'
import { Popup, Picker } from 'vant'
export default {
name: 'FieldSelect',
components: { GField, Popup, Picker },
props: {
type: { type: Number, default: FIELD_SELECT_TYPE.other }
},
data() {
return {
show: false
}
},
computed: {
// eslint-disable-next-line vue/return-in-computed-property
columns() {
if (this.type === FIELD_SELECT_TYPE.other) return ['没有对应列表内容,请检查类型']
switch (Number(this.type)) {
case FIELD_SELECT_TYPE.idType:
return DataDictionary.insuredIdType
case FIELD_SELECT_TYPE.riskType:
return [1, 2, 3]
}
}
},
methods: {
handlePopupSubmit(value) {
if (this.type === FIELD_SELECT_TYPE.other) return
this.$emit('submit', value)``
}
}
}
</script>
<template>
<div class="field-select-container">
<GField v-bind="$attrs" @click="show = true" />
<Popup v-model="show" position="bottom">
<Picker :columns="columns" show-toolbar @cancel="show = false" @confirm="handlePopupSubmit" />
</Popup>
</div>
</template>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,44 @@
<script>
import { Field } from 'vant'
export default {
components: { Field },
inheritAttrs: false,
name: 'GField',
props: {
feetLine: { type: Boolean, default: true },
hairLine: { type: Boolean, default: true },
required: { type: Boolean, default: true },
label: { type: String, default: '' },
value: { type: String, default: '' },
placeholder: { type: String, default: '' },
type: { type: String, default: 'text' },
clearable: { type: Boolean, default: true },
password: { type: Boolean, default: false },
errorMessage: { type: String, default: '' },
error: { type: Boolean, default: false }
}
}
</script>
<template>
<div class="g-field">
<Field ref="field" class="hairline" required v-bind="{ ...$attrs, ...$props }" v-on="$listeners">
<template v-for="(_, slot) of $slots" v-slot:[slot]>
<slot :name="slot" />
</template>
</Field>
</div>
</template>
<style lang="scss" scoped>
::v-deep .van-field {
&.hairline::after {
border-bottom: 1px solid #f5f5f5 !important;
content: ' ';
width: 90%;
position: absolute;
left: 3%;
bottom: 0;
}
}
</style>

View File

@@ -0,0 +1,140 @@
<script>
import filter from '@/filters'
import { ORDER_STATUS } from '@/views/ebiz/underwriting/js/const'
import { navigateRouter } from '@/views/ebiz/underwriting/js/navigate'
import { Button, Tag } from 'vant'
export default {
name: 'OrderListCard',
components: { Tag, Button },
props: {
info: {
type: Object,
default: () => {
return {
orderNo: '8186270000130107',
name: '王飞',
sex: '1',
birthday: null,
idType: '1',
idNo: '522530199208180048',
riskCode: 'GFRS_M0109',
riskName: '国富人寿富贵年年终身寿险 ',
orderStatus: ORDER_STATUS.declined.value,
createdDate: '2029-07-09 10:05:05'
}
}
},
type: { type: String, default: '' }
},
computed: {
idType: {
get() {
return filter.idToText(this.info.idType, 'insuredIdType')
}
},
orderStatus() {
const status = Object.values(ORDER_STATUS).find(item => item.value === /* this.info.orderStatus */ this.type)
if (!status) {
console.error('未定义的订单状态, 请重新检查相应代码内容')
return
}
return status.label
},
executeBtn() {
const status = this./* info.orderStatus */ type
const date = this.info.createdDate
return {
label: status === ORDER_STATUS.standard.value ? '去投保' : status === ORDER_STATUS.referred.value ? '补充资料' : '查看结果',
pathname:
status === ORDER_STATUS.standard.value
? 'UnderwritingDataCollection'
: status === ORDER_STATUS.referred.value
? 'UnderwritingSupplementaryInformation'
: 'UnderwritingResult',
// 如果这个是标准单且距今超过一个月了,则禁用当前按钮
disabled: status === ORDER_STATUS.standard.value && new Date() - new Date(date) > 2592000000
}
}
},
methods: {
handleNavigate() {
navigateRouter({
name: this.executeBtn.pathname,
params: {
orderId: this.info.orderId
}
})
}
}
}
</script>
<template>
<div class="order-list-card-container mt10 fs12 mh10">
<section class="flex align-items-c">
<div>
<Tag plain type="danger">险种</Tag>
<span>{{ info.riskName }}</span>
</div>
<div>{{ info.createdDate }}</div>
</section>
<section class="hairline">
<div>
<Tag plain type="warning">被保</Tag> <span>{{ info.name }}</span>
</div>
<div>
<Tag plain type="danger">类型</Tag> <span>{{ idType }}</span>
</div>
<div>
<Tag plain type="danger">证号</Tag> <span>{{ info.idNo }}</span>
</div>
</section>
<section class="hairline">
<div>
<Tag plain type="danger"><span class="mh15">状态</span></Tag> <span>{{ orderStatus }}</span>
</div>
</section>
<section class="hairline">
<div class="flex" style="flex-direction: row-reverse">
<Button
:class="{ disable: executeBtn.disabled }"
:disabled="executeBtn.disabled"
class="delete-btn ph30 pv5 mb10"
color="#FF1113"
round
@click="handleNavigate(executeBtn.pathname)"
>
{{ executeBtn.label }}
</Button>
</div>
</section>
</div>
</template>
<style lang="scss" scoped>
.order-list-card-container {
background-color: #fff;
border-radius: 6px;
.hairline {
border-top: 1px solid #eaeaea;
}
section {
div {
margin: 10px;
}
::v-deep .van-tag {
margin-right: 10px;
}
}
.delete-btn {
height: unset;
line-height: unset;
}
button.disable {
background-color: grey !important;
border-color: grey !important;
}
}
</style>

View File

@@ -0,0 +1,64 @@
<script>
import { SEX } from '@/views/ebiz/underwriting/js/const.js'
import SelectRadio from '@/components/ebiz/SelectRadio.vue'
export default {
components: { SelectRadio },
name: 'SexRadio',
props: {
required: { type: Boolean, default: true },
sex: { type: String, default: SEX.male },
hairline: { type: Boolean, default: true }
},
data() {
return {
sexRadio: [
{ label: '男', value: '0' },
{ label: '女', value: '1' }
]
}
},
computed: {
_sex: {
get() {
return this.sex
},
set(value) {
this.$emit('update:sex', value)
this.$emit('changeSex', value)
}
}
},
methods: {
radioChange: value => this.$emit('changeSex', value)
}
}
</script>
<template>
<div class="sex-radio-container">
<SelectRadio
:class="{ hairline }"
:radios="sexRadio"
:required="required"
:value.sync="_sex"
label="性别"
name="性别"
@radio-change="radioChange"
></SelectRadio>
</div>
</template>
<style lang="scss">
.sex-radio-container {
.hairline {
&::after {
border-bottom: 1px solid #f5f5f5 !important;
content: ' ';
width: 90%;
position: absolute;
left: 3%;
//bottom: 10px;
}
}
}
</style>

View File

@@ -0,0 +1,24 @@
<script>
import { Uploader } from 'vant'
export default {
name: 'UnderwritingCollectionMedias',
components: { Uploader },
props: {
isSupplementary: { type: Boolean, default: false },
medias: { type: Array, default: () => [] }
}
}
</script>
<template>
<div class="underwriting-collection-medias-container mt10">
<h4>影像信息</h4>
<article class="mh15 mt15 flex column gap-20">
<p>
请您上传关于当前疾病的所有影像资料如等费用收据门诊病历出院小结处方明细等......
</p>
<Uploader v-model="medias"></Uploader>
</article>
</div>
</template>

View File

@@ -0,0 +1,85 @@
export const SEX = Object.freeze({ male: '0', female: '1' })
export const UNDERWRITING_STATUS = Object.freeze({
standard: 'standard',
referred: 'referred',
declined: 'declined',
postponed: 'postponed'
})
export const FIELD_SELECT_TYPE = Object.freeze({
other: 0,
idType: 1,
riskType: 2
})
export const ORDER_STATUS = Object.freeze({
/**
* 待核保
*/
pending: {
value: 'pending',
label: '待核保'
},
/* 标准体 */
standard: {
value: 'standard',
label: '标准体'
},
/* 问题件 */
referred: {
value: 'referred',
label: '问题件'
},
/* 拒保 */
declined: {
value: 'declined',
label: '拒保'
},
/* 延期 */
postponed: {
value: 'postponed',
label: '延期'
}
})
export const LIST_TYPE = {
unreviewed: 'unreviewed',
reviewed: 'reviewed'
}
// export const cardSide = Object.freeze({
// front: 1,
// back: 0
// })
// export const user = Object.freeze({
// applicant: '0',
// insured: '1',
// beneficiary: '3'
// })
// // user 别名
// export const subBusinessType = user
//
// export const OcrDocumentType = Object.freeze({
// ID_CARD_FRONT: 1, // 身份证头像面
// ID_CARD_BACK: 2, // 身份证国徽面
// BANK_CARD: 3, // 银行卡正面影像
// FACE_PHOTO: 4, // 人脸影像
// HOUSEHOLD_REGISTER_HEAD: 5, // 户口本户主页
// HOUSEHOLD_REGISTER_SELF: 6, // 户口本本人页
// BIRTH_CERTIFICATE: 7, // 出生证
// PASSPORT: 8, // 护照本人照片页
// HK_MACAO_PERMIT: 9, // 港澳居民通行证头像面
// TAIWAN_PERMIT: 10, // 台湾居民通行证头像面
// OTHER: 11 // 其他
// })
// // OcrDocumentType 别名
// export const imageType = OcrDocumentType
//
// /**受益人类型*/
// export const beneficiaryType = Object.freeze({
// /** 法人 */
// legalPerson: '0',
// /** 指定受益人 */
// designateBeneficiary: '1'
// })

View File

@@ -0,0 +1,82 @@
import jump from '@/assets/js/utils/jump'
import router from '@/router'
/**
* 路由跳转函数
* @param path { String}路径
* @param name
* @param title {String} 标题内容
* @param params { Object}参数
*/
export function navigateRouter({ path = '', name = '', title = '', params = {} } = {}) {
const routes = router.options.routes
let options = {
flag: 'h5',
extra: {
title,
// forbidSwipeBack: 1, //当前页面禁止右滑返回
url: ''
},
routerInfo: { query: params }
}
if (path) {
const route = routes.find(item => {
if (item.children) {
// 可能后面 children 存在多个,目前暂时先判定一层。 待优化
return item.children.find(childItem => `${item.path}/${childItem.path}` === path)
}
return item.path === path
})
if (!route) {
console.error('在 routers 中无法找到该路径,请检查路径是否正确')
return
}
options.extra = {
title: route.meta && route.meta.title ? route.meta.title : '',
url: location.origin + '/#' + path + '?' + processJson(params)
}
options.routerInfo = { ...options.routerInfo, path }
} else if (name) {
const route = routes.find(item => {
if (item.children) {
// 可能后面 children 存在多个,目前暂时先判定一层。 待优化
return item.children.find(childItem => childItem.name === name)
}
return item.name === name
})
if (!route) {
console.error('在 routers 中无法找到该路径,请检查路径是否正确')
return
}
// 如何存在子元素, 需要重新更改 path 地址
if (route.children) {
route.path = `${route.path}/${route.children.find(childItem => childItem.name === name).path}`
}
console.log(route)
options.extra = {
title: route.meta && route.meta.title ? route.meta.title : '',
url: location.origin + '/#' + route.path + '?' + processJson(params)
}
options.routerInfo = { ...options.routerInfo, name }
}
jump(options)
}
/**
* 处理 json 键值对,到 url 中
*/
function processJson(json) {
if (typeof json !== 'object') return
if (window.URLSearchParams) return new URLSearchParams(json).toString()
let url = ''
for (const key in json) {
if (json.hasOwnProperty(key)) {
url += key + '=' + json[key] + '&'
}
}
return url.substring(0, url.length - 1)
}