修复
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m35s

This commit is contained in:
sexygoat
2026-03-31 15:13:19 +08:00
parent 104d62b17d
commit 3372be99b8
9 changed files with 383 additions and 218 deletions

View File

@@ -10,12 +10,13 @@
generateSeriesCode,
generatePackageCode,
generateShopCode,
generateCarrierCode
generateCarrierCode,
generateEnterpriseCode
} from '@/utils/codeGenerator'
interface Props {
// 编码类型: series(套餐系列) | package(套餐) | shop(店铺) | carrier(运营商)
codeType: 'series' | 'package' | 'shop' | 'carrier'
// 编码类型: series(套餐系列) | package(套餐) | shop(店铺) | carrier(运营商) | enterprise(企业)
codeType: 'series' | 'package' | 'shop' | 'carrier' | 'enterprise'
// 按钮文字
buttonText?: string
// 表单字段名,用于清除验证
@@ -47,6 +48,9 @@
case 'carrier':
code = generateCarrierCode()
break
case 'enterprise':
code = generateEnterpriseCode()
break
default:
ElMessage.error('未知的编码类型')
return

View File

@@ -24,9 +24,8 @@ export interface PaginationParams {
export interface PaginationData<T> {
items: T[] // 数据列表
total: number // 总数
page_size: number // 每页数量
size: number // 每页数量
page: number // 当前页
total_pages: number // 总页数
}
// 分页响应

View File

@@ -39,7 +39,7 @@ export interface EnterpriseDevicePageResult {
* 授权设备请求参数
*/
export interface AllocateDevicesRequest {
virtual_nos: string[] | null // 虚拟号列表最多100个,原 device_nos
device_nos: string[] | null // 设备号列表最多100个
remark?: string // 授权备注
}
@@ -76,7 +76,7 @@ export interface AllocateDevicesResponse {
* 撤销设备授权请求参数
*/
export interface RecallDevicesRequest {
virtual_nos: string[] | null // 虚拟号列表最多100个,原 device_nos
device_nos: string[] | null // 设备号列表最多100个
}
/**

View File

@@ -3,7 +3,7 @@
*/
// 支付渠道类型
export type PaymentProviderType = 'wechat' | 'fuiou'
export type PaymentProviderType = 'wechat' | 'wechat_v2' | 'fuiou'
// 微信支付配置
export interface WechatConfig {

View File

@@ -89,3 +89,25 @@ export function generateCarrierCode(): string {
return `CARRIER${year}${month}${day}${hours}${minutes}${seconds}${randomChars}`
}
/**
* 生成企业编码
* 规则: ENT + 年月日时分秒 + 4位随机数
* 示例: ENT20260331143025ABCD
*/
export function generateEnterpriseCode(): string {
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const hours = String(now.getHours()).padStart(2, '0')
const minutes = String(now.getMinutes()).padStart(2, '0')
const seconds = String(now.getSeconds()).padStart(2, '0')
// 生成4位随机大写字母
const randomChars = Array.from({ length: 4 }, () =>
String.fromCharCode(65 + Math.floor(Math.random() * 26))
).join('')
return `ENT${year}${month}${day}${hours}${minutes}${seconds}${randomChars}`
}

View File

@@ -57,28 +57,28 @@
width="600px"
>
<ElForm ref="formRef" :model="form" :rules="rules" label-width="100px">
<ElFormItem label="企业编号" prop="enterprise_code">
<div style="display: flex; gap: 8px">
<ElInput
v-model="form.enterprise_code"
placeholder="请输入企业编号或点击生成"
:disabled="dialogType === 'edit'"
clearable
style="flex: 1"
/>
<CodeGeneratorButton
v-if="dialogType === 'add'"
code-type="enterprise"
@generated="handleCodeGenerated"
/>
</div>
</ElFormItem>
<ElRow :gutter="20">
<ElCol :span="12">
<ElFormItem label="企业编号" prop="enterprise_code">
<ElInput
v-model="form.enterprise_code"
placeholder="请输入企业编号"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="企业名称" prop="enterprise_name">
<ElInput v-model="form.enterprise_name" placeholder="请输入企业名称" />
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="20">
<ElCol :span="12">
<ElFormItem label="营业执照号" prop="business_license">
<ElInput v-model="form.business_license" placeholder="请输入营业执照号" />
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="法人代表" prop="legal_person">
<ElInput v-model="form.legal_person" placeholder="请输入法人代表" />
@@ -86,6 +86,11 @@
</ElCol>
</ElRow>
<ElRow :gutter="20">
<ElCol :span="12">
<ElFormItem label="营业执照号" prop="business_license">
<ElInput v-model="form.business_license" placeholder="请输入营业执照号" />
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="所在地区" prop="region">
<ElCascader
@@ -99,6 +104,8 @@
/>
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="20">
<!-- 只有非代理账号才显示归属店铺选择 -->
<ElCol :span="12" v-if="!isAgentAccount">
<ElFormItem label="归属店铺" prop="owner_shop_id">
@@ -225,6 +232,7 @@
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
import CodeGeneratorButton from '@/components/business/CodeGeneratorButton.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
import { formatDateTime } from '@/utils/business/format'
import { regionData } from '@/utils/constants/regionData'
@@ -478,7 +486,7 @@
{
prop: 'operation',
label: '操作',
width: 190,
width: 280,
fixed: 'right',
formatter: (row: EnterpriseItem) => {
const buttons = []
@@ -501,6 +509,15 @@
)
}
if (hasAuth('enterprise_customer:device_authorization')) {
buttons.push(
h(ArtButtonTable, {
text: '设备授权',
onClick: () => manageDevices(row)
})
)
}
return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
@@ -712,6 +729,11 @@
})
}
// 处理企业编码生成
const handleCodeGenerated = (code: string) => {
form.enterprise_code = code
}
// 提交表单
const handleSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
@@ -810,6 +832,13 @@
})
}
const manageDevices = (row: EnterpriseItem) => {
router.push({
path: '/asset-management/enterprise-devices',
query: { id: row.id }
})
}
// 企业客户操作菜单项配置
const enterpriseOperationMenuItems = computed((): MenuItemType[] => {
const items: MenuItemType[] = []

View File

@@ -24,6 +24,7 @@
<ArtSearchBar
v-model:filter="searchForm"
:items="searchFormItems"
:show-expand="false"
@reset="handleReset"
@search="handleSearch"
></ArtSearchBar>
@@ -73,45 +74,55 @@
<ElDialog
v-model="allocateDialogVisible"
:title="$t('enterpriseDevices.dialog.allocateTitle')"
width="700px"
width="75%"
@close="handleAllocateDialogClose"
>
<ElForm
ref="allocateFormRef"
:model="allocateForm"
:rules="allocateRules"
label-width="120px"
<!-- 搜索过滤条件 -->
<ArtSearchBar
v-model:filter="deviceSearchForm"
:items="deviceSearchFormItems"
label-width="85"
:show-expand="false"
@reset="handleDeviceSearchReset"
@search="handleDeviceSearch"
></ArtSearchBar>
<!-- 设备列表 -->
<div class="device-selection-info">
已选择 {{ selectedAvailableDevices.length }} 台设备
</div>
<ArtTable
ref="availableDevicesTableRef"
row-key="id"
:loading="availableDevicesLoading"
:data="availableDevicesList"
:currentPage="devicePagination.page"
:pageSize="devicePagination.pageSize"
:total="devicePagination.total"
:marginTop="10"
@size-change="handleDevicePageSizeChange"
@current-change="handleDevicePageChange"
@selection-change="handleAvailableDevicesSelectionChange"
>
<ElFormItem :label="$t('enterpriseDevices.form.deviceNos')" prop="virtual_nos">
<ElInput
v-model="deviceNosText"
type="textarea"
:rows="6"
:placeholder="$t('enterpriseDevices.form.deviceNosPlaceholder')"
@input="handleDeviceNosChange"
<template #default>
<ElTableColumn type="selection" width="55" :selectable="checkDeviceSelectable" />
<ElTableColumn
v-for="col in availableDeviceColumns"
:key="col.prop || col.type"
v-bind="col"
/>
<div style="margin-top: 4px; font-size: 12px; color: var(--el-color-info)">
{{
$t('enterpriseDevices.form.selectedCount', {
count: allocateForm.virtual_nos?.length || 0
})
}}
</div>
</ElFormItem>
<ElFormItem :label="$t('enterpriseDevices.form.remark')">
<ElInput
v-model="allocateForm.remark"
type="textarea"
:rows="3"
:placeholder="$t('enterpriseDevices.form.remarkPlaceholder')"
/>
</ElFormItem>
</ElForm>
</template>
</ArtTable>
<template #footer>
<div class="dialog-footer">
<ElButton @click="allocateDialogVisible = false">{{ $t('common.cancel') }}</ElButton>
<ElButton type="primary" @click="handleAllocate" :loading="allocateLoading">
<ElButton
type="primary"
@click="handleAllocate"
:loading="allocateLoading"
:disabled="selectedAvailableDevices.length === 0"
>
{{ $t('common.confirm') }}
</ElButton>
</div>
@@ -225,7 +236,7 @@
import { h } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { EnterpriseService } from '@/api/modules'
import { EnterpriseService, DeviceService } from '@/api/modules'
import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type { SearchFormItem } from '@/types'
@@ -239,6 +250,7 @@
FailedDeviceItem
} from '@/types/api/enterpriseDevice'
import type { EnterpriseItem } from '@/types/api'
import type { Device, DeviceStatus } from '@/types/api/device'
defineOptions({ name: 'EnterpriseDevices' })
@@ -252,12 +264,10 @@
const recallLoading = ref(false)
const resultDialogVisible = ref(false)
const tableRef = ref()
const allocateFormRef = ref<FormInstance>()
const recallFormRef = ref<FormInstance>()
const selectedDevices = ref<EnterpriseDeviceItem[]>([])
const enterpriseId = ref<number>(0)
const enterpriseInfo = ref<EnterpriseItem | null>(null)
const deviceNosText = ref('')
const operationResult = ref<AllocateDevicesResponse | RecallDevicesResponse>({
success_count: 0,
fail_count: 0,
@@ -265,6 +275,28 @@
authorized_devices: null
})
// 可用设备列表相关
const availableDevicesTableRef = ref()
const availableDevicesLoading = ref(false)
const availableDevicesList = ref<Device[]>([])
const selectedAvailableDevices = ref<Device[]>([])
const allocatedDeviceVirtualNos = ref<Set<string>>(new Set())
// 设备搜索表单初始值
const initialDeviceSearchState = {
status: undefined,
virtual_no: '',
device_name: '',
shop_id: undefined
}
const deviceSearchForm = reactive({ ...initialDeviceSearchState })
const devicePagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
// 搜索表单初始值
const initialSearchState = {
virtual_no: ''
@@ -273,31 +305,6 @@
// 搜索表单
const searchForm = reactive({ ...initialSearchState })
// 授权表单
const allocateForm = reactive({
virtual_nos: [] as string[],
remark: ''
})
// 授权表单验证规则
const allocateRules = reactive<FormRules>({
virtual_nos: [
{
required: true,
validator: (rule, value, callback) => {
if (!value || value.length === 0) {
callback(new Error(t('enterpriseDevices.validation.deviceNosRequired')))
} else if (value.length > 100) {
callback(new Error(t('enterpriseDevices.validation.deviceNosMaxLength')))
} else {
callback()
}
},
trigger: 'change'
}
]
})
// 撤销表单
const recallForm = reactive({})
@@ -324,6 +331,43 @@
}
]
// 设备列表搜索表单配置
const deviceSearchFormItems: SearchFormItem[] = [
{
label: '设备状态',
prop: 'status',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '在库', value: 1 },
{ label: '已分销', value: 2 },
{ label: '已激活', value: 3 },
{ label: '已停用', value: 4 }
]
},
{
label: '设备号',
prop: 'virtual_no',
type: 'input',
config: {
clearable: true,
placeholder: '请输入设备号'
}
},
{
label: '设备名称',
prop: 'device_name',
type: 'input',
config: {
clearable: true,
placeholder: '请输入设备名称'
}
}
]
// 列配置
const columnOptions = [
{ label: t('enterpriseDevices.table.deviceId'), prop: 'device_id' },
@@ -336,6 +380,38 @@
const deviceList = ref<EnterpriseDeviceItem[]>([])
// 获取设备状态标签类型
const getDeviceStatusTag = (status: DeviceStatus) => {
switch (status) {
case 1:
return 'info'
case 2:
return 'warning'
case 3:
return 'success'
case 4:
return 'danger'
default:
return 'info'
}
}
// 获取设备状态文本
const getDeviceStatusText = (status: DeviceStatus) => {
switch (status) {
case 1:
return '在库'
case 2:
return '已分销'
case 3:
return '已激活'
case 4:
return '已停用'
default:
return '未知'
}
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
@@ -372,6 +448,45 @@
}
])
// 可用设备列表列配置
const availableDeviceColumns = computed(() => [
{
prop: 'virtual_no',
label: '设备号',
minWidth: 150
},
{
prop: 'device_name',
label: '设备名称',
minWidth: 150
},
{
prop: 'device_model',
label: '设备型号',
width: 120
},
{
prop: 'status',
label: '状态',
width: 100,
formatter: (row: Device) => {
return h(ElTag, { type: getDeviceStatusTag(row.status) }, () =>
getDeviceStatusText(row.status)
)
}
},
{
prop: 'shop_name',
label: '所属店铺',
width: 150
},
{
prop: 'bound_card_count',
label: '绑定卡数',
width: 100
}
])
onMounted(() => {
const id = route.query.id
if (id) {
@@ -469,86 +584,130 @@
router.back()
}
// 显示授权对话框
const showAllocateDialog = () => {
allocateDialogVisible.value = true
deviceNosText.value = ''
allocateForm.virtual_nos = []
allocateForm.remark = ''
if (allocateFormRef.value) {
allocateFormRef.value.resetFields()
// 获取可用设备列表
const getAvailableDevicesList = async () => {
availableDevicesLoading.value = true
try {
const params: any = {
page: devicePagination.page,
page_size: devicePagination.pageSize,
...deviceSearchForm
}
// 清理空值
Object.keys(params).forEach((key) => {
if (params[key] === '' || params[key] === undefined) {
delete params[key]
}
})
const res = await DeviceService.getDevices(params)
if (res.code === 0) {
availableDevicesList.value = res.data.items || []
devicePagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
ElMessage.error('获取设备列表失败')
} finally {
availableDevicesLoading.value = false
}
}
// 处理设备号输入变化
const handleDeviceNosChange = () => {
// 解析输入的设备号,支持逗号、空格、换行分隔
const deviceNos = deviceNosText.value
.split(/[,\s\n]+/)
.map((no) => no.trim())
.filter((no) => no.length > 0)
allocateForm.virtual_nos = deviceNos
// 处理设备列表选择变化
const handleAvailableDevicesSelectionChange = (selection: Device[]) => {
selectedAvailableDevices.value = selection
}
// 设备列表搜索
const handleDeviceSearch = () => {
devicePagination.page = 1
getAvailableDevicesList()
}
// 设备列表重置
const handleDeviceSearchReset = () => {
Object.assign(deviceSearchForm, { ...initialDeviceSearchState })
devicePagination.page = 1
getAvailableDevicesList()
}
// 设备列表分页大小变化
const handleDevicePageSizeChange = (newPageSize: number) => {
devicePagination.pageSize = newPageSize
getAvailableDevicesList()
}
// 设备列表页码变化
const handleDevicePageChange = (newPage: number) => {
devicePagination.page = newPage
getAvailableDevicesList()
}
// 检查设备是否可选(已授权的设备不可选)
const checkDeviceSelectable = (row: Device) => {
return !allocatedDeviceVirtualNos.value.has(row.virtual_no)
}
// 显示授权对话框
const showAllocateDialog = () => {
allocateDialogVisible.value = true
selectedAvailableDevices.value = []
// 收集已授权的设备的 virtual_no
allocatedDeviceVirtualNos.value = new Set(deviceList.value.map((device) => device.virtual_no))
// 重置搜索条件
Object.assign(deviceSearchForm, { ...initialDeviceSearchState })
devicePagination.page = 1
devicePagination.pageSize = 20
// 加载可用设备列表
getAvailableDevicesList()
}
// 执行授权
const handleAllocate = async () => {
if (!allocateFormRef.value) return
if (selectedAvailableDevices.value.length === 0) {
ElMessage.warning('请选择要授权的设备')
return
}
await allocateFormRef.value.validate(async (valid) => {
if (valid) {
if (allocateForm.virtual_nos.length === 0) {
ElMessage.warning(t('enterpriseDevices.messages.deviceNosEmpty'))
return
}
allocateLoading.value = true
try {
const device_nos = selectedAvailableDevices.value.map((device) => device.virtual_no)
const res = await EnterpriseService.allocateDevices(enterpriseId.value, {
device_nos
})
if (allocateForm.virtual_nos.length > 100) {
ElMessage.warning(t('enterpriseDevices.messages.deviceNosMaxLimit'))
return
}
if (res.code === 0) {
operationResult.value = res.data
allocateDialogVisible.value = false
resultDialogVisible.value = true
allocateLoading.value = true
try {
const res = await EnterpriseService.allocateDevices(enterpriseId.value, {
virtual_nos: allocateForm.virtual_nos,
remark: allocateForm.remark || undefined
})
if (res.code === 0) {
operationResult.value = res.data
allocateDialogVisible.value = false
resultDialogVisible.value = true
// 显示成功消息
if (res.data.success_count > 0 && res.data.fail_count === 0) {
ElMessage.success(t('enterpriseDevices.messages.allocateSuccess'))
} else if (res.data.success_count > 0 && res.data.fail_count > 0) {
ElMessage.warning(
t('enterpriseDevices.messages.allocatePartialSuccess', {
success: res.data.success_count,
fail: res.data.fail_count
})
)
} else {
ElMessage.error(t('enterpriseDevices.messages.allocateFailed'))
}
getTableData()
}
} catch (error) {
console.error(error)
// 显示成功消息
if (res.data.success_count > 0 && res.data.fail_count === 0) {
ElMessage.success(t('enterpriseDevices.messages.allocateSuccess'))
} else if (res.data.success_count > 0 && res.data.fail_count > 0) {
ElMessage.warning(
t('enterpriseDevices.messages.allocatePartialSuccess', {
success: res.data.success_count,
fail: res.data.fail_count
})
)
} else {
ElMessage.error(t('enterpriseDevices.messages.allocateFailed'))
} finally {
allocateLoading.value = false
}
getTableData()
}
})
} catch (error) {
console.error(error)
ElMessage.error(t('enterpriseDevices.messages.allocateFailed'))
} finally {
allocateLoading.value = false
}
}
// 关闭授权对话框
const handleAllocateDialogClose = () => {
if (allocateFormRef.value) {
allocateFormRef.value.resetFields()
}
selectedAvailableDevices.value = []
}
// 显示撤销授权对话框
@@ -582,7 +741,7 @@
recallLoading.value = true
try {
const res = await EnterpriseService.recallDevices(enterpriseId.value, {
virtual_nos: selectedDevices.value.map((device) => device.virtual_no)
device_nos: selectedDevices.value.map((device) => device.virtual_no)
})
if (res.code === 0) {
@@ -636,5 +795,12 @@
align-items: center;
justify-content: space-between;
}
.device-selection-info {
margin-top: 10px;
margin-bottom: 12px;
color: var(--el-color-info);
font-size: 14px;
}
}
</style>

View File

@@ -83,7 +83,7 @@
placeholder="请选择支付方式"
style="width: 100%"
>
<ElOption label="微信在线支付" value="wechat" />
<!--<ElOption label="微信在线支付" value="wechat" />-->
<!-- 只有平台用户才显示线下转账选项 -->
<ElOption
v-if="userStore.info.user_type === 1 || userStore.info.user_type === 2"
@@ -298,7 +298,7 @@
const createForm = reactive<{ amount: number; payment_method: string; shop_id: number | null }>({
amount: 100,
payment_method: 'wechat',
payment_method: '线下转账',
shop_id: null
})
@@ -537,7 +537,7 @@
const handleCreateDialogClosed = () => {
createFormRef.value?.resetFields()
createForm.amount = 100
createForm.payment_method = 'wechat'
createForm.payment_method = '线下转账'
createForm.shop_id = null
}

View File

@@ -77,10 +77,10 @@
v-model="form.provider_type"
placeholder="请选择支付渠道类型"
style="width: 100%"
:disabled="dialogType === 'edit'"
@change="handleProviderTypeChange"
>
<ElOption label="微信直连" value="wechat" />
<ElOption label="微信直连V2" value="wechat_v2" />
<ElOption label="富友支付" value="fuiou" />
</ElSelect>
</ElFormItem>
@@ -96,7 +96,7 @@
</ElFormItem>
<!-- 微信相关配置 -->
<template v-if="form.provider_type === 'wechat'">
<template v-if="form.provider_type === 'wechat' || form.provider_type === 'wechat_v2'">
<ElDivider content-position="left">
<span class="divider-title">小程序配置</span>
</ElDivider>
@@ -106,7 +106,6 @@
<ElInput
v-model="form.miniapp_app_id"
placeholder="请输入小程序AppID"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
@@ -131,7 +130,6 @@
<ElInput
v-model="form.oa_app_id"
placeholder="请输入公众号AppID"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
@@ -172,7 +170,6 @@
<ElInput
v-model="form.oa_oauth_redirect_url"
placeholder="请输入OAuth回调地址"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
@@ -185,7 +182,6 @@
<ElInput
v-model="form.wx_mch_id"
placeholder="请输入微信商户号"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
@@ -194,7 +190,6 @@
<ElInput
v-model="form.wx_serial_no"
placeholder="请输入微信证书序列号"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
@@ -225,7 +220,6 @@
<ElInput
v-model="form.wx_notify_url"
placeholder="请输入微信支付回调地址"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
<ElRow :gutter="20">
@@ -261,7 +255,6 @@
<ElInput
v-model="form.miniapp_app_id"
placeholder="请输入小程序AppID"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
@@ -286,7 +279,6 @@
<ElInput
v-model="form.oa_app_id"
placeholder="请输入公众号AppID"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
@@ -327,7 +319,6 @@
<ElInput
v-model="form.oa_oauth_redirect_url"
placeholder="请输入OAuth回调地址"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
@@ -340,7 +331,6 @@
<ElInput
v-model="form.wx_mch_id"
placeholder="请输入微信商户号"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
@@ -349,7 +339,6 @@
<ElInput
v-model="form.wx_serial_no"
placeholder="请输入微信证书序列号"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
@@ -380,7 +369,6 @@
<ElInput
v-model="form.wx_notify_url"
placeholder="请输入微信支付回调地址"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
<ElRow :gutter="20">
@@ -415,7 +403,6 @@
<ElInput
v-model="form.fy_api_url"
placeholder="请输入富友API地址"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
@@ -424,7 +411,6 @@
<ElInput
v-model="form.fy_ins_cd"
placeholder="请输入富友机构号"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
@@ -435,7 +421,6 @@
<ElInput
v-model="form.fy_mchnt_cd"
placeholder="请输入富友商户号"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
@@ -444,7 +429,6 @@
<ElInput
v-model="form.fy_term_id"
placeholder="请输入富友终端号"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
</ElCol>
@@ -453,7 +437,6 @@
<ElInput
v-model="form.fy_notify_url"
placeholder="请输入富友支付回调地址"
:disabled="dialogType === 'edit'"
/>
</ElFormItem>
<ElRow :gutter="20">
@@ -551,6 +534,7 @@
placeholder: '请选择支付渠道类型',
options: [
{ label: '微信直连', value: 'wechat' },
{ label: '微信直连V2', value: 'wechat_v2' },
{ label: '富友支付', value: 'fuiou' }
],
config: {
@@ -607,55 +591,15 @@
}
}
// 自定义验证器 - 编辑模式下允许为空,新增模式下必填
const createRequiredValidator = (fieldName: string) => {
return (rule: any, value: any, callback: any) => {
if (dialogType.value === 'add' && !value) {
callback(new Error(`请输入${fieldName}`))
} else {
callback()
}
}
}
// 静态验证规则 - 一次性定义所有规则
// 静态验证规则 - 只有配置名称和支付渠道类型是必填
const rules = reactive<FormRules>({
name: [{ required: true, message: '请输入配置名称', trigger: 'blur' }],
provider_type: [{ required: true, message: '请选择支付渠道类型', trigger: 'change' }],
// 所有字段的验证规则都定义好
miniapp_app_id: [{ required: true, validator: createRequiredValidator('小程序AppID'), trigger: 'blur' }],
miniapp_app_secret: [{ required: true, validator: createRequiredValidator('小程序AppSecret'), trigger: 'blur' }],
oa_app_id: [{ required: true, validator: createRequiredValidator('公众号AppID'), trigger: 'blur' }],
oa_app_secret: [{ required: true, validator: createRequiredValidator('公众号AppSecret'), trigger: 'blur' }],
oa_token: [{ required: true, validator: createRequiredValidator('公众号Token'), trigger: 'blur' }],
oa_aes_key: [{ required: true, validator: createRequiredValidator('公众号AES Key'), trigger: 'blur' }],
oa_oauth_redirect_url: [
{ required: true, validator: createRequiredValidator('OAuth回调地址'), trigger: 'blur' },
{ validator: urlValidator, trigger: 'blur' }
],
wx_mch_id: [{ required: true, validator: createRequiredValidator('微信商户号'), trigger: 'blur' }],
wx_api_v2_key: [{ required: true, validator: createRequiredValidator('微信APIv2密钥'), trigger: 'blur' }],
wx_api_v3_key: [{ required: true, validator: createRequiredValidator('微信APIv3密钥'), trigger: 'blur' }],
wx_serial_no: [{ required: true, validator: createRequiredValidator('微信证书序列号'), trigger: 'blur' }],
wx_cert_content: [{ required: true, validator: createRequiredValidator('微信支付证书内容'), trigger: 'blur' }],
wx_key_content: [{ required: true, validator: createRequiredValidator('微信支付密钥内容'), trigger: 'blur' }],
wx_notify_url: [
{ required: true, validator: createRequiredValidator('微信支付回调地址'), trigger: 'blur' },
{ validator: urlValidator, trigger: 'blur' }
],
fy_api_url: [
{ required: true, validator: createRequiredValidator('富友API地址'), trigger: 'blur' },
{ validator: urlValidator, trigger: 'blur' }
],
fy_ins_cd: [{ required: true, validator: createRequiredValidator('富友机构号'), trigger: 'blur' }],
fy_mchnt_cd: [{ required: true, validator: createRequiredValidator('富友商户号'), trigger: 'blur' }],
fy_term_id: [{ required: true, validator: createRequiredValidator('富友终端号'), trigger: 'blur' }],
fy_private_key: [{ required: true, validator: createRequiredValidator('富友私钥'), trigger: 'blur' }],
fy_public_key: [{ required: true, validator: createRequiredValidator('富友公钥'), trigger: 'blur' }],
fy_notify_url: [
{ required: true, validator: createRequiredValidator('富友支付回调地址'), trigger: 'blur' },
{ validator: urlValidator, trigger: 'blur' }
]
// 其他字段都是可选的,只验证URL格式
oa_oauth_redirect_url: [{ validator: urlValidator, trigger: 'blur' }],
wx_notify_url: [{ validator: urlValidator, trigger: 'blur' }],
fy_api_url: [{ validator: urlValidator, trigger: 'blur' }],
fy_notify_url: [{ validator: urlValidator, trigger: 'blur' }]
})
const form = reactive({
@@ -721,6 +665,7 @@
const getProviderTypeText = (type: PaymentProviderType): string => {
const typeMap: Record<PaymentProviderType, string> = {
wechat: '微信直连',
wechat_v2: '微信直连V2',
fuiou: '富友支付'
}
return typeMap[type] || type
@@ -728,7 +673,7 @@
// 获取商户号
const getMerchantId = (row: WechatConfig): string => {
if (row.provider_type === 'wechat') {
if (row.provider_type === 'wechat' || row.provider_type === 'wechat_v2') {
return row.wx_mch_id || '-'
}
if (row.provider_type === 'fuiou') {
@@ -739,7 +684,7 @@
// 获取支付回调地址
const getNotifyUrl = (row: WechatConfig): string => {
if (row.provider_type === 'wechat') {
if (row.provider_type === 'wechat' || row.provider_type === 'wechat_v2') {
return row.wx_notify_url || '-'
}
if (row.provider_type === 'fuiou') {