feat 支付记录/退款记录/回调记录 查看和对应搜索

This commit is contained in:
xxm
2022-11-30 23:28:20 +08:00
parent 06fcb97871
commit 9d1488b324
15 changed files with 646 additions and 642 deletions

View File

@@ -125,7 +125,6 @@
"rimraf": "^3.0.2",
"rollup": "^2.70.2",
"rollup-plugin-visualizer": "^5.6.0",
"sass": "^1.55.0",
"stylelint": "^14.7.1",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-recommended": "^7.0.0",

32
src/api/common/Pay.ts Normal file
View File

@@ -0,0 +1,32 @@
import { defHttp } from '/@/utils/http/axios'
/**
* 取消支付(支付id)
*/
export function cancelByPaymentId(paymentId) {
return defHttp.post({
url: '/uni_pay/cancelByPaymentId',
params: { paymentId },
})
}
/**
* 退款
*/
export function refund(obj) {
return defHttp.post({
url: '/uni_pay/refund',
data: obj,
})
}
/**
* 刷新指定业务id的支付单状态
*/
export function syncByBusinessId(businessId) {
return defHttp.post({
url: '/uni_pay/syncByBusinessId',
method: 'POST',
params: { businessId },
})
}

View File

@@ -75,11 +75,6 @@
function handleReset() {
queryParams = []
}
// 删除
function handleDel(index) {
queryParams.splice(index, 1)
}
defineExpose({ show, handleReset })
</script>

View File

@@ -1,132 +0,0 @@
<template>
<basic-modal
v-bind="$attrs"
:loading="confirmLoading"
:title="title"
:width="modalWidth"
:visible="visible"
:mask-closable="showable"
@cancel="handleCancel"
>
<a-form
class="small-from-item"
ref="formRef"
:model="form"
:rules="rules"
:validate-trigger="['blur', 'change']"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="主键" :hidden="true">
<a-input v-model:value="form.id" :disabled="showable" />
</a-form-item>
<a-form-item label="支付号" name="paymentId">
<a-input v-model:value="form.paymentId" :disabled="showable" placeholder="请输入支付号" />
</a-form-item>
<a-form-item label="通知消息" name="notifyInfo">
<a-input v-model:value="form.notifyInfo" :disabled="showable" placeholder="请输入通知消息" />
</a-form-item>
<a-form-item label="支付通道" name="payChannel">
<a-input v-model:value="form.payChannel" :disabled="showable" placeholder="请输入支付通道" />
</a-form-item>
<a-form-item label="处理状态" name="status">
<a-input v-model:value="form.status" :disabled="showable" placeholder="请输入处理状态" />
</a-form-item>
<a-form-item label="提示信息" name="msg">
<a-input v-model:value="form.msg" :disabled="showable" placeholder="请输入提示信息" />
</a-form-item>
<a-form-item label="回调时间" name="notifyTime">
<a-input v-model:value="form.notifyTime" :disabled="showable" placeholder="请输入回调时间" />
</a-form-item>
</a-form>
<template #footer>
<a-space>
<a-button key="cancel" @click="handleCancel">取消</a-button>
<a-button v-if="!showable" key="forward" :loading="confirmLoading" type="primary" @click="handleOk">保存</a-button>
</a-space>
</template>
</basic-modal>
</template>
<script lang="ts" setup>
import { nextTick, reactive } from 'vue'
import { $ref } from 'vue/macros'
import useFormEdit from '/@/hooks/bootx/useFormEdit'
import { add, get, update, PayNotifyRecord } from './PayNotifyRecord.api'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { FormEditType } from '/@/enums/formTypeEnum'
import { BasicModal } from '/@/components/Modal'
const {
initFormEditType,
handleCancel,
search,
labelCol,
wrapperCol,
modalWidth,
title,
confirmLoading,
visible,
editable,
showable,
formEditType,
} = useFormEdit()
// 表单
const formRef = $ref<FormInstance>()
let form = $ref<PayNotifyRecord>({
id: null,
paymentId: null,
notifyInfo: null,
payChannel: null,
status: null,
msg: null,
notifyTime: null,
})
// 校验
const rules = reactive({} as Record<string, Rule[]>)
// 事件
const emits = defineEmits(['ok'])
// 入口
function init(id, editType: FormEditType) {
initFormEditType(editType)
resetForm()
getInfo(id, editType)
}
// 获取信息
function getInfo(id, editType: FormEditType) {
if ([FormEditType.Edit, FormEditType.Show].includes(editType)) {
confirmLoading.value = true
get(id).then(({ data }) => {
form = data
confirmLoading.value = false
})
} else {
confirmLoading.value = false
}
}
// 保存
function handleOk() {
formRef?.validate().then(async () => {
confirmLoading.value = true
if (formEditType.value === FormEditType.Add) {
await add(form)
} else if (formEditType.value === FormEditType.Edit) {
await update(form)
}
confirmLoading.value = false
handleCancel()
emits('ok')
})
}
// 重置表单
function resetForm() {
nextTick(() => {
formRef.resetFields()
})
}
defineExpose({
init,
})
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,90 @@
<template>
<basic-modal
title="查看"
v-bind="$attrs"
:loading="confirmLoading"
:width="modalWidth"
:visible="visible"
:mask-closable="showable"
@cancel="handleCancel"
>
<a-spin :spinning="confirmLoading">
<a-descriptions title="" :column="{ md: 1, sm: 1, xs: 1 }">
<a-descriptions-item label="业务id">
{{ form.paymentId }}
</a-descriptions-item>
<a-descriptions-item label="支付通道">
{{ dictConvert('PayChannel', form.payChannel) }}
</a-descriptions-item>
<a-descriptions-item label="通知消息">
<json-preview :data="JSON.parse(form.notifyInfo || '{}')" />
</a-descriptions-item>
<a-descriptions-item label="状态">
{{ form.status === 1 ? '成功' : '失败' }}
</a-descriptions-item>
<a-descriptions-item label="提示消息">
{{ form.msg }}
</a-descriptions-item>
<a-descriptions-item label="通知时间">
{{ form.notifyTime }}
</a-descriptions-item>
</a-descriptions>
</a-spin>
<template #footer>
<a-button key="cancel" @click="handleCancel">取消</a-button>
</template>
</basic-modal>
</template>
<script lang="ts" setup>
import { $ref } from 'vue/macros'
import useFormEdit from '/@/hooks/bootx/useFormEdit'
import { get, PayNotifyRecord } from './PayNotifyRecord.api'
import { FormInstance } from 'ant-design-vue/lib/form'
import { FormEditType } from '/@/enums/formTypeEnum'
import { BasicModal } from '/@/components/Modal'
import { useDict } from '/@/hooks/bootx/useDict'
import JsonPreview from '/@/components/CodeEditor/src/json-preview/JsonPreview.vue'
const {
initFormEditType,
handleCancel,
search,
labelCol,
wrapperCol,
modalWidth,
title,
confirmLoading,
visible,
editable,
showable,
formEditType,
} = useFormEdit()
const { dictConvert } = useDict()
// 表单
const formRef = $ref<FormInstance>()
let form = $ref<PayNotifyRecord>({
id: null,
paymentId: '',
notifyInfo: '',
payChannel: 1,
status: 1,
msg: '',
notifyTime: '',
})
// 入口
function init(id, editType: FormEditType) {
visible.value = true
confirmLoading.value = true
get(id).then(({ data }) => {
form = data
confirmLoading.value = false
})
}
defineExpose({
init,
})
</script>
<style lang="less" scoped></style>

View File

@@ -4,17 +4,10 @@
<b-query :query-params="model.queryParam" :fields="fields" @query="queryPage" @reset="resetQueryParams" />
</div>
<div class="m-3 p-3 bg-white">
<vxe-toolbar ref="xToolbar" custom :refresh="{ query: queryPage }">
<template #buttons>
<a-space>
<a-button type="primary" pre-icon="ant-design:plus-outlined" @click="add">新建</a-button>
</a-space>
</template>
</vxe-toolbar>
<vxe-toolbar ref="xToolbar" custom :refresh="{ query: queryPage }" />
<vxe-table row-id="id" ref="xTable" :data="pagination.records" :loading="loading">
<vxe-column type="seq" title="序号" width="60" />
<vxe-column field="paymentId" title="支付号" />
<vxe-column field="notifyInfo" title="通知消息" />
<vxe-column field="payChannel" title="支付通道">
<template #default="{ row }">
{{ dictConvert('PayChannel', row.payChannel) }}
@@ -43,7 +36,7 @@
:total="pagination.total"
@page-change="handleTableChange"
/>
<pay-notify-record-edit ref="payNotifyRecordEdit" @ok="queryPage" />
<pay-notify-record-info ref="payNotifyRecordInfo" />
</div>
</div>
</template>
@@ -51,26 +44,42 @@
<script lang="ts" setup>
import { onMounted } from 'vue'
import { $ref } from 'vue/macros'
import { del, page } from './PayNotifyRecord.api'
import { page } from './PayNotifyRecord.api'
import useTablePage from '/@/hooks/bootx/useTablePage'
import PayNotifyRecordEdit from './PayNotifyRecordEdit.vue'
import { VxeTableInstance, VxeToolbarInstance } from 'vxe-table'
import BQuery from '/@/components/Bootx/Query/BQuery.vue'
import { FormEditType } from '/@/enums/formTypeEnum'
import { useMessage } from '/@/hooks/web/useMessage'
import { QueryField } from '/@/components/Bootx/Query/Query'
import { LIST, QueryField, STRING } from '/@/components/Bootx/Query/Query'
import { useDict } from '/@/hooks/bootx/useDict'
import PayNotifyRecordInfo from './PayNotifyRecordInfo.vue'
// 使用hooks
const { handleTableChange, pageQueryResHandel, resetQueryParams, pagination, pages, model, loading } = useTablePage(queryPage)
const { notification, createMessage, createConfirm } = useMessage()
const { dictConvert } = useDict()
const { dictConvert, dictDropDownNumber } = useDict()
// 查询条件
const fields = [] as QueryField[]
const fields = [
{ field: 'paymentId', type: STRING, name: '支付单号', placeholder: '请输入支付单号' },
{
field: 'payChannel',
type: LIST,
name: '支付通道',
placeholder: '请选择支付通道',
selectList: dictDropDownNumber('AsyncPayChannel'),
},
{
field: 'status',
type: LIST,
name: '处理状态',
placeholder: '请选择消息处理状态',
selectList: dictDropDownNumber('PayNotifyProcess'),
},
] as QueryField[]
const xTable = $ref<VxeTableInstance>()
const xToolbar = $ref<VxeToolbarInstance>()
const payNotifyRecordEdit = $ref<any>()
const payNotifyRecordInfo = $ref<any>()
onMounted(() => {
vxeBind()
@@ -90,25 +99,9 @@
pageQueryResHandel(data)
})
}
// 新增
function add() {
payNotifyRecordEdit.init(null, FormEditType.Add)
}
// 编辑
function edit(record) {
payNotifyRecordEdit.init(record.id, FormEditType.Edit)
}
// 查看
function show(record) {
payNotifyRecordEdit.init(record.id, FormEditType.Show)
}
// 删除
function remove(record) {
del(record.id).then(() => {
createMessage.success('删除成功')
queryPage()
})
payNotifyRecordInfo.init(record.id)
}
</script>

View File

@@ -1,6 +1,7 @@
import { defHttp } from '/@/utils/http/axios'
import { PageResult, Result } from '/#/axios'
import { BaseEntity } from '/#/web'
import { RefundableInfo } from '/@/views/modules/payment/order/refund/RefundRecord.api'
/**
* 分页
@@ -32,32 +33,13 @@ export function get(id) {
}
/**
* 添加
* 超级查询
*/
export function add(obj: Payment) {
return defHttp.post({
url: '/payment/add',
data: obj,
})
}
/**
* 更新
*/
export function update(obj: Payment) {
return defHttp.post({
url: '/payment/update',
data: obj,
})
}
/**
* 删除
*/
export function del(id) {
return defHttp.delete({
url: '/payment/delete',
params: { id },
export function superPage(params, queryParams) {
return defHttp.post<Result<PageResult<Payment>>>({
url: '/payment/superPage',
params: params,
data: queryParams,
})
}
@@ -90,9 +72,9 @@ export interface Payment extends BaseEntity {
// 异步支付方式
asyncPayChannel?: number
// 支付通道信息列表
payChannelInfo?: string
payChannelInfo?: PayChannelInfo[]
// 可退款信息
refundableInfo?: string
refundableInfo?: RefundableInfo[]
// 支付时间
payTime?: string
// 过期时间
@@ -100,3 +82,17 @@ export interface Payment extends BaseEntity {
// 客户ip
clientIp?: string
}
/**
* 支付通道信息
*/
export interface PayChannelInfo {
// 支付通道
payChannel?: number
// 支付方式
payWay?: number
// 金额
amount?: number
// 扩展参数的json字符串
extraParamsJson?: string
}

View File

@@ -1,176 +0,0 @@
<template>
<basic-modal
v-bind="$attrs"
:loading="confirmLoading"
:title="title"
:width="modalWidth"
:visible="visible"
:mask-closable="showable"
@cancel="handleCancel"
>
<a-form
class="small-from-item"
ref="formRef"
:model="form"
:rules="rules"
:validate-trigger="['blur', 'change']"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="主键" :hidden="true">
<a-input v-model:value="form.id" :disabled="showable" />
</a-form-item>
<a-form-item label="用户id" name="userId">
<a-input v-model:value="form.userId" :disabled="showable" placeholder="请输入用户id" />
</a-form-item>
<a-form-item label="业务id" name="businessId">
<a-input v-model:value="form.businessId" :disabled="showable" placeholder="请输入业务id" />
</a-form-item>
<a-form-item label="标题" name="title">
<a-input v-model:value="form.title" :disabled="showable" placeholder="请输入标题" />
</a-form-item>
<a-form-item label="描述" name="description">
<a-input v-model:value="form.description" :disabled="showable" placeholder="请输入描述" />
</a-form-item>
<a-form-item label="金额" name="amount">
<a-input v-model:value="form.amount" :disabled="showable" placeholder="请输入金额" />
</a-form-item>
<a-form-item label="可退款余额" name="refundableBalance">
<a-input v-model:value="form.refundableBalance" :disabled="showable" placeholder="请输入可退款余额" />
</a-form-item>
<a-form-item label="支付状态" name="payStatus">
<a-input v-model:value="form.payStatus" :disabled="showable" placeholder="请输入支付状态" />
</a-form-item>
<a-form-item label="错误码" name="errorCode">
<a-input v-model:value="form.errorCode" :disabled="showable" placeholder="请输入错误码" />
</a-form-item>
<a-form-item label="错误信息" name="errorMsg">
<a-input v-model:value="form.errorMsg" :disabled="showable" placeholder="请输入错误信息" />
</a-form-item>
<a-form-item label="支付信息" name="payTypeInfo">
<a-input v-model:value="form.payTypeInfo" :disabled="showable" placeholder="请输入支付信息" />
</a-form-item>
<a-form-item label="是否是异步支付" name="asyncPayMode">
<a-input v-model:value="form.asyncPayMode" :disabled="showable" placeholder="请输入是否是异步支付" />
</a-form-item>
<a-form-item label="异步支付方式" name="asyncPayChannel">
<a-input v-model:value="form.asyncPayChannel" :disabled="showable" placeholder="请输入异步支付方式" />
</a-form-item>
<a-form-item label="支付通道信息列表" name="payChannelInfo">
<a-input v-model:value="form.payChannelInfo" :disabled="showable" placeholder="请输入支付通道信息列表" />
</a-form-item>
<a-form-item label="可退款信息" name="refundableInfo">
<a-input v-model:value="form.refundableInfo" :disabled="showable" placeholder="请输入可退款信息" />
</a-form-item>
<a-form-item label="支付时间" name="payTime">
<a-input v-model:value="form.payTime" :disabled="showable" placeholder="请输入支付时间" />
</a-form-item>
<a-form-item label="过期时间" name="expiredTime">
<a-input v-model:value="form.expiredTime" :disabled="showable" placeholder="请输入过期时间" />
</a-form-item>
<a-form-item label="客户ip" name="clientIp">
<a-input v-model:value="form.clientIp" :disabled="showable" placeholder="请输入客户ip" />
</a-form-item>
</a-form>
<template #footer>
<a-space>
<a-button key="cancel" @click="handleCancel">取消</a-button>
<a-button v-if="!showable" key="forward" :loading="confirmLoading" type="primary" @click="handleOk">保存</a-button>
</a-space>
</template>
</basic-modal>
</template>
<script lang="ts" setup>
import { nextTick, reactive } from 'vue'
import { $ref } from 'vue/macros'
import useFormEdit from '/@/hooks/bootx/useFormEdit'
import { add, get, update, Payment } from './Payment.api'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { FormEditType } from '/@/enums/formTypeEnum'
import { BasicModal } from '/@/components/Modal'
const {
initFormEditType,
handleCancel,
search,
labelCol,
wrapperCol,
modalWidth,
title,
confirmLoading,
visible,
editable,
showable,
formEditType,
} = useFormEdit()
// 表单
const formRef = $ref<FormInstance>()
let form = $ref<Payment>({
id: null,
userId: null,
businessId: null,
title: null,
description: null,
amount: null,
refundableBalance: null,
payStatus: null,
errorCode: null,
errorMsg: null,
payTypeInfo: null,
asyncPayMode: null,
asyncPayChannel: null,
payChannelInfo: null,
refundableInfo: null,
payTime: null,
expiredTime: null,
clientIp: null,
})
// 校验
const rules = reactive({} as Record<string, Rule[]>)
// 事件
const emits = defineEmits(['ok'])
// 入口
function init(id, editType: FormEditType) {
initFormEditType(editType)
resetForm()
getInfo(id, editType)
}
// 获取信息
function getInfo(id, editType: FormEditType) {
if ([FormEditType.Edit, FormEditType.Show].includes(editType)) {
confirmLoading.value = true
get(id).then(({ data }) => {
form = data
confirmLoading.value = false
})
} else {
confirmLoading.value = false
}
}
// 保存
function handleOk() {
formRef?.validate().then(async () => {
confirmLoading.value = true
if (formEditType.value === FormEditType.Add) {
await add(form)
} else if (formEditType.value === FormEditType.Edit) {
await update(form)
}
confirmLoading.value = false
handleCancel()
emits('ok')
})
}
// 重置表单
function resetForm() {
nextTick(() => {
formRef.resetFields()
})
}
defineExpose({
init,
})
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,124 @@
<template>
<basic-modal
title="查看"
v-bind="$attrs"
:loading="confirmLoading"
:width="modalWidth"
:visible="visible"
:mask-closable="showable"
@cancel="handleCancel"
>
<a-spin :spinning="confirmLoading">
<a-descriptions title="" :column="{ md: 2, sm: 1, xs: 1 }">
<a-descriptions-item label="用户id">
{{ form.userId }}
</a-descriptions-item>
<a-descriptions-item label="标题">
{{ form.title }}
</a-descriptions-item>
<a-descriptions-item label="业务id">
{{ form.businessId }}
</a-descriptions-item>
<a-descriptions-item label="金额">
{{ form.amount }}
</a-descriptions-item>
<a-descriptions-item label="可退余额">
{{ form.refundableBalance }}
</a-descriptions-item>
<a-descriptions-item label="支付状态">
{{ dictConvert('PayStatus', form.payStatus) }}
</a-descriptions-item>
<a-descriptions-item label="是否是异步支付">
{{ form.asyncPayMode ? '是' : '否' }}
</a-descriptions-item>
<a-descriptions-item label="异步支付方式">
{{ dictConvert('PayChannel', form.asyncPayChannel) }}
</a-descriptions-item>
<a-descriptions-item label="支付信息">
<a-tag v-for="o in form.payChannelInfo" :key="o.payChannel">{{ dictConvert('PayChannel', o.payChannel) }}: {{ o.amount }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="可退款信息">
<a-tag v-for="o in form.refundableInfo" :key="o.payChannel">{{ dictConvert('PayChannel', o.payChannel) }}: {{ o.amount }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="客户IP">
{{ form.clientIp }}
</a-descriptions-item>
<a-descriptions-item label="描述">
{{ form.description }}
</a-descriptions-item>
<a-descriptions-item label="错误码">
{{ form.errorCode }}
</a-descriptions-item>
<a-descriptions-item label="错误信息">
{{ form.errorMsg }}
</a-descriptions-item>
</a-descriptions>
</a-spin>
<template #footer>
<a-space>
<a-button key="cancel" @click="handleCancel">取消</a-button>
</a-space>
</template>
</basic-modal>
</template>
<script lang="ts" setup>
import { $ref } from 'vue/macros'
import useFormEdit from '/@/hooks/bootx/useFormEdit'
import { get, Payment } from './Payment.api'
import { FormInstance } from 'ant-design-vue/lib/form'
import { FormEditType } from '/@/enums/formTypeEnum'
import { BasicModal } from '/@/components/Modal'
import { useDict } from "/@/hooks/bootx/useDict";
const {
initFormEditType,
handleCancel,
search,
labelCol,
wrapperCol,
modalWidth,
title,
confirmLoading,
visible,
editable,
showable,
formEditType,
} = useFormEdit()
const { dictConvert } = useDict()
// 表单
const formRef = $ref<FormInstance>()
let form = $ref<Payment>({
id: '',
userId: '',
businessId: '',
amount: 0,
refundableBalance: 0,
payStatus: 0,
title: '',
clientIp: '',
description: '',
errorCode: '',
asyncPayMode: true,
asyncPayChannel: 0,
payChannelInfo: [],
refundableInfo: [],
payTime: '',
expiredTime: '',
})
// 入口
function init(id) {
visible.value = true
confirmLoading.value = true
get(id).then(({ data }) => {
form = data
confirmLoading.value = false
})
}
// 获取信息
defineExpose({
init,
})
</script>
<style lang="less" scoped></style>

View File

@@ -6,12 +6,25 @@
<div class="m-3 p-3 bg-white">
<vxe-toolbar ref="xToolbar" custom :refresh="{ query: queryPage }">
<template #buttons>
<a-space>
<a-button type="primary" pre-icon="ant-design:plus-outlined" @click="add">新建</a-button>
</a-space>
<b-super-query
:queryState="superQueryFlag"
:fields="queryFields"
:width="880"
button-title="超级查询器"
model-title="超级查询器"
@query="changeSuperQuery"
@reset="resetQuery"
/>
</template>
</vxe-toolbar>
<vxe-table row-id="id" ref="xTable" :data="pagination.records" :loading="loading">
<vxe-table
row-id="id"
ref="xTable"
:sort-config="{ remote: true, trigger: 'cell', defaultSort: { field: 'createTime', order: 'desc' } }"
:data="pagination.records"
:loading="loading"
@sort-change="sortChange"
>
<vxe-column type="seq" title="序号" width="60" />
<vxe-column field="businessId" title="业务ID" />
<vxe-column field="title" title="标题" />
@@ -39,13 +52,22 @@
<a-link @click="show(row)">查看</a-link>
</span>
<a-divider type="vertical" />
<span>
<a-link @click="edit(row)">编辑</a-link>
</span>
<a-divider type="vertical" />
<a-popconfirm title="是否删除" @confirm="remove(row)" okText="是" cancelText="否">
<a-link danger>删除</a-link>
</a-popconfirm>
<a-dropdown>
<a> 更多 <icon icon="ant-design:down-outlined" :size="12" /></a>
<template #overlay>
<a-menu>
<a-menu-item>
<a-link @click="sync(row)">刷新信息</a-link>
</a-menu-item>
<a-menu-item v-if="[0].includes(row.payStatus)">
<a-link @click="remove(row)" danger>关闭</a-link>
</a-menu-item>
<a-menu-item v-if="[1, 4].includes(row.payStatus) && row.refundableBalance > 0">
<a-link @click="refund(row)" danger>退款</a-link>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</vxe-column>
</vxe-table>
@@ -57,7 +79,8 @@
:total="pagination.total"
@page-change="handleTableChange"
/>
<payment-edit ref="paymentEdit" @ok="queryPage" />
<payment-info ref="paymentInfo" />
<refund-model ref="refundModel" @ok="queryPage" />
</div>
</div>
</template>
@@ -65,27 +88,53 @@
<script lang="ts" setup>
import { onMounted } from 'vue'
import { $ref } from 'vue/macros'
import { del, page } from './Payment.api'
import { page, superPage } from './Payment.api'
import useTablePage from '/@/hooks/bootx/useTablePage'
import PaymentEdit from './PaymentEdit.vue'
import PaymentInfo from './PaymentInfo.vue'
import { VxeTableInstance, VxeToolbarInstance } from 'vxe-table'
import BQuery from '/@/components/Bootx/Query/BQuery.vue'
import { FormEditType } from '/@/enums/formTypeEnum'
import { useMessage } from '/@/hooks/web/useMessage'
import { QueryField } from '/@/components/Bootx/Query/Query'
import { useDict } from "/@/hooks/bootx/useDict";
import { BOOLEAN, DATE_TIME, LIST, NUMBER, QueryField, QueryParam, STRING } from '/@/components/Bootx/Query/Query'
import { useDict } from '/@/hooks/bootx/useDict'
import { cancelByPaymentId, syncByBusinessId } from '/@/api/common/Pay'
import RefundModel from './RefundModel.vue'
import BSuperQuery from '/@/components/Bootx/SuperQuery/BSuperQuery.vue'
// 使用hooks
const { handleTableChange, pageQueryResHandel, resetQueryParams, pagination, pages, model, loading } = useTablePage(queryPage)
const { handleTableChange, pageQueryResHandel, resetQuery, resetQueryParams, pagination, pages, model, loading, superQueryFlag } =
useTablePage(queryPage)
const { notification, createMessage, createConfirm } = useMessage()
const { dictConvert } = useDict()
const { dictConvert, dictDropDownNumber } = useDict()
// 查询条件
const fields = [] as QueryField[]
const fields = [
{ field: 'paymentId', type: STRING, name: '支付单号', placeholder: '请输入支付单号' },
{ field: 'businessId', type: STRING, name: '业务ID', placeholder: '请输入业务ID' },
{ field: 'title', type: STRING, name: '标题', placeholder: '请输入标题' },
] as QueryField[]
const queryFields = [
{ field: 'id', name: '支付ID', type: STRING },
{ field: 'businessId', name: '业务ID', type: STRING },
{ field: 'userId', name: '用户ID', type: STRING },
{ field: 'title', name: '标题', type: STRING },
{ field: 'amount', name: '金额', type: NUMBER, precision: 2 },
{ field: 'errorCode', name: '错误码', type: STRING },
{ field: 'asyncPayMode', name: '异步支付', type: BOOLEAN },
{ field: 'asyncPayChannel', name: '异步支付方式', type: LIST, selectList: dictDropDownNumber('PayChannel') },
{ field: 'payTime', name: '支付时间', type: DATE_TIME },
{ field: 'payStatus', name: '支付状态', type: LIST, selectList: dictDropDownNumber('PayStatus') },
] as QueryField[]
let superQueryParam = $ref<QueryParam[]>([])
let sortParam = $ref({
sortField: 'createTime',
asc: false,
})
const xTable = $ref<VxeTableInstance>()
const xToolbar = $ref<VxeToolbarInstance>()
const paymentEdit = $ref<any>()
const paymentInfo = $ref<any>()
const refundModel = $ref<any>()
onMounted(() => {
vxeBind()
@@ -95,36 +144,79 @@
xTable?.connect(xToolbar as VxeToolbarInstance)
}
function init() {
if (superQueryFlag.value) {
superQuery()
} else {
queryPage()
}
}
// 分页查询
function queryPage() {
loading.value = true
page({
...model.queryParam,
...pages,
...sortParam,
}).then(({ data }) => {
pageQueryResHandel(data)
})
}
// 新增
function add() {
paymentEdit.init(null, FormEditType.Add)
// 排序条件变动
function sortChange({ order, property }) {
sortParam.sortField = order ? property : null
sortParam.asc = order === 'asc'
init()
}
// 编辑
function edit(record) {
paymentEdit.init(record.id, FormEditType.Edit)
// 超级查询条件变动
function changeSuperQuery(queryParams) {
superQueryParam = queryParams
superQuery()
}
// 查看
function show(record) {
paymentEdit.init(record.id, FormEditType.Show)
// 超级查询
function superQuery() {
superQueryFlag.value = true
loading.value = true
superPage(pages, {
queryParams: superQueryParam,
queryOrders: [sortParam],
}).then(({ data }) => {
pageQueryResHandel(data)
})
}
// 删除
function remove(record) {
del(record.id).then(() => {
createMessage.success('删除成功')
// 查看
function show(record) {
paymentInfo.init(record.id)
}
// 同步信息
function sync(record) {
loading.value = true
syncByBusinessId(record.businessId).then(() => {
createMessage.success('信息同步成功')
queryPage()
})
}
// 关闭支付
function cancel(record) {
createConfirm({
iconType: 'warning',
title: '警告',
content: '是否关闭支付',
onOk: () => {
cancelByPaymentId(record.id).then(() => {
queryPage()
})
},
})
}
// 退款
function refund(record) {
refundModel.init(record.id)
}
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,88 @@
<template>
<basic-modal
title="退款申请"
v-bind="$attrs"
:loading="confirmLoading"
:width="modalWidth"
:visible="visible"
:mask-closable="showable"
@cancel="handleCancel"
>
<a-spin :spinning="confirmLoading">
<a-form ref="formRef" :model="form" :label-col="labelCol" :wrapper-col="wrapperCol">
<template :key="o.payChannel" v-for="o in form.refundModeParams">
<a-form-item :label="dictConvert('PayChannel', o.payChannel)" name="name">
<a-input-number :min="0.01" :max="o.amount" :precision="2" v-model:value="o.amount" />
</a-form-item>
</template>
</a-form>
</a-spin>
</basic-modal>
</template>
<script lang="ts" setup>
import { get } from '/@/views/modules/payment/order/payment/Payment.api'
import { $ref } from 'vue/macros'
import useFormEdit from '/@/hooks/bootx/useFormEdit'
import { useDict } from '/@/hooks/bootx/useDict'
import { refund } from '/@/api/common/Pay'
import { RefundableInfo } from '/@/views/modules/payment/order/refund/RefundRecord.api'
import BasicModal from '/@/components/Modal/src/BasicModal.vue'
import { FormInstance } from 'ant-design-vue/lib/form'
import { useMessage } from '/@/hooks/web/useMessage'
const {
initFormEditType,
handleCancel,
search,
labelCol,
wrapperCol,
modalWidth,
title,
confirmLoading,
visible,
editable,
showable,
formEditType,
} = useFormEdit()
const { createMessage, createConfirm } = useMessage()
const { dictConvert } = useDict()
const formRef = $ref<FormInstance>()
let form = $ref({
businessId: '',
// 可退款明细
refundModeParams: [] as RefundableInfo[],
})
const emits = defineEmits(['ok'])
function init(id) {
confirmLoading.value = true
visible.value = true
get(id).then(({ data }) => {
const { businessId, refundableInfo } = data
form.businessId = businessId as string
form.refundModeParams = [...(refundableInfo as RefundableInfo[])]
confirmLoading.value = false
})
}
function handleOk() {
createConfirm({
iconType: 'warning',
title: '警告',
content: '确实要申请退款!',
onOk: () => {
confirmLoading.value = true
refund(form).then(() => {
visible.value = false
createMessage.success('退款请求已提交')
emits('ok')
})
},
})
confirmLoading.value = true
}
defineExpose({ init })
</script>
<style scoped></style>

View File

@@ -12,15 +12,6 @@ export function page(params) {
})
}
/**
* 查询全部
*/
export function findAll() {
return defHttp.get<Result<RefundRecord[]>>({
url: '/pay/refund/findAll',
})
}
/**
* 获取单条
*/
@@ -31,36 +22,6 @@ export function get(id) {
})
}
/**
* 添加
*/
export function add(obj: RefundRecord) {
return defHttp.post({
url: '/pay/refund/add',
data: obj,
})
}
/**
* 更新
*/
export function update(obj: RefundRecord) {
return defHttp.post({
url: '/pay/refund/update',
data: obj,
})
}
/**
* 删除
*/
export function del(id) {
return defHttp.delete({
url: '/pay/refund/delete',
params: { id },
})
}
/**
* 退款记录
*/
@@ -80,7 +41,7 @@ export interface RefundRecord extends BaseEntity {
// 剩余可退款金额
refundableBalance?: number
// 可退款信息
refundableInfo?: string
refundableInfo?: RefundableInfo[]
// 退款状态
refundStatus?: number
// 支付时间
@@ -92,3 +53,13 @@ export interface RefundRecord extends BaseEntity {
// 错误信息
errorMsg?: string
}
/**
* 可退款信息
*/
export interface RefundableInfo {
// 支付通道
payChannel?: number
// 金额
amount?: number
}

View File

@@ -1,160 +0,0 @@
<template>
<basic-modal
v-bind="$attrs"
:loading="confirmLoading"
:title="title"
:width="modalWidth"
:visible="visible"
:mask-closable="showable"
@cancel="handleCancel"
>
<a-form
class="small-from-item"
ref="formRef"
:model="form"
:rules="rules"
:validate-trigger="['blur', 'change']"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="主键" :hidden="true">
<a-input v-model:value="form.id" :disabled="showable" />
</a-form-item>
<a-form-item label="支付记录id" name="paymentId">
<a-input v-model:value="form.paymentId" :disabled="showable" placeholder="请输入支付记录id" />
</a-form-item>
<a-form-item label="关联业务id" name="businessId">
<a-input v-model:value="form.businessId" :disabled="showable" placeholder="请输入关联业务id" />
</a-form-item>
<a-form-item label="异步方式关联退款请求号" name="refundRequestNo">
<a-input v-model:value="form.refundRequestNo" :disabled="showable" placeholder="请输入异步方式关联退款请求号" />
</a-form-item>
<a-form-item label="用户id" name="userId">
<a-input v-model:value="form.userId" :disabled="showable" placeholder="请输入用户id" />
</a-form-item>
<a-form-item label="标题" name="title">
<a-input v-model:value="form.title" :disabled="showable" placeholder="请输入标题" />
</a-form-item>
<a-form-item label="金额" name="amount">
<a-input v-model:value="form.amount" :disabled="showable" placeholder="请输入金额" />
</a-form-item>
<a-form-item label="剩余可退款金额" name="refundableBalance">
<a-input v-model:value="form.refundableBalance" :disabled="showable" placeholder="请输入剩余可退款金额" />
</a-form-item>
<a-form-item label="可退款信息" name="refundableInfo">
<a-input v-model:value="form.refundableInfo" :disabled="showable" placeholder="请输入可退款信息" />
</a-form-item>
<a-form-item label="退款状态" name="refundStatus">
<a-input v-model:value="form.refundStatus" :disabled="showable" placeholder="请输入退款状态" />
</a-form-item>
<a-form-item label="支付时间" name="refundTime">
<a-input v-model:value="form.refundTime" :disabled="showable" placeholder="请输入支付时间" />
</a-form-item>
<a-form-item label="客户ip" name="clientIp">
<a-input v-model:value="form.clientIp" :disabled="showable" placeholder="请输入客户ip" />
</a-form-item>
<a-form-item label="错误码" name="errorCode">
<a-input v-model:value="form.errorCode" :disabled="showable" placeholder="请输入错误码" />
</a-form-item>
<a-form-item label="错误信息" name="errorMsg">
<a-input v-model:value="form.errorMsg" :disabled="showable" placeholder="请输入错误信息" />
</a-form-item>
</a-form>
<template #footer>
<a-space>
<a-button key="cancel" @click="handleCancel">取消</a-button>
<a-button v-if="!showable" key="forward" :loading="confirmLoading" type="primary" @click="handleOk">保存</a-button>
</a-space>
</template>
</basic-modal>
</template>
<script lang="ts" setup>
import { nextTick, reactive } from 'vue'
import { $ref } from 'vue/macros'
import useFormEdit from '/@/hooks/bootx/useFormEdit'
import { add, get, update, RefundRecord } from './RefundRecord.api'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { FormEditType } from '/@/enums/formTypeEnum'
import { BasicModal } from '/@/components/Modal'
const {
initFormEditType,
handleCancel,
search,
labelCol,
wrapperCol,
modalWidth,
title,
confirmLoading,
visible,
editable,
showable,
formEditType,
} = useFormEdit()
// 表单
const formRef = $ref<FormInstance>()
let form = $ref<RefundRecord>({
id: null,
paymentId: null,
businessId: null,
refundRequestNo: null,
userId: null,
title: null,
amount: null,
refundableBalance: null,
refundableInfo: null,
refundStatus: null,
refundTime: null,
clientIp: null,
errorCode: null,
errorMsg: null,
})
// 校验
const rules = reactive({} as Record<string, Rule[]>)
// 事件
const emits = defineEmits(['ok'])
// 入口
function init(id, editType: FormEditType) {
initFormEditType(editType)
resetForm()
getInfo(id, editType)
}
// 获取信息
function getInfo(id, editType: FormEditType) {
if ([FormEditType.Edit, FormEditType.Show].includes(editType)) {
confirmLoading.value = true
get(id).then(({ data }) => {
form = data
confirmLoading.value = false
})
} else {
confirmLoading.value = false
}
}
// 保存
function handleOk() {
formRef?.validate().then(async () => {
confirmLoading.value = true
if (formEditType.value === FormEditType.Add) {
await add(form)
} else if (formEditType.value === FormEditType.Edit) {
await update(form)
}
confirmLoading.value = false
handleCancel()
emits('ok')
})
}
// 重置表单
function resetForm() {
nextTick(() => {
formRef.resetFields()
})
}
defineExpose({
init,
})
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,111 @@
<template>
<basic-modal
title="查看"
v-bind="$attrs"
:loading="confirmLoading"
:width="modalWidth"
:visible="visible"
:mask-closable="showable"
@cancel="handleCancel"
>
<a-descriptions title="" :column="{ md: 1, sm: 1, xs: 1 }">
<a-descriptions-item label="付款记录ID">
{{ form.paymentId }}
</a-descriptions-item>
<a-descriptions-item label="业务ID">
{{ form.businessId }}
</a-descriptions-item>
<a-descriptions-item label="多次退款业务号">
{{ form.refundRequestNo }}
</a-descriptions-item>
<a-descriptions-item label="标题">
{{ form.title }}
</a-descriptions-item>
<a-descriptions-item label="退款金额">
{{ form.amount }}
</a-descriptions-item>
<a-descriptions-item label="剩余可退款金额">
{{ form.refundableBalance }}
</a-descriptions-item>
<a-descriptions-item label="退款时间">
{{ form.refundTime }}
</a-descriptions-item>
<a-descriptions-item label="退款状态">
<a-tag>{{ form.refundStatus ? '成功' : '失败' }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="退款终端ip">
{{ form.clientIp }}
</a-descriptions-item>
<a-descriptions-item label="退款信息">
<a-tag v-for="o in form.refundableInfo" :key="o.payChannel">{{ dictConvert('PayChannel', o.payChannel) }}: {{ o.amount }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="错误码">
{{ form.errorCode }}
</a-descriptions-item>
<a-descriptions-item label="错误信息">
{{ form.errorMsg }}
</a-descriptions-item>
</a-descriptions>
<template #footer>
<a-button key="cancel" @click="handleCancel">取消</a-button>
</template>
</basic-modal>
</template>
<script lang="ts" setup>
import { $ref } from 'vue/macros'
import useFormEdit from '/@/hooks/bootx/useFormEdit'
import { get, RefundRecord } from './RefundRecord.api'
import { FormInstance } from 'ant-design-vue/lib/form'
import { BasicModal } from '/@/components/Modal'
import { useDict } from '/@/hooks/bootx/useDict'
const {
initFormEditType,
handleCancel,
search,
labelCol,
wrapperCol,
modalWidth,
title,
confirmLoading,
visible,
editable,
showable,
formEditType,
} = useFormEdit()
const { dictConvert } = useDict()
// 表单
const formRef = $ref<FormInstance>()
let form = $ref<RefundRecord>({
id: null,
paymentId: '',
refundRequestNo: '',
userId: '',
title: '',
amount: 0,
refundableInfo: [],
refundStatus: 0,
refundTime: '',
clientIp: '',
errorCode: '',
errorMsg: '',
})
// 事件
const emits = defineEmits(['ok'])
// 入口
function init(id) {
visible.value = true
confirmLoading.value = true
get(id).then(({ data }) => {
form = data
confirmLoading.value = false
})
}
defineExpose({
init,
})
</script>
<style lang="less" scoped></style>

View File

@@ -4,13 +4,7 @@
<b-query :query-params="model.queryParam" :fields="fields" @query="queryPage" @reset="resetQueryParams" />
</div>
<div class="m-3 p-3 bg-white">
<vxe-toolbar ref="xToolbar" custom :refresh="{ query: queryPage }">
<template #buttons>
<a-space>
<a-button type="primary" pre-icon="ant-design:plus-outlined" @click="add">新建</a-button>
</a-space>
</template>
</vxe-toolbar>
<vxe-toolbar ref="xToolbar" custom :refresh="{ query: queryPage }" />
<vxe-table row-id="id" ref="xTable" :data="pagination.records" :loading="loading">
<vxe-column type="seq" title="序号" width="60" />
<vxe-column field="paymentId" title="支付记录单号">
@@ -30,19 +24,11 @@
<a-tag>{{ row.refundStatus ? '成功' : '失败' }}</a-tag>
</template>
</vxe-column>
<vxe-column fixed="right" width="150" :showOverflow="false" title="操作">
<vxe-column fixed="right" width="60" :showOverflow="false" title="操作">
<template #default="{ row }">
<span>
<a-link @click="show(row)">查看</a-link>
</span>
<a-divider type="vertical" />
<span>
<a-link @click="edit(row)">编辑</a-link>
</span>
<a-divider type="vertical" />
<a-popconfirm title="是否删除" @confirm="remove(row)" okText="是" cancelText="否">
<a-link danger>删除</a-link>
</a-popconfirm>
</template>
</vxe-column>
</vxe-table>
@@ -55,6 +41,7 @@
@page-change="handleTableChange"
/>
<refund-record-edit ref="refundRecordEdit" @ok="queryPage" />
<payment-info ref="paymentInfo" />
</div>
</div>
</template>
@@ -62,15 +49,16 @@
<script lang="ts" setup>
import { onMounted } from 'vue'
import { $ref } from 'vue/macros'
import { del, page } from './RefundRecord.api'
import { page } from './RefundRecord.api'
import useTablePage from '/@/hooks/bootx/useTablePage'
import RefundRecordEdit from './RefundRecordEdit.vue'
import RefundRecordEdit from './RefundRecordInfo.vue'
import { VxeTableInstance, VxeToolbarInstance } from 'vxe-table'
import BQuery from '/@/components/Bootx/Query/BQuery.vue'
import { FormEditType } from '/@/enums/formTypeEnum'
import { useMessage } from '/@/hooks/web/useMessage'
import { QueryField } from '/@/components/Bootx/Query/Query'
import { QueryField, STRING } from '/@/components/Bootx/Query/Query'
import { useDict } from '/@/hooks/bootx/useDict'
import PaymentInfo from '/@/views/modules/payment/order/payment/PaymentInfo.vue'
// 使用hooks
const { handleTableChange, pageQueryResHandel, resetQueryParams, pagination, pages, model, loading } = useTablePage(queryPage)
@@ -78,11 +66,16 @@
const { dictConvert } = useDict()
// 查询条件
const fields = [] as QueryField[]
const fields = [
{ field: 'paymentId', type: STRING, name: '支付单号', placeholder: '请输入支付单号' },
{ field: 'businessId', type: STRING, name: '业务ID', placeholder: '请输入业务ID' },
{ field: 'title', type: STRING, name: '标题', placeholder: '请输入标题' },
] as QueryField[]
const xTable = $ref<VxeTableInstance>()
const xToolbar = $ref<VxeToolbarInstance>()
const refundRecordEdit = $ref<any>()
const paymentInfo = $ref<any>()
onMounted(() => {
vxeBind()
@@ -102,25 +95,13 @@
pageQueryResHandel(data)
})
}
// 新增
function add() {
refundRecordEdit.init(null, FormEditType.Add)
}
// 编辑
function edit(record) {
refundRecordEdit.init(record.id, FormEditType.Edit)
}
// 查看
function show(record) {
refundRecordEdit.init(record.id, FormEditType.Show)
}
// 删除
function remove(record) {
del(record.id).then(() => {
createMessage.success('删除成功')
queryPage()
})
function showPayment(paymentId) {
paymentInfo.init(paymentId, 'show')
}
</script>