Commit 91132dd3 authored by WangRui's avatar WangRui
parents 6da225d1 26f6927d
......@@ -22,30 +22,30 @@ npm run preview 本地预览打包完的文件
---
> ✋ :计划人天数; ✅ :页面已画完
> ✋ :计划人天数; ✅ :页面已联调
### 子应用
1. 融资计算器 ✅ (纯前端,等额本息) ( ✋.5)
2. 融资意向 ✅✋.5)
3. 项目查询 ✅ (✋ 1.5 项目信息明细页面需要重头画)
4. 合同签约 (E签宝, 预览签署文件, 由消息推送后做)✅✋ 2)
5. 还款计划 ✅(支付, 只能从上到下支付)(✋ 2)
6. 还款计划查询 (发票预览下载)✅✋ 2)
7. 自助服务 ✅ ✋.5)
8. 客户信息变更 ✅✋ 1)
1. 融资计算器 (纯前端,等额本息) ( ✋.5) ✅
2. 融资意向 ✋.5) ✅
3. 项目查询 (✋ 1.5 项目信息明细页面需要重头画) ✅
4. 合同签约 (E签宝, 预览签署文件, 由消息推送后做)✋ 2) ✅
5. 还款计划 (支付, 只能从上到下支付)(✋ 2)
6. 还款计划查询 (发票预览下载) ✋ 2)
7. 自助服务 ( ✋.5)
8. 客户信息变更 (✋ 1)
#### 首页
1. 还款情况展示 (✋ 2)
2. 最近消息展板 (✋ 2)
3. 定位 (只作用于 **融资意向** 选择代理店的时候使用)?
3. 定位 (只作用于 **融资意向** 选择代理店的时候使用)?✅
#### 我的
1. 我的奖励券 ✅✋ 1)
2. 关联项目 ✅ (验证码)(✋ 1.5)
3. 投诉与建议 ✅✋ .5)
4. 关于我们 ✅ ✋ .5)
1. 我的奖励券 (✋ 1)
2. 关联项目 (验证码)(✋ 1.5) ✅
3. 投诉与建议 ✋ .5) ✅
4. 关于我们 (✋ .5)
### 其他
......
......@@ -12,7 +12,8 @@
"build:complaint": "VITE_TARGET=complaint vite build --mode test",
"build:infoChange": "VITE_TARGET=infoChange vite build --mode test",
"build:relatedProject": "VITE_TARGET=relatedProject vite build --mode test",
"build:repayPlanCheck": "VITE_TARGET=repayPlanCheck vite build --mode test"
"build:repayPlanCheck": "VITE_TARGET=repayPlanCheck vite build --mode test",
"build:contractSign": "VITE_TARGET=contractSign vite build --mode test"
},
"dependencies": {
"@hips/plugin-vue-jssdk": "^1.1.2",
......
......@@ -21,6 +21,7 @@
<script>
import login from '@/utils/login'
import { post } from '@/utils/http'
export default {
name: "App",
......@@ -30,10 +31,14 @@ export default {
};
},
async mounted() {
localStorage.setItem('userId', 5)
const TempAccess = import.meta.env.DEV ? 'cb3939fa-8c0a-447d-8842-2d17d7b4555b' : ''
localStorage.setItem('userId', 5) // 可删
localStorage.setItem('phone', "18388105084")
const TempAccess = import.meta.env.DEV ? 'bcec5d6e-6d4e-4cff-abcc-b5a92ad6b58b' : ''
if (import.meta.env.DEV) window.localStorage.setItem('access_token', TempAccess) // 可删
let homePage = { path: "applications", replace: true }
this.getBusinessToken()
if (import.meta.env.PROD) {
// 得保证 每个子应用中首页有 title
let jumRouter;
......@@ -59,6 +64,17 @@ export default {
}
this.$router.push(homePage);
},
methods: {
async getBusinessToken() {
const baseURL = import.meta.env.DEV ? '/api' : import.meta.env.VITE_HTTP_BASE_URL
let res = await post(`${baseURL}/app/api/app/user/token/get`, {
mobilePhone: window.localStorage.getItem('phone'), loginType: 'HLS_LOGIN', hlsUserId: window.localStorage.getItem('userId'), receivets: + new Date() + ''
})
if (res.success && res.total > 0) {
window.localStorage.setItem('_token', res.rows[0]?.token)
}
}
}
};
</script>
......
......@@ -11,17 +11,18 @@
<Field v-else-if="item.type === 'input'" v-model="modelValue.value[index].value" :class="getClass(item)"
:placeholder="placeHolder(item.value, 'input', item)" :label="item.title" input-align="right"
:readonly="item.disabled" :right-icon="item.disabled ? '' : item.rightIconName"
@click-right-icon="item.rightIconCb" />
@focus="fieldOnFocus(modelValue.value[index])" @click-right-icon="item.rightIconCb" />
<!-- 金钱字符串input -->
<Field v-else-if="item.type === 'currency'" v-model="modelValue.value[index].value" :class="getClass(item)"
:placeholder="placeHolder(item.value, 'input', item)" :label="item.title" input-align="right"
:formatter="currencyFormatter" format-trigger="onBlur" :readonly="item.disabled" />
@focus="fieldOnFocus(modelValue.value[index])" :formatter="currencyFormatter" format-trigger="onBlur"
:readonly="item.disabled" />
<!-- 百分比字符串input -->
<Field v-else-if="item.type === 'rate'" v-model="modelValue.value[index].value" :class="getClass(item)"
:placeholder="placeHolder(item.value, 'input', item)" :label="item.title" input-align="right"
:readonly="item.disabled">
@focus="fieldOnFocus(modelValue.value[index])" :readonly="item.disabled">
<template #extra> %
</template>
</Field>
......@@ -29,18 +30,18 @@
<!-- textarea -->
<Field v-else-if="item.type === 'textarea'" v-model="modelValue.value[index].value" :class="getClass(item)"
:placeholder="placeHolder(item.value, 'input', item)" type="textarea" :label="item.title" input-align="right"
autosize :readonly="item.disabled">
@focus="fieldOnFocus(modelValue.value[index])" autosize :readonly="item.disabled">
</Field>
<!-- 整数 digit -->
<Field v-else-if="item.type === 'digit'" v-model.number="modelValue.value[index].value" :class="getClass(item)"
:placeholder="placeHolder(item.value, 'input', item)" type="digit" :label="item.title" input-align="right"
autosize :readonly="item.disabled" />
@focus="fieldOnFocus(modelValue.value[index])" autosize :readonly="item.disabled" />
<!-- 数字 digit -->
<Field v-else-if="item.type === 'number'" v-model.number="modelValue.value[index].value" :class="getClass(item)"
:placeholder="placeHolder(item.value, 'input', item)" type="number" :label="item.title" input-align="right"
autosize :readonly="item.disabled" />
@focus="fieldOnFocus(modelValue.value[index])" autosize :readonly="item.disabled" />
<!-- 复选框组 -->
<Field v-else-if="item.type === 'checkGroup'"
......@@ -81,7 +82,7 @@
<script setup>
import { Popup, Picker, Cell, Field, DatetimePicker, CheckboxGroup, Checkbox, Cascader, Area } from 'vant';
import { currency, toPercent } from '@/utils/textFormat'
import { ref } from "vue";
import { ref, unref } from "vue";
// {
// value: '',
......@@ -194,6 +195,12 @@ function typeOnCancel() {
typeShowArea.value = false;
}
function fieldOnFocus(obj) {
if (!obj.value || +obj.value == 0) {
obj.value = ''
}
}
function getClass(item) {
return [item.validateRes ? 'fail' : '', item.isRequired ? 'required' : '']
}
......
import { get, put, post, File, deleteReq } from '@/utils/http'
import { post } from '@/utils/http'
const baseURL = import.meta.env.DEV ? '/api' : import.meta.env.VITE_HTTP_BASE_URL
const baseURL = import.meta.env.VITE_HTTP_BASE_URL
const api = {
// getMakingList (data) { // 查询合同制作列表
// return get(
// `${baseURL}/hlct/v1/0/contracts/toMake`,
// {...data}
// )
// },
// 首页查询
contextQuery(data = {}) {
return post(`${baseURL}/app/api/app/sign/context/query`, data)
},
// 业务经办保存
saveInfo(data = {}) {
return post(`${baseURL}/app/api/hls/payment/info/save`, data)
// {
// paymentReqId: '',
// oppositeFinancialContact: '对方财务联系人',
// contactInformation: '联系方式',
// receivets: ’时间戳‘
// }
}
};
export default api;
const state = {};
import { ref } from 'vue'
import { defineStore } from 'pinia'
export default {
namespaced: true,
state,
};
export const usePlan = defineStore('plan', () => {
const planData = ref({})
return { planData }
})
<template>
<div class="container">
<NavBar title="e签宝签约" left-arrow @click-left="toLastPage" />
<div class="content">
<iframe class="eiframe" :src="iframeUrl" frameborder="0"></iframe>
</div>
</div>
</template>
<script setup>
import { NavBar } from "vant";
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
let iframeUrl = $ref(route.query.url)
let toLastPage = () => {
router.replace({ name: 'contractSign-views' })
}
</script>
<style lang="less" scoped>
.content {
height: calc(100% - var(--van-nav-bar-height));
overflow: hidden;
}
.eiframe {
width: 100%;
height: 100%;
}
</style>
......@@ -3,15 +3,15 @@
<NavBar title="合同签约" left-arrow @click-left="goBack" />
<section class="list">
<Tabs v-model:active="active" title-active-color="#0277bc" color="#0277bc">
<Search v-model="value" placeholder="请输入合同编号/代理店" />
<Tabs v-model:active="active" title-active-color="#0277bc" color="#0277bc" @change="TabChange">
<Search v-model="searchValue" placeholder="请输入合同编号/代理店" />
<Tab title="未签署">
<Notice detail="只对已关联的项目发送合同签约链接" />
<PullRefresh v-model="refreshing" @refresh="onRefresh" class="scroll">
<List :finished="finished" finished-text="没有更多了" @load="loadApproval">
<SignListItem v-for="item in notList" :sdata="item" @click="jump" />
<SignListItem v-for="item in notList" :sdata="item" @click="eSign(item.signUrl)" />
</List>
</PullRefresh>
</Tab>
......@@ -19,7 +19,7 @@
<Tab title="已签署">
<PullRefresh v-model="refreshing" @refresh="onRefresh" class="scroll">
<List :finished="finished" finished-text="没有更多了" @load="loadApproval">
<SignListItem v-for="item in alreadyList" :sdata="item" @click="jump" />
<SignListItem v-for="item in alreadyList" :sdata="item" @click="pdfPre" />
</List>
</PullRefresh>
</Tab>
......@@ -30,58 +30,93 @@
<script setup>
import { goBack } from "@/utils/globalFun"
import { NavBar, List, PullRefresh, Tab, Tabs, Search } from "vant";
import { NavBar, List, PullRefresh, Tab, Tabs, Search, Toast } from "vant";
import Notice from '@/components/Notice.vue'
import SignListItem from '../components/SignListItem.vue'
import api from "../api";
import { useRouter } from "vue-router";
const notList = $ref([
{
isSign: false,
contractId: 'KFLC22022002',
dl: "河南焦作",
date: "2022-11-03"
},
{
isSign: false,
contractId: 'KFLC22022002',
dl: "河南焦作",
date: "2022-11-03"
},
])
const alreadyList = $ref([
{
isSign: true,
contractId: 'KFLC22022002',
dl: "河南焦作",
date: "2022-11-03"
},
{
isSign: true,
contractId: 'KFLC22022002',
dl: "河南焦作",
date: "2022-11-03"
},
])
const refreshing = $ref(true);
const finished = $ref(true);
const notList = $ref([])
const alreadyList = $ref([])
const active = $ref(0)
const searchValue = $ref('')
const refreshing = $ref(false);
const finished = $ref(false);
let pager = {
page: 1,
pageSize: 10
}
let queryList = async () => {
refreshing = true
let res = await api.contextQuery({
actionType: active,
agencyName: '',
receivets: + new Date() + '',
projectNumber: '',
...pager
})
if (res.success && res.total > 0) {
res.rows.forEach(item => {
let temp = {
...item,
isSign: false,
contractId: item.contractId,
dl: item.agencyName,
date: item.deadlineDate
}
if (active === 0) {
notList.push(temp)
} else {
alreadyList.push({ ...temp, isSign: true })
}
})
} else {
finished = true
// Toast.fail('获取列表失败!');
}
refreshing = false
}
const onRefresh = () => {
// 下拉重置参数,重新加载
if (active === 0) {
notList = []
} else {
alreadyList = []
}
finished = false
pager.page = 1;
pager.pageSize = 10;
queryList()
};
const loadApproval = () => {
let TabChange = () => {
if ((active === 0 && !notList.length) || (active === 1 && !alreadyList.length)) {
onRefresh()
}
}
const loadApproval = () => {
pager.page++
queryList()
}
const router = useRouter();
const jump = () => {
const pdfPre = () => {
router.push({ name: 'contractSign-views-pdfPre' })
}
let eSign = (eUrl) => {
router.push({ path: '/contractsign/views/esign', query: { url: eUrl } })
}
</script>
<style scoped lang="less">
......
import { get, put, post, File, deleteReq } from '@/utils/http'
import { post } from '@/utils/http'
const baseURL = import.meta.env.DEV ? '/api' : import.meta.env.VITE_HTTP_BASE_URL
const baseURL = import.meta.env.VITE_HTTP_BASE_URL
const api = {
// getMakingList (data) { // 查询合同制作列表
// return get(
// `${baseURL}/hlct/v1/0/contracts/toMake`,
// {...data}
// )
// },
// 首页查询
contractQueryAll(data = {}) {
return post(`${baseURL}/app/api/app/bind/contract/query/all`, data)
},
// 解绑
contractDelete(data = {}) {
return post(`${baseURL}/app/api/app/bind/contract/delete`, data)
},
// 查询指定项目信息
contractQuery(data = {}) {
return post(`${baseURL}/app/api/app/specify/contract/query`, data)
},
// 校验手机号并发送 验证码
phoneValid(data = {}) {
return post(`${baseURL}/app/api/app/specify/contract/phone/valid`, data)
},
// 校验验证码
codeValid(data = {}) {
return post(`${baseURL}/app/api/app/specify/contract/code/valid`, data)
},
// 绑定指定项目
contractBind(data = {}) {
return post(`${baseURL}/app/api/app/specify/contract/bind`, data)
},
};
export default api;
......@@ -8,7 +8,7 @@
<PullRefresh v-model="refreshing" @refresh="onRefresh" class="scroll">
<List :finished="finished" finished-text="没有更多了" @load="loadApproval">
<ListItem v-for="item in listValue" :other_title="item.other_title" :title="item.title" :values="item.values"
@click="jump" isSwiper>
isSwiper @clickRight="unbind(item.associationId)">
<template #rightTxt>
<span>取消解绑</span>
</template>
......@@ -24,35 +24,79 @@
<script setup>
import { goBack } from "@/utils/globalFun"
import Notice from "@/components/Notice.vue"
import { NavBar, List, PullRefresh } from "vant";
import { NavBar, List, PullRefresh, Dialog } from "vant";
import ListItem from '@/components/ListItem.vue'
import Plus from '@/components/Plus.vue'
import api from "../api";
import { useRouter } from "vue-router";
const listValue = $ref([
{
title: 'R00001',
other_title: "2022-07-18",
values: [["机型", "机号", "状态", "代理店"], ["HB205-1M0", "234333", "签约", '江苏宁隆']]
const listValue = $ref([])
let pager = { page: 1, pageSize: 10 }
const refreshing = $ref(false);
const finished = $ref(false);
const queryList = async () => {
refreshing = true
let res = await api.contractQueryAll({ receivets: + new Date() + '', ...pager })
if (res.success) {
pager.page++
if (res.total == 0) finished = true
res.rows.forEach(item => {
listValue.push({
title: item.contractId + '' || '',
other_title: item.acceptDate || '',
values: [["机型", "机号", "状态", "代理店"], [item.model, item.machineNumber, item.contractStatus, item.agencyName]],
...item
})
})
} else {
finished = true
}
])
const refreshing = $ref(true);
const finished = $ref(true);
refreshing = false
}
queryList()
const onRefresh = () => {
// 下拉重置参数,重新加载
listValue = [];
pager.page = 1;
pager.pageSize = 10;
finished = false
queryList()
};
const loadApproval = () => {
pager.page++;
queryList()
}
const router = useRouter();
const jump = () => {
router.push({ name: "relatedProject-views-relatedFlow" })
}
const unbind = async (associationId) => {
Dialog.confirm({
title: '确认取消绑定',
message: '',
})
.then(async () => {
// on confirm
let res = await api.contractDelete({
associationId: associationId + '',
receivets: +new Date() + '',
})
if (res.success) {
onRefresh()
}
})
.catch(() => {
// on cancel
});
}
</script>
<style scoped lang="less">
......@@ -68,7 +112,7 @@ const jump = () => {
.scroll {
box-sizing: border-box;
height: 100%;
overflow-y: auto;
}
</style>
......
......@@ -9,10 +9,12 @@
</template>
<template v-else-if="(step == 2)">
<Field v-model="tel" type="tel" label="手机号" input-align="right" />
<Field v-model="sms" center clearable label="验证码" placeholder="请输入短信验证码">
<Field v-model="secondFormConfig.tel" type="tel" label="手机号" placeholder="请输入手机号" input-align="right"
class="required" />
<Field v-model="secondFormConfig.sms" center clearable label="验证码" placeholder="请输入短信验证码" class="required">
<template #button>
<Button size="small" type="primary">获取验证码</Button>
<Button size="small" :disabled="buttonValues !== '获取验证码'" type="primary" @click="getSms">
{{ buttonValues }}</Button>
</template>
</Field>
</template>
......@@ -24,7 +26,7 @@
<section class="sub">
<Button v-if="(step < 3)" type="primary" block @click="nextStep">下一步</Button>
<Button v-else type="primary" block @click="nextStep">确认绑定</Button>
<Button v-else type="primary" block @click="submit">确认绑定</Button>
</section>
</div>
</div>
......@@ -32,108 +34,178 @@
<script setup>
import { goBack } from "@/utils/globalFun"
import { NavBar, Button, Field } from "vant";
import { NavBar, Button, Field, Toast } from "vant";
import AuForm from '@/components/AuForm.vue';
import AuFormClass from "@/components/useAuForm";
import { phoneReg } from '@/utils/Reg'
import api from "../api";
import { useRouter } from "vue-router";
const step = $ref(1);
const firstFormConfig = $ref(new AuFormClass([
{
title: '项目编号 ',
propName: 'paymentReqDate',
propName: 'contractNumber',
type: 'input',
value: '',
isRequired: true
},
{
title: '机型',
propName: 'nput',
propName: 'model',
type: 'input',
value: '',
isRequired: true
},
{
title: '机号',
propName: 'paymentReqDat3e',
propName: 'machineNumber',
type: 'input',
value: '',
isRequired: true
}
]))
const secondFormConfig = $ref(new AuFormClass([
{
title: '合同经办人手机号',
propName: 'paymentReqDate',
type: 'input',
value: '',
isRequired: true
},
{
title: '验证码',
propName: 'paymentReqDate',
type: 'input',
value: '',
isRequired: true
},
]))
const FirstFormValues = $ref({})
const secondFormConfig = $ref({})
const lastFormConfig = $ref(new AuFormClass([
{
title: '项目编号',
propName: 'paymentReqDate',
propName: 'contractId',
type: 'input',
value: '33333333333333',
value: '',
isRequired: true,
disabled: true
},
{
title: '机型',
propName: 'paymentReqDate',
propName: 'machineNumber',
type: 'input',
value: '洛基亚',
value: '',
isRequired: true,
disabled: true
},
{
title: '机号',
propName: 'paymentReqDate',
propName: 'machineNumber',
type: 'input',
value: '309937',
value: '',
isRequired: true,
disabled: true
},
{
title: '承租人名称',
propName: 'paymentReqDate',
propName: 'bpName',
type: 'input',
value: '张三',
value: '',
isRequired: true,
disabled: true
},
{
title: '代理店名称',
propName: 'paymentReqDate',
propName: 'agencyBpName',
type: 'input',
value: '将来发扽代理店',
value: '',
isRequired: true,
disabled: true
},
{
title: '融资金额',
propName: 'paymentReqDate',
propName: 'financeAmount',
type: 'currency',
value: '80000000',
value: '',
isRequired: true,
disabled: true
},
]))
const nextStep = () => {
if (step < 3) {
step += 1
let checkSms = async () => {
let res = await api.codeValid({
code: secondFormConfig.sms,
phone: secondFormConfig.tel,
receivets: + new Date() + ''
})
if (res.success && res.total > 0 && res.rows[0]) {
step++
return true
} else {
Toast.fail('信息校验失败')
return false
}
}
let checkPrj = async () => {
let res = await api.contractQuery({ ...firstFormConfig.getValues(), receivets: + new Date() + '' })
if (res.success && res.total > 0) {
lastFormConfig.fillValues(res.rows[0])
if(!res.rows[0].contractId) return false
shouldSaveContractId = res.rows[0].contractId
return true
} else {
Toast.fail('未找到该项目')
return false
}
}
let shouldSaveContractId = ''
let queryContractInfo = async () => {
let res = await api.contractQuery({
...firstFormConfig.getValues(),
receivets: +new Date() + ''
})
if (res.success && res.total > 0) {
lastFormConfig.fillValues(res.rows[0])
shouldSaveContractId = res.rows[0].contractId
} else {
Toast.fail('未找到该项目')
}
}
const nextStep = async () => {
console.log(step);
if (step === 1 && firstFormConfig.validate()) {
FirstFormValues = firstFormConfig.getValues()
if (await checkPrj()) {
step++
}else {
Toast.fail('未查询到该关联项目')
};
} else if (step === 2 && secondFormConfig.sms && secondFormConfig.tel) {
if (checkSms()) {
queryContractInfo()
}
} else {
Toast.fail('请将信息填写完整!')
}
}
const buttonValues = $ref('获取验证码')
const getSms = async () => {
if (phoneReg.test(secondFormConfig.tel)) {
let res = await api.phoneValid({
contractId: shouldSaveContractId,
phone: secondFormConfig.tel,
receivets: + new Date() + ''
})
if (res.success) {
CountDown()
}
} else {
Toast.fail("请将手机号输入正确")
}
}
const CountDown = (count = 60) => {
let clearCountDown = setInterval(() => {
let temp = (parseInt(buttonValues))
if (temp == 0) {
buttonValues = '获取验证码'
clearInterval(clearCountDown)
} else {
buttonValues = (isNaN(temp - 1) ? count : (temp - 1)) + 's'
}
}, 1000);
}
const lastStep = () => {
if (step > 1) {
......@@ -142,6 +214,24 @@ const lastStep = () => {
goBack()
}
}
const router = useRouter()
const submit = async () => {
let res = await api.contractBind({
contractId: shouldSaveContractId,
receivets: + new Date() + ''
})
if (res.success && res.total > 0 && res.rows[0]?.associationId) {
Toast.success('绑定成功')
setTimeout(() => {
goBack()
}, 1000);
} else {
Toast.fail('绑定失败,请稍后重试。')
}
}
</script>
<style lang="less" scoped>
......@@ -158,4 +248,9 @@ const lastStep = () => {
height: calc(100% - var(--van-nav-bar-height));
overflow-y: auto;
}
:deep(.required .van-cell__title::before) {
content: '*';
color: red;
}
</style>
\ No newline at end of file
<template>
<div class="container">
<NavBar title="还款计划查询" left-arrow @click-left="goBack" />
<NavBar title="还款计划" left-arrow @click-left="goBack" />
<section class="list">
<Search v-model="searchVal" placeholder="请输入项目编号/融资金额" />
......@@ -92,7 +92,7 @@ const jump = () => {
<route>
{
meta: {
title: '还款计划查询'
title: '还款计划'
}
}
</route>
......
......@@ -169,7 +169,7 @@ export function post(url, param = {}) {
method: 'post',
headers: headers,
url,
data: param,
data: {...param, _token: window.localStorage.getItem('_token')},
}).then((res) => {
resolve(res)
}).catch((err) => {
......
......@@ -14,6 +14,7 @@ export function bridgeLogin() {
pushExtra: object.pushExtra
}
window.localStorage.setItem('userId', object.userId)
window.localStorage.setItem('phone', object.loginAccount)
resolve(data)
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment