修复platform的支付问题
This commit is contained in:
@@ -267,7 +267,8 @@ onMounted(refresh);
|
||||
|
||||
<style scoped lang="less">
|
||||
.container-box {
|
||||
padding: 4px;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.header-bar {
|
||||
@@ -289,7 +290,7 @@ onMounted(refresh);
|
||||
}
|
||||
|
||||
.tip-alert {
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
p {
|
||||
margin: 4px 0;
|
||||
@@ -299,7 +300,15 @@ onMounted(refresh);
|
||||
}
|
||||
|
||||
.package-tabs {
|
||||
margin-top: 8px;
|
||||
margin-top: 0;
|
||||
|
||||
:deep(.el-tabs__header) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__nav-wrap::after) {
|
||||
height: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.module-tag {
|
||||
@@ -318,4 +327,37 @@ onMounted(refresh);
|
||||
:deep(.el-empty) {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
// ===== 暗色主题适配 =====
|
||||
.container-box {
|
||||
html.dark & {
|
||||
.header-bar h2 {
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
:deep(.el-divider) {
|
||||
border-color: var(--el-border-color-light);
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
--el-table-border-color: var(--el-border-color-lighter);
|
||||
--el-table-header-text-color: var(--el-text-color-secondary);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
:deep(.el-table__inner-wrapper),
|
||||
:deep(.el-table tr),
|
||||
:deep(.el-table td.el-table__cell) {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__item) {
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
:deep(.el-tabs__item.is-active) {
|
||||
color: var(--el-color-primary-dark-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -139,8 +139,7 @@ const form = reactive({
|
||||
|
||||
const rules: FormRules = {
|
||||
client_id: [{ required: true, message: '请输入 Client ID', trigger: 'blur' }],
|
||||
client_secret: [{ required: true, message: '请输入 Client Secret', trigger: 'blur' }],
|
||||
webhook_id: [{ required: true, message: '请输入 Webhook ID', trigger: 'blur' }]
|
||||
client_secret: [{ required: true, message: '请输入 Client Secret', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
/* ---------------- 敏感字段掩码编辑 ---------------- */
|
||||
@@ -226,7 +225,7 @@ async function handleTest() {
|
||||
},
|
||||
extra: { env: form.env }
|
||||
})
|
||||
ElMessage.success(res?.data?.message || '连接成功:OAuth2 凭证校验通过')
|
||||
ElMessage.success(res?.data?.message || '连接成功:OAuth2 凭证校验通过(请点击下方「保存配置」使配置生效)')
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.response?.data?.msg || error?.message || '连接失败,请检查 Client ID / Secret 与运行环境')
|
||||
} finally {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
<div class="title-wrap">
|
||||
<div class="channel-name">{{ name }}</div>
|
||||
<div class="merchant-no">商户号:{{ merchantNo || '未配置' }}</div>
|
||||
<div class="merchant-no">{{ idLabel }}:{{ merchantNo || (status === 'unconfigured' ? '未配置' : '已配置') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<StatusTag :status="status" dict="channel" />
|
||||
@@ -22,7 +22,16 @@
|
||||
<el-button v-if="status === 'enabled'" size="small" type="danger" plain @click="emit('disable')">
|
||||
停用
|
||||
</el-button>
|
||||
<el-button v-else size="small" type="success" plain @click="emit('enable')">启用</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
size="small"
|
||||
type="success"
|
||||
plain
|
||||
:disabled="status === 'unconfigured'"
|
||||
@click="emit('enable')"
|
||||
>
|
||||
启用
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
@@ -51,6 +60,8 @@ const ICONS: Record<string, any> = {
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/** 渠道标识,如 wechat / alipay / paypal */
|
||||
channel?: string
|
||||
/** 渠道名称,如「微信支付」 */
|
||||
name: string
|
||||
/** 渠道状态:enabled 已启用 / disabled 已停用 / unconfigured 未配置 */
|
||||
@@ -63,6 +74,7 @@ const props = withDefaults(
|
||||
desc?: string
|
||||
}>(),
|
||||
{
|
||||
channel: '',
|
||||
status: 'unconfigured',
|
||||
merchantNo: '',
|
||||
icon: 'PriceTag',
|
||||
@@ -76,6 +88,12 @@ const emit = defineEmits<{
|
||||
(e: 'disable'): void
|
||||
}>()
|
||||
|
||||
const idLabel = computed(() => {
|
||||
if (props.channel === 'paypal') return 'Client ID'
|
||||
if (props.channel === 'cloudpay') return '银联商户号'
|
||||
return '商户号'
|
||||
})
|
||||
|
||||
const iconComp = computed(() => ICONS[props.icon] || PriceTag)
|
||||
|
||||
const statusClass = computed(() => {
|
||||
|
||||
@@ -23,79 +23,15 @@
|
||||
<span class="card-title">渠道状态概览</span>
|
||||
<div class="card-header-right">
|
||||
<span class="card-tip">共 {{ channelList.length }} 个渠道,已启用 {{ enabledCount }} 个</span>
|
||||
<el-radio-group v-model="viewMode" size="small">
|
||||
<el-radio-button value="table">表格</el-radio-button>
|
||||
<el-radio-button value="card">卡片</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table
|
||||
v-if="viewMode === 'table'"
|
||||
:data="channelList"
|
||||
stripe
|
||||
v-loading="loading"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column label="渠道名称" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<div class="channel-cell">
|
||||
<el-icon :size="16" class="channel-icon">
|
||||
<component :is="channelIcon(row.icon)" />
|
||||
</el-icon>
|
||||
<span>{{ row.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<StatusTag :status="row.status" dict="channel" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商户号" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.merchant_no">{{ row.merchant_no }}</span>
|
||||
<span v-else class="text-muted">未配置</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="说明" min-width="260" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span class="text-muted">{{ row.remark }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" type="primary" link @click="goChannelConfig(row)">
|
||||
<el-icon><Setting /></el-icon>
|
||||
配置
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'enabled'"
|
||||
size="small"
|
||||
type="danger"
|
||||
link
|
||||
@click="toggleChannelStatus(row, false)"
|
||||
>
|
||||
停用
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
size="small"
|
||||
type="success"
|
||||
link
|
||||
:disabled="row.status === 'unconfigured'"
|
||||
@click="toggleChannelStatus(row, true)"
|
||||
>
|
||||
启用
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div v-else class="channel-card-grid" v-loading="loading">
|
||||
<div class="channel-card-grid" v-loading="loading">
|
||||
<ChannelCard
|
||||
v-for="row in channelList"
|
||||
:key="row.channel"
|
||||
:channel="row.channel"
|
||||
:name="row.name"
|
||||
:status="row.status"
|
||||
:merchant-no="row.merchant_no"
|
||||
@@ -174,27 +110,18 @@ import { useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import {
|
||||
Refresh,
|
||||
Setting,
|
||||
ArrowRight,
|
||||
Tickets,
|
||||
Coin,
|
||||
DataAnalysis,
|
||||
Document,
|
||||
ChatDotRound,
|
||||
Wallet,
|
||||
CreditCard,
|
||||
Lightning,
|
||||
Money
|
||||
Document
|
||||
} from '@element-plus/icons-vue'
|
||||
import StatusTag from './components/StatusTag.vue'
|
||||
import ChannelCard from './components/ChannelCard.vue'
|
||||
// TODO: 接口联调时打开
|
||||
// import { getChannelList, getOrders, toggleChannel } from '@/api/payment'
|
||||
import { getChannelList, getOrders, toggleChannel } from '@/api/payment'
|
||||
|
||||
const router = useRouter()
|
||||
const loading = ref(false)
|
||||
/** 渠道概览视图:table 表格 / card 卡片 */
|
||||
const viewMode = ref<'table' | 'card'>('table')
|
||||
|
||||
/** TODO: 路由 path 需与你配置的菜单 path 保持一致,例如实际为 /payment/orders 时请同步修改 */
|
||||
const ROUTES = {
|
||||
@@ -212,16 +139,6 @@ const ROUTES = {
|
||||
} as Record<string, string>
|
||||
}
|
||||
|
||||
/** 渠道图标映射(图标名与 ChannelCard 组件保持一致) */
|
||||
const CHANNEL_ICONS: Record<string, any> = {
|
||||
ChatDotRound,
|
||||
Wallet,
|
||||
CreditCard,
|
||||
Lightning,
|
||||
Money
|
||||
}
|
||||
const channelIcon = (icon: string) => CHANNEL_ICONS[icon] || Money
|
||||
|
||||
/* ---------------- 渠道概览 ---------------- */
|
||||
|
||||
interface ChannelRow {
|
||||
@@ -233,20 +150,48 @@ interface ChannelRow {
|
||||
remark: string
|
||||
}
|
||||
|
||||
const DEFAULT_CHANNEL_META: Record<string, { name: string; icon: string; remark: string }> = {
|
||||
wechat: {
|
||||
name: '微信支付',
|
||||
icon: 'ChatDotRound',
|
||||
remark: 'JSAPI / Native 扫码收款,平台使用费主渠道'
|
||||
},
|
||||
alipay: {
|
||||
name: '支付宝',
|
||||
icon: 'Wallet',
|
||||
remark: '电脑网站支付 / 手机网站支付'
|
||||
},
|
||||
unionpay: {
|
||||
name: '银联',
|
||||
icon: 'CreditCard',
|
||||
remark: '银联全渠道收单,需上传商户证书'
|
||||
},
|
||||
cloudpay: {
|
||||
name: '云闪付',
|
||||
icon: 'Lightning',
|
||||
remark: '复用银联商户参数,仅控制收银台是否展示云闪付标识'
|
||||
},
|
||||
paypal: {
|
||||
name: 'PayPal',
|
||||
icon: 'Money',
|
||||
remark: '面向港澳台及海外租户,当前无跨境需求,默认关闭'
|
||||
}
|
||||
}
|
||||
|
||||
const channelList = ref<ChannelRow[]>([
|
||||
{
|
||||
channel: 'wechat',
|
||||
name: '微信支付',
|
||||
status: 'enabled',
|
||||
merchant_no: '1620888999',
|
||||
status: 'unconfigured',
|
||||
merchant_no: '',
|
||||
icon: 'ChatDotRound',
|
||||
remark: 'JSAPI / Native 扫码收款,平台使用费主渠道'
|
||||
},
|
||||
{
|
||||
channel: 'alipay',
|
||||
name: '支付宝',
|
||||
status: 'enabled',
|
||||
merchant_no: '2088123456789012',
|
||||
status: 'unconfigured',
|
||||
merchant_no: '',
|
||||
icon: 'Wallet',
|
||||
remark: '电脑网站支付 / 手机网站支付'
|
||||
},
|
||||
@@ -261,15 +206,15 @@ const channelList = ref<ChannelRow[]>([
|
||||
{
|
||||
channel: 'cloudpay',
|
||||
name: '云闪付',
|
||||
status: 'disabled',
|
||||
merchant_no: '898110158000000',
|
||||
status: 'unconfigured',
|
||||
merchant_no: '',
|
||||
icon: 'Lightning',
|
||||
remark: '复用银联商户参数,仅控制收银台是否展示云闪付标识'
|
||||
},
|
||||
{
|
||||
channel: 'paypal',
|
||||
name: 'PayPal',
|
||||
status: 'disabled',
|
||||
status: 'unconfigured',
|
||||
merchant_no: '',
|
||||
icon: 'Money',
|
||||
remark: '面向港澳台及海外租户,当前无跨境需求,默认关闭'
|
||||
@@ -331,34 +276,66 @@ interface RecentOrder {
|
||||
|
||||
const recentOrders = ref<RecentOrder[]>([])
|
||||
|
||||
/** 最近 10 条:TODO 换成 getOrders({ page: 1, pageSize: 10 }) */
|
||||
function loadRecentOrders() {
|
||||
recentOrders.value = [
|
||||
{ pay_no: 'P202609150009', tenant_name: '杭州云泽科技有限公司', amount: 299900, channel: 'wechat', status: 'paid', create_time: '2026-09-15 10:24:31' },
|
||||
{ pay_no: 'P202609150008', tenant_name: '宁波海曙贸易有限公司', amount: 99900, channel: 'alipay', status: 'paid', create_time: '2026-09-15 09:58:02' },
|
||||
{ pay_no: 'P202609150007', tenant_name: '上海弘远信息技术有限公司', amount: 199900, channel: 'wechat', status: 'pending', create_time: '2026-09-15 09:31:47' },
|
||||
{ pay_no: 'P202609150006', tenant_name: '深圳市博通电子商务有限公司', amount: 499900, channel: 'alipay', status: 'paid', create_time: '2026-09-15 09:12:15' },
|
||||
{ pay_no: 'P202609150005', tenant_name: '北京中科智联科技有限公司', amount: 99900, channel: 'wechat', status: 'failed', create_time: '2026-09-15 08:56:39' },
|
||||
{ pay_no: 'P202609140021', tenant_name: '成都天府软件有限公司', amount: 299900, channel: 'wechat', status: 'paid', create_time: '2026-09-14 18:42:10' },
|
||||
{ pay_no: 'P202609140020', tenant_name: '武汉光谷数字科技有限公司', amount: 199900, channel: 'alipay', status: 'closed', create_time: '2026-09-14 17:20:55' },
|
||||
{ pay_no: 'P202609140019', tenant_name: '西安丝路网络科技有限公司', amount: 99900, channel: 'wechat', status: 'paid', create_time: '2026-09-14 16:05:23' },
|
||||
{ pay_no: 'P202609140018', tenant_name: '青岛海洋装备有限公司', amount: 599900, channel: 'alipay', status: 'paid', create_time: '2026-09-14 14:48:07' },
|
||||
{ pay_no: 'P202609140017', tenant_name: '长沙湘江云计算有限公司', amount: 299900, channel: 'wechat', status: 'refunded', create_time: '2026-09-14 11:33:41' }
|
||||
]
|
||||
/** 最近 10 条 */
|
||||
async function loadRecentOrders() {
|
||||
try {
|
||||
const res: any = await getOrders({ page: 1, pageSize: 10 })
|
||||
if (res?.code === 200 && res.data) {
|
||||
const list = res.data.list || []
|
||||
recentOrders.value = list.map((item: any) => ({
|
||||
pay_no: item.pay_no,
|
||||
tenant_name: item.tenant_name || '-',
|
||||
amount: item.amount,
|
||||
channel: item.channel,
|
||||
status: item.status,
|
||||
create_time: item.create_time
|
||||
}))
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('加载最近交易失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------- 数据加载与操作 ---------------- */
|
||||
|
||||
async function fetchChannels() {
|
||||
// TODO: 接口联通后替换为真实请求
|
||||
// const res = await getChannelList()
|
||||
// if (res.success) channelList.value = res.data.list || res.data || []
|
||||
try {
|
||||
const res: any = await getChannelList()
|
||||
if (res?.code === 200 && res.data) {
|
||||
const list = res.data.list || []
|
||||
channelList.value = list.map((item: any) => {
|
||||
const meta = DEFAULT_CHANNEL_META[item.channel] || {
|
||||
name: item.name,
|
||||
icon: 'Money',
|
||||
remark: ''
|
||||
}
|
||||
let status: 'enabled' | 'disabled' | 'unconfigured' = 'unconfigured'
|
||||
if (item.status) {
|
||||
status = item.status
|
||||
} else if (item.enabled) {
|
||||
status = 'enabled'
|
||||
} else if (item.merchant_no || item.has_config || item.last_test_result) {
|
||||
status = 'disabled'
|
||||
}
|
||||
return {
|
||||
channel: item.channel,
|
||||
name: item.name || meta.name,
|
||||
status,
|
||||
merchant_no: item.merchant_no || '',
|
||||
icon: meta.icon || 'Money',
|
||||
remark: item.remark || meta.remark || ''
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.response?.data?.msg || error?.message || '获取渠道列表失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchData() {
|
||||
loading.value = true
|
||||
try {
|
||||
await Promise.all([fetchChannels(), Promise.resolve(loadRecentOrders())])
|
||||
await Promise.allSettled([fetchChannels(), loadRecentOrders()])
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.message || '加载支付概览失败')
|
||||
} finally {
|
||||
@@ -390,9 +367,17 @@ function goOrderDetail(row: RecentOrder) {
|
||||
}
|
||||
|
||||
async function toggleChannelStatus(row: ChannelRow, enable: boolean) {
|
||||
// TODO: 接口联通后替换为 toggleChannel(row.channel, enable)
|
||||
row.status = enable ? 'enabled' : 'disabled'
|
||||
ElMessage.success(`${row.name}已${enable ? '启用' : '停用'}`)
|
||||
try {
|
||||
const res: any = await toggleChannel(row.channel, enable)
|
||||
if (res?.code === 200) {
|
||||
row.status = enable ? 'enabled' : 'disabled'
|
||||
ElMessage.success(`${row.name}已${enable ? '启用' : '停用'}`)
|
||||
} else {
|
||||
ElMessage.error(res?.msg || '操作失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.response?.data?.msg || error?.message || '操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------- 工具函数 ---------------- */
|
||||
@@ -472,13 +457,15 @@ onMounted(() => {
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.channel-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@media (max-width: 1200px) {
|
||||
.channel-card-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.channel-icon {
|
||||
color: #409eff;
|
||||
@media (max-width: 768px) {
|
||||
.channel-card-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user