Commit ec282208 authored by Step_by_step's avatar Step_by_step

feat: 融资计算器接口联调

parent e0ce61ef
NODE_ENV='development'
VITE_DEBUG='true'
VITE_HTTP_HEADER={}
VITE_HTTP_BASE_URL='http://192.168.101.1:28080'
VITE_HTTP_BASE_URL='http://139.196.201.22:8080/leaf_dev'
VITE_HTTP_NOAUTH_BASE_URL=''
VITE_Alipay='https://test-api-open.chinaums.com/v1/netpay/trade/h5-pay'
VITE_Wechat='https://test-api-open.chinaums.com/v1/netpay/wxpay/h5-pay'
......
NODE_ENV='production'
VITE_DEBUG='false'
VITE_HTTP_HEADER={}
VITE_HTTP_BASE_URL='https://yw.fshsfl.com/api'
VITE_HTTP_BASE_URL='http://139.196.201.22:8080/leaf_dev'
VITE_HTTP_NOAUTH_BASE_URL=''
VITE_Alipay='https://api-mop.chinaums.com/v1/netpay/trade/h5-pay'
VITE_Wechat='https://api-mop.chinaums.com/v1/netpay/wxpay/h5-pay'
......
NODE_ENV=production
VITE_DEBUG='true'
VITE_HTTP_HEADER={}
VITE_HTTP_BASE_URL='http://192.168.101.1:8001'
VITE_HTTP_BASE_URL='http://139.196.201.22:8080/leaf_dev'
VITE_HTTP_NOAUTH_BASE_URL=''
VITE_Alipay='https://test-api-open.chinaums.com/v1/netpay/trade/h5-pay'
VITE_Wechat='https://test-api-open.chinaums.com/v1/netpay/wxpay/h5-pay'
......
......@@ -14,6 +14,11 @@
<meta name="viewport"
content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=cover">
<title>Vite App</title>
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
// VConsole will be exported to `window.VConsole` by default.
var vConsole = new window.VConsole();
</script>
</head>
<body>
......
......@@ -17,7 +17,7 @@
"dependencies": {
"@hips/plugin-vue-jssdk": "^1.1.2",
"@vant/area-data": "^1.3.2",
"axios": "^1.2.0",
"axios": "^0.27.2",
"crypto-js": "^4.1.1",
"moment": "^2.29.4",
"pinia": "^2.0.23",
......
......@@ -30,12 +30,10 @@ export default {
};
},
async mounted() {
console.log('此处未报错 app.vue');
const TempAccess = import.meta.env.DEV ? '60243b9f-5f2b-41f8-8007-67984b5ae1cd' : ''
localStorage.setItem('userId', 5)
const TempAccess = import.meta.env.DEV ? 'cb3939fa-8c0a-447d-8842-2d17d7b4555b' : ''
let homePage = { path: "applications", replace: true }
console.log('此处未报错 app.vue2');
if (import.meta.env.PROD) {
// 得保证 每个子应用中首页有 title
let jumRouter;
......
......@@ -37,6 +37,11 @@
:placeholder="placeHolder(item.value, 'input', item)" type="digit" :label="item.title" input-align="right"
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" />
<!-- 复选框组 -->
<Field v-else-if="item.type === 'checkGroup'"
:class="[item.validateRes ? 'fail' : '', item.isRequired ? 'required' : '']" :label="item.title"
......
<template>
<div class="container">
<NavBar :title="navTitle" left-arrow @click-left="goBack" :right-text="navText" @click-right="onClickRight"/>
<NavBar :title="navTitle" left-arrow @click-left="onLeftClick" :right-text="navText" @click-right="onClickRight"/>
<div class="content">
<header>
<div class="header-top">
......@@ -42,7 +42,7 @@
import { goBack } from "@/utils/globalFun"
import { NavBar } from "vant";
const emit = defineEmits(['onClickRight'])
const emit = defineEmits(['onClickRight', 'leftClick'])
defineProps({
navTitle: {
type: String,
......@@ -60,13 +60,17 @@ defineProps({
fristCurrency: '0.00',
times: '0'
}
}
},
})
const onClickRight = () => {
emit('onClickRight');
}
const onLeftClick = () => {
emit('leftClick')
}
</script>
<style lang="less" scoped>
......@@ -121,22 +125,25 @@ header {
border-top-left-radius: 20px;
border-top-right-radius: 20px;
box-sizing: border-box;
padding: 20px 5px 5px 20px;
padding: 10px 5px 5px 20px;
color: black;
overflow: hidden;
p {
font-size: 17px;
margin-bottom: 3px;
}
}
.plan-list {
flex: 1 0 auto;
height: 99%;
height:80%;
}
.plan-footer {
flex: 0 1 50px;
display: flex;
justify-content: center;
}
.header-top {
......
......@@ -13,9 +13,6 @@ moment.locale('zh-cn')
// }
const app = createApp(App)
console.log('此处未报错 main.js');
app.use(createPinia())
app.use(router)
app.use(Notify)
......
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}
// )
// },
// 首页查询
financingCalculator(data = {}) {
return post(`${baseURL}/app/api/app/financing/calculator`, data)
},
planInfo(data = {}) {
return post(`${baseURL}/app/api/app/repayment/schedule`, data)
},
getFormInfo(data = {}) {
return post(`${baseURL}/app/api/app/trial/file`, data)
},
saveInfo(data = {}) {
return post(`${baseURL}/app/api/app/trial/save`, { userId: window.localStorage.getItem('userId'), ...data })
},
deleteInfo(data = {}) {
return post(`${baseURL}/app/api/app/financing/remove`, data)
}
};
export default api;
......@@ -2,13 +2,8 @@ import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const usePlan = defineStore('plan', () => {
const planData = ref({})
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
return { planData }
})
<template>
<div class="container">
<NavBar title="试算详情" left-arrow @click-left="goBack" />
<NavBar title="试算详情" left-arrow @click-left="backLast" />
<Notice detail="如需使用其他测算方式可联系当地代理店"></Notice>
......@@ -9,7 +9,7 @@
<AuForm v-model="formConfig" :key="form" />
</section>
<section class="sub">
<section class="sub" v-show="!isComplete">
<Button type="primary" block @click="computing">计算</Button>
</section>
</div>
......@@ -23,73 +23,174 @@ import Notice from "@/components/Notice.vue"
import AuForm from '@/components/AuForm.vue';
import AuFormClass from "@/components/useAuForm";
import { useRouter } from "vue-router";
import moment from 'moment'
import { usePlan } from '../store/index'
import { onActivated } from "vue";
import api from "../api";
const form = $ref();
const formConfig = $ref(new AuFormClass([
const formConfigArr = [
{
title: '物件价格',
propName: 'price',
propName: 'priceOfItem',
type: 'currency',
value: '',
isRequired: true,
},
{
title: '还款方式',
propName: 'payType',
type: 'cell',
propName: 'repayment',
type: 'input',
value: '均等还款',
isRequired: true,
select: [],
disabled: true
},
{
title: '融资租期(月)',
propName: 'leaseTermMonth',
propName: 'term',
type: 'digit',
value: '',
isRequired: true
},
{
title: '首付款比例',
propName: 'firstPaymentRatio',
propName: 'firstGoldRatio',
type: 'rate',
value: '',
value: '0',
isRequired: true
},
{
title: '首付租金日',
propName: 'firstPaymentDate',
propName: 'firstRentDay',
type: 'cell',
value: '',
isRequired: true,
select: 'date'
},
]))
{
title: '合同利率',
propName: 'contractRate',
type: 'rate',
value: '6.00',
isRequired: true,
},
]
const planStore = usePlan()
const router = useRouter();
const form = $ref();
const formConfig = $ref(new AuFormClass(formConfigArr))
const isComplete = $ref(false)
const updateForm = async () => {
const testQuotationId = planStore.planData?.testQuotationId;
isComplete = planStore.planData?.isComplete;
if (testQuotationId) {
let res = await api.getFormInfo({ testQuotationId: '' + testQuotationId })
if (res.result === 'SUCCESS') {
let resData = res.data[0];
formConfig.replace(formConfigArr.map(item => {
item.value = resData[item.propName] || '';
if (item.propName === "firstGoldRatio") {
item.value = resData[item.propName] * 100 || 0;
} else if (item.propName === "firstRentDay") {
item.value = resData[item.propName].split(' ')[0] || '';
} else if (item.propName === "contractRate") {
item.value = resData[item.propName] * 100 || '';
}
item.disabled = true;
return item;
}))
}
} else {
formConfig.replace(formConfigArr.map(item => {
if (item.propName !== "repayment") {
item.disabled = false;
}
return item;
}))
}
}
onActivated(() => {
updateForm()
})
const checkValues = (values) => {
if (+values.price <= 0) {
if (+values.priceOfItem <= 0) {
Toast({ message: '请输入正确的物件价格', position: 'top' });
return false;
} else if (values.firstPaymentRatio > 1) {
} else if (values.firstGoldRatio > 1 || isNaN(+values.firstGoldRatio)) {
Toast({ message: '请输入正确首付款比例', position: 'top' });
return false;
} else if (isNaN(+values.contractRate)) {
Toast({ message: '请输入正确的合同利率', position: 'top' });
return false;
}
return true;
}
const router = useRouter();
const getResult = (formValueObj) => {
let { priceOfItem, term, firstGoldRatio, firstRentDay, contractRate } = formValueObj
let firstGold = priceOfItem * firstGoldRatio;
let financeAmount = priceOfItem - firstGold;
let monthRate = contractRate / 12;
let temp = (1 + monthRate);
let cashflow = []
for (let i = 1; i <= term; i++) {
// 每月应还利息,每月应还本金,月供, 还款日
let interest = (financeAmount * monthRate * (temp ** term - temp ** (i - 1)) / (temp ** term - 1)).toFixed(2)
let principal = (financeAmount * monthRate * temp ** (i - 1) / (temp ** term - 1)).toFixed(2)
let dueAmount = (financeAmount * monthRate * temp ** term / (temp ** term - 1)).toFixed(2)
let chargeDueDate = moment(firstRentDay).add(i, 'month').format('YYYY-MM-DD')
cashflow.push({ interest, principal, dueAmount, times: i + "", chargeDueDate })
}
let formValueObjTemp = {}
for (const key in formValueObj) {
if (key === 'firstRentDay') {
formValueObjTemp[key] = moment(formValueObj[key]).format('YYYY-MM-DD')
} else {
formValueObjTemp[key] = '' + formValueObj[key]
}
}
planStore.planData = {
firstGold: firstGold.toFixed(2) + "",
financeAmount: financeAmount.toFixed(2) + "",
cashflow,
...formValueObjTemp
}
console.log({ firstGold, financeAmount, cashflow })
}
const computing = () => {
if (formConfig.validate()) {
let formValueObj = formConfig.getValues()
let validateValue = checkValues(formValueObj)
if (validateValue) {
try {
formValueObj.priceOfItem = +formValueObj.priceOfItem;
getResult(formValueObj)
router.push({ name: 'calculator-views-plan' })
} catch (error) {
console.error(error)
Toast.fail('计算失败失败');
}
}
} else {
Toast({ message: '请将信息填写完整', position: 'top' });
}
}
const backLast = () => {
planStore.planData = {}
goBack()
}
</script>
<style lang="less" scoped>
......@@ -107,3 +208,11 @@ const computing = () => {
overflow-y: auto;
}
</style>
<route>
{
meta: {
keepAlive: true
}
}
</route>
\ No newline at end of file
......@@ -3,51 +3,69 @@
<NavBar title="融资计算器" left-arrow @click-left="goBack" />
<section class="list">
<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" @clickRight="clickRight" isSwiper />
<PullRefresh v-model="refreshing" @refresh="getList" class="scroll">
<List :finished="finished" finished-text="没有更多了">
<Empty v-if="!listValue.length" description="无计算记录" />
<ListItem v-else v-for="item in listValue" :title="item.title" :values="item.values"
@click="jump(item.testQuotationId)" @clickRight="clickRight(item.testQuotationId)" isSwiper />
</List>
</PullRefresh>
</section>
<Plus @click="jump" />
<Plus @click="jump()" />
</div>
</template>
<script setup>
import { goBack } from "@/utils/globalFun"
import { NavBar, List, PullRefresh } from "vant";
import { NavBar, List, PullRefresh, Empty, Toast } from "vant";
import ListItem from '@/components/ListItem.vue'
import Plus from '@/components/Plus.vue'
import api from "../api";
import { useRouter } from "vue-router";
import { usePlan } from '../store/index'
const listValue = $ref([
{
title: '2022-07-18',
other_title: "2022-07-18",
values: [["物件价格", "融资金额", "每期还款"], ["¥ 20,000.00", "¥ 20,000.00", "¥ 20,000.00"]]
}
])
const planStore = usePlan()
const refreshing = $ref(true);
const finished = $ref(true);
const listValue = $ref([])
const onRefresh = () => {
// 下拉重置参数,重新加载
};
const loadApproval = () => {
let getList = async () => {
let list = await api.financingCalculator({ userId: window.localStorage.getItem('userId') })
if (list.data && list.data.length) {
list = list.data.map(item =>
({ ...item, title: item.creationDate.split(' ')[0], values: [["物件价格", "融资金额", "每期还款"], [`¥ ${item.priceOfItem}`, `¥ ${item.financeAmount}`, `¥ ${item.dueAmount}`]] })
)
} else {
list = []
}
listValue = list.reverse()
refreshing = false;
}
getList()
const router = useRouter();
const jump = () => {
const jump = async (testQuatationId) => {
if (testQuatationId) {
let res = await api.planInfo({ testQuatationId: testQuatationId + '' })
if (res.result === 'SUCCESS') {
res.data.cashflow = res.data.cashflow.map(item => { item.chargeDueDate = item.chargeDueDate.split(" ")[0]; return item })
planStore.planData = { ...res.data, isComplete: true };
router.push({ name: 'calculator-views-plan' })
} else {
Toast.fail('获取不到计算数据');
}
} else {
router.push({ name: 'calculator-views-detail' })
}
}
const clickRight = () => { console.log(333) }
const clickRight = async (testQuatationId) => {
await api.deleteInfo({ testQuatationId: testQuatationId + '' })
listValue = [];
await getList()
}
</script>
<style scoped lang="less">
......
<template>
<RepayPlan navTitle="还款计划" :currencyObj="currencyObj">
<RepayPlan navTitle="还款计划" :currencyObj="currencyObj" @leftClick="backToLast">
<template #list>
<div class="repay_list">
<div class="repay_list" v-if="currencyObj.times && plan.financeAmount">
<div class="left">
<div class="times-box" v-for="i in 12">
<div class="times-box" v-for="i in currencyObj.times">
<div class="times">{{ i }}</div>
</div>
</div>
<div class="right">
<div class="item" v-for="i in 12">
<div class="item" v-for="item in plan.cashflow">
<div class="item-head">
<span>租金</span>
<span>10,000.00</span>
<span>2022-06-20</span>
<span>{{ item.dueAmount }}</span>
<span>{{ item.chargeDueDate }}</span>
</div>
<div class="item-foot">
<span>本金</span>
<span>10,000.00</span>
<span>{{ item.principal }}</span>
<span>利息</span>
<span>10,000.00</span>
<span>{{ item.interest }}</span>
</div>
</div>
</div>
</div>
</template>
<template #footer>
<Button size="small" type="primary" style="width: 150px;" @click="save">试算完成</Button>
</template>
</RepayPlan>
</template>
<script setup>
import RepayPlan from '@/components/RepayPlan.vue'
import { Button, Toast } from "vant";
import { onActivated } from 'vue';
import api from "../api";
import { usePlan } from '../store/index'
import { useRouter } from "vue-router";
const currencyObj = $ref({})
const plan = $ref({})
const isComplete = $ref(false)
const getPlan = () => {
const planStore = usePlan()
plan = planStore.planData;
isComplete = plan.isComplete;
currencyObj = {
financingCurrency: plan.financeAmount,
headerCurrency: plan.firstGold,
fristCurrency: plan.cashflow[0].dueAmount,
times: plan.cashflow.length
}
}
const currencyObj = $ref({
financingCurrency: '200,000.00',
headerCurrency: '5,000.00',
fristCurrency: '10,000.00',
times: '12'
onActivated(() => {
getPlan()
})
const router = useRouter();
const save = async () => {
if (isComplete) {
const planStore = usePlan()
planStore.planData = {};
return router.push({ name: 'calculator-views' })
}
let res = await api.saveInfo(plan)
if (res.result == "SUCCESS") {
router.push({ name: 'calculator-views' })
Toast.success('保存成功');
} else {
Toast.fail('保存失败');
}
}
const backToLast = () => {
router.replace({ name: 'calculator-views-detail' })
}
</script>
<style lang="less" scoped>
......@@ -49,11 +89,13 @@ const currencyObj = $ref({
width: 100%;
height: 98%;
overflow-y: auto;
scrollbar-width: none; /* firefox */
scrollbar-width: none;
/* firefox */
}
.repay_list::-webkit-scrollbar {
display: none; /* Chrome Safari */
display: none;
/* Chrome Safari */
}
@leftW: 45px;
......@@ -139,3 +181,11 @@ const currencyObj = $ref({
}
}
</style>
<route>
{
meta: {
keepAlive: true
}
}
</route>
\ No newline at end of file
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}
// )
// },
// 首页查询
getlist(data = {}) {
return post(`${baseURL}/app/api/app/financing/intention/query`, { userId: window.localStorage.getItem('userId'), ...data })
},
getDetail(data = {}) {
return post(`${baseURL}/app/api/app/financing/intention/file`, data)
},
};
export default api;
......@@ -42,7 +42,10 @@ const setRouteConfig = (VITE_TARGET) => {
// https://vitejs.dev/config/
export default ({ mode }) => {
let VITE_TARGET = loadEnv(mode, process.cwd()).VITE_TARGET;
let VITE_HTTP_BASE_URL = loadEnv(mode, process.cwd()).VITE_HTTP_BASE_URL;
return {
base: './',
plugins: [
vue({ reactivityTransform: true }),
Pages(setRouteConfig(VITE_TARGET)),
......@@ -70,7 +73,15 @@ export default ({ mode }) => {
},
server: {
host: '0.0.0.0',
port: 5678
port: 5678,
cors: true,
proxy: {
'/api': {
target: VITE_HTTP_BASE_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/\/api/, '')
}
}
},
build: {
minify: 'terser',
......@@ -81,7 +92,6 @@ export default ({ mode }) => {
},
},
outDir: `dist/${VITE_TARGET}`,
base: './'
}
}
}
......@@ -268,14 +268,13 @@ available-typed-arrays@^1.0.5:
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
axios@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.0.tgz#1cb65bd75162c70e9f8d118a905126c4a201d383"
integrity sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==
axios@^0.27.2:
version "0.27.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
dependencies:
follow-redirects "^1.15.0"
follow-redirects "^1.14.9"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
braces@^3.0.2:
version "3.0.2"
......@@ -640,7 +639,7 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
follow-redirects@^1.15.0:
follow-redirects@^1.14.9:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
......@@ -1145,11 +1144,6 @@ postcss@>=5.0.2, postcss@^8.1.10, postcss@^8.4.18:
picocolors "^1.0.0"
source-map-js "^1.0.2"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
prr@~1.0.1:
version "1.0.1"
resolved "http://nexus.saas.hand-china.com/repository/hzero-npm-group/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
......
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