init
24
.editorconfig
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Editor configuration, see http://editorconfig.org
|
||||||
|
|
||||||
|
# 表示最顶层的 EditorConfig 配置文件
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*] # 表示所有文件适用
|
||||||
|
charset=utf-8 # 设置文件字符集为 utf-8
|
||||||
|
end_of_line=LF # 控制换行类型(lf | cr | crlf)
|
||||||
|
insert_final_newline=true# 始终在文件末尾插入一个新行
|
||||||
|
indent_style=space # 缩进风格(tab | space)
|
||||||
|
indent_size=2 # 缩进大小
|
||||||
|
max_line_length = 100
|
||||||
|
trim_trailing_whitespace = true # 去除行首的任意空白字符
|
||||||
|
|
||||||
|
[*.{yml,yaml,json}]# 表示仅 yml,yaml,json} 文件适用以下规则
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.md] # 表示仅 md 文件适用以下规则
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
max_line_length = off
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
77
.eslintrc.js
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// @ts-check
|
||||||
|
const { defineConfig } = require('eslint-define-config');
|
||||||
|
module.exports = defineConfig({
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
node: true,
|
||||||
|
es6: true,
|
||||||
|
},
|
||||||
|
parser: 'vue-eslint-parser',
|
||||||
|
parserOptions: {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
sourceType: 'module',
|
||||||
|
jsxPragma: 'React',
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/vue3-recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'prettier',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
"vue/multi-word-component-names":"off",
|
||||||
|
'vue/script-setup-uses-vars': 'error',
|
||||||
|
'@typescript-eslint/ban-ts-ignore': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'vue/custom-event-name-casing': 'off',
|
||||||
|
'no-use-before-define': 'off',
|
||||||
|
'@typescript-eslint/no-use-before-define': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'@typescript-eslint/ban-types': 'off',
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'space-before-function-paren': 'off',
|
||||||
|
|
||||||
|
'vue/attributes-order': 'off',
|
||||||
|
'vue/one-component-per-file': 'off',
|
||||||
|
'vue/html-closing-bracket-newline': 'off',
|
||||||
|
'vue/max-attributes-per-line': 'off',
|
||||||
|
'vue/multiline-html-element-content-newline': 'off',
|
||||||
|
'vue/singleline-html-element-content-newline': 'off',
|
||||||
|
'vue/attribute-hyphenation': 'off',
|
||||||
|
'vue/require-default-prop': 'off',
|
||||||
|
'vue/html-self-closing': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
html: {
|
||||||
|
void: 'always',
|
||||||
|
normal: 'never',
|
||||||
|
component: 'always',
|
||||||
|
},
|
||||||
|
svg: 'always',
|
||||||
|
math: 'always',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
26
.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
components.d.ts
|
||||||
4
.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"proseWrap": "never"
|
||||||
|
}
|
||||||
3
.stylelintignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/dist/*
|
||||||
|
/public/*
|
||||||
|
public/*
|
||||||
21
index.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"
|
||||||
|
/>
|
||||||
|
<meta name="format-detection" content="telephone=yes" />
|
||||||
|
<!-- 验证码程序依赖(必须)。请勿修改以下程序依赖,如通过其他手段规避加载,会导致验证码无法正常更新,对抗能力无法保证,甚至引起误拦截。 -->
|
||||||
|
<script src="https://ssl.captcha.qq.com/TCaptcha.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<title>loan</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
11558
package-lock.json
generated
Normal file
127
package.json
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
{
|
||||||
|
"name": "loan",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||||
|
"@vant/area-data": "^1.4.1",
|
||||||
|
"@vant/use": "^1.5.1",
|
||||||
|
"@vuemap/vue-amap": "^2.0.5",
|
||||||
|
"@vuemap/vue-amap-extra": "^2.0.1",
|
||||||
|
"@vuemap/vue-amap-loca": "^2.0.3",
|
||||||
|
"@vueup/vue-quill": "1.0.0",
|
||||||
|
"@vueuse/core": "^9.6.0",
|
||||||
|
"axios": "^1.3.4",
|
||||||
|
"blueimp-md5": "^2.19.0",
|
||||||
|
"date-fns": "^2.29.3",
|
||||||
|
"echarts": "^5.4.0",
|
||||||
|
"element-resize-detector": "^1.2.4",
|
||||||
|
"json-bigint": "^1.0.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"mitt": "^3.0.0",
|
||||||
|
"pinia": "^2.0.23",
|
||||||
|
"qrcode": "^1.5.1",
|
||||||
|
"qs": "^6.11.0",
|
||||||
|
"vant": "^4.8.0",
|
||||||
|
"vue": "^3.2.47",
|
||||||
|
"vue-i18n": "^9.2.2",
|
||||||
|
"vue-router": "^4.1.6",
|
||||||
|
"vue-types": "^4.2.1",
|
||||||
|
"vuedraggable": "^4.1.0",
|
||||||
|
"weixin-js-sdk": "^1.6.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^18.15.5",
|
||||||
|
"@vitejs/plugin-vue": "^4.1.0",
|
||||||
|
"fs-extra": "^11.0.0",
|
||||||
|
"postcss": "^8.3.8",
|
||||||
|
"postcss-loader": "^6.1.1",
|
||||||
|
"postcss-px-to-viewport-8-plugin": "^1.2.1",
|
||||||
|
"sass": "^1.59.3",
|
||||||
|
"typescript": "^4.9.3",
|
||||||
|
"unplugin-vue-components": "^0.24.1",
|
||||||
|
"vite": "^4.2.3",
|
||||||
|
"vue-router": "^4.1.6",
|
||||||
|
"vue-tsc": "^1.2.0",
|
||||||
|
"vue-types": "^4.2.1",
|
||||||
|
"@commitlint/cli": "^17.3.0",
|
||||||
|
"@commitlint/config-conventional": "^17.3.0",
|
||||||
|
"@eslint/create-config": "^0.4.1",
|
||||||
|
"@types/lodash": "^4.14.191",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.45.1",
|
||||||
|
"@typescript-eslint/parser": "^5.45.1",
|
||||||
|
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||||
|
"@vue/compiler-sfc": "^3.2.45",
|
||||||
|
"@vue/eslint-config-typescript": "^11.0.2",
|
||||||
|
"autoprefixer": "^10.4.13",
|
||||||
|
"commitizen": "^4.2.6",
|
||||||
|
"consola": "^2.15.3",
|
||||||
|
"core-js": "^3.26.1",
|
||||||
|
"crypto-js": "^4.1.1",
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
|
"eslint": "8.22.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"eslint-config-standard": "^17.0.0",
|
||||||
|
"eslint-define-config": "^1.12.0",
|
||||||
|
"eslint-plugin-import": "^2.26.0",
|
||||||
|
"eslint-plugin-jest": "^27.1.6",
|
||||||
|
"eslint-plugin-n": "^15.6.0",
|
||||||
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
|
"eslint-plugin-vue": "^9.8.0",
|
||||||
|
"esno": "^0.16.3",
|
||||||
|
"gh-pages": "^4.0.0",
|
||||||
|
"husky": "^8.0.2",
|
||||||
|
"jest": "^29.3.1",
|
||||||
|
"lint-staged": "^13.1.0",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"pretty-quick": "^3.1.3",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"rollup-plugin-visualizer": "^5.9.0",
|
||||||
|
"stylelint": "^14.16.0",
|
||||||
|
"stylelint-config-prettier": "^9.0.4",
|
||||||
|
"stylelint-config-standard": "^29.0.0",
|
||||||
|
"stylelint-order": "^5.0.0",
|
||||||
|
"stylelint-scss": "^4.3.0",
|
||||||
|
"unplugin-auto-import": "^0.12.0",
|
||||||
|
"vite-plugin-compression": "^0.5.1",
|
||||||
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
|
"vite-plugin-html": "^3.2.0",
|
||||||
|
"vite-plugin-mock": "^2.9.6",
|
||||||
|
"vite-plugin-style-import": "^2.0.0",
|
||||||
|
"vue-demi": "^0.13.11",
|
||||||
|
"vue-draggable-next": "^2.1.1",
|
||||||
|
"vue-eslint-parser": "^9.1.0",
|
||||||
|
"vue-global-api": "^0.4.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"postcss": "^8.4.21"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{vue,js,ts,tsx}": "eslint --fix"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"commitizen": {
|
||||||
|
"path": "./node_modules/cz-customizable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"vue",
|
||||||
|
"vue3",
|
||||||
|
"ts",
|
||||||
|
"tsx",
|
||||||
|
"admin",
|
||||||
|
"typescript"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12 || >=14"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
public/MP_verify_wPuKfPmKlhtYlJ1i.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
wPuKfPmKlhtYlJ1i
|
||||||
1
public/vite.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
28
src/App.vue
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<!-- <router-view v-slot="{ Component }">-->
|
||||||
|
<!-- <transition name="fade-transition" mode="out-in">-->
|
||||||
|
<!-- <keep-alive>-->
|
||||||
|
<!-- <component :is="Component"></component>-->
|
||||||
|
<!-- </keep-alive>-->
|
||||||
|
<!-- </transition>-->
|
||||||
|
<!-- </router-view>-->
|
||||||
|
|
||||||
|
<!-- v-if="showBar"-->
|
||||||
|
<div>
|
||||||
|
<!-- <van-sticky>-->
|
||||||
|
<!-- </van-sticky>-->
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@import "style/index.scss";
|
||||||
|
body {
|
||||||
|
//background-color: #f8f8f8;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
192
src/api/index.ts
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
import {http} from "@/utils/http/axios";
|
||||||
|
import {RequestEnum} from "@/enums/httpEnum";
|
||||||
|
|
||||||
|
|
||||||
|
const baseUrl = '/api'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: getLoansInfo
|
||||||
|
*/
|
||||||
|
export function getLoansInfo() {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/home/setting/loans`,
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: getHomeInfo
|
||||||
|
*/
|
||||||
|
export function getHomeInfo() {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/home/setting/home`,
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: getCalLoan
|
||||||
|
*/
|
||||||
|
export function getCalLoan(data) {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/home/loans/calLoan`,
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: getLoansUser
|
||||||
|
*/
|
||||||
|
export function getLoansUser() {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/home/loans/loansUser`,
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: getUserInfo
|
||||||
|
*/
|
||||||
|
export function getUserInfo() {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/customer/card/info`,
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: uploadCommon
|
||||||
|
*/
|
||||||
|
export function uploadCommon(file, onUploadProgress?) {
|
||||||
|
return http.uploadFile({
|
||||||
|
url: `${baseUrl}/v2/common/upload`,
|
||||||
|
method: 'POST',
|
||||||
|
onUploadProgress: (progressEvent) => {
|
||||||
|
onUploadProgress && onUploadProgress(progressEvent)
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
file: file
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* updateCustomerCard
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export function updateCustomerCard(data) {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/customer/updateCustomerCard`,
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getBankType
|
||||||
|
*/
|
||||||
|
export function getBankType() {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/home/setting/bankType`,
|
||||||
|
method: RequestEnum.GET
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getBankType
|
||||||
|
*/
|
||||||
|
export function getAgreement() {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/home/setting/agreement`,
|
||||||
|
method: RequestEnum.GET
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getCustomerInfo
|
||||||
|
*/
|
||||||
|
export function getCustomerInfo() {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/customer/info`,
|
||||||
|
method: RequestEnum.GET
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getCustomerInfo
|
||||||
|
*/
|
||||||
|
export function getStepBorrow() {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/borrow/getStepBorrow`,
|
||||||
|
method: RequestEnum.GET
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getBorrowPage
|
||||||
|
*/
|
||||||
|
export function getBorrowPage(params) {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/borrow/page`,
|
||||||
|
method: RequestEnum.GET,
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getBorrowInfo
|
||||||
|
*/
|
||||||
|
export function getBorrowInfo(params) {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/borrow/info`,
|
||||||
|
method: RequestEnum.GET,
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getBorrowWithdraw
|
||||||
|
*/
|
||||||
|
export function getBorrowWithdraw(params) {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/borrow/withdraw`,
|
||||||
|
method: RequestEnum.GET,
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getSetting
|
||||||
|
*/
|
||||||
|
export function getSetting() {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/home/setting/home`,
|
||||||
|
method: RequestEnum.GET
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getSetting
|
||||||
|
*/
|
||||||
|
export function getContract(params?) {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/borrow/getContract`,
|
||||||
|
method: RequestEnum.GET,
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* startBorrow
|
||||||
|
*/
|
||||||
|
export function startBorrow(data) {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/borrow/start`,
|
||||||
|
method: RequestEnum.POST,
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
97
src/api/login/index.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import { http } from '@/utils/http/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/api'
|
||||||
|
/**
|
||||||
|
* @description: sendSmsRegister
|
||||||
|
*/
|
||||||
|
export function sendSmsRegister(params) {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/customer/open/sms/register`,
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
},
|
||||||
|
{
|
||||||
|
withToken: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: sendSmsForget
|
||||||
|
*/
|
||||||
|
export function sendSmsForget(params) {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/customer/open/sms/forget`,
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
},
|
||||||
|
{
|
||||||
|
withToken: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: login
|
||||||
|
*/
|
||||||
|
export function login(data) {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/customer/login`,
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isTransformResponse: false,
|
||||||
|
withToken: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: register
|
||||||
|
*/
|
||||||
|
export function register(data) {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/customer/open/register`,
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
withToken: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: updatePwd
|
||||||
|
*/
|
||||||
|
export function updatePwd(data) {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/customer/open/updatePwd`,
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
withToken: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: getUserInfo
|
||||||
|
*/
|
||||||
|
export function getUserInfo() {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/customer/info`,
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注销账号
|
||||||
|
* @description: closeAmount
|
||||||
|
*/
|
||||||
|
export function closeAmount() {
|
||||||
|
return http.request({
|
||||||
|
url: `${baseUrl}/app/user/close/amount`,
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
0
src/api/system/user.ts
Normal file
260
src/array.prototype.d.ts
vendored
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
interface Array {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空数组
|
||||||
|
*/
|
||||||
|
clear(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空数组并添加
|
||||||
|
* @param items
|
||||||
|
*/
|
||||||
|
newPush(...items: T[]): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组根据对象删除指定元素
|
||||||
|
* @param val
|
||||||
|
*/
|
||||||
|
remove(val: T | T[]): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组根据下标删除指定元素
|
||||||
|
* @param index
|
||||||
|
* @param {number} index
|
||||||
|
*/
|
||||||
|
removeByIndex(index: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组指定位置添加元素
|
||||||
|
* @param index
|
||||||
|
* @param item
|
||||||
|
* @param {number} index
|
||||||
|
* @param {*} item
|
||||||
|
*/
|
||||||
|
insert(index: number, ...item: T): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组指定位置替换元素
|
||||||
|
* @param index
|
||||||
|
* @param item
|
||||||
|
* @param {number} index
|
||||||
|
* @param {*} item
|
||||||
|
*/
|
||||||
|
replace(index: number, ...item: T): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组排序(根据某一个字段排序或者直接排序)
|
||||||
|
* @param { key, desc } options
|
||||||
|
*/
|
||||||
|
shellSort(options?: {key: string, desc: boolean}): void;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组去重 (key为元素中的某一个属性,默认对比整个对象)
|
||||||
|
* @param key
|
||||||
|
* @returns {*|*[]}
|
||||||
|
*/
|
||||||
|
deDuplication(key?: string): T[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断元素中是否包含此元素 (key为元素中的某一个属性,默认对比整个对象)
|
||||||
|
* @param val
|
||||||
|
* @param key1
|
||||||
|
* @param key2
|
||||||
|
* @returns {boolean}
|
||||||
|
* @param {any} val
|
||||||
|
* @param {string} key1
|
||||||
|
* @param {string | null} key2
|
||||||
|
*/
|
||||||
|
contain(val: any, key1?: string, key2?: string): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空数组
|
||||||
|
*/
|
||||||
|
Array.prototype.clear = function() {
|
||||||
|
this.length = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空数组并添加
|
||||||
|
* @param items
|
||||||
|
*/
|
||||||
|
Array.prototype.newPush = function(...items: T[]) {
|
||||||
|
this.clear()
|
||||||
|
// 判断是否传入了多个参数
|
||||||
|
if (items && Array.isArray(items)) {
|
||||||
|
// 循环多个参数
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
// 判断每个参数是否为数组
|
||||||
|
if (items[i] && Array.isArray(items[i])) {
|
||||||
|
// 循环是数组的元素并添加
|
||||||
|
for (let j = 0; j < items[i].length; j++) {
|
||||||
|
this.push(items[i][j])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.push(items[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.push(items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组根据对象删除指定元素
|
||||||
|
* @param val
|
||||||
|
*/
|
||||||
|
Array.prototype.remove = function (val: T | T[]) {
|
||||||
|
const index = this.indexOf(val)
|
||||||
|
if (index > -1) {
|
||||||
|
this.removeByIndex(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组根据下标删除指定元素
|
||||||
|
* @param index
|
||||||
|
* @param {number} index
|
||||||
|
*/
|
||||||
|
Array.prototype.removeByIndex = function (index: number) {
|
||||||
|
this.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组指定位置添加元素
|
||||||
|
* @param index
|
||||||
|
* @param item
|
||||||
|
* @param {number} index
|
||||||
|
* @param {*} item
|
||||||
|
*/
|
||||||
|
Array.prototype.insert = function (index: number, item: T) {
|
||||||
|
const len = this.length
|
||||||
|
if (len < index) {
|
||||||
|
for (let i = 0; i < (index - len); i++) {
|
||||||
|
this.push(null)
|
||||||
|
}
|
||||||
|
} else if (index < 0) {
|
||||||
|
for (let i = 0; i > index + len; i--) {
|
||||||
|
this.splice(i, 0, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.splice(index, 0, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组指定位置替换元素
|
||||||
|
* @param index
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
Array.prototype.replace = function (index: number, item: T) {
|
||||||
|
if (index < 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const len = this.length
|
||||||
|
if (len < index) {
|
||||||
|
this.insert(index, item)
|
||||||
|
} else {
|
||||||
|
this.splice(index, 1, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组排序(根据某一个字段排序或者直接排序)
|
||||||
|
* @param { key, desc } options
|
||||||
|
*/
|
||||||
|
Array.prototype.shellSort = function (options?: {key: string, desc: boolean} | null = {}) {
|
||||||
|
const {key, desc} = options
|
||||||
|
const N = this.length
|
||||||
|
let h = 1
|
||||||
|
while (h < N / 3) {
|
||||||
|
h = 3 * h + 1
|
||||||
|
}
|
||||||
|
while (h >= 1) {
|
||||||
|
for (let i = h; i < N; i++) {
|
||||||
|
if (key) {
|
||||||
|
for (let j = i; j >= h && (desc ? this[j][key] > this[j - h][key] : this[j][key] < this[j - h][key]); j -= h) {
|
||||||
|
const temp = this[j]
|
||||||
|
this[j] = this[j - h]
|
||||||
|
this[j - h] = temp
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let j = i; j >= h && (desc ? this[j] > this[j - h] : this[j] < this[j - h]); j -= h) {
|
||||||
|
const temp = this[j]
|
||||||
|
this[j] = this[j - h]
|
||||||
|
this[j - h] = temp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h = (h - 1) / 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组去重 (key为元素中的某一个属性,默认对比整个对象)
|
||||||
|
* @param key
|
||||||
|
* @returns {*|*[]}
|
||||||
|
*/
|
||||||
|
Array.prototype.deDuplication = function (key?: string) {
|
||||||
|
return this.reduce((pre: Array, cur) => {
|
||||||
|
return pre.contain(cur, key) ? pre : pre.concat(cur)
|
||||||
|
}, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断元素中是否包含此元素 (key为元素中的某一个属性,默认对比整个对象)
|
||||||
|
* @param val
|
||||||
|
* @param key1
|
||||||
|
* @param key2
|
||||||
|
* @returns {boolean}
|
||||||
|
* @param {any} val
|
||||||
|
* @param {string} key1
|
||||||
|
* @param {string} key2
|
||||||
|
*/
|
||||||
|
Array.prototype.contain = function (val: any, key1?: string, key2?: string): boolean {
|
||||||
|
if (key1) {
|
||||||
|
if (key2) {
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (typeof val === 'object') {
|
||||||
|
if (this[i][key1][key2] === val[key1][key2]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this[i][key1][key2] === val) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (typeof val === 'object') {
|
||||||
|
if (this[i][key1] === val[key1]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// console.log(this[i][key1], '===', val)
|
||||||
|
if (this[i][key1] === val) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return this.includes(val)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BIN
src/assets/images/common/bg_wallet.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
src/assets/images/common/idCard.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
src/assets/images/common/img.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/images/common/rectangle.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/assets/images/common/yhd.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/images/common/zyzgzs.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
src/assets/images/home/apply_bg.png
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
src/assets/images/home/banner_home.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
src/assets/images/home/detail_bg.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
src/assets/images/home/strip-add.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/images/home/strip-block.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src/assets/images/home/strip-reduce.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/images/home/xlb.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/images/my/head_img.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
src/assets/images/my/idcard1.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src/assets/images/my/idcard2.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/images/my/idcard3.png
Normal file
|
After Width: | Height: | Size: 1012 B |
BIN
src/assets/images/my/idcard4.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/assets/images/my/idcard5.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
src/assets/images/my/my_info.png
Normal file
|
After Width: | Height: | Size: 971 B |
BIN
src/assets/images/my/my_info1.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/images/my/my_info2.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/images/my/my_info3.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/assets/images/my/my_info4.png
Normal file
|
After Width: | Height: | Size: 892 B |
BIN
src/assets/images/my/my_info5.png
Normal file
|
After Width: | Height: | Size: 762 B |
BIN
src/assets/images/my/my_info66.png
Normal file
|
After Width: | Height: | Size: 1004 B |
BIN
src/assets/images/my/user_card.png
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
src/assets/images/my/uuuu1.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
src/assets/images/my/uuuu2.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
src/assets/images/my/uuuu3.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
src/assets/images/tabBar/artificer-active.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
src/assets/images/tabBar/artificer-inactive.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/assets/images/tabBar/home-active.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
src/assets/images/tabBar/home-inactive.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
src/assets/images/tabBar/my-active.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/images/tabBar/my-inactive.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
1
src/assets/vue.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 496 B |
22
src/components/JGap/JGap.vue
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<div :style="{height: px2vw(<number>baseProps.height), background: baseProps.background, opacity: baseProps.opacity ? 0 : 'none'}">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {px2vw} from "@/utils";
|
||||||
|
const baseProps = defineProps({
|
||||||
|
height: Number,
|
||||||
|
opacity: Boolean,
|
||||||
|
background: {
|
||||||
|
type: String,
|
||||||
|
default: () => '#ffffff00'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
54
src/components/JLabel/JLabel.vue
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div :style="{...props.style, ...(placement === 'bottom' ? { display: 'block' } : {})}" class="j-label">
|
||||||
|
<div
|
||||||
|
class="label"
|
||||||
|
:class="{'j-class-label' : props.labelBorder, 'required': props.required}"
|
||||||
|
v-if="label"
|
||||||
|
:style="{...labelStyleComputed, minWidth: (isNumber(props.labelWidth) ? props.labelWidth + 'px' : props.labelWidth)}"
|
||||||
|
>
|
||||||
|
{{ props.label }}
|
||||||
|
</div>
|
||||||
|
<div :class="props.labelBorder ? '' : 'content'">
|
||||||
|
<slot name="default"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { isNumber } from "@/utils/is";
|
||||||
|
import { basicProps } from './props';
|
||||||
|
import { addStyle } from "@/utils";
|
||||||
|
import {computed} from "vue";
|
||||||
|
// todo
|
||||||
|
const props = defineProps({ ...basicProps });
|
||||||
|
const labelStyleComputed = computed(() => {
|
||||||
|
return addStyle(props.labelStyle)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
.j-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.required {
|
||||||
|
&:after {
|
||||||
|
content: '*';
|
||||||
|
color: #F35534;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.j-class-label {
|
||||||
|
font-weight: bolder;
|
||||||
|
padding-left: 6px;
|
||||||
|
border-left: 3px solid #008ded;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
1
src/components/JLabel/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default as JLabel } from './JLabel.vue';
|
||||||
39
src/components/JLabel/props.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { PropType } from "vue";
|
||||||
|
|
||||||
|
export const basicProps = {
|
||||||
|
// 标签宽度
|
||||||
|
labelWidth: {
|
||||||
|
type: [Number, String] as PropType<number | string>,
|
||||||
|
default: '70px',
|
||||||
|
},
|
||||||
|
// 标签样式
|
||||||
|
labelStyle: {
|
||||||
|
type: [Object, String] as PropType<Object | string>,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
// 样式
|
||||||
|
style: {
|
||||||
|
type: [Object] as PropType<Object>,
|
||||||
|
default: {}
|
||||||
|
},
|
||||||
|
// 标签
|
||||||
|
label: {
|
||||||
|
type: String as PropType<string>,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 标签
|
||||||
|
placement: {
|
||||||
|
type: String as PropType<'left' | 'bottom'>,
|
||||||
|
default: 'left'
|
||||||
|
},
|
||||||
|
// 标签
|
||||||
|
labelBorder: {
|
||||||
|
type: Boolean as PropType<boolean>,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 标签
|
||||||
|
required: {
|
||||||
|
type: Boolean as PropType<boolean>,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
110
src/components/JList/JList.vue
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<van-pull-refresh v-model="loadingUp" @refresh="onRefresh">
|
||||||
|
<van-list
|
||||||
|
v-model:loading="loadingDown"
|
||||||
|
:finished="finished"
|
||||||
|
finished-text=""
|
||||||
|
@load="onLoad"
|
||||||
|
>
|
||||||
|
<div style="min-height: 78vh">
|
||||||
|
123
|
||||||
|
|
||||||
|
|
||||||
|
<van-empty v-if="orderList?.length <= 0" description="没有暂时还没有内容~"/>
|
||||||
|
</div>
|
||||||
|
</van-list>
|
||||||
|
</van-pull-refresh>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {ref} from "vue";
|
||||||
|
import {onMounted, reactive} from "vue";
|
||||||
|
|
||||||
|
const baseProps = defineProps({
|
||||||
|
orderList: {
|
||||||
|
type: Array,
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
serviceStatus: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits([
|
||||||
|
'cancelOrder',
|
||||||
|
'removeOrder',
|
||||||
|
'refundOrder',
|
||||||
|
'commitOrder',
|
||||||
|
'goInfo',
|
||||||
|
])
|
||||||
|
|
||||||
|
const orderList: any[] = reactive([])
|
||||||
|
|
||||||
|
|
||||||
|
const loadingUp = ref(false);
|
||||||
|
const loadingDown = ref(false);
|
||||||
|
const finished = ref(false);
|
||||||
|
const page = reactive({
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 5,
|
||||||
|
total: 20,
|
||||||
|
})
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
page.pageNum = 1
|
||||||
|
finished.value = true
|
||||||
|
orderList.clear()
|
||||||
|
_getUserOrderPage()
|
||||||
|
};
|
||||||
|
const onLoad = () => {
|
||||||
|
if (finished.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
page.pageNum = page.pageNum + 1
|
||||||
|
_getUserOrderPage()
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
onRefresh
|
||||||
|
})
|
||||||
|
|
||||||
|
const _getUserOrderPage = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelOrderBtn = (order) => {
|
||||||
|
emit('cancelOrder', order)
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeOrderBtn = (order) => {
|
||||||
|
emit('removeOrder', order)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const refundOrderBtn = (order) => {
|
||||||
|
emit('refundOrder', order)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const commitOrderBtn = (order) => {
|
||||||
|
emit('commitOrder', order)
|
||||||
|
}
|
||||||
|
|
||||||
|
const goInfoBtn = (order) => {
|
||||||
|
emit('goInfo', order)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
_getUserOrderPage()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
62
src/components/JNavBar/JNavBar.vue
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<van-nav-bar
|
||||||
|
:placeholder="baseProps.placeholder"
|
||||||
|
fixed
|
||||||
|
safe-area-inset-top
|
||||||
|
:title="title"
|
||||||
|
:style="{
|
||||||
|
'--van-nav-bar-background': baseProps.navBarBackground,
|
||||||
|
'--van-nav-bar-icon-color': baseProps.color,
|
||||||
|
'--van-nav-bar-text-color': baseProps.color,
|
||||||
|
'--van-nav-bar-title-text-color': baseProps.color,
|
||||||
|
'--van-border-width': '0',
|
||||||
|
}"
|
||||||
|
:left-arrow="showBar"
|
||||||
|
@click-left="onClickLeft"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {computed} from 'vue'
|
||||||
|
import {useRoute, useRouter} from "vue-router";
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const baseProps = defineProps({
|
||||||
|
navBarBackground: {
|
||||||
|
type: String,
|
||||||
|
default: '#151515'
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: '#fff'
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: Boolean as PropType<boolean>,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const onClickLeft = () => {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = computed(() => {
|
||||||
|
return baseProps.title || route.meta.title
|
||||||
|
})
|
||||||
|
const showBar = computed(() => {
|
||||||
|
return route.meta.showBar
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
41
src/date.prototype.d.ts
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
interface Date {
|
||||||
|
format(format: string): string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间格式化
|
||||||
|
* @param format
|
||||||
|
*/
|
||||||
|
Date.prototype.format = function (format: DateType | undefined = 'datetime'): string {
|
||||||
|
if (format === 'date') {
|
||||||
|
format = 'yyyy-MM-dd'
|
||||||
|
} else if (format === 'datetime') {
|
||||||
|
format = 'yyyy-MM-dd hh:mm:ss'
|
||||||
|
} else if (format === 'time') {
|
||||||
|
format = 'hh:mm:ss'
|
||||||
|
}
|
||||||
|
const o = {
|
||||||
|
"M+": this.getMonth() + 1, //month
|
||||||
|
"d+": this.getDate(), //day
|
||||||
|
"h+": this.getHours(), //hour
|
||||||
|
"m+": this.getMinutes(), //minute
|
||||||
|
"s+": this.getSeconds(), //second
|
||||||
|
"q+": Math.floor((this.getMonth() + 3) / 3), //quarter
|
||||||
|
"S": this.getMilliseconds() //millisecond
|
||||||
|
}
|
||||||
|
if (/(y+)/.test(format)) {
|
||||||
|
format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
|
||||||
|
}
|
||||||
|
for (const k in o) {
|
||||||
|
if (new RegExp("(" + k + ")").test(format)) {
|
||||||
|
format = format.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// String.prototype.formatDate = function (format) {
|
||||||
|
// const time = this.replaceAll(/-/g, "/");
|
||||||
|
// return new Date(time).format(format);
|
||||||
|
// }
|
||||||
34
src/enums/httpEnum.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @description: 请求结果集
|
||||||
|
*/
|
||||||
|
export enum ResultEnum {
|
||||||
|
SUCCESS = 200,
|
||||||
|
ERROR = 400,
|
||||||
|
TIMEOUT = 10042,
|
||||||
|
TYPE = 'success',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 请求方法
|
||||||
|
*/
|
||||||
|
export enum RequestEnum {
|
||||||
|
GET = 'GET',
|
||||||
|
POST = 'POST',
|
||||||
|
PATCH = 'PATCH',
|
||||||
|
PUT = 'PUT',
|
||||||
|
DELETE = 'DELETE',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 常用的contentTyp类型
|
||||||
|
*/
|
||||||
|
export enum ContentTypeEnum {
|
||||||
|
// json
|
||||||
|
JSON = 'application/json;charset=UTF-8',
|
||||||
|
// json
|
||||||
|
TEXT = 'text/plain;charset=UTF-8',
|
||||||
|
// form-data 一般配合qs
|
||||||
|
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||||
|
// form-data 上传
|
||||||
|
FORM_DATA = 'multipart/form-data;charset=UTF-8',
|
||||||
|
}
|
||||||
52
src/enums/orderEnum.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
export const OrderEnum = {
|
||||||
|
NO_PAY: {
|
||||||
|
value: 0,
|
||||||
|
name: '待支付'
|
||||||
|
},
|
||||||
|
NO_ORDER: {
|
||||||
|
value: 1,
|
||||||
|
name: '待接单'
|
||||||
|
},
|
||||||
|
ALREADY_ORDER: {
|
||||||
|
value: 2,
|
||||||
|
name: '待消费'
|
||||||
|
},
|
||||||
|
ALREADY_EVALUATION: {
|
||||||
|
value: 90,
|
||||||
|
name: '待评价'
|
||||||
|
},
|
||||||
|
COMMIT: {
|
||||||
|
value: 99,
|
||||||
|
name: '已完成'
|
||||||
|
},
|
||||||
|
CANCEL: {
|
||||||
|
value: -1,
|
||||||
|
name: '已取消'
|
||||||
|
},
|
||||||
|
REFUND: {
|
||||||
|
value: -2,
|
||||||
|
name: '已退款'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OrderEnumMap = {
|
||||||
|
0: '待支付',
|
||||||
|
1: '待接单',
|
||||||
|
2: '待消费',
|
||||||
|
90: '待评价',
|
||||||
|
99: '已完成',
|
||||||
|
'-1': '已取消',
|
||||||
|
'-2': '已退款',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const OrderIconMap = {
|
||||||
|
0: 'order/pay.png',
|
||||||
|
1: 'order/djd.png',
|
||||||
|
2: 'order/daixiaofei.png',
|
||||||
|
90: 'order/dpj.png',
|
||||||
|
99: 'order/ywc.png',
|
||||||
|
'-1': 'order/yqx.png',
|
||||||
|
'-2': 'order/ytk.png',
|
||||||
|
}
|
||||||
|
|
||||||
26
src/hooks/useBackgroundHook.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import {onMounted, onUnmounted} from "vue";
|
||||||
|
|
||||||
|
export const useBackgroundHook = () => {
|
||||||
|
|
||||||
|
|
||||||
|
let bodyList = document.getElementsByTagName('body');
|
||||||
|
let body = bodyList[0]
|
||||||
|
let bodyBackground = body.style.background
|
||||||
|
|
||||||
|
|
||||||
|
const setBodyBackground = (option?: {mounted?: Function | undefined, unmounted?: Function | undefined}) => {
|
||||||
|
let {mounted, unmounted} = option || {}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
body.style.background = 'linear-gradient(167.96deg, #E6FAE1 0%, #F2E7B7 98.44%) no-repeat'
|
||||||
|
mounted && mounted()
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
body.style.background = bodyBackground
|
||||||
|
unmounted && unmounted()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return { setBodyBackground }
|
||||||
|
}
|
||||||
37
src/hooks/useMittHook.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import mitt from 'mitt'
|
||||||
|
const emitter = mitt()
|
||||||
|
|
||||||
|
export const useMittHook = () => {
|
||||||
|
|
||||||
|
const emit = (eventName, params) => {
|
||||||
|
emitter.emit(eventName, params)
|
||||||
|
emitter.emit(`${eventName}Once`, eventName)
|
||||||
|
}
|
||||||
|
|
||||||
|
const on = (eventName, fun) => {
|
||||||
|
emitter.on(eventName, fun)
|
||||||
|
}
|
||||||
|
|
||||||
|
const once = (eventName, fun) => {
|
||||||
|
emitter.on(eventName, fun)
|
||||||
|
emitter.on(`${eventName}Once`, (en) => {
|
||||||
|
emitter.off(en)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const off = (eventName, params?) => {
|
||||||
|
emitter.off(eventName, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearAll = () => {
|
||||||
|
emitter.all.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
emit,
|
||||||
|
on,
|
||||||
|
once,
|
||||||
|
off,
|
||||||
|
clearAll
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/main.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import './array.prototype.d.ts';
|
||||||
|
import './date.prototype.d.ts';
|
||||||
|
import './string.prototype.d.ts';
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from "./router";
|
||||||
|
import {setupStore} from "@/store";
|
||||||
|
import {setupCustomComponents} from "@/plugins/customComponents";
|
||||||
|
// 2. 引入组件样式
|
||||||
|
import 'vant/lib/index.css';
|
||||||
|
import 'vant/es/toast/style'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
app.use(router)
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
|
|
||||||
|
setupStore(app)
|
||||||
|
|
||||||
|
setupCustomComponents(app)
|
||||||
14
src/plugins/customComponents.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { App } from "vue";
|
||||||
|
import { JLabel } from "@/components/JLabel";
|
||||||
|
import JGap from "@/components/JGap/JGap.vue";
|
||||||
|
import JNavBar from "@/components/JNavBar/JNavBar.vue";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局注册自定义组件 待完善
|
||||||
|
* @param app
|
||||||
|
*/
|
||||||
|
export function setupCustomComponents(app: App<Element>) {
|
||||||
|
app.component('JGap',JGap)
|
||||||
|
app.component('JLabel',JLabel)
|
||||||
|
app.component('JNavBar',JNavBar)
|
||||||
|
}
|
||||||
253
src/router/index.ts
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
import {createRouter, createWebHashHistory, RouteRecordRaw} from 'vue-router';
|
||||||
|
import {useUserStore} from "@/store/modules/user";
|
||||||
|
|
||||||
|
export const constantRouter: Array<RouteRecordRaw> = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'Root',
|
||||||
|
redirect: '/home',
|
||||||
|
meta: {
|
||||||
|
title: 'Root',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'index',
|
||||||
|
component: () => import('@/views/index/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '首页',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/home',
|
||||||
|
name: 'Home',
|
||||||
|
component: () => import('@/views/index/home/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '首页',
|
||||||
|
showBar: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/serveList',
|
||||||
|
name: 'serveList',
|
||||||
|
component: () => import('@/views/index/serveList/index.vue'),
|
||||||
|
meta: {
|
||||||
|
isPermissions: true,
|
||||||
|
title: '钱包',
|
||||||
|
showBar: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/message',
|
||||||
|
name: 'message',
|
||||||
|
component: () => import('@/views/index/message/index.vue'),
|
||||||
|
meta: {
|
||||||
|
isPermissions: true,
|
||||||
|
title: '聊天',
|
||||||
|
showBar: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/my',
|
||||||
|
name: 'my',
|
||||||
|
component: () => import('@/views/index/my/index.vue'),
|
||||||
|
meta: {
|
||||||
|
isPermissions: true,
|
||||||
|
title: '我的',
|
||||||
|
showBar: false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/userInfo',
|
||||||
|
name: 'userInfo',
|
||||||
|
component: () => import('@/views/my/userInfo/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '我的资料',
|
||||||
|
isPermissions: true,
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/userInfo1',
|
||||||
|
name: 'userInfo1',
|
||||||
|
component: () => import('@/views/my/userInfo1/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '基本信息',
|
||||||
|
isPermissions: true,
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/signature',
|
||||||
|
name: 'signature',
|
||||||
|
component: () => import('@/views/my/signature/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '签名',
|
||||||
|
isPermissions: true,
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/contract',
|
||||||
|
name: 'contract',
|
||||||
|
component: () => import('@/views/my/contract/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '合同',
|
||||||
|
isPermissions: true,
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/userInfo2',
|
||||||
|
name: 'userInfo2',
|
||||||
|
component: () => import('@/views/my/userInfo2/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '提交资料',
|
||||||
|
isPermissions: true,
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/userInfo3',
|
||||||
|
name: 'userInfo3',
|
||||||
|
component: () => import('@/views/my/userInfo3/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '收款银行卡',
|
||||||
|
isPermissions: true,
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/loansInfo',
|
||||||
|
name: 'loansInfo',
|
||||||
|
component: () => import('@/views/loans/info/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '借款详情',
|
||||||
|
isPermissions: true,
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/loansInfo1',
|
||||||
|
name: 'loansInfo1',
|
||||||
|
component: () => import('@/views/loans/info1/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '提现',
|
||||||
|
isPermissions: true,
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/myLoan',
|
||||||
|
name: 'myLoan',
|
||||||
|
component: () => import('@/views/my/myLoan/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '我的借款',
|
||||||
|
isPermissions: true,
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/myRepayment',
|
||||||
|
name: 'myRepayment',
|
||||||
|
component: () => import('@/views/my/myRepayment/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '我的还款',
|
||||||
|
isPermissions: true,
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: '/borrowInfo',
|
||||||
|
name: 'borrowInfo',
|
||||||
|
component: () => import('@/views/borrowInfo/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '贷款详情',
|
||||||
|
isPermissions: true,
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/uploadPassword',
|
||||||
|
name: 'uploadPassword',
|
||||||
|
component: () => import('@/views/uploadPassword/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '修改密码',
|
||||||
|
isPermissions: true,
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'login',
|
||||||
|
component: () => import('@/views/login/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '登录',
|
||||||
|
showBar: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/register',
|
||||||
|
name: 'register',
|
||||||
|
component: () => import('@/views/register/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '注册',
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/forget',
|
||||||
|
name: 'forget',
|
||||||
|
component: () => import('@/views/forget/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '忘记密码',
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/agreement',
|
||||||
|
name: 'agreement',
|
||||||
|
component: () => import('@/views/agreement/agreement.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '协议',
|
||||||
|
showBar: true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
//需要验证权限
|
||||||
|
|
||||||
|
//普通路由 无需验证权限
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(''),
|
||||||
|
routes: constantRouter,
|
||||||
|
strict: true,
|
||||||
|
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
if (to.meta.title) { // 判断是否有标题
|
||||||
|
document.title = to.meta.title as string;
|
||||||
|
}
|
||||||
|
if (to.meta.isPermissions) {
|
||||||
|
const userStore = useUserStore()
|
||||||
|
if (!userStore.getToken) {
|
||||||
|
next({
|
||||||
|
path: '/login'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router;
|
||||||
10
src/store/index.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import type { App } from 'vue';
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
|
|
||||||
|
const store = createPinia();
|
||||||
|
|
||||||
|
export function setupStore(app: App<Element>) {
|
||||||
|
app.use(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { store };
|
||||||
14
src/store/modules/index.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const allModules: any = import.meta.glob('./*/index.ts', { eager: true });
|
||||||
|
const modules = {} as any;
|
||||||
|
Object.keys(allModules).forEach((path) => {
|
||||||
|
const fileName = path.split('/')[1];
|
||||||
|
modules[fileName] = allModules[path][fileName] || allModules[path].default || allModules[path];
|
||||||
|
});
|
||||||
|
|
||||||
|
// export default modules
|
||||||
|
// @ts-ignore
|
||||||
|
import user from './user';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
user,
|
||||||
|
};
|
||||||
137
src/store/modules/user.ts
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { store } from '@/store';
|
||||||
|
import { storage } from '@/utils/Storage';
|
||||||
|
import {ACCESS_TOKEN, CURRENT_USER, LOCATION_MAP, OPEN_ID, WX_CONFIG} from "@/store/mutation-types";
|
||||||
|
import {getUserInfo, login} from "@/api/login";
|
||||||
|
import {ResultEnum} from "@/enums/httpEnum";
|
||||||
|
|
||||||
|
export interface IUserState {
|
||||||
|
showCoupon: boolean;
|
||||||
|
token: string;
|
||||||
|
openId: string;
|
||||||
|
username: string;
|
||||||
|
welcome: string;
|
||||||
|
avatar: string;
|
||||||
|
permissions: any[];
|
||||||
|
info: any;
|
||||||
|
wxConfig: any;
|
||||||
|
locationMap: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useUserStore = defineStore({
|
||||||
|
id: 'app-user',
|
||||||
|
state: (): IUserState => ({
|
||||||
|
showCoupon: true,
|
||||||
|
token: storage.get(ACCESS_TOKEN, ''),
|
||||||
|
openId: storage.get(OPEN_ID, ''),
|
||||||
|
// openId: storage.get(OPEN_ID, 'omWdJ62bH_6HXLQVOIefzN9J1oi4'),
|
||||||
|
username: '',
|
||||||
|
welcome: '',
|
||||||
|
avatar: '',
|
||||||
|
permissions: [],
|
||||||
|
info: storage.get(CURRENT_USER, {}),
|
||||||
|
wxConfig: storage.get(WX_CONFIG, null),
|
||||||
|
locationMap: storage.get(LOCATION_MAP, {}),
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
getShowCoupon(): boolean {
|
||||||
|
return this.showCoupon;
|
||||||
|
},
|
||||||
|
getToken(): string {
|
||||||
|
return this.token;
|
||||||
|
},
|
||||||
|
getOpenId(): string {
|
||||||
|
return this.openId;
|
||||||
|
},
|
||||||
|
getAvatar(): string {
|
||||||
|
return this.avatar;
|
||||||
|
},
|
||||||
|
getNickname(): string {
|
||||||
|
return this.username;
|
||||||
|
},
|
||||||
|
getPermissions(): [any][] {
|
||||||
|
return this.permissions;
|
||||||
|
},
|
||||||
|
getUserInfo(): object {
|
||||||
|
return this.info;
|
||||||
|
},
|
||||||
|
getWxConfig(): object {
|
||||||
|
return this.wxConfig;
|
||||||
|
},
|
||||||
|
getLocationMap(): object {
|
||||||
|
return this.locationMap;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setShowCoupon(showCoupon: boolean) {
|
||||||
|
this.showCoupon = showCoupon;
|
||||||
|
},
|
||||||
|
setToken(token: string) {
|
||||||
|
const ex = 1 * 4 * 60 * 60;
|
||||||
|
storage.set(ACCESS_TOKEN, token, ex);
|
||||||
|
this.token = token;
|
||||||
|
},
|
||||||
|
setOpenId(openId: string) {
|
||||||
|
const ex = 1000 * 24 * 60 * 60;
|
||||||
|
storage.set(OPEN_ID, openId, ex);
|
||||||
|
this.openId = openId;
|
||||||
|
},
|
||||||
|
setAvatar(avatar: string) {
|
||||||
|
this.avatar = avatar;
|
||||||
|
},
|
||||||
|
setPermissions(permissions) {
|
||||||
|
this.permissions = permissions;
|
||||||
|
},
|
||||||
|
setUserInfo(info) {
|
||||||
|
const ex = 1 * 4 * 60 * 60;
|
||||||
|
storage.set(CURRENT_USER, info, ex);
|
||||||
|
this.info = info;
|
||||||
|
},
|
||||||
|
setWxConfig(wxConfig) {
|
||||||
|
const ex = 7000;
|
||||||
|
storage.set(WX_CONFIG, wxConfig, ex);
|
||||||
|
this.wxConfig = wxConfig;
|
||||||
|
},
|
||||||
|
setLocationMap(locationMap) {
|
||||||
|
const ex = 7000;
|
||||||
|
storage.set(LOCATION_MAP, locationMap, ex);
|
||||||
|
this.locationMap = locationMap;
|
||||||
|
},
|
||||||
|
// 登录
|
||||||
|
async login(loginData) {
|
||||||
|
try {
|
||||||
|
const {data, code, msg} = await login(loginData)
|
||||||
|
if (code === ResultEnum.SUCCESS) {
|
||||||
|
this.setToken(data.token);
|
||||||
|
const userInfo = await getUserInfo()
|
||||||
|
this.setUserInfo(userInfo)
|
||||||
|
return Promise.resolve(data.token);
|
||||||
|
} else {
|
||||||
|
return Promise.reject(msg);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return Promise.reject(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 登出
|
||||||
|
async logout() {
|
||||||
|
this.setToken('');
|
||||||
|
this.setUserInfo(null);
|
||||||
|
this.setWxConfig(null);
|
||||||
|
// this.setLocationMap(null);
|
||||||
|
this.setUserInfo(null);
|
||||||
|
this.setOpenId('');
|
||||||
|
storage.remove(ACCESS_TOKEN);
|
||||||
|
storage.remove(CURRENT_USER);
|
||||||
|
storage.remove(WX_CONFIG);
|
||||||
|
// storage.remove(LOCATION_MAP);
|
||||||
|
storage.remove(OPEN_ID);
|
||||||
|
return Promise.resolve('');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Need to be used outside the setup
|
||||||
|
export function useUserStoreWidthOut() {
|
||||||
|
return useUserStore(store);
|
||||||
|
}
|
||||||
5
src/store/mutation-types.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export const ACCESS_TOKEN = 'ACCESS-TOKEN'; // 用户token
|
||||||
|
export const OPEN_ID = 'OPEN-ID'; // 用户token
|
||||||
|
export const CURRENT_USER = 'CURRENT-USER'; // 当前用户信息
|
||||||
|
export const WX_CONFIG = 'WX-CONFIG'; // 当前用户信息
|
||||||
|
export const LOCATION_MAP = 'LOCATION-MAP'; // 当前用户信息
|
||||||
96
src/string.prototype.d.ts
vendored
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
interface String {
|
||||||
|
clearSpaces(pos?: string | null): string;
|
||||||
|
clearBr(): string;
|
||||||
|
clearSpacesAndBr(): string;
|
||||||
|
clearLeftSpaces(): string;
|
||||||
|
clearRightSpaces(): string;
|
||||||
|
clearBothSidesSpaces(): string;
|
||||||
|
clearInsideSpaces(): string;
|
||||||
|
formatDate(format: string): string;
|
||||||
|
replaceAll(searchValue: string | RegExp, replaceValue: string): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除字符串所有空格
|
||||||
|
* @param pos both(左右)|left|right|all|inner 默认all
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
String.prototype.clearSpaces = function (pos?: string | null = 'all') {
|
||||||
|
if (pos === 'all') {
|
||||||
|
return this.replace(/\s+/g, '')
|
||||||
|
}
|
||||||
|
if (pos === 'both') {
|
||||||
|
return this.replace(/^\s+|\s+$/g, '')
|
||||||
|
}
|
||||||
|
if (pos === 'left') {
|
||||||
|
return this.replace(/^\s*/, '')
|
||||||
|
}
|
||||||
|
if (pos === 'right') {
|
||||||
|
return this.replace(/(\s*$)/g, '')
|
||||||
|
}
|
||||||
|
if (pos === 'inner') {
|
||||||
|
return this.replace(/\s/g, '')
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除字符串所有换行
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
String.prototype.clearBr = function () {
|
||||||
|
const str = this.replace(/<\/?.+?>/g, '')
|
||||||
|
str.replace(/[\r\n]/g, '')
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除字符串所有空格和回车
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
String.prototype.clearSpacesAndBr = function () {
|
||||||
|
let str = this.clearBr()
|
||||||
|
str = str.clearSpaces()
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 清除字符串左边空格
|
||||||
|
// * @return {string}
|
||||||
|
// */
|
||||||
|
// String.prototype.clearLeftSpaces = function () {
|
||||||
|
// return this.replace(/^\s*/g, '')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 清除字符串右边空格
|
||||||
|
// * @return {string}
|
||||||
|
// */
|
||||||
|
// String.prototype.clearRightSpaces = function () {
|
||||||
|
// return this.replace(/\s*$/g, '')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 清除字符串两边空格
|
||||||
|
// * @return {string}
|
||||||
|
// */
|
||||||
|
// String.prototype.clearBothSidesSpaces = function () {
|
||||||
|
// return this.replace(/(^\s*)|(\s*$)/g, '')
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除字符串中间空格
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
String.prototype.clearInsideSpaces = function () {
|
||||||
|
return this.replace(/\s/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
String.prototype.formatDate = function (format) {
|
||||||
|
const time = this.replaceAll(/-/g, "/");
|
||||||
|
return new Date(time).format(format);
|
||||||
|
}
|
||||||
|
String.prototype.replaceAll = function (searchValue: string | RegExp, replaceValue: string): string{
|
||||||
|
const target = this;
|
||||||
|
return target.replace(new RegExp(searchValue, 'g'), replaceValue);
|
||||||
|
}
|
||||||
112
src/style/index.scss
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#app,
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei,
|
||||||
|
'\5FAE\8F6F\96C5\9ED1', Arial, sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
//background-color: #f7f7f7;
|
||||||
|
background-color: #f6f9fd;
|
||||||
|
//background: #f6f9fd
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
//重置样式
|
||||||
|
.anticon {
|
||||||
|
svg {
|
||||||
|
vertical-align: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #2d8cf0;
|
||||||
|
background: transparent;
|
||||||
|
text-decoration: none;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:active,
|
||||||
|
a:hover {
|
||||||
|
outline-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #57a3f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:active {
|
||||||
|
color: #2b85e4;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:active,
|
||||||
|
a:hover {
|
||||||
|
outline: 0;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条凹槽的颜色,还可以设置边框属性 */
|
||||||
|
*::-webkit-scrollbar-track-piece {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
-webkit-border-radius: 2em;
|
||||||
|
-moz-border-radius: 2em;
|
||||||
|
border-radius: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条的宽度 */
|
||||||
|
*::-webkit-scrollbar {
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条的设置 */
|
||||||
|
*::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #ddd;
|
||||||
|
background-clip: padding-box;
|
||||||
|
-webkit-border-radius: 2em;
|
||||||
|
-moz-border-radius: 2em;
|
||||||
|
border-radius: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条鼠标移上去 */
|
||||||
|
*::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* router view transition */
|
||||||
|
.zoom-fade-enter-active,
|
||||||
|
.zoom-fade-leave-active {
|
||||||
|
transition: transform 0.35s, opacity 0.28s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zoom-fade-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.97);
|
||||||
|
}
|
||||||
|
|
||||||
|
.zoom-fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-ellipsis {
|
||||||
|
word-break:keep-all;/* 不换行 */
|
||||||
|
white-space:nowrap;/* 不换行 */
|
||||||
|
overflow:hidden;/* 内容超出宽度时隐藏超出部分的内容 */
|
||||||
|
text-overflow:ellipsis;/* 当对象内文本溢出时显示省略标记(...) ;需与overflow:hidden;一起使用。*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.yellow_color {
|
||||||
|
color: #BC7C1C;
|
||||||
|
}
|
||||||
|
.yellow_color1 {
|
||||||
|
color: #F9BF3A;
|
||||||
|
}
|
||||||
|
.gray_color {
|
||||||
|
color: #858B9C;
|
||||||
|
}
|
||||||
101
src/type/global.d.ts
vendored
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import type {
|
||||||
|
ComponentRenderProxy,
|
||||||
|
VNode,
|
||||||
|
VNodeChild,
|
||||||
|
ComponentPublicInstance,
|
||||||
|
FunctionalComponent,
|
||||||
|
PropType as VuePropType,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
const __APP_INFO__: {
|
||||||
|
pkg: {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
dependencies: Recordable<string>;
|
||||||
|
devDependencies: Recordable<string>;
|
||||||
|
};
|
||||||
|
lastBuildTime: string;
|
||||||
|
};
|
||||||
|
// declare interface Window {
|
||||||
|
// // Global vue app instance
|
||||||
|
// __APP__: App<Element>;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// vue
|
||||||
|
declare type PropType<T> = VuePropType<T>;
|
||||||
|
declare type VueNode = VNodeChild | JSX.Element;
|
||||||
|
|
||||||
|
export type Writable<T> = {
|
||||||
|
-readonly [P in keyof T]: T[P];
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type Nullable<T> = T | null;
|
||||||
|
declare type NonNullable<T> = T extends null | undefined ? never : T;
|
||||||
|
declare type Recordable<T = any> = Record<string, T>;
|
||||||
|
declare type ReadonlyRecordable<T = any> = {
|
||||||
|
readonly [key: string]: T;
|
||||||
|
};
|
||||||
|
declare type Indexable<T = any> = {
|
||||||
|
[key: string]: T;
|
||||||
|
};
|
||||||
|
declare type DeepPartial<T> = {
|
||||||
|
[P in keyof T]?: DeepPartial<T[P]>;
|
||||||
|
};
|
||||||
|
declare type TimeoutHandle = ReturnType<typeof setTimeout>;
|
||||||
|
declare type IntervalHandle = ReturnType<typeof setInterval>;
|
||||||
|
|
||||||
|
declare interface ChangeEvent extends Event {
|
||||||
|
target: HTMLInputElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface WheelEvent {
|
||||||
|
path?: EventTarget[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMetaEnv extends ViteEnv {
|
||||||
|
__: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface ViteEnv {
|
||||||
|
VITE_PORT: number;
|
||||||
|
VITE_USE_MOCK: boolean;
|
||||||
|
VITE_PUBLIC_PATH: string;
|
||||||
|
VITE_GLOB_APP_TITLE: string;
|
||||||
|
VITE_GLOB_APP_SHORT_NAME: string;
|
||||||
|
VITE_DROP_CONSOLE: boolean;
|
||||||
|
VITE_GLOB_IMG_URL: string;
|
||||||
|
VITE_PROXY: [string, string][];
|
||||||
|
VITE_BUILD_COMPRESS: 'gzip' | 'brotli' | 'none';
|
||||||
|
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare function parseInt(s: string | number, radix?: number): number;
|
||||||
|
|
||||||
|
declare function parseFloat(string: string | number): number;
|
||||||
|
|
||||||
|
namespace JSX {
|
||||||
|
// tslint:disable no-empty-interface
|
||||||
|
type Element = VNode;
|
||||||
|
// tslint:disable no-empty-interface
|
||||||
|
type ElementClass = ComponentRenderProxy;
|
||||||
|
|
||||||
|
interface ElementAttributesProperty {
|
||||||
|
$props: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IntrinsicElements {
|
||||||
|
[elem: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IntrinsicAttributes {
|
||||||
|
[elem: string]: any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
export type JSXComponent<Props = any> =
|
||||||
|
| { new (): ComponentPublicInstance<Props> }
|
||||||
|
| FunctionalComponent<Props>;
|
||||||
|
}
|
||||||
28
src/type/index.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
declare interface Fn<T = any, R = T> {
|
||||||
|
(...arg: T[]): R;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface PromiseFn<T = any, R = T> {
|
||||||
|
(...arg: T[]): Promise<R>;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type RefType<T> = T | null;
|
||||||
|
|
||||||
|
declare type LabelValueOptions = {
|
||||||
|
label: string;
|
||||||
|
value: any;
|
||||||
|
disabled: boolean;
|
||||||
|
[key: string]: string | number | boolean;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
declare type EmitType = (event: string, ...args: any[]) => void;
|
||||||
|
|
||||||
|
declare type TargetContext = '_self' | '_blank';
|
||||||
|
|
||||||
|
declare interface ComponentElRef<T extends HTMLElement = HTMLDivElement> {
|
||||||
|
$el: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type ComponentRef<T extends HTMLElement = HTMLDivElement> = ComponentElRef<T> | null;
|
||||||
|
|
||||||
|
declare type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>;
|
||||||
127
src/utils/Storage.ts
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
// 默认缓存期限为7天
|
||||||
|
const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建本地缓存对象
|
||||||
|
* @param {string=} prefixKey -
|
||||||
|
* @param {Object} [storage=localStorage] - sessionStorage | localStorage
|
||||||
|
*/
|
||||||
|
export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) => {
|
||||||
|
/**
|
||||||
|
* 本地缓存类
|
||||||
|
* @class Storage
|
||||||
|
*/
|
||||||
|
const Storage = class {
|
||||||
|
private storage = storage;
|
||||||
|
private prefixKey?: string = prefixKey;
|
||||||
|
|
||||||
|
private getKey(key: string) {
|
||||||
|
return `${this.prefixKey}${key}`.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 设置缓存
|
||||||
|
* @param {string} key 缓存键
|
||||||
|
* @param {*} value 缓存值
|
||||||
|
* @param expire
|
||||||
|
*/
|
||||||
|
set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
|
||||||
|
const stringData = JSON.stringify({
|
||||||
|
value,
|
||||||
|
expire: expire !== null ? new Date().getTime() + expire * 1000 : null,
|
||||||
|
});
|
||||||
|
this.storage.setItem(this.getKey(key), stringData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取缓存
|
||||||
|
* @param {string} key 缓存键
|
||||||
|
* @param {*=} def 默认值
|
||||||
|
*/
|
||||||
|
get(key: string, def: any = null) {
|
||||||
|
const item = this.storage.getItem(this.getKey(key));
|
||||||
|
if (item) {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(item);
|
||||||
|
const { value, expire } = data;
|
||||||
|
// 在有效期内直接返回
|
||||||
|
if (expire === null || expire >= Date.now()) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
this.remove(key);
|
||||||
|
} catch (e) {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从缓存删除某项
|
||||||
|
* @param {string} key
|
||||||
|
*/
|
||||||
|
remove(key: string) {
|
||||||
|
this.storage.removeItem(this.getKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空所有缓存
|
||||||
|
* @memberOf Cache
|
||||||
|
*/
|
||||||
|
clear(): void {
|
||||||
|
this.storage.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置cookie
|
||||||
|
* @param {string} name cookie 名称
|
||||||
|
* @param {*} value cookie 值
|
||||||
|
* @param {number=} expire 过期时间
|
||||||
|
* 如果过期时间未设置,默认关闭浏览器自动删除
|
||||||
|
* @example
|
||||||
|
*/
|
||||||
|
setCookie(name: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
|
||||||
|
document.cookie = `${this.getKey(name)}=${value}; Max-Age=${expire}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据名字获取cookie值
|
||||||
|
* @param name
|
||||||
|
*/
|
||||||
|
getCookie(name: string): string {
|
||||||
|
const cookieArr = document.cookie.split('; ');
|
||||||
|
for (let i = 0, length = cookieArr.length; i < length; i++) {
|
||||||
|
const kv = cookieArr[i].split('=');
|
||||||
|
if (kv[0] === this.getKey(name)) {
|
||||||
|
return kv[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据名字删除指定的cookie
|
||||||
|
* @param {string} key
|
||||||
|
*/
|
||||||
|
removeCookie(key: string) {
|
||||||
|
this.setCookie(key, 1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空cookie,使所有cookie失效
|
||||||
|
*/
|
||||||
|
clearCookie(): void {
|
||||||
|
const keys = document.cookie.match(/[^ =;]+(?==)/g);
|
||||||
|
if (keys) {
|
||||||
|
for (let i = keys.length; i--; ) {
|
||||||
|
document.cookie = keys[i] + '=0;expire=' + new Date(0).toUTCString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return new Storage();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const storage = createStorage();
|
||||||
|
|
||||||
|
export default Storage;
|
||||||
41
src/utils/dataUtil.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 重置对象会自动判断类型
|
||||||
|
* 当你传了第二个值之后会把两个对象合并
|
||||||
|
* 所有的值都置空
|
||||||
|
* 数字变为0
|
||||||
|
* @param obj
|
||||||
|
* @param newData
|
||||||
|
*/
|
||||||
|
export const resetData = (obj: object, newData?: object | null) => {
|
||||||
|
// 清空对象属性
|
||||||
|
Object.keys(obj).forEach(key => {
|
||||||
|
let type = "Undefined";
|
||||||
|
if (obj[key] !== undefined) {
|
||||||
|
type = toString.call(obj[key]).slice(8, -1); // 获取属性值的类型
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case "String":
|
||||||
|
obj[key] = "";
|
||||||
|
break;
|
||||||
|
case "Number":
|
||||||
|
obj[key] = null;
|
||||||
|
break;
|
||||||
|
case "Boolean":
|
||||||
|
obj[key] = false;
|
||||||
|
break;
|
||||||
|
case "Array":
|
||||||
|
obj[key] = [];
|
||||||
|
break;
|
||||||
|
case "Date":
|
||||||
|
obj[key] = null; // 日期类型置为 null
|
||||||
|
break;
|
||||||
|
case "Object":
|
||||||
|
resetData(obj[key]); // 对象类型递归遍历
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
obj[key] = null; // 其他类型都置为 null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 合并新数据到原对象上
|
||||||
|
Object.assign(obj, newData);
|
||||||
|
};
|
||||||
212
src/utils/http/axios/Axios.ts
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
import { AxiosCanceler } from './axiosCancel';
|
||||||
|
import { isFunction } from '@/utils/is';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
|
import type { RequestOptions, CreateAxiosOptions, Result, UploadFileParams } from './types';
|
||||||
|
import { ContentTypeEnum } from '@/enums/httpEnum';
|
||||||
|
|
||||||
|
export * from './axiosTransform';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: axios模块
|
||||||
|
*/
|
||||||
|
export class VAxios {
|
||||||
|
private axiosInstance: AxiosInstance;
|
||||||
|
private options: CreateAxiosOptions;
|
||||||
|
|
||||||
|
constructor(options: CreateAxiosOptions) {
|
||||||
|
this.options = options;
|
||||||
|
this.axiosInstance = axios.create(options);
|
||||||
|
this.setupInterceptors();
|
||||||
|
}
|
||||||
|
|
||||||
|
getAxios(): AxiosInstance {
|
||||||
|
return this.axiosInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 重新配置axios
|
||||||
|
*/
|
||||||
|
configAxios(config: CreateAxiosOptions) {
|
||||||
|
if (!this.axiosInstance) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.createAxios(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 设置通用header
|
||||||
|
*/
|
||||||
|
setHeader(headers: any): void {
|
||||||
|
if (!this.axiosInstance) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object.assign(this.axiosInstance.defaults.headers, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 请求方法
|
||||||
|
*/
|
||||||
|
request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
|
||||||
|
let conf: AxiosRequestConfig = cloneDeep(config);
|
||||||
|
const transform = this.getTransform();
|
||||||
|
|
||||||
|
const { requestOptions } = this.options;
|
||||||
|
|
||||||
|
const opt: RequestOptions = Object.assign({}, requestOptions, options);
|
||||||
|
|
||||||
|
const { beforeRequestHook, requestCatch, transformRequestData } = transform || {};
|
||||||
|
if (beforeRequestHook && isFunction(beforeRequestHook)) {
|
||||||
|
conf = beforeRequestHook(conf, opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//这里重新 赋值成最新的配置
|
||||||
|
// @ts-ignore
|
||||||
|
conf.requestOptions = opt;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.axiosInstance
|
||||||
|
.request<any, AxiosResponse<Result>>(conf)
|
||||||
|
.then((res: AxiosResponse<Result>) => {
|
||||||
|
// 请求是否被取消
|
||||||
|
const isCancel = axios.isCancel(res);
|
||||||
|
if (transformRequestData && isFunction(transformRequestData) && !isCancel) {
|
||||||
|
try {
|
||||||
|
const ret = transformRequestData(res, opt);
|
||||||
|
resolve(ret);
|
||||||
|
} catch (err) {
|
||||||
|
reject(err || new Error('request error!'));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(res as unknown as Promise<T>);
|
||||||
|
})
|
||||||
|
.catch((e: Error) => {
|
||||||
|
if (requestCatch && isFunction(requestCatch)) {
|
||||||
|
reject(requestCatch(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reject(e);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 创建axios实例
|
||||||
|
*/
|
||||||
|
private createAxios(config: CreateAxiosOptions): void {
|
||||||
|
this.axiosInstance = axios.create(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTransform() {
|
||||||
|
const { transform } = this.options;
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 文件上传
|
||||||
|
*/
|
||||||
|
uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
|
||||||
|
const formData = new window.FormData();
|
||||||
|
const customFilename = params.name || 'file';
|
||||||
|
|
||||||
|
let conf: AxiosRequestConfig = cloneDeep(config);
|
||||||
|
const transform = this.getTransform();
|
||||||
|
|
||||||
|
const { requestOptions } = this.options;
|
||||||
|
|
||||||
|
const opt: RequestOptions = Object.assign({}, requestOptions);
|
||||||
|
|
||||||
|
const { beforeRequestHook } = transform || {};
|
||||||
|
if (beforeRequestHook && isFunction(beforeRequestHook)) {
|
||||||
|
conf = beforeRequestHook(conf, opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.filename) {
|
||||||
|
formData.append(customFilename, params.file, params.filename);
|
||||||
|
} else {
|
||||||
|
formData.append(customFilename, params.file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.data) {
|
||||||
|
Object.keys(params.data).forEach((key) => {
|
||||||
|
const value = params.data![key];
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach((item) => {
|
||||||
|
formData.append(`${key}[]`, item);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
formData.append(key, params.data![key]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.axiosInstance.request<T>({
|
||||||
|
...conf,
|
||||||
|
method: 'POST',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-type': ContentTypeEnum.FORM_DATA,
|
||||||
|
ignoreCancelToken: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 拦截器配置
|
||||||
|
*/
|
||||||
|
private setupInterceptors() {
|
||||||
|
const transform = this.getTransform();
|
||||||
|
if (!transform) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
requestInterceptors,
|
||||||
|
requestInterceptorsCatch,
|
||||||
|
responseInterceptors,
|
||||||
|
responseInterceptorsCatch,
|
||||||
|
} = transform;
|
||||||
|
|
||||||
|
const axiosCanceler = new AxiosCanceler();
|
||||||
|
|
||||||
|
// 请求拦截器配置处理
|
||||||
|
// @ts-ignore
|
||||||
|
this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
|
||||||
|
// @ts-ignore
|
||||||
|
const { headers: { ignoreCancelToken } } = config;
|
||||||
|
const ignoreCancel =
|
||||||
|
ignoreCancelToken !== undefined
|
||||||
|
? ignoreCancelToken
|
||||||
|
: this.options.requestOptions?.ignoreCancelToken;
|
||||||
|
|
||||||
|
!ignoreCancel && axiosCanceler.addPending(config);
|
||||||
|
if (requestInterceptors && isFunction(requestInterceptors)) {
|
||||||
|
config = requestInterceptors(config, this.options);
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}, undefined);
|
||||||
|
|
||||||
|
// 请求拦截器错误捕获
|
||||||
|
requestInterceptorsCatch &&
|
||||||
|
isFunction(requestInterceptorsCatch) &&
|
||||||
|
this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);
|
||||||
|
|
||||||
|
// 响应结果拦截器处理
|
||||||
|
this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
|
||||||
|
res && axiosCanceler.removePending(res.config);
|
||||||
|
if (responseInterceptors && isFunction(responseInterceptors)) {
|
||||||
|
res = responseInterceptors(res, {});
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}, undefined);
|
||||||
|
|
||||||
|
// 响应结果拦截器错误捕获
|
||||||
|
responseInterceptorsCatch &&
|
||||||
|
isFunction(responseInterceptorsCatch) &&
|
||||||
|
this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/utils/http/axios/axiosCancel.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import axios, { AxiosRequestConfig, Canceler } from 'axios';
|
||||||
|
import qs from 'qs';
|
||||||
|
|
||||||
|
import { isFunction } from '@/utils/is';
|
||||||
|
|
||||||
|
// 声明一个 Map 用于存储每个请求的标识 和 取消函数
|
||||||
|
let pendingMap = new Map<string, Canceler>();
|
||||||
|
|
||||||
|
export const getPendingUrl = (config: AxiosRequestConfig) =>
|
||||||
|
[config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)].join('&');
|
||||||
|
|
||||||
|
export class AxiosCanceler {
|
||||||
|
/**
|
||||||
|
* 添加请求
|
||||||
|
* @param {Object} config
|
||||||
|
*/
|
||||||
|
addPending(config: AxiosRequestConfig) {
|
||||||
|
this.removePending(config);
|
||||||
|
const url = getPendingUrl(config);
|
||||||
|
config.cancelToken =
|
||||||
|
config.cancelToken ||
|
||||||
|
new axios.CancelToken((cancel) => {
|
||||||
|
if (!pendingMap.has(url)) {
|
||||||
|
// 如果 pending 中不存在当前请求,则添加进去
|
||||||
|
pendingMap.set(url, cancel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 清空所有pending
|
||||||
|
*/
|
||||||
|
removeAllPending() {
|
||||||
|
pendingMap.forEach((cancel) => {
|
||||||
|
cancel && isFunction(cancel) && cancel();
|
||||||
|
});
|
||||||
|
pendingMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除请求
|
||||||
|
* @param {Object} config
|
||||||
|
*/
|
||||||
|
removePending(config: AxiosRequestConfig) {
|
||||||
|
const url = getPendingUrl(config);
|
||||||
|
|
||||||
|
if (pendingMap.has(url)) {
|
||||||
|
// 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
|
||||||
|
const cancel = pendingMap.get(url);
|
||||||
|
cancel && cancel(url);
|
||||||
|
pendingMap.delete(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 重置
|
||||||
|
*/
|
||||||
|
reset(): void {
|
||||||
|
pendingMap = new Map<string, Canceler>();
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/utils/http/axios/axiosTransform.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* 数据处理类,可以根据项目自行配置
|
||||||
|
*/
|
||||||
|
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||||
|
import type { RequestOptions, Result } from './types';
|
||||||
|
|
||||||
|
export interface CreateAxiosOptions extends AxiosRequestConfig {
|
||||||
|
authenticationScheme?: string;
|
||||||
|
transform?: AxiosTransform;
|
||||||
|
requestOptions?: RequestOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class AxiosTransform {
|
||||||
|
/**
|
||||||
|
* @description: 请求之前处理配置
|
||||||
|
* @description: Process configuration before request
|
||||||
|
*/
|
||||||
|
beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 请求成功处理
|
||||||
|
*/
|
||||||
|
transformRequestData?: (res: AxiosResponse<Result>, options: RequestOptions) => any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 请求失败处理
|
||||||
|
*/
|
||||||
|
requestCatch?: (e: Error) => Promise<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 请求之前的拦截器
|
||||||
|
*/
|
||||||
|
requestInterceptors?: (
|
||||||
|
config: AxiosRequestConfig,
|
||||||
|
options: CreateAxiosOptions
|
||||||
|
) => AxiosRequestConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 请求之后的拦截器
|
||||||
|
*/
|
||||||
|
responseInterceptors?: (res: AxiosResponse<any>, options: RequestOptions) => AxiosResponse<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 请求之前的拦截器错误处理
|
||||||
|
*/
|
||||||
|
requestInterceptorsCatch?: (error: Error) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 请求之后的拦截器错误处理
|
||||||
|
*/
|
||||||
|
responseInterceptorsCatch?: (error: Error) => void;
|
||||||
|
}
|
||||||
47
src/utils/http/axios/checkStatus.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { showFailToast } from 'vant';
|
||||||
|
export function checkStatus(status: number, msg: string): void {
|
||||||
|
switch (status) {
|
||||||
|
case 400:
|
||||||
|
showFailToast(msg);
|
||||||
|
break;
|
||||||
|
// 401: 未登录
|
||||||
|
// 未登录则跳转登录页面,并携带当前页面的路径
|
||||||
|
// 在登录成功后返回当前页面,这一步需要在登录页操作。
|
||||||
|
case 401:
|
||||||
|
showFailToast('用户没有权限(令牌、用户名、密码错误)!');
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
showFailToast('用户得到授权,但是访问是被禁止的。!');
|
||||||
|
break;
|
||||||
|
// 404请求不存在
|
||||||
|
case 404:
|
||||||
|
showFailToast('网络请求错误,未找到该资源!');
|
||||||
|
break;
|
||||||
|
case 405:
|
||||||
|
showFailToast('网络请求错误,请求方法未允许!');
|
||||||
|
break;
|
||||||
|
case 408:
|
||||||
|
showFailToast('网络请求超时');
|
||||||
|
break;
|
||||||
|
case 500:
|
||||||
|
showFailToast('服务器错误,请联系管理员!');
|
||||||
|
break;
|
||||||
|
case 501:
|
||||||
|
showFailToast('网络未实现');
|
||||||
|
break;
|
||||||
|
case 502:
|
||||||
|
showFailToast('网络错误');
|
||||||
|
break;
|
||||||
|
case 503:
|
||||||
|
showFailToast('服务不可用,服务器暂时过载或维护!');
|
||||||
|
break;
|
||||||
|
case 504:
|
||||||
|
showFailToast('网络超时');
|
||||||
|
break;
|
||||||
|
case 505:
|
||||||
|
showFailToast('http版本不支持该请求!');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
showFailToast(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/utils/http/axios/helper.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { isObject, isString } from '@/utils/is';
|
||||||
|
|
||||||
|
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm';
|
||||||
|
|
||||||
|
export function joinTimestamp<T extends boolean>(
|
||||||
|
join: boolean,
|
||||||
|
restful: T
|
||||||
|
): T extends true ? string : object;
|
||||||
|
|
||||||
|
export function joinTimestamp(join: boolean, restful = false): string | object {
|
||||||
|
if (!join) {
|
||||||
|
return restful ? '' : {};
|
||||||
|
}
|
||||||
|
const now = new Date().getTime();
|
||||||
|
if (restful) {
|
||||||
|
return `?_t=${now}`;
|
||||||
|
}
|
||||||
|
return { _t: now };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: Format request parameter time
|
||||||
|
*/
|
||||||
|
export function formatRequestDate(params: Recordable) {
|
||||||
|
if (Object.prototype.toString.call(params) !== '[object Object]') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in params) {
|
||||||
|
if (params[key] && params[key]._isAMomentObject) {
|
||||||
|
params[key] = params[key].format(DATE_TIME_FORMAT);
|
||||||
|
}
|
||||||
|
if (isString(key)) {
|
||||||
|
const value = params[key];
|
||||||
|
if (value) {
|
||||||
|
try {
|
||||||
|
params[key] = isString(value) ? value.trim() : value;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error as any);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isObject(params[key])) {
|
||||||
|
formatRequestDate(params[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
289
src/utils/http/axios/index.ts
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
// axios配置 可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动
|
||||||
|
import {showDialog, showFailToast, showSuccessToast} from 'vant';
|
||||||
|
import { VAxios } from './Axios';
|
||||||
|
import { AxiosTransform } from './axiosTransform';
|
||||||
|
import axios, { AxiosResponse } from 'axios';
|
||||||
|
import { checkStatus } from './checkStatus';
|
||||||
|
import { joinTimestamp, formatRequestDate } from './helper';
|
||||||
|
import { RequestEnum, ResultEnum, ContentTypeEnum } from '@/enums/httpEnum';
|
||||||
|
|
||||||
|
import { isString } from '@/utils/is/';
|
||||||
|
import { deepMerge, isUrl } from '@/utils';
|
||||||
|
import { setObjToUrlParams } from '@/utils/urlUtils';
|
||||||
|
|
||||||
|
import { RequestOptions, Result, CreateAxiosOptions } from './types';
|
||||||
|
|
||||||
|
import { useUserStoreWidthOut } from '@/store/modules/user';
|
||||||
|
|
||||||
|
const urlPrefix = '';
|
||||||
|
|
||||||
|
// import router from '@/router';
|
||||||
|
// import { storage } from '@/utils/Storage';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 数据处理,方便区分多种处理方式
|
||||||
|
*/
|
||||||
|
const transform: AxiosTransform = {
|
||||||
|
/**
|
||||||
|
* @description: 处理请求数据
|
||||||
|
*/
|
||||||
|
transformRequestData: (res: AxiosResponse<Result>, options: RequestOptions) => {
|
||||||
|
const {
|
||||||
|
isShowMessage = false,
|
||||||
|
isShowErrorMessage,
|
||||||
|
isShowSuccessMessage,
|
||||||
|
successMessageText,
|
||||||
|
errorMessageText,
|
||||||
|
isTransformResponse,
|
||||||
|
isReturnNativeResponse,
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
// 是否返回原生响应头 比如:需要获取响应头时使用该属性
|
||||||
|
if (isReturnNativeResponse) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// 不进行任何处理,直接返回
|
||||||
|
// 用于页面代码可能需要直接获取code,data,message这些信息时开启
|
||||||
|
if (!isTransformResponse) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const $dialog = window['$dialog'];
|
||||||
|
// const $message = window['$message'];
|
||||||
|
|
||||||
|
if (!res.data) {
|
||||||
|
// return '[HTTP] Request has no return value';
|
||||||
|
throw new Error('请求出错,请稍候重试');
|
||||||
|
}
|
||||||
|
// rows 特殊处理
|
||||||
|
if (res.data.rows) {
|
||||||
|
res.data.data = res.data.rows
|
||||||
|
}
|
||||||
|
// 这里 code,result,message为 后台统一的字段,需要修改为项目自己的接口返回格式
|
||||||
|
const { code, data, msg } = res.data;
|
||||||
|
// 是否显示提示信息
|
||||||
|
if (isShowMessage) {
|
||||||
|
// 请求成功
|
||||||
|
const hasSuccess = data && Reflect.has(res.data, 'code') && code === ResultEnum.SUCCESS;
|
||||||
|
if (hasSuccess && (successMessageText || isShowSuccessMessage)) {
|
||||||
|
// 是否显示自定义信息提示
|
||||||
|
showSuccessToast(successMessageText || msg || '操作成功!');
|
||||||
|
} else if (!hasSuccess && (errorMessageText || isShowErrorMessage)) {
|
||||||
|
// 是否显示自定义信息提示
|
||||||
|
showFailToast(msg || errorMessageText || '操作失败!');
|
||||||
|
} else if (!hasSuccess && options.errorMessageMode === 'modal') {
|
||||||
|
// errorMessageMode=‘custom-modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
|
||||||
|
showDialog({
|
||||||
|
title: '提示',
|
||||||
|
message: msg,
|
||||||
|
confirmButtonText: '确定'
|
||||||
|
}).then(() => {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接口请求成功,直接返回结果
|
||||||
|
if (code === ResultEnum.SUCCESS) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
// 接口请求错误,统一提示错误信息 这里逻辑可以根据项目进行修改
|
||||||
|
let errorMsg = msg;
|
||||||
|
switch (code) {
|
||||||
|
// 请求失败
|
||||||
|
case ResultEnum.ERROR:
|
||||||
|
showFailToast(errorMsg);
|
||||||
|
break;
|
||||||
|
// 登录超时
|
||||||
|
// case ResultEnum.TIMEOUT:
|
||||||
|
// const LoginName = PageEnum.BASE_LOGIN_NAME;
|
||||||
|
// const LoginPath = PageEnum.BASE_LOGIN;
|
||||||
|
// if (router.currentRoute.value?.name === LoginName) return;
|
||||||
|
// // 到登录页
|
||||||
|
// errorMsg = '登录超时,请重新登录!';
|
||||||
|
// $dialog.warning({
|
||||||
|
// title: '提示',
|
||||||
|
// content: '登录身份已失效,请重新登录!',
|
||||||
|
// positiveText: '确定',
|
||||||
|
// //negativeText: '取消',
|
||||||
|
// closable: false,
|
||||||
|
// maskClosable: false,
|
||||||
|
// onPositiveClick: () => {
|
||||||
|
// storage.clear();
|
||||||
|
// window.location.href = LoginPath;
|
||||||
|
// },
|
||||||
|
// onNegativeClick: () => {},
|
||||||
|
// });
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
throw new Error(errorMsg);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 请求之前处理config
|
||||||
|
beforeRequestHook: (config, options) => {
|
||||||
|
const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options;
|
||||||
|
|
||||||
|
const isUrlStr = isUrl(config.url as string);
|
||||||
|
|
||||||
|
if (!isUrlStr && joinPrefix) {
|
||||||
|
config.url = `${urlPrefix}${config.url}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isUrlStr && apiUrl && isString(apiUrl)) {
|
||||||
|
config.url = `${apiUrl}${config.url}`;
|
||||||
|
}
|
||||||
|
const params = config.params || {};
|
||||||
|
const data = config.data || false;
|
||||||
|
if (config.method?.toUpperCase() === RequestEnum.GET) {
|
||||||
|
if (!isString(params)) {
|
||||||
|
// 给 get 请求加上时间戳参数,避免从缓存中拿数据。
|
||||||
|
config.params = Object.assign(params || {}, joinTimestamp(joinTime, false));
|
||||||
|
} else {
|
||||||
|
// 兼容restful风格
|
||||||
|
config.url = config.url + params + `${joinTimestamp(joinTime, true)}`;
|
||||||
|
config.params = undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!isString(params)) {
|
||||||
|
formatDate && formatRequestDate(params);
|
||||||
|
if (Reflect.has(config, 'data') && config.data && Object.keys(config.data).length > 0) {
|
||||||
|
config.data = data;
|
||||||
|
config.params = params;
|
||||||
|
} else {
|
||||||
|
config.data = params;
|
||||||
|
config.params = undefined;
|
||||||
|
}
|
||||||
|
if (joinParamsToUrl) {
|
||||||
|
config.url = setObjToUrlParams(
|
||||||
|
config.url as string,
|
||||||
|
Object.assign({}, config.params, config.data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 兼容restful风格
|
||||||
|
config.url = config.url + params;
|
||||||
|
config.params = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 请求拦截器处理
|
||||||
|
*/
|
||||||
|
requestInterceptors: (config, options) => {
|
||||||
|
// 请求之前处理config
|
||||||
|
const headers = (config as Recordable).headers
|
||||||
|
const userStore = useUserStoreWidthOut();
|
||||||
|
const token = userStore.getToken;
|
||||||
|
headers.TYPE = 'TBS'
|
||||||
|
headers.channelId = 'MP'
|
||||||
|
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
|
||||||
|
// jwt token
|
||||||
|
headers.Authorization = options.authenticationScheme
|
||||||
|
? `${options.authenticationScheme} ${token}`
|
||||||
|
: token;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 响应错误处理
|
||||||
|
*/
|
||||||
|
responseInterceptorsCatch: (error: any) => {
|
||||||
|
// const $dialog = window['$dialog'];
|
||||||
|
// const $message = window['$message'];
|
||||||
|
const { response, code, message } = error || {};
|
||||||
|
// TODO 此处要根据后端接口返回格式修改
|
||||||
|
const msg: string =
|
||||||
|
response && response.data && response.data.message ? response.data.message : '';
|
||||||
|
const err: string = error.toString();
|
||||||
|
try {
|
||||||
|
if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
|
||||||
|
showFailToast('接口请求超时,请刷新页面重试!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (err && err.includes('Network Error')) {
|
||||||
|
showFailToast('请检查您的网络连接是否正常')
|
||||||
|
// $dialog.info({
|
||||||
|
// title: '网络异常',
|
||||||
|
// content: '请检查您的网络连接是否正常',
|
||||||
|
// positiveText: '确定',
|
||||||
|
// //negativeText: '取消',
|
||||||
|
// closable: false,
|
||||||
|
// maskClosable: false,
|
||||||
|
// onPositiveClick: () => {},
|
||||||
|
// onNegativeClick: () => {},
|
||||||
|
// });
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error as any);
|
||||||
|
}
|
||||||
|
// 请求是否被取消
|
||||||
|
const isCancel = axios.isCancel(error);
|
||||||
|
if (!isCancel) {
|
||||||
|
checkStatus(error.response && error.response.status, msg);
|
||||||
|
} else {
|
||||||
|
console.warn(error, '请求被取消!');
|
||||||
|
}
|
||||||
|
//return Promise.reject(error);
|
||||||
|
return Promise.reject(response?.data);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
||||||
|
return new VAxios(
|
||||||
|
deepMerge(
|
||||||
|
{
|
||||||
|
timeout: 10 * 1000,
|
||||||
|
authenticationScheme: '',
|
||||||
|
// 接口前缀
|
||||||
|
prefixUrl: urlPrefix,
|
||||||
|
headers: { 'Content-Type': ContentTypeEnum.JSON },
|
||||||
|
// 数据处理方式
|
||||||
|
transform,
|
||||||
|
// 配置项,下面的选项都可以在独立的接口请求中覆盖
|
||||||
|
requestOptions: {
|
||||||
|
// 默认将prefix 添加到url
|
||||||
|
joinPrefix: true,
|
||||||
|
// 是否返回原生响应头 比如:需要获取响应头时使用该属性
|
||||||
|
isReturnNativeResponse: false,
|
||||||
|
// 需要对返回数据进行处理
|
||||||
|
isTransformResponse: true,
|
||||||
|
// post请求的时候添加参数到url
|
||||||
|
joinParamsToUrl: false,
|
||||||
|
// 格式化提交参数时间
|
||||||
|
formatDate: true,
|
||||||
|
// 消息提示类型
|
||||||
|
errorMessageMode: 'none',
|
||||||
|
// 接口地址
|
||||||
|
apiUrl: 'http://101.32.189.213:2781',
|
||||||
|
// apiUrl: '',
|
||||||
|
// 接口拼接地址
|
||||||
|
urlPrefix: urlPrefix,
|
||||||
|
// 是否加入时间戳
|
||||||
|
joinTime: true,
|
||||||
|
// 忽略重复请求
|
||||||
|
ignoreCancelToken: true,
|
||||||
|
// 是否携带token
|
||||||
|
withToken: true,
|
||||||
|
},
|
||||||
|
withCredentials: false,
|
||||||
|
},
|
||||||
|
opt || {}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const http = createAxios();
|
||||||
|
|
||||||
|
// 项目,多个不同 api 地址,直接在这里导出多个
|
||||||
|
// src/api ts 里面接口,就可以单独使用这个请求,
|
||||||
|
// import { httpTwo } from '@/utils/http/axios'
|
||||||
|
// export const httpTwo = createAxios({
|
||||||
|
// requestOptions: {
|
||||||
|
// apiUrl: 'http://localhost:9001',
|
||||||
|
// urlPrefix: 'api',
|
||||||
|
// },
|
||||||
|
// });
|
||||||
66
src/utils/http/axios/types.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { AxiosRequestConfig } from 'axios';
|
||||||
|
import { AxiosTransform } from '@/utils/http/axios/axiosTransform';
|
||||||
|
|
||||||
|
export interface CreateAxiosOptions extends AxiosRequestConfig {
|
||||||
|
transform?: AxiosTransform;
|
||||||
|
requestOptions?: RequestOptions;
|
||||||
|
authenticationScheme?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
export interface UploadFileParams {
|
||||||
|
// 其他参数
|
||||||
|
data?: Recordable;
|
||||||
|
// 文件参数接口字段名
|
||||||
|
name?: string;
|
||||||
|
// 文件
|
||||||
|
file: File | Blob;
|
||||||
|
// 文件名称
|
||||||
|
filename?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RequestOptions {
|
||||||
|
// 请求参数拼接到url
|
||||||
|
joinParamsToUrl?: boolean;
|
||||||
|
// 格式化请求参数时间
|
||||||
|
formatDate?: boolean;
|
||||||
|
// 是否显示提示信息
|
||||||
|
isShowMessage?: boolean;
|
||||||
|
// 是否解析成JSON
|
||||||
|
isParseToJson?: boolean;
|
||||||
|
// 成功的文本信息
|
||||||
|
successMessageText?: string;
|
||||||
|
// 是否显示成功信息
|
||||||
|
isShowSuccessMessage?: boolean;
|
||||||
|
// 是否显示失败信息
|
||||||
|
isShowErrorMessage?: boolean;
|
||||||
|
// 错误的文本信息
|
||||||
|
errorMessageText?: string;
|
||||||
|
// 是否加入url
|
||||||
|
joinPrefix?: boolean;
|
||||||
|
// 接口地址, 不填则使用默认apiUrl
|
||||||
|
apiUrl?: string;
|
||||||
|
// 请求拼接路径
|
||||||
|
urlPrefix?: string;
|
||||||
|
// 错误消息提示类型
|
||||||
|
errorMessageMode?: 'none' | 'modal';
|
||||||
|
// 是否添加时间戳
|
||||||
|
joinTime?: boolean;
|
||||||
|
// 不进行任何处理,直接返回
|
||||||
|
isTransformResponse?: boolean;
|
||||||
|
// 是否返回原生响应头
|
||||||
|
isReturnNativeResponse?: boolean;
|
||||||
|
//忽略重复请求
|
||||||
|
ignoreCancelToken?: boolean;
|
||||||
|
// 是否携带token
|
||||||
|
withToken?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Result<T = any> {
|
||||||
|
code: Number;
|
||||||
|
type?: 'success' | 'error' | 'warning';
|
||||||
|
msg: string;
|
||||||
|
data?: T;
|
||||||
|
rows?: T;
|
||||||
|
}
|
||||||
278
src/utils/index.ts
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
import {isObject} from "@/utils/is";
|
||||||
|
|
||||||
|
/** px 转 vw, 375 是设置的屏幕宽度 */
|
||||||
|
export const px2vw = (px: number): string => {
|
||||||
|
return `${window.screen.width / 750 * px / window.screen.width * 100}vw`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否 url
|
||||||
|
* */
|
||||||
|
export function isUrl(url: string) {
|
||||||
|
return /^(http|https):\/\//g.test(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
|
||||||
|
let key: string;
|
||||||
|
for (key in target) {
|
||||||
|
src[key] = isObject(src[key]) ? deepMerge(src[key], target[key]) : (src[key] = target[key]);
|
||||||
|
}
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ¥6,285
|
||||||
|
export function toIntMark(value) {
|
||||||
|
try {
|
||||||
|
if (value.toString().includes('%')) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
const newVal = parseFloat(value).toFixed(0)
|
||||||
|
if (newVal == 'NaN') {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return '¥' + newVal.replace(/(\d{1,3})(?=(\d{3})+(?:$|\.))/g, '$1,')
|
||||||
|
} catch (e) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6,285
|
||||||
|
export function toInt(value) {
|
||||||
|
try {
|
||||||
|
if (value.toString().includes('%')) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
const newVal = parseFloat(value).toFixed(0)
|
||||||
|
if (newVal == 'NaN') {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return newVal.replace(/(\d{1,3})(?=(\d{3})+(?:$|\.))/g, '$1,')
|
||||||
|
} catch (e) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ¥6k
|
||||||
|
export function toThousand(value) {
|
||||||
|
try {
|
||||||
|
if (!value) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if (value.toString().includes('%')) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
const newVal = parseFloat(value / 1000 + '').toFixed(0)
|
||||||
|
if (newVal == 'NaN') {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return '¥' + newVal.replace(/(\d{1,3})(?=(\d{3})+(?:$|\.))/g, '$1,') + 'k'
|
||||||
|
} catch (e) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ¥6,285.00
|
||||||
|
export function toRoundMark(value: number | string) {
|
||||||
|
value = value || 0
|
||||||
|
const options = {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'CNY',
|
||||||
|
};
|
||||||
|
return value.toLocaleString('zh-CN', options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6285.00
|
||||||
|
export function toRound(value) {
|
||||||
|
try {
|
||||||
|
if (value.toString().includes('%')) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
const newVal = parseFloat(value).toFixed(2)
|
||||||
|
if (newVal == 'NaN') {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return newVal
|
||||||
|
} catch (e) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const regFenToYuan = (fen) => {
|
||||||
|
let num;
|
||||||
|
num=fen*0.01;
|
||||||
|
num+='';
|
||||||
|
const reg = num.indexOf('.') > -1 ? /(\d{1,3})(?=(?:\d{3})+\.)/g : /(\d{1,3})(?=(?:\d{3})+$)/g;
|
||||||
|
num=num.replace(reg,'$1');
|
||||||
|
num = toDecimal2(num)
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
|
||||||
|
export const toDecimal2 = (x) => {
|
||||||
|
let f = parseFloat(x);
|
||||||
|
if (isNaN(f)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
f = Math.round(x * 100) / 100;
|
||||||
|
let s = f.toString();
|
||||||
|
let rs = s.indexOf('.');
|
||||||
|
if (rs < 0) {
|
||||||
|
rs = s.length;
|
||||||
|
s += '.';
|
||||||
|
}
|
||||||
|
while (s.length <= rs + 2) {
|
||||||
|
s += '0';
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param customStyle
|
||||||
|
* @param target
|
||||||
|
*/
|
||||||
|
export function addStyle(customStyle, target = 'object') {
|
||||||
|
// 字符串转字符串,对象转对象情形,直接返回
|
||||||
|
if (empty(customStyle) || typeof(customStyle) === 'object' && target === 'object' || target === 'string' &&
|
||||||
|
typeof(customStyle) === 'string') {
|
||||||
|
return customStyle
|
||||||
|
}
|
||||||
|
// 字符串转对象
|
||||||
|
if (target === 'object') {
|
||||||
|
// 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的
|
||||||
|
customStyle = customStyle.clearSpaces('both')
|
||||||
|
// 根据";"将字符串转为数组形式
|
||||||
|
const styleArray = customStyle.split(';')
|
||||||
|
const style = {}
|
||||||
|
// 历遍数组,拼接成对象
|
||||||
|
for (let i = 0; i < styleArray.length; i++) {
|
||||||
|
// 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤
|
||||||
|
if (styleArray[i]) {
|
||||||
|
const item = styleArray[i].split(':')
|
||||||
|
style[item[0].clearSpaces('both')] = item[1].clearSpaces('both')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return style
|
||||||
|
}
|
||||||
|
// 这里为对象转字符串形式
|
||||||
|
let string = ''
|
||||||
|
for (const i in customStyle) {
|
||||||
|
// 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名
|
||||||
|
const key = i.replace(/([A-Z])/g, '-$1').toLowerCase()
|
||||||
|
string += `${key}:${customStyle[i]};`
|
||||||
|
}
|
||||||
|
// 去除两端空格
|
||||||
|
return string.clearSpaces('both')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为空
|
||||||
|
*/
|
||||||
|
export function empty(value) {
|
||||||
|
switch (typeof value) {
|
||||||
|
case 'undefined':
|
||||||
|
return true
|
||||||
|
case 'string':
|
||||||
|
if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length === 0) return true
|
||||||
|
break
|
||||||
|
case 'boolean':
|
||||||
|
if (!value) return true
|
||||||
|
break
|
||||||
|
case 'number':
|
||||||
|
if (value === 0 || isNaN(value)) return true
|
||||||
|
break
|
||||||
|
case 'object':
|
||||||
|
if (value === null || value.length === 0) return true
|
||||||
|
// for (const i in value) {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本地图片
|
||||||
|
* @param name // 文件名 如 doc.png
|
||||||
|
* @returns {*|string}
|
||||||
|
*/
|
||||||
|
export function getAssetsImages(name) {
|
||||||
|
return new URL(`/src/assets/images/${name}`, import.meta.url).href;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加法 (处理精度问题)
|
||||||
|
* @param num1
|
||||||
|
* @param num2
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function numAdd(num1, num2) {
|
||||||
|
let baseNum, baseNum1, baseNum2, precision // 精度
|
||||||
|
try {
|
||||||
|
baseNum1 = num1.toString().split('.')[1].length
|
||||||
|
} catch (e) {
|
||||||
|
baseNum1 = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
baseNum2 = num2.toString().split('.')[1].length
|
||||||
|
} catch (e) {
|
||||||
|
baseNum2 = 0
|
||||||
|
}
|
||||||
|
baseNum = Math.pow(10, Math.max(baseNum1, baseNum2))
|
||||||
|
precision = (baseNum1 >= baseNum2) ? baseNum1 : baseNum2
|
||||||
|
return ((num1 * baseNum + num2 * baseNum) / baseNum).toFixed(precision)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 减法 (处理精度问题)
|
||||||
|
* @param num1
|
||||||
|
* @param num2
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function numSub(num1, num2) {
|
||||||
|
let baseNum, baseNum1, baseNum2, precision // 精度
|
||||||
|
try {
|
||||||
|
baseNum1 = num1.toString().split('.')[1].length
|
||||||
|
} catch (e) {
|
||||||
|
baseNum1 = 0
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
baseNum2 = num2.toString().split('.')[1].length
|
||||||
|
} catch (e) {
|
||||||
|
baseNum2 = 0
|
||||||
|
}
|
||||||
|
baseNum = Math.pow(10, Math.max(baseNum1, baseNum2))
|
||||||
|
precision = (baseNum1 >= baseNum2) ? baseNum1 : baseNum2
|
||||||
|
return ((num1 * baseNum - num2 * baseNum) / baseNum).toFixed(precision)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打电话
|
||||||
|
* @param phoneNumber
|
||||||
|
*/
|
||||||
|
export const callPhone = (phoneNumber) => {
|
||||||
|
if (phoneNumber) {
|
||||||
|
window.location.href = "tel://" + phoneNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function debounce(func: Function, time: number, immediate = false) {
|
||||||
|
let timer: number | null = null;
|
||||||
|
return (...args: any) => {
|
||||||
|
if (timer) clearInterval(timer)
|
||||||
|
if (immediate) {
|
||||||
|
if (!timer) func.apply(this, args);
|
||||||
|
timer = window.setTimeout(() => {
|
||||||
|
timer = null
|
||||||
|
}, time)
|
||||||
|
} else {
|
||||||
|
timer = window.setTimeout(() => {
|
||||||
|
func.apply(this, args)
|
||||||
|
}, time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
118
src/utils/is/index.ts
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
const toString = Object.prototype.toString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 判断值是否未某个类型
|
||||||
|
*/
|
||||||
|
export function is(val: unknown, type: string) {
|
||||||
|
return toString.call(val) === `[object ${type}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 是否为函数
|
||||||
|
*/
|
||||||
|
export function isFunction<T = Function>(val: unknown): val is T {
|
||||||
|
return is(val, 'Function') || is(val, 'AsyncFunction');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 是否已定义
|
||||||
|
*/
|
||||||
|
export const isDef = <T = unknown>(val?: T): val is T => {
|
||||||
|
return typeof val !== 'undefined';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isUnDef = <T = unknown>(val?: T): val is T => {
|
||||||
|
return !isDef(val);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @description: 是否为对象
|
||||||
|
*/
|
||||||
|
export const isObject = (val: any): val is Record<any, any> => {
|
||||||
|
return val !== null && is(val, 'Object');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 是否为时间
|
||||||
|
*/
|
||||||
|
export function isDate(val: unknown): val is Date {
|
||||||
|
return is(val, 'Date');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 是否为数值
|
||||||
|
*/
|
||||||
|
export function isNumber(val: unknown): val is number {
|
||||||
|
return is(val, 'Number');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 是否为AsyncFunction
|
||||||
|
*/
|
||||||
|
export function isAsyncFunction<T = any>(val: unknown): val is () => Promise<T> {
|
||||||
|
return is(val, 'AsyncFunction');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 是否为promise
|
||||||
|
*/
|
||||||
|
export function isPromise<T = any>(val: unknown): val is Promise<T> {
|
||||||
|
return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 是否为字符串
|
||||||
|
*/
|
||||||
|
export function isString(val: unknown): val is string {
|
||||||
|
return is(val, 'String');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 是否为boolean类型
|
||||||
|
*/
|
||||||
|
export function isBoolean(val: unknown): val is boolean {
|
||||||
|
return is(val, 'Boolean');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 是否为数组
|
||||||
|
*/
|
||||||
|
export function isArray(val: any): val is Array<any> {
|
||||||
|
return val && Array.isArray(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 是否客户端
|
||||||
|
*/
|
||||||
|
export const isClient = () => {
|
||||||
|
return typeof window !== 'undefined';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 是否为浏览器
|
||||||
|
*/
|
||||||
|
export const isWindow = (val: any): val is Window => {
|
||||||
|
return typeof window !== 'undefined' && is(val, 'Window');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isElement = (val: unknown): val is Element => {
|
||||||
|
return isObject(val) && !!val.tagName;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isServer = typeof window === 'undefined';
|
||||||
|
|
||||||
|
// 是否为图片节点
|
||||||
|
export function isImageDom(o: Element) {
|
||||||
|
return o && ['IMAGE', 'IMG'].includes(o.tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNull(val: unknown): val is null {
|
||||||
|
return val === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNullAndUnDef(val: unknown): val is null | undefined {
|
||||||
|
return isUnDef(val) && isNull(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNullOrUnDef(val: unknown): val is null | undefined {
|
||||||
|
return isUnDef(val) || isNull(val);
|
||||||
|
}
|
||||||
47
src/utils/mapUtil.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import AMapLoader from "@amap/amap-jsapi-loader";
|
||||||
|
import {useUserStore} from "@/store/modules/user";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const getLocation = () => {
|
||||||
|
const user = useUserStore()
|
||||||
|
|
||||||
|
|
||||||
|
AMapLoader.load({
|
||||||
|
key: 'cd9769b4feec118fe176763bd10168a0',
|
||||||
|
version: "2.0",
|
||||||
|
AMapUI: {
|
||||||
|
version: "1.1",
|
||||||
|
},
|
||||||
|
Loca: {
|
||||||
|
version: "2.0.0"
|
||||||
|
},
|
||||||
|
}).then((AMap) => {
|
||||||
|
const map = new AMap.Map("container", {
|
||||||
|
zoom: 15,
|
||||||
|
features: ['bg', 'point', 'road', 'building'],
|
||||||
|
viewMode: '2D', //设置地图模式
|
||||||
|
// center: [116.47394,39.904211],
|
||||||
|
resizeEnable: true
|
||||||
|
})
|
||||||
|
|
||||||
|
AMap.plugin(['AMap.AutoComplete', 'AMap.PlaceSearch', 'AMap.Geolocation', 'AMap.CitySearch'], () => {
|
||||||
|
const geolocation = new AMap.Geolocation({
|
||||||
|
enableHighAccuracy: true, // 是否使用高精度定位,默认:true
|
||||||
|
timeout: 10000, // 设置定位超时时间,默认:无穷大
|
||||||
|
showButton: true, //显示定位按钮,默认:true
|
||||||
|
needAddress: true, //是否需要将定位结果进行逆地理编码操作
|
||||||
|
convert: true, //自动偏移坐标,偏移后的坐标为高德坐标,默认:true
|
||||||
|
position: 'RB',
|
||||||
|
offset: [10, 20],
|
||||||
|
})
|
||||||
|
map.addControl(geolocation)
|
||||||
|
geolocation.getCurrentPosition()
|
||||||
|
// 获取用户当前的精确位置信息
|
||||||
|
geolocation.getCurrentPosition((status, result) => {
|
||||||
|
// myCity.value = result.addressComponent.province
|
||||||
|
user.setLocationMap(result)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
37
src/utils/urlUtils.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* 将对象添加当作参数拼接到URL上面
|
||||||
|
* @param baseUrl 需要拼接的url
|
||||||
|
* @param obj 参数对象
|
||||||
|
* @returns {string} 拼接后的对象
|
||||||
|
* 例子:
|
||||||
|
* let obj = {a: '3', b: '4'}
|
||||||
|
* setObjToUrlParams('www.baidu.com', obj)
|
||||||
|
* ==>www.baidu.com?a=3&b=4
|
||||||
|
*/
|
||||||
|
export function setObjToUrlParams(baseUrl: string, obj: any): string {
|
||||||
|
let parameters = '';
|
||||||
|
let url = '';
|
||||||
|
for (const key in obj) {
|
||||||
|
parameters += key + '=' + encodeURIComponent(obj[key]) + '&';
|
||||||
|
}
|
||||||
|
parameters = parameters.replace(/&$/, '');
|
||||||
|
if (/\?$/.test(baseUrl)) {
|
||||||
|
url = baseUrl + parameters;
|
||||||
|
} else {
|
||||||
|
url = baseUrl.replace(/\/?$/, '?') + parameters;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据name从url中取值
|
||||||
|
* @param name
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function getQueryString(name) {
|
||||||
|
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
|
||||||
|
var r = window.location.search.substr(1).match(reg);
|
||||||
|
if (r != null) return unescape(r[2]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
99
src/utils/wexin.ts
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import wx from "weixin-js-sdk";
|
||||||
|
import {jsapiToken} from "@/api/pay";
|
||||||
|
import {useUserStore} from "@/store/modules/user";
|
||||||
|
import {showToast} from "vant";
|
||||||
|
|
||||||
|
export const getWxConfig = (url) => {
|
||||||
|
const user = useUserStore()
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (user.getWxConfig) {
|
||||||
|
resolve(user.getWxConfig)
|
||||||
|
} else {
|
||||||
|
jsapiToken({
|
||||||
|
url: url
|
||||||
|
}).then(res => {
|
||||||
|
user.setWxConfig(res)
|
||||||
|
resolve(user.getWxConfig)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLocation = (params, callback) => {
|
||||||
|
getWxConfig(params.url).then(res => {
|
||||||
|
wx.config({
|
||||||
|
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
|
||||||
|
appId: res.appId, // 必填,公众号的唯一标识
|
||||||
|
timestamp: res.timestamp, // 必填,生成签名的时间戳
|
||||||
|
nonceStr: res.nonceStr, // 必填,生成签名的随机串
|
||||||
|
signature: res.signature, // 必填,签名
|
||||||
|
jsApiList: ['checkJsApi', 'getLocation'] // 必填,需要使用的JS接口列表
|
||||||
|
})
|
||||||
|
wx.ready(() => {
|
||||||
|
wx.checkJsApi({
|
||||||
|
jsApiList: ['getLocation'],
|
||||||
|
success: function () {
|
||||||
|
wx.getLocation({
|
||||||
|
type: 'gcj02', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
|
||||||
|
success: (res) => {
|
||||||
|
console.log('res11111')
|
||||||
|
callback(res)
|
||||||
|
},
|
||||||
|
fail: (res) => {
|
||||||
|
// showToast('定位失败 -- > ' + JSON.stringify(res))
|
||||||
|
console.error('定位失败')
|
||||||
|
},
|
||||||
|
cancel: function (res) {
|
||||||
|
console.error(res)
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: function (res) {
|
||||||
|
showToast('fail -- > ' + JSON.stringify(res))
|
||||||
|
console.error(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
wx.error(err => {
|
||||||
|
showToast('err -- > ' + JSON.stringify(err))
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const openLocation = (params) => {
|
||||||
|
getWxConfig(params.url).then(res => {
|
||||||
|
wx.config({
|
||||||
|
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
|
||||||
|
appId: res.appId, // 必填,公众号的唯一标识
|
||||||
|
timestamp: res.timestamp, // 必填,生成签名的时间戳
|
||||||
|
nonceStr: res.nonceStr, // 必填,生成签名的随机串
|
||||||
|
signature: res.signature, // 必填,签名
|
||||||
|
jsApiList: ['checkJsApi', 'openLocation'] // 必填,需要使用的JS接口列表
|
||||||
|
})
|
||||||
|
wx.ready(() => {
|
||||||
|
wx.checkJsApi({
|
||||||
|
jsApiList: ['openLocation'],
|
||||||
|
success: function () {
|
||||||
|
wx.openLocation({
|
||||||
|
latitude: params.latitude, // 纬度,浮点数,范围为90 ~ -90
|
||||||
|
longitude: params.longitude, // 经度,浮点数,范围为180 ~ -180。
|
||||||
|
name: '', // 位置名
|
||||||
|
address: '', // 地址详情说明
|
||||||
|
scale: 1, // 地图缩放级别,整型值,范围从1~28。默认为最大
|
||||||
|
infoUrl: '' // 在查看位置界面底部显示的超链接,可点击跳转
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: function (res) {
|
||||||
|
console.error(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
wx.error(err => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
54
src/utils/wexinPay.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import wx from 'weixin-js-sdk'
|
||||||
|
export function pay(signInfo, payData,callback,errorCallBack?) {
|
||||||
|
wx.config({
|
||||||
|
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
|
||||||
|
appId: signInfo.appId, // 必填,公众号的唯一标识
|
||||||
|
timestamp: signInfo.timestamp, // 必填,生成签名的时间戳
|
||||||
|
nonceStr: signInfo.nonceStr, // 必填,生成签名的随机串
|
||||||
|
signature: signInfo.signature, // 必填,签名
|
||||||
|
jsApiList: ['checkJsApi', 'chooseWXPay'] // 必填,需要使用的JS接口列表
|
||||||
|
})
|
||||||
|
|
||||||
|
wx.ready(() => {
|
||||||
|
wx.checkJsApi({
|
||||||
|
jsApiList: ['chooseWXPay'],
|
||||||
|
success: function () {
|
||||||
|
wx.chooseWXPay({
|
||||||
|
// 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
|
||||||
|
// appId: payData.appId,
|
||||||
|
timestamp: payData.timeStamp, // 必填,生成签名的时间戳
|
||||||
|
nonceStr: payData.nonceStr, // 必填,生成签名的随机串
|
||||||
|
package: payData.package,
|
||||||
|
signType: payData.signType,
|
||||||
|
paySign: payData.paySign, // 必填,签名
|
||||||
|
success: function (res) { // 支付成功后的回调函数
|
||||||
|
console.log('支付成功' + res)
|
||||||
|
if (callback) {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: function (reg) {
|
||||||
|
console.log(JSON.stringify(reg))
|
||||||
|
if (errorCallBack) {
|
||||||
|
errorCallBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fail: function (res) {
|
||||||
|
console.log(JSON.stringify(res))
|
||||||
|
if (errorCallBack) {
|
||||||
|
errorCallBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
wx.error(err => {
|
||||||
|
console.log(JSON.stringify(err))
|
||||||
|
if (errorCallBack) {
|
||||||
|
errorCallBack()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
40
src/views/agreement/agreement.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<j-nav-bar navBarBackground="#fff" color="#000" :title="tittleMap[route.query.key]"/>
|
||||||
|
<div :style="{padding: px2vw(20)}">
|
||||||
|
<div v-html="agreement">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {getAgreement} from "@/api";
|
||||||
|
import {onMounted, ref} from "vue";
|
||||||
|
import {useRoute} from "vue-router";
|
||||||
|
import {px2vw} from "@/utils";
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const agreement = ref('')
|
||||||
|
|
||||||
|
const _getSystemConfigOne = (key) => {
|
||||||
|
getAgreement().then(res => {
|
||||||
|
agreement.value = res[key]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const tittleMap = {
|
||||||
|
loansAgreement: '借款协议',
|
||||||
|
serviceAgreement: '平台服务协议',
|
||||||
|
authAgreement: '委托授权协议',
|
||||||
|
lawAgreement: '法律协议',
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
tittleMap[route.query.key] ? document.title = tittleMap[route.query.key] : ''
|
||||||
|
|
||||||
|
_getSystemConfigOne(route.query.key)
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
230
src/views/borrowInfo/index.vue
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
<template>
|
||||||
|
<j-nav-bar color="#FFF" nav-bar-background="#f9bf3a" :placeholder="false"/>
|
||||||
|
<div class="content">
|
||||||
|
<j-gap height="120" background="#F9BF3A" opacity="0"/>
|
||||||
|
<div class="action">
|
||||||
|
<view class="tt">申请时间 {{ new Date(borrowInfo.createTime).format('yyyy-MM-dd hh:mm:ss') }}</view>
|
||||||
|
|
||||||
|
|
||||||
|
<view class="footer-content">
|
||||||
|
<view class="footer-content-1">
|
||||||
|
<van-steps active-icon="success" active-color="#07c160" :active="active">
|
||||||
|
<van-step v-for="(item, index) in stepBorrow.borrowStep" :key="index">{{ item.name }}</van-step>
|
||||||
|
</van-steps>
|
||||||
|
</view>
|
||||||
|
<view class="footer-content-2">
|
||||||
|
<view class="footer-content-2-tit gray_color">温馨提示</view>
|
||||||
|
<view class="footer-content-2-content" :style="{color: stepBorrow.borrowNameStyle || '#e84a10'}">
|
||||||
|
{{ stepBorrow.borrowRemark }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action">
|
||||||
|
<view class="tt">
|
||||||
|
<view>贷款详情</view>
|
||||||
|
</view>
|
||||||
|
<van-cell title="贷款编号" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="borrowInfo.tradeNo" />
|
||||||
|
<van-cell title="借款金额" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="toRoundMark(borrowInfo.totalLoanMoney)" />
|
||||||
|
<!-- <van-cell title="借款期限" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="borrowInfo.totalMonth + '个月'" />-->
|
||||||
|
<van-cell title="贷款周期" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="borrowInfo.totalMonth + '个月'" />
|
||||||
|
<van-cell title="提现银行" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="borrowInfo.bankType" />
|
||||||
|
<van-cell title="每期还款" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="toRoundMark(borrowInfo.avgRepayment)" />
|
||||||
|
<van-cell title="描述" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="borrowInfo.noteRemark" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {onMounted, reactive, ref} from "vue";
|
||||||
|
import {getBorrowInfo, getUserInfo} from "@/api";
|
||||||
|
import {resetData} from "@/utils/dataUtil";
|
||||||
|
import {useRoute} from "vue-router";
|
||||||
|
import {toRoundMark} from "@/utils";
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const active = ref(0);
|
||||||
|
|
||||||
|
const stepBorrow = reactive({
|
||||||
|
"borrowNameStyle": "",
|
||||||
|
"borrowRemark": "",
|
||||||
|
"borrowStep": [
|
||||||
|
{
|
||||||
|
"name": "提交成功",
|
||||||
|
"over": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "银行卡异常",
|
||||||
|
"over": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "到账成功",
|
||||||
|
"over": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const borrowInfo = reactive({
|
||||||
|
"auditFlag": true,
|
||||||
|
"avgRepayment": 0,
|
||||||
|
"backCardNum": "",
|
||||||
|
"bankType": "",
|
||||||
|
"borrowName": "",
|
||||||
|
"borrowNameStyle": "",
|
||||||
|
"borrowRemark": "",
|
||||||
|
"cardBackPicture": "",
|
||||||
|
"cardFrontPicture": "",
|
||||||
|
"cardNum": "",
|
||||||
|
"companyAddress": "",
|
||||||
|
"companyAddressInfo": "",
|
||||||
|
"companyName": "",
|
||||||
|
"companyPhone": "",
|
||||||
|
"companyTitle": "",
|
||||||
|
"companyYear": "",
|
||||||
|
"createTime": "",
|
||||||
|
"customerAddress": "",
|
||||||
|
"customerAddressInfo": "",
|
||||||
|
"customerId": 0,
|
||||||
|
"dueDate": 0,
|
||||||
|
"firstBackCardNum": "",
|
||||||
|
"firstBankType": "",
|
||||||
|
"firstRepayment": 0,
|
||||||
|
"id": 0,
|
||||||
|
"infoJson": "",
|
||||||
|
"kinsfolkName": "",
|
||||||
|
"kinsfolkPhone": "",
|
||||||
|
"kinsfolkRef": "",
|
||||||
|
"loanMonthRate": 0,
|
||||||
|
"loanProcessResp": {
|
||||||
|
"borrowNameStyle": "",
|
||||||
|
"borrowRemark": "",
|
||||||
|
"borrowStep": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"over": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"loanYearRate": 0,
|
||||||
|
"noteRemark": "",
|
||||||
|
"realName": "",
|
||||||
|
"remitFlag": 0,
|
||||||
|
"repayRemark": "",
|
||||||
|
"totalInterest": 0,
|
||||||
|
"totalLoanMoney": 0,
|
||||||
|
"totalMonth": 0,
|
||||||
|
"totalRepayment": 0,
|
||||||
|
"tradeNo": "",
|
||||||
|
"updateBackNum": 0,
|
||||||
|
"updateTime": ""
|
||||||
|
})
|
||||||
|
const _getBorrowInfo = () => {
|
||||||
|
getBorrowInfo({tradeNo: route.query.tradeNo}).then(res => {
|
||||||
|
resetData(borrowInfo, res)
|
||||||
|
resetData(stepBorrow, borrowInfo.loanProcessResp)
|
||||||
|
stepBorrow.borrowStep.forEach((sb, index) => {
|
||||||
|
if (sb.over) {
|
||||||
|
active.value = index
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
_getBorrowInfo()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.content {
|
||||||
|
background-image: linear-gradient(to bottom, #f9bf3a, #ffffff) !important;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
|
||||||
|
.action {
|
||||||
|
margin: 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
box-shadow: 0 0 1vw 0px #e0e0e0;
|
||||||
|
&-content {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px 20px;
|
||||||
|
box-shadow: 0 0 1vw 0px #e0e0e0;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.id-card-box {
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 0 1vw 0px #e0e0e0;
|
||||||
|
width: 500px;
|
||||||
|
height: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tt {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: start;
|
||||||
|
background: #f6f9fd;
|
||||||
|
color: #000;
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 20px 20px;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-22 {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
width: 100%;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 0 1vw 0px #e0e0e0;
|
||||||
|
|
||||||
|
&-1 {
|
||||||
|
padding: 20px 80px;
|
||||||
|
//background: #f9fafb;
|
||||||
|
}
|
||||||
|
&-2 {
|
||||||
|
display: flex;
|
||||||
|
padding: 20px;
|
||||||
|
&-tit {
|
||||||
|
display: block;
|
||||||
|
width: 220px;
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
&-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
156
src/views/forget/index.vue
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<j-nav-bar :placeholder="false" color="#FFF" nav-bar-background="#F9BF3A00"/>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
<j-gap height="50" background="#F9BF3A" opacity="0"/>
|
||||||
|
<j-gap height="120" opacity="1"/>
|
||||||
|
<van-field
|
||||||
|
v-model="loginData.phoneNumber"
|
||||||
|
v-if="flag === '1'"
|
||||||
|
class="login-btn"
|
||||||
|
label="手机号码"
|
||||||
|
placeholder="请输入手机号码"
|
||||||
|
label-align="top"
|
||||||
|
style="background: #12332100"
|
||||||
|
type="tel"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-field
|
||||||
|
v-model="loginData.code"
|
||||||
|
class="login-btn"
|
||||||
|
v-if="flag === '1'"
|
||||||
|
label="验证码"
|
||||||
|
placeholder="请输入验证码"
|
||||||
|
label-align="top"
|
||||||
|
style="background: #12332100"
|
||||||
|
type="number"
|
||||||
|
>
|
||||||
|
<template #button>
|
||||||
|
<div class="password-btn">
|
||||||
|
<van-count-down v-show="countDownFlag" ref="countDown" :auto-start="false" :time="time" @finish="onFinish">
|
||||||
|
<template #default="timeData">
|
||||||
|
<span class="block">{{ timeData.seconds }}秒</span>
|
||||||
|
</template>
|
||||||
|
</van-count-down>
|
||||||
|
<div v-show="!countDownFlag" style="color: #bc7c1c" @click="start">发送验证码</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</van-field>
|
||||||
|
|
||||||
|
<van-field
|
||||||
|
v-model="loginData.password"
|
||||||
|
class="login-btn"
|
||||||
|
v-if="flag === '2'"
|
||||||
|
label="登录密码"
|
||||||
|
label-align="top"
|
||||||
|
placeholder="请设置6-16位密码"
|
||||||
|
style="background: #12332100"
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-field
|
||||||
|
v-model="loginData.confirmPassword"
|
||||||
|
class="login-btn"
|
||||||
|
v-if="flag === '2'"
|
||||||
|
label="确认密码"
|
||||||
|
label-align="top"
|
||||||
|
placeholder="请再次输入密码"
|
||||||
|
style="background: #12332100"
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-button
|
||||||
|
:disabled="!loginData.phoneNumber || !loginData.code"
|
||||||
|
color="linear-gradient(to right, #D2A64C, #F9D88D)"
|
||||||
|
round
|
||||||
|
style="width: 100%; margin: 20px 0"
|
||||||
|
v-if="flag === '1'"
|
||||||
|
@click.stop="next"
|
||||||
|
>
|
||||||
|
下一步
|
||||||
|
</van-button>
|
||||||
|
|
||||||
|
<van-button
|
||||||
|
:disabled="!loginData.password || !loginData.confirmPassword"
|
||||||
|
color="linear-gradient(to right, #D2A64C, #F9D88D)"
|
||||||
|
round
|
||||||
|
style="width: 100%; margin: 20px 0"
|
||||||
|
v-if="flag === '2'"
|
||||||
|
@click.stop="updatePwdBtn"
|
||||||
|
>
|
||||||
|
确认修改
|
||||||
|
</van-button>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {reactive, ref} from "vue";
|
||||||
|
import {sendSmsForget, updatePwd} from "@/api/login";
|
||||||
|
import {showToast} from "vant";
|
||||||
|
|
||||||
|
const loginData = reactive({
|
||||||
|
phone: null,
|
||||||
|
phoneNumber: '',
|
||||||
|
code: null,
|
||||||
|
password: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
checkCode: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const flag = ref('1')
|
||||||
|
|
||||||
|
|
||||||
|
const time = ref(60 * 1000);
|
||||||
|
const countDown = ref();
|
||||||
|
const countDownFlag = ref(false);
|
||||||
|
const start = () => {
|
||||||
|
if (loginData.phoneNumber) {
|
||||||
|
countDown.value.start();
|
||||||
|
countDownFlag.value = true
|
||||||
|
sendSmsForget({
|
||||||
|
phoneNumber: loginData.phoneNumber
|
||||||
|
}).then(res => {
|
||||||
|
console.log(res)
|
||||||
|
loginData.code = res
|
||||||
|
loginData.checkCode = res
|
||||||
|
onFinish()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
showToast('请输入手机号')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const next = () => {
|
||||||
|
flag.value = '2'
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatePwdBtn = () => {
|
||||||
|
updatePwd({
|
||||||
|
checkCode: loginData.checkCode,
|
||||||
|
confirmPassword: loginData.confirmPassword,
|
||||||
|
password: loginData.password
|
||||||
|
}).then(res => {
|
||||||
|
console.log(res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const onFinish = () => {
|
||||||
|
countDownFlag.value = false
|
||||||
|
countDown.value.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.content {
|
||||||
|
padding: 0 10px;
|
||||||
|
background-image: linear-gradient(to bottom, #F9BF3A, #ffffff, #ffffff, #ffffff, #ffffff);
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
444
src/views/index/home/index.vue
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<j-nav-bar/>
|
||||||
|
<!-- banner -->
|
||||||
|
<div :style="{paddingBottom: px2vw(20)}" style="overflow: hidden;">
|
||||||
|
<van-swipe :autoplay="3000" lazy-render>
|
||||||
|
<van-swipe-item v-for="banner in bannerList" :key="banner.bannerUrl">
|
||||||
|
<van-image :src="banner.bannerUrl"></van-image>
|
||||||
|
</van-swipe-item>
|
||||||
|
</van-swipe>
|
||||||
|
</div>
|
||||||
|
<div :style="{padding: '0 ' + px2vw(30) + ' ' + px2vw(20)}">
|
||||||
|
|
||||||
|
<div class="product">
|
||||||
|
<div class="product-title">产品详情</div>
|
||||||
|
|
||||||
|
<div class="product-content">
|
||||||
|
<div class="product-content-label">
|
||||||
|
<div>最低日息</div>
|
||||||
|
<div><span>{{ calLoan.loanRate }}%</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="product-content-label">
|
||||||
|
<div>借款额度</div>
|
||||||
|
<div><span>¥{{ loans.loansMinAccount }}-{{ loans.loansMaxAccount }}</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="product-content-label">
|
||||||
|
<div>分期期限</div>
|
||||||
|
<div><span>可选{{ loans.loansMonth.replaceAll(',', '/') }}</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="apply">
|
||||||
|
<div class="apply-title yellow_color">申请金额(元)</div>
|
||||||
|
|
||||||
|
<div class="apply-money">{{ strip }}</div>
|
||||||
|
|
||||||
|
<div class="apply-strip-box">
|
||||||
|
<div class="reduce" @click="moneyReduce(100)"></div>
|
||||||
|
|
||||||
|
<div class="slider">
|
||||||
|
<van-slider bar-height="16" active-color="linear-gradient(to bottom, #f3654d, #f9ad7d)" :max="loans.loansMaxAccount" :min="loans.loansMinAccount" :step="100" v-model="strip">
|
||||||
|
<template #button>
|
||||||
|
<div class="custom-button"></div>
|
||||||
|
</template>
|
||||||
|
</van-slider>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="add" @click="moneyAdd(100)"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="apply-jkqx">
|
||||||
|
<div class="apply-jkqx-title">借款期限</div>
|
||||||
|
<div class="apply-jkqx-item-box">
|
||||||
|
<div v-for="lm in loans.loansMonthList" :key="lm" class="apply-jkqx-item" :class="{'checked': lm === lmChecked}" @click="lmChecked = lm">
|
||||||
|
{{ lm }}个月
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<j-gap height="2" background="#fff"/>
|
||||||
|
|
||||||
|
<div class="apply-mqhk">
|
||||||
|
<div>每期还款</div>
|
||||||
|
<div>¥{{ calLoan.avgRepayment }}</div>
|
||||||
|
<div>(日利率{{ calLoan.loanRate }}% 总利息¥{{ calLoan.totalInterest }})</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notice">
|
||||||
|
<van-icon :name="getAssetsImages('home/xlb.png')" size="30"/>
|
||||||
|
<div>{{ loansUser.time }}</div>
|
||||||
|
<div style="color: #ec6401">{{ loansUser.phone }}</div>
|
||||||
|
<div>成功借款</div>
|
||||||
|
<div style="color: #BC7C1C; font-weight: 600;">¥{{ loansUser.amount }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="xieyi">
|
||||||
|
<van-checkbox v-model="checked" :icon-size="px2vw(28)" style="align-items: baseline" checked-color="linear-gradient(to right, #F9D88D, #D2A64C)">
|
||||||
|
我已阅读并同意
|
||||||
|
<span class="yellow_color1" @click.stop="go('authAgreement')">《委托授权协议》</span>
|
||||||
|
<span class="yellow_color1" @click.stop="go('serviceAgreement')">《平台服务协议》</span>
|
||||||
|
<span class="yellow_color1" @click.stop="go('loansAgreement')">《借款协议》</span>
|
||||||
|
</van-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div style="padding: 0 20px 0">
|
||||||
|
<van-button
|
||||||
|
color="linear-gradient(to right, #D2A64C, #F9D88D)"
|
||||||
|
round
|
||||||
|
style="width: 100%; "
|
||||||
|
@click.stop="immediateBorrowing"
|
||||||
|
>
|
||||||
|
立即借款
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<j-gap :height="170" opacity/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {debounce, getAssetsImages, px2vw} from "@/utils";
|
||||||
|
import {computed, onMounted, reactive, ref,onUnmounted} from "vue";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
import {useUserStore} from "@/store/modules/user";
|
||||||
|
import {getCalLoan, getHomeInfo, getLoansInfo, getLoansUser, getUserInfo} from "@/api";
|
||||||
|
import {resetData} from "@/utils/dataUtil";
|
||||||
|
import {watch} from "vue-demi";
|
||||||
|
import {showToast} from "vant";
|
||||||
|
import JGap from "@/components/JGap/JGap.vue";
|
||||||
|
|
||||||
|
const user = useUserStore()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const bannerList = [
|
||||||
|
{
|
||||||
|
bannerUrl: getAssetsImages('home/banner_home.png')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const checked = ref(false);
|
||||||
|
const strip = ref(3000)
|
||||||
|
const lmChecked = ref('')
|
||||||
|
|
||||||
|
|
||||||
|
const moneyReduce = (step: number) => {
|
||||||
|
strip.value-=step
|
||||||
|
}
|
||||||
|
|
||||||
|
const moneyAdd = (step: number) => {
|
||||||
|
strip.value+=step
|
||||||
|
}
|
||||||
|
|
||||||
|
const go = (key) => {
|
||||||
|
// agreement
|
||||||
|
router.push({
|
||||||
|
path: '/agreement',
|
||||||
|
query: {
|
||||||
|
key: key
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const immediateBorrowing = () => {
|
||||||
|
if (checked.value) {
|
||||||
|
if (userInfo.infoFlag && userInfo.bankFlag && userInfo.cardFlag) {
|
||||||
|
router.push({
|
||||||
|
path: '/loansInfo',
|
||||||
|
query: {
|
||||||
|
"totalLoanMoney": strip.value,
|
||||||
|
"totalMonth": lmChecked.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
showToast('请先填写个人资料')
|
||||||
|
router.push({
|
||||||
|
path: '/userInfo'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
showToast('请阅读并同意相关协议')
|
||||||
|
}
|
||||||
|
|
||||||
|
const loans = reactive({
|
||||||
|
dueDate: 0,
|
||||||
|
id: 0,
|
||||||
|
loansInitAccount: 0,
|
||||||
|
loansInitMonth: "",
|
||||||
|
loansMaxAccount: 0,
|
||||||
|
loansMinAccount: 0,
|
||||||
|
loansMonth: "",
|
||||||
|
loansMonthList: [],
|
||||||
|
serviceRate: "",
|
||||||
|
serviceRateList: [],
|
||||||
|
})
|
||||||
|
const _getLoansInfo = () => {
|
||||||
|
getLoansInfo().then(res => {
|
||||||
|
resetData(loans, res)
|
||||||
|
loans.loansMonthList.newPush(loans.loansMonth.split(','))
|
||||||
|
loans.serviceRateList.newPush(loans.serviceRate.split(','))
|
||||||
|
lmChecked.value = loans.loansInitMonth
|
||||||
|
strip.value = loans.loansInitAccount
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const homeInfo = reactive({
|
||||||
|
"id": "1",
|
||||||
|
"homeTitle": "123123",
|
||||||
|
"bannerOne": "http://localhost:8082/profile/upload/2023/11/29/5ea3a159-81e9-4630-a72d-3ff2ce68b306.jpg",
|
||||||
|
"commonSeal": null
|
||||||
|
})
|
||||||
|
const _getHomeInfo = () => {
|
||||||
|
getHomeInfo().then(res => {
|
||||||
|
resetData(homeInfo, res)
|
||||||
|
bannerList.newPush({
|
||||||
|
bannerUrl: homeInfo.bannerOne
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const calLoan = reactive({
|
||||||
|
"avgRepayment": 0,
|
||||||
|
"firstRepayment": 0,
|
||||||
|
"loanRate": 0,
|
||||||
|
"totalInterest": 0,
|
||||||
|
"totalLoanMoney": 0,
|
||||||
|
"totalMonth": 0,
|
||||||
|
"totalRepayment": 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const params = computed(() => {
|
||||||
|
return {
|
||||||
|
"totalLoanMoney": strip.value,
|
||||||
|
"totalMonth": lmChecked.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(params, () => {
|
||||||
|
if (params.value.totalLoanMoney && params.value.totalMonth) {
|
||||||
|
_getCalLoan()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const _getCalLoan = debounce(() => {
|
||||||
|
getCalLoan(params.value).then(res => {
|
||||||
|
resetData(calLoan, res)
|
||||||
|
})
|
||||||
|
}, 200)
|
||||||
|
|
||||||
|
|
||||||
|
const loansUser = reactive({
|
||||||
|
amount : "48000",
|
||||||
|
phone : "153****0552",
|
||||||
|
time : "2023/11/27"
|
||||||
|
})
|
||||||
|
|
||||||
|
const timer = ref(0)
|
||||||
|
const _getLoansUser = () => {
|
||||||
|
getLoansUser().then(res => {
|
||||||
|
resetData(loansUser, res)
|
||||||
|
})
|
||||||
|
timer.value = setInterval(() => {
|
||||||
|
getLoansUser().then(res => {
|
||||||
|
resetData(loansUser, res)
|
||||||
|
})
|
||||||
|
}, 30000);
|
||||||
|
}
|
||||||
|
|
||||||
|
const userInfo = reactive({
|
||||||
|
cardFlag: false,
|
||||||
|
infoFlag: false,
|
||||||
|
bankFlag: false
|
||||||
|
})
|
||||||
|
const _getUserInfo = () => {
|
||||||
|
getUserInfo().then(res => {
|
||||||
|
resetData(userInfo, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
_getLoansInfo()
|
||||||
|
_getHomeInfo()
|
||||||
|
_getLoansUser()
|
||||||
|
_getUserInfo()
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(timer.value);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.product {
|
||||||
|
&-title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 40px;
|
||||||
|
padding: 0 0 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
background: url("../../../assets/images/home/detail_bg.png") no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
color: #a2a2a2;
|
||||||
|
padding: 30px;
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 7px 0;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #F9BF3A
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.apply {
|
||||||
|
background: url("../../../assets/images/home/apply_bg.png") no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
padding: 30px;
|
||||||
|
margin: 30px 0 15px;
|
||||||
|
&-title {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 34px;
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
width: 120px;
|
||||||
|
height: 1px;
|
||||||
|
margin-right: 20px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
background-image: -webkit-gradient(linear, left top, right top, from(#fbde60), to(#e46f00));
|
||||||
|
background-image: linear-gradient(to right, #fbde60, #e46f00);
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
width: 120px;
|
||||||
|
height: 1px;
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
background-image: linear-gradient(to right, #e46f00, #fbde60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-money {
|
||||||
|
font-size: 80px;
|
||||||
|
text-align: center;
|
||||||
|
color: #5c2d00;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-strip-box{
|
||||||
|
margin-bottom: 60px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.reduce {
|
||||||
|
background: url("../../../assets/images/home/strip-reduce.png") no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add {
|
||||||
|
background: url("../../../assets/images/home/strip-add.png") no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin-left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-button {
|
||||||
|
background: url("../../../assets/images/home/strip-block.png") no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
height: 58px;
|
||||||
|
width: 43px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-jkqx {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
&-title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #141414;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
&-item-box {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.apply-jkqx-item{
|
||||||
|
border-radius: 40px;
|
||||||
|
text-align: center;
|
||||||
|
background: #fffbba;
|
||||||
|
padding: 4px 16px;
|
||||||
|
color: #BC7C1C;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.checked {
|
||||||
|
background-image: linear-gradient(to bottom, #f3654d, #f9ad7d);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
&-mqhk {
|
||||||
|
margin-top: 40px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: end;
|
||||||
|
font-size: 28px;
|
||||||
|
|
||||||
|
div:nth-child(1) {
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #141414;
|
||||||
|
}
|
||||||
|
div:nth-child(2) {
|
||||||
|
color: #b25700;
|
||||||
|
}
|
||||||
|
|
||||||
|
div:last-child {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice {
|
||||||
|
background-image: linear-gradient(to right, #fae1cf, #fcfaf8);
|
||||||
|
padding: 8px 20px;
|
||||||
|
margin: 0 30px;
|
||||||
|
border-radius: 40px;
|
||||||
|
font-size: 24px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xieyi {
|
||||||
|
padding: 30px 35px;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
68
src/views/index/index.vue
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<!-- <div style="background: linear-gradient(167.96deg, #E6FAE1 0%, #F2E7B7 98.44%) no-repeat;min-height: 100%" >-->
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<router-view />
|
||||||
|
|
||||||
|
<van-tabbar route v-model="active" active-color="#F9BF3A">
|
||||||
|
<van-tabbar-item v-for="tabBar in tabBarList" :key="tabBar.name" :replace="tabBar.replace" :to="tabBar.to">
|
||||||
|
<span>{{ tabBar.text }}</span>
|
||||||
|
<template #icon="props">
|
||||||
|
<van-icon :color="props.active ? '' : 'rgb(229, 229, 229)'" :name="props.active ? tabBar.active : tabBar.inactive" />
|
||||||
|
</template>
|
||||||
|
</van-tabbar-item>
|
||||||
|
</van-tabbar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {ref} from "vue";
|
||||||
|
import JGap from "@/components/JGap/JGap.vue";
|
||||||
|
import {getAssetsImages} from "@/utils";
|
||||||
|
import {useBackgroundHook} from "@/hooks/useBackgroundHook";
|
||||||
|
|
||||||
|
const tabBarList = [
|
||||||
|
{
|
||||||
|
to: '/home',
|
||||||
|
name: 'home',
|
||||||
|
text: '首页',
|
||||||
|
replace: true,
|
||||||
|
active: getAssetsImages('tabBar/home-active.png'),
|
||||||
|
inactive: getAssetsImages('tabBar/home-inactive.png')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
to: '/serveList',
|
||||||
|
name: 'serveList',
|
||||||
|
text: '钱包',
|
||||||
|
replace: true,
|
||||||
|
active: getAssetsImages('tabBar/artificer-active.png'),
|
||||||
|
inactive: getAssetsImages('tabBar/artificer-inactive.png')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
to: '/message',
|
||||||
|
name: 'message',
|
||||||
|
text: '客服',
|
||||||
|
replace: true,
|
||||||
|
active: 'chat',
|
||||||
|
inactive: 'chat'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
to: '/my',
|
||||||
|
name: 'my',
|
||||||
|
text: '我的',
|
||||||
|
replace: true,
|
||||||
|
active: getAssetsImages('tabBar/my-active.png'),
|
||||||
|
inactive: getAssetsImages('tabBar/my-inactive.png')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const active = ref(0)
|
||||||
|
|
||||||
|
// const {setBodyBackground} = useBackgroundHook()
|
||||||
|
// setBodyBackground()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
46
src/views/index/message/index.vue
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<template>
|
||||||
|
<j-nav-bar/>
|
||||||
|
<div ref="root" class="frame" style="height: calc(100vh - 50px - 46px)">
|
||||||
|
<iframe :src="iframeSrc" class="frame-iframe" ref="frameRef"></iframe>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {ref} from "vue";
|
||||||
|
import {getSetting} from "@/api";
|
||||||
|
import {onMounted} from "vue";
|
||||||
|
|
||||||
|
const iframeSrc = ref('')
|
||||||
|
// const iframeSrc = ref('https://chatlink.mstatik.com/widget/standalone.html?eid=329d34187acc7ebda66a12a0671e3d70')
|
||||||
|
|
||||||
|
|
||||||
|
const _getSetting = () => {
|
||||||
|
getSetting().then(res => {
|
||||||
|
iframeSrc.value = res.chatUrl
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
_getSetting()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.frame {
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
&-iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
200
src/views/index/my/index.vue
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
<template>
|
||||||
|
<j-nav-bar/>
|
||||||
|
<div class="header">
|
||||||
|
<div class="header-bj">
|
||||||
|
<div class="header-head" :style="{'--bg-image': `url(${headerImage}) no-repeat`}"></div>
|
||||||
|
<div class="header-text">{{ customerInfo.nickName }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
<div class="action">
|
||||||
|
<van-cell title="我的资料" is-link to="userInfo" >
|
||||||
|
<template #icon>
|
||||||
|
<div style="display: flex; justify-content: center; align-items: center; padding-right: 5px">
|
||||||
|
<van-icon :name="getAssetsImages('my/my_info.png')" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</van-cell>
|
||||||
|
<van-cell title="我的借款" is-link to="myLoan" >
|
||||||
|
<template #icon>
|
||||||
|
<div style="display: flex; justify-content: center; align-items: center; padding-right: 5px">
|
||||||
|
<van-icon :name="getAssetsImages('my/my_info1.png')" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</van-cell>
|
||||||
|
<van-cell title="我的还款" is-link to="myRepayment" >
|
||||||
|
<template #icon>
|
||||||
|
<div style="display: flex; justify-content: center; align-items: center; padding-right: 5px">
|
||||||
|
<van-icon :name="getAssetsImages('my/my_info2.png')" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</van-cell>
|
||||||
|
<!-- <van-cell title="获取开户权限" is-link to="" >-->
|
||||||
|
<!-- <template #icon>-->
|
||||||
|
<!-- <div style="display: flex; justify-content: center; align-items: center; padding-right: 5px">-->
|
||||||
|
<!-- <van-icon name="/src/assets/images/my/my_info3.png" />-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </van-cell>-->
|
||||||
|
<van-cell title="法律责任" is-link @click="go('/agreement', 'lawAgreement')" >
|
||||||
|
<template #icon>
|
||||||
|
<div style="display: flex; justify-content: center; align-items: center; padding-right: 5px">
|
||||||
|
<van-icon :name="getAssetsImages('my/my_info4.png')" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</van-cell>
|
||||||
|
<!-- <van-cell title="签名" is-link @click="go('/signature', null)" >-->
|
||||||
|
<!-- <template #icon>-->
|
||||||
|
<!-- <div style="display: flex; justify-content: center; align-items: center; padding-right: 5px">-->
|
||||||
|
<!-- <van-icon name="invitation" />-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </van-cell>-->
|
||||||
|
<!-- <van-cell title="我的合同" is-link @click="go('/contract', null)" >-->
|
||||||
|
<!-- <template #icon>-->
|
||||||
|
<!-- <div style="display: flex; justify-content: center; align-items: center; padding-right: 5px">-->
|
||||||
|
<!-- <van-icon name="completed" />-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </van-cell>-->
|
||||||
|
</div>
|
||||||
|
<div class="action">
|
||||||
|
<van-cell title="修改密码" is-link to="uploadPassword" >
|
||||||
|
<template #icon>
|
||||||
|
<div style="display: flex; justify-content: center; align-items: center; padding-right: 5px">
|
||||||
|
<van-icon :name="getAssetsImages('my/my_info5.png')" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</van-cell>
|
||||||
|
<van-cell title="退出登录" is-link @click="logout" >
|
||||||
|
<template #icon>
|
||||||
|
<div style="display: flex; justify-content: center; align-items: center; padding-right: 5px">
|
||||||
|
<van-icon :name="getAssetsImages('my/my_info66.png')" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</van-cell>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {showConfirmDialog} from "vant";
|
||||||
|
import {getAssetsImages} from "@/utils";
|
||||||
|
import {useUserStore} from "@/store/modules/user";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
import {onMounted, reactive, ref} from "vue";
|
||||||
|
import {getCustomerInfo} from "@/api";
|
||||||
|
import {resetData} from "@/utils/dataUtil";
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const headerImage = ref('https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg')
|
||||||
|
|
||||||
|
const logout = () => {
|
||||||
|
|
||||||
|
showConfirmDialog({
|
||||||
|
title: '提示',
|
||||||
|
message: '您确定要退出登录吗',
|
||||||
|
width: '500px'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
userStore.logout().then(res => {
|
||||||
|
router.push({
|
||||||
|
path: '/login'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// on cancel
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const go = (url, key) => {
|
||||||
|
// agreement
|
||||||
|
router.push({
|
||||||
|
path: url,
|
||||||
|
query: {
|
||||||
|
key: key
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const customerInfo = reactive({
|
||||||
|
"account": 0,
|
||||||
|
"borrowAccount": 0,
|
||||||
|
"id": 0,
|
||||||
|
"lastLoginIp": "",
|
||||||
|
"lastLoginTime": "",
|
||||||
|
"loansFlag": 0,
|
||||||
|
"nickName": "",
|
||||||
|
"phoneNumber": "",
|
||||||
|
"realNameAuth": 0,
|
||||||
|
"repaymentAccount": 0,
|
||||||
|
"status": 0,
|
||||||
|
"updateTime": "",
|
||||||
|
"withdrawFlag": 0
|
||||||
|
})
|
||||||
|
const _getCustomerInfo = () => {
|
||||||
|
getCustomerInfo().then(res => {
|
||||||
|
resetData(customerInfo, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
_getCustomerInfo()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background: #151515;
|
||||||
|
height: 400px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: end;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&-bj {
|
||||||
|
background: url("../../../assets/images/my/user_card.png") no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
height: 330px;
|
||||||
|
width: 666px;
|
||||||
|
transform: translateY(80px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-flow: column;
|
||||||
|
.header-head {
|
||||||
|
height: 180px;
|
||||||
|
width: 180px;
|
||||||
|
border: 10px solid #f9c947;
|
||||||
|
border-radius: 100px;
|
||||||
|
background: #f9c947;
|
||||||
|
transform: translateY(-90px);
|
||||||
|
background: var(--bg-image);
|
||||||
|
background-size: 100% 100%;
|
||||||
|
}
|
||||||
|
.header-text {
|
||||||
|
font-size: 38px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #111a34;
|
||||||
|
transform: translateY(-60px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
|
||||||
|
.action {
|
||||||
|
margin: 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
290
src/views/index/serveList/index.vue
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
<template>
|
||||||
|
<j-nav-bar color="#FFF" nav-bar-background="#F9BF3A" />
|
||||||
|
<div class="content">
|
||||||
|
<view class="bg-1">
|
||||||
|
<view class="bg-1-1">
|
||||||
|
<div class="header-head" :style="{'--bg-image': `url(${headerImage}) no-repeat`}"></div>
|
||||||
|
<view class="bg-1-1-text">我的贷款:{{toRoundMark(customerInfo.borrowAccount)}}</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="accountBalance" style="border-bottom: 1px #fff dashed;">
|
||||||
|
<view class="yellow_color">账户余额(元)</view>
|
||||||
|
<view class="money">{{ toRoundMark(customerInfo.account) }}</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="accountBalance">
|
||||||
|
<view class="yellow_color">待还款金额(元)</view>
|
||||||
|
<view class="money">{{ toRoundMark(customerInfo.repaymentAccount) }}</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="btn">
|
||||||
|
|
||||||
|
<van-button
|
||||||
|
color="#fff"
|
||||||
|
round
|
||||||
|
style="color: #e6a600;width: 80%"
|
||||||
|
@click.stop="withdrawalBtn"
|
||||||
|
>
|
||||||
|
立即提现
|
||||||
|
</van-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="footer">
|
||||||
|
<view class="footer-t gray_color ">
|
||||||
|
<van-icon :name="getAssetsImages('common/yhd.png')" size="18"/>
|
||||||
|
账户资金安全由银行保障
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="footer-content">
|
||||||
|
<view class="footer-content-1">
|
||||||
|
<van-steps active-icon="success" active-color="#07c160" :active="active">
|
||||||
|
<van-step v-for="(item, index) in stepBorrow.borrowStep" :key="index">{{ item.name }}</van-step>
|
||||||
|
</van-steps>
|
||||||
|
</view>
|
||||||
|
<view class="footer-content-2">
|
||||||
|
<view class="footer-content-2-tit gray_color">温馨提示</view>
|
||||||
|
<view class="footer-content-2-content" :style="{color: stepBorrow.borrowNameStyle || '#e84a10'}">
|
||||||
|
{{ stepBorrow.borrowRemark }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</div>
|
||||||
|
<van-dialog :width="px2vw(650)" v-model:show="withdrawalShow" title="提现" show-cancel-button @confirm="saveUserInfoBtn">
|
||||||
|
<van-field v-model="withdrawAmount" type="number" label="提现金额" placeholder="请输入你的提现金额"/>
|
||||||
|
</van-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {onMounted, reactive, ref} from "vue";
|
||||||
|
import {showConfirmDialog, showToast} from "vant";
|
||||||
|
import {useUserStore} from "@/store/modules/user";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
import {getBorrowWithdraw, getCustomerInfo, getStepBorrow} from "@/api";
|
||||||
|
import {resetData} from "@/utils/dataUtil";
|
||||||
|
import {getAssetsImages, px2vw, toRoundMark} from "../../../utils";
|
||||||
|
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const withdrawalShow = ref(false);
|
||||||
|
const withdrawAmount = ref(0);
|
||||||
|
const active = ref(0);
|
||||||
|
const headerImage = ref('https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg')
|
||||||
|
|
||||||
|
|
||||||
|
const go = (key) => {
|
||||||
|
// agreement
|
||||||
|
router.push({
|
||||||
|
path: '/agreement',
|
||||||
|
query: {
|
||||||
|
key: key
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const customerInfo = reactive({
|
||||||
|
"account": 0,
|
||||||
|
"borrowAccount": 0,
|
||||||
|
"id": 0,
|
||||||
|
"lastLoginIp": "",
|
||||||
|
"lastLoginTime": "",
|
||||||
|
"loansFlag": 0,
|
||||||
|
"nickName": "",
|
||||||
|
"phoneNumber": "",
|
||||||
|
"realNameAuth": 0,
|
||||||
|
"repaymentAccount": 0,
|
||||||
|
"status": 0,
|
||||||
|
"updateTime": "",
|
||||||
|
"withdrawFlag": 0
|
||||||
|
})
|
||||||
|
const _getCustomerInfo = () => {
|
||||||
|
getCustomerInfo().then(res => {
|
||||||
|
resetData(customerInfo, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const stepBorrow = reactive({
|
||||||
|
"borrowNameStyle": "",
|
||||||
|
"borrowRemark": "",
|
||||||
|
"borrowStep": [
|
||||||
|
{
|
||||||
|
"name": "提交成功",
|
||||||
|
"over": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "银行卡异常",
|
||||||
|
"over": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "到账成功",
|
||||||
|
"over": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const _getStepBorrow = () => {
|
||||||
|
getStepBorrow().then(res => {
|
||||||
|
resetData(stepBorrow, res)
|
||||||
|
stepBorrow.borrowStep.forEach((sb, index) => {
|
||||||
|
if (sb.over) {
|
||||||
|
active.value = index
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const withdrawalBtn = () => {
|
||||||
|
|
||||||
|
withdrawalShow.value = true
|
||||||
|
withdrawAmount.value = customerInfo.account
|
||||||
|
// router.push({
|
||||||
|
// path: '/loansInfo1',
|
||||||
|
// query: {
|
||||||
|
// account: customerInfo.account
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveUserInfoBtn = () => {
|
||||||
|
|
||||||
|
// showConfirmDialog({
|
||||||
|
// title: '提示',
|
||||||
|
// message: '您确定要提现吗',
|
||||||
|
// width: '500px'
|
||||||
|
// })
|
||||||
|
// .then(() => {
|
||||||
|
getBorrowWithdraw({withdrawAmount: withdrawAmount.value}).then(res => {
|
||||||
|
showToast('提现成功')
|
||||||
|
router.push({
|
||||||
|
path: '/serveList'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// })
|
||||||
|
// .catch(() => {
|
||||||
|
// // on cancel
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
_getCustomerInfo()
|
||||||
|
_getStepBorrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.content {
|
||||||
|
padding: 0 20px;
|
||||||
|
background-image: linear-gradient(to bottom, #F9BF3A, #ffffff);
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
.bg-1 {
|
||||||
|
display: block;
|
||||||
|
background: url("../../../assets/images/common/bg_wallet.png") no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
padding-top: 100px;
|
||||||
|
width: 692px;
|
||||||
|
height: 680px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
&-1 {
|
||||||
|
display: flex;
|
||||||
|
&-text {
|
||||||
|
color: #111a34;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-head {
|
||||||
|
height: 180px;
|
||||||
|
width: 180px;
|
||||||
|
border: 10px solid #f9c947;
|
||||||
|
border-radius: 100px;
|
||||||
|
background: #f9c947;
|
||||||
|
transform: translateY(-90px);
|
||||||
|
background: var(--bg-image);
|
||||||
|
background-size: 100% 100%;
|
||||||
|
margin-left: 45px;
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.accountBalance {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
align-items: start;
|
||||||
|
padding-left: 80px;
|
||||||
|
padding-top: 30px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
margin: 0 20px;
|
||||||
|
transform: translateY(-70px);
|
||||||
|
font-size: 40px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
|
||||||
|
.yellow_color {
|
||||||
|
color: #b25700;
|
||||||
|
}
|
||||||
|
.money {
|
||||||
|
padding-top: 10px;
|
||||||
|
font-size: 56px;
|
||||||
|
color: #5c2d00;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
//padding-top: 100px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transform: translateY(-70px);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 30px;
|
||||||
|
flex-flow: column;
|
||||||
|
&-t {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
}
|
||||||
|
&-content {
|
||||||
|
width: 100%;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 0 1vw 0px #e0e0e0;
|
||||||
|
|
||||||
|
&-1 {
|
||||||
|
padding: 20px 80px;
|
||||||
|
//background: #f9fafb;
|
||||||
|
}
|
||||||
|
&-2 {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20px;
|
||||||
|
&-tit {
|
||||||
|
display: block;
|
||||||
|
width: 220px;
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
&-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
211
src/views/loans/info/index.vue
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
<template>
|
||||||
|
<j-nav-bar color="#FFF" nav-bar-background="#f9bf3a" :placeholder="false"/>
|
||||||
|
<div class="content">
|
||||||
|
<j-gap height="120" background="#F9BF3A" opacity="0"/>
|
||||||
|
<div class="action">
|
||||||
|
<view class="tt">
|
||||||
|
确认借款信息
|
||||||
|
</view>
|
||||||
|
<van-cell title="借款金额" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="toRoundMark(loansInfo.totalLoanMoney)" />
|
||||||
|
<van-cell title="借款期限" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="loansInfo.totalMonth + '个月'" />
|
||||||
|
<van-cell title="到账银行" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="userInfo.bankType" />
|
||||||
|
<van-cell title="收款账号" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="backCardNumDesensitization(userInfo.backCardNum)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action">
|
||||||
|
<view class="tt">
|
||||||
|
借款用途
|
||||||
|
</view>
|
||||||
|
<van-field
|
||||||
|
v-model="loansInfo.noteRemark"
|
||||||
|
rows="5"
|
||||||
|
autosize
|
||||||
|
type="textarea"
|
||||||
|
placeholder="请输入你的借款用途"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="xieyi">
|
||||||
|
<van-checkbox v-model="checked" :icon-size="px2vw(28)" style="align-items: baseline" checked-color="linear-gradient(to right, #F9D88D, #D2A64C)">
|
||||||
|
我已阅读并同意
|
||||||
|
<span class="yellow_color1" @click.stop="go('loansAgreement')">《借款协议》</span>
|
||||||
|
</van-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div style="padding: 0 20px 60px">
|
||||||
|
<van-button
|
||||||
|
color="linear-gradient(to right, #D2A64C, #F9D88D)"
|
||||||
|
round
|
||||||
|
style="width: 100%; "
|
||||||
|
@click.stop="saveUserInfoBtn"
|
||||||
|
:disabled="!checked"
|
||||||
|
>
|
||||||
|
提交申请
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {onMounted, reactive, ref} from "vue";
|
||||||
|
import {useRoute, useRouter} from "vue-router";
|
||||||
|
import {px2vw, toRoundMark} from "@/utils";
|
||||||
|
import {useUserStore} from "@/store/modules/user";
|
||||||
|
import {getUserInfo, startBorrow} from "@/api";
|
||||||
|
import {resetData} from "@/utils/dataUtil";
|
||||||
|
import {showConfirmDialog, showToast} from "vant";
|
||||||
|
|
||||||
|
const useUser = useUserStore()
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const loansInfo = reactive({
|
||||||
|
"customerId": useUser.getUserInfo.id,
|
||||||
|
"noteRemark": "",
|
||||||
|
"totalLoanMoney": 0,
|
||||||
|
"totalMonth": 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const checked = ref(false);
|
||||||
|
const go = (key) => {
|
||||||
|
// agreement
|
||||||
|
router.push({
|
||||||
|
path: '/agreement',
|
||||||
|
query: {
|
||||||
|
key: key
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const saveUserInfoBtn = () => {
|
||||||
|
|
||||||
|
showConfirmDialog({
|
||||||
|
title: '提示',
|
||||||
|
message: '您确定要申请贷款吗',
|
||||||
|
width: '500px'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
startBorrow(loansInfo).then(res => {
|
||||||
|
showToast('申请成功')
|
||||||
|
router.push({
|
||||||
|
path: '/serveList'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// on cancel
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const userInfo = reactive({
|
||||||
|
backCardNum: '',
|
||||||
|
bankType: '',
|
||||||
|
cardBackPicture: '',
|
||||||
|
cardFrontPicture: '',
|
||||||
|
cardNum: '',
|
||||||
|
companyAddress: '',
|
||||||
|
companyAddressInfo: '',
|
||||||
|
companyName: '',
|
||||||
|
companyPhone: '',
|
||||||
|
companyTitle: '',
|
||||||
|
companyYear: '',
|
||||||
|
customerAddress: '',
|
||||||
|
customerAddressInfo: '',
|
||||||
|
customerId: 0,
|
||||||
|
id: 0,
|
||||||
|
kinsfolkName: '',
|
||||||
|
kinsfolkPhone: '',
|
||||||
|
kinsfolkRef: '',
|
||||||
|
kinsfolkRefText: '',
|
||||||
|
realName: ''
|
||||||
|
})
|
||||||
|
const _getUserInfo = () => {
|
||||||
|
getUserInfo().then(res => {
|
||||||
|
resetData(userInfo, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收款账号脱敏
|
||||||
|
const backCardNumDesensitization = (backCardNum: string) => {
|
||||||
|
const blen = backCardNum.length
|
||||||
|
if (blen < 8) {
|
||||||
|
return backCardNum
|
||||||
|
}
|
||||||
|
let str = ''
|
||||||
|
for (let i = 0; i < blen - 7; i++) {
|
||||||
|
str+='*'
|
||||||
|
}
|
||||||
|
|
||||||
|
return backCardNum.slice(0, 4) + str + backCardNum.slice(blen-3, blen)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loansInfo.totalLoanMoney = route.query.totalLoanMoney
|
||||||
|
loansInfo.totalMonth = route.query.totalMonth
|
||||||
|
_getUserInfo()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.content {
|
||||||
|
background-image: linear-gradient(to bottom, #f9bf3a, #ffffff) !important;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
.action {
|
||||||
|
margin: 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
box-shadow: 0 0 1vw 0px #e0e0e0;
|
||||||
|
&-content {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px 20px;
|
||||||
|
box-shadow: 0 0 1vw 0px #e0e0e0;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.id-card-box {
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 0 1vw 0px #e0e0e0;
|
||||||
|
width: 500px;
|
||||||
|
height: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tt {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
background: #f6f9fd;
|
||||||
|
color: #738aa4;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
.xieyi {
|
||||||
|
padding: 30px 35px;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-22 {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
204
src/views/loans/info1/index.vue
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
<template>
|
||||||
|
<j-nav-bar color="#FFF" nav-bar-background="#f9bf3a" :placeholder="false"/>
|
||||||
|
<div class="content">
|
||||||
|
<j-gap height="120" background="#F9BF3A" opacity="0"/>
|
||||||
|
<div class="action">
|
||||||
|
<view class="tt">
|
||||||
|
确认提现信息
|
||||||
|
</view>
|
||||||
|
<van-cell title="到账银行" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="userInfo.bankType" />
|
||||||
|
<van-cell title="收款账号" title-style="color: #8997ae;" style="--van-cell-value-color: #000" :value="backCardNumDesensitization(userInfo.backCardNum)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action">
|
||||||
|
<view class="tt">
|
||||||
|
提现金额
|
||||||
|
</view>
|
||||||
|
<van-field
|
||||||
|
v-model="withdrawAmount"
|
||||||
|
label="金额"
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入你的提现金额"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="xieyi">-->
|
||||||
|
<!-- <van-checkbox v-model="checked" :icon-size="px2vw(28)" style="align-items: baseline" checked-color="linear-gradient(to right, #F9D88D, #D2A64C)">-->
|
||||||
|
<!-- 我已阅读并同意-->
|
||||||
|
<!-- <span class="yellow_color1" @click.stop="go('loansAgreement')">《借款协议》</span>-->
|
||||||
|
<!-- </van-checkbox>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
|
||||||
|
<div style="padding: 0 20px 60px">
|
||||||
|
<van-button
|
||||||
|
color="linear-gradient(to right, #D2A64C, #F9D88D)"
|
||||||
|
round
|
||||||
|
style="width: 100%; "
|
||||||
|
@click.stop="saveUserInfoBtn"
|
||||||
|
:disabled="!withdrawAmount || withdrawAmount == 0"
|
||||||
|
>
|
||||||
|
提现
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {onMounted, reactive, ref} from "vue";
|
||||||
|
import {useRoute, useRouter} from "vue-router";
|
||||||
|
import {useUserStore} from "@/store/modules/user";
|
||||||
|
import {getBorrowWithdraw, getUserInfo} from "@/api";
|
||||||
|
import {resetData} from "@/utils/dataUtil";
|
||||||
|
import {showConfirmDialog, showToast} from "vant";
|
||||||
|
|
||||||
|
const useUser = useUserStore()
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
|
||||||
|
const withdrawAmount = ref()
|
||||||
|
|
||||||
|
const go = (key) => {
|
||||||
|
// agreement
|
||||||
|
router.push({
|
||||||
|
path: '/agreement',
|
||||||
|
query: {
|
||||||
|
key: key
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const saveUserInfoBtn = () => {
|
||||||
|
|
||||||
|
showConfirmDialog({
|
||||||
|
title: '提示',
|
||||||
|
message: '您确定要提现吗',
|
||||||
|
width: '500px'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
getBorrowWithdraw({withdrawAmount: withdrawAmount.value}).then(res => {
|
||||||
|
showToast('提现成功')
|
||||||
|
router.push({
|
||||||
|
path: '/serveList'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// on cancel
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const userInfo = reactive({
|
||||||
|
backCardNum: '',
|
||||||
|
bankType: '',
|
||||||
|
cardBackPicture: '',
|
||||||
|
cardFrontPicture: '',
|
||||||
|
cardNum: '',
|
||||||
|
companyAddress: '',
|
||||||
|
companyAddressInfo: '',
|
||||||
|
companyName: '',
|
||||||
|
companyPhone: '',
|
||||||
|
companyTitle: '',
|
||||||
|
companyYear: '',
|
||||||
|
customerAddress: '',
|
||||||
|
customerAddressInfo: '',
|
||||||
|
customerId: 0,
|
||||||
|
id: 0,
|
||||||
|
kinsfolkName: '',
|
||||||
|
kinsfolkPhone: '',
|
||||||
|
kinsfolkRef: '',
|
||||||
|
kinsfolkRefText: '',
|
||||||
|
realName: ''
|
||||||
|
})
|
||||||
|
const _getUserInfo = () => {
|
||||||
|
getUserInfo().then(res => {
|
||||||
|
resetData(userInfo, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收款账号脱敏
|
||||||
|
const backCardNumDesensitization = (backCardNum: string) => {
|
||||||
|
const blen = backCardNum.length
|
||||||
|
if (blen < 8) {
|
||||||
|
return backCardNum
|
||||||
|
}
|
||||||
|
let str = ''
|
||||||
|
for (let i = 0; i < blen - 7; i++) {
|
||||||
|
str+='*'
|
||||||
|
}
|
||||||
|
|
||||||
|
return backCardNum.slice(0, 4) + str + backCardNum.slice(blen-3, blen)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
_getUserInfo()
|
||||||
|
withdrawAmount.value = route.query.account
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.content {
|
||||||
|
background-image: linear-gradient(to bottom, #f9bf3a, #ffffff) !important;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
.action {
|
||||||
|
margin: 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
box-shadow: 0 0 1vw 0px #e0e0e0;
|
||||||
|
&-content {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px 20px;
|
||||||
|
box-shadow: 0 0 1vw 0px #e0e0e0;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.id-card-box {
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 0 1vw 0px #e0e0e0;
|
||||||
|
width: 500px;
|
||||||
|
height: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tt {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
background: #f6f9fd;
|
||||||
|
color: #738aa4;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
.xieyi {
|
||||||
|
padding: 30px 35px;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-22 {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||