feat 聚合支付处理和一些页面编写

This commit is contained in:
bootx
2024-02-11 21:47:27 +08:00
parent 034ab631ce
commit ffc9a0ea7b
21 changed files with 476 additions and 51 deletions

View File

@@ -2,7 +2,7 @@
VITE_PORT = 9100 VITE_PORT = 9100
# 网站根目录 # 网站根目录
VITE_PUBLIC_PATH = / VITE_PUBLIC_PATH = /h5
# 是否删除console # 是否删除console
VITE_DROP_CONSOLE = true VITE_DROP_CONSOLE = true

6
components.d.ts vendored
View File

@@ -7,10 +7,14 @@ export {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
export interface GlobalComponents { 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'] VanButton: typeof import('vant/es')['Button']
VanConfigProvider: typeof import('vant/es')['ConfigProvider'] VanConfigProvider: typeof import('vant/es')['ConfigProvider']
VanIcon: typeof import('vant/es')['Icon']
VanLoading: typeof import('vant/es')['Loading']
VanNavBar: typeof import('vant/es')['NavBar'] VanNavBar: typeof import('vant/es')['NavBar']
VanOverlay: typeof import('vant/es')['Overlay']
VanTabbar: typeof import('vant/es')['Tabbar'] VanTabbar: typeof import('vant/es')['Tabbar']
VanTabbarItem: typeof import('vant/es')['TabbarItem'] VanTabbarItem: typeof import('vant/es')['TabbarItem']
} }

View 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>

View File

@@ -42,7 +42,7 @@
const currentRoute = useRoute() const currentRoute = useRoute()
const getTitle = computed(() => currentRoute.meta.title as string) const getTitle = computed(() => currentRoute.meta.title as string)
console.log(getTitle)
// 菜单 // 菜单
const getMenus = computed(() => const getMenus = computed(() =>
routeStore.menus.filter((item) => { 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) // const getShowTabbar = computed(() => !currentRoute.meta.hiddenTabbar)
// TODO 默认不显示tabbar // TODO 默认不显示tabbar
const getShowTabbar = ref<boolean>(false) const getShowTabbar = ref<boolean>(false)

View File

@@ -20,5 +20,21 @@ export const BusinessRoute: RouteRecordRaw = {
hiddenTabbar: true, 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: '支付失败',
},
},
], ],
} }

View File

@@ -11,35 +11,19 @@ export const DemoRoute: RouteRecordRaw = {
component: Layout, component: Layout,
children: [ children: [
{ {
path: '/cashier/alipay', path: '/aggregate/alipay',
name: 'AlipayCashier', name: 'AliPayAggregate',
component: () => import('@/views/demo/cashier/AlipayCashier.vue'), component: () => import('@/views/demo/aggregate/AliPayAggregate.vue'),
meta: { meta: {
title: '支付宝收银台', title: '支付宝聚合支付',
}, },
}, },
{ {
path: '/cashier/wxJsapiPay', path: '/aggregate/wechatPay',
name: 'WechatJsapiPay', name: 'WechatPayAggregate',
component: () => import('@/views/demo/cashier/WechatJsapiPay.vue'), component: () => import('@/views/demo/aggregate/WechatPayAggregate.vue'),
meta: { meta: {
title: '支付宝收银台', 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: '支付超时',
}, },
}, },
], ],

View File

@@ -7,6 +7,7 @@ import { useRouteStoreWidthOut } from '@/store/modules/route'
import routeModuleList from './modules' import routeModuleList from './modules'
import { BusinessRoute } from '@/router/business' import { BusinessRoute } from '@/router/business'
import { DemoRoute } from '@/router/demo' import { DemoRoute } from '@/router/demo'
import { createRouterGuards } from "@/router/router-guards";
// 普通路由 // 普通路由
export const constantRouter: RouteRecordRaw[] = [ export const constantRouter: RouteRecordRaw[] = [
@@ -30,8 +31,7 @@ const router = createRouter({
export function setupRouter(app: App) { export function setupRouter(app: App) {
app.use(router) app.use(router)
// TODO 不使用路由守卫 createRouterGuards(router)
// createRouterGuards(router)
} }
export default router export default router

View File

@@ -1,6 +1,5 @@
import { isNavigationFailure, Router } from 'vue-router' import { isNavigationFailure, Router } from 'vue-router'
import { useRouteStoreWidthOut } from '@/store/modules/route' import { useRouteStoreWidthOut } from '@/store/modules/route'
import { useUserStoreWidthOut } from '@/store/modules/user'
import { ACCESS_TOKEN } from '@/store/mutation-types' import { ACCESS_TOKEN } from '@/store/mutation-types'
import { storage } from '@/utils/Storage' import { storage } from '@/utils/Storage'
import { PageEnum } from '@/enums/pageEnum' import { PageEnum } from '@/enums/pageEnum'
@@ -11,10 +10,12 @@ const whitePathList = [LOGIN_PATH] // no redirect whitelist
export function createRouterGuards(router: Router) { export function createRouterGuards(router: Router) {
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
// 所有的的页面都可以访问
next()
return
// to: 即将要进入的目标 // to: 即将要进入的目标
// from: 当前导航正要离开的路由 // from: 当前导航正要离开的路由
const userStore = useUserStoreWidthOut()
if (from.path === LOGIN_PATH && to.name === PageEnum.ERROR_PAGE_NAME) { if (from.path === LOGIN_PATH && to.name === PageEnum.ERROR_PAGE_NAME) {
next(PageEnum.BASE_HOME) next(PageEnum.BASE_HOME)
return return

View File

@@ -9,3 +9,11 @@ body {
.van-action-sheet__cancel { .van-action-sheet__cancel {
color: var(--van-primary-color) !important; color: var(--van-primary-color) !important;
} }
// loading 遮罩全屏铺盖
.loading-overlay {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}

View File

@@ -24,7 +24,7 @@
<script setup lang="ts" name="DashboardPage"> <script setup lang="ts" name="DashboardPage">
import { computed } from 'vue' import { computed } from 'vue'
import { useDesignSettingStore } from '@/store/modules/designSetting' import { useDesignSettingStore } from '@/store/modules/designSetting'
import SvgIcon from '@/components/SvgIcon.vue' import SvgIcon from '@/components/SvgIcon/SvgIcon.vue'
import { useGlobSetting } from '@/hooks/setting' import { useGlobSetting } from '@/hooks/setting'
const designStore = useDesignSettingStore() const designStore = useDesignSettingStore()

View 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
}

View 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>

View 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>

View File

@@ -1,11 +1,11 @@
<template>
123
</template>
<script setup lang="ts"> <script setup lang="ts">
</script> </script>
<template>
</template>
<style scoped lang="less"> <style scoped lang="less">
</style> </style>

View File

@@ -1,12 +1,11 @@
<template>
<div>
345
</div>
</template>
<script setup lang="ts"> <script setup lang="ts">
</script> </script>
<template>
</template>
<style scoped lang="less"> <style scoped lang="less">
</style> </style>

View File

@@ -1,11 +1,43 @@
<script setup lang="ts">
</script>
<template> <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> </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> </style>

View File

@@ -5,7 +5,7 @@
</div> </div>
<div class="text-center"> <div class="text-center">
<h1 class="text-base text-gray-500">抱歉你无权访问该页面</h1> <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>
</div> </div>
</template> </template>

View File

@@ -5,7 +5,7 @@
</div> </div>
<div class="text-center"> <div class="text-center">
<h1 class="text-base text-gray-500">抱歉服务器出错了</h1> <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>
</div> </div>
</template> </template>

View 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>

View 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>