mirror of
https://gitee.com/bootx/dax-pay-h5.git
synced 2025-10-14 06:04:51 +00:00
feat 聚合支付处理和一些页面编写
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
VITE_PORT = 9100
|
||||
|
||||
# 网站根目录
|
||||
VITE_PUBLIC_PATH = /
|
||||
VITE_PUBLIC_PATH = /h5
|
||||
|
||||
# 是否删除console
|
||||
VITE_DROP_CONSOLE = true
|
||||
|
6
components.d.ts
vendored
6
components.d.ts
vendored
@@ -7,10 +7,14 @@ export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
SvgIcon: typeof import('./src/components/SvgIcon.vue')['default']
|
||||
Loading: typeof import('./src/components/Loading/Loading.vue')['default']
|
||||
SvgIcon: typeof import('./src/components/SvgIcon/SvgIcon.vue')['default']
|
||||
VanButton: typeof import('vant/es')['Button']
|
||||
VanConfigProvider: typeof import('vant/es')['ConfigProvider']
|
||||
VanIcon: typeof import('vant/es')['Icon']
|
||||
VanLoading: typeof import('vant/es')['Loading']
|
||||
VanNavBar: typeof import('vant/es')['NavBar']
|
||||
VanOverlay: typeof import('vant/es')['Overlay']
|
||||
VanTabbar: typeof import('vant/es')['Tabbar']
|
||||
VanTabbarItem: typeof import('vant/es')['TabbarItem']
|
||||
}
|
||||
|
38
src/components/Loading/Loading.vue
Normal file
38
src/components/Loading/Loading.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<van-loading
|
||||
class="loading-overlay"
|
||||
:text-color="textColor"
|
||||
:color="color"
|
||||
:size="size"
|
||||
:text-size="textSize"
|
||||
vertical
|
||||
>{{ text }}</van-loading
|
||||
>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
default: '加载中...',
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#1172d5',
|
||||
},
|
||||
textColor: {
|
||||
type: String,
|
||||
default: '#fff',
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 50,
|
||||
},
|
||||
textSize: {
|
||||
type: Number,
|
||||
default: 18,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
@@ -42,7 +42,7 @@
|
||||
const currentRoute = useRoute()
|
||||
|
||||
const getTitle = computed(() => currentRoute.meta.title as string)
|
||||
|
||||
console.log(getTitle)
|
||||
// 菜单
|
||||
const getMenus = computed(() =>
|
||||
routeStore.menus.filter((item) => {
|
||||
@@ -50,7 +50,7 @@
|
||||
}),
|
||||
)
|
||||
|
||||
const getShowHeader = computed(() => currentRoute.meta.showHeader)
|
||||
const getShowHeader = computed(() => currentRoute.meta.showHeader) || true
|
||||
// const getShowTabbar = computed(() => !currentRoute.meta.hiddenTabbar)
|
||||
// TODO 默认不显示tabbar
|
||||
const getShowTabbar = ref<boolean>(false)
|
||||
|
@@ -20,5 +20,21 @@ export const BusinessRoute: RouteRecordRaw = {
|
||||
hiddenTabbar: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/result/success',
|
||||
name: 'PaySuccessResult',
|
||||
component: () => import('@/views/result/PaySuccessResult.vue'),
|
||||
meta: {
|
||||
title: '支付成功',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/result/error',
|
||||
name: 'PayErrorResult',
|
||||
component: () => import('@/views/result/PayErrorResult.vue'),
|
||||
meta: {
|
||||
title: '支付失败',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@@ -11,35 +11,19 @@ export const DemoRoute: RouteRecordRaw = {
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: '/cashier/alipay',
|
||||
name: 'AlipayCashier',
|
||||
component: () => import('@/views/demo/cashier/AlipayCashier.vue'),
|
||||
path: '/aggregate/alipay',
|
||||
name: 'AliPayAggregate',
|
||||
component: () => import('@/views/demo/aggregate/AliPayAggregate.vue'),
|
||||
meta: {
|
||||
title: '支付宝收银台',
|
||||
title: '支付宝聚合支付',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/cashier/wxJsapiPay',
|
||||
name: 'WechatJsapiPay',
|
||||
component: () => import('@/views/demo/cashier/WechatJsapiPay.vue'),
|
||||
path: '/aggregate/wechatPay',
|
||||
name: 'WechatPayAggregate',
|
||||
component: () => import('@/views/demo/aggregate/WechatPayAggregate.vue'),
|
||||
meta: {
|
||||
title: '支付宝收银台',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/exception/timeout',
|
||||
name: 'TimeoutPay',
|
||||
component: () => import('@/views/demo/exception/TimeoutPay.vue'),
|
||||
meta: {
|
||||
title: '支付超时',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/exception/errorPay',
|
||||
name: 'TimeoutPay',
|
||||
component: () => import('@/views/demo/exception/ErrorPay.vue'),
|
||||
meta: {
|
||||
title: '支付超时',
|
||||
title: '微信聚合支付',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@@ -7,6 +7,7 @@ import { useRouteStoreWidthOut } from '@/store/modules/route'
|
||||
import routeModuleList from './modules'
|
||||
import { BusinessRoute } from '@/router/business'
|
||||
import { DemoRoute } from '@/router/demo'
|
||||
import { createRouterGuards } from "@/router/router-guards";
|
||||
|
||||
// 普通路由
|
||||
export const constantRouter: RouteRecordRaw[] = [
|
||||
@@ -30,8 +31,7 @@ const router = createRouter({
|
||||
|
||||
export function setupRouter(app: App) {
|
||||
app.use(router)
|
||||
// TODO 不使用路由守卫
|
||||
// createRouterGuards(router)
|
||||
createRouterGuards(router)
|
||||
}
|
||||
|
||||
export default router
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { isNavigationFailure, Router } from 'vue-router'
|
||||
import { useRouteStoreWidthOut } from '@/store/modules/route'
|
||||
import { useUserStoreWidthOut } from '@/store/modules/user'
|
||||
import { ACCESS_TOKEN } from '@/store/mutation-types'
|
||||
import { storage } from '@/utils/Storage'
|
||||
import { PageEnum } from '@/enums/pageEnum'
|
||||
@@ -11,10 +10,12 @@ const whitePathList = [LOGIN_PATH] // no redirect whitelist
|
||||
|
||||
export function createRouterGuards(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
// 所有的的页面都可以访问
|
||||
next()
|
||||
return
|
||||
|
||||
// to: 即将要进入的目标
|
||||
// from: 当前导航正要离开的路由
|
||||
const userStore = useUserStoreWidthOut()
|
||||
|
||||
if (from.path === LOGIN_PATH && to.name === PageEnum.ERROR_PAGE_NAME) {
|
||||
next(PageEnum.BASE_HOME)
|
||||
return
|
||||
|
@@ -9,3 +9,11 @@ body {
|
||||
.van-action-sheet__cancel {
|
||||
color: var(--van-primary-color) !important;
|
||||
}
|
||||
|
||||
// loading 遮罩全屏铺盖
|
||||
.loading-overlay {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
@@ -24,7 +24,7 @@
|
||||
<script setup lang="ts" name="DashboardPage">
|
||||
import { computed } from 'vue'
|
||||
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||
import SvgIcon from '@/components/SvgIcon.vue'
|
||||
import SvgIcon from '@/components/SvgIcon/SvgIcon.vue'
|
||||
import { useGlobSetting } from '@/hooks/setting'
|
||||
|
||||
const designStore = useDesignSettingStore()
|
||||
|
63
src/views/demo/aggregate/Aggregate.api.ts
Normal file
63
src/views/demo/aggregate/Aggregate.api.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { http } from '@/utils/http/axios'
|
||||
import { Result } from '#/axios'
|
||||
|
||||
/**
|
||||
* 获取支付信息
|
||||
*/
|
||||
export function getInfo(code): Promise<Result<AggregatePayInfo>> {
|
||||
return http.request({
|
||||
url: '/demo/aggregate/getInfo',
|
||||
method: 'get',
|
||||
params: { code },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付宝h5支付
|
||||
*/
|
||||
export function aliH5Pay(code): Promise<Result<PayOrderResult>> {
|
||||
return http.request({
|
||||
url: '/demo/aggregate/aliH5Pay',
|
||||
method: 'post',
|
||||
params: { code },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信H5预支付签名信息
|
||||
*/
|
||||
export function getWxJsapiPay(aggregateCode, openId): Promise<Result<WxJsapiSignResult>> {
|
||||
return http.request({
|
||||
url: '/demo/aggregate/getWxJsapiPay',
|
||||
method: 'post',
|
||||
params: { aggregateCode, openId },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*支付信息
|
||||
*/
|
||||
export interface AggregatePayInfo {
|
||||
title?: string
|
||||
businessNo?: string
|
||||
amount?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起支付后响应对象
|
||||
*/
|
||||
export interface PayOrderResult {
|
||||
payBody: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信Jsapi预支付签名返回信息
|
||||
*/
|
||||
export interface WxJsapiSignResult {
|
||||
appId?: string
|
||||
timeStamp?: string
|
||||
nonceStr?: string
|
||||
prePayId?: string
|
||||
signType?: string
|
||||
paySign?: string
|
||||
}
|
89
src/views/demo/aggregate/AliPayAggregate.vue
Normal file
89
src/views/demo/aggregate/AliPayAggregate.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="flex flex-col justify-center page-container">
|
||||
<van-overlay lock-scroll :show="loading">
|
||||
<Loading text="调起支付中..." />
|
||||
</van-overlay>
|
||||
<div class="text-center">
|
||||
<h1>金额: {{ info.amount / 100.0 }} 元</h1>
|
||||
<h1>标题: {{ info.title }}</h1>
|
||||
<h1>业务号: {{ info.businessNo }}</h1>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<van-button type="primary" @click="pay">去支付</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, unref } from 'vue'
|
||||
import { AggregatePayInfo, aliH5Pay, getInfo } from './Aggregate.api'
|
||||
import { useRouter } from 'vue-router'
|
||||
import router from '@/router'
|
||||
import Loading from '@/components/Loading/Loading.vue'
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
|
||||
const { currentRoute } = useRouter()
|
||||
const { query } = unref(currentRoute)
|
||||
|
||||
const loading = ref<boolean>(false)
|
||||
const code = ref<string>(query.code as string)
|
||||
const info = ref<AggregatePayInfo>({})
|
||||
|
||||
/**
|
||||
* 根据code获取支付信息
|
||||
*/
|
||||
function init() {
|
||||
loading.value = true
|
||||
getInfo(code.value)
|
||||
.then(({ data }) => {
|
||||
info.value = data
|
||||
loading.value = false
|
||||
})
|
||||
.catch((err) => {
|
||||
// 跳转到错误页
|
||||
router.push({
|
||||
name: 'PayErrorResult',
|
||||
query: { msg: err.message },
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起支付
|
||||
*/
|
||||
function pay() {
|
||||
loading.value = true
|
||||
aliH5Pay(code.value)
|
||||
.then(({ data }) => {
|
||||
loading.value = false
|
||||
// 跳转到H5支付页面
|
||||
window.location.href = data.payBody
|
||||
})
|
||||
.catch((err) => {
|
||||
// 跳转到错误页
|
||||
router.push({
|
||||
name: 'PayErrorResult',
|
||||
query: { msg: err.message },
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.page-container {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
padding: 50px 0;
|
||||
height: 100vh;
|
||||
|
||||
.text-center {
|
||||
h1 {
|
||||
color: #666;
|
||||
padding: 2vh 0;
|
||||
font-size: x-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
112
src/views/demo/aggregate/WechatPayAggregate.vue
Normal file
112
src/views/demo/aggregate/WechatPayAggregate.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="flex flex-col justify-center page-container">
|
||||
<van-overlay lock-scroll :show="loading">
|
||||
<Loading text="调起支付中..." />
|
||||
</van-overlay>
|
||||
<div class="text-center">
|
||||
<h1>金额: {{ info.amount / 100.0 }} 元</h1>
|
||||
<h1>标题: {{ info.title }}</h1>
|
||||
<h1>业务号: {{ info.businessNo }}</h1>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<van-button type="primary" @click="pay">去支付</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, unref } from 'vue'
|
||||
import { AggregatePayInfo, getWxJsapiPay, getInfo, WxJsapiSignResult } from './Aggregate.api'
|
||||
import { useRouter } from 'vue-router'
|
||||
import router from '@/router'
|
||||
import Loading from '@/components/Loading/Loading.vue'
|
||||
|
||||
const { currentRoute } = useRouter()
|
||||
const { query } = unref(currentRoute)
|
||||
|
||||
const aggregateCode = ref<string>(query.aggregateCode as string)
|
||||
const openId = ref<string>(query.openId as string)
|
||||
const info = ref<AggregatePayInfo>({})
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
|
||||
/**
|
||||
* 根据code获取支付信息
|
||||
*/
|
||||
function init() {
|
||||
loading.value = true
|
||||
// 获取支付信息
|
||||
getInfo(aggregateCode.value)
|
||||
.then(({ data }) => {
|
||||
loading.value = false
|
||||
info.value = data
|
||||
})
|
||||
.catch((err) => {
|
||||
// 跳转到错误页
|
||||
router.push({
|
||||
name: 'PayErrorResult',
|
||||
query: { msg: err.message },
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起支付
|
||||
*/
|
||||
function pay() {
|
||||
loading.value = true
|
||||
getWxJsapiPay(aggregateCode.value, openId.value)
|
||||
.then(({ data }) => {
|
||||
loading.value = false
|
||||
doPay(data)
|
||||
})
|
||||
.catch(() => {
|
||||
// 跳转到错误页
|
||||
router.push({
|
||||
name: 'PayErrorResult',
|
||||
query: { msg: '支付失败' },
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉起支付
|
||||
*/
|
||||
function doPay(data: WxJsapiSignResult) {
|
||||
const form = {
|
||||
appId: data.appId, //公众号ID,由商户传入
|
||||
timeStamp: data.timeStamp, //时间戳,自1970年以来的秒数
|
||||
nonceStr: data.nonceStr, //随机串
|
||||
package: data.prePayId, // 预支付ID
|
||||
signType: data.signType, //微信签名方式:
|
||||
paySign: data.paySign, //微信签名
|
||||
}
|
||||
// 使用微信JsSdk拉起支付
|
||||
WeixinJSBridge.invoke('getBrandWCPayRequest', form, function (res) {
|
||||
if (res.err_msg === 'get_brand_wcpay_request:ok') {
|
||||
setTimeout(() => {
|
||||
WeixinJSBridge.call('closeWindow')
|
||||
}, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.page-container {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
padding: 50px 0;
|
||||
height: 100vh;
|
||||
|
||||
.text-center {
|
||||
h1 {
|
||||
color: #666;
|
||||
padding: 2vh 0;
|
||||
font-size: x-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
123
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
@@ -1,12 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
345
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
@@ -1,11 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
支付错误
|
||||
<div class="flex flex-col justify-center page-container">
|
||||
<div class="text-center">
|
||||
<img src="~@/assets/icons/exception/403.svg" alt="" />
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<h1 class="text-base text-gray-500">{{ msg }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { unref } from 'vue'
|
||||
|
||||
const { currentRoute } = useRouter()
|
||||
const { query } = unref(currentRoute)
|
||||
|
||||
// 获取请求路径下的错误信息
|
||||
let msg = (query.error as string) || '支付出现错误,请稍后重试'
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.page-container {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
padding: 50px 0;
|
||||
height: 100vh;
|
||||
|
||||
.text-center {
|
||||
h1 {
|
||||
color: #666;
|
||||
padding: 5vh 0;
|
||||
font-size: x-large;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 350px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<h1 class="text-base text-gray-500">抱歉,你无权访问该页面</h1>
|
||||
<n-button type="info" @click="goHome">回到首页</n-button>
|
||||
<van-button type="info" @click="goHome">回到首页</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<h1 class="text-base text-gray-500">抱歉,服务器出错了</h1>
|
||||
<n-button type="info" @click="goHome">回到首页</n-button>
|
||||
<van-button type="info" @click="goHome">回到首页</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
43
src/views/result/PayErrorResult.vue
Normal file
43
src/views/result/PayErrorResult.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="flex flex-col justify-center page-container">
|
||||
<div class="text-center">
|
||||
<img src="~@/assets/icons/exception/403.svg" alt="" />
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<h1 class="text-base text-gray-500">{{ msg }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { unref } from 'vue'
|
||||
|
||||
const { currentRoute } = useRouter()
|
||||
const { query } = unref(currentRoute)
|
||||
|
||||
// 获取请求路径下的错误信息
|
||||
let msg = (query.msg as string) || '支付出现错误,请稍后重试'
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.page-container {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
padding: 50px 0;
|
||||
height: 100vh;
|
||||
|
||||
.text-center {
|
||||
h1 {
|
||||
color: #666;
|
||||
padding: 5vh 0;
|
||||
font-size: x-large;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 350px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
</style>
|
36
src/views/result/PaySuccessResult.vue
Normal file
36
src/views/result/PaySuccessResult.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="flex flex-col justify-center page-container">
|
||||
<div class="text-center"> </div>
|
||||
<div class="text-center">
|
||||
<h1 class="text-base text-gray-500">{{ msg }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { unref } from 'vue'
|
||||
|
||||
const { currentRoute } = useRouter()
|
||||
const { query } = unref(currentRoute)
|
||||
|
||||
// 获取请求路径下的错误信息
|
||||
let msg = (query.msg as string) || '支付成功...'
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.page-container {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
padding: 50px 0;
|
||||
height: 100vh;
|
||||
|
||||
.text-center {
|
||||
h1 {
|
||||
color: #666;
|
||||
padding: 5vh 0;
|
||||
font-size: x-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user