18 changed files with 431 additions and 5 deletions
			
			
		| After Width: | Height: | Size: 1.6 KiB | 
| After Width: | Height: | Size: 3.9 KiB | 
| After Width: | Height: | Size: 1.7 KiB | 
| After Width: | Height: | Size: 1.0 KiB | 
| @ -0,0 +1,43 @@ | ||||
| <template> | ||||
|   <BasicModal v-bind="$attrs" @register="registerModal" title="发起订单" @ok="handleSubmit"> | ||||
|     <BasicForm @register="registerForm" /> | ||||
|   </BasicModal> | ||||
| </template> | ||||
| <script lang="ts" setup name="PayDemoModal"> | ||||
| import { useI18n } from '@/hooks/web/useI18n' | ||||
| import { useMessage } from '@/hooks/web/useMessage' | ||||
| import { BasicForm, useForm } from '@/components/Form' | ||||
| import { BasicModal, useModalInner } from '@/components/Modal' | ||||
| import { formSchema } from './demo.data' | ||||
| import { createDemoOrder } from '@/api/pay/demo' | ||||
| 
 | ||||
| const { t } = useI18n() | ||||
| const { createMessage } = useMessage() | ||||
| const emit = defineEmits(['success', 'register']) | ||||
| 
 | ||||
| const [registerForm, { resetFields, validate }] = useForm({ | ||||
|   labelWidth: 120, | ||||
|   baseColProps: { span: 24 }, | ||||
|   schemas: formSchema, | ||||
|   showActionButtonGroup: false, | ||||
|   actionColOptions: { span: 23 } | ||||
| }) | ||||
| 
 | ||||
| const [registerModal, { setModalProps, closeModal }] = useModalInner(async () => { | ||||
|   resetFields() | ||||
|   setModalProps({ confirmLoading: false }) | ||||
| }) | ||||
| 
 | ||||
| async function handleSubmit() { | ||||
|   try { | ||||
|     const values = await validate() | ||||
|     setModalProps({ confirmLoading: true }) | ||||
|     await createDemoOrder(values) | ||||
|     closeModal() | ||||
|     emit('success') | ||||
|     createMessage.success(t('common.saveSuccessText')) | ||||
|   } finally { | ||||
|     setModalProps({ confirmLoading: false }) | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| @ -0,0 +1,110 @@ | ||||
| import { BasicColumn, FormSchema, useRender } from '@/components/Table' | ||||
| import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||||
| 
 | ||||
| export const columns: BasicColumn[] = [ | ||||
|   { | ||||
|     title: '订单编号', | ||||
|     dataIndex: 'id', | ||||
|     width: 100 | ||||
|   }, | ||||
|   { | ||||
|     title: '用户编号', | ||||
|     dataIndex: 'userId', | ||||
|     width: 100 | ||||
|   }, | ||||
|   { | ||||
|     title: '商品名字', | ||||
|     dataIndex: 'spuName', | ||||
|     width: 100 | ||||
|   }, | ||||
|   { | ||||
|     title: '支付价格', | ||||
|     dataIndex: 'price', | ||||
|     width: 100, | ||||
|     customRender: ({ text }) => { | ||||
|       return useRender.renderTag('¥' + (text / 100.0).toFixed(2)) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: '退款金额', | ||||
|     dataIndex: 'refundPrice', | ||||
|     width: 100, | ||||
|     customRender: ({ text }) => { | ||||
|       return useRender.renderTag('¥' + (text / 100.0).toFixed(2)) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: '支付单号', | ||||
|     dataIndex: 'payOrderId', | ||||
|     width: 100 | ||||
|   }, | ||||
|   { | ||||
|     title: '创建时间', | ||||
|     dataIndex: 'createTime', | ||||
|     width: 180, | ||||
|     customRender: ({ text }) => { | ||||
|       return useRender.renderDate(text) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: '是否支付', | ||||
|     dataIndex: 'payed', | ||||
|     width: 100, | ||||
|     customRender: ({ text }) => { | ||||
|       return useRender.renderDict(text, DICT_TYPE.INFRA_BOOLEAN_STRING) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: '支付时间', | ||||
|     dataIndex: 'payTime', | ||||
|     width: 180, | ||||
|     customRender: ({ text }) => { | ||||
|       return useRender.renderDate(text) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: '退款时间', | ||||
|     dataIndex: 'refundTime', | ||||
|     width: 180, | ||||
|     customRender: ({ text }) => { | ||||
|       return useRender.renderDate(text) | ||||
|     } | ||||
|   } | ||||
| ] | ||||
| 
 | ||||
| export const searchFormSchema: FormSchema[] = [ | ||||
|   { | ||||
|     label: '支付单号', | ||||
|     field: 'payOrderId', | ||||
|     component: 'Input', | ||||
|     colProps: { span: 8 } | ||||
|   }, | ||||
|   { | ||||
|     label: '是否支付', | ||||
|     field: 'payed', | ||||
|     component: 'Select', | ||||
|     componentProps: { | ||||
|       options: getIntDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING) | ||||
|     }, | ||||
|     colProps: { span: 8 } | ||||
|   } | ||||
| ] | ||||
| 
 | ||||
| export const formSchema: FormSchema[] = [ | ||||
|   { | ||||
|     label: '商品', | ||||
|     field: 'spuId', | ||||
|     component: 'Select', | ||||
|     defaultValue: 0, | ||||
|     required: true, | ||||
|     componentProps: { | ||||
|       options: [ | ||||
|         { value: 1, label: '华为手机', price: 1 }, | ||||
|         { value: 2, label: '小米电视', price: 10 }, | ||||
|         { value: 3, label: '苹果手表', price: 100 }, | ||||
|         { value: 4, label: '华硕笔记本', price: 1000 }, | ||||
|         { value: 5, label: '蔚来汽车', price: 200000 } | ||||
|       ] | ||||
|     } | ||||
|   } | ||||
| ] | ||||
| @ -1,3 +1,74 @@ | ||||
| <template> | ||||
|   <div>开发中</div> | ||||
|   <div> | ||||
|     <BasicTable @register="registerTable"> | ||||
|       <template #toolbar> | ||||
|         <a-button type="primary" v-auth="['pay:app:create']" :preIcon="IconEnum.ADD" @click="handleAdd"> 发起订单 </a-button> | ||||
|       </template> | ||||
|       <template #bodyCell="{ column, record }"> | ||||
|         <template v-if="column.key === 'action'"> | ||||
|           <TableAction | ||||
|             :actions="[ | ||||
|               { icon: IconEnum.EDIT, label: '前往支付', onClick: handlePay.bind(null, record) }, | ||||
|               { icon: IconEnum.DELETE, color: 'error', label: '发起退款', onClick: handleRefund.bind(null, record) } | ||||
|             ]" | ||||
|           /> | ||||
|         </template> | ||||
|       </template> | ||||
|     </BasicTable> | ||||
|     <DemoModal @register="registerModal" @success="reload()" /> | ||||
|   </div> | ||||
| </template> | ||||
| <script lang="ts" setup name="PayDemo"> | ||||
| import { useGo } from '@/hooks/web/usePage' | ||||
| import { useI18n } from '@/hooks/web/useI18n' | ||||
| import { useMessage } from '@/hooks/web/useMessage' | ||||
| import { useModal } from '@/components/Modal' | ||||
| import DemoModal from './DemoModal.vue' | ||||
| import { IconEnum } from '@/enums/appEnum' | ||||
| import { BasicTable, useTable, TableAction } from '@/components/Table' | ||||
| import { getDemoOrderPage, refundDemoOrder } from '@/api/pay/demo' | ||||
| import { columns, searchFormSchema } from './demo.data' | ||||
| 
 | ||||
| const go = useGo() | ||||
| const { t } = useI18n() | ||||
| const { createConfirm, createMessage } = useMessage() | ||||
| const [registerModal, { openModal }] = useModal() | ||||
| 
 | ||||
| const [registerTable, { reload }] = useTable({ | ||||
|   title: '接入示例列表', | ||||
|   api: getDemoOrderPage, | ||||
|   columns, | ||||
|   formConfig: { labelWidth: 120, schemas: searchFormSchema }, | ||||
|   useSearchForm: true, | ||||
|   showTableSetting: true, | ||||
|   actionColumn: { | ||||
|     width: 180, | ||||
|     title: t('common.action'), | ||||
|     dataIndex: 'action', | ||||
|     fixed: 'right' | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| function handleAdd() { | ||||
|   openModal(true) | ||||
| } | ||||
| 
 | ||||
| /** 支付按钮操作 */ | ||||
| function handlePay(record: Recordable) { | ||||
|   go('/cashRegister/submit?id=' + record.id) | ||||
| } | ||||
| 
 | ||||
| /** 退款按钮操作 */ | ||||
| async function handleRefund(record: Recordable) { | ||||
|   createConfirm({ | ||||
|     title: '退款', | ||||
|     iconType: 'warning', | ||||
|     content: '是否确认退款编号为"' + record.id + '"的示例订单?', | ||||
|     async onOk() { | ||||
|       await refundDemoOrder(record.id) | ||||
|       createMessage.success(t('common.successText')) | ||||
|       reload() | ||||
|     } | ||||
|   }) | ||||
| } | ||||
| </script> | ||||
|  | ||||
| @ -0,0 +1,121 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <Card> | ||||
|       <Description :bordered="false" :column="3" :data="orderData" :schema="descSchema" /> | ||||
|     </Card> | ||||
|     <Card class="mt-4 justify-center"> | ||||
|       <List :grid="{ column: 8 }" header="选择支付宝支付" :data-source="aliPayChannels"> | ||||
|         <template #renderItem="{ item }"> | ||||
|           <ListItem> | ||||
|             <Card hoverable class="w-30 h-28 mt-3 pb-3"> | ||||
|               <template #cover> | ||||
|                 <img class="w-40px h-40px mt-2" :src="icons[item.code]" /> | ||||
|                 <p class="mt-3 text-center"> {{ item.name }} </p> | ||||
|               </template> | ||||
|               <template #actions> | ||||
|                 <Icon icon="ant-design:alipay-outlined" @click="submit(item.code)" /> | ||||
|               </template> | ||||
|             </Card> | ||||
|           </ListItem> | ||||
|         </template> | ||||
|       </List> | ||||
|       <List :grid="{ column: 8 }" class="mt-4" header="选择微信支付" :data-source="wxPayChannels"> | ||||
|         <template #renderItem="{ item }"> | ||||
|           <ListItem> | ||||
|             <Card hoverable class="w-30 h-28 pt-3 pb-3"> | ||||
|               <template #cover> | ||||
|                 <img class="w-40px h-40px mt-2" :src="icons[item.code]" /> | ||||
|                 <p class="mt-3 text-center"> {{ item.name }} </p> | ||||
|               </template> | ||||
|             </Card> | ||||
|           </ListItem> | ||||
|         </template> | ||||
|       </List> | ||||
|       <List :grid="{ column: 8 }" class="mt-4" header="选择其它支付" :data-source="otherPayChannels"> | ||||
|         <template #renderItem="{ item }"> | ||||
|           <ListItem> | ||||
|             <Card hoverable class="w-30 h-28 pt-3 pb-3"> | ||||
|               <template #cover> | ||||
|                 <img class="w-40px h-40px mt-2" :src="icons[item.code]" /> | ||||
|                 <p class="mt-3 text-center"> {{ item.name }} </p> | ||||
|               </template> | ||||
|             </Card> | ||||
|           </ListItem> | ||||
|         </template> | ||||
|       </List> | ||||
|     </Card> | ||||
|   </div> | ||||
| </template> | ||||
| <script lang="ts" setup name="PayOrderSubmit"> | ||||
| import { ref, onMounted } from 'vue' | ||||
| import { Card, List } from 'ant-design-vue' | ||||
| import { Description } from '@/components/Description' | ||||
| import { descSchema } from './submit.data' | ||||
| import { getOrder, submitOrder } from '@/api/pay/order' | ||||
| import { DICT_TYPE, getStrDictOptions } from '@/utils/dict' | ||||
| import { useRoute } from 'vue-router' | ||||
| import alipay_qr from '@/assets/images/pay/icon/alipay_qr.svg' | ||||
| import alipay_app from '@/assets/images/pay/icon/alipay_app.svg' | ||||
| import alipay_wap from '@/assets/images/pay/icon/alipay_wap.svg' | ||||
| import alipay_pc from '@/assets/images/pay/icon/alipay_pc.svg' | ||||
| import alipay_bar from '@/assets/images/pay/icon/alipay_bar.svg' | ||||
| import wx_app from '@/assets/images/pay/icon/wx_app.svg' | ||||
| import wx_lite from '@/assets/images/pay/icon/wx_lite.svg' | ||||
| import wx_pub from '@/assets/images/pay/icon/wx_pub.svg' | ||||
| import mock from '@/assets/images/pay/icon/mock.svg' | ||||
| 
 | ||||
| const ListItem = List.Item | ||||
| 
 | ||||
| const icons = { | ||||
|   alipay_qr: alipay_qr, | ||||
|   alipay_app: alipay_app, | ||||
|   alipay_wap: alipay_wap, | ||||
|   alipay_pc: alipay_pc, | ||||
|   alipay_bar: alipay_bar, | ||||
|   wx_app: wx_app, | ||||
|   wx_lite: wx_lite, | ||||
|   wx_pub: wx_pub, | ||||
|   mock: mock | ||||
| } | ||||
| 
 | ||||
| const { query } = useRoute() | ||||
| const orderData = ref() | ||||
| const aliPayChannels = ref<any[]>([]) | ||||
| const wxPayChannels = ref<any[]>([]) | ||||
| const otherPayChannels = ref<any[]>([]) | ||||
| 
 | ||||
| function initPayChannels() { | ||||
|   // 微信支付 | ||||
|   for (const dict of getStrDictOptions(DICT_TYPE.PAY_CHANNEL_CODE_TYPE)) { | ||||
|     const payChannel = { | ||||
|       name: dict.label, | ||||
|       code: dict.value as string | ||||
|     } | ||||
|     if (payChannel.code.startsWith('wx_')) { | ||||
|       wxPayChannels.value.push(payChannel) | ||||
|     } else if (payChannel.code.startsWith('alipay_')) { | ||||
|       aliPayChannels.value.push(payChannel) | ||||
|     } else if (payChannel.code) { | ||||
|       otherPayChannels.value.push(payChannel) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| async function getDetail() { | ||||
|   const queryId = query.id as unknown as number | ||||
|   if (!queryId) { | ||||
|     return | ||||
|   } | ||||
| 
 | ||||
|   const res = await getOrder(queryId) | ||||
|   orderData.value = res | ||||
| } | ||||
| 
 | ||||
| function submit(channelCode) { | ||||
|   submitOrder({ id: query.id as unknown as number, channelCode }) | ||||
| } | ||||
| 
 | ||||
| onMounted(async () => { | ||||
|   await initPayChannels() | ||||
|   await getDetail() | ||||
| }) | ||||
| </script> | ||||
| @ -0,0 +1,38 @@ | ||||
| import { DescItem } from '@/components/Description' | ||||
| import { useRender } from '@/components/Table' | ||||
| 
 | ||||
| export const descSchema: DescItem[] = [ | ||||
|   { | ||||
|     label: '支付单号', | ||||
|     field: 'id' | ||||
|   }, | ||||
|   { | ||||
|     label: '商品标题', | ||||
|     field: 'subject' | ||||
|   }, | ||||
|   { | ||||
|     label: '商品内容', | ||||
|     field: 'body' | ||||
|   }, | ||||
|   { | ||||
|     label: '支付金额', | ||||
|     field: 'amount', | ||||
|     render: (curVal) => { | ||||
|       return useRender.renderText('¥', parseFloat(curVal || 0 / 100).toFixed(2)) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     label: '创建时间', | ||||
|     field: 'createTime', | ||||
|     render: (curVal) => { | ||||
|       return useRender.renderDate(curVal) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     label: '过期时间', | ||||
|     field: 'expireTime', | ||||
|     render: (curVal) => { | ||||
|       return useRender.renderDate(curVal) | ||||
|     } | ||||
|   } | ||||
| ] | ||||
		Reference in new issue