Parcourir la source

Merge remote-tracking branch 'yudao/dev' into dev-to-dev

puhui999 il y a 1 an
Parent
commit
161feab244

+ 15 - 16
package.json

@@ -57,7 +57,6 @@
     "nprogress": "^0.2.0",
     "pinia": "^2.1.6",
     "qrcode": "^1.5.3",
-    "qrcode.vue": "^3.4.0",
     "qs": "^6.11.2",
     "steady-xml": "^0.1.0",
     "url": "^0.11.1",
@@ -72,9 +71,9 @@
     "xml-js": "^1.6.11"
   },
   "devDependencies": {
-    "@commitlint/cli": "^17.6.7",
-    "@commitlint/config-conventional": "^17.6.7",
-    "@iconify/json": "^2.2.98",
+    "@commitlint/cli": "^17.7.1",
+    "@commitlint/config-conventional": "^17.7.0",
+    "@iconify/json": "^2.2.100",
     "@intlify/unplugin-vue-i18n": "^0.12.2",
     "@purge-icons/generated": "^0.9.0",
     "@types/intro.js": "^5.1.1",
@@ -83,30 +82,30 @@
     "@types/nprogress": "^0.2.0",
     "@types/qrcode": "^1.5.1",
     "@types/qs": "^6.9.7",
-    "@typescript-eslint/eslint-plugin": "^6.2.1",
-    "@typescript-eslint/parser": "^6.2.1",
-    "@unocss/transformer-variant-group": "^0.54.1",
+    "@typescript-eslint/eslint-plugin": "^6.3.0",
+    "@typescript-eslint/parser": "^6.3.0",
+    "@unocss/transformer-variant-group": "^0.55.0",
     "@vitejs/plugin-legacy": "^4.1.1",
     "@vitejs/plugin-vue": "^4.2.3",
     "@vitejs/plugin-vue-jsx": "^3.0.1",
-    "@vue-macros/volar": "^0.13.2",
+    "@vue-macros/volar": "^0.13.3",
     "autoprefixer": "^10.4.14",
     "bpmn-js": "^8.9.0",
     "bpmn-js-properties-panel": "^0.46.0",
     "consola": "^3.2.3",
-    "eslint": "^8.46.0",
-    "eslint-config-prettier": "^8.10.0",
-    "eslint-define-config": "^1.22.0",
+    "eslint": "^8.47.0",
+    "eslint-config-prettier": "^9.0.0",
+    "eslint-define-config": "^1.23.0",
     "eslint-plugin-prettier": "^5.0.0",
-    "eslint-plugin-vue": "^9.16.1",
+    "eslint-plugin-vue": "^9.17.0",
     "lint-staged": "^13.2.3",
     "postcss": "^8.4.27",
     "postcss-html": "^1.5.0",
     "postcss-scss": "^4.0.6",
     "prettier": "^3.0.1",
     "rimraf": "^5.0.1",
-    "rollup": "^3.27.2",
-    "sass": "^1.64.2",
+    "rollup": "^3.28.0",
+    "sass": "^1.65.1",
     "stylelint": "^15.10.2",
     "stylelint-config-html": "^1.1.0",
     "stylelint-config-recommended": "^13.0.0",
@@ -114,11 +113,11 @@
     "stylelint-order": "^6.0.3",
     "terser": "^5.19.2",
     "typescript": "5.1.6",
-    "unocss": "^0.54.1",
+    "unocss": "^0.55.0",
     "unplugin-auto-import": "^0.16.6",
     "unplugin-element-plus": "^0.8.0",
     "unplugin-vue-components": "^0.25.1",
-    "vite": "4.4.8",
+    "vite": "4.4.9",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-ejs": "^1.6.4",
     "vite-plugin-eslint": "^1.8.1",

+ 34 - 0
src/api/member/user/index.ts

@@ -0,0 +1,34 @@
+import request from '@/config/axios'
+
+export interface UserVO {
+  id: number
+  mobile: string
+  password: string
+  status: number
+  registerIp: string
+  loginIp: string
+  loginDate: Date
+  nickname: string
+  avatar: string
+  name: string
+  sex: number
+  areaId: number
+  birthday: Date
+  mark: string
+  createTime: Date
+}
+
+// 查询会员用户列表
+export const getUserPage = async (params) => {
+  return await request.get({ url: `/member/user/page`, params })
+}
+
+// 查询会员用户详情
+export const getUser = async (id: number) => {
+  return await request.get({ url: `/member/user/get?id=` + id })
+}
+
+// 修改会员用户
+export const updateUser = async (data: UserVO) => {
+  return await request.put({ url: `/member/user/update`, data })
+}

+ 1 - 5
src/utils/dict.ts

@@ -131,12 +131,8 @@ export enum DICT_TYPE {
 
   // ========== PAY 模块 ==========
   PAY_CHANNEL_CODE = 'pay_channel_code', // 支付渠道编码类型
-  PAY_CHANNEL_CODE_TYPE = 'pay_channel_code_type', // 支付渠道编码类型
   PAY_ORDER_STATUS = 'pay_order_status', // 商户支付订单状态
-  PAY_ORDER_REFUND_STATUS = 'pay_order_refund_status', // 商户支付订单退款状态
-  PAY_REFUND_ORDER_STATUS = 'pay_refund_order_status', // 退款订单状态
-  PAY_REFUND_ORDER_TYPE = 'pay_refund_order_type', // 退款订单类别
-
+  PAY_REFUND_STATUS = 'pay_refund_status', // 退款订单状态
   PAY_NOTIFY_STATUS = 'pay_notify_status', // 商户支付回调状态
   PAY_NOTIFY_TYPE = 'pay_notify_type', // 商户支付回调状态
 

+ 170 - 0
src/views/member/user/UserForm.vue

@@ -0,0 +1,170 @@
+<template>
+  <Dialog :title="dialogTitle" v-model="dialogVisible">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="100px"
+      v-loading="formLoading"
+    >
+      <el-form-item label="手机号" prop="mobile">
+        <el-input v-model="formData.mobile" placeholder="请输入手机号" />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-radio-group v-model="formData.status">
+          <el-radio
+            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
+            :key="dict.value"
+            :label="dict.value"
+          >
+            {{ dict.label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="用户昵称" prop="nickname">
+        <el-input v-model="formData.nickname" placeholder="请输入用户昵称" />
+      </el-form-item>
+      <el-form-item label="头像" prop="avatar">
+        <UploadImg v-model="formData.avatar" :limit="1" :is-show-tip="false" />
+      </el-form-item>
+      <el-form-item label="真实名字" prop="name">
+        <el-input v-model="formData.name" placeholder="请输入真实名字" />
+      </el-form-item>
+      <el-form-item label="用户性别" prop="sex">
+        <el-radio-group v-model="formData.sex">
+          <el-radio
+            v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
+            :key="dict.value"
+            :label="dict.value"
+          >
+            {{ dict.label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="出生日期" prop="birthday">
+        <el-date-picker
+          v-model="formData.birthday"
+          type="date"
+          value-format="x"
+          placeholder="选择出生日期"
+        />
+      </el-form-item>
+      <el-form-item label="所在地" prop="areaId">
+        <el-tree-select
+          v-model="formData.areaId"
+          :data="areaList"
+          :props="defaultProps"
+          :render-after-expand="true"
+        />
+      </el-form-item>
+      <el-form-item label="会员备注" prop="mark">
+        <el-input type="textarea" v-model="formData.mark" placeholder="请输入会员备注" />
+      </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 { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import * as UserApi from '@/api/member/user'
+import * as AreaApi from '@/api/system/area'
+import { defaultProps } from '@/utils/tree'
+
+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,
+  mobile: undefined,
+  status: undefined,
+  nickname: undefined,
+  avatar: undefined,
+  name: undefined,
+  sex: undefined,
+  areaId: undefined,
+  birthday: undefined,
+  mark: undefined
+})
+const formRules = reactive({
+  mobile: [{ required: true, message: '手机号不能为空', trigger: 'blur' }],
+  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
+})
+const formRef = ref() // 表单 Ref
+const areaList = ref([]) // 地区列表
+
+/** 打开弹窗 */
+const open = async (type: string, id?: number) => {
+  dialogVisible.value = true
+  dialogTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await UserApi.getUser(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+  // 获得地区列表
+  areaList.value = await AreaApi.getAreaTree()
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+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 as unknown as UserApi.UserVO
+    if (formType.value === 'create') {
+      // 说明:目前暂时没有新增操作。如果自己业务需要,可以进行扩展
+      // await UserApi.createUser(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await UserApi.updateUser(data)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    mobile: undefined,
+    password: undefined,
+    status: undefined,
+    registerIp: undefined,
+    loginIp: undefined,
+    loginDate: undefined,
+    nickname: undefined,
+    avatar: undefined,
+    name: undefined,
+    sex: undefined,
+    areaId: undefined,
+    birthday: undefined,
+    mark: undefined,
+    createTime: undefined
+  }
+  formRef.value?.resetFields()
+}
+</script>

+ 176 - 0
src/views/member/user/index.vue

@@ -0,0 +1,176 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="用户昵称" prop="nickname">
+        <el-input
+          v-model="queryParams.nickname"
+          placeholder="请输入用户昵称"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
+        />
+      </el-form-item>
+      <el-form-item label="手机号" prop="mobile">
+        <el-input
+          v-model="queryParams.mobile"
+          placeholder="请输入手机号"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
+        />
+      </el-form-item>
+      <el-form-item label="注册时间" prop="createTime">
+        <el-date-picker
+          v-model="queryParams.createTime"
+          value-format="YYYY-MM-DD HH:mm:ss"
+          type="daterange"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+          class="!w-240px"
+        />
+      </el-form-item>
+      <el-form-item label="登录时间" prop="loginDate">
+        <el-date-picker
+          v-model="queryParams.loginDate"
+          value-format="YYYY-MM-DD HH:mm:ss"
+          type="daterange"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+          class="!w-240px"
+        />
+      </el-form-item>
+      <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-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+      <el-table-column label="用户编号" align="center" prop="id" width="120px" />
+      <el-table-column label="头像" align="center" prop="avatar" width="80px">
+        <template #default="scope">
+          <img :src="scope.row.avatar" style="width: 40px" />
+        </template>
+      </el-table-column>
+      <el-table-column label="手机号" align="center" prop="mobile" width="120px" />
+      <el-table-column label="昵称" align="center" prop="nickname" width="80px" />
+      <!-- TODO 芋艿:待接入 -->
+      <el-table-column label="等级" align="center" width="100px" />
+      <el-table-column label="分组" align="center" width="100px" />
+      <el-table-column label="积分" align="center" width="100px" />
+      <el-table-column label="状态" align="center" prop="status" width="100px">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="登录时间"
+        align="center"
+        prop="loginDate"
+        :formatter="dateFormatter"
+        width="180px"
+      />
+      <el-table-column
+        label="注册时间"
+        align="center"
+        prop="createTime"
+        :formatter="dateFormatter"
+        width="180px"
+      />
+      <el-table-column label="操作" align="center">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+            v-hasPermi="['member:user:update']"
+          >
+            编辑
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <UserForm ref="formRef" @success="getList" />
+</template>
+<script setup lang="ts">
+import { dateFormatter } from '@/utils/formatTime'
+import * as UserApi from '@/api/member/user'
+import UserForm from './UserForm.vue'
+import { DICT_TYPE } from '@/utils/dict'
+
+defineOptions({ name: 'MemberUser' })
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  nickname: null,
+  mobile: null,
+  loginDate: [],
+  createTime: []
+})
+const queryFormRef = ref() // 搜索的表单
+const exportLoading = ref(false) // 导出的加载中
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await UserApi.getUserPage(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 formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>

+ 1 - 2
src/views/pay/app/index.vue

@@ -244,7 +244,7 @@
             <el-button
               type="success"
               circle
-              v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.MOCK)"
+              v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.MOCK.code)"
               @click="openChannelForm(scope.row, PayChannelEnum.MOCK.code)"
             >
               <Icon icon="ep:check" />
@@ -326,7 +326,6 @@ const queryParams = reactive({
   createTime: []
 })
 const queryFormRef = ref() // 搜索的表单
-const exportLoading = ref(false) // 导出的加载中
 
 /** 查询列表 */
 const getList = async () => {

+ 5 - 5
src/views/pay/cashier/index.vue

@@ -68,7 +68,7 @@
     append-to-body
     :close-on-press-escape="false"
   >
-    <qrcode-vue :value="qrCode.url" :size="310" level="L" />
+    <Qrcode :text="qrCode.url" :width="310" />
   </Dialog>
 
   <!-- 展示形式:BarCode 条形码 -->
@@ -115,7 +115,7 @@
 </template>
 
 <script lang="ts" setup>
-import QrcodeVue from 'qrcode.vue'
+import { Qrcode } from '@/components/Qrcode'
 import * as PayOrderApi from '@/api/pay/order'
 import { PayChannelEnum, PayDisplayModeEnum, PayOrderStatusEnum } from '@/utils/constants'
 import { formatDate } from '@/utils/formatTime'
@@ -462,12 +462,12 @@ onMounted(() => {
 
   .box {
     width: 160px;
-    border: 1px solid #e6ebf5;
-    cursor: pointer;
-    text-align: center;
     padding-top: 10px;
     padding-bottom: 5px;
     margin-right: 10px;
+    text-align: center;
+    cursor: pointer;
+    border: 1px solid #e6ebf5;
 
     img {
       width: 40px;

+ 6 - 4
src/views/pay/demo/index.vue

@@ -96,7 +96,7 @@
         >
           <el-option v-for="item in spus" :key="item.id" :label="item.name" :value="item.id">
             <span style="float: left">{{ item.name }}</span>
-            <span style="float: right; color: #8492a6; font-size: 13px">
+            <span style="float: right; font-size: 13px; color: #8492a6">
               ¥{{ (item.price / 100.0).toFixed(2) }}
             </span>
           </el-option>
@@ -121,7 +121,8 @@ const message = useMessage() // 消息弹窗
 const loading = ref(true) // 列表的加载中
 const total = ref(0) // 列表的总页数
 const list = ref([]) // 列表的数据
-const queryParams = ref({
+// 查询条件
+const queryParams = reactive({
   pageNo: 1,
   pageSize: 10
 })
@@ -141,7 +142,7 @@ const getList = async () => {
 }
 
 /** 支付按钮操作 */
-const handlePay = (row) => {
+const handlePay = (row: any) => {
   router.push({
     name: 'PayCashier',
     query: {
@@ -195,7 +196,7 @@ const spus = ref([
 
 const dialogVisible = ref(false) // 弹窗的是否展示
 const formLoading = ref(false) // 表单的加载中
-const formData = ref({}) // 表单数据
+const formData = ref<any>({}) // 表单数据
 const formRules = {
   spuId: [{ required: true, message: '商品编号不能为空', trigger: 'blur' }]
 }
@@ -228,6 +229,7 @@ const submitForm = async () => {
     dialogVisible.value = false
   } finally {
     formLoading.value = false
+    getList()
   }
 }
 

+ 7 - 10
src/views/pay/notify/NotifyDetail.vue

@@ -7,32 +7,27 @@
       <el-descriptions-item label="通知状态">
         <dict-tag :type="DICT_TYPE.PAY_NOTIFY_STATUS" :value="detailData.status" />
       </el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2">
+
       <el-descriptions-item label="应用编号">{{ detailData.appId }}</el-descriptions-item>
       <el-descriptions-item label="应用名称">{{ detailData.appName }}</el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2">
+
       <el-descriptions-item label="关联编号">{{ detailData.dataId }}</el-descriptions-item>
       <el-descriptions-item label="通知类型">
         <dict-tag :type="DICT_TYPE.PAY_NOTIFY_TYPE" :value="detailData.type" />
       </el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2">
+
       <el-descriptions-item label="通知次数">{{ detailData.notifyTimes }}</el-descriptions-item>
       <el-descriptions-item label="最大通知次数">
         {{ detailData.maxNotifyTimes }}
       </el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2">
+
       <el-descriptions-item label="最后通知时间">
         {{ formatDate(detailData.lastExecuteTime) }}
       </el-descriptions-item>
       <el-descriptions-item label="下次通知时间">
         {{ formatDate(detailData.nextNotifyTime) }}
       </el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2">
+
       <el-descriptions-item label="创建时间">
         {{ formatDate(detailData.createTime) }}
       </el-descriptions-item>
@@ -40,8 +35,10 @@
         {{ formatDate(detailData.updateTime) }}
       </el-descriptions-item>
     </el-descriptions>
+
     <!-- 分割线 -->
     <el-divider />
+
     <el-descriptions :column="1" direction="vertical" border>
       <el-descriptions-item label="回调日志">
         <el-table :data="detailData.logs">

+ 0 - 16
src/views/pay/order/OrderDetail.vue

@@ -7,20 +7,14 @@
       <el-descriptions-item label="支付单号">
         <el-tag type="warning" size="small" v-if="detailData.no">{{ detailData.no }}</el-tag>
       </el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2" label-class-name="desc-label">
       <el-descriptions-item label="应用编号">{{ detailData.appId }}</el-descriptions-item>
       <el-descriptions-item label="应用名称">{{ detailData.appName }}</el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2" label-class-name="desc-label">
       <el-descriptions-item label="支付状态">
         <dict-tag :type="DICT_TYPE.PAY_ORDER_STATUS" :value="detailData.status" size="small" />
       </el-descriptions-item>
       <el-descriptions-item label="支付金额">
         <el-tag type="success" size="small">¥{{ (detailData.price / 100.0).toFixed(2) }}</el-tag>
       </el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2" label-class-name="desc-label">
       <el-descriptions-item label="手续费">
         <el-tag type="warning" size="small">
           ¥{{ (detailData.channelFeePrice / 100.0).toFixed(2) }}
@@ -29,16 +23,12 @@
       <el-descriptions-item label="手续费比例">
         {{ (detailData.channelFeeRate / 100.0).toFixed(2) }}%
       </el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2" label-class-name="desc-label">
       <el-descriptions-item label="支付时间">
         {{ formatDate(detailData.successTime) }}
       </el-descriptions-item>
       <el-descriptions-item label="失效时间">
         {{ formatDate(detailData.expireTime) }}
       </el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2" label-class-name="desc-label">
       <el-descriptions-item label="创建时间">
         {{ formatDate(detailData.createTime) }}
       </el-descriptions-item>
@@ -51,22 +41,16 @@
     <el-descriptions :column="2" label-class-name="desc-label">
       <el-descriptions-item label="商品标题">{{ detailData.subject }}</el-descriptions-item>
       <el-descriptions-item label="商品描述">{{ detailData.body }}</el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2" label-class-name="desc-label">
       <el-descriptions-item label="支付渠道">
         <dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="detailData.channelCode" />
       </el-descriptions-item>
       <el-descriptions-item label="支付 IP">{{ detailData.userIp }}</el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2" label-class-name="desc-label">
       <el-descriptions-item label="渠道单号">
         <el-tag size="mini" type="success" v-if="detailData.channelOrderNo">
           {{ detailData.channelOrderNo }}
         </el-tag>
       </el-descriptions-item>
       <el-descriptions-item label="渠道用户">{{ detailData.channelUserId }}</el-descriptions-item>
-    </el-descriptions>
-    <el-descriptions :column="2" label-class-name="desc-label">
       <el-descriptions-item label="退款金额">
         <el-tag size="mini" type="danger">
           ¥{{ (detailData.refundPrice / 100.0).toFixed(2) }}

+ 42 - 66
src/views/pay/refund/RefundDetail.vue

@@ -1,86 +1,69 @@
 <template>
-  <Dialog v-model="dialogVisible" title="详情" width="50%">
-    <el-descriptions :column="2">
-      <el-descriptions-item label="商户名称">{{ detailData.merchantName }}</el-descriptions-item>
-      <el-descriptions-item label="应用名称">{{ detailData.appName }}</el-descriptions-item>
-      <el-descriptions-item label="商品名称">{{ detailData.subject }}</el-descriptions-item>
-    </el-descriptions>
-    <el-divider />
-    <el-descriptions :column="2">
+  <Dialog v-model="dialogVisible" title="详情" width="700px">
+    <el-descriptions :column="2" label-class-name="desc-label">
       <el-descriptions-item label="商户退款单号">
-        <el-tag>{{ detailData.merchantRefundNo }}</el-tag>
+        <el-tag size="small">{{ refundDetail.merchantRefundId }}</el-tag>
       </el-descriptions-item>
-      <el-descriptions-item label="商户订单号">
-        {{ detailData.merchantOrderId }}
+      <el-descriptions-item label="渠道退款单号">
+        <el-tag type="success" size="small" v-if="refundDetail.channelRefundNo">{{
+          refundDetail.channelRefundNo
+        }}</el-tag>
       </el-descriptions-item>
-      <el-descriptions-item label="交易订单号">{{ detailData.tradeNo }}</el-descriptions-item>
-    </el-descriptions>
-    <el-divider />
-    <el-descriptions :column="2">
+      <el-descriptions-item label="商户支付单号">
+        <el-tag size="small">{{ refundDetail.merchantOrderId }}</el-tag>
+      </el-descriptions-item>
+      <el-descriptions-item label="渠道支付单号">
+        <el-tag type="success" size="small">{{ refundDetail.channelOrderNo }}</el-tag>
+      </el-descriptions-item>
+      <el-descriptions-item label="应用编号">{{ refundDetail.appId }}</el-descriptions-item>
+      <el-descriptions-item label="应用名称">{{ refundDetail.appName }}</el-descriptions-item>
       <el-descriptions-item label="支付金额">
-        <el-tag type="success">¥{{ parseFloat(detailData.payAmount / 100, 2).toFixed(2) }}</el-tag>
+        <el-tag type="success" size="small">
+          ¥{{ (refundDetail.payPrice / 100.0).toFixed(2) }}
+        </el-tag>
       </el-descriptions-item>
       <el-descriptions-item label="退款金额">
-        <el-tag class="tag-purple">
-          ¥{{ parseFloat(detailData.refundAmount / 100).toFixed(2) }}
+        <el-tag size="mini" type="danger">
+          ¥{{ (refundDetail.refundPrice / 100.0).toFixed(2) }}
         </el-tag>
       </el-descriptions-item>
-      <el-descriptions-item label="退款类型">
-        <dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_TYPE" :value="detailData.type" />
-      </el-descriptions-item>
       <el-descriptions-item label="退款状态">
-        <dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_STATUS" :value="detailData.status" />
+        <dict-tag :type="DICT_TYPE.PAY_REFUND_STATUS" :value="refundDetail.status" />
       </el-descriptions-item>
-      <el-descriptions-item label="创建时间">
-        {{ formatDate(detailData.createTime) }}
+      <el-descriptions-item label="退款时间">
+        {{ formatDate(refundDetail.successTime) }}
       </el-descriptions-item>
-      <el-descriptions-item label="退款成功时间">
-        {{ formatDate(detailData.successTime) }}
-      </el-descriptions-item>
-      <el-descriptions-item label="退款失效时间">
-        {{ formatDate(detailData.expireTime) }}
+      <el-descriptions-item label="创建时间">
+        {{ formatDate(refundDetail.createTime) }}
       </el-descriptions-item>
       <el-descriptions-item label="更新时间">
-        {{ formatDate(detailData.updateTime) }}
+        {{ formatDate(refundDetail.updateTime) }}
       </el-descriptions-item>
     </el-descriptions>
+    <!-- 分割线 -->
     <el-divider />
-    <el-descriptions :column="2">
-      <el-descriptions-item label="支付渠道">
-        {{ detailData.channelCodeName }}
-      </el-descriptions-item>
-      <el-descriptions-item label="支付 IP">
-        {{ detailData.userIp }}
-      </el-descriptions-item>
-      <el-descriptions-item label="回调地址">{{ detailData.notifyUrl }}</el-descriptions-item>
-      <el-descriptions-item label="回调状态">
-        <dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="detailData.notifyStatus" />
-      </el-descriptions-item>
-      <el-descriptions-item label="回调时间">
-        {{ formatDate(detailData.notifyTime) }}
+    <el-descriptions :column="2" label-class-name="desc-label">
+      <el-descriptions-item label="退款渠道">
+        <dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="refundDetail.channelCode" />
       </el-descriptions-item>
+      <el-descriptions-item label="退款原因">{{ refundDetail.reason }}</el-descriptions-item>
+      <el-descriptions-item label="退款 IP">{{ refundDetail.userIp }}</el-descriptions-item>
+      <el-descriptions-item label="通知 URL">{{ refundDetail.notifyUrl }}</el-descriptions-item>
     </el-descriptions>
+    <!-- 分割线 -->
     <el-divider />
-    <el-descriptions :column="2">
-      <el-descriptions-item label="渠道订单号">
-        {{ detailData.channelOrderNo }}
-      </el-descriptions-item>
-      <el-descriptions-item label="渠道退款单号">
-        {{ detailData.channelRefundNo }}
-      </el-descriptions-item>
+    <el-descriptions :column="2" label-class-name="desc-label">
       <el-descriptions-item label="渠道错误码">
-        {{ detailData.channelErrorCode }}
+        {{ refundDetail.channelErrorCode }}
       </el-descriptions-item>
       <el-descriptions-item label="渠道错误码描述">
-        {{ detailData.channelErrorMsg }}
+        {{ refundDetail.channelErrorMsg }}
       </el-descriptions-item>
     </el-descriptions>
-    <br />
-    <el-descriptions :column="1" border direction="vertical">
-      <el-descriptions-item label="渠道额外参数">
-        {{ detailData.channelExtras }}
+    <el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border>
+      <el-descriptions-item label="支付通道异步回调内容">
+        {{ refundDetail.channelNotifyData }}
       </el-descriptions-item>
-      <el-descriptions-item label="退款原因">{{ detailData.reason }}</el-descriptions-item>
     </el-descriptions>
   </Dialog>
 </template>
@@ -93,7 +76,7 @@ defineOptions({ name: 'PayRefundDetail' })
 
 const dialogVisible = ref(false) // 弹窗的是否展示
 const detailLoading = ref(false) // 表单的加载中
-const detailData = ref({})
+const refundDetail = ref({})
 
 /** 打开弹窗 */
 const open = async (id: number) => {
@@ -101,17 +84,10 @@ const open = async (id: number) => {
   // 设置数据
   detailLoading.value = true
   try {
-    detailData.value = await RefundApi.getRefund(id)
+    refundDetail.value = await RefundApi.getRefund(id)
   } finally {
     detailLoading.value = false
   }
 }
 defineExpose({ open }) // 提供 open 方法,用于打开弹窗
 </script>
-<style>
-.tag-purple {
-  color: #722ed1;
-  background: #f9f0ff;
-  border-color: #d3adf7;
-}
-</style>

+ 77 - 125
src/views/pay/refund/index.vue

@@ -10,21 +10,6 @@
       :inline="true"
       label-width="120px"
     >
-      <el-form-item label="所属商户" prop="merchantId">
-        <el-select
-          v-model="queryParams.merchantId"
-          clearable
-          placeholder="请选择所属商户"
-          class="!w-240px"
-        >
-          <el-option
-            v-for="item in merchantList"
-            :key="item.id"
-            :label="item.name"
-            :value="item.id"
-          />
-        </el-select>
-      </el-form-item>
       <el-form-item label="应用编号" prop="appId">
         <el-select
           v-model="queryParams.appId"
@@ -35,69 +20,66 @@
           <el-option v-for="item in appList" :key="item.id" :label="item.name" :value="item.id" />
         </el-select>
       </el-form-item>
-      <el-form-item label="渠道编码" prop="channelCode">
+      <el-form-item label="退款渠道" prop="channelCode">
         <el-select
           v-model="queryParams.channelCode"
-          placeholder="请输入渠道编码"
+          placeholder="请选择退款渠道"
           clearable
           class="!w-240px"
         >
           <el-option
-            v-for="dict in getStrDictOptions(DICT_TYPE.PAY_CHANNEL_CODE_TYPE)"
+            v-for="dict in getStrDictOptions(DICT_TYPE.PAY_CHANNEL_CODE)"
             :key="dict.value"
             :label="dict.label"
             :value="dict.value"
           />
         </el-select>
       </el-form-item>
-      <el-form-item label="退款类型" prop="type">
-        <el-select
-          v-model="queryParams.type"
-          placeholder="请选择退款类型"
+      <el-form-item label="商户支付单号" prop="merchantOrderId">
+        <el-input
+          v-model="queryParams.merchantOrderId"
+          placeholder="请输入商户支付单号"
           clearable
+          @keyup.enter="handleQuery"
           class="!w-240px"
-        >
-          <el-option
-            v-for="dict in getIntDictOptions(DICT_TYPE.PAY_REFUND_ORDER_TYPE)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
+        />
       </el-form-item>
-      <el-form-item label="商户退款订单号" prop="merchantRefundNo">
+      <el-form-item label="商户退款单号" prop="merchantRefundId">
         <el-input
-          v-model="queryParams.merchantRefundNo"
-          placeholder="请输入商户退款单号"
+          v-model="queryParams.merchantRefundId"
+          placeholder="请输入商户退款单号"
           clearable
           @keyup.enter="handleQuery"
           class="!w-240px"
         />
       </el-form-item>
-      <el-form-item label="退款状态" prop="status">
-        <el-select
-          v-model="queryParams.status"
-          placeholder="请选择退款状态"
+      <el-form-item label="渠道支付单号" prop="channelOrderNo">
+        <el-input
+          v-model="queryParams.channelOrderNo"
+          placeholder="请输入渠道支付单号"
           clearable
+          @keyup.enter="handleQuery"
           class="!w-240px"
-        >
-          <el-option
-            v-for="dict in getIntDictOptions(DICT_TYPE.PAY_REFUND_ORDER_STATUS)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
+        />
+      </el-form-item>
+      <el-form-item label="渠道退款单号" prop="channelRefundNo">
+        <el-input
+          v-model="queryParams.channelRefundNo"
+          placeholder="请输入渠道退款单号"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
+        />
       </el-form-item>
-      <el-form-item label="退款回调状态" prop="notifyStatus">
+      <el-form-item label="退款状态" prop="status">
         <el-select
-          v-model="queryParams.notifyStatus"
-          placeholder="请选择通知商户退款结果的回调状态"
+          v-model="queryParams.status"
+          placeholder="请选择退款状态"
           clearable
           class="!w-240px"
         >
           <el-option
-            v-for="dict in getIntDictOptions(DICT_TYPE.PAY_ORDER_NOTIFY_STATUS)"
+            v-for="dict in getIntDictOptions(DICT_TYPE.PAY_REFUND_STATUS)"
             :key="dict.value"
             :label="dict.label"
             :value="dict.value"
@@ -116,8 +98,8 @@
         />
       </el-form-item>
       <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 @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="success"
           plain
@@ -131,84 +113,72 @@
     </el-form>
   </ContentWrap>
 
+  <!-- 列表 -->
   <ContentWrap>
     <el-table v-loading="loading" :data="list">
       <el-table-column label="编号" align="center" prop="id" />
-      <el-table-column label="商户名称" align="center" prop="merchantName" width="120" />
-      <el-table-column label="应用名称" align="center" prop="appName" width="120" />
-      <el-table-column label="渠道名称" align="center" prop="channelCodeName" width="120" />
-      <el-table-column label="交易订单号" align="center" prop="tradeNo" width="140" />
-      <el-table-column label="商户订单编号" align="center" prop="merchantOrderId" width="140" />
-      <el-table-column label="商户订单号" align="left" width="230">
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        width="180"
+        :formatter="dateFormatter"
+      />
+      <el-table-column label="支付金额" align="center" prop="payPrice" width="100">
+        <template #default="scope">
+          ¥{{ parseFloat(scope.row.payPrice / 100).toFixed(2) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="退款金额" align="center" prop="refundPrice" width="100">
+        <template #default="scope">
+          ¥{{ parseFloat(scope.row.refundPrice / 100).toFixed(2) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="退款订单号" align="left" width="300">
         <template #default="scope">
           <p class="order-font">
-            <el-tag>退款</el-tag>
-            {{ scope.row.merchantRefundNo }}
+            <el-tag size="small">商户</el-tag> {{ scope.row.merchantRefundId }}
           </p>
           <p class="order-font">
-            <el-tag type="success">交易</el-tag>
-            {{ scope.row.merchantOrderId }}
+            <el-tag size="small" type="warning">退款</el-tag> {{ scope.row.no }}
+          </p>
+          <p class="order-font" v-if="scope.row.channelRefundNo">
+            <el-tag size="small" type="success">渠道</el-tag> {{ scope.row.channelRefundNo }}
           </p>
         </template>
       </el-table-column>
-      <el-table-column label="支付订单号" align="center" prop="merchantRefundNo" width="250">
+      <el-table-column label="支付订单号" align="left" width="300">
         <template #default="scope">
           <p class="order-font">
-            <el-tag>交易</el-tag>
-            {{ scope.row.tradeNo }}
+            <el-tag size="small">商户</el-tag> {{ scope.row.merchantOrderId }}
           </p>
           <p class="order-font">
-            <el-tag type="warning">渠道</el-tag>
-            {{ scope.row.channelOrderNo }}
+            <el-tag size="small" type="success">渠道</el-tag> {{ scope.row.channelOrderNo }}
           </p>
         </template>
       </el-table-column>
-      <el-table-column label="支付金额(元)" align="center" prop="payAmount" width="100">
-        <template #default="scope">
-          ¥{{ parseFloat(scope.row.payAmount / 100).toFixed(2) }}
-        </template>
-      </el-table-column>
-      <el-table-column label="退款金额(元)" align="center" prop="refundAmount" width="100">
-        <template #default="scope">
-          ¥{{ parseFloat(scope.row.refundAmount / 100).toFixed(2) }}
-        </template>
-      </el-table-column>
-      <el-table-column label="退款类型" align="center" prop="type" width="80">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_TYPE" :value="scope.row.type" />
-        </template>
-      </el-table-column>
       <el-table-column label="退款状态" align="center" prop="status">
         <template #default="scope">
-          <dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_STATUS" :value="scope.row.status" />
+          <dict-tag :type="DICT_TYPE.PAY_REFUND_STATUS" :value="scope.row.status" />
         </template>
       </el-table-column>
-      <el-table-column label="回调状态" align="center" prop="notifyStatus">
+      <el-table-column label="退款渠道" align="center" width="140">
         <template #default="scope">
-          <dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="scope.row.notifyStatus" />
+          <dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="scope.row.channelCode" />
         </template>
       </el-table-column>
       <el-table-column
-        label="退款原因"
-        align="center"
-        prop="reason"
-        width="140"
-        :show-overflow-tooltip="true"
-      />
-      <el-table-column
-        label="创建时间"
-        align="center"
-        prop="createTime"
-        width="180"
-        :formatter="dateFormatter"
-      />
-      <el-table-column
-        label="退款成功时间"
+        label="成功时间"
         align="center"
         prop="successTime"
         width="180"
         :formatter="dateFormatter"
       />
+      <el-table-column label="支付应用" align="center" prop="successTime" width="100">
+        <template #default="scope">
+          <span>{{ scope.row.appName }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="操作" align="center" fixed="right">
         <template #default="scope">
           <el-button
@@ -237,8 +207,8 @@
 <script lang="ts" setup>
 import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
 import { dateFormatter } from '@/utils/formatTime'
-// import * as MerchantApi from '@/api/pay/merchant'
 import * as RefundApi from '@/api/pay/refund'
+import * as AppApi from '@/api/pay/app'
 import RefundDetail from './RefundDetail.vue'
 import download from '@/utils/download'
 
@@ -254,34 +224,20 @@ const queryParams = reactive({
   pageSize: 10,
   merchantId: undefined,
   appId: undefined,
-  channelId: undefined,
   channelCode: undefined,
-  orderId: undefined,
-  tradeNo: undefined,
   merchantOrderId: undefined,
-  merchantRefundNo: undefined,
-  notifyUrl: undefined,
-  notifyStatus: undefined,
+  merchantRefundId: undefined,
   status: undefined,
-  type: undefined,
-  payAmount: undefined,
-  refundAmount: undefined,
-  reason: undefined,
-  userIp: undefined,
+  payPrice: undefined,
+  refundPrice: undefined,
   channelOrderNo: undefined,
   channelRefundNo: undefined,
-  channelErrorCode: undefined,
-  channelErrorMsg: undefined,
-  channelExtras: undefined,
-  expireTime: [],
-  successTime: [],
-  notifyTime: [],
-  createTime: []
+  createTime: [],
+  successTime: []
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出等待
 const appList = ref([]) // 支付应用列表集合
-const merchantList = ref([]) // 商户列表
 
 /** 搜索按钮操作 */
 const handleQuery = () => {
@@ -303,7 +259,7 @@ const getList = async () => {
 
 /** 重置按钮操作 */
 const resetQuery = () => {
-  queryFormRef.value.resetFields()
+  queryFormRef.value?.resetFields()
   handleQuery()
 }
 
@@ -331,13 +287,9 @@ const openDetail = (id: number) => {
 /** 初始化 **/
 onMounted(async () => {
   await getList()
-  // 加载商户列表
-  // merchantList.value = await MerchantApi.getMerchantListByName()
-  // TODO 芋艿:候选少一个查询应用列表的接口
-  // appList.value = await AppApi.getAppListByMerchantId()
+  appList.value = await AppApi.getAppList()
 })
 </script>
-
 <style>
 .order-font {
   padding: 2px 0;