Files
one-pipe-system/src/views/settings/payment-settings/index.vue
luo 4c0207c6e6
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m41s
fix: 将微信配置改成支付配置
2026-07-10 18:09:19 +08:00

1091 lines
33 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<ArtTableFullScreen>
<div class="payment-settings-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"
v-if="hasAuth('payment_settings:create')"
>新增支付配置</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"
:actions="getActions"
:actionsWidth="120"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 创建/编辑对话框 -->
<ElDialog
v-model="dialogVisible"
:title="dialogType === 'add' ? '新增支付配置' : '编辑支付配置'"
width="900px"
:close-on-click-modal="false"
@closed="handleDialogClosed"
>
<ElForm
ref="formRef"
:model="form"
:rules="rules"
label-width="140px"
class="config-form"
>
<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%"
@change="handleProviderTypeChange"
>
<ElOption label="微信直连" value="wechat" />
<ElOption label="微信直连V2" value="wechat_v2" />
<ElOption label="富友支付" value="fuiou" />
</ElSelect>
</ElFormItem>
</ElCol>
</ElRow>
<ElFormItem label="配置描述" prop="description">
<ElInput
v-model="form.description"
type="textarea"
:rows="2"
placeholder="请输入配置描述"
/>
</ElFormItem>
<ElDivider content-position="left">
<span class="divider-title">小程序配置</span>
</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">
<span class="divider-title">公众号配置</span>
</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">
<span class="divider-title">支付宝配置</span>
</ElDivider>
<ElRow :gutter="20">
<ElCol :span="12">
<ElFormItem label="支付宝AppID" prop="ali_app_id">
<ElInput v-model="form.ali_app_id" placeholder="请输入支付宝AppID" />
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="支付过期分钟数" prop="ali_pay_expire_minutes">
<ElInputNumber
v-model="form.ali_pay_expire_minutes"
:min="1"
:step="1"
controls-position="right"
style="width: 100%"
/>
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="20">
<ElCol :span="12">
<ElFormItem label="异步通知地址" prop="ali_notify_url">
<ElInput v-model="form.ali_notify_url" placeholder="请输入支付宝异步通知地址" />
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="同步跳转地址" prop="ali_return_url">
<ElInput v-model="form.ali_return_url" placeholder="请输入支付宝同步跳转地址" />
</ElFormItem>
</ElCol>
</ElRow>
<ElFormItem label="生产环境" prop="ali_production">
<ElSwitch
v-model="form.ali_production"
active-text="生产环境"
inactive-text="测试环境"
inline-prompt
/>
</ElFormItem>
<ElRow :gutter="20">
<ElCol :span="12">
<ElFormItem label="应用私钥" prop="ali_private_key">
<ElInput
v-model="form.ali_private_key"
type="textarea"
:rows="3"
placeholder="请粘贴支付宝应用私钥内容"
/>
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="支付宝公钥" prop="ali_public_key">
<ElInput
v-model="form.ali_public_key"
type="textarea"
:rows="3"
placeholder="请粘贴支付宝公钥内容"
/>
</ElFormItem>
</ElCol>
</ElRow>
<template v-if="form.provider_type === 'wechat' || form.provider_type === 'wechat_v2'">
<ElDivider content-position="left">
<span class="divider-title">微信支付配置</span>
</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">
<span class="divider-title">富友支付配置</span>
</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, nextTick, onMounted, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { PaymentSettingsService } from '@/api/modules'
import { ElMessage, ElMessageBox, ElSwitch, ElTag } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type {
CreatePaymentSettingsRequest,
PaymentProviderType,
UpdatePaymentSettingsRequest,
PaymentSettings,
PaymentSettingsQueryParams
} from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useAuth } from '@/composables/useAuth'
import { RoutesAlias } from '@/router/routesAlias'
import { formatDateTime } from '@/utils/business/format'
import {
getPaymentMerchantId,
getPaymentNotifyUrl,
getPaymentProviderText,
isMaskedPaymentConfigValue
} from '@/utils/business/paymentConfig'
defineOptions({ name: 'PaymentSettingsList' })
type PaymentConfigFormModel = {
id: number
name: string
provider_type: PaymentProviderType
description: string
miniapp_app_id: string
miniapp_app_secret: string
oa_app_id: string
oa_app_secret: string
oa_token: string
oa_aes_key: string
oa_oauth_redirect_url: string
wx_mch_id: string
wx_api_v2_key: string
wx_api_v3_key: string
wx_notify_url: string
wx_serial_no: string
wx_cert_content: string
wx_key_content: string
ali_app_id: string
ali_private_key: string
ali_public_key: string
ali_notify_url: string
ali_return_url: string
ali_pay_expire_minutes: number
ali_production: boolean
fy_api_url: string
fy_ins_cd: string
fy_mchnt_cd: string
fy_term_id: string
fy_notify_url: string
fy_private_key: string
fy_public_key: string
}
const textFieldKeys = [
'description',
'miniapp_app_id',
'oa_app_id',
'oa_oauth_redirect_url',
'wx_mch_id',
'wx_notify_url',
'wx_serial_no',
'ali_app_id',
'ali_notify_url',
'ali_return_url',
'fy_api_url',
'fy_ins_cd',
'fy_mchnt_cd',
'fy_term_id',
'fy_notify_url'
] as const
const sensitiveFieldKeys = [
'miniapp_app_secret',
'oa_app_secret',
'oa_token',
'oa_aes_key',
'wx_api_v2_key',
'wx_api_v3_key',
'wx_cert_content',
'wx_key_content',
'ali_private_key',
'ali_public_key',
'fy_private_key',
'fy_public_key'
] as const
const { hasAuth } = useAuth()
const router = useRouter()
const canUpdatePaymentSettingsStatus = hasAuth('payment_settings:status')
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 configList = ref<PaymentSettings[]>([])
const originalFormState = ref<PaymentConfigFormModel | null>(null)
const createInitialFormState = (): PaymentConfigFormModel => ({
id: 0,
name: '',
provider_type: 'wechat',
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: '',
ali_app_id: '',
ali_private_key: '',
ali_public_key: '',
ali_notify_url: '',
ali_return_url: '',
ali_pay_expire_minutes: 30,
ali_production: true,
fy_api_url: '',
fy_ins_cd: '',
fy_mchnt_cd: '',
fy_term_id: '',
fy_notify_url: '',
fy_private_key: '',
fy_public_key: ''
})
const mapConfigToForm = (detail: PaymentSettings): PaymentConfigFormModel => ({
...createInitialFormState(),
...detail,
description: detail.description || '',
ali_pay_expire_minutes: detail.ali_pay_expire_minutes ?? 30,
ali_production: detail.ali_production ?? true
})
const trimString = (value: string): string => value.trim()
const normalizeOptionalString = (value: string): string | undefined => {
const trimmed = trimString(value)
return trimmed || undefined
}
const form = reactive<PaymentConfigFormModel>(createInitialFormState())
const isWechatProvider = (): boolean =>
form.provider_type === 'wechat' || form.provider_type === 'wechat_v2'
const getStatusText = (value: boolean): string => (value ? '已激活' : '未激活')
const requiredValidator =
(shouldRequire: () => boolean, label: string) =>
(_rule: unknown, value: unknown, callback: (error?: Error) => void) => {
if (!shouldRequire()) {
callback()
return
}
if (typeof value === 'string') {
if (!trimString(value)) {
callback(new Error(`请输入${label}`))
return
}
callback()
return
}
if (value === undefined || value === null || value === '') {
callback(new Error(`请输入${label}`))
return
}
callback()
}
const urlValidator = (_rule: unknown, value: unknown, callback: (error?: Error) => void) => {
if (!value || typeof value !== 'string') {
callback()
return
}
const urlPattern = /^(https?:\/\/)[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:/~+#]*[\w\-@?^=%&/~+#])?$/
if (!urlPattern.test(value)) {
callback(new Error('请输入正确的URL格式(http://或https://)'))
return
}
callback()
}
const expireMinutesValidator = (
_rule: unknown,
value: unknown,
callback: (error?: Error) => void
) => {
if (typeof value !== 'number' || Number.isNaN(value) || value < 1) {
callback(new Error('支付过期分钟数必须大于 0'))
return
}
callback()
}
const rules = reactive<FormRules>({
name: [{ required: true, message: '请输入配置名称', trigger: 'blur' }],
provider_type: [{ required: true, message: '请选择支付渠道类型', trigger: 'change' }],
wx_mch_id: [{ validator: requiredValidator(isWechatProvider, '微信商户号'), trigger: 'blur' }],
wx_api_v3_key: [
{
validator: requiredValidator(() => form.provider_type === 'wechat', '微信APIv3密钥'),
trigger: 'blur'
}
],
wx_serial_no: [
{ validator: requiredValidator(isWechatProvider, '证书序列号'), trigger: 'blur' }
],
wx_cert_content: [
{ validator: requiredValidator(isWechatProvider, '支付证书内容'), trigger: 'blur' }
],
wx_key_content: [
{ validator: requiredValidator(isWechatProvider, '支付密钥内容'), trigger: 'blur' }
],
oa_oauth_redirect_url: [{ validator: urlValidator, trigger: 'blur' }],
wx_notify_url: [{ validator: urlValidator, trigger: 'blur' }],
ali_notify_url: [{ validator: urlValidator, trigger: 'blur' }],
ali_return_url: [{ validator: urlValidator, trigger: 'blur' }],
fy_api_url: [{ validator: urlValidator, trigger: 'blur' }],
fy_notify_url: [{ validator: urlValidator, trigger: 'blur' }],
ali_pay_expire_minutes: [{ validator: expireMinutesValidator, trigger: 'change' }]
})
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: '微信直连V2', value: 'wechat_v2' },
{ 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: '配置名称', prop: 'name' },
{ label: '配置描述', prop: 'description' },
{ label: '支付渠道类型', prop: 'provider_type' },
{ label: '激活状态', prop: 'is_active' },
{ label: '商户号', prop: 'merchant_id' },
{ label: '小程序AppID', prop: 'miniapp_app_id' },
{ label: '公众号AppID', prop: 'oa_app_id' },
{ label: '支付宝AppID', prop: 'ali_app_id' },
{ label: '支付回调地址', prop: 'notify_url' },
{ label: '创建时间', prop: 'created_at' },
{ label: '更新时间', prop: 'updated_at' }
]
const { columnChecks, columns } = useCheckedColumns(() => [
{
prop: 'name',
label: '配置名称',
minWidth: 160,
showOverflowTooltip: true,
formatter: (row: PaymentSettings) => {
return h(
'span',
{
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
onClick: (e: MouseEvent) => {
e.stopPropagation()
handleNameClick(row)
}
},
row.name
)
}
},
{
prop: 'description',
label: '配置描述',
minWidth: 180,
showOverflowTooltip: true,
formatter: (row: PaymentSettings) => row.description || '-'
},
{
prop: 'provider_type',
label: '支付渠道类型',
width: 130,
formatter: (row: PaymentSettings) => getPaymentProviderText(row.provider_type)
},
{
prop: 'is_active',
label: '激活状态',
width: 110,
formatter: (row: PaymentSettings) => {
if (canUpdatePaymentSettingsStatus) {
return h(ElSwitch, {
modelValue: row.is_active,
activeText: '已激活',
inactiveText: '未激活',
inlinePrompt: true,
'onUpdate:modelValue': (val) => handleStatusChange(row, val)
})
}
return h(ElTag, { type: row.is_active ? 'success' : 'info' }, () =>
getStatusText(row.is_active)
)
}
},
{
prop: 'merchant_id',
label: '商户号',
width: 150,
showOverflowTooltip: true,
formatter: (row: PaymentSettings) => getPaymentMerchantId(row)
},
{
prop: 'miniapp_app_id',
label: '小程序AppID',
width: 160,
showOverflowTooltip: true,
formatter: (row: PaymentSettings) => row.miniapp_app_id || '-'
},
{
prop: 'oa_app_id',
label: '公众号AppID',
width: 160,
showOverflowTooltip: true,
formatter: (row: PaymentSettings) => row.oa_app_id || '-'
},
{
prop: 'ali_app_id',
label: '支付宝AppID',
width: 160,
showOverflowTooltip: true,
formatter: (row: PaymentSettings) => row.ali_app_id || '-'
},
{
prop: 'notify_url',
label: '支付回调地址',
minWidth: 200,
showOverflowTooltip: true,
formatter: (row: PaymentSettings) => getPaymentNotifyUrl(row)
},
{
prop: 'created_at',
label: '创建时间',
width: 180,
formatter: (row: PaymentSettings) => formatDateTime(row.created_at)
},
{
prop: 'updated_at',
label: '更新时间',
width: 180,
formatter: (row: PaymentSettings) => formatDateTime(row.updated_at)
}
])
const resetForm = () => {
Object.assign(form, createInitialFormState())
originalFormState.value = null
}
const getTableData = async () => {
loading.value = true
try {
const params: PaymentSettingsQueryParams = {
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 PaymentSettingsService.getPaymentSettings(params)
if (res.code === 0) {
configList.value = res.data.items || []
pagination.page = res.data.page || pagination.page
pagination.page_size = res.data.size || res.data.page_size || pagination.page_size
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 = () => {
resetForm()
dialogType.value = 'add'
dialogVisible.value = true
nextTick(() => {
formRef.value?.clearValidate()
})
}
const showEditDialog = async (row: PaymentSettings) => {
try {
const res = await PaymentSettingsService.getPaymentSettingsById(row.id)
if (res.code === 0 && res.data) {
const mappedForm = mapConfigToForm(res.data)
Object.assign(form, mappedForm)
originalFormState.value = { ...mappedForm }
dialogType.value = 'edit'
dialogVisible.value = true
await nextTick()
formRef.value?.clearValidate()
}
} catch (error) {
console.error('获取详情失败:', error)
}
}
const handleProviderTypeChange = () => {
nextTick(() => {
formRef.value?.clearValidate()
})
}
const handleDialogClosed = () => {
resetForm()
formRef.value?.clearValidate()
}
const buildCreatePayload = (): CreatePaymentSettingsRequest => {
const payload: CreatePaymentSettingsRequest = {
name: trimString(form.name),
provider_type: form.provider_type,
ali_pay_expire_minutes: form.ali_pay_expire_minutes,
ali_production: form.ali_production
}
for (const key of textFieldKeys) {
const value = normalizeOptionalString(form[key])
if (value !== undefined) {
payload[key] = value
}
}
for (const key of sensitiveFieldKeys) {
const value = normalizeOptionalString(form[key])
if (value !== undefined) {
payload[key] = value
}
}
return payload
}
const buildUpdatePayload = (): UpdatePaymentSettingsRequest => {
const initial = originalFormState.value
if (!initial) return {}
const payload: UpdatePaymentSettingsRequest = {}
const currentName = trimString(form.name)
const initialName = trimString(initial.name)
if (currentName !== initialName) {
payload.name = currentName
}
if (form.provider_type !== initial.provider_type) {
payload.provider_type = form.provider_type
}
for (const key of textFieldKeys) {
const currentValue = trimString(form[key])
const initialValue = trimString(initial[key])
if (currentValue !== initialValue) {
payload[key] = currentValue
}
}
if (form.ali_pay_expire_minutes !== initial.ali_pay_expire_minutes) {
payload.ali_pay_expire_minutes = form.ali_pay_expire_minutes
}
if (form.ali_production !== initial.ali_production) {
payload.ali_production = form.ali_production
}
for (const key of sensitiveFieldKeys) {
const currentValue = trimString(form[key])
if (!currentValue || isMaskedPaymentConfigValue(currentValue)) {
continue
}
const initialValue = trimString(initial[key])
if (currentValue !== initialValue) {
payload[key] = currentValue
}
}
return payload
}
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
} catch {
return
}
submitLoading.value = true
try {
if (dialogType.value === 'add') {
await PaymentSettingsService.createPaymentSettings(buildCreatePayload())
ElMessage.success('创建成功')
} else {
const payload = buildUpdatePayload()
if (Object.keys(payload).length === 0) {
ElMessage.info('未检测到可提交的变更')
return
}
await PaymentSettingsService.updatePaymentSettings(form.id, payload)
ElMessage.success('更新成功')
}
dialogVisible.value = false
await getTableData()
} catch (error) {
console.error(error)
} finally {
submitLoading.value = false
}
}
const handleStatusChange = async (row: PaymentSettings, newStatus: string | number | boolean) => {
if (!hasAuth('payment_settings:status')) {
ElMessage.warning('您没有修改激活状态的权限')
return
}
const newBoolStatus = Boolean(newStatus)
const oldStatus = row.is_active
row.is_active = newBoolStatus
try {
if (newBoolStatus) {
await PaymentSettingsService.activatePaymentSettings(row.id)
ElMessage.success('激活成功')
} else {
await PaymentSettingsService.deactivatePaymentSettings(row.id)
ElMessage.success('停用成功')
}
await getTableData()
} catch (error) {
row.is_active = oldStatus
console.error(error)
}
}
const handleDelete = async (row: PaymentSettings) => {
try {
await ElMessageBox.confirm(`确定要删除支付配置"${row.name}"吗?`, '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await PaymentSettingsService.deletePaymentSettings(row.id)
ElMessage.success('删除成功')
await getTableData()
} catch (error) {
if (error !== 'cancel') {
console.error(error)
}
}
}
const handleViewDetail = (row: PaymentSettings) => {
router.push({
path: `${RoutesAlias.PaymentSettings}/detail/${row.id}`
})
}
const handleNameClick = (row: PaymentSettings) => {
if (hasAuth('payment_settings:detail')) {
handleViewDetail(row)
} else {
ElMessage.warning('您没有查看详情的权限')
}
}
const getActions = (row: PaymentSettings) => {
const actions: any[] = []
if (hasAuth('payment_settings:edit')) {
actions.push({
label: '编辑',
handler: () => showEditDialog(row),
type: 'primary'
})
}
if (hasAuth('payment_settings:delete')) {
actions.push({
label: '删除',
handler: () => handleDelete(row),
type: 'danger'
})
}
return actions
}
onMounted(() => {
getTableData()
})
</script>
<style scoped lang="scss">
.payment-settings-page {
height: 100%;
}
.config-form {
max-height: 600px;
padding-right: 10px;
overflow-y: auto;
.divider-title {
font-size: 14px;
font-weight: 600;
color: var(--el-text-color-primary);
}
:deep(.el-divider) {
margin: 24px 0 20px;
}
:deep(.el-form-item) {
margin-bottom: 18px;
}
:deep(.el-input__wrapper) {
transition: all 0.3s ease;
}
:deep(.el-textarea__inner) {
transition: all 0.3s ease;
}
}
// 优化滚动条样式
.config-form::-webkit-scrollbar {
width: 6px;
}
.config-form::-webkit-scrollbar-thumb {
background-color: var(--el-border-color-darker);
border-radius: 3px;
}
.config-form::-webkit-scrollbar-track {
background-color: var(--el-fill-color-lighter);
}
</style>