Commit ff619225 authored by 贾慧斌's avatar 贾慧斌

feat: 羊小咩h5代码测试

parent 5b1b86ea
> 1%
last 2 versions
not dead
safari >= 8
chrome >= 38
ie >= 11
\ No newline at end of file
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 100
node_modules/*
build/*
config/*
dist/*
lib/*
static/*
\ No newline at end of file
module.exports = {
root: true,
env: {
node: true,
browser: true,
},
extends: ["plugin:vue/essential", "plugin:vue/recommended", "eslint:recommended", "@vue/prettier"],
parserOptions: {
parser: "babel-eslint"
},
// TODO 配置强制使用模板字符串
rules: {
'prettier/prettier': 'error',
'vue/max-attributes-per-line': 'off',
"vue/html-self-closing": ["error", {
"html": {
"void": "always",
"normal": "always",
"component": "always"
},
"svg": "always",
"math": "always"
}],
'no-empty': ["error", { "allowEmptyCatch": true }],
'vue/no-v-html': 'off',
'vue/require-default-prop': 'off',
'arrow-parens': 'off', // allow paren-less arrow functions
'generator-star-spacing': 'off', // allow async-await
'no-unused-vars': 'error', // disabled no ununsed var
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', // no use debugger in production
'indent': [2, 2, { SwitchCase: 1 }], // 2 space for tab for perttier
'space-before-function-paren': ['error', 'never'], // no space in function name for perttier
}
};
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
.eslintcache
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
/*
* @Description:
* @Date: 2020-11-18 16:24:20
* @LastEditors: gzw
* @LastEditTime: 2020-11-18 16:47:05
*/
// prettier.config.js or .prettierrc.js
module.exports = {
printWidth: 100, // 设置prettier单行输出(不折行)的(最大)长度
tabWidth: 2, // 设置工具每一个水平缩进的空格数
semi: true, // 在语句末尾添加分号
singleQuote: true, // 使用单引号而非双引号
arrowParens: 'avoid', // 为单行箭头函数的参数添加圆括号,参数个数为1时可以省略圆括号
rangeStart: 0, // 只格式化某个文件的一部分
rangeEnd: Infinity, // 只格式化某个文件的一部分
};
const crypto = require('crypto');
const pkgInfo = require('./package.json');
const project = pkgInfo.name;
const hashName = crypto
.createHash('md5')
.update(project)
.digest('hex')
.substr(0, 8);
module.exports = {
org: 'sentry',
project,
authToken: '3f1f6f3789594e3a81280c1cd8d4d1008037a7abad714698b60b6f358c3d7562',
url: 'http://newsentry.quantgroups.com',
release: `${project}@1.0.0`,
ignore: ['node_modules', 'tests'],
silent: true,
include: [
{
paths: ['./dist/js'],
urlPrefix: `https://misc.lkbang.net/${hashName}/`
}
]
};
# pay-center-ui
基于vue-cli 3.0整合,目的为统一H5收银台支付,提供统一的支付入口与支付回调
## Quick start
```bash
git clone git@git.quantgroup.cn:ui/pay-center-ui.git
cd pay-center-ui
npm install && npm run serve
```
## Feature
- **入口参数统一**。对收银台支付流程进行整合。
- **出口参数统一**。对支付后的回调进行处理。
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
plugins: [
[
'import',
{
libraryName: '@qg/cherry-ui',
libraryDirectory: 'src',
style: name => {
// eslint-disable-next-line no-useless-escape
const lessName = name.match(/[^\/]+$/)[0];
return `${name}/${lessName}.less`;
}
}
],
'@babel/plugin-proposal-nullish-coalescing-operator',
'@babel/plugin-proposal-optional-chaining'
]
};
{
"compilerOptions": {
"target": "es2017",
"allowSyntheticDefaultImports": true,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"],
"~@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"],
"include": ["src"]
}
This diff is collapsed.
{
"name": "phobos-ui",
"version": "1.0.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --open",
"report": "vue-cli-service build --report",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@better-scroll/core": "^2.0.0-beta.6",
"@qg/cherry-ui": "^2.23.9",
"@qg/js-bridge": "^1.1.9",
"@qg/qg-scroll": "^1.4.2",
"@qg/ui-request": "^0.0.20",
"@qg/ui-track-web": "0.0.3",
"@sentry/vue": "^7.9.0",
"core-js": "^3.6.5",
"crypto-js": "^3.1.9-1",
"js-cookie": "^2.2.1",
"sa-sdk-javascript": "^1.15.16",
"vue": "2.6.11",
"vue-router": "^3.2.0",
"vuex": "^3.4.0",
"weixin-js-sdk": "^1.6.0"
},
"devDependencies": {
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1",
"@babel/plugin-proposal-optional-chaining": "^7.12.7",
"@qg/webpack-cos-cdn-upload-plugin": "1.0.2",
"@qg/sentry-webpack-plugin": "^1.19.0",
"@vue/cli-plugin-babel": "^4.4.0",
"@vue/cli-plugin-eslint": "^4.4.0",
"@vue/cli-plugin-router": "^4.4.0",
"@vue/cli-plugin-vuex": "^4.4.0",
"@vue/cli-service": "^4.4.0",
"@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.1.0",
"babel-plugin-import": "^1.13.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-vue": "^6.2.2",
"husky": "^4.2.5",
"less": "^3.0.4",
"less-loader": "^5.0.0",
"lint-staged": "^10.2.11",
"postcss-px2rem": "^0.3.0",
"prettier": "^1.19.1",
"svg-sprite-loader": "^5.2.1",
"vue-template-compiler": "^2.6.11"
},
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
}
module.exports = {
plugins: {
'postcss-px2rem': {
remUnit: 37.5
}
}
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="format-detection" content="telephone=yes">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no,viewport-fit=cover">
<link rel="icon" href="./logo.png" type="image/x-icon">
<title><%= htmlWebpackPlugin.options.title %></title>
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="external nofollow" rel="external nofollow" rel="stylesheet">
<% } %>
<style>
.mainload {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
text-align: center;
width: 100px;
height: 100px;
}
.mainload p {
font-size: 16px;
color: #666;
margin-top: 10px;
}
.mainload .circular {
height: 36px;
width: 36px;
animation: loading-rotate 2s linear infinite;
}
.mainload .path {
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90,150;
stroke-dashoffset: 0;
stroke-width: 3;
stroke: #ec3333;
stroke-linecap: round;
}
@keyframes loading-rotate {
to {
transform: rotate(1turn)
}
}
@keyframes loading-dash {
0% {
stroke-dasharray: 1,200;
stroke-dashoffset: 0
}
50% {
stroke-dasharray: 90,150;
stroke-dashoffset: -40px
}
to {
stroke-dasharray: 90,150;
stroke-dashoffset: -120px
}
}
@media screen and (min-width: 768px) {
html {
font-size: 37.5px !important;
background-color: #EEF0F3;
box-shadow: 0 1px 6px rgba(0,0,0,.117647), 0 1px 4px rgba(0,0,0,.117647);
}
body {
max-width: 375px;
left: 0;
right: 0;
margin: 0 auto !important;
}
.cr-nav-bar {
max-width: 375px;
left: 0;
right: 0;
margin: 0 auto !important;
z-index: 999;
}
}
</style>
<script>
window.onload = function() {
// 阻止双击放大
var lastTouchEnd = 0;
document.addEventListener('touchstart', function(event) {
if (event.touches.length > 1) {
event.preventDefault();
}
});
document.addEventListener('touchend', function(event) {
var now = (new Date()).getTime();
if (now - lastTouchEnd <= 300) {
event.preventDefault();
}
lastTouchEnd = now;
}, false);
<html lang="zh-CN">
// 阻止双指放大
document.addEventListener('gesturestart', function(event) {
event.preventDefault();
});
}
</script>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app">
<div class="mainload">
<svg viewBox="25 25 50 50" class="circular"><circle cx="50" cy="50" r="20" fill="none" class="path"></circle></svg>
<p>加载中</p>
</div>
</div>
<!-- built files will be auto injected -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
</body>
</html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />
</head>
<body>
<noscript>
<strong>Please enable JavaScript to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
\ No newline at end of file
<template>
<div id="app">
<cr-nav-bar v-if="header" :title="title" left-text="" @click-left="backFun" />
<div class="app">
<router-view />
</div>
<div v-if="loading" class="loading-container">
<cr-loading class="loading" size="24px">加载中...</cr-loading>
</div>
<net-error />
</div>
</template>
<script>
import { mapState } from 'vuex';
import NetError from '@/components/NetError';
export default {
name: 'App',
components: {
NetError
},
data() {
return {};
},
computed: {
...mapState({
title: state => state.pay.title,
header: state => state.pay.header,
loading: state => state.pay.loading
})
},
watch: {
header: {
immediate: true,
handler(val, oldVal) {
if (val !== oldVal) {
document.body.className = val ? 'has-header' : '';
}
}
}
},
methods: {
backFun() {
this.$router.go(-1);
}
}
};
</script>
<style lang="less">
.app {
user-select: none;
height: 100%;
min-height: 100%;
position: relative;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.loading-container {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 999;
display: flex;
justify-content: center;
align-items: center;
.loading {
padding: 6px;
border-radius: 6px;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
@{deep} .cr-loading--text {
color: #fff;
}
}
}
</style>
import request from '@/service/httpDecorator';
import config from '@/config';
import { saDeviceId } from '@/service/sa.service';
const { talosHost } = config;
const queryPayInfo = function(data) {
return request.post(`${talosHost}/open/checkout`, data);
};
const prepay = async function(data) {
const scDeviceId = await saDeviceId();
return request.post(`${talosHost}/open/checkout/prepay`, data, {
customHeader: {
scDeviceId
},
hideToast: true
});
};
const pay = async function(data) {
const scDeviceId = await saDeviceId();
return request.post(`${talosHost}/open/checkout/pay`, data, {
customHeader: {
scDeviceId
},
hideToast: true
});
};
const queryPayStatus = function(data) {
return request.post(`${talosHost}/open/checkout/pay_status/query`, data);
};
const sendSms = function(data) {
return request.post(`${talosHost}/open/checkout/send_sms`, data);
};
const desSalt = function() {
return request.get(`${talosHost}/vcc/account/salt`);
};
const h5AppyUrl = function() {
return request.get(`${talosHost}/vcc/xyqb_mall/app_url`);
};
const getCoupon = async function(params) {
const scDeviceId = await saDeviceId();
return request.post(`${talosHost}/open/checkout/pay_coupon_list`, params, {
customHeader: {
scDeviceId
}
});
};
const ocrFaceId = function(params) {
return request.post(`${talosHost}/open/checkout/ocr_faceId`, params);
};
const getGoodsList = async function(data) {
const scDeviceId = await saDeviceId();
return request.get(`${talosHost}/vcc/xyqb/recommend/goods-list`, {
params: data,
hideLoading: true,
customHeader: {
scDeviceId
}
});
};
// KA流程节点
const kaGetNextUrl = function() {
return request.get(`${talosHost}/api/kdsp/ka/process/get-next-url`);
};
const reissueContract = function(params) {
return request.post(`${talosHost}/open/checkout/payReissueContract`, params);
};
export default {
// 商品详情页面
desSalt() {
return request.get(`${talosHost}/vcc/account/salt`);
}
};
const getCouponList = function(params) {
return request.get(`${talosHost}/api/kdsp/appconfig/pay-succ-page/guide-stream`, { params });
};
// 获取用户手机号
const getPhoneNumber = function() {
return request.get(`${talosHost}/api/kdsp/user/phone`);
};
export {
pay,
prepay,
sendSms,
desSalt,
h5AppyUrl,
getCoupon,
ocrFaceId,
queryPayInfo,
getGoodsList,
kaGetNextUrl,
getCouponList,
queryPayStatus,
reissueContract,
getPhoneNumber
};
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1600935447036" class="icon" viewBox="0 0 1150 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13605" xmlns:xlink="http://www.w3.org/1999/xlink" width="898.4375" height="800"><defs><style type="text/css"></style></defs><path d="M815.338057 760.110324c-3.569371 0-6.456076-2.720914-6.456076-6.075734s2.886705-6.075733 6.456076-6.075733h226.0992c3.559619 0 6.446324 2.720914 6.446324 6.075733s-2.886705 6.075733-6.436571 6.075734H815.328305z m-804.015543 0c-3.559619 0-6.446324-2.720914-6.446324-6.075734s2.886705-6.075733 6.446324-6.075733h257.589638c3.559619 0 6.436571 2.720914 6.436572 6.075733s-2.876952 6.075733-6.436572 6.075734H11.312762z m286.573715 0c-3.559619 0-6.446324-2.720914-6.446324-6.075734s2.886705-6.075733 6.436571-6.075733h33.83101c3.559619 0 6.446324 2.720914 6.446324 6.075733s-2.886705 6.075733-6.446324 6.075734h-33.821257z m787.504761 0c-3.559619 0-6.436571-2.720914-6.436571-6.075734s2.876952-6.075733 6.436571-6.075733h50.25402c3.559619 0 6.436571 2.720914 6.436571 6.075733s-2.876952 6.075733-6.436571 6.075734h-50.244267z" fill="#666666" p-id="13606"></path><path d="M256.585143 713.56221a30.086095 30.086095 0 1 1 0-60.172191 30.086095 30.086095 0 1 1 0 60.172191z m0-12.892648a17.193448 17.193448 0 1 0 0-34.386895 17.193448 17.193448 0 0 0 0 34.386895z m55.881143-640.487619a30.086095 30.086095 0 1 1 0-60.181943 30.086095 30.086095 0 1 1 0 60.17219z m0-12.892648a17.193448 17.193448 0 1 0 0-34.396647 17.193448 17.193448 0 1 0 0 34.396647z" fill="#999999" p-id="13607"></path><path d="M174.723657 455.387429a7.528838 7.528838 0 0 0 7.3728-7.3728v-13.165715h13.692343a7.528838 7.528838 0 0 0 7.3728-7.3728 7.528838 7.528838 0 0 0-7.3728-7.3728h-13.692343v-13.165714a7.528838 7.528838 0 0 0-7.3728-7.3728 7.528838 7.528838 0 0 0-7.3728 7.3728v13.165714h-13.692343a7.528838 7.528838 0 0 0-7.3728 7.3728c0 3.6864 3.159771 7.3728 7.3728 7.3728h13.692343v13.165715c0 4.213029 3.159771 7.3728 7.3728 7.3728z" fill="#D1E7FF" p-id="13608"></path><path d="M1008.620495 479.534324c0.692419-0.029257 0.897219 0.195048 0.877715 0.877714-0.029257 3.637638 0 7.285029 0 10.922667 0 3.676648-0.029257 7.353295 0 11.02019 0 0.57539-0.126781 0.838705-0.546134 0.906972l-0.37059 0.019504c-1.755429-0.03901-3.510857-0.03901-5.256534 0-0.565638 0-0.741181-0.195048-0.741181-0.750933v-22.206171c0-0.585143 0.175543-0.809448 0.780191-0.789943 1.755429 0.019505 3.510857 0.029257 5.256533 0z m21.767315-10.990934l18.178438 18.217448c0.03901 0.03901 0.068267 0.097524 0.195047 0.321829-1.531124 1.501867-3.052495 3.03299-4.612876 4.486095-0.126781 0.117029-0.672914-0.03901-0.877714-0.234057-2.555124-2.506362-5.090743-5.061486-7.626362-7.587353l-9.908419-9.918171-0.682667-0.721676c0.117029-0.175543 0.214552-0.360838 0.331581-0.487619 1.355581-1.355581 2.691657-2.681905 4.017981-4.027734 0.331581-0.341333 0.585143-0.44861 0.984991-0.048762z m-42.383848 0.175543c1.170286 1.26781 2.408838 2.4576 3.64739 3.657143 0.341333 0.341333 0.44861 0.594895 0.058515 1.004495l-17.905372 17.885867c-0.078019 0.078019-0.195048 0.126781-0.390095 0.273067-1.531124-1.521371-3.023238-2.993981-4.46659-4.5056-0.107276-0.107276-0.068267-0.44861 0-0.643657 0.03901-0.146286 0.224305-0.253562 0.341333-0.370591 5.753905-5.763657 11.537067-11.517562 17.290971-17.300724 0.555886-0.585143 0.897219-0.565638 1.423848 0z m-7.5776-22.89859c0.594895 0 0.887467 0.117029 0.965486 0.565638l0.019504 0.399848a97.123962 97.123962 0 0 0 0 5.168761c0 0.594895-0.2048 0.78019-0.789942 0.780191-7.41181-0.019505-14.823619-0.019505-22.206172 0-0.643657 0-0.770438-0.263314-0.770438-0.828952 0.03901-1.755429 0.03901-3.501105 0-5.246781 0-0.643657 0.2048-0.838705 0.848457-0.838705l5.500343 0.019505h5.510095c3.637638 0 7.285029 0.019505 10.922667-0.019505z m78.682209 0c0.702171 0 0.955733 0.185295 0.936229 0.926476a116.736 116.736 0 0 0 0 5.237029c0 0.565638-0.195048 0.750933-0.750933 0.750933h-22.196419c-0.682667 0-0.8192-0.263314-0.809448-0.877714 0.029257-1.706667 0.03901-3.44259 0-5.168762-0.029257-0.682667 0.214552-0.867962 0.877714-0.867962l5.510096 0.019505h5.510095c3.64739 0 7.285029 0.019505 10.922666-0.019505z m-85.586895-33.704229l0.721676 0.682667 17.534781 17.505524c0.585143 0.585143 0.526629 0.897219-0.019504 1.4336-1.39459 1.345829-2.750171 2.759924-4.183772 4.242285-0.390095-0.351086-0.643657-0.565638-0.877714-0.809447-5.783162-5.77341-11.546819-11.566324-17.359238-17.320229-0.633905-0.633905-0.585143-0.975238 0.029257-1.560381 1.414095-1.336076 2.750171-2.750171 4.154514-4.174019z m70.090362 0c1.511619 1.531124 2.993981 3.003733 4.447086 4.5056 0.107276 0.117029 0.068267 0.44861 0.009752 0.643657-0.048762 0.146286-0.224305 0.253562-0.331581 0.370591-5.783162 5.77341-11.556571 11.527314-17.300724 17.310476-0.565638 0.57539-0.887467 0.565638-1.4336-0.009752-1.170286-1.26781-2.408838-2.4576-3.637638-3.666896-0.331581-0.331581-0.458362-0.585143-0.068266-0.98499l17.915123-17.895619c0.087771-0.078019 0.185295-0.126781 0.390096-0.273067z m-34.98179-11.234743c0.702171-0.029257 0.877714 0.224305 0.877714 0.877715-0.029257 3.676648-0.019505 7.353295-0.019505 11.02019v0.009753c0 3.618133-0.019505 7.236267 0.019505 10.8544 0 0.633905-0.146286 0.916724-0.604648 0.994742l-0.4096 0.019505a101.015162 101.015162 0 0 0-5.090743-0.009752c-0.565638 0-0.8192-0.126781-0.8192-0.750934v-22.225676c0-0.604648 0.195048-0.809448 0.780191-0.78019 1.765181 0.019505 3.52061 0.029257 5.266286 0z" fill="#52CC90" p-id="13609"></path><path d="M890.63619 235.129905a13.390019 13.390019 0 0 1-8.97219 3.462095c-3.540114 0-7.12899-1.414095-9.752381-4.174019-66.540495-69.992838-160.797257-110.162895-258.769676-110.162895s-188.884114 38.180571-256.195048 107.539504a13.507048 13.507048 0 0 1-18.724571 0.507124 12.65859 12.65859 0 0 1-0.585143-18.217447c72.313905-74.556952 170.159543-115.614476 275.446248-115.614477s206.652952 43.17379 278.196419 118.432915c5.002971 5.198019 4.681143 13.409524-0.643658 18.2272z m-277.494247-24.254172c79.306362 0 155.140876 32.914286 208.0768 90.277791 4.934705 5.383314 4.486095 13.653333-1.024 18.471009-2.574629 2.243048-5.77341 3.335314-8.981943 3.335315-3.72541 0-7.382552-1.462857-10.07421-4.359315-47.806171-51.843657-116.3264-81.607924-187.92838-81.607923-70.256152 0-137.820648 28.808533-185.490286 78.97478a13.789867 13.789867 0 0 1-19.056153 0.780191 12.804876 12.804876 0 0 1-0.770438-18.490514c52.682362-55.559314 127.561143-87.381333 205.24861-87.381334z m0.068267 125.942248c48.313295 0 93.671619 22.528 124.411123 61.732571a14.316495 14.316495 0 0 1-1.80419 19.436496 12.648838 12.648838 0 0 1-18.47101-1.930972c-25.736533-32.846019-63.712305-51.707124-104.135923-51.707124-39.136305 0-76.283124 17.837105-101.883124 48.956953a12.746362 12.746362 0 0 1-18.549029 1.404343 14.238476 14.238476 0 0 1-1.345828-19.436496c30.671238-37.156571 74.99581-58.455771 121.777981-58.455771z m1.287314 98.108952a56.046933 56.046933 0 0 1 56.07619 56.076191 56.046933 56.046933 0 0 1-56.07619 56.07619 56.046933 56.046933 0 0 1-56.076191-56.07619 56.134705 56.134705 0 0 1 56.076191-56.076191z m0 84.114286a28.067352 28.067352 0 0 0 28.038095-28.038095 28.028343 28.028343 0 0 0-28.038095-28.038095 28.028343 28.028343 0 0 0-28.038095 28.038095 28.067352 28.067352 0 0 0 28.038095 28.038095zM616.272457 565.062705c6.524343 0 11.6736 5.15901 11.6736 11.332266l0.341333 61.810591h35.108572a132.047238 132.047238 0 0 0 75.12259 80.115809v17.408a24.478476 24.478476 0 0 1-24.380952 24.380953H419.498667a24.478476 24.478476 0 0 1-24.380953-24.380953v-73.142857a24.478476 24.478476 0 0 1 24.380953-24.380952H603.574857v-61.810591c0-6.173257 5.149257-11.332267 11.332267-11.332266h1.365333zM505.699962 687.318552c-6.875429 0-12.366019 5.49059-12.366019 12.014934s5.49059 12.014933 12.366019 12.014933c6.865676 0 12.356267-5.49059 12.356267-12.014933 0-6.865676-5.49059-12.014933-12.356267-12.014934z m48.761905 0c-6.875429 0-12.366019 5.49059-12.366019 12.014934 0.341333 6.524343 5.841676 12.014933 12.366019 12.014933 6.865676 0 12.356267-5.49059 12.356266-12.014933 0-6.865676-5.49059-12.014933-12.356266-12.014934z m-97.874896 0c-6.865676 0-12.356267 5.49059-12.356266 12.014934s5.49059 12.014933 12.356266 12.014933c6.875429 0 12.366019-5.49059 12.366019-12.014933 0-6.865676-5.49059-12.014933-12.366019-12.014934z" fill="#999999" p-id="13610"></path><path d="M788.284952 493.987352c-56.554057 0-102.4 45.855695-102.4 102.4s45.845943 102.4 102.4 102.4c56.534552 0 102.4-45.855695 102.4-102.4s-45.865448-102.4-102.4-102.4z m49.893181 137.840153a10.24 10.24 0 0 1-14.492038 14.482285l-35.401143-35.440152-35.440152 35.410895a10.259505 10.259505 0 0 1-14.482286 0 10.24 10.24 0 0 1 0-14.482285l35.4304-35.410896-35.410895-35.440152a10.24 10.24 0 0 1 14.482286-14.482286l35.410895 35.440153 35.440152-35.440153a10.24 10.24 0 0 1 14.482286 14.482286l-35.4304 35.440152 35.401143 35.440153z" fill="#FC5A5A" p-id="13611"></path></svg>
\ No newline at end of file
<template>
<cr-overlay custom-class="net-error-mask" :show="showError">
<svg-icon icon-class="ufo" />
<p class="net-error-title">网络异常</p>
<cr-button type="danger" size="large" @click="refresh">刷新试试</cr-button>
</cr-overlay>
</template>
<script>
export default {
name: 'NetError',
data() {
return {
showError: false
};
},
mounted() {
this.init();
},
methods: {
refresh() {
window.location.reload();
},
init() {
window.addEventListener(
'online',
() => {
this.showError = false;
},
true
);
window.addEventListener(
'offline',
() => {
this.showError = true;
},
true
);
}
}
};
</script>
<style lang="less" scoped>
@import '../style/var.less';
.net-error {
&-mask {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 101;
background-color: #fff;
.svg-icon {
width: 180px;
height: 180px;
margin-top: -120px;
}
.cr-button {
margin-top: 32px;
width: 200px;
border-radius: 6px;
}
}
&-title {
color: @gray-3;
.text-16;
}
}
</style>
<template>
<svg :class="[className, 'svg-icon']" aria-hidden="true">
<use :xlink:href="iconName" />
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: [String, Array],
required: true
},
className: {
type: [String, Array],
default: ''
}
},
computed: {
iconName() {
return `#icon-${this.iconClass}`;
}
}
};
</script>
<style>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>
<template>
<div class="page error">
<cr-image src="@/assets/images/error/error404.png" width="298px" height="153px" />
<p>你要访问的页面找不到了</p>
<cr-button type="primary" block shape="circle" @click="backurl">返回</cr-button>
</div>
</template>
<script>
export default {
name: '',
data() {
return {};
},
mounted() {},
methods: {
backurl() {
this.$router.go(-1);
}
}
};
</script>
<style lang="less" scoped>
@import '../../style/var.less';
.error {
text-align: center;
padding-top: 0;
padding-bottom: 0;
.cr-image {
margin-top: @padding-sm * 3;
}
p {
color: @black;
.text-14;
margin: @padding-unit * 7 0;
}
}
</style>
<template>
<div class="page error">
<cr-image src="@/assets/images/error/error.png" width="298px" height="153px" />
<p>oh,no…出错了</p>
<cr-button type="primary" block shape="circle" @click="backurl">返回</cr-button>
</div>
</template>
<script>
export default {
name: '',
data() {
return {};
},
mounted() {},
methods: {
backurl() {
this.$router.go(-1);
}
}
};
</script>
<style lang="less" scoped>
@import '../../style/var.less';
.error {
text-align: center;
padding-top: 0;
padding-bottom: 0;
.cr-image {
margin-top: @padding-sm * 3;
}
p {
color: @black;
.text-14;
margin: @padding-unit * 7 0;
}
}
</style>
const shenceHost = 'https://bn.xyqb.com/sa?project=default'; // 测试地址
const talosHost = 'https://talos-vcc2.liangkebang.net'; // 电商分期测试环境服务地址
const operatorHost = 'https://operator.liangkebang.com';
const defaultChannelCode = '155913';
export default {
talosHost,
operatorHost,
test: true,
shenceHost,
defaultChannelCode
};
import production from './production.config';
import env from './env.config';
export default process.env.NODE_ENV === 'production' ? production : env;
const protocol = window.location.protocol;
const talosHost = protocol + '//talos.xyqb.com';
const operatorHost = protocol + '//auth.quantgroup.cn';
const shenceHost = 'https://bn.xyqb.com/sa?project=production';
const defaultChannelCode = '155913';
export default {
test: false,
shenceHost,
talosHost,
operatorHost,
defaultChannelCode
};
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import '@/service/svg.service';
import '@/service/cherryUI.service';
import './style/index.less';
import saService from '@/service/sa.service';
import Bridge from '@qg/js-bridge';
import { init as sentryInit } from '@sentry/vue';
if (process.env.SENTRY_ENV !== 'test' && process.env.NODE_ENV === 'production') {
sentryInit({
Vue,
dsn: 'http://ab941e35776348e6aed32324658798f3@sentry.q-gp.com/17', // 项目设置中的Client Keys
release: 'phobos-ui@1.0.0', // 项目名加版本号
tracesSampleRate: 0.5, // 上报频率, 1最大, 0最小, 建议验证设置为1, 项目运行根据情况降低频率
environment: process.env.NODE_ENV
});
}
Vue.prototype.util = new Bridge();
Vue.prototype.$track = saService;
saService.init(router);
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app');
import Vue from 'vue';
import VueRouter from 'vue-router';
import initService from '@/service/init.service';
import routes from './routes';
Vue.use(VueRouter);
// 解决跳转相同路由报错
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err);
};
const originalReplace = VueRouter.prototype.replace;
VueRouter.prototype.replace = function replace(location) {
return originalReplace.call(this, location).catch(err => err);
};
const scrollBehavior = (to, from, savedPosition) => {
if (savedPosition) {
return savedPosition;
}
return {
x: 0,
y: 0
};
};
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
scrollBehavior
});
initService.init(router);
export default router;
export default [
{
path: '/',
redirect: '/pay'
},
{
path: '/pay',
name: 'pay',
meta: {
title: '收银台'
},
component: () => import('../views/pay')
},
{
path: '/payFail',
name: 'payFail',
alias: ['/payFail'],
meta: {
title: '支付失败'
},
component: () => import('../views/pay/payResult')
},
{
path: '/paySuccess',
name: 'paySuccess',
alias: ['/paySuccess'],
meta: {
title: '支付成功',
success: true
},
component: () => import('../views/pay/payResult')
},
{
path: '/payWaiting',
name: 'payWaiting',
meta: {
title: '支付中',
success: true
},
component: () => import('../views/pay/payWaiting')
},
{
path: '/error',
name: 'error',
meta: {
title: '出错了'
},
component: () => import('../components/error')
},
{
path: '*',
name: 'notFound',
meta: {
title: '找不到页面'
},
component: () => import('../components/error/NotFound')
}
];
import Vue from 'vue';
// import store from '../store';
import {
Button,
Image,
Icon,
Cell,
CellGroup,
Radio,
RadioGroup,
Row,
Col,
Dialog,
Popup,
Overlay,
Divider,
Loading,
Toast,
Picker,
NavBar,
Field,
Checkbox,
CardList,
List,
Form,
Sticky,
Tab,
Tabs,
Empty,
Swipe,
SwipeItem,
Tabbar,
TabbarItem,
BackTop,
Stepper,
AddressEdit,
SubmitBar,
CouponList,
Switch,
AddressList,
Collapse,
CollapseItem,
Uploader,
IndexList,
IndexListItem,
Tag,
ActionSheet,
Steps,
Step,
Notify,
CountDown,
PwdField,
AuthcodeField,
Skeleton,
Card,
Progress,
GoodsAction
} from '@qg/cherry-ui';
import DialogFn from '@qg/cherry-ui/src/dialog/func';
// import "@qg/cherry-ui/dist/cherry.css";
Vue.use(NavBar);
Vue.use(Button);
Vue.use(Image);
Vue.use(Cell);
Vue.use(CellGroup);
Vue.use(Radio);
Vue.use(RadioGroup);
Vue.use(Row);
Vue.use(Col);
Vue.use(Popup);
Vue.use(Divider);
Vue.use(Toast);
Vue.use(Picker);
Vue.use(Field);
Vue.use(Dialog);
Vue.use(Checkbox);
Vue.use(Form);
Vue.use(Icon);
Vue.use(Sticky);
Vue.use(Overlay);
Vue.use(CardList);
Vue.use(Loading);
Vue.use(List);
Vue.use(Tab);
Vue.use(Tabs);
Vue.use(Empty);
Vue.use(Swipe);
Vue.use(SwipeItem);
Vue.use(Tabbar);
Vue.use(TabbarItem);
Vue.use(Stepper);
Vue.use(BackTop);
Vue.use(AddressEdit);
Vue.use(SubmitBar);
Vue.use(CouponList);
Vue.use(Switch);
Vue.use(AddressList);
Vue.use(Collapse);
Vue.use(CollapseItem);
Vue.use(Uploader);
Vue.use(IndexList);
Vue.use(IndexListItem);
Vue.use(Tag);
Vue.use(ActionSheet);
Vue.use(Steps);
Vue.use(Step);
Vue.use(Notify);
Vue.use(CountDown);
Vue.use(PwdField);
Vue.use(AuthcodeField);
Vue.use(Skeleton);
Vue.use(Card);
Vue.use(Progress);
Vue.use(GoodsAction);
// const _proto = Vue.prototype;
// const proto = Object.create(_proto);
// proto.$notify = option => {
// option.customClass = store.state.pay.header ? 'has-header' : '';
// return _proto.$notify(option);
// };
// Vue.prototype = proto;
Vue.prototype.$dialog = DialogFn;
import cookies from 'js-cookie';
const option = {
domain: window.location.host.indexOf('.q-gp.com') >= 0 ? '.q-gp.com' : '',
expires: 1
};
const Cookies = {
get(key) {
return cookies.get(key) || '';
},
set(key, value) {
return cookies.set(key, value, option);
},
clear() {
return Object.keys(cookies.get()).forEach(function(cookie) {
Cookies.remove(cookie, option);
});
},
remove(key) {
return cookies.remove(key, option);
}
};
export default Cookies;
import CryptoJS from 'crypto-js';
import pay from '@/api/pay.api';
/**
* @description: 数据加密
* @message {String} message 数据源
* @return {String} 加密后的数据16进制
*/
export async function encryptByDESModeEBC(message) {
console.log(message);
const [{ payPwdSalt }] = await pay.desSalt();
var keyHex = CryptoJS.enc.Utf8.parse(payPwdSalt);
var encrypted = CryptoJS.DES.encrypt(message, keyHex, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.ciphertext.toString().toLocaleUpperCase();
}
import HttpRequest from '@qg/ui-request';
import { Toast } from '@qg/cherry-ui';
import store from '@/store';
import router from '@/router';
import { appVersion, isWxMp } from '@/service/validation.service';
import { getVccChannel } from './userInfo.service';
import { parseSearch } from './utils.service';
import localStorage from '@/service/localStorage.service';
import sessionStorage from '@/service/sessionStorage.service';
function getURLSearchParams(json) {
if (!json) return '';
const dataArray = Object.keys(json).map(key => {
if (json[key] === undefined) return '';
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);
});
return dataArray.filter(item => item !== undefined && item !== null).join('&');
}
const http = new HttpRequest(
{
payCenter: {
response(res) {
const { data, config } = res;
const returnRawData = config.rawData;
if (returnRawData) return [data, null];
const success =
(data.code === '0000' && data.businessCode === '0000') || data.business_code === 0;
if (success) {
return [data.data, null];
}
const msg = data.msg || '服务异常';
if (!config.hideToast || !data.noAlert) {
Toast(msg);
}
let error = new Error(msg);
error.response = data;
if (
error?.response?.businessCode === '6049' ||
error?.response?.businessCode === '401' ||
error?.response?.businessCode === '0401'
) {
localStorage.remove('vccToken');
sessionStorage.remove('vccToken');
const toast = Toast('未登录!');
toast.onClose = () => {
const { returnUrl } = parseSearch(window.location.href);
if (returnUrl) {
window.location.href = returnUrl;
} else {
router.replace({ name: 'error' });
}
};
}
console.error(error);
return [null, error];
},
request(cfg) {
const { method } = cfg;
cfg.headers['X-Auth-Token'] = localStorage.get('vccToken') || '';
if (cfg.customHeader) {
Object.assign(cfg.headers, cfg.customHeader);
}
cfg.headers['x-user-terminal'] = isWxMp ? 'MINI-APP' : 'H5';
cfg.headers['vccChannel'] = getVccChannel();
cfg.headers['sonVccChannel'] = getVccChannel('sonVccChannel');
if (method === 'post' && cfg.emulateJSON) {
cfg.headers['Content-Type'] = 'application/x-www-form-urlencoded';
cfg.data = getURLSearchParams(cfg.data);
}
return cfg;
}
}
},
{
strategy: 'payCenter',
headers: {
'x-user-terminal': isWxMp ? 'MINI-APP' : 'H5',
version: appVersion || '7900'
}
},
function(msg) {
Toast(msg);
},
function(loadingState) {
store.dispatch('change_loading', loadingState);
}
).getInstance();
export default http;
/**
* Created by jialiu on 17/1/11.
*/
import store from '../store';
import { isWechat, isApp, isAndroid, isIOS } from './validation.service';
import Cookies from './cookieStorage.service';
import localStorage from './localStorage.service';
import sessionStorage from './sessionStorage.service';
import { loginSa } from './sa.service';
// import { Toast } from '@qg/cherry-ui';
export default {
// token校验,整个流程都是登陆后的
init: router => {
router.beforeEach((to, from, next) => {
Cookies.set('h', 0);
const query = JSON.parse(JSON.stringify(to.query));
if (Object.keys(query).indexOf('vccToken') == -1) {
to.query.vccToken = sessionStorage.get('vccToken') || '';
}
// console.log(to.query, 1);
// 所有自定义路由字段在此处理
const { meta } = to;
isWechat && localStorage.set('vccChannel', '159913');
isIOS && localStorage.set('vccChannel', '214');
isAndroid && localStorage.set('vccChannel', '217');
meta?.has?.header && store.commit('CHANGE_HEADER', meta.has.header); // 改变header
const localParams = [
'redirectUrl',
'h',
'vccToken',
'vccChannel',
'sonVccChannel',
'distinctID'
];
// TODO: 类似逻辑可以整合
localParams.forEach(item => {
const value = to.query[item];
if (item === 'vccToken' && value === '') {
localStorage.remove('vccToken');
sessionStorage.remove(item);
}
if (value && value !== '{token}' && value !== '{vccToken}') {
localStorage.set(item, value);
sessionStorage.set(item, value);
}
if (item === 'distinctID' && to.query[item]) {
loginSa(to.query[item]);
}
});
(isWechat || isApp || Cookies.get('h') === '0') && store.commit('CHANGE_HEADER', false); // 改变header
document.body.className = store.state.pay.header ? 'has-header' : '';
store.commit('CHANGE_TITLE', meta?.title); // 改变title
next();
});
}
};
export default {
get(key) {
let result = window.localStorage.getItem(key);
try {
return JSON.parse(result);
} catch (e) {
return result;
}
},
set(key, value) {
let toString = Object.prototype.toString;
if (toString.call(value) === '[object Array]' || toString.call(value) === '[object Object]') {
value = JSON.stringify(value);
}
return window.localStorage.setItem(key, value);
},
remove(key) {
return window.localStorage.removeItem(key);
},
clear() {
return window.localStorage.clear();
}
};
/* eslint-disable space-before-function-paren */
/* eslint-disable prettier/prettier */
/*
* @Description: 支付微信h5, jsapi, 第三方收银台,跳转)
* @Date: 2020-07-28 15:03:52
* @LastEditors: gzw
* @LastEditTime: 2021-11-08 19:36:02
*/
import qs from 'qs';
import cookies from '@/service/cookieStorage.service';
import localStorage from '@/service/localStorage.service';
/**
* payByWeixinJsapi
* @description: 微信通过jsapi支付
* 可以在微信浏览器调起支付
* @param {type}
* @return:
*/
function payByWeixinJsapi(info = {}, callback) {
function onBridgeReady() {
// eslint-disable-next-line no-undef
WeixinJSBridge.invoke('getBrandWCPayRequest', info, function (res) {
if (res.err_msg == 'get_brand_wcpay_request:ok') {
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
callback('ok');
} else if (res.err_msg == 'get_brand_wcpay_request:cancel') {
// 支付取消
callback('cancel');
} else {
// 支付失败
callback('fail');
}
});
}
if (typeof WeixinJSBridge == 'undefined') {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
}
/**
* payByWeixinH5
* @description: 微信通过H5支付
* 可以在浏览器调起微信支付
* 在羊小咩(信用钱包)app中调起微信支付,会先唤起微信,原来的页面会自动跳转到redirect_url设定的地址
* @param {type}
* @return:
*/
function payByWeixinH5(info) {
if (!info.url) return;
if (!info.params) {
info.params = {};
}
const vccToken = localStorage.get('vccToken');
const orderNo = cookies.get('orderNo');
info.params.isWxH5 = 1;
info.params.vccToken = vccToken;
info.params.orderNo = orderNo;
info.params.isWx = true;
const redirectUrl = localStorage.get('redirectUrl') || window.location.origin;
const currentPath = encodeURIComponent(
redirectUrl +
// eslint-disable-next-line prettier/prettier
'/payWaiting' +
qs.stringify(info.params, { encode: true, addQueryPrefix: true })
);
console.log(`${info.url}&redirect_url=${currentPath}`);
try {
const nextPage = document.createElement('a');
nextPage.setAttribute('href',);
nextPage.click();
} catch (error) {
window.location.href = `${info.url}&redirect_url=${currentPath}`;
}
}
/**
* payByALIH5
* @description: 支付宝支付
* @param {type}
* @return:
*/
function payByALIH5(info) {
if (!info.url) return;
const aliWrap = document.createElement('div');
aliWrap.id = 'ALIWEB_WRAP';
aliWrap.setAttribute('id', 'ALIWEB_WRAP');
aliWrap.innerHTML = info.url;
document.body.appendChild(aliWrap);
document.forms[0].submit();
setTimeout(() => {
const ALIWEB_WRAP = document.getElementById('ALIWEB_WRAP');
if (ALIWEB_WRAP != null) ALIWEB_WRAP.parentNode.removeChild(ALIWEB_WRAP);
}, 500);
}
/**
* payByThirdPartyCashier
* @description: 第三方收银台
* @param {type}
* @return:
*/
function payByThirdPartyCashier(info) {
if (!info.url) return;
const orderNo = cookies.get('orderNo');
info.params.third = 1;
info.params.orderNo = orderNo;
const currentPath = encodeURIComponent(
window.location.origin +
'/payWaiting' +
qs.stringify(info.params, { encode: true, addQueryPrefix: true })
);
const nextPage = document.createElement('a');
nextPage.setAttribute('href', `${info.url}&redirect_uri=${currentPath}`);
nextPage.click();
window.location.href = `${info.url}&redirect_uri=${currentPath}`;
}
/**
* @description: 支付方式判断, 返回promise
* NATIVE=原生扫码支付.,APP=ap支付,,JSAPI=公众号支付/小程序支付,,MWEB=H5支付.,MICROPAY=刷卡支付,默认JSAPI
* @param {String} type 支付方式,THIRD -> 第三方,MWEB -> H5支付, JSAPI -> jsapi支付,默认支付方式,THIRD
* @param {Object/String} payInfo 支付信息
* @return {Promise} 回调
*/
export function payByWay(type = 'THIRD', payInfo) {
return new Promise((resolve, reject) => {
if (type === 'JSAPI' && !payInfo.url) {
payByWeixinJsapi(payInfo, function (e) {
if (e === 'ok') {
resolve();
} else {
reject();
}
});
} else {
if (payInfo.url.indexOf('tenpay') > -1) {
payByWeixinH5(payInfo);
reject();
} else if (type === 'ALIWEB') {
payByALIH5(payInfo);
resolve();
} else {
payByThirdPartyCashier(payInfo);
reject();
}
// resolve();
}
});
}
import Vue from 'vue';
import sa from 'sa-sdk-javascript';
import config from '@/config';
const saCookiesKey = 'sensorsdata2015jssdkcross';
export default {
init: function(router) {
sa.init({
server_url: config.shenceHost,
heatmap: {
// 是否开启点击图,默认 default 表示开启,自动采集 $WebClick 事件,可以设置 'not_collect' 表示关闭
clickmap: 'default',
// 关闭触达注意力
scroll_notice_map: 'not_collect'
},
// TODO: app是如何判断的
// 判断外层是否有 App 的 SDK ,如果有的话,会往 App 的 SDK 发数据。如果没有,就正常发送数据。
use_app_track: false,
show_log: config.env === 'test'
});
// 添加公共属性
sa.registerPage({ platformType: 'H5' });
router.afterEach(() => {
Vue.nextTick(() => {
sa.quick('autoTrackSinglePage');
});
});
}
};
export function saTrackEvent(name, data) {
sa.track(name, data);
}
export function saDeviceId() {
return new Promise(resolve => {
sa.quick('isReady', function() {
resolve(sa.quick('getAnonymousID'));
});
});
}
// 设置公共属性
export function setRegisterPage(registerPageObj) {
sa.registerPage(registerPageObj);
}
// 用户登录神策埋点
export function loginSa(uuid = localStorage.get('uuid')) {
if (!uuid) return;
localStorage.set('uuid', uuid);
sa.login(uuid);
}
export function saDistinctId() {
const reg = new RegExp(`(^| )${saCookiesKey}=([^;]*)(;|$)`);
let cookieValue = document.cookie.match(reg);
if (!cookieValue) return '';
cookieValue = JSON.parse(decodeURIComponent(cookieValue[2]));
return cookieValue.distinct_id || '';
}
export default {
get(key) {
let result = window.sessionStorage.getItem(key);
try {
return JSON.parse(result);
} catch (e) {
return result;
}
},
set(key, value) {
let toString = Object.prototype.toString;
if (toString.call(value) === '[object Array]' || toString.call(value) === '[object Object]') {
value = JSON.stringify(value);
}
return window.sessionStorage.setItem(key, value);
},
remove(key) {
return window.sessionStorage.removeItem(key);
},
clear() {
return window.sessionStorage.clear();
}
};
import localStorage from './localStorage.service';
export function wxsdk() {
if (+localStorage.get('vccChannel') !== 159913) return; // 是否是小程序
var oHead = document.getElementsByTagName('head').item(0);
var oScript = document.createElement('script');
oScript.type = 'text/javascript';
oScript.src = 'https://res.wx.qq.com/open/js/jweixin-1.4.0.js';
oHead.appendChild(oScript);
}
import Vue from 'vue';
import SvgIcon from '@/components/SvgIcon'; // svg component
// register globally
Vue.component('svg-icon', SvgIcon);
const req = require.context('../assets/svg', false, /\.svg$/);
const requireAll = requireContext => requireContext.keys().map(requireContext);
requireAll(req);
import { parseSearch } from './utils.service';
import localStorage from './localStorage.service';
// import config from '@/config/index';
export function getVccChannel(channelKey = 'vccChannel') {
if (channelKey === 'sonVccChannel') {
return localStorage.get(channelKey) || parseSearch(window.location.href)[channelKey] || '';
}
return localStorage.get(channelKey) || parseSearch(window.location.href)[channelKey] || '';
}
import { isAndroid, isIOS } from './validation.service';
/**
* hash路由获取url参数
* @param {String} searchString 输入字符串
* @return: {String} str 字符串
*/
export function parseSearch(searchString) {
if (!searchString) {
return {};
}
if (!searchString.includes('?')) {
return {};
}
return searchString
.split('?')[1]
.split('#')[0]
.split('&')
.reduce((result, next) => {
const pair = next.split('=');
try {
result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
} catch (e) {
// eslint-disable-next-line
// result[decodeURIComponent(pair[0])] = window.$URL.decode(pair[1]);
}
return result;
}, {});
}
/**
* @description: 移动端监听软键盘弹起或收起,IOS,android
* @param {Boolean} off 是否销毁
* @return {Function} callback 软键盘弹起或收取回调, true => 弹起, false => 隐藏
*/
export function onKeyboardStateChange(callback = () => {}, off = false) {
if (isIOS) {
let isReset = true; //是否归位
const focusinHandler = () => {
isReset = false; //聚焦时键盘弹出,焦点在输入框之间切换时,会先触发上一个输入框的失焦事件,再触发下一个输入框的聚焦事件
callback(true);
};
const focusoutHandler = () => {
isReset = true;
setTimeout(() => {
//当焦点在弹出层的输入框之间切换时先不归位
if (isReset) {
// window.scroll(0, 0); // 【暂时取掉】确定延时后没有聚焦下一元素,是由收起键盘引起的失焦,则强制让页面归位
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
callback(false);
}
}, 30);
};
if (off) {
document.body.removeEventListener('focusin', focusinHandler);
document.body.removeEventListener('focusout', focusoutHandler);
} else {
document.body.addEventListener('focusin', focusinHandler);
document.body.addEventListener('focusout', focusoutHandler);
}
}
if (isAndroid) {
const originHeight = document.documentElement.clientHeight || document.body.clientHeight;
const resizeHandler = () => {
const resizeHeight = document.documentElement.clientHeight || document.body.clientHeight;
const activeElement = document.activeElement;
if (resizeHeight < originHeight) {
// 键盘弹起后逻辑
if (
activeElement &&
(activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA')
) {
setTimeout(() => {
activeElement.scrollIntoView({ block: 'center' }); //焦点元素滚到可视区域的问题
}, 0);
callback(true);
}
} else {
// 键盘收起后逻辑
callback(false);
}
};
if (off) {
window.removeEventListener('resize', resizeHandler);
} else {
window.addEventListener('resize', resizeHandler);
}
}
}
export function json2Query(json) {
function cleanArray(actual) {
const newArray = [];
for (let i = 0; i < actual.length; i++) {
if (actual[i]) {
newArray.push(actual[i]);
}
}
return newArray;
}
if (!json) return '';
return cleanArray(
Object.keys(json).map(key => {
if (json[key] === undefined) return '';
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);
})
).join('&');
}
export function throttle(fn, wait) {
let flag = true;
return function() {
if (flag) {
fn.apply(this, arguments);
flag = false;
setTimeout(() => {
flag = true;
}, wait);
}
};
}
export function changeTitleOfIOS() {
if (isIOS) {
const hackIframe = document.createElement('iframe');
hackIframe.style.display = 'none';
hackIframe.src = '/fixIosTitle.html?r=' + Math.random();
document.body.appendChild(hackIframe);
setTimeout(() => {
document.body.removeChild(hackIframe);
}, 300);
}
}
// 清除键盘
export function clearKeyboard() {
var input = document.getElementsByTagName('input');
for (let i = 0; i < input.length; i++) {
input[i].blur();
}
var textarea = document.getElementsByTagName('textarea');
for (let i = 0; i < textarea.length; i++) {
textarea[i].blur();
}
}
const ua = window.navigator.userAgent.toLowerCase();
// 判断微信环境
export const isWechat = ua.match(/MicroMessenger/i) == 'micromessenger';
// 判断小程序
export const isWxMp = ua.match(/miniProgram/i) == 'miniprogram';
// 判断羊小咩(信用钱包)环境
export const isApp = ua.match(/xyqb/i) == 'xyqb';
// 判断IOS环境
export const isIOS = /iphone|ipad|ipod/.test(ua);
// 判读Android环境
export const isAndroid = /android/.test(ua);
// 判断
export const appVersion = ua.match(/xyqb\/([\d|.]+)/) ? ua.match(/xyqb\/([\d|.]+)/)[1] : '7.7.00';
import Vue from 'vue';
import Vuex from 'vuex';
import payModule from './module';
Vue.use(Vuex);
let store = new Vuex.Store({
modules: {
pay: payModule
}
});
export default store;
import * as types from './type';
const state = {
header: true,
title: '支付中心',
loading: false,
meta: {}
};
// getters
const getters = {};
// actions
const actions = {
change_header({ commit }, header) {
commit(types.CHANGE_HEADER, header);
},
change_title({ commit }, title) {
commit(types.CHANGE_TITLE, title);
},
change_meta({ commit }, meta) {
commit(types.CHANGE_META, meta);
},
change_loading({ commit }, loading) {
commit(types.CHANGE_LOADING, loading);
}
};
// mutations
const mutations = {
[types.CHANGE_HEADER](state, header) {
if (header !== undefined) {
state.header = header;
}
},
[types.CHANGE_LOADING](state, loading) {
state.loading = loading;
},
[types.CHANGE_TITLE](state, title) {
if (title) {
state.title = title;
}
document.title = state.title;
},
[types.CHANGE_META](state, meta) {
for (let i in meta) {
state.meta[i] = meta[i];
}
}
};
export default {
state,
getters,
actions,
mutations
};
export const CHANGE_HEADER = 'CHANGE_HEADER';
export const CHANGE_TITLE = 'CHANGE_TITLE';
export const CHANGE_META = 'CHANGE_META';
export const CHANGE_LOADING = 'CHANGE_LOADING';
This diff is collapsed.
// @font-face {
// font-family: 'iconfont'; /* project id 2032151 */
// src: url('//at.alicdn.com/t/font_2032151_38j664if0q9.eot');
// src: url('//at.alicdn.com/t/font_2032151_38j664if0q9.eot?#iefix') format('embedded-opentype'),
// url('//at.alicdn.com/t/font_2032151_38j664if0q9.woff2') format('woff2'),
// url('//at.alicdn.com/t/font_2032151_38j664if0q9.woff') format('woff'),
// url('//at.alicdn.com/t/font_2032151_38j664if0q9.ttf') format('truetype'),
// url('//at.alicdn.com/t/font_2032151_38j664if0q9.svg#iconfont') format('svg');
// }
@font-face {
font-family: 'din';
src: url('./DIN.ttf') format('truetype');
}
\ No newline at end of file
@import 'reset.less';
@import 'var.less';
@import 'mixins.less';
@import 'iconfont.less';
@import 'icon.less';
html,
body,
#app {
height: 100%;
}
html {
font-size: 37.5px;
font-size: 10vw;
}
body {
font-family: @font-family;
background-color: @background-color;
}
button{
border:0;
}
.tel {
color: #5371E0;
font-size: 12px;
}
.page {
background-size: contain;
background-color: @background-color;
height: 100%;
padding: 0 @page-padding;
&__ios-fix-bottom {
&::after {
content: '';
display: block;
padding-bottom: @ios-bottom-height;
}
}
&__nopad {
padding: 0;
}
}
a {
color: @red;
.text-14;
}
textarea::-webkit-input-placeholder,
input::-webkit-input-placeholder {
color: @gray-3;
}
input,
textarea {
outline: none !important;
}
input {
-webkit-appearance: none;
}
strong {
font-weight: @font-weight-bold;
}
.cr-nav-bar {
&__title {
font-weight: 400 !important;
}
&__left {
color: @gray-4 !important;
}
}
.cr-cell-group::after, .cr-cell-group_content::after {
display: none !important;
}
.cr-picker {
&--toolbar {
&-title {
font-size: 16px;
color: #333;
}
&-cancel {
color: #999999 !important;
}
&-confirm {
color: @red !important;
}
&::after {
display: none;
}
}
}
.has-header {
.app {
height: calc(100% - @nav-bar-height);
min-height: calc(100% - @nav-bar-height);
}
}
.has-tab-bar {
.app {
height: calc(100% - @tab-bar-height);
min-height: calc(100% - @tab-bar-height);
}
}
.cr-dialog--confirm, .cr-dialog--confirm:active {
color: @red;
}
.cr-cell:last-child::after {
position: absolute;
box-sizing: border-box;
content: ' ';
pointer-events: none;
bottom: 0;
left: 0.426667rem;
right: 0;
border-bottom: 0.026667rem solid #f2f3f5;
transform: scaleY(0.5);
}
.ellipsis{
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap
}
.cr-dialog--header {
padding: @padding-xl;
&--isolated {
padding-bottom: 0;
}
}
\ No newline at end of file
.iphonex-fix-padding {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.iphonex-fix-margin {
margin-bottom: constant(safe-area-inset-bottom);
margin-bottom: env(safe-area-inset-bottom);
}
.countdown-mixin(@txtColor, @bgColor, @colonColor, @txtSize, @txtWeight) {
.cr-count-down {
font-size: 10px;
span {
box-sizing: border-box;
display: inline-block;
text-align: center;
height: @txtSize;
line-height: @txtSize+1;
font-weight: @txtWeight;
}
}
.block {
background-color: @bgColor;
color: @txtColor;
border-radius: 3.2px;
width: @txtSize;
}
.colon {
color: @colonColor;
width: 12px;
margin: 0 2px;
}
}
.through-line-mixin(@color) {
position: relative;
&:after {
content: ' ';
display: block;
position: absolute;
top: 0;
bottom: 0;
left: -5%;
margin: auto;
width: 110%;
height: 1px;
background-color: @color;
}
}
\ No newline at end of file
/**
* Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/)
* http://cssreset.com
*/
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video,
input {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font-weight: normal;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/* custom */
a {
color: #7e8c8d;
text-decoration: none;
}
li {
list-style: none;
}
body {
-webkit-text-size-adjust: 100% !important;
text-size-adjust: 100% !important;
-moz-text-size-adjust: 100% !important;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
// 覆盖cherry-ui样式
@import "./var.less";
@button-border-width: 1px;
@button-default-height: 37px;
@button-default-line-height: 37px;
@button-default-font-size: 16px;
@button-default-color: @gray-4;
@button-default-border-color: @gray-4;
@button-primary-color: @white;
@button-primary-background-color: @gradient-red;
@button-danger-background-color: @red;
@button-danger-border-color: @red;
@button-large-height: 48px;
@button-large-line-height: 48px;
@image-placeholder-text-color: @gray-4;
@field-error-message-color: @red-dark;
@field-error-message-font-size: 12px;
@field-error-placeholder-color: @red-dark;
@index-list-bar-font-size: 11px;
@checkbox-icon-border: @border-width-base solid @gray-2;
@checkbox-label-color: @text-color;
@checkbox-checked-color: @white;
@checkbox-checked-border-color: @red;
@checkbox-checked-background-color: @red;
@cell-group-title-background: @gray-1;
@cell-group-title-color: @orange;
@cell-group-title-padding: @padding-xs @padding-lg @padding-xs;
@cell-group-title-font-size: 12px;
@cell-border-color: @grey-border;
@cell-right-icon-color: @gray-3;
@cell-icon-size: 16px;
@field-label-width: 75px;
@dialog-border-radius: @border-radius-lg;
@dialog-has-title-message-text-color: @text-grey;
@navbar-default-height: @nav-bar-height;
@picker-toolbar-padding: @padding-unit - 2 @padding-md;
@picker-font-size: 14px;
@empty-description-color: @gray-4;
@tabs-nav-background-color: @background-color;
@list-text-color: @gray-4;
@index-list-title-color: @gray-5;
@back-top-txt-color: @gray-5;
\ No newline at end of file
// page Colors
@page-color-base: #f7f8fa;
// Color Palette
@black: #333333;
@white: #fff;
@gray-1: #f7f8fa;
@gray-2: #dcdcdc;
@gray-3: #c8c9cc;
@gray-4: #999999;
@gray-5: #666666;
@gray-6: #f8f8f8;
@gray-7: #d8d8d8;
@red: #ec1500;
@red-light: #ec3333;
@red-dark: #ee0a24;
@orange: #faab0c;
@red-btn: #ff5d00;
@green: #07C160;
@grey-border: #f2f3f5;
// Gradient Colors
@gradient-red: linear-gradient(269deg, #ff5d00 12%, #ff1900 86%);
@gradient-pink: linear-gradient(180deg, #fff7f0 0%, #ffe4dc 100%);
@primary-bg: {
background-image: linear-gradient(269deg, #ff4b00 12%, #ff7705 86%);
background-image: linear-gradient(269deg, #ff5d00 12%, #ff1900 86%);
};
@cherry-color-error: #dd524d;
@text-color-red: #e81800;
@text-color-light: #ff5a4b;
// Component Colors
@text-color: @black;
@text-grey: @gray-4;
@active-opacity: 0.8;
@disabled-opacity: 0.7;
@background-color: @gray-1;
@line-height-sm: 16px;
@line-height-md: 22px;
@line-height-lg: 24px;
// Border
@border-width-base: 1px;
@border-radius-sm: 6px;
@border-radius-md: 8px;
@border-radius-lg: 16px;
@border-radius-lx: 20px;
@border-radius-max: 999px;
// Padding
@padding-unit: 4px;
@padding-x: @padding-unit + 2;
@padding-xs: @padding-unit * 2;
@padding-sm: @padding-unit * 3;
@padding-md: @padding-unit * 4;
@padding-lg: @padding-unit * 5;
@padding-xl: @padding-unit * 8;
@padding-sd: @padding-xs + 4;
@font-color-disabled: #c0c4cc;
@font-color-dark: #333333;
@font-color-light: #909399;
@font-color-red: #f23e33;
// Font
@font-size-list: 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, 26, 28, 30, 52, 40;
.generate-text(15);
.generate-text(@n, @i: 1) when (@i =< @n) {
@font: extract(@font-size-list, @i);
.text-@{font} {
font-size: @font + 0px;
line-height: @font + 6px;
}
.generate-text(@n, @i + 1);
}
@font-weight-bold: 600;
@font-family: 'Helvetica Neue', Helvetica, Segoe UI, Arial,
Roboto, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif;
@font-din: 'din';
@deep: ~'>>>';
// 页面内边距
@page-padding: @padding-sm;
@page-padding-lg: @padding-lg;
// ios底部防遮挡高度
@ios-bottom-height: 60px;
// 页面顶部header高度
@nav-bar-height: 48px;
@tab-bar-height: 50px;
/* 页面左右间距 */
@page-row-spacing: 15px;
/* 行为相关颜色 */
@color-primary: #fa436a;
// 灰色
// 主要操作按钮背景色
@background-primary: linear-gradient(269deg, #ff5d00 12%, #ff1900 86%);
@background-coupon: linear-gradient(270deg, #f76b1c 0%, #ffc049 100%);
/*文字颜色*/
@font-color-dark: #333333;
@font-color-base: #606266;
@font-color-light: #909399;
@font-color-disabled: #c0c4cc;
@font-color-spec: #4399fc;
@font-color-search: #ec1500;
@font-color-red: #f23e33;
// @font-color-red: #EC3333;
@font-color-red: #f23e33;
// Font
@font-size-11: 11px;
@font-size-12: 12px;
@font-size-13: 14px;
@font-size-14: 14px;
@font-size-16: 16px;
@font-size-18: 18px;
@font-size-20: 20px;
@font-size-26: 26px;
@font-size-28: 28px;
@font-size-30: 30px;
@font-size-32: 32px;
@font-size-36: 36px;
// 边框
@border-sm: 2rpx;
@border-md: 4rpx;
@border-gray: #999;
@border-red: #ec1500;
/* 边框颜色 */
@border-color-dark: #dcdfe6;
@border-color-base: #e4e7ed;
@border-color-light: #ebeef5;
@border-color-search: #ec1500;
const creditPayStatusType = {
1: 'pwd',
2: 'sms'
};
const payTypeText = ['享花卡支付', '微信支付'];
const status = ['未申请', '审核中', '审核失败', '已有额度未激活', '开户成功'];
const payTypeE = [
'PD_YXMMAEC_UserClickCashierSelectXiangHuaCardPay',
'PD_YXMMAEC_UserClickCashierSelectWechatPay'
];
const codeArr = ['4034', '4035', '4036', '3005'];
// (4034, "密码错误"),
// (4035, "密码重试超限,无法验证"),
// (4036, "验证码错误");
// (3005, "组合支付方式被锁定")
// (6049, "token过期")
const payStatus = [3, 5]; // 展示支付文案
const creditStatus = [1, 2]; //展示开通文案
// (1, "VCC未申请"),
// (2, "VCC审核中"),
// (3, "VCC审核失败"),
// (4, "VCC审核成功未开户(包含开户失败)"),
// (5, "已开户"),;
const ACCOUNT_NO_APPLY = 1;
const ACCOUNT_APPLY_AUDITING = 2;
const ACCOUNT_APPLY_AUDIT_FAIL = 3;
const ACCOUNT_APPLY_FAIL = 4;
const ACCOUNT_APPLY_SUCCESS = 5;
// 1信用支付 2微信支付 3支付宝支付 4信用支付_微信支付 5信用支付_支付宝支付 6虚拟支付0元
const CREDIT_PAY = 1;
const WECHAT_PAY = 2;
const ALIPAY_PAY = 3;
const CREDIT_AND_WECHAT_PAY = 4;
const CREDIT_AND_ALIPAY_PAY = 5;
// 三方支付
const IS_THIRD_PAY = method => [WECHAT_PAY, ALIPAY_PAY].indexOf(method) > -1;
//组合支付
const IS_GROUP_PAY = method => [CREDIT_AND_WECHAT_PAY, CREDIT_AND_ALIPAY_PAY].indexOf(method) > -1;
//信用支付
const IS_CREDIT_PAY = method =>
[CREDIT_AND_WECHAT_PAY, CREDIT_AND_ALIPAY_PAY, CREDIT_PAY].indexOf(method) > -1;
const PAYMENT_CODE_PAY = 1; // 支付密码
const SMS_VERIFICATION_CODE_PAY = 2; //短信验证码
const FACE_VERIFICATION_CODE_PAY = 4; //人脸验证
const PAY_SUCCESS = 3; // 支付成功
const PAY_PAYING = 2; //支付中
function isDetentionFn() {
// 推荐开通享花卡
if (this.creditPayInfo.accountStatus !== ACCOUNT_APPLY_SUCCESS) {
this.$dialog({
message: this.creditPayInfo.creditPayOpenDesc,
confirmButtonText: '立即开通',
cancelButtonText: '继续支付',
confirmButtonColor: '#EC1500',
onCancel: () => {
this.pay();
},
onConfirm: () => {
this.goApply();
}
});
} else if (
// 推荐享花卡支付
this.creditPayInfo.accountStatus === ACCOUNT_APPLY_SUCCESS &&
this.creditPayList.isGroupPay
) {
this.$dialog({
message: this.creditPayInfo.creditPayOpenDesc,
confirmButtonText: '组合支付',
cancelButtonText: '继续支付',
confirmButtonColor: '#EC1500',
onCancel: () => {
this.pay();
},
onConfirm: () => {
// 推荐组合支付
for (let key in this.creditPayList.payList) {
if (this.creditPayList.payList[key].isRecommend) {
const type = this.creditPayList.payList[key].payType;
this.changePayType(type, this.creditPayList.payList[key].mergePayPretreatmentInfo);
this.isCheckAgreement = true;
this.pay();
return;
}
}
}
});
} else if (
this.creditPayInfo.accountStatus === ACCOUNT_APPLY_SUCCESS &&
!this.creditPayList.isGroupPay
) {
this.$dialog({
message: this.creditPayInfo.creditPayOpenDesc,
confirmButtonText: '享花卡支付',
cancelButtonText: '继续支付',
confirmButtonColor: '#EC1500',
onCancel: () => {
this.pay();
},
onConfirm: () => {
// 切换享花卡支付
this.changePayType(CREDIT_PAY);
this.isCheckAgreement = true;
this.pay();
}
});
} else if (
// 本笔订单不支持+额度已用完
this.creditPayInfo.accountStatus === ACCOUNT_APPLY_SUCCESS &&
(this.creditQuotaInfo.orderCanUseAmt === 0 || this.creditPayInfo.canAmt === 0)
) {
this.$dialog({
message: this.creditPayInfo.creditPayOpenDesc,
confirmButtonText: '其他支付方式',
showCancelButton: false,
onConfirm: () => {}
});
}
}
function havePayingOrder() {
/* 有享花卡未支付的订单 */
this.$dialog({
message:
this.creditPayInfo.riskLimitWindowDesc ||
'您的享花卡额度被其他订单占用,暂时不可使用享花卡支付哦!',
confirmButtonText: '继续支付',
closeOnClickOverlay: true,
showCancelButton: false,
confirmButtonColor: '#EC1500'
});
}
function filterAllPayList(type, data) {
for (let item in data.payList) {
if (data.payList[item].payType === type) {
data.payList[item].isCheck = true;
continue;
}
data.payList[item].isCheck = false;
}
return data;
}
const payTypeMap = {
'1': '信用支付',
'2': '微信支付',
'3': '支付宝支付',
'4': '信用支付_微信支付',
'5': '信用支付_支付宝支付',
'6': '虚拟支付0元付'
};
export {
status,
codeArr,
payTypeE,
payStatus,
PAY_PAYING,
CREDIT_PAY,
WECHAT_PAY,
ALIPAY_PAY,
PAY_SUCCESS,
payTypeText,
creditStatus,
IS_THIRD_PAY,
IS_GROUP_PAY,
IS_CREDIT_PAY,
isDetentionFn,
havePayingOrder,
filterAllPayList,
ACCOUNT_NO_APPLY,
PAYMENT_CODE_PAY,
ACCOUNT_APPLY_FAIL,
creditPayStatusType,
CREDIT_AND_WECHAT_PAY,
CREDIT_AND_ALIPAY_PAY,
ACCOUNT_APPLY_SUCCESS,
ACCOUNT_APPLY_AUDITING,
ACCOUNT_APPLY_AUDIT_FAIL,
SMS_VERIFICATION_CODE_PAY,
FACE_VERIFICATION_CODE_PAY,
payTypeMap
};
<template>
<div class="contract">
<cr-checkbox
v-model="isCheck"
shape="round"
class="contract-checkbox"
btn-size="mini"
@change="change"
/>
<p class="contract-list">
勾选同意
<span
v-for="contract in contractList"
:key="contract.contractName"
@click="goview(contract.contractVisitUrl)"
>
{{ contract.contractName }}</span
>
</p>
</div>
</template>
<script>
// TODO 协议需要对接需求http://confluence.quantgroup.cn/pages/viewpage.action?pageId=52461913
export default {
name: 'Contract',
props: {
contractList: Array,
value: Boolean
},
data() {
return {
isCheck: this.value
};
},
methods: {
goview(url) {
window.location.href = url;
},
change(e) {
this.$emit('input', e);
}
}
};
</script>
<style lang="less">
.contract {
width: 100%;
position: relative;
display: flex;
align-items: flex-start;
color: @gray-5;
.text-10;
&-list {
color: @gray-5;
span {
color: @red;
}
}
}
</style>
<template>
<div class="pay-type-list">
<p class="type-title">{{ value.title }}</p>
<payGroupItem
v-if="value.isGroupPay"
v-model="payList"
:pay-type="payType"
:disabled="disabled"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:coupon-disabled="showCoupon && !payCouponCouldBeUsed"
/>
<template v-else>
<template v-for="(item, key) of payList">
<payCardItem
v-if="item.show"
:key="key"
:pay-type="payType"
:value="item"
:disabled="disabled"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:coupon-disabled="showCoupon && !payCouponCouldBeUsed"
/>
</template>
</template>
</div>
</template>
<script>
import payCardItem from './PayCardItem';
import payGroupItem from './PayGroupCard.vue';
export default {
name: 'PayCard',
components: {
payCardItem,
payGroupItem
},
provide() {
return {
payCard: this
};
},
props: {
value: Object,
payType: Number,
payText: String,
disabled: Boolean,
couponInfo: Object,
riskLimit: Boolean,
showCoupon: Boolean,
payCouponCouldBeUsed: Boolean,
single: { type: Boolean, default: false },
creditPay: { type: Boolean, default: false }
},
data() {
return {
payList: this?.value?.payList || {}
};
},
mounted() {},
methods: {}
};
</script>
<style lang="less">
.pay-type-list {
background-color: #fff;
border-radius: @border-radius-sm;
margin-top: @padding-sm;
.type-title {
height: 48px;
.text-16;
font-weight: bold;
padding-left: @padding-md;
position: relative;
line-height: 48px;
}
}
</style>
<template>
<div class="pay-type-list">
<p class="type-title">{{ value.title }}</p>
<payGroupItem
v-if="value.isGroupPay"
v-model="payList"
:pay-type="payType"
:disabled="disabled"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:coupon-disabled="showCoupon && !payCouponCouldBeUsed"
/>
<template v-else>
<template v-for="(item, key) of payList">
<payCardItem
v-if="item.show"
:key="key"
:pay-type="payType"
:value="item"
:disabled="disabled"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:coupon-disabled="showCoupon && !payCouponCouldBeUsed"
/>
</template>
</template>
</div>
</template>
<script>
import payCardItem from './PayCardItem';
import payGroupItem from './PayGroupCard.vue';
export default {
name: 'PayCard',
components: {
payCardItem,
payGroupItem
},
provide() {
return {
payCard: this
};
},
props: {
value: Object,
payType: Number,
payText: String,
disabled: Boolean,
couponInfo: Object,
riskLimit: Boolean,
showCoupon: Boolean,
payCouponCouldBeUsed: Boolean,
single: { type: Boolean, default: false },
creditPay: { type: Boolean, default: false }
},
data() {
return {
payList: this?.value?.payList || {}
};
},
mounted() {},
methods: {}
};
</script>
<style lang="less">
.pay-type-list {
background-color: #fff;
border-radius: @border-radius-sm;
margin-top: @padding-sm;
.type-title {
height: 48px;
.text-16;
font-weight: bold;
padding-left: @padding-md;
position: relative;
line-height: 48px;
}
}
</style>
<template>
<div class="payCardItem">
<div
:class="['type-item', { 'b-t': !isGroupPay || value.payType === 1 }]"
@click="changePayType(value)"
>
<div class="type-item-content">
<div class="type-item-content-info">
<cr-image :src="value.icon" class="icon" mode="aspectFit" width="39" height="39" />
<div class="content-info-con">
<div class="content-info-tit">
<div class="content-info-tit-wrap">
<p class="content-info-tit">{{ value.name }}</p>
<p v-if="value.tagName" class="content-info-tit_tag">{{ value.tagName }}</p>
</div>
<p v-if="isGroupPay && value.payAmt" class="content-info-amount">
{{ value.payAmt }}
</p>
</div>
<p>{{ value.accountStatusDesc }}</p>
</div>
</div>
<div v-if="showCoupon && value.payType === 1" class="coupon">
<p class="couponDes" :class="{ disable: couponDisabled }" @click.stop="openCouponModal">
<span v-if="couponInfo.pickupId">
{{ `满${couponInfo.limitAmountNew}减${couponInfo.faceValueNew}元` }}
</span>
<span v-else>
未选择优惠券
</span>
<cr-icon type="arrow" class="selectArrow" size="15px" />
</p>
</div>
<p v-if="value.riskLimitDesc" class="limitDes">
{{ value.riskLimitDesc }}
</p>
</div>
<cr-checkbox
v-if="!isGroupPay"
v-model="value.isCheck"
shape="round"
class="type-item-checkbox"
checked-color="#EC1500"
:disabled="disabled || value.disabled"
@click.native="changePayType(value)"
/>
</div>
</div>
</template>
<script>
export default {
name: 'PayCardItem',
inject: ['payCard', 'pay'],
props: {
value: Object,
payType: Number,
disabled: Boolean,
couponInfo: Object,
riskLimit: Boolean,
showCoupon: Boolean,
isGroupPay: Boolean,
couponDisabled: Boolean
},
methods: {
changePayType({ payType, mergePayPretreatmentInfo }) {
if (this.disabled || this.value.disabled || this.isGroupPay) {
return;
}
this.$emit('click');
this.pay.changePayType(payType, mergePayPretreatmentInfo);
},
openCouponModal() {
if (this.couponDisabled) {
this.$toast('优惠券不可更改');
return;
}
this.pay.openCouponModal(this.pay.orderNo);
}
}
};
</script>
<style lang="less" scoped>
.payCardItem {
width: 100%;
padding-bottom: 2px;
}
.coupon {
margin-top: 4px;
height: 20px;
padding-left: 51px;
display: flex;
}
.couponDes {
display: inline-block;
color: @text-color-red;
.text-12;
border: @border-width-base solid @text-color-light;
border-radius: 3px;
padding: 0 0 0 3px;
&.disable {
color: @font-color-disabled;
border-color: @font-color-disabled;
}
text {
color: @text-color-red;
.text-12;
&.disable {
color: @font-color-disabled;
}
}
}
.limitDes {
color: #ed6a0c;
background: #fffbe8;
.text-11;
padding: 4px 8px;
margin: 8px 8px 8px 4px;
border-radius: 3px;
}
.type-item {
width: 100%;
padding: 12px 8px 12px 12px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
&-content {
width: 100%;
}
&-content-info {
width: 100%;
display: flex;
align-items: center;
}
&-checkbox {
width: 18px;
}
}
.icon {
width: 39px;
height: 39px;
margin-right: @padding-sm;
}
.content-info-tit {
font-size: 15px;
color: @font-color-dark;
margin-bottom: 2px;
display: flex;
align-items: center;
justify-content: space-between;
&-wrap {
display: flex;
}
&_tag {
display: inline-flex;
align-items: center;
height: 16px;
border: @border-width-base solid @text-color-light;
border-radius: 3px;
font-size: 11px;
color: @text-color-light;
margin-left: @padding-unit;
padding: 0 2px;
}
}
.content-info-con {
flex: 1;
display: flex;
flex-direction: column;
.text-12;
color: @font-color-light;
}
.content-info-amount {
width: 100px;
text-align: left;
word-break: break-all;
&::before {
content: '¥';
}
.text-15;
color: @black;
}
.selectArrow {
vertical-align: text-top;
margin-left: -2px;
}
</style>
<template>
<div>
<div @click="changePayType(thirdPayInfo)">
<div class="groupCard">
<PayCardItem
:is-group-pay="true"
:value="creditPayInfo"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:risk-limit="riskLimit"
:coupon-disabled="couponDisabled"
/>
</div>
<div class="dashed">
<p class="dashed-line" />
<cr-image
src="@/assets/images/pay/addicon.png"
width="24px"
height="24px"
class="dashed-icon"
/>
<cr-checkbox
v-model="thirdPayInfo.isCheck"
shape="round"
checked-color="#EC1500"
:disabled="disabled || thirdPayInfo.disabled"
class="dashed-checkbox"
@click.native="changePayType(thirdPayInfo)"
/>
</div>
<div class="groupCard">
<PayCardItem :is-group-pay="true" :value="thirdPayInfo" />
</div>
</div>
<p class="group-more b-t" @click="openMore">更多支付组合<cr-icon type="arrow" size="15px" /></p>
<cr-popup v-model="morePopup" round position="bottom" :style="{ height: '30%' }" closeable>
<p class="more-title">更换支付组合</p>
<PayCardItem
v-for="item in thirdPayList"
:key="item.payType"
:value="item"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:risk-limit="riskLimit"
@click="morePopup = false"
/>
</cr-popup>
</div>
</template>
<script>
import PayCardItem from './PayCardItem.vue';
export default {
name: 'PayGroupCard',
inject: ['payCard', 'pay'],
components: {
PayCardItem
},
props: {
value: Object,
payType: Number,
disabled: Boolean,
couponInfo: Object,
showCoupon: Boolean,
riskLimit: Boolean,
couponDisabled: Boolean
},
data() {
return {
morePopup: false,
thirdPayList: [],
creditPayInfo: this.value.creditPayInfo || {}
};
},
computed: {
thirdPayInfo: function() {
let temp = {};
for (const key in this.value) {
if (this.value[key].isRecommend || this.value[key].isCheck) {
const mergePayPretreatmentInfo = this.value[key]?.mergePayPretreatmentInfo || {};
temp = {
...this.value[key],
payAmt: mergePayPretreatmentInfo?.otherPayAmt
};
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
this.creditPayInfo.payAmt = mergePayPretreatmentInfo.creditPayAmt;
}
}
return temp;
}
},
mounted() {
for (const key in this.value) {
if (this.value[key].isGroupPay) {
this.thirdPayList.push(this.value[key]);
}
}
},
methods: {
changePayType({ payType, mergePayPretreatmentInfo }) {
if (this.disabled || this.creditPayInfo.disabled) {
return;
}
this.pay.changePayType(payType, mergePayPretreatmentInfo);
},
openMore() {
this.morePopup = true;
}
}
};
</script>
<style lang="less">
.groupCard {
width: 100%;
}
.group-more {
.text-13;
width: 335px;
height: 36px;
margin: auto;
display: flex;
color: #ec1500;
position: relative;
align-items: center;
justify-content: center;
}
.cr-popup--close-top-right {
z-index: 2;
}
.dashed {
width: 100%;
padding: 0 12px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
&-line {
width: 310px;
border-bottom: 1px dashed #dcdcdc;
}
&-checkbox {
width: 18px;
}
&-icon {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
}
.more-title {
.text-16;
display: flex;
justify-content: center;
align-items: center;
height: 48px;
color: @black;
position: relative;
}
</style>
<template>
<cr-overlay :show="value">
<div class="sms-modal">
<div class="sms">
<p class="sms-icon"><cr-icon type="cross" color="#999999" @click="closeModal" /></p>
<p class="sms-title">请输入短信验证码</p>
<p class="sms-des">为保证您账户安全,此笔交易需要短信验证</p>
<p class="phone">已发送至 {{ phoneNumber }}</p>
<cr-authcode-field
span-size="24px"
type="number"
border-type="bottom"
:number="6"
height="40px"
span-color="#666"
input-color="#666"
input-size="20px"
class="sms-input"
:success="success"
/>
</div>
<div slot="footer" class="sms-action">
<p v-if="errorInfo" class="sms-error">{{ errorInfo }}</p>
<p v-if="!send" class="sms-set" @click="sendSms()">获取验证码</p>
<p v-else class="sms-set grey">{{ time }}s后重新获取验证码</p>
</div>
</div>
</cr-overlay>
</template>
<script>
import { sendSms as sendSmsApi, getPhoneNumber } from '@/api/pay.api';
export default {
props: {
value: {
type: Boolean,
default: true
},
orderNo: String,
flowOrderNo: String,
errorInfo: String
},
data() {
return {
send: false,
time: 60,
timer: null,
phoneNumber: ''
};
},
computed: {},
watch: {
value: function(val) {
if (val) {
this.sendSms(1);
}
},
errorInfo: function() {}
},
created() {
this.getPhone();
},
methods: {
closeModal() {
this.clearTimer();
this.$emit('close');
},
success(smsCode) {
this.$emit('submit', smsCode);
},
async sendSms() {
// 页面摧毁清掉定时器
if (this.send) return;
const [, error] = await sendSmsApi({ orderNo: this.orderNo, flowOrderNo: this.flowOrderNo });
if (!error) {
this.send = true;
this.timer = setInterval(() => {
--this.time;
if (!this.time) {
this.clearTimer();
}
}, 1000);
}
},
async getPhone() {
const [phoneNumber] = await getPhoneNumber();
this.phoneNumber = phoneNumber;
},
clearTimer() {
clearInterval(this.timer);
this.send = false;
this.time = 60;
}
}
};
</script>
<style lang="less">
.sms-modal {
box-sizing: border-box;
position: absolute;
left: 50%;
top: 26%;
margin-left: -150px;
padding: 12px 18px 24px;
width: 300px;
border-radius: 16px;
background: #fff;
}
.sms {
&-icon {
width: 100%;
height: 20px;
line-height: 20px;
text-align: right;
}
&-title {
text-align: center;
width: 100%;
font-size: 16px;
font-weight: bold;
}
&-des {
padding: 18px 0 0 8px;
display: flex;
justify-content: flex-start;
font-size: 12px;
color: #999999;
}
&-input {
margin: auto;
}
.phone {
padding-left: 8px;
margin-top: 8px;
color: #323233;
font-size: 14px;
}
&-sms {
margin-top: 4px;
font-size: 30px;
color: #333333;
&:before {
content: '¥';
.text-12;
}
}
&-input {
margin: 20px 0;
}
}
.sms-action {
width: 100%;
display: flex;
justify-content: space-between;
p {
font-size: 14px;
color: #666666;
}
.sms-error {
flex: 1;
color: #ee0a24;
}
.sms-set {
flex: 1;
text-align: right;
}
.grey {
color: #999999;
}
}
</style>
import localStorage from '@/service/localStorage.service';
export const goUrlExtends = {
methods: {
goHome() {
// 商城地址
setTimeout(() => {
localStorage.remove('redirectUrl');
window.location.href = localStorage.get('redirectUrl');
}, 500);
}
}
};
@import url('../../style/var.less');
@import url('../../style/mixins');
.pay {
padding: @padding-xs;
}
.price-box {
background-color: @white;
height: 120px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.text-13;
color: @black;
border-radius: @border-radius-sm;
.price{
.text-30;
color: @font-color-red;
font-weight: bold;
margin-top: @padding-xs;
&:before{
content: '¥';
.text-14;
font-weight: bold;
}
}
.time{
display: flex;
align-items: center;
margin-top: @padding-unit;
}
}
.payBtn{
width: 100%;
position: fixed;
padding-left: @padding-lg;
padding-right: @padding-lg;
padding-top: 10px;
box-sizing: border-box;
z-index: 99;
bottom: 0;
left:0;
background-color:@white;
padding-bottom: calc(10px + constant(safe-area-inset-bottom));
padding-bottom: calc(10px + env(safe-area-inset-bottom));
button{
.text-16;
width: 100%;
color: @white;
font-size: @line-height-sm;
border-radius: @border-radius-lx;
span{
font-weight: bold;
}
}
button[disabled] {
color: rgba(255,255,255,.6);
}
&-primary {
@primary-bg();
}
}
.btn{
width: 100%;
position: fixed;
padding-left: @padding-lg;
padding-right: @padding-lg;
padding-top: 10px;
padding-bottom: calc(10px + constant(safe-area-inset-bottom));
padding-bottom: calc(10px + env(safe-area-inset-bottom));
box-sizing: border-box;
bottom: 0;
left:0;
background-color:@white;
display: flex;
align-items: center;
justify-content: center;
button{
flex:1;
color: @white;
font-size: @line-height-sm;
border-radius: @border-radius-lx;
}
button[disabled] {
color: rgba(255,255,255,.6);
}
&-primary {
@primary-bg();
}
&-default{
border: 1px solid @cherry-color-error;
color: @cherry-color-error !important;
margin-right: @padding-xs;
background-color: @white !important;
}
}
.placeholder{
width: 100%;
height: 100px;
}
@{deep} .pwd-modal {
.cr-pwd-field {
height: auto;
}
.Cp-field-dialog {
position: relative;
left: 0;
margin-left: 0;
width: 100%;
border-radius: 16px 16px 0 0;
.Cpf-dialog-info {
display: none;
}
.Cpf-dialog-list {
padding-top: 20px;
}
}
.Cp-field-keys {
position: relative;
}
}
\ No newline at end of file
This diff is collapsed.
<template>
<div>
<div v-if="isSuccess" class="card">
<div class="info">
<cr-image
width="100px"
height="100px"
class="info__image"
src="https://img.lkbang.net/xcx/pay-success.png"
/>
<div class="info__desc">
<p class="info__text">订单支付成功!</p>
<p v-if="money" class="info__text info__money">实付¥{{ money }}</p>
<p v-if="freeAmount > 0" class="info__text info__free">已优惠¥{{ freeAmount }}</p>
</div>
</div>
<div v-if="hasRedirectUrl" :class="['actions', 'no-order']">
<cr-button shape="circle" type="primary" @click="goHome">返回</cr-button>
</div>
</div>
<div v-else class="card">
<div class="info">
<cr-image
width="100px"
height="100px"
class="info__image"
src="https://img.lkbang.net/xcx/pay-fail.png"
/>
<p class="info__text">订单支付失败!</p>
</div>
<p class="tips">
{{ reason }}
</p>
<cr-button
v-if="hasRedirectUrl"
type="primary"
shape="circle"
class="info_button"
@click="goPay"
>
重新支付
</cr-button>
</div>
</div>
</template>
<script>
import cookies from '@/service/cookieStorage.service';
import localStorage from '@/service/localStorage.service';
import { isApp } from '@/service/validation.service';
import { goUrlExtends } from './extends';
import { payTypeMap } from './STATIC_DATA';
export default {
extends: goUrlExtends,
data() {
return {
money: '00.00',
orderNo: null,
isSuccess: false,
freeAmount: 0,
payStatus: '',
hasRedirectUrl: !!localStorage.get('redirectUrl'),
returnUrl: ''
};
},
created() {
this.getQuery();
const { orderNo, reason, payType } = this.$route.query;
const { success } = this.$route.meta;
const amount = cookies.get('amount') || {};
this.money = amount.finalAmt;
this.orderNo = orderNo;
this.reason = reason || '';
this.isSuccess = success || false;
this.freeAmount = amount.freeAmount;
this.payStatus = success ? '订单支付成功' : '订单支付失败';
this.$track.registeredEvents('h5_RechargeResultPageExposure', {
pay_status: this.payStatus
});
this.isSuccess &&
isApp &&
this.util.run({
event: 'reyunAnalytics',
data: {
method: 'PaySuccess', //string submitOrder OR PaySuccess
orderId: orderNo, //string 订单号
amount: amount.finalAmt, //float 金额
payType: payTypeMap[payType] //string 支付类型
}
});
},
methods: {
getQuery() {
this.returnUrl = cookies.get('returnUrl') || '';
},
goPay() {
this.$track.registeredEvents('h5_RechargeResultPageClick', {
pay_status: this.payStatus,
buttons_name: '重新支付'
});
this.$router.replace({ name: 'pay', query: { orderNo: this.orderNo } });
}
}
};
</script>
<style lang="less" scoped>
.card {
margin: @padding-sm;
background-color: @white;
border-radius: @border-radius-sm;
padding: 28px @padding-sm @padding-sm @padding-sm;
.info {
display: flex;
justify-content: center;
align-items: center;
&__image {
width: 100px;
height: 100px;
}
&__text {
.text-16;
margin-left: @padding-md;
}
&__desc {
display: flex;
flex-direction: column;
}
&__money {
margin-top: @padding-xs;
}
&__free {
.text-13;
margin-top: @padding-xs;
color: @font-color-light;
}
}
.tips {
.text-12;
text-align: center;
margin-top: @padding-lg;
color: @font-color-light;
}
.actions {
display: flex;
flex-direction: row;
margin-top: @padding-lg;
justify-content: space-between;
&.no-order {
justify-content: center;
}
&__back {
color: @cherry-color-error;
border: 1px solid @cherry-color-error;
}
&__order {
@primary-bg();
margin-left: @padding-sm;
}
button {
// flex: 1;
.text-17;
width: 161px;
}
}
.info_button {
width: 100%;
}
}
</style>
<template>
<div class="card">
<div class="info">
<cr-image
width="63px"
height="63px"
class="info__image"
src="@/assets/images/pay/paying.png"
/>
<div v-if="init" class="info__desc">
<p class="info__text">
<span v-if="time">{{ time }}s)</span>支付中...
</p>
</div>
</div>
<p class="tips">
努力返回支付结果中,请留心查看!
</p>
<div v-if="hasRedirectUrl" :class="['actions', 'no-order']">
<cr-button shape="circle" type="primary" @click="goHome">返回</cr-button>
</div>
</div>
</template>
<script>
import { queryPayStatus } from '@/api/pay.api';
import cookies from '@/service/cookieStorage.service';
import { goUrlExtends } from './extends';
export default {
components: {},
extends: goUrlExtends,
data() {
return {
orderNo: null,
timer: null,
time: 10,
init: false,
hasRedirectUrl: !!localStorage.get('redirectUrl'),
returnUrl: ''
};
},
created() {
this.orderNo = this.$route.query.orderNo || cookies.get('orderNo')?.orderNo;
},
mounted() {
this.getQuery();
this.$track.registeredEvents('h5_RechargeResultPageExposure', {
pay_status: '正在支付'
});
this.$dialog({
message: '请确认订单已完成支付',
confirmButtonText: '已完成支付',
cancelButtonText: '重新支付',
confirmButtonColor: '#EC1500',
onCancel: () => {
this.$track.registeredEvents('h5_RechargeResultPageClick', {
pay_status: '正在支付',
buttons_name: '重新支付'
});
this.goPay();
},
onConfirm: () => {
this.init = true;
this.loop();
}
});
},
beforeDestroy() {
clearInterval(this.timer);
},
methods: {
getQuery() {
this.returnUrl = cookies.get('returnUrl') || '';
},
loop() {
this.query();
this.timer = setInterval(() => {
this.time -= 1;
if (this.time % 2 === 0) {
this.query();
}
if (this.time < 1) {
clearInterval(this.timer);
}
}, 2000);
},
goSuccess() {
// 支付成功
this.$router.push({ name: 'paySuccess' });
},
goPay() {
this.$router.replace({ name: 'pay', query: { orderNo: this.orderNo } });
},
async query() {
const [data, error] = await queryPayStatus({ orderNo: this.orderNo });
if (error) {
this.$router.push({
name: 'payFail',
query: { reason: error.message, orderNo: this.orderNo }
});
return;
}
if (+data?.payStatus === 3) {
this.goSuccess();
}
}
}
};
</script>
<style lang="less" scoped>
.card {
margin: @padding-sm;
background-color: @white;
border-radius: @border-radius-sm;
padding: 28px @padding-sm @padding-sm @padding-sm;
.info {
display: flex;
justify-content: center;
align-items: center;
&__image {
width: 100px;
height: 100px;
}
&__text {
.text-16;
margin-left: @padding-md;
}
&__desc {
display: flex;
flex-direction: column;
}
&__money {
margin-top: @padding-xs;
}
&__free {
.text-13;
margin-top: @padding-xs;
color: @font-color-light;
}
}
.tips {
.text-12;
text-align: center;
margin-top: @padding-lg;
color: @font-color-light;
}
.actions {
display: flex;
flex-direction: row;
margin-top: @padding-lg;
justify-content: space-between;
&.no-order {
justify-content: center;
}
&__back {
color: @cherry-color-error;
border: 1px solid @cherry-color-error;
}
&__order {
@primary-bg();
margin-left: @padding-sm;
}
button {
.text-17;
width: 161px;
}
}
.info_button {
width: 100%;
}
}
</style>
const path = require('path');
const resolve = dir => path.join(__dirname, dir);
const IS_PROD = process.env.NODE_ENV === 'production';
const WebpackCosCdnUploadPlugin = require('@qg/webpack-cos-cdn-upload-plugin');
const SentryPlugin = require('@qg/sentry-webpack-plugin');
const SentryConfig = require('./.sentryclirc');
const webpack = require('webpack');
// cdn预加载使用
const externals = {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
'sa-sdk-javascript': 'sensorsDataAnalytic201505' // 神策js在window下的全局引用会出现两个,这个才是真物
// axios: "axios"
};
const cdn = {
// 生产环境
build: {
js: [
'https://activitystatic.lkbang.net/vue/2.6.11/vue.min.js',
'https://activitystatic.lkbang.net/vue-router/3.2.0/vue-router.min.js',
'https://activitystatic.lkbang.net/vuex/3.4.0/vuex.min.js',
'https://activitystatic.lkbang.net/sa-sdk-javascript/1.15.16/sensorsdata.min.js'
]
}
};
module.exports = {
transpileDependencies: [/[/\\]node_modules[/\\]@qg[/\\]cherry-ui[/\\]/],
chainWebpack: config => {
config.plugin('html').tap(args => {
args[0].title = '';
if (IS_PROD) {
args[0].cdn = cdn.build;
}
return args;
});
// 修复HMR
config.resolve.symlinks(true);
// 移除 prefetch 插件(针对生产环境首屏请求数进行优化)
config.plugins.delete('prefetch');
// 移除 preload 插件(针对生产环境首屏请求数进行优化) preload 插件的用途:https://cli.vuejs.org/zh/guide/html-and-static-assets.html#preload
config.plugins.delete('preload');
// 添加别名
config.resolve.alias.set('@', resolve('src'));
// svg-sprite
config.module
.rule('svg')
.exclude.add(resolve('src/assets/svg'))
.end();
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/assets/svg'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end();
// 组件静态资源引入
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => {
return {
...options,
transformAssetUrls: {
'cr-image': 'src',
'cr-button': 'icon',
'cr-icon': 'type',
'cr-field': ['right-icon', 'left-icon'],
'cr-cell': ['right-icon', 'icon']
}
};
});
// 分包优化
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
initial: {
name: 'chunk-init', // 分离需要在入口加载的依赖
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // only package third parties that are initially dependent
},
sentry: {
name: 'chunk-sentry', // sentry
priority: 20, // the weigh1t needs to be larger than libs and app or it will be packaged into libs or app
test: /[\\/]node_modules[\\/]@sentry[\\/]/ // in order to adapt to cnpm
},
libs: {
name: 'chunk-libs', // 分离非在入口加载的,但是引用超过2次的依赖
test: /[\\/]node_modules[\\/]/,
priority: 8,
minChunks: 2,
chunks: 'all' // only package third parties that are initially dependent
},
svg: {
name: 'chunk-svg',
priority: 9,
chunks: 'initial',
test: resolve('src/assets/svg'),
reuseExistingChunk: true,
enforce: true
},
cherryUI: {
name: 'chunk-cherryUI', // 分离组件库
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
test: /[\\/]node_modules[\\/]@qg[\\/]_?cherry-ui(.*)/ // in order to adapt to cnpm
},
commons: {
name: 'chunk-commons', // 将src中components|service|mixins|api|config三个文件夹下的引用次数超过2次的js合并
test: /[\\/]src[\\/](components|service|mixins|api|config)[\\/]/, // can customize your rules
minChunks: 2, // minimum common number
priority: 5,
reuseExistingChunk: true
}
}
});
// https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk
config.optimization.runtimeChunk('single');
return config;
},
configureWebpack: config => {
let configObj = null;
if (IS_PROD) {
config.externals = externals;
configObj = {
plugins: [
new WebpackCosCdnUploadPlugin(),
new SentryPlugin(SentryConfig),
new webpack.DefinePlugin({
'process.env.SENTRY_ENV': `"${process.env.SENTRY_ENV || ''}"`
})
]
};
}
return configObj;
},
lintOnSave: true,
runtimeCompiler: false, // 是否使用包含运行时编译器的 Vue 构建版本
productionSourceMap: IS_PROD, // 生产环境的 source map
devServer: {
proxy: 'http://kdsp-vcc.liangkebang.net'
},
css: {
loaderOptions: {
less: {
// 若使用 less-loader@5,请移除 lessOptions 这一级,直接配置选项。
modifyVars: {
// 直接覆盖变量
hack: `true; @import "${resolve('./src/style/theme.less')}";`
}
}
}
}
};
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment