Browse Source

转账 - 管理后台新增转账示例

jason 1 năm trước cách đây
mục cha
commit
55a961e862

+ 25 - 0
src/api/pay/demo/transfer/index.ts

@@ -0,0 +1,25 @@
+import request from '@/config/axios'
+
+export interface DemoTransferVO {
+  price: number
+  type: number
+  userName: string
+  alipayLogonId: string
+  openid: string
+}
+
+// 创建示例转账单
+export function createDemoTransfer(data: DemoTransferVO) {
+  return request.post({
+    url: '/pay/demo-transfer/create',
+    data: data
+  })
+}
+
+// 获得示例订单分页
+export function getDemoTransferPage(query: PageParam) {
+  return request.get({
+    url: '/pay/demo-transfer/page',
+    params: query
+  })
+}

+ 18 - 0
src/api/pay/transfer/index.ts

@@ -0,0 +1,18 @@
+import request from '@/config/axios'
+
+export interface TransferVO {
+  appId: number
+  channelCode: string
+  merchantTransferId: string
+  type: number
+  price: number
+  subject: string
+  userName: string
+  alipayLogonId: string
+  openid: string
+}
+
+// 新增转账单
+export const createTransfer = async (data: TransferVO) => {
+  return await request.post({ url: `/pay/transfer/create`, data })
+}

+ 2 - 0
src/utils/dict.ts

@@ -143,6 +143,8 @@ export enum DICT_TYPE {
   PAY_REFUND_STATUS = 'pay_refund_status', // 退款订单状态
   PAY_NOTIFY_STATUS = 'pay_notify_status', // 商户支付回调状态
   PAY_NOTIFY_TYPE = 'pay_notify_type', // 商户支付回调状态
+  PAY_TRANSFER_STATUS = 'pay_transfer_status', // 转账订单状态
+  PAY_TRANSFER_TYPE = 'pay_transfer_type', // 转账订单状态
 
   // ========== MP 模块 ==========
   MP_AUTO_REPLY_REQUEST_MATCH = 'mp_auto_reply_request_match', // 自动回复请求匹配类型

+ 0 - 0
src/views/pay/demo/index.vue → src/views/pay/demo/order/index.vue


+ 122 - 0
src/views/pay/demo/transfer/DemoTransferForm.vue

@@ -0,0 +1,122 @@
+<template>
+  <Dialog :title="dialogTitle" v-model="dialogVisible" width="800px">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="120px"
+      v-loading="formLoading"
+    >
+      <el-form-item label="转账类型" prop="type">
+        <el-radio-group v-model="formData.type">
+          <el-radio
+            v-for="dict in getIntDictOptions(DICT_TYPE.PAY_TRANSFER_TYPE)"
+            :key="dict.value"
+            :label="dict.value"
+            :disabled="dict.value === 2 || dict.value === 3 || dict.value === 4"
+          >
+            {{ dict.label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="转账金额(元)" prop="price">
+        <el-input-number
+          v-model="formData.price"
+          :min="0"
+          :precision="2"
+          :step="0.01"
+          placeholder="请输入转账金额"
+          style="width: 200px"
+        />
+      </el-form-item>
+      <el-form-item label="收款人姓名" prop="userName">
+        <el-input v-model="formData.userName" placeholder="请输入收款人姓名" />
+      </el-form-item>
+      <el-form-item v-show="formData.type === 1" label="支付宝登录账号" prop="alipayLogonId">
+        <el-input v-model="formData.alipayLogonId" placeholder="请输入支付宝登录账号" />
+      </el-form-item>
+      <el-form-item v-show="formData.type === 2" label="微信 openid" prop="openid">
+        <el-input v-model="formData.openid" placeholder="请输入微信 openid" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script setup lang="ts">
+import * as DemoTransferApi from '@/api/pay/demo/transfer'
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { yuanToFen } from '@/utils'
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref({
+  id: undefined,
+  price: undefined,
+  type: undefined,
+  userName: undefined,
+  alipayLogonId: undefined,
+  openid: undefined
+})
+const formRules = reactive({
+  price: [{ required: true, message: '转账金额不能为空', trigger: 'blur' }],
+  type: [{ required: true, message: '转账类型不能为空', trigger: 'change' }]
+})
+const formRef = ref() // 表单 Ref
+
+/** 打开弹窗 */
+const open = async (type: string) => {
+  dialogVisible.value = true
+  dialogTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+}
+/** 关闭弹窗 */
+const close = async () => {
+  dialogVisible.value = false
+  resetForm()
+}
+defineExpose({ open, close }) // 提供 open, close 方法,用于打开, 关闭弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = { ...formData.value }
+    data.price = yuanToFen(data.price)
+    if (formType.value === 'create') {
+      await DemoTransferApi.createDemoTransfer(data)
+      message.success(t('common.createSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    price: undefined,
+    userName: undefined,
+    alipayLogonId: undefined,
+    openid: undefined
+  }
+  formRef.value?.resetFields()
+}
+</script>

+ 152 - 0
src/views/pay/demo/transfer/index.vue

@@ -0,0 +1,152 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item>
+        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        <el-button type="primary" plain @click="openForm('create')"
+          ><Icon icon="ep:plus" />创建业务转账单
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list" :show-overflow-tooltip="true">
+      <el-table-column label="订单编号" align="center" prop="id" />
+      <el-table-column label="转账类型" align="center" prop="type" width="120">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.PAY_TRANSFER_TYPE" :value="scope.row.type" />
+        </template>
+      </el-table-column>
+      <el-table-column label="转账金额" align="center" prop="price">
+        <template #default="scope">
+          <span>¥{{ (scope.row.price / 100.0).toFixed(2) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="收款人姓名" align="center" prop="userName" width="120" />
+      <el-table-column label="支付宝登录账号" align="center" prop="alipayLogonId" width="180" />
+      <el-table-column label="微信 openid" align="center" prop="openid" width="120" />
+      <el-table-column label="转账状态" align="center" prop="transferStatus">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.PAY_TRANSFER_STATUS" :value="scope.row.transferStatus" />
+        </template>
+      </el-table-column>
+      <el-table-column label="转账单号" align="center" prop="payTransferId" />
+      <el-table-column label="支付渠道" align="center" prop="payChannelCode" />
+      <el-table-column
+        label="转账时间"
+        align="center"
+        prop="transferTime"
+        :formatter="dateFormatter"
+        width="180px"
+      />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="handleTransfer(scope.row)"
+            v-if="scope.row.transferStatus === 0"
+          >
+            发起转账
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <DemoTransferForm ref="demoFormRef" @success="getList" />
+  <CreatePayTransfer ref="payTransferRef" :payTransfer="payTransfer" @success="getList" />
+</template>
+
+<script setup lang="ts">
+import { dateFormatter } from '@/utils/formatTime'
+import * as DemoTransferApi from '@/api/pay/demo/transfer'
+import * as PayTransferApi from '@/api/pay/transfer'
+import DemoTransferForm from './DemoTransferForm.vue'
+import CreatePayTransfer from '../../transfer/CreatePayTransfer.vue'
+import { DICT_TYPE } from '@/utils/dict'
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10
+})
+const queryFormRef = ref() // 搜索的表单
+
+let payTransfer = {
+  appId: undefined,
+  merchantTransferId: undefined,
+  type: undefined,
+  price: undefined,
+  subject: undefined,
+  userName: undefined,
+  alipayLogonId: undefined,
+  openid: undefined
+} as PayTransferApi.TransferVO // 传递给转账订单的数据
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await DemoTransferApi.getDemoTransferPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 创建业务转账单操作 */
+const demoFormRef = ref()
+const payTransferRef = ref()
+const openForm = (type: string) => {
+  demoFormRef.value.open(type)
+}
+
+/** 发起转账操作 */
+const handleTransfer = (row: any) => {
+  payTransfer = { ...row }
+  payTransfer.merchantTransferId = row.id.toString()
+  payTransfer.subject = '示例转账'
+  payTransferRef.value.showPayTransfer(payTransfer)
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>

+ 141 - 0
src/views/pay/transfer/CreatePayTransfer.vue

@@ -0,0 +1,141 @@
+<template>
+  <Dialog title="发起转账" v-model="dialogVisible" width="800px">
+    <el-card style="margin-top: 10px">
+      <el-descriptions title="转账信息" :column="2" border>
+        <el-descriptions-item label="转账类型">
+          {{ typeName }}
+        </el-descriptions-item>
+        <el-descriptions-item label="转账金额(元)">
+          ¥{{ (transfer.price / 100.0).toFixed(2) }}
+        </el-descriptions-item>
+        <el-descriptions-item label="收款人姓名">
+          {{ transfer.userName }}
+        </el-descriptions-item>
+        <el-descriptions-item label="支付宝登录账号" v-if="transfer.type === 1">
+          {{ transfer.alipayLogonId }}
+        </el-descriptions-item>
+        <el-descriptions-item label="微信 openid" v-if="transfer.type === 2">
+          {{ transfer.openid }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </el-card>
+    <el-card style="margin-top: 20px">
+      <template #header>
+        <div class="card-header">
+          <span>选择转账渠道</span>
+        </div>
+      </template>
+      <div>
+        <el-radio-group v-model="channelCode">
+          <el-radio
+            label="alipay_pc"
+            :disabled="transfer.type === 2 || transfer.type === 3 || transfer.type === 4"
+          >
+            <img :src="svg_alipay_app" />
+          </el-radio>
+          <el-radio
+            label="wx_app"
+            :disabled="transfer.type === 1 || transfer.type === 3 || transfer.type === 4"
+          >
+            <img :src="svg_wx_app" />
+          </el-radio>
+        </el-radio-group>
+      </div>
+    </el-card>
+    <el-divider />
+    <div style="text-align: right">
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </div>
+  </Dialog>
+</template>
+
+<script lang="ts" setup>
+import * as PayTransferApi from '@/api/pay/transfer'
+import { computed, PropType } from 'vue'
+import { DICT_TYPE, getDictLabel } from '@/utils/dict'
+// 导入图标
+import svg_alipay_app from '@/assets/svgs/pay/icon/alipay_app.svg'
+import svg_wx_app from '@/assets/svgs/pay/icon/wx_app.svg'
+import { yuanToFen } from '@/utils'
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+const formLoading = ref(false) // 提交的按钮禁用
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+defineOptions({ name: 'CreatePayTransfer' })
+
+const props = defineProps({
+  payTransfer: {
+    type: Object as PropType<PayTransferApi.TransferVO>,
+    required: true
+  }
+})
+// 提交数据
+let submitTransferData = {
+  appId: undefined,
+  channelCode: undefined,
+  merchantTransferId: undefined,
+  type: undefined,
+  price: undefined,
+  subject: undefined,
+  userName: undefined,
+  alipayLogonId: undefined,
+  openid: undefined
+} as PayTransferApi.TransferVO
+
+const transfer = reactive(props.payTransfer)
+const dialogVisible = ref(false)
+const typeName = computed(() => {
+  return getDictLabel(DICT_TYPE.PAY_TRANSFER_TYPE, transfer.type)
+})
+const channelCode = computed(() => {
+  let channelCode = 'alipay_pc'
+  if (transfer.type === 2) {
+    channelCode = 'wx_app'
+  }
+  // TODO 银行卡和钱包 转账待实现
+  return channelCode
+})
+
+/** 打开弹窗 */
+const showPayTransfer = async (payTransfer: PayTransferApi.TransferVO) => {
+  dialogVisible.value = true
+  submitTransferData = payTransfer
+  transfer.merchantTransferId = payTransfer.merchantTransferId
+  transfer.price = payTransfer.price
+  transfer.userName = payTransfer.userName
+  transfer.type = payTransfer.type
+  transfer.appId = payTransfer.appId
+  transfer.subject = payTransfer.subject
+  transfer.alipayLogonId = payTransfer.alipayLogonId
+  transfer.openid = payTransfer.openid
+}
+/** 关闭弹窗 */
+const close = async () => {
+  dialogVisible.value = false
+}
+defineExpose({ showPayTransfer, close }) // 提供 showPayTransfer, close 方法,用于打开, 关闭弹窗
+
+const submitForm = async () => {
+  // 校验表单
+  formLoading.value = true
+  try {
+    submitTransferData.channelCode = channelCode.value
+    await PayTransferApi.createTransfer(submitTransferData)
+    message.success('发起转账成功. 是否转账成功,以转账订单状态为准')
+    // 发送操作成功的事件
+    emit('success')
+    dialogVisible.value = false
+  } finally {
+    formLoading.value = false
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+</style>

+ 1 - 1
src/views/pay/wallet/rechargePackage/WalletRechargePackageForm.vue

@@ -90,7 +90,7 @@ const submitForm = async () => {
   // 提交请求
   formLoading.value = true
   try {
-    const data = formData.value as unknown as WalletRechargePackageApi.WalletRechargePackageVO
+    const data = { ...formData.value }
     data.payPrice = yuanToFen(data.payPrice)
     data.bonusPrice = yuanToFen(data.bonusPrice)
     if (formType.value === 'create') {