“陈博论” hace 10 meses
padre
commit
a511deb462

+ 19 - 0
web/src/api/backend/pay/accountConfig.ts

@@ -0,0 +1,19 @@
+import createAxios from '/@/utils/axios'
+
+// 添加、编辑
+export function edit(data: anyObj) {
+    return createAxios({
+        url: '/admin/pay.AccountConfig/edit',
+        method: 'post',
+        data: data,
+    })
+}
+
+// 删除
+export function del(data: anyObj) {
+    return createAxios({
+        url: '/admin/pay.AccountConfig/delete',
+        method: 'post',
+        data: data,
+    })
+}

+ 28 - 0
web/src/api/backend/pay/config.ts

@@ -0,0 +1,28 @@
+import createAxios from '/@/utils/axios'
+
+// 添加、编辑
+export function edit(data: anyObj) {
+    return createAxios({
+        url: '/admin/pay.Config/edit',
+        method: 'post',
+        data: data,
+    })
+}
+
+// 删除
+export function del(data: anyObj) {
+    return createAxios({
+        url: '/admin/pay.Config/delete',
+        method: 'post',
+        data: data,
+    })
+}
+
+// 启用/禁用配置(启用此配置,需要禁用同类型的其他配置)
+export function toggleStatus(data: anyObj) {
+    return createAxios({
+        url: '/admin/pay.Config/toggleStatus',
+        method: 'post',
+        data: data,
+    })
+}

+ 1 - 0
web/src/assets/icons/pay-list.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1766990560067" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7081" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M832 256H192a32 32 0 0 1 0-64h640a32 32 0 1 1 0 64zM342.4 448H192a32 32 0 0 1 0-64h150.4a32 32 0 0 1 0 64zM342.4 640H192a32 32 0 0 1 0-64h150.4a32 32 0 0 1 0 64zM342.4 832H192a32 32 0 0 1 0-64h150.4a32 32 0 1 1 0 64zM810.88 579.2H512a32 32 0 0 1-32-32 32 32 0 0 1 32-32h296.32a32 32 0 0 1 32 32 32.64 32.64 0 0 1-29.44 32zM810.88 704H512a32 32 0 1 1 0-64h296.32a32 32 0 0 1 0 64z" fill="#333333" p-id="7082"></path><path d="M662.4 547.84a31.36 31.36 0 0 1-22.4-9.6L523.52 421.76a32 32 0 0 1 0-45.44 32 32 0 0 1 44.8 0l117.12 117.12a32 32 0 0 1 0 44.8 32.64 32.64 0 0 1-23.04 9.6z" fill="#333333" p-id="7083"></path><path d="M662.4 547.84a32 32 0 0 1-22.4-54.4l116.48-117.12a32 32 0 1 1 45.44 45.44l-116.48 116.48a32.64 32.64 0 0 1-23.04 9.6z" fill="#333333" p-id="7084"></path><path d="M662.4 832a32 32 0 0 1-32-32V512a32 32 0 0 1 32-32 32 32 0 0 1 32 32v288a32 32 0 0 1-32 32z" fill="#333333" p-id="7085"></path></svg>

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
web/src/assets/icons/withdrawal-configuration.svg


+ 247 - 0
web/src/views/backend/pay/accountConfig/index.vue

@@ -0,0 +1,247 @@
+<template>
+	<div class="default-main ba-table-box">
+		<!-- 表格顶部菜单 -->
+		<TableHeader :buttons="['refresh','columnDisplay']">
+			<el-form style="margin-left: 5px;padding-top: 20px;" inline :model="seachForm" @submit.prevent="handleSearch">
+				<el-form-item>
+					<el-input v-model="seachForm.user_name" placeholder="请输入用户名称" @blur="handleSearch" clearable />
+				</el-form-item>
+				<el-form-item>
+					<el-select v-model="seachForm.account_type" placeholder="请选择账户类型" style="width: 150px;" clearable filterable @change="handleSearch">
+						<el-option label="银行卡" value="bank" />
+						<el-option label="支付宝" value="alipay" />
+						<el-option label="微信" value="wechat" />
+					</el-select>
+				</el-form-item>
+				<el-form-item>
+					<el-select v-model="seachForm.status" placeholder="请选择状态" style="width: 150px;" clearable filterable @change="handleSearch">
+						<el-option label="禁用" value="0" />
+						<el-option label="启用" value="1" />
+					</el-select>
+				</el-form-item>
+                <el-form-item>
+                    <el-button type="primary" @click="onAdd" v-auth="'edit'">添加</el-button>
+                </el-form-item>
+			</el-form>
+		</TableHeader>
+		<!-- 表格 -->
+		<!-- 要使用`el-table`组件原有的属性,直接加在Table标签上即可 -->
+		<Table ref="tableRef">
+            <template #operate>
+                <el-table-column prop="operate" align="center" label="操作" min-width="150" fixed="right">
+                    <template #default="scope">
+                        <el-button type="primary" size="small" @click="onEdit(scope.row)" v-auth="'edit'">编辑</el-button>
+                        <el-button type="danger" size="small" @click="handleDel(scope.row)" v-auth="'delete'">删除</el-button>
+                    </template>
+                </el-table-column>
+            </template>
+        </Table>
+        <!-- 表单 -->
+        <PopupForm @success="baTable.getIndex()" />
+
+	</div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, provide, useTemplateRef } from 'vue'
+import baTableClass from '/@/utils/baTable'
+import Table from '/@/components/table/index.vue'
+import TableHeader from '/@/components/table/header/index.vue'
+import PopupForm from './popupForm.vue'
+import { baTableApi } from '/@/api/common'
+import { useI18n } from 'vue-i18n'
+import { cloneDeep } from 'lodash-es'
+import { ElMessageBox } from 'element-plus'
+import { del } from '/@/api/backend/pay/accountConfig'
+
+defineOptions({
+    name: 'pay/accountConfig',
+})
+
+
+const { t } = useI18n()
+
+const seachForm = reactive({
+	user_name: "",
+	account_type: "",
+	status: ""
+})
+
+// 搜索
+const handleSearch = () => {
+	baTable.table.filter!.page = 1
+	baTable.table.filter!.user_name = seachForm.user_name
+	baTable.table.filter!.account_type = seachForm.account_type
+	baTable.table.filter!.status = seachForm.status
+	baTable.getIndex()
+}
+
+const baTable = new baTableClass(
+	new baTableApi('/admin/pay.AccountConfig/'),
+	{
+		column: [
+			{ 
+				label: '编号', 
+				prop: 'id', 
+				align: 'center', 
+				width: 70,
+			},
+			{ 
+				label: '用户名称', 
+				prop: 'user_name', 
+				align: 'center', 
+				minWidth: 120,
+			},
+			{
+				label: '账户类型',
+				prop: 'account_type_text',
+				align: 'center',
+				minWidth: 100,
+				render: 'tag',
+				custom: {
+					'银行卡': 'warning',
+					'支付宝': '',
+					'微信': 'success',
+				},
+			},
+			{ 
+				label: '账户姓名', 
+				prop: 'account_name', 
+				align: 'center', 
+				minWidth: 120,
+			},
+			{ 
+				label: '银行名称', 
+				prop: 'bank_name', 
+				align: 'center', 
+				minWidth: 150,
+			},
+			{ 
+				label: '卡号', 
+				prop: 'card_number_masked', 
+				align: 'center', 
+				minWidth: 180,
+			},
+			{
+				label: '是否默认',
+				prop: 'is_default_text',
+				align: 'center',
+				render: 'tag',
+				custom: {
+					'是': '',
+					'否': 'info',
+				},
+				minWidth: 100,
+			},
+			{
+				label: '状态',
+				prop: 'status_text',
+				align: 'center',
+				render: 'tag',
+				custom: {
+					'禁用': 'danger',
+					'启用': 'success',
+				},
+				minWidth: 100,
+			},
+			{ 
+				label: '备注', 
+				prop: 'remark', 
+				align: 'center', 
+				minWidth: 150,
+			},
+			{ 
+				label: '创建时间', 
+				prop: 'create_time', 
+				align: 'center',
+				minWidth: 170,
+				operator: false,
+			},
+			{ 
+				label: '更新时间', 
+				prop: 'update_time', 
+				align: 'center',
+				minWidth: 170,
+				operator: false,
+			},
+			{
+				label: '操作',
+				align: 'center',
+				width: '180',
+				render: 'slot',
+				slotName: 'operate',
+			},
+		],
+		dblClickNotEditColumn: [undefined,],
+		filter: {
+			limit: 20,
+		},
+	},
+	{
+		defaultItems: {
+			id: null,
+			user_id: '',
+			account_type: 'bank',
+			account_name: '',
+			is_default: '0',
+			status: '1',
+			remark: '',
+			bank_name: '',
+			bank_branch: '',
+			bank_province: '',
+			bank_city: '',
+			card_number: '',
+			alipay_account: '',
+			wechat_account: '',
+		},
+	},
+)
+
+baTable.mount()
+baTable.getIndex()
+provide('baTable', baTable)
+
+// 删除
+const handleDel = (row: TableRow) => {
+    ElMessageBox.confirm(`确定删除账户配置"${row.account_name || row.id}"吗?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+    }).then(() => {
+        del({ id: row.id }).then(() => {
+            baTable.getIndex()
+        })
+    })
+}
+
+const openForm = (type: 'Add' | 'Edit', row?: TableRow) => {
+    if (type === 'Edit' && !row) return
+    baTable.form.items = row ? cloneDeep(row) : cloneDeep(baTable.form.defaultItems)
+    // 统一状态和是否默认为字符串,避免下拉框/单选框显示问题
+    if (baTable.form.items) {
+        if (baTable.form.items.status !== undefined && baTable.form.items.status !== null) {
+            baTable.form.items.status = String(baTable.form.items.status)
+        }
+        if (baTable.form.items.is_default !== undefined && baTable.form.items.is_default !== null) {
+            baTable.form.items.is_default = String(baTable.form.items.is_default)
+        }
+        if (baTable.form.items.account_type !== undefined && baTable.form.items.account_type !== null) {
+            baTable.form.items.account_type = String(baTable.form.items.account_type)
+        }
+        if (baTable.form.items.user_id !== undefined && baTable.form.items.user_id !== null) {
+            baTable.form.items.user_id = String(baTable.form.items.user_id)
+        }
+    }
+    baTable.form.operate = type
+    baTable.form.operateIds = row && baTable.table.pk ? [row[baTable.table.pk]] : []
+}
+
+const onAdd = () => openForm('Add')
+const onEdit = (row: TableRow) => openForm('Edit', row)
+
+</script>
+<style scoped lang="scss">
+.el-form--inline .el-form-item {
+	margin-right: 8px;
+}
+</style>

+ 227 - 0
web/src/views/backend/pay/accountConfig/popupForm.vue

@@ -0,0 +1,227 @@
+<template>
+    <!-- 对话框表单 -->
+    <el-dialog class="ba-operate-dialog" :close-on-click-modal="false"
+        :model-value="['Add', 'Edit'].includes(baTable.form.operate!)" @close="baTable.toggleForm"
+        :destroy-on-close="true">
+        <template #header>
+            <div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
+                {{ baTable.form.operate ? t(baTable.form.operate) : '' }}
+            </div>
+        </template>
+        <el-scrollbar v-loading="baTable.form.loading" class="ba-table-form-scrollbar">
+            <div class="ba-operate-form" :class="'ba-' + baTable.form.operate + '-form'"
+                :style="config.layout.shrink ? '' : 'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'">
+                <el-form ref="formRef" @keyup.enter="btnSubmit" :model="baTable.form.items"
+                    :label-position="config.layout.shrink ? 'top' : 'right'"
+                    :label-width="baTable.form.labelWidth + 'px'" :rules="rules" v-if="!baTable.form.loading">
+                    
+                    <FormItem
+                        type="remoteSelect"
+                        prop="user_id"
+                        label="用户"
+                        v-model="baTable.form.items!.user_id"
+                        placeholder="请选择用户"
+                        :input-attr="{
+                            pk: 'user.id',
+                            field: 'name',
+                            remoteUrl: '/admin/userCenter.user/index',
+                        }"
+                    />
+                    
+                    <el-form-item label="账户类型" prop="account_type">
+                        <el-select v-model="baTable.form.items!.account_type" placeholder="请选择账户类型" style="width: 100%">
+                            <el-option label="银行卡" value="bank" />
+                            <el-option label="支付宝" value="alipay" />
+                            <el-option label="微信" value="wechat" />
+                        </el-select>
+                    </el-form-item>
+                    
+                    <FormItem label="账户姓名" prop="account_name" v-model="baTable.form.items!.account_name" type="string"
+                        placeholder="请输入账户姓名" />
+                    
+                    <FormItem label="是否默认" prop="is_default" type="radio" v-model="baTable.form.items!.is_default"
+                        :input-attr="{ border: true, content: isDefaultOptions }" />
+                    
+                    <FormItem label="状态" prop="status" type="radio" v-model="baTable.form.items!.status"
+                        :input-attr="{ border: true, content: statusOptions }" />
+                    
+                    <!-- 银行卡相关字段 -->
+                    <template v-if="baTable.form.items!.account_type === 'bank'">
+                        <FormItem label="银行名称" prop="bank_name" v-model="baTable.form.items!.bank_name" type="string"
+                            placeholder="请输入银行名称" />
+                        <FormItem label="支行名称" prop="bank_branch" v-model="baTable.form.items!.bank_branch" type="string"
+                            placeholder="请输入支行名称" />
+                        <FormItem label="省份" prop="bank_province" v-model="baTable.form.items!.bank_province" type="string"
+                            placeholder="请输入省份" />
+                        <FormItem label="城市" prop="bank_city" v-model="baTable.form.items!.bank_city" type="string"
+                            placeholder="请输入城市" />
+                        <FormItem label="银行卡号" prop="card_number" v-model="baTable.form.items!.card_number" type="string"
+                            placeholder="请输入银行卡号" />
+                    </template>
+                    
+                    <!-- 支付宝相关字段 -->
+                    <template v-if="baTable.form.items!.account_type === 'alipay'">
+                        <FormItem label="支付宝账号" prop="alipay_account" v-model="baTable.form.items!.alipay_account" type="string"
+                            placeholder="请输入支付宝账号" />
+                    </template>
+                    
+                    <!-- 微信相关字段 -->
+                    <template v-if="baTable.form.items!.account_type === 'wechat'">
+                        <FormItem label="微信账号" prop="wechat_account" v-model="baTable.form.items!.wechat_account" type="string"
+                            placeholder="请输入微信账号" />
+                    </template>
+                    
+                    <el-form-item label="备注" prop="remark">
+                        <el-input
+                            v-model="baTable.form.items!.remark"
+                            type="textarea"
+                            :rows="3"
+                            placeholder="请输入备注"
+                        />
+                    </el-form-item>
+                </el-form>
+            </div>
+        </el-scrollbar>
+        <template #footer>
+            <div :style="'width: calc(100% - ' + baTable.form.labelWidth! / 1.8 + 'px)'">
+                <el-button @click="baTable.toggleForm('')">{{ t('Cancel') }}</el-button>
+                <el-button v-blur :loading="baTable.form.submitLoading" @click="btnSubmit" type="primary">
+                    {{ baTable.form.operateIds && baTable.form.operateIds.length > 1 ? t('Save and edit next item') :
+                    t('Save') }}
+                </el-button>
+            </div>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { reactive, inject, useTemplateRef, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+import type baTableClass from '/@/utils/baTable'
+import FormItem from '/@/components/formItem/index.vue'
+import type { FormItemRule } from 'element-plus'
+import { useConfig } from '/@/stores/config'
+import { buildValidatorData } from '/@/utils/validate'
+import { edit } from '/@/api/backend/pay/accountConfig'
+
+const config = useConfig()
+const formRef = useTemplateRef('formRef')
+const baTable = inject('baTable') as baTableClass
+const emit = defineEmits(['success'])
+
+const { t } = useI18n()
+
+const statusOptions = {
+    '0': '禁用',
+    '1': '启用',
+}
+
+const isDefaultOptions = {
+    '0': '否',
+    '1': '是',
+}
+
+// 验证规则
+const rules: Partial<Record<string, FormItemRule[]>> = reactive({
+    user_id: [buildValidatorData({ name: 'required', title: '用户' })],
+    account_type: [buildValidatorData({ name: 'required', trigger: 'change', title: '账户类型' })],
+    account_name: [buildValidatorData({ name: 'required', title: '账户姓名' })],
+    is_default: [buildValidatorData({ name: 'required', trigger: 'change', title: '是否默认' })],
+    status: [buildValidatorData({ name: 'required', trigger: 'change', title: '状态' })],
+})
+
+// 监听账户类型变化,动态更新验证规则
+const updateValidationRules = (accountType: string) => {
+    // 移除条件字段的验证规则(如果存在)
+    if (rules.bank_name) delete rules.bank_name
+    if (rules.card_number) delete rules.card_number
+    if (rules.alipay_account) delete rules.alipay_account
+    if (rules.wechat_account) delete rules.wechat_account
+    
+    // 根据账户类型添加相应的验证规则
+    if (accountType === 'bank') {
+        rules.bank_name = [buildValidatorData({ name: 'required', title: '银行名称' })]
+        rules.card_number = [buildValidatorData({ name: 'required', title: '银行卡号' })]
+    } else if (accountType === 'alipay') {
+        rules.alipay_account = [buildValidatorData({ name: 'required', title: '支付宝账号' })]
+    } else if (accountType === 'wechat') {
+        rules.wechat_account = [buildValidatorData({ name: 'required', title: '微信账号' })]
+    }
+}
+
+watch(
+    () => baTable.form.items!.account_type,
+    (newType) => {
+        if (newType) {
+            updateValidationRules(newType)
+        }
+    },
+    { immediate: true }
+)
+
+// 监听表单打开,确保验证规则正确初始化
+watch(
+    () => baTable.form.operate,
+    (newOperate) => {
+        if (newOperate && baTable.form.items!.account_type) {
+            updateValidationRules(baTable.form.items!.account_type)
+        }
+    }
+)
+
+// 提交
+const btnSubmit = () => {
+    formRef.value?.validate((valid: boolean) => {
+        if (valid) {
+            baTable.table.loading = true
+
+            let params: any = {
+                id: baTable.form.items!.id,
+                user_id: baTable.form.items!.user_id,
+                account_type: baTable.form.items!.account_type,
+                account_name: baTable.form.items!.account_name,
+                is_default: baTable.form.items!.is_default,
+                status: baTable.form.items!.status,
+                remark: baTable.form.items!.remark || '',
+            }
+            
+            // 根据账户类型添加相应字段
+            if (baTable.form.items!.account_type === 'bank') {
+                params.bank_name = baTable.form.items!.bank_name || ''
+                params.bank_branch = baTable.form.items!.bank_branch || ''
+                params.bank_province = baTable.form.items!.bank_province || ''
+                params.bank_city = baTable.form.items!.bank_city || ''
+                params.card_number = baTable.form.items!.card_number || ''
+                params.alipay_account = ''
+                params.wechat_account = ''
+            } else if (baTable.form.items!.account_type === 'alipay') {
+                params.alipay_account = baTable.form.items!.alipay_account || ''
+                params.bank_name = ''
+                params.bank_branch = ''
+                params.bank_province = ''
+                params.bank_city = ''
+                params.card_number = ''
+                params.wechat_account = ''
+            } else if (baTable.form.items!.account_type === 'wechat') {
+                params.wechat_account = baTable.form.items!.wechat_account || ''
+                params.bank_name = ''
+                params.bank_branch = ''
+                params.bank_province = ''
+                params.bank_city = ''
+                params.card_number = ''
+                params.alipay_account = ''
+            }
+
+            edit(params).then(() => {
+                baTable.toggleForm()
+                emit('success')
+            }).finally(() => {
+                baTable.table.loading = false
+            })
+
+        }
+    })
+}
+</script>
+
+<style scoped lang="scss"></style>

+ 251 - 0
web/src/views/backend/pay/config/index.vue

@@ -0,0 +1,251 @@
+<template>
+	<div class="default-main ba-table-box">
+		<!-- 表格顶部菜单 -->
+		<TableHeader :buttons="['refresh','columnDisplay']">
+			<el-form style="margin-left: 5px;padding-top: 20px;" inline :model="seachForm" @submit.prevent="handleSearch">
+				<el-form-item>
+					<el-input v-model="seachForm.name" placeholder="请输入配置名称" @blur="handleSearch" clearable />
+				</el-form-item>
+				<el-form-item>
+					<el-select v-model="seachForm.type" placeholder="请选择支付类型" style="width: 150px;" clearable filterable @change="handleSearch">
+						<el-option value="1" label="微信支付" />
+						<el-option value="2" label="支付宝支付" />
+					</el-select>
+				</el-form-item>
+				<el-form-item>
+					<el-select v-model="seachForm.status" placeholder="请选择状态" style="width: 150px;" clearable filterable @change="handleSearch">
+						<el-option label="禁用" value="0" />
+						<el-option label="启用" value="1" />
+					</el-select>
+				</el-form-item>
+                <el-form-item>
+                    <el-button type="primary" @click="onAdd" v-auth="'edit'">添加</el-button>
+                </el-form-item>
+			</el-form>
+		</TableHeader>
+		<!-- 表格 -->
+		<!-- 要使用`el-table`组件原有的属性,直接加在Table标签上即可 -->
+		<Table ref="tableRef">
+            <template #operate>
+                <el-table-column prop="operate" align="center" label="操作" min-width="150" fixed="right">
+                    <template #default="scope">
+                        <el-button type="primary" size="small" @click="onEdit(scope.row)" v-auth="'edit'">编辑</el-button>
+                        <el-button type="danger" size="small" @click="handleDel(scope.row)" v-auth="'delete'">删除</el-button>
+                    </template>
+                </el-table-column>
+            </template>
+        </Table>
+        <!-- 表单 -->
+        <PopupForm @success="baTable.getIndex()" />
+
+	</div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, provide, useTemplateRef } from 'vue'
+import baTableClass from '/@/utils/baTable'
+import Table from '/@/components/table/index.vue'
+import TableHeader from '/@/components/table/header/index.vue'
+import PopupForm from './popupForm.vue'
+import { baTableApi } from '/@/api/common'
+import { useI18n } from 'vue-i18n'
+import { cloneDeep } from 'lodash-es'
+import { ElMessageBox } from 'element-plus'
+import { del } from '/@/api/backend/pay/config'
+
+defineOptions({
+    name: 'pay/config',
+})
+
+
+const { t } = useI18n()
+
+const seachForm = reactive({
+	name: "",
+	type: "",
+	status: ""
+})
+
+// 搜索
+const handleSearch = () => {
+	baTable.table.filter!.page = 1
+	baTable.table.filter!.name = seachForm.name
+	baTable.table.filter!.type = seachForm.type
+	baTable.table.filter!.status = seachForm.status
+	baTable.getIndex()
+}
+
+const baTable = new baTableClass(
+	new baTableApi('/admin/pay.Config/'),
+	{
+		column: [
+			{ 
+				label: '编号', 
+				prop: 'id', 
+				align: 'center', 
+				width: 70,
+			},
+			{ 
+				label: '配置名称', 
+				prop: 'name', 
+				align: 'center', 
+				minWidth: 160,
+			},
+			{
+				label: '支付类型',
+				prop: 'type',
+				align: 'center',
+				minWidth: 120,
+				render: 'tag',
+				custom: {
+					'1': 'success',
+					'2': '',
+				},
+				replaceValue: {
+					'1': '微信支付',
+					'2': '支付宝支付',
+				},
+			},
+			{ 
+				label: '应用ID/AppId', 
+				prop: 'app_id', 
+				align: 'center', 
+				minWidth: 180,
+			},
+			{ 
+				label: '商户ID', 
+				prop: 'mch_id', 
+				align: 'center', 
+				minWidth: 120,
+			},
+			{ 
+				label: '商户密钥/API密钥', 
+				prop: 'key', 
+				align: 'center', 
+				minWidth: 200,
+			},
+			{ 
+				label: '支付通知地址', 
+				prop: 'notify_url', 
+				align: 'center', 
+				minWidth: 250,
+			},
+			{ 
+				label: 'H5支付通知地址', 
+				prop: 'h5_notify_url', 
+				align: 'center', 
+				minWidth: 250,
+			},
+			{ 
+				label: '支付宝公钥地址', 
+				prop: 'public_key_path', 
+				align: 'center', 
+				minWidth: 180,
+			},
+			{ 
+				label: '支付宝私钥地址', 
+				prop: 'private_key_path', 
+				align: 'center', 
+				minWidth: 180,
+			},
+			{
+				label: '状态',
+				prop: 'status',
+				align: 'center',
+				render: 'tag',
+				custom: {
+					'0': 'danger',
+					'1': 'success',
+				},
+				replaceValue: {
+					'0': '禁用',
+					'1': '启用',
+				},
+				minWidth: 100,
+			},
+			{ 
+				label: '创建时间', 
+				prop: 'create_time', 
+				align: 'center',
+				minWidth: 170,
+				operator: false,
+			},
+			{ 
+				label: '更新时间', 
+				prop: 'update_time', 
+				align: 'center',
+				minWidth: 170,
+				operator: false,
+			},
+			{
+				label: '操作',
+				align: 'center',
+				width: '180',
+				render: 'slot',
+				slotName: 'operate',
+			},
+		],
+		dblClickNotEditColumn: [undefined,],
+		filter: {
+			limit: 20,
+		},
+	},
+	{
+		defaultItems: {
+            id: null,
+            name: '',
+            type: '',
+            status: '1',
+            app_id: '',
+            mch_id: '',
+            key: '',
+            notify_url: '',
+            h5_notify_url: '',
+            public_key_path: '',
+            private_key_path: '',
+		},
+	},
+)
+
+baTable.mount()
+baTable.getIndex()
+provide('baTable', baTable)
+
+// 删除
+const handleDel = (row: TableRow) => {
+    ElMessageBox.confirm(`确定删除配置"${row.name}"吗?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+    }).then(() => {
+        del({ id: row.id }).then(() => {
+            baTable.getIndex()
+        })
+    })
+}
+
+const openForm = (type: 'Add' | 'Edit', row?: TableRow) => {
+    if (type === 'Edit' && !row) return
+    baTable.form.items = row ? cloneDeep(row) : cloneDeep(baTable.form.defaultItems)
+    // 统一支付类型和状态为字符串,避免下拉框/单选框显示为“1”而不是对应中文
+    if (baTable.form.items) {
+        if (baTable.form.items.type !== undefined && baTable.form.items.type !== null) {
+            baTable.form.items.type = String(baTable.form.items.type)
+        }
+        if (baTable.form.items.status !== undefined && baTable.form.items.status !== null) {
+            baTable.form.items.status = String(baTable.form.items.status)
+        }
+    }
+    baTable.form.operate = type
+    baTable.form.operateIds = row && baTable.table.pk ? [row[baTable.table.pk]] : []
+}
+
+const onAdd = () => openForm('Add')
+const onEdit = (row: TableRow) => openForm('Edit', row)
+
+</script>
+<style scoped lang="scss">
+.el-form--inline .el-form-item {
+	margin-right: 8px;
+}
+</style>

+ 131 - 0
web/src/views/backend/pay/config/popupForm.vue

@@ -0,0 +1,131 @@
+<template>
+    <!-- 对话框表单 -->
+    <el-dialog class="ba-operate-dialog" :close-on-click-modal="false"
+        :model-value="['Add', 'Edit'].includes(baTable.form.operate!)" @close="baTable.toggleForm"
+        :destroy-on-close="true">
+        <template #header>
+            <div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
+                {{ baTable.form.operate ? t(baTable.form.operate) : '' }}
+            </div>
+        </template>
+        <el-scrollbar v-loading="baTable.form.loading" class="ba-table-form-scrollbar">
+            <div class="ba-operate-form" :class="'ba-' + baTable.form.operate + '-form'"
+                :style="config.layout.shrink ? '' : 'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'">
+                <el-form ref="formRef" @keyup.enter="btnSubmit" :model="baTable.form.items"
+                    :label-position="config.layout.shrink ? 'top' : 'right'"
+                    :label-width="baTable.form.labelWidth + 'px'" :rules="rules" v-if="!baTable.form.loading">
+                    <FormItem label="配置名称" prop="name" v-model="baTable.form.items!.name" type="string"
+                        :placeholder="t('Please input field', { field: '配置名称' })" />
+                    <el-form-item label="支付类型" prop="type">
+                        <el-select v-model="baTable.form.items!.type" placeholder="请选择支付类型" style="width: 100%">
+                            <el-option label="微信支付" value="1" />
+                            <el-option label="支付宝支付" value="2" />
+                        </el-select>
+                    </el-form-item>
+                    <FormItem label="状态" prop="status" type="radio" v-model="baTable.form.items!.status"
+                        :input-attr="{ border: true, content: statusOptions }" />
+                    <el-form-item label="应用ID/AppId" prop="app_id">
+                        <el-input v-model="baTable.form.items!.app_id" placeholder="请输入应用ID/AppId" />
+                    </el-form-item>
+                    <el-form-item label="商户ID" prop="mch_id">
+                        <el-input v-model="baTable.form.items!.mch_id" placeholder="请输入商户ID(商户号/PID)" />
+                    </el-form-item>
+                    <el-form-item label="商户密钥/API密钥" prop="key">
+                        <el-input v-model="baTable.form.items!.key" placeholder="请输入商户密钥或API密钥" />
+                    </el-form-item>
+                    <el-form-item label="支付通知地址" prop="notify_url">
+                        <el-input v-model="baTable.form.items!.notify_url"
+                            placeholder="https://your-domain.com/api/pay/notify" />
+                    </el-form-item>
+                    <el-form-item label="H5支付通知地址" prop="h5_notify_url">
+                        <el-input v-model="baTable.form.items!.h5_notify_url"
+                            placeholder="https://your-domain.com/api/pay/h5-notify" />
+                    </el-form-item>
+                    <el-form-item label="支付宝公钥地址" prop="public_key_path">
+                        <el-input v-model="baTable.form.items!.public_key_path"
+                            placeholder="请输入支付宝公钥证书路径或内容标识" />
+                    </el-form-item>
+                    <el-form-item label="支付宝私钥地址" prop="private_key_path">
+                        <el-input v-model="baTable.form.items!.private_key_path"
+                            placeholder="请输入支付宝私钥证书路径或内容标识" />
+                    </el-form-item>
+                </el-form>
+            </div>
+        </el-scrollbar>
+        <template #footer>
+            <div :style="'width: calc(100% - ' + baTable.form.labelWidth! / 1.8 + 'px)'">
+                <el-button @click="baTable.toggleForm('')">{{ t('Cancel') }}</el-button>
+                <el-button v-blur :loading="baTable.form.submitLoading" @click="btnSubmit" type="primary">
+                    {{ baTable.form.operateIds && baTable.form.operateIds.length > 1 ? t('Save and edit next item') :
+                    t('Save') }}
+                </el-button>
+            </div>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { reactive, inject, useTemplateRef, } from 'vue'
+import { useI18n } from 'vue-i18n'
+import type baTableClass from '/@/utils/baTable'
+import FormItem from '/@/components/formItem/index.vue'
+import type { FormItemRule } from 'element-plus'
+import { useConfig } from '/@/stores/config'
+import { buildValidatorData } from '/@/utils/validate'
+import { edit } from '/@/api/backend/pay/config'
+
+const config = useConfig()
+const formRef = useTemplateRef('formRef')
+const baTable = inject('baTable') as baTableClass
+const emit = defineEmits(['success'])
+
+const { t } = useI18n()
+
+const rules: Partial<Record<string, FormItemRule[]>> = reactive({
+    name: [buildValidatorData({ name: 'required', title: '配置名称' })],
+    type: [buildValidatorData({ name: 'required', trigger: 'change', title: '支付类型' })],
+    status: [buildValidatorData({ name: 'required', trigger: 'change', title: '状态' })],
+    app_id: [buildValidatorData({ name: 'required', title: '应用ID/AppId' })],
+    mch_id: [buildValidatorData({ name: 'required', title: '商户ID' })],
+    key: [buildValidatorData({ name: 'required', title: '商户密钥/API密钥' })],
+    notify_url: [buildValidatorData({ name: 'required', title: '支付通知地址' })],
+})
+
+const statusOptions = {
+    '0': '禁用',
+    '1': '启用',
+}
+
+// 提交
+const btnSubmit = () => {
+    formRef.value?.validate((valid: boolean) => {
+        if (valid) {
+            baTable.table.loading = true
+
+            let params = {
+                id: baTable.form.items!.id,
+                name: baTable.form.items!.name,
+                type: baTable.form.items!.type,
+                status: baTable.form.items!.status,
+                app_id: baTable.form.items!.app_id,
+                mch_id: baTable.form.items!.mch_id,
+                key: baTable.form.items!.key,
+                notify_url: baTable.form.items!.notify_url,
+                h5_notify_url: baTable.form.items!.h5_notify_url,
+                public_key_path: baTable.form.items!.public_key_path,
+                private_key_path: baTable.form.items!.private_key_path,
+            }
+
+            edit(params).then(() => {
+                baTable.toggleForm()
+                emit('success')
+            }).finally(() => {
+                baTable.table.loading = false
+            })
+
+        }
+    })
+}
+</script>
+
+<style scoped lang="scss"></style>

+ 0 - 13
web/src/views/backend/payment/alipay/index.vue

@@ -1,13 +0,0 @@
-<template>
-    <div class="default-main ba-table-box">
-        <el-card shadow="never">
-            支付宝配置
-        </el-card>
-    </div>
-</template>
-
-<script setup lang="ts">
-defineOptions({
-    name: 'payment/alipay',
-})
-</script>

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio