Browse Source

支付宝生活号收银台系统

master
commit
cc00353469
  1. 13
      .env.development
  2. 11
      .env.production
  3. 3
      .env.test
  4. 1
      .gitignore
  5. 29
      README.md
  6. 5
      babel.config.js
  7. 12507
      package-lock.json
  8. 26
      package.json
  9. 5
      postcss.config.js
  10. BIN
      public/favicon.ico
  11. 33
      public/index.html
  12. 8
      src/App.vue
  13. BIN
      src/assets/logo.png
  14. BIN
      src/assets/payment.png
  15. BIN
      src/assets/success.png
  16. 57
      src/components/HelloWorld.vue
  17. 42
      src/config/axios.js
  18. 15
      src/config/plugins.js
  19. 13
      src/config/setting.js
  20. 9
      src/main.js
  21. 53
      src/router/index.js
  22. 517
      src/utils/util.js
  23. 73
      src/views/auth/index.vue
  24. 323
      src/views/cashier/index.vue
  25. 102
      src/views/cashier/result.vue
  26. 34
      src/views/index/index.vue
  27. 47
      vue.config.js

13
.env.development

@ -0,0 +1,13 @@
NODE_ENV=development
#本地开发代理路径
VUE_APP_BASE_URL="http://192.168.1.39:8090/"
#项目的前缀名
VUE_APP_CONTEXT_PATH="/alipayapi"
#学员端项目的前缀名
VUE_APP_CONTEXT_PATH1=""
#驾校端项目的前缀名
VUE_APP_CONTEXT_PATH2=""
#路由前缀名
VUE_APP_PATH="/alipay"

11
.env.production

@ -0,0 +1,11 @@
NODE_ENV=production
#本地开发代理路径
VUE_APP_BASE_URL=""
#项目的前缀名
VUE_APP_CONTEXT_PATH="/"
#学员端项目的前缀名
VUE_APP_CONTEXT_PATH1="api"
#驾校端项目的前缀名
VUE_APP_CONTEXT_PATH2="schoolapi"
#路由前缀名
VUE_APP_PATH="/alipay"

3
.env.test

@ -0,0 +1,3 @@
NODE_ENV=test
VUE_APP_BASE_URL="http://192.168.1.9:4031/managementapi/"
VUE_APP_CONTEXT_PATH="/admin"

1
.gitignore

@ -0,0 +1 @@
node_modules/

29
README.md

@ -0,0 +1,29 @@
# default
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Run your tests
```
npm run test
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

5
babel.config.js

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/app'
]
}

12507
package-lock.json
File diff suppressed because it is too large
View File

26
package.json

@ -0,0 +1,26 @@
{
"name": "jiapei-alipay",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"@fingerprintjs/fingerprintjs": "^3.3.3",
"axios": "^0.19.2",
"core-js": "^3.6.5",
"vant": "^2.12",
"vue": "^2.6.11",
"vue-axios": "^2.1.5",
"vue-cookies": "^1.8.1",
"vue-router": "^3.2.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.4.0",
"@vue/cli-service": "^4.4.0",
"vue-template-compiler": "^2.6.11",
"webpack-dev-server": "^4.4.0",
"compression-webpack-plugin": "^6.1.1"
}
}

5
postcss.config.js

@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}

BIN
public/favicon.ico

33
public/index.html

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>浙里学车生活号</title>
<style>
.van-dialog-bule{border-radius:0px!important;}
.van-dialog-bule .van-dialog__header{background-color:#1890ff;font-family: 'Arial Negreta', 'Arial Normal', 'Arial';font-weight: 700;font-style: normal;color: #FFFFFF;text-align: left;padding-left:20px;height:30px;margin-top:-15px;}
.van-dialog-bule .van-dialog__footer{height:80px;text-align: center;width: 300px;margin-left:20%;}
.van-dialog-bule .van-dialog__footer .van-dialog__cancel{background-color:#d7d7d7;width:10px;border-radius:5px;width:40%;}
.van-dialog-bule .van-dialog__footer .van-dialog__confirm{background-color: #1890ff;color:#FFFFFF;border-radius:5px;width:40%;margin-left:30px;}
.van-dialog-bule1 .van-dialog__footer .van-dialog__confirm{margin-left:0px;}
.van-dialog-purple{border-radius:0px!important;}
.van-dialog-purple .van-dialog__header{background-color:#8080ff;font-family: 'Arial Negreta', 'Arial Normal', 'Arial';font-weight: 700;font-style: normal;color: #FFFFFF;text-align: left;padding-left:20px;height:30px;margin-top:-15px;}
.van-dialog-purple .van-dialog__footer{height:80px;text-align: center;width: 300px;margin-left:20%;}
.van-dialog-purple .van-dialog__footer .van-dialog__cancel{background-color:#d7d7d7;width:10px;border-radius:5px;width:40%;}
.van-dialog-purple .van-dialog__footer .van-dialog__confirm{background-color: #1890ff;color:#FFFFFF;border-radius:5px;width:40%;margin-left:30px;}
</style>
</head>
<body>
<noscript>
<strong>We're sorry but default doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

8
src/App.vue

@ -0,0 +1,8 @@
<template>
<div id="app">
<router-view/>
</div>
</template>
<style>
</style>

BIN
src/assets/logo.png

After

Width: 200  |  Height: 200  |  Size: 6.7 KiB

BIN
src/assets/payment.png

After

Width: 308  |  Height: 308  |  Size: 45 KiB

BIN
src/assets/success.png

After

Width: 494  |  Height: 494  |  Size: 72 KiB

57
src/components/HelloWorld.vue

@ -0,0 +1,57 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

42
src/config/axios.js

@ -0,0 +1,42 @@
/**
* axios配置
*/
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import setting from './setting'
import cookies from 'vue-cookies';
import { Dialog } from 'vant';
import router from '@/router'
Vue.use(VueAxios, axios);
//axios.defaults.baseURL = setting.baseURL;
axios.defaults.baseURL =setting.context_path;
axios.defaults.headers.common[setting.tokenHeaderName] ='';
axios.interceptors.request.use((config ) => {
var url=config.url;
if (url.indexOf("?")!=-1) {
let queryString = url.split('?')[1] // code=12313123&a=444
if (queryString) {
let query = queryString.split('&')
for (let i = 0; i < query.length; i++) {
let arr = query[i].split('=')
if(arr[0]=="token"){
config.headers[setting.tokenHeaderName] =arr[1];
break;
}
}
}
}
return config;
}, (error) => {
return Promise.reject(error);
});
/* 响应拦截器 */
axios.interceptors.response.use((res) => {
return res;
}, (error) => {
return Promise.reject(error);
});

15
src/config/plugins.js

@ -0,0 +1,15 @@
/**
* 引用框架
*/
import Vue from 'vue'
import './axios'
import setting from './setting'
import util from '@/utils/util'
// UI框架
import Vant from 'vant'; // vant
import 'vant/lib/index.css'; // 样式
import cookies from 'vue-cookies';
Vue.prototype.$setting = setting;
Vue.prototype.$util = util;
Vue.prototype.$cookies = cookies
Vue.use(Vant);

13
src/config/setting.js

@ -0,0 +1,13 @@
/**
* 项目统一配置
*/
export default {
version: '1.0',
name: '浙里学车生活号', // 项目名称
baseURL: process.env.VUE_APP_BASE_URL, // 接口地址
context_path: process.env.VUE_APP_CONTEXT_PATH, //项目的前缀名
context_path1: process.env.VUE_APP_CONTEXT_PATH1, //学员端项目的前缀名
context_path2: process.env.VUE_APP_CONTEXT_PATH2, //学员端项目的前缀名
vue_app_path: process.env.VUE_APP_PATH, //路由前缀名
tokenHeaderName: 'token', // token传递的header名称
}

9
src/main.js

@ -0,0 +1,9 @@
import Vue from 'vue'
import App from './App.vue'
import './config/plugins'
import router from './router'
Vue.config.productionTip = false
new Vue({router,
render: h => h(App),
}).$mount('#app')

53
src/router/index.js

@ -0,0 +1,53 @@
/**
* 路由配置
*/
import Vue from 'vue'
import VueRouter from 'vue-router'
import setting from '@/config/setting'
Vue.use(VueRouter);
/* 静态路由配置 */
const routes = [
{
path: '/',
component: (resolve) => require(['@/views/index/index'], resolve),//懒加载
meta: {hide: true, title: '首页'}
},
{
path: '/auth',
component: (resolve) => require(['@/views/auth/index'], resolve),//懒加载
meta: {hide: true, title: '授权页'}
},
{
path: '/cashier',
component: (resolve) => require(['@/views/cashier/index'], resolve),//懒加载
meta: {hide: true, title: '收银台'}
},
{
path: '/result',
component: (resolve) => require(['@/views/cashier/result'], resolve),//懒加载
meta: {hide: true, title: '支付结果'}
},
];
const router = new VueRouter({
base: process.env.VUE_APP_PATH,
routes,
mode: 'history' // history
});
/* 路由守卫 */
router.beforeEach((to, from, next) => {
// NProgress.start();
document.title = `${to.meta.title || ''}${to.meta.title ? ' - ' : ''}${setting.name}`;
next();
});
router.afterEach(() => {
setTimeout(() => {
// NProgress.done();
}, 150);
});
export default router

517
src/utils/util.js

@ -0,0 +1,517 @@
/**
* 常用工具方法
*/
export default {
/**
* 倒计时
* @param endTime 结束时间
* @param serverTime 服务端当前时间
* @param callback 回调
* @returns {number} 定时器实例
*/
countdown(endTime, serverTime, callback) {
let type = typeof serverTime === 'function',
end = new Date(endTime).getTime(),
now = new Date((!serverTime || type) ? new Date().getTime() : serverTime).getTime(),
count = end - now,
time = [
Math.floor(count / (1000 * 60 * 60 * 24)), // 天
Math.floor(count / (1000 * 60 * 60)) % 24, // 时
Math.floor(count / (1000 * 60)) % 60, // 分
Math.floor(count / 1000) % 60 // 秒
];
if (type) callback = serverTime;
let timer = setTimeout(() => {
this.countdown(endTime, now + 1000, callback);
}, 1000);
callback && callback(count > 0 ? time : [0, 0, 0, 0], serverTime, timer);
if (count <= 0) clearTimeout(timer);
return timer;
},
/**
* 某个时间在当前时间的多久前
* @param time 需要语义化的时间
* @param onlyDate 超过30天是否仅返回日期
* @returns {string} 语义化后的时间
*/
timeAgo(time, onlyDate) {
if (!time) return '';
if (typeof time === 'string') time = time.replace(/-/g, '/');
let arr = [
[],
[]
],
stamp = new Date().getTime() - new Date(time).getTime();
// 30天以上返回具体日期
if (stamp > 1000 * 60 * 60 * 24 * 31) {
stamp = new Date(time);
arr[0][0] = this.digit(stamp.getFullYear(), 4);
arr[0][1] = this.digit(stamp.getMonth() + 1);
arr[0][2] = this.digit(stamp.getDate());
if (!onlyDate) { // 是否输出时间
arr[1][0] = this.digit(stamp.getHours());
arr[1][1] = this.digit(stamp.getMinutes());
arr[1][2] = this.digit(stamp.getSeconds());
}
return arr[0].join('-') + ' ' + arr[1].join(':');
}
// 30天以内,返回“多久前”
if (stamp >= 1000 * 60 * 60 * 24) {
return ((stamp / 1000 / 60 / 60 / 24) | 0) + '天前';
} else if (stamp >= 1000 * 60 * 60) {
return ((stamp / 1000 / 60 / 60) | 0) + '小时前';
} else if (stamp >= 1000 * 60 * 3) { // 3分钟以内为:刚刚
return ((stamp / 1000 / 60) | 0) + '分钟前';
} else if (stamp < 0) {
return '未来';
} else {
return '刚刚';
}
},
/**
* 数字前置补零
* @param num 数字
* @param length 位数
* @returns {string}
*/
digit(num, length) {
let str = '';
num = String(num);
length = length || 2;
for (let i = num.length; i < length; i++) str += '0';
return num < Math.pow(10, length) ? str + (num | 0) : num;
},
/**
* 转化为日期格式字符
* @param time 时间
* @param format 格式
* @returns {string}
*/
toDateString(time, format) {
if (!time) return '';
if (typeof time === 'string') time = time.replace(/-/g, '/');
let date = new Date(time || new Date()),
ymd = [
this.digit(date.getFullYear(), 4),
this.digit(date.getMonth() + 1),
this.digit(date.getDate())
],
hms = [
this.digit(date.getHours()),
this.digit(date.getMinutes()),
this.digit(date.getSeconds())
];
format = format || 'yyyy-MM-dd HH:mm:ss';
return format.replace(/yyyy/g, ymd[0])
.replace(/MM/g, ymd[1])
.replace(/dd/g, ymd[2])
.replace(/HH/g, hms[0])
.replace(/mm/g, hms[1])
.replace(/ss/g, hms[2]);
},
toDate(time, intnum) {
if (!time) return '';
if (typeof time === 'string') time = time.replace(/-/g, '/');
if (intnum) {
let date = new Date(time || new Date()),
ymd = [
this.digit(date.getFullYear(), 4),
this.digit(date.getMonth() + 1),
this.digit(date.getDate() + intnum)
];
return 'yyyy-MM-dd'.replace(/yyyy/g, ymd[0])
.replace(/MM/g, ymd[1])
.replace(/dd/g, ymd[2])
} else {
let date = new Date(time || new Date()),
ymd = [
this.digit(date.getFullYear(), 4),
this.digit(date.getMonth() + 1),
this.digit(date.getDate())
];
return 'yyyy-MM-dd'.replace(/yyyy/g, ymd[0])
.replace(/MM/g, ymd[1])
.replace(/dd/g, ymd[2])
}
},
getBeforeDate(num, time) {
let n = num;
let d = '';
if (time) {
d = new Date(time);
} else {
d = new Date();
}
let year = d.getFullYear();
let mon = d.getMonth() + 1;
let day = d.getDate();
/* if (day <= n) {
if (mon > 1) {
mon = mon - 1;
} else {
year = year - 1;
mon = 12;
}
} */
d.setDate(d.getDate() - n);
year = d.getFullYear();
mon = d.getMonth() + 1;
day = d.getDate();
let s = year + "-" + (mon < 10 ? ('0' + mon) : mon) + "-" + (day < 10 ? ('0' + day) : day);
return s;
},
/**
* html转义, 防止xss攻击
* @param html 需要转义的字符串
* @returns {string}
*/
escape(html) {
return String(html || '').replace(/&(?!#?[a-zA-Z0-9]+;)/g, '&amp;')
.replace(/</g, '&lt;').replace(/>/g, '&gt;')
.replace(/'/g, '&#39;').replace(/"/g, '&quot;');
},
/**
* pid形式数据转children形式
* @param data 需要转换的数组
* @param idKey id字段名
* @param pidKey pid字段名
* @param childKey 生成的children字段名
* @param pid 顶级的pid
* @returns {[]}
*/
toTreeData(data, idKey, pidKey, childKey, pid) {
if (!childKey) childKey = 'children';
if (pid === undefined) {
pid = [];
data.forEach(d => {
let flag = true;
for (let i = 0; i < data.length; i++) {
if (d[pidKey] == data[i][idKey]) {
flag = false;
break;
}
}
if (flag) pid.push(d[pidKey]);
});
}
let result = [];
data.forEach(d => {
if (d[idKey] == d[pidKey]) return console.error('data error: ', d);
if (Array.isArray(pid) ? (pid.indexOf(d[pidKey]) !== -1) : (d[pidKey] == pid)) {
let children = this.toTreeData(data, idKey, pidKey, childKey, d[idKey]);
if (children.length > 0) d[childKey] = children;
result.push(d);
}
});
return result;
},
/**
* 遍历children形式数据
* @param data 需要遍历的数组
* @param callback 回调
* @param childKey children字段名
*/
eachTreeData(data, callback, childKey) {
if (!childKey) childKey = 'children';
data.forEach(d => {
if (callback(d) !== false && d[childKey]) this.eachTreeData(d[childKey], callback, childKey);
});
},
/**
* 让浏览器全屏切换
* @returns {Element|*|boolean} 是否是全屏状态
*/
fullScreen() {
let isFull = document.fullscreenElement || document.msFullscreenElement || document.mozFullScreenElement ||
document.webkitFullscreenElement || false;
if (isFull) {
let efs = document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen ||
document.msExitFullscreen;
if (efs) {
efs.call(document);
} else if (window.ActiveXObject) {
let ws = new window.ActiveXObject('WScript.Shell');
ws && ws.SendKeys('{F11}');
}
} else {
let el = document.documentElement;
let rfs = el.requestFullscreen || el.webkitRequestFullscreen || el.mozRequestFullScreen ||
el.msRequestFullscreen;
if (rfs) {
rfs.call(el);
} else if (window.ActiveXObject) {
let wss = new window.ActiveXObject('WScript.Shell');
wss && wss.SendKeys('{F11}');
}
}
return isFull;
},
/**
* 获取屏幕宽度
* @returns {number}
*/
screenWidth() {
return document.documentElement.clientWidth || document.body.clientWidth;
},
/**
* 获取屏幕高度
* @returns {number}
*/
screenHeight() {
return document.documentElement.clientHeight || document.body.clientHeight;
},
/**
* html转text, 获取html的纯文本
* @param html
* @returns {*}
*/
htmlToText(html) {
/*let elem = document.createElement('div');
elem.innerHTML = html;
return elem.innerText;*/
return html.replace(/<[^>]+>/g, '');
},
/**
* 获取设备信息
* @param key 自定义的agent
* @returns {{weixin: *, os: (string|undefined), ie: boolean}}
*/
device(key) {
let agent = navigator.userAgent.toLowerCase(),
// 获取版本号
getVersion = function(label) {
const exp = new RegExp(label + '/([^\\s\\_\\-]+)');
label = (agent.match(exp) || [])[1];
return label || false;
},
// 返回结果集
result = {
os: function() { // 底层操作系统
if (/windows/.test(agent)) {
return 'windows';
} else if (/linux/.test(agent)) {
return 'linux';
} else if (/iphone|ipod|ipad|ios/.test(agent)) {
return 'ios';
} else if (/mac/.test(agent)) {
return 'mac';
} else if (/android/.test(agent)) {
return 'android';
}
}(),
ie: function() { // ie版本
return (!!window.ActiveXObject || 'ActiveXObject' in window) ? (
(agent.match(/msie\s(\d+)/) || [])[1] || '11' // 由于ie11并没有msie的标识
) : false;
}(),
weixin: getVersion('micromessenger') // 是否微信
};
// 任意的key
if (key && !result[key]) {
result[key] = getVersion(key);
}
// 移动设备
result.android = /android/.test(agent);
result.ios = result.os === 'ios';
result.mobile = (result.android || result.ios) ? true : false;
return result;
},
/**
* 生成随机id
* @param length 长度
* @returns {string}
*/
uuid(length = 32) {
const num = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
let str = '';
for (let i = 0; i < length; i++) {
str += num.charAt(Math.floor(Math.random() * num.length));
}
return str;
},
/**
* 生成m到n的随机数不包含n
* @param m 最小值
* @param n 最大值
* @returns {number}
*/
random(m, n) {
return Math.floor(Math.random() * (m - n) + n);
},
/**
* 百度地图坐标转高德地图坐标
* @param point 坐标
* @returns {{lng: number, lat: number}}
*/
bd09ToGcj02: function(point) {
const x_pi = (3.14159265358979324 * 3000.0) / 180.0;
const x = point.lng - 0.0065,
y = point.lat - 0.006;
const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
return {
lng: z * Math.cos(theta),
lat: z * Math.sin(theta)
};
},
/**
* 高德地图坐标转百度地图坐标
* @param point 坐标
* @returns {{lng: number, lat: number}}
*/
gcj02ToBd09: function(point) {
const x_pi = (3.14159265358979324 * 3000.0) / 180.0;
const x = point.lng,
y = point.lat;
const z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
const theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
return {
lng: z * Math.cos(theta) + 0.0065,
lat: z * Math.sin(theta) + 0.006
};
},
/**
* 深度克隆对象
* @param obj
* @returns {{}|*}
*/
deepClone: function(obj) {
let result;
const type = this.typeOf(obj);
if (type === 'Object') result = {};
else if (type === 'Array') result = [];
else return obj;
Object.keys(obj).forEach(key => {
const copy = obj[key],
cType = this.typeOf(copy);
if (cType === 'Object' || cType === 'Array') result[key] = this.deepClone(copy);
else result[key] = obj[key];
});
return result;
},
/**
* 获取变量类型
* @param obj
* @returns {string}
*/
typeOf(obj) {
if (obj === null) return 'Null';
if (obj === undefined) return 'Undefined';
return Object.prototype.toString.call(obj).slice(8, -1);
},
/**
* 播放音频
* @param url 音频地址
*/
play(url) {
return new Audio(url).play();
},
/**
* 判断富文本是否为空
* @param html
*/
htmlIsBlank(html) {
if (!html) return true;
const media = ['img', 'audio', 'video', 'iframe', 'object'];
for (let i = 0; i < media.length; i++) {
if (html.indexOf('<' + media[i]) > -1) return false;
}
let str = html.replace(/\s*/g, ''); // 去掉所有空格
if (!str) return true;
str = str.replace(/&nbsp;/ig, ''); // 去掉所有&nbsp;
if (!str) return true;
str = str.replace(/<[^>]+>/g, ''); // 去掉所有html标签
return !str;
},
/**
* 导出excel
* @param XLSX XLSX对象
* @param sheet 数组或sheet对象
* @param sheetname 文件名称
* @param type 文件格式
*/
exportSheet(XLSX, sheet, sheetname, type) {
if (!sheetname) sheetname = 'sheet1';
if (!type) type = 'xlsx';
if (Array.isArray(sheet)) sheet = XLSX.utils.aoa_to_sheet(sheet);
let workbook = {
SheetNames: [sheetname],
Sheets: {}
};
workbook.Sheets[sheetname] = sheet;
XLSX.writeFile(workbook, sheetname + '.' + type);
},
/**
* 获取不同屏幕的高度
* @returns {number}
*/
toHeight() {
let screenWidth = document.documentElement.clientWidth || document.body.clientWidth;
if (screenWidth <= 1366) {
return 'calc(100vh - 135px)';
} else if (screenWidth <= 1600 && screenWidth > 1366) {
return 'calc(100vh - 265px)';
} else if (screenWidth <= 1680 && screenWidth > 1600) {
return 'calc(100vh - 465px)';
} else {
return 'calc(100vh - 500px)';
}
},
/**
* 获取表头样式
*/
headerCellStyle() {
return {
'text-align': 'center',
'height': '10px'
}
},
/**
* 获取表每列样式
*/
cellStyle() {
return {
'text-align': 'center',
'padding': '0px'
}
},
/**
* 获取表每行样式
*/
rowStyle() {
return {
'height': '40px'
}
},
getParam(key) {
let href = location.href.split('#')[1] // http://localhost:8080/?code=12313123&a=444
if(href==null||href==''){
href = location.href.split('#')[0]
}
let queryString = href.split('?')[1] // code=12313123&a=444
if (!queryString) {
return false
}
let query = queryString.split('&')
let obj = {}
for (let i = 0; i < query.length; i++) {
let arr = query[i].split('=')
obj[arr[0]] = arr[1]
}
if (key) {
return obj[key]
}
return obj
},
/**
* 常用颜色
*/
beautifulColors: [
'rgb(24,144,255)', 'rgb(102,181,255)', 'rgb(65, 217, 199)', 'rgb(47, 194, 91)',
'rgb(110, 219, 143)', 'rgb(154, 230, 92)', 'rgb(250, 204, 20)', 'rgb(230, 150, 92)',
'rgb(87, 173, 113)', 'rgb(34, 50, 115)', 'rgb(115, 138, 230)', 'rgb(117, 100, 204)',
'rgb(133, 67, 224)', 'rgb(168, 119, 237)', 'rgb(92, 142, 230)', 'rgb(19, 194, 194)',
'rgb(112, 224, 224)', 'rgb(92, 163, 230)', 'rgb(52, 54, 199)', 'rgb(128, 130, 255)',
'rgb(221, 129, 230)', 'rgb(240, 72, 100)', 'rgb(250, 125, 146)', 'rgb(213, 152, 217)'
]
}

73
src/views/auth/index.vue

@ -0,0 +1,73 @@
<template>
</template>
<script>
export default {
name: "Auth",
components: {
},
data() {
return {
};
},
computed: {
},
mounted() {
var authCode=this.$util.getParam("auth_code");
if(authCode!=null&&authCode!=''){
var token=this.$util.getParam("token");
this.getAlipayUnionId(authCode);
}else{
window.location.href="https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id=2021004116616163&scope=auth_base&state=state&redirect_uri="+location.href
}
},
methods: {
getAlipayUnionId(authCode){
this.$toast.loading({
message: '加载中...',
loadingType: 'spinner',
});
this.$http.post(this.$setting.context_path1+'/util/manage/getAlipayUnionId.do?code='+authCode).then(res => {
if (res.data.code==0) {
var params="";
var unionId=res.data.data;
var type= this.$util.getParam("type") //type==1 type=2type
var trainingOrderId= this.$util.getParam("trainingOrderId");//id
var fromAppurl= this.$util.getParam("fromAppurl");//url
var token= this.$util.getParam("token");//apptoken
var rechargeAmount= this.$util.getParam("rechargeAmount");//()
if(type!=null&&type!=""){
params=params+"&type="+type;
}
if(trainingOrderId!=null&&trainingOrderId!=""){
params=params+"&trainingOrderId="+trainingOrderId;
}
if(fromAppurl!=null&&fromAppurl!=""){
params=params+"&fromAppurl="+fromAppurl;
}
if(token!=null&&token!=""){
params=params+"&token="+token;
}
if(rechargeAmount!=null&&rechargeAmount!=""){
params=params+"&rechargeAmount="+rechargeAmount;
}
this.$router.push('/cashier?unionId='+unionId+params);
} else {
this.$toast({ type: 'fail', message:res.data.message });
}
}).catch(e => {
this.$toast({ type: 'fail', message:e.message });
});
},
},
created() {
},
};
</script>
<style scoped>
</style>

323
src/views/cashier/index.vue

@ -0,0 +1,323 @@
<template>
<div class="cashier_detial">
<div class="icon">
<div class="img"><van-image :src="payment_logo" /></div>
<div class="price">{{rechargeAmount>0?rechargeAmount:price}}</div>
</div>
<div class="content">
<div class="title">
<div class="text">订单信息</div>
<div class="c">{{schoolName}}报名费</div>
</div>
<div class="title">
<div class="text"> 订单金额</div>
<div class="p">{{rechargeAmount>0?rechargeAmount:price}}</div>
</div>
</div>
<div class="button">
<div>
<van-button class="add-btn" @click="save">确认支付完成</van-button>
</div>
<van-button class="add-btn" @click="goToApp">返回商户界面</van-button>
</div>
</div>
</template>
<script>
import payment_logo from '@/assets/payment.png'
import Big from 'big.js';
export default {
name: "Cashier",
components: {},
data() {
return {
payment_logo: payment_logo,
rechargeAmount: 0,
price: 0,
schoolName: '',
fromAppurl1: '',
type: null,
unionId: null,
trainingOrderId: null,
fromAppurl: null,
token: null,
ipType:"04",
payType:2,
riskType:"01",
deviceType:"1",
verifyTp:"01",
verifyRt:"01",
deviceId:"1",
longitude:"120.21649",
latitude:"30.36193",
clientIp:"127.0.0.1"
};
},
computed: {
},
mounted() {
var unionId = this.$util.getParam("unionId");
var type = this.$util.getParam("type") //type==1 type=2type
var trainingOrderId = this.$util.getParam("trainingOrderId"); //id
var fromAppurl = this.$util.getParam("fromAppurl"); //url
var token = this.$util.getParam("token"); //apptoken
var rechargeAmount = this.$util.getParam("rechargeAmount"); //()
if (unionId == '' || unionId == null) {
this.$toast({type: 'fail',message: "获取用户信息失败!"});
} else {
this.unionId = unionId;
this.type = type;
this.trainingOrderId = trainingOrderId;
this.fromAppurl = decodeURIComponent(fromAppurl);
this.fromAppurl1 = decodeURIComponent(fromAppurl);
this.token = token;
this.rechargeAmount = rechargeAmount;
this.ready();
this.getCurrentLocation();
this.detail(trainingOrderId,token,type);
}
},
methods: {
ready(callback) {
// jsbridge
if (window.AlipayJSBridge) {
callback && callback();
} else {
//
document.addEventListener('AlipayJSBridgeReady', callback, false);
}
},
getCurrentLocation (){
AlipayJSBridge.call('getCurrentLocation', { bizType: '$s' }, function(result) {
if (result.error) {
return;
}
this.latitude=result.latitude;
this.longitude=result.longitude;
});
},
/**
* 订单详情
* @return {Promise}
*/
detail(trainingOrderId, token, type) {
var url = type == 1 ? this.$setting.context_path2 +'/order/manage/getNoPayOrderDetails?v=1&ts=1&sign=1' : type == 2 ? this.$setting.context_path1 +'/apply/manage/getChangeModelDetails.do?1=1' : this.$setting.context_path1 +'/order/manage/getNoPayOrderDetails.do?1=1';
url = url+"&token=" + token + "&trainingOrderId=" + trainingOrderId;
// AlipayJSBridge
this.$toast.loading({
message: '加载中...',
forbidClick: true,
loadingType: 'spinner',
});
this.$http.post(url).then(res => {
if (res.data.code == 0) {
this.schoolName = res.data.data.schoolName;
if (type == 2) {
this.price = new Big(res.data.data.balance).div(100).toFixed(2)
} else {
this.price = new Big(res.data.data.price).div(100).toFixed(2)
}
this.save();
} else {
this.$toast({type: 'fail',message: res.data.message});
}
}).catch(e => {
this.$toast({type: 'fail',message: e.message});
});
},
/***预支付 */
createPrepaid(token, type) {
var url = type == 1 ? this.$setting.context_path2 +'/order/manage/createPrepaid?v=1&ts=1&sign=1' : type == 2 ? this.$setting.context_path1 +'/order/manage/createChangeModelPrepaid.do?1=1' : this.$setting.context_path1 +'/order/manage/createPrepaid.do?1=1';
url = url+"&token=" + token
var param={clientIp:this.clientIp,longitude:this.longitude,latitude:this.latitude,ipType:this.ipType,payType:this.payType,fromAppurl:this.fromAppurl,trainingOrderId:this.trainingOrderId,riskType:this.riskType,deviceType:this.deviceType,verifyTp:this.verifyTp,verifyRt:this.verifyRt,deviceId:this.deviceId,openid:this.unionId}
this.$http.post(url,type == 1?param:null,type!=1?{params:param}:null).then(res => {
if (res.data.code == 0) {
var outTradeNo=res.data.data.outTradeNo;//
if(outTradeNo!=''&&outTradeNo!=null){
this.fromAppurl1=this.fromAppurl+'?outTradeNo='+outTradeNo////
}
var payInfo=JSON.parse(res.data.data.payInfo);
//api
AlipayJSBridge.call("tradePay",{tradeNO: payInfo.tradeNO,},(result) => {
const { resultCode = "", result: resultMsg = "支付失败" } =result;
if (resultCode == "9000") {
//
this.$router.push('/result?price='+this.price+"&fromAppurl="+this.fromAppurl+'&outTradeNo='+outTradeNo);
}else if(resultCode=="6001"){
this.$toast({type: 'fail',message: "用户取消支付"});
}else{
this.$toast({type: 'fail',message: result});
}
}
);
} else {
this.$toast({type: 'fail',message: res.data.message});
}
}).catch(e => {
this.$toast({type: 'fail',message: e.message});
});
},
/***充值预支付 */
createRechargePrepaid(token, type) {
var param={clientIp:this.clientIp,longitude:this.longitude,latitude:this.latitude,ipType:this.ipType,payType:this.payType,fromAppurl:this.fromAppurl,trainingOrderId:this.trainingOrderId,riskType:this.riskType,deviceType:this.deviceType,verifyTp:this.verifyTp,verifyRt:this.verifyRt,deviceId:this.deviceId,openid:this.unionId,rechargeAmount:this.rechargeAmount}
var url = type == 1 ? this.$setting.context_path2 +'/order/manage/createRechargePrepaid?v=1&ts=1&sign=1': this.$setting.context_path1 +'/order/manage/createRechargePrepaid.do?1=1';
url = url+"&token=" + token
this.$http.post(url,type == 1?param:null,type!=1?{params:param}:null).then(res => {
if (res.data.code == 0) {
var outTradeNo=res.data.data.outTradeNo;//
if(outTradeNo!=''&&outTradeNo!=null){
this.fromAppurl1=this.fromAppurl+'?outTradeNo='+outTradeNo//
}
var payInfo=JSON.parse(res.data.data.payInfo);
//api
AlipayJSBridge.call("tradePay",{tradeNO: payInfo.tradeNO,},(result) => {
const { resultCode = "", result: resultMsg = "支付失败" } =result;
if (resultCode == "9000") {
//
this.$router.push('/result?price='+this.rechargeAmount+"&fromAppurl="+this.fromAppurl+'&outTradeNo='+outTradeNo);
}else if(resultCode=="6001"){
this.$toast({type: 'fail',message: "用户取消支付"});
}else{
this.$toast({type: 'fail',message: result});
}
}
);
} else {
this.$toast({type: 'fail',message: res.data.message});
}
}).catch(e => {
this.$toast({type: 'fail',message: e.message});
});
},
//
save(){
this.$toast.loading({
message: '加载中...',
duration:3000,
forbidClick: true,
loadingType: 'spinner',
});
if(this.rechargeAmount!=null&&this.rechargeAmount!=''&&this.rechargeAmount>0){
this.createRechargePrepaid(this.token,this.type);//
}else{
this.createPrepaid(this.token,this.type);
}
},
goToApp() {
window.location.href =this.fromAppurl1;
},
},
created() {
// jsapi
const s = document.createElement("script");
s.type = "text/javascript";
s.src ="https://gw.alipayobjects.com/as/g/h5-lib/alipayjsapi/3.1.1/alipayjsapi.min.js";
document.body.appendChild(s);
},
};
</script>
<style scoped>
body {
height: 100%;
width: 100%;
background-color: #f3f3f3;
}
.cashier_detial {
width: 100%;
height: 1206rpx;
overflow: hidden;
}
.cashier_detial .icon {
width: 100%;
height: 200px;
}
.cashier_detial .icon .img {
width: 100%;
text-align: center;
}
.cashier_detial .icon .img .van-image {
margin-top: 20px;
width: 100px;
height: 100px;
margin-bottom: 15px;
}
.cashier_detial .icon .price {
text-align: center;
font-size: 32px;
font-weight: bold;
}
.cashier_detial .content {
background-color: #fff;
width: 100%;
height: 100px;
font-size: 15px;
}
.cashier_detial .content .title {
font-size: 15px;
height: 58px;
border-bottom: 1px solid #f8f8f8;
}
.cashier_detial .content .title .text {
margin-left: 15px;
margin-top: 15px;
float: left;
width: 20%;
color: #9b9999;
}
.cashier_detial .content .title .c {
float: left;
width: 72%;
margin-top: 15px;
}
.cashier_detial .content .title .p {
float: left;
width: 72%;
margin-top: 15px;
}
.button {
width: 100%;
margin-top: 175px;
text-align: center;
}
.add-btn {
width: 351px;
height: 46px;
background: linear-gradient(0deg, #3593fb 0%, #53d3e5 100%);
border-radius: 10px;
margin-top: 25px;
line-height: 46px;
font-weight: bold;
font-family: PingFang SC;
color: #fff;
cursor: pointer;
}
.text-center {
text-align: center;
font-size: 16px;
}
</style>

102
src/views/cashier/result.vue

@ -0,0 +1,102 @@
<template>
<div class="cashier_detial">
<div class="icon">
<div class="img"><van-image :src="success_logo" /></div>
<div class="price">{{price}}</div>
</div>
<div class="button">
<van-button class="add-btn" @click="goToApp">返回商户界面</van-button>
</div>
</div>
</template>
<script>
import success_logo from '@/assets/success.png'
export default {
name: "CashierResult",
components: {
},
data() {
return {
success_logo:success_logo,
price:0,
fromAppurl:"",
};
},
computed: {
},
mounted() {
var price = this.$util.getParam("price");//
var fromAppurl = this.$util.getParam("fromAppurl"); //url
var outTradeNo = this.$util.getParam("outTradeNo"); //id
if(outTradeNo!=''&&outTradeNo!=null){
fromAppurl=fromAppurl+'?outTradeNo='+outTradeNo;
}
this.price=price;
this.fromAppurl=fromAppurl;
},
methods: {
goToApp() {
window.location.href =this.fromAppurl;
},
},
created() {
},
};
</script>
<style scoped>
body{
height: 100%;
width: 100%;
background-color: #f3f3f3;
}
.cashier_detial {
width: 100%;
height: 1206rpx;
overflow: hidden;
}
.cashier_detial .icon {
width: 100%;
height: 200px;
}
.cashier_detial .icon .img {
width: 100%;
text-align: center;
}
.cashier_detial .icon .img .van-image{
margin-top: 20px;
width: 100px;
height: 100px;
margin-bottom: 15px;
}
.cashier_detial .icon .price {
text-align: center;
font-size: 32px;
font-weight: bold;
}
.button {
width: 100%;
margin-top: 175px;
text-align: center;
}
.add-btn {
width: 351px;
height: 46px;
background: linear-gradient(0deg, #3593fb 0%, #53d3e5 100%);
border-radius: 10px;
margin-top: 25px;
line-height: 46px;
font-weight: bold;
font-family: PingFang SC;
color:#fff;
cursor: pointer;
}
.text-center {
text-align: center;
font-size: 16px;
}
</style>

34
src/views/index/index.vue

@ -0,0 +1,34 @@
<template>
<van-button type="primary" @click="pay">拉起支付宝</van-button>
</template>
<script>
export default {
name: "Index",
components: {
},
data() {
return {
};
},
computed: {
},
mounted() {
},
methods: {
pay(){
//window.location.href = 'alipays://platformapi/startapp?saId=10000007&qrcode='+encodeURIComponent('http://192.168.1.39:9103/alipay/auth?trainingOrderId=65aee95e818f4571b2480bdafba4cd4a&token=amlhcGVpMTY5NDE1MjM0MzU5NTM0OTQ2&fromAppurl=paymoney://car:8888/carstep')//
window.location.href="alipays://platformapi/startapp?appId=20000042&publicBizType=LIFE_APP&publicId=2021004116616163&url="+encodeURIComponent('http://192.168.1.39:9103/alipay/auth?trainingOrderId=65aee95e818f4571b2480bdafba4cd4a&token=amlhcGVpMTY5NDE1MjM0MzU5NTM0OTQ2&fromAppurl=paymoney://car:8888/carstep')//
//window.location.href ='alipayqr://platformapi/startapp?saId=10000007&qrcode='+encodeURI('http://192.168.1.39:9103/alipay/auth?type=111&trainingOrderId=111&fromAppurl=111&token=111')//
}
},
created() {
},
};
</script>
<style scoped>
</style>

47
vue.config.js

@ -0,0 +1,47 @@
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const port = process.env.port || process.env.npm_config_port || 9103 // dev port
const productionGzipExtensions = ['js','css','less']
module.exports = {
publicPath: process.env.VUE_APP_PATH,
outputDir: 'dist',
assetsDir: 'static',
productionSourceMap: false,
transpileDependencies: ['vant'], // 需要兼容IE10要放开这个
devServer: {
port: port,
open: true,
host: '0.0.0.0',
overlay: {
warnings: false,
errors: true
},
proxy: {
'/alipayapi': {
target: process.env.VUE_APP_BASE_URL,
changeOrigin: true,
pathRewrite: {
'^/alipayapi': ''
}
}
}
},
chainWebpack: config => {
config.plugin('html').tap(options => {
options[0].title = '浙里学车生活号';
return options;
});
if (process.env.NODE_ENV === 'production') {
config.plugin('compressionPlugin').use(new CompressionWebpackPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
// deleteOriginalAssets:true, //删除源文件,不建议
minRatio: 0.8
}));
}
}
}
Loading…
Cancel
Save