This commit is contained in:
307
src/views/settings/wechat-config/detail.vue
Normal file
307
src/views/settings/wechat-config/detail.vue
Normal file
@@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<div class="wechat-config-detail-page">
|
||||
<ElCard shadow="never">
|
||||
<!-- 页面头部 -->
|
||||
<div class="detail-header">
|
||||
<ElButton @click="handleBack">
|
||||
<template #icon>
|
||||
<ElIcon><ArrowLeft /></ElIcon>
|
||||
</template>
|
||||
返回
|
||||
</ElButton>
|
||||
<h2 class="detail-title">{{ pageTitle }}</h2>
|
||||
</div>
|
||||
|
||||
<!-- 详情内容 -->
|
||||
<DetailPage v-if="detailData" :sections="detailSections" :data="detailData" />
|
||||
|
||||
<!-- 加载中 -->
|
||||
<div v-if="loading" class="loading-container">
|
||||
<ElIcon class="is-loading"><Loading /></ElIcon>
|
||||
<span>加载中...</span>
|
||||
</div>
|
||||
</ElCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElCard, ElButton, ElIcon, ElMessage } from 'element-plus'
|
||||
import { ArrowLeft, Loading } from '@element-plus/icons-vue'
|
||||
import DetailPage from '@/components/common/DetailPage.vue'
|
||||
import type { DetailSection } from '@/components/common/DetailPage.vue'
|
||||
import { WechatConfigService } from '@/api/modules'
|
||||
import type { WechatConfig, PaymentProviderType } from '@/types/api'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { RoutesAlias } from '@/router/routesAlias'
|
||||
|
||||
defineOptions({ name: 'WechatConfigDetail' })
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const loading = ref(false)
|
||||
const detailData = ref<WechatConfig | null>(null)
|
||||
|
||||
const configId = computed(() => Number(route.params.id))
|
||||
const pageTitle = computed(() => `支付配置详情 #${configId.value}`)
|
||||
|
||||
// 获取支付渠道类型文本
|
||||
const getProviderTypeText = (type: PaymentProviderType): string => {
|
||||
const typeMap: Record<PaymentProviderType, string> = {
|
||||
wechat: '微信直连',
|
||||
fuiou: '富友支付'
|
||||
}
|
||||
return typeMap[type] || type
|
||||
}
|
||||
|
||||
// 获取配置状态文本
|
||||
const getConfigStatus = (value: string | undefined | null): string => {
|
||||
if (!value) return '未配置'
|
||||
return value === 'configured' ? '已配置' : value
|
||||
}
|
||||
|
||||
// 基础详情配置
|
||||
const baseSection: DetailSection = {
|
||||
title: '基本信息',
|
||||
fields: [
|
||||
{ label: '配置ID', prop: 'id' },
|
||||
{ label: '配置名称', prop: 'name' },
|
||||
{
|
||||
label: '支付渠道类型',
|
||||
formatter: (_, data) => getProviderTypeText(data.provider_type)
|
||||
},
|
||||
{
|
||||
label: '激活状态',
|
||||
formatter: (_, data) => (data.is_active ? '已激活' : '未激活')
|
||||
},
|
||||
{
|
||||
label: '配置描述',
|
||||
prop: 'description',
|
||||
formatter: (value) => value || '-',
|
||||
fullWidth: true
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'created_at',
|
||||
formatter: (value) => formatDateTime(value)
|
||||
},
|
||||
{
|
||||
label: '更新时间',
|
||||
prop: 'updated_at',
|
||||
formatter: (value) => formatDateTime(value)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 微信小程序配置
|
||||
const miniappSection: DetailSection = {
|
||||
title: '小程序配置',
|
||||
fields: [
|
||||
{
|
||||
label: '小程序AppID',
|
||||
prop: 'miniapp_app_id',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '小程序AppSecret',
|
||||
prop: 'miniapp_app_secret',
|
||||
formatter: (value) => value || '-'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 微信公众号配置
|
||||
const oaSection: DetailSection = {
|
||||
title: '公众号配置',
|
||||
fields: [
|
||||
{
|
||||
label: '公众号AppID',
|
||||
prop: 'oa_app_id',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '公众号AppSecret',
|
||||
prop: 'oa_app_secret',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '公众号Token',
|
||||
prop: 'oa_token',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '公众号AES Key',
|
||||
prop: 'oa_aes_key',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: 'OAuth回调地址',
|
||||
prop: 'oa_oauth_redirect_url',
|
||||
formatter: (value) => value || '-',
|
||||
fullWidth: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 微信支付配置
|
||||
const wechatPaySection: DetailSection = {
|
||||
title: '微信支付配置',
|
||||
fields: [
|
||||
{
|
||||
label: '微信商户号',
|
||||
prop: 'wx_mch_id',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '证书序列号',
|
||||
prop: 'wx_serial_no',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: 'APIv2密钥',
|
||||
prop: 'wx_api_v2_key',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: 'APIv3密钥',
|
||||
prop: 'wx_api_v3_key',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '支付回调地址',
|
||||
prop: 'wx_notify_url',
|
||||
formatter: (value) => value || '-',
|
||||
fullWidth: true
|
||||
},
|
||||
{
|
||||
label: '支付证书',
|
||||
prop: 'wx_cert_content',
|
||||
formatter: (value) => getConfigStatus(value)
|
||||
},
|
||||
{
|
||||
label: '支付密钥',
|
||||
prop: 'wx_key_content',
|
||||
formatter: (value) => getConfigStatus(value)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 富友支付配置
|
||||
const fuiouSection: DetailSection = {
|
||||
title: '富友支付配置',
|
||||
fields: [
|
||||
{
|
||||
label: '富友API地址',
|
||||
prop: 'fy_api_url',
|
||||
formatter: (value) => value || '-',
|
||||
fullWidth: true
|
||||
},
|
||||
{
|
||||
label: '富友机构号',
|
||||
prop: 'fy_ins_cd',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '富友商户号',
|
||||
prop: 'fy_mchnt_cd',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '富友终端号',
|
||||
prop: 'fy_term_id',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '支付回调地址',
|
||||
prop: 'fy_notify_url',
|
||||
formatter: (value) => value || '-'
|
||||
},
|
||||
{
|
||||
label: '富友私钥',
|
||||
prop: 'fy_private_key',
|
||||
formatter: (value) => getConfigStatus(value)
|
||||
},
|
||||
{
|
||||
label: '富友公钥',
|
||||
prop: 'fy_public_key',
|
||||
formatter: (value) => getConfigStatus(value)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 根据支付类型动态构建详情配置
|
||||
const detailSections = computed<DetailSection[]>(() => {
|
||||
if (!detailData.value) return [baseSection]
|
||||
|
||||
const sections = [baseSection]
|
||||
|
||||
if (detailData.value.provider_type === 'wechat') {
|
||||
sections.push(miniappSection, oaSection, wechatPaySection)
|
||||
} else if (detailData.value.provider_type === 'fuiou') {
|
||||
sections.push(fuiouSection)
|
||||
}
|
||||
|
||||
return sections
|
||||
})
|
||||
|
||||
// 返回列表
|
||||
const handleBack = () => {
|
||||
router.push(RoutesAlias.WechatConfig)
|
||||
}
|
||||
|
||||
// 获取详情数据
|
||||
const fetchDetail = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await WechatConfigService.getWechatConfigById(configId.value)
|
||||
if (res.code === 0) {
|
||||
detailData.value = res.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
ElMessage.error('获取支付配置详情失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wechat-config-detail-page {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding-bottom: 16px;
|
||||
|
||||
.detail-title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 20px;
|
||||
gap: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
|
||||
.el-icon {
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
863
src/views/settings/wechat-config/index.vue
Normal file
863
src/views/settings/wechat-config/index.vue
Normal file
@@ -0,0 +1,863 @@
|
||||
<template>
|
||||
<ArtTableFullScreen>
|
||||
<div class="wechat-config-page" id="table-full-screen">
|
||||
<!-- 搜索栏 -->
|
||||
<ArtSearchBar
|
||||
v-model:filter="searchForm"
|
||||
:items="searchFormItems"
|
||||
label-width="100"
|
||||
:show-expand="false"
|
||||
@reset="handleReset"
|
||||
@search="handleSearch"
|
||||
></ArtSearchBar>
|
||||
|
||||
<ElCard shadow="never" class="art-table-card">
|
||||
<!-- 表格头部 -->
|
||||
<ArtTableHeader
|
||||
:columnList="columnOptions"
|
||||
v-model:columns="columnChecks"
|
||||
@refresh="handleRefresh"
|
||||
>
|
||||
<template #left>
|
||||
<ElButton type="primary" @click="showCreateDialog">新增支付配置</ElButton>
|
||||
</template>
|
||||
</ArtTableHeader>
|
||||
|
||||
<!-- 表格 -->
|
||||
<ArtTable
|
||||
ref="tableRef"
|
||||
row-key="id"
|
||||
:loading="loading"
|
||||
:data="configList"
|
||||
:currentPage="pagination.page"
|
||||
:pageSize="pagination.page_size"
|
||||
:total="pagination.total"
|
||||
:marginTop="10"
|
||||
:row-class-name="getRowClassName"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
@row-contextmenu="handleRowContextMenu"
|
||||
@cell-mouse-enter="handleCellMouseEnter"
|
||||
@cell-mouse-leave="handleCellMouseLeave"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
||||
</template>
|
||||
</ArtTable>
|
||||
|
||||
<!-- 鼠标悬浮提示 -->
|
||||
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
|
||||
|
||||
<!-- 支付配置操作右键菜单 -->
|
||||
<ArtMenuRight
|
||||
ref="configOperationMenuRef"
|
||||
:menu-items="configOperationMenuItems"
|
||||
:menu-width="140"
|
||||
@select="handleConfigOperationMenuSelect"
|
||||
/>
|
||||
|
||||
<!-- 创建/编辑对话框 -->
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogType === 'add' ? '新增支付配置' : '编辑支付配置'"
|
||||
width="800px"
|
||||
@closed="handleDialogClosed"
|
||||
>
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="130px">
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="配置名称" prop="name">
|
||||
<ElInput v-model="form.name" placeholder="请输入配置名称" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="支付渠道类型" prop="provider_type">
|
||||
<ElSelect
|
||||
v-model="form.provider_type"
|
||||
placeholder="请选择支付渠道类型"
|
||||
style="width: 100%"
|
||||
:disabled="dialogType === 'edit'"
|
||||
>
|
||||
<ElOption label="微信直连" value="wechat" />
|
||||
<ElOption label="富友支付" value="fuiou" />
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElFormItem label="配置描述" prop="description">
|
||||
<ElInput
|
||||
v-model="form.description"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="请输入配置描述"
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 微信相关配置 -->
|
||||
<template v-if="form.provider_type === 'wechat'">
|
||||
<ElDivider content-position="left">小程序配置</ElDivider>
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="小程序AppID" prop="miniapp_app_id">
|
||||
<ElInput v-model="form.miniapp_app_id" placeholder="请输入小程序AppID" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="小程序AppSecret" prop="miniapp_app_secret">
|
||||
<ElInput
|
||||
v-model="form.miniapp_app_secret"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="请输入小程序AppSecret"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<ElDivider content-position="left">公众号配置</ElDivider>
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="公众号AppID" prop="oa_app_id">
|
||||
<ElInput v-model="form.oa_app_id" placeholder="请输入公众号AppID" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="公众号AppSecret" prop="oa_app_secret">
|
||||
<ElInput
|
||||
v-model="form.oa_app_secret"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="请输入公众号AppSecret"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="公众号Token" prop="oa_token">
|
||||
<ElInput
|
||||
v-model="form.oa_token"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="请输入公众号Token"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="公众号AES Key" prop="oa_aes_key">
|
||||
<ElInput
|
||||
v-model="form.oa_aes_key"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="请输入公众号AES加密Key"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElFormItem label="OAuth回调地址" prop="oa_oauth_redirect_url">
|
||||
<ElInput v-model="form.oa_oauth_redirect_url" placeholder="请输入OAuth回调地址" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElDivider content-position="left">微信支付配置</ElDivider>
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="微信商户号" prop="wx_mch_id">
|
||||
<ElInput v-model="form.wx_mch_id" placeholder="请输入微信商户号" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="证书序列号" prop="wx_serial_no">
|
||||
<ElInput v-model="form.wx_serial_no" placeholder="请输入微信证书序列号" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="APIv2密钥" prop="wx_api_v2_key">
|
||||
<ElInput
|
||||
v-model="form.wx_api_v2_key"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="请输入微信APIv2密钥"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="APIv3密钥" prop="wx_api_v3_key">
|
||||
<ElInput
|
||||
v-model="form.wx_api_v3_key"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="请输入微信APIv3密钥"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElFormItem label="支付回调地址" prop="wx_notify_url">
|
||||
<ElInput v-model="form.wx_notify_url" placeholder="请输入微信支付回调地址" />
|
||||
</ElFormItem>
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="支付证书" prop="wx_cert_content">
|
||||
<ElInput
|
||||
v-model="form.wx_cert_content"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请粘贴PEM格式证书内容"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="支付密钥" prop="wx_key_content">
|
||||
<ElInput
|
||||
v-model="form.wx_key_content"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请粘贴PEM格式密钥内容"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
|
||||
<!-- 富友支付配置 -->
|
||||
<template v-if="form.provider_type === 'fuiou'">
|
||||
<ElDivider content-position="left">富友支付配置</ElDivider>
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="富友API地址" prop="fy_api_url">
|
||||
<ElInput v-model="form.fy_api_url" placeholder="请输入富友API地址" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="富友机构号" prop="fy_ins_cd">
|
||||
<ElInput v-model="form.fy_ins_cd" placeholder="请输入富友机构号" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="富友商户号" prop="fy_mchnt_cd">
|
||||
<ElInput v-model="form.fy_mchnt_cd" placeholder="请输入富友商户号" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="富友终端号" prop="fy_term_id">
|
||||
<ElInput v-model="form.fy_term_id" placeholder="请输入富友终端号" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElFormItem label="支付回调地址" prop="fy_notify_url">
|
||||
<ElInput v-model="form.fy_notify_url" placeholder="请输入富友支付回调地址" />
|
||||
</ElFormItem>
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="富友私钥" prop="fy_private_key">
|
||||
<ElInput
|
||||
v-model="form.fy_private_key"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请粘贴PEM格式私钥内容"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="富友公钥" prop="fy_public_key">
|
||||
<ElInput
|
||||
v-model="form.fy_public_key"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请粘贴PEM格式公钥内容"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="dialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleSubmit" :loading="submitLoading">
|
||||
提交
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</ElCard>
|
||||
</div>
|
||||
</ArtTableFullScreen>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { WechatConfigService } from '@/api/modules'
|
||||
import { ElMessage, ElTag, ElMessageBox, ElSwitch, ElButton } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import type {
|
||||
WechatConfig,
|
||||
WechatConfigQueryParams,
|
||||
PaymentProviderType,
|
||||
CreateWechatConfigRequest,
|
||||
UpdateWechatConfigRequest
|
||||
} from '@/types/api'
|
||||
import type { SearchFormItem } from '@/types'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { useTableContextMenu } from '@/composables/useTableContextMenu'
|
||||
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
|
||||
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
|
||||
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { RoutesAlias } from '@/router/routesAlias'
|
||||
|
||||
defineOptions({ name: 'WechatConfigList' })
|
||||
|
||||
const { hasAuth } = useAuth()
|
||||
const router = useRouter()
|
||||
|
||||
const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const tableRef = ref()
|
||||
const dialogVisible = ref(false)
|
||||
const dialogType = ref<'add' | 'edit'>('add')
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 右键菜单
|
||||
const configOperationMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
||||
const currentOperatingConfig = ref<WechatConfig | null>(null)
|
||||
|
||||
// 搜索表单初始值
|
||||
const initialSearchState = {
|
||||
provider_type: undefined as PaymentProviderType | undefined,
|
||||
is_active: undefined as number | undefined
|
||||
}
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({ ...initialSearchState })
|
||||
|
||||
// 搜索表单配置
|
||||
const searchFormItems: SearchFormItem[] = [
|
||||
{
|
||||
label: '支付渠道类型',
|
||||
prop: 'provider_type',
|
||||
type: 'select',
|
||||
placeholder: '请选择支付渠道类型',
|
||||
options: [
|
||||
{ label: '微信直连', value: 'wechat' },
|
||||
{ label: '富友支付', value: 'fuiou' }
|
||||
],
|
||||
config: {
|
||||
clearable: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '激活状态',
|
||||
prop: 'is_active',
|
||||
type: 'select',
|
||||
placeholder: '请选择激活状态',
|
||||
options: [
|
||||
{ label: '已激活', value: 1 },
|
||||
{ label: '未激活', value: 0 }
|
||||
],
|
||||
config: {
|
||||
clearable: true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
page_size: 20,
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 列配置
|
||||
const columnOptions = [
|
||||
{ label: 'ID', prop: 'id' },
|
||||
{ label: '配置名称', prop: 'name' },
|
||||
{ label: '支付渠道类型', prop: 'provider_type' },
|
||||
{ label: '激活状态', prop: 'is_active' },
|
||||
{ label: '商户号', prop: 'merchant_id' },
|
||||
{ label: '创建时间', prop: 'created_at' },
|
||||
{ label: '更新时间', prop: 'updated_at' }
|
||||
]
|
||||
|
||||
// 动态表单验证规则
|
||||
const rules = computed<FormRules>(() => {
|
||||
const baseRules: FormRules = {
|
||||
name: [{ required: true, message: '请输入配置名称', trigger: 'blur' }],
|
||||
provider_type: [{ required: true, message: '请选择支付渠道类型', trigger: 'change' }]
|
||||
}
|
||||
|
||||
// 只在创建模式下添加必填验证
|
||||
if (dialogType.value === 'add') {
|
||||
if (form.provider_type === 'wechat') {
|
||||
// 微信直连必填字段
|
||||
baseRules.wx_mch_id = [{ required: true, message: '请输入微信商户号', trigger: 'blur' }]
|
||||
baseRules.wx_api_v3_key = [
|
||||
{ required: true, message: '请输入微信APIv3密钥', trigger: 'blur' }
|
||||
]
|
||||
baseRules.wx_cert_content = [
|
||||
{ required: true, message: '请输入微信支付证书内容', trigger: 'blur' }
|
||||
]
|
||||
baseRules.wx_key_content = [
|
||||
{ required: true, message: '请输入微信支付密钥内容', trigger: 'blur' }
|
||||
]
|
||||
baseRules.wx_serial_no = [
|
||||
{ required: true, message: '请输入微信证书序列号', trigger: 'blur' }
|
||||
]
|
||||
baseRules.wx_notify_url = [
|
||||
{ required: true, message: '请输入微信支付回调地址', trigger: 'blur' }
|
||||
]
|
||||
} else if (form.provider_type === 'fuiou') {
|
||||
// 富友支付必填字段
|
||||
baseRules.fy_api_url = [{ required: true, message: '请输入富友API地址', trigger: 'blur' }]
|
||||
baseRules.fy_ins_cd = [{ required: true, message: '请输入富友机构号', trigger: 'blur' }]
|
||||
baseRules.fy_mchnt_cd = [{ required: true, message: '请输入富友商户号', trigger: 'blur' }]
|
||||
baseRules.fy_term_id = [{ required: true, message: '请输入富友终端号', trigger: 'blur' }]
|
||||
baseRules.fy_private_key = [{ required: true, message: '请输入富友私钥', trigger: 'blur' }]
|
||||
baseRules.fy_public_key = [{ required: true, message: '请输入富友公钥', trigger: 'blur' }]
|
||||
baseRules.fy_notify_url = [
|
||||
{ required: true, message: '请输入富友支付回调地址', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
return baseRules
|
||||
})
|
||||
|
||||
const initialFormState = {
|
||||
name: '',
|
||||
provider_type: 'wechat' as PaymentProviderType,
|
||||
description: '',
|
||||
miniapp_app_id: '',
|
||||
miniapp_app_secret: '',
|
||||
oa_app_id: '',
|
||||
oa_app_secret: '',
|
||||
oa_token: '',
|
||||
oa_aes_key: '',
|
||||
oa_oauth_redirect_url: '',
|
||||
wx_mch_id: '',
|
||||
wx_api_v2_key: '',
|
||||
wx_api_v3_key: '',
|
||||
wx_notify_url: '',
|
||||
wx_serial_no: '',
|
||||
wx_cert_content: '',
|
||||
wx_key_content: '',
|
||||
fy_api_url: '',
|
||||
fy_ins_cd: '',
|
||||
fy_mchnt_cd: '',
|
||||
fy_term_id: '',
|
||||
fy_notify_url: '',
|
||||
fy_private_key: '',
|
||||
fy_public_key: ''
|
||||
}
|
||||
|
||||
const form = reactive({ id: 0, ...initialFormState })
|
||||
|
||||
const configList = ref<WechatConfig[]>([])
|
||||
|
||||
// 获取支付渠道类型文本
|
||||
const getProviderTypeText = (type: PaymentProviderType): string => {
|
||||
const typeMap: Record<PaymentProviderType, string> = {
|
||||
wechat: '微信直连',
|
||||
fuiou: '富友支付'
|
||||
}
|
||||
return typeMap[type] || type
|
||||
}
|
||||
|
||||
// 获取商户号
|
||||
const getMerchantId = (row: WechatConfig): string => {
|
||||
if (row.provider_type === 'wechat') {
|
||||
return row.wx_mch_id || '-'
|
||||
}
|
||||
if (row.provider_type === 'fuiou') {
|
||||
return row.fy_mchnt_cd || '-'
|
||||
}
|
||||
return '-'
|
||||
}
|
||||
|
||||
// 动态列配置
|
||||
const { columnChecks, columns } = useCheckedColumns(() => [
|
||||
{
|
||||
prop: 'id',
|
||||
label: 'ID',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
prop: 'name',
|
||||
label: '配置名称',
|
||||
minWidth: 180,
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: WechatConfig) => {
|
||||
return h(
|
||||
'span',
|
||||
{
|
||||
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
||||
onClick: (e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
handleNameClick(row)
|
||||
}
|
||||
},
|
||||
row.name
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'provider_type',
|
||||
label: '支付渠道类型',
|
||||
width: 130,
|
||||
formatter: (row: WechatConfig) => getProviderTypeText(row.provider_type)
|
||||
},
|
||||
{
|
||||
prop: 'is_active',
|
||||
label: '激活状态',
|
||||
width: 100,
|
||||
formatter: (row: WechatConfig) => {
|
||||
return h(ElSwitch, {
|
||||
modelValue: row.is_active,
|
||||
activeText: '已激活',
|
||||
inactiveText: '未激活',
|
||||
inlinePrompt: true,
|
||||
'onUpdate:modelValue': (val) => handleStatusChange(row, val)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'merchant_id',
|
||||
label: '商户号',
|
||||
width: 150,
|
||||
formatter: (row: WechatConfig) => getMerchantId(row)
|
||||
},
|
||||
{
|
||||
prop: 'created_at',
|
||||
label: '创建时间',
|
||||
width: 180,
|
||||
formatter: (row: WechatConfig) => formatDateTime(row.created_at)
|
||||
},
|
||||
{
|
||||
prop: 'updated_at',
|
||||
label: '更新时间',
|
||||
width: 180,
|
||||
formatter: (row: WechatConfig) => formatDateTime(row.updated_at)
|
||||
}
|
||||
])
|
||||
|
||||
// 监听支付渠道类型变化,清除表单验证
|
||||
watch(
|
||||
() => form.provider_type,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate()
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
getTableData()
|
||||
})
|
||||
|
||||
// 获取配置列表
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const params: WechatConfigQueryParams = {
|
||||
page: pagination.page,
|
||||
page_size: pagination.page_size,
|
||||
provider_type: searchForm.provider_type,
|
||||
is_active: searchForm.is_active !== undefined ? searchForm.is_active === 1 : undefined
|
||||
}
|
||||
const res = await WechatConfigService.getWechatConfigs(params)
|
||||
if (res.code === 0) {
|
||||
configList.value = res.data.items || []
|
||||
pagination.total = res.data.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
const handleReset = () => {
|
||||
Object.assign(searchForm, { ...initialSearchState })
|
||||
pagination.page = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
pagination.page = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 刷新表格
|
||||
const handleRefresh = () => {
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 处理表格分页变化
|
||||
const handleSizeChange = (newPageSize: number) => {
|
||||
pagination.page_size = newPageSize
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (newCurrentPage: number) => {
|
||||
pagination.page = newCurrentPage
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 显示创建对话框
|
||||
const showCreateDialog = () => {
|
||||
dialogType.value = 'add'
|
||||
Object.assign(form, { id: 0, ...initialFormState })
|
||||
dialogVisible.value = true
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate()
|
||||
})
|
||||
}
|
||||
|
||||
// 显示编辑对话框
|
||||
const showEditDialog = (row: WechatConfig) => {
|
||||
dialogType.value = 'edit'
|
||||
Object.assign(form, {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
provider_type: row.provider_type,
|
||||
description: row.description,
|
||||
miniapp_app_id: row.miniapp_app_id,
|
||||
miniapp_app_secret: '',
|
||||
oa_app_id: row.oa_app_id,
|
||||
oa_app_secret: '',
|
||||
oa_token: '',
|
||||
oa_aes_key: '',
|
||||
oa_oauth_redirect_url: row.oa_oauth_redirect_url,
|
||||
wx_mch_id: row.wx_mch_id,
|
||||
wx_api_v2_key: '',
|
||||
wx_api_v3_key: '',
|
||||
wx_notify_url: row.wx_notify_url,
|
||||
wx_serial_no: row.wx_serial_no,
|
||||
wx_cert_content: '',
|
||||
wx_key_content: '',
|
||||
fy_api_url: row.fy_api_url,
|
||||
fy_ins_cd: row.fy_ins_cd,
|
||||
fy_mchnt_cd: row.fy_mchnt_cd,
|
||||
fy_term_id: row.fy_term_id,
|
||||
fy_notify_url: row.fy_notify_url,
|
||||
fy_private_key: '',
|
||||
fy_public_key: ''
|
||||
})
|
||||
dialogVisible.value = true
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate()
|
||||
})
|
||||
}
|
||||
|
||||
// 对话框关闭后的清理
|
||||
const handleDialogClosed = () => {
|
||||
formRef.value?.resetFields()
|
||||
Object.assign(form, { id: 0, ...initialFormState })
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
await formRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
submitLoading.value = true
|
||||
try {
|
||||
if (dialogType.value === 'add') {
|
||||
const data: CreateWechatConfigRequest = {
|
||||
name: form.name,
|
||||
provider_type: form.provider_type,
|
||||
description: form.description || undefined,
|
||||
miniapp_app_id: form.miniapp_app_id || undefined,
|
||||
miniapp_app_secret: form.miniapp_app_secret || undefined,
|
||||
oa_app_id: form.oa_app_id || undefined,
|
||||
oa_app_secret: form.oa_app_secret || undefined,
|
||||
oa_token: form.oa_token || undefined,
|
||||
oa_aes_key: form.oa_aes_key || undefined,
|
||||
oa_oauth_redirect_url: form.oa_oauth_redirect_url || undefined,
|
||||
wx_mch_id: form.wx_mch_id || undefined,
|
||||
wx_api_v2_key: form.wx_api_v2_key || undefined,
|
||||
wx_api_v3_key: form.wx_api_v3_key || undefined,
|
||||
wx_notify_url: form.wx_notify_url || undefined,
|
||||
wx_serial_no: form.wx_serial_no || undefined,
|
||||
wx_cert_content: form.wx_cert_content || undefined,
|
||||
wx_key_content: form.wx_key_content || undefined,
|
||||
fy_api_url: form.fy_api_url || undefined,
|
||||
fy_ins_cd: form.fy_ins_cd || undefined,
|
||||
fy_mchnt_cd: form.fy_mchnt_cd || undefined,
|
||||
fy_term_id: form.fy_term_id || undefined,
|
||||
fy_notify_url: form.fy_notify_url || undefined,
|
||||
fy_private_key: form.fy_private_key || undefined,
|
||||
fy_public_key: form.fy_public_key || undefined
|
||||
}
|
||||
await WechatConfigService.createWechatConfig(data)
|
||||
ElMessage.success('创建成功')
|
||||
} else {
|
||||
const data: UpdateWechatConfigRequest = {
|
||||
name: form.name,
|
||||
description: form.description || undefined
|
||||
}
|
||||
// 只有填写了新值才更新敏感字段
|
||||
if (form.miniapp_app_secret) data.miniapp_app_secret = form.miniapp_app_secret
|
||||
if (form.oa_app_secret) data.oa_app_secret = form.oa_app_secret
|
||||
if (form.oa_token) data.oa_token = form.oa_token
|
||||
if (form.oa_aes_key) data.oa_aes_key = form.oa_aes_key
|
||||
if (form.wx_api_v2_key) data.wx_api_v2_key = form.wx_api_v2_key
|
||||
if (form.wx_api_v3_key) data.wx_api_v3_key = form.wx_api_v3_key
|
||||
if (form.wx_cert_content) data.wx_cert_content = form.wx_cert_content
|
||||
if (form.wx_key_content) data.wx_key_content = form.wx_key_content
|
||||
if (form.fy_private_key) data.fy_private_key = form.fy_private_key
|
||||
if (form.fy_public_key) data.fy_public_key = form.fy_public_key
|
||||
|
||||
// 非敏感字段总是更新
|
||||
if (form.miniapp_app_id) data.miniapp_app_id = form.miniapp_app_id
|
||||
if (form.oa_app_id) data.oa_app_id = form.oa_app_id
|
||||
if (form.oa_oauth_redirect_url) data.oa_oauth_redirect_url = form.oa_oauth_redirect_url
|
||||
if (form.wx_mch_id) data.wx_mch_id = form.wx_mch_id
|
||||
if (form.wx_notify_url) data.wx_notify_url = form.wx_notify_url
|
||||
if (form.wx_serial_no) data.wx_serial_no = form.wx_serial_no
|
||||
if (form.fy_api_url) data.fy_api_url = form.fy_api_url
|
||||
if (form.fy_ins_cd) data.fy_ins_cd = form.fy_ins_cd
|
||||
if (form.fy_mchnt_cd) data.fy_mchnt_cd = form.fy_mchnt_cd
|
||||
if (form.fy_term_id) data.fy_term_id = form.fy_term_id
|
||||
if (form.fy_notify_url) data.fy_notify_url = form.fy_notify_url
|
||||
|
||||
await WechatConfigService.updateWechatConfig(form.id, data)
|
||||
ElMessage.success('更新成功')
|
||||
}
|
||||
|
||||
dialogVisible.value = false
|
||||
formRef.value?.resetFields()
|
||||
await getTableData()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 激活/停用状态切换
|
||||
const handleStatusChange = async (row: WechatConfig, newStatus: string | number | boolean) => {
|
||||
const newBoolStatus = Boolean(newStatus)
|
||||
const oldStatus = row.is_active
|
||||
row.is_active = newBoolStatus
|
||||
try {
|
||||
if (newBoolStatus) {
|
||||
await WechatConfigService.activateWechatConfig(row.id)
|
||||
ElMessage.success('激活成功')
|
||||
} else {
|
||||
await WechatConfigService.deactivateWechatConfig(row.id)
|
||||
ElMessage.success('停用成功')
|
||||
}
|
||||
await getTableData()
|
||||
} catch (error) {
|
||||
row.is_active = oldStatus
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除配置
|
||||
const handleDelete = async (row: WechatConfig) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要删除支付配置"${row.name}"吗?`, '删除确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
await WechatConfigService.deleteWechatConfig(row.id)
|
||||
ElMessage.success('删除成功')
|
||||
await getTableData()
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
const handleViewDetail = (row: WechatConfig) => {
|
||||
router.push({
|
||||
path: `${RoutesAlias.WechatConfig}/detail/${row.id}`
|
||||
})
|
||||
}
|
||||
|
||||
// 处理名称点击
|
||||
const handleNameClick = (row: WechatConfig) => {
|
||||
handleViewDetail(row)
|
||||
}
|
||||
|
||||
// 支付配置操作菜单项配置
|
||||
const configOperationMenuItems = computed((): MenuItemType[] => {
|
||||
const items: MenuItemType[] = []
|
||||
|
||||
// 编辑
|
||||
items.push({
|
||||
key: 'edit',
|
||||
label: '编辑'
|
||||
})
|
||||
|
||||
// 删除
|
||||
items.push({
|
||||
key: 'delete',
|
||||
label: '删除'
|
||||
})
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
// 显示支付配置操作右键菜单
|
||||
const showConfigOperationMenu = (e: MouseEvent, row: WechatConfig) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
currentOperatingConfig.value = row
|
||||
configOperationMenuRef.value?.show(e)
|
||||
}
|
||||
|
||||
// 处理表格行右键菜单
|
||||
const handleRowContextMenu = (row: WechatConfig, column: any, event: MouseEvent) => {
|
||||
showConfigOperationMenu(event, row)
|
||||
}
|
||||
|
||||
// 处理支付配置操作菜单选择
|
||||
const handleConfigOperationMenuSelect = (item: MenuItemType) => {
|
||||
if (!currentOperatingConfig.value) return
|
||||
|
||||
switch (item.key) {
|
||||
case 'edit':
|
||||
showEditDialog(currentOperatingConfig.value)
|
||||
break
|
||||
case 'delete':
|
||||
handleDelete(currentOperatingConfig.value)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 使用表格右键菜单功能
|
||||
const {
|
||||
showContextMenuHint,
|
||||
hintPosition,
|
||||
getRowClassName,
|
||||
handleCellMouseEnter,
|
||||
handleCellMouseLeave
|
||||
} = useTableContextMenu()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wechat-config-page {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-table__row.table-row-with-context-menu) {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user