feat(payment): 支付&付费菜单按参考主题重构并新增提现策略页
- 子菜单重组为知识付费/余额充值/VIP会员/创作分成/推广返佣/收款接口/提现策略 - 收款接口页 1:1 复刻 Zibll 双下拉结构,仅保留支付宝/微信/PayPal/Stripe/易支付 - 订单前缀/流水号前缀/提现单号前缀配置保留并移入收款接口页 - 知识付费/推广返佣按参考主题全字段复刻,余额充值并入充值卡配置 - 新增 tabbed/between_number/page_select 表单控件及后端 pages 搜索源 - 清理旧 SPA 组件、无消费者 REST 端点与废弃配置键(不写双路兼容) - 更新存量契约测试至新 schema 文件
This commit is contained in:
@@ -2,7 +2,6 @@ import { globalApiMethods } from './global.js'
|
||||
import { pageDisplayApiMethods } from './page-display.js'
|
||||
import { permissionsApiMethods } from './permissions.js'
|
||||
import { usersApiMethods } from './users.js'
|
||||
import { paymentApiMethods } from './payment.js'
|
||||
import { extensionsApiMethods } from './extensions.js'
|
||||
import { toolsApiMethods } from './tools.js'
|
||||
import { schemaApiMethods } from './schema.js'
|
||||
@@ -13,7 +12,6 @@ export const apiMethods = {
|
||||
...pageDisplayApiMethods,
|
||||
...permissionsApiMethods,
|
||||
...usersApiMethods,
|
||||
...paymentApiMethods,
|
||||
...extensionsApiMethods,
|
||||
...toolsApiMethods,
|
||||
...schemaApiMethods,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export const paymentApiMethods = {
|
||||
/* ================================================================
|
||||
* 支付&付费 (Payment)
|
||||
* ================================================================ */
|
||||
|
||||
getPaymentOverview() { return this.request('/payment/overview') },
|
||||
}
|
||||
@@ -142,6 +142,22 @@
|
||||
@update:value="emit('update:modelValue', $event)"
|
||||
/>
|
||||
|
||||
<!-- Page Select (async page list) -->
|
||||
<n-select
|
||||
v-else-if="schema.type === 'page_select'"
|
||||
:value="modelValue"
|
||||
:options="pageOptions"
|
||||
:loading="pageLoading"
|
||||
:placeholder="schema.placeholder || '选择页面'"
|
||||
:multiple="schema.multiple === true"
|
||||
:filterable="true"
|
||||
:remote="true"
|
||||
:clearable="schema.clearable !== false"
|
||||
:disabled="schema.disabled"
|
||||
@search="handlePageSearch"
|
||||
@update:value="emit('update:modelValue', $event)"
|
||||
/>
|
||||
|
||||
<!-- Radio group -->
|
||||
<n-radio-group
|
||||
v-else-if="schema.type === 'radio'"
|
||||
@@ -332,6 +348,32 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Between number (min/max pair, Zibll between_number) -->
|
||||
<div v-else-if="schema.type === 'between_number'" class="aksrt-field__between">
|
||||
<n-input-number
|
||||
:value="betweenValue('min')"
|
||||
:min="schema.min"
|
||||
:max="schema.max"
|
||||
:step="schema.step || 1"
|
||||
:placeholder="schema.minPlaceholder || '最小'"
|
||||
:disabled="schema.disabled"
|
||||
style="flex: 1"
|
||||
@update:value="updateBetween('min', $event)"
|
||||
/>
|
||||
<span class="aksrt-field__between-sep">—</span>
|
||||
<n-input-number
|
||||
:value="betweenValue('max')"
|
||||
:min="schema.min"
|
||||
:max="schema.max"
|
||||
:step="schema.step || 1"
|
||||
:placeholder="schema.maxPlaceholder || '最大'"
|
||||
:disabled="schema.disabled"
|
||||
style="flex: 1"
|
||||
@update:value="updateBetween('max', $event)"
|
||||
/>
|
||||
<span v-if="schema.unit" class="aksrt-field__between-unit">{{ schema.unit }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Spacing (top right bottom left) -->
|
||||
<div v-else-if="schema.type === 'spacing'" class="aksrt-field__spacing">
|
||||
<div class="aksrt-field__spacing-label">{{ schema.spacingLabels?.[0] || '上' }}</div>
|
||||
@@ -807,6 +849,9 @@ onMounted(() => {
|
||||
if (props.schema.type === 'async_select') {
|
||||
loadAsyncOptions()
|
||||
}
|
||||
if (props.schema.type === 'page_select') {
|
||||
loadPages()
|
||||
}
|
||||
})
|
||||
|
||||
// ============ User Select (async search) ============
|
||||
@@ -850,6 +895,31 @@ function handleUserSearch(query) {
|
||||
}, 300)
|
||||
}
|
||||
|
||||
// ============ Page Select (async page list) ============
|
||||
const pageOptions = ref([])
|
||||
const pageLoading = ref(false)
|
||||
let pageSearchTimer = null
|
||||
|
||||
async function loadPages(query = '') {
|
||||
pageLoading.value = true
|
||||
try {
|
||||
const selected = props.modelValue ? [props.modelValue] : []
|
||||
const res = await api.searchPageDisplayOptions('pages', query, query ? [] : selected)
|
||||
const items = res?.data || res || []
|
||||
pageOptions.value = Array.isArray(items) ? items : []
|
||||
} catch (e) {
|
||||
console.warn('[AksField] page load failed:', e)
|
||||
pageOptions.value = []
|
||||
} finally {
|
||||
pageLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handlePageSearch(query) {
|
||||
clearTimeout(pageSearchTimer)
|
||||
pageSearchTimer = setTimeout(() => loadPages(query), 250)
|
||||
}
|
||||
|
||||
const fieldClasses = computed(() => ({
|
||||
'aksrt-field--disabled': props.schema.disabled,
|
||||
'aksrt-field--inline': props.schema.inline,
|
||||
@@ -870,6 +940,21 @@ function updateDimension(part, value) {
|
||||
emit('update:modelValue', { ...current, [part]: value })
|
||||
}
|
||||
|
||||
// ============ Between Number (min/max pair) ============
|
||||
function betweenValue(part) {
|
||||
const current = props.modelValue && typeof props.modelValue === 'object' && !Array.isArray(props.modelValue)
|
||||
? props.modelValue
|
||||
: {}
|
||||
return current[part] !== undefined ? current[part] : (props.schema.default?.[part] ?? 0)
|
||||
}
|
||||
|
||||
function updateBetween(part, value) {
|
||||
const current = props.modelValue && typeof props.modelValue === 'object' && !Array.isArray(props.modelValue)
|
||||
? props.modelValue
|
||||
: {}
|
||||
emit('update:modelValue', { ...current, [part]: value })
|
||||
}
|
||||
|
||||
// ============ Spacing ============
|
||||
function updateSpacing(part, value) {
|
||||
const current = props.modelValue || {}
|
||||
@@ -1115,6 +1200,25 @@ function confirmRepeaterModal() {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Between number (min/max) */
|
||||
.aksrt-field__between {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
max-width: 360px;
|
||||
}
|
||||
|
||||
.aksrt-field__between-sep {
|
||||
color: var(--aksrt-color-border-strong);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.aksrt-field__between-unit {
|
||||
color: var(--aksrt-color-muted);
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Spacing field */
|
||||
.aksrt-field__spacing {
|
||||
display: grid;
|
||||
|
||||
@@ -56,8 +56,33 @@
|
||||
<template v-if="group.fields && !hasGroupSlot(group)">
|
||||
<template v-for="field in group.fields" :key="field.key">
|
||||
<template v-if="isFieldVisible(field)">
|
||||
<!-- Tabbed: renders tabs whose sub-fields are flat top-level keys -->
|
||||
<div
|
||||
v-if="field.type === 'tabbed' && (field.tabs || []).length"
|
||||
class="aksrt-form-renderer__tabbed"
|
||||
>
|
||||
<n-tabs type="line" animated>
|
||||
<n-tab-pane
|
||||
v-for="tab in field.tabs"
|
||||
:key="tab.title || tab.key"
|
||||
:name="tab.title || tab.key"
|
||||
:tab="tab.title || tab.key"
|
||||
>
|
||||
<template v-for="sub in (tab.fields || [])" :key="sub.key">
|
||||
<AksField
|
||||
v-if="isFieldVisible(sub)"
|
||||
:schema="sub"
|
||||
:model-value="getFieldValue(sub.key)"
|
||||
:error-message="errors?.[sub.key]"
|
||||
@update:model-value="setFieldValue(sub.key, $event)"
|
||||
@action="emit('field-action', $event)"
|
||||
/>
|
||||
</template>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</div>
|
||||
<slot
|
||||
v-if="field.type === 'custom'"
|
||||
v-else-if="field.type === 'custom'"
|
||||
:name="`field-${field.key}`"
|
||||
v-bind="{ field, value: getFieldValue(field.key), setValue: (value) => setFieldValue(field.key, value), formData }"
|
||||
>
|
||||
@@ -268,6 +293,15 @@ const filteredSchema = computed(() => {
|
||||
// Filter fields by label, key, or help text matching query
|
||||
const matchingFields = group.fields.filter(field => {
|
||||
if (groupMatch) return true
|
||||
if (field.type === 'tabbed' && Array.isArray(field.tabs)) {
|
||||
return (field.label || '').toLowerCase().includes(q)
|
||||
|| field.tabs.some(tab => (tab.fields || []).some(sub =>
|
||||
(sub.label || '').toLowerCase().includes(q)
|
||||
|| (sub.key || '').toLowerCase().includes(q)
|
||||
|| (sub.help || '').toLowerCase().includes(q)
|
||||
|| (sub.description || '').toLowerCase().includes(q)
|
||||
))
|
||||
}
|
||||
return (field.label || '').toLowerCase().includes(q)
|
||||
|| (field.key || '').toLowerCase().includes(q)
|
||||
|| (field.help || '').toLowerCase().includes(q)
|
||||
@@ -318,6 +352,10 @@ const filteredSchema = computed(() => {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.aksrt-form-renderer__tabbed {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.aksrt-form-renderer__search {
|
||||
min-width: 200px;
|
||||
max-width: 280px;
|
||||
|
||||
@@ -15,7 +15,7 @@ const SECTION_DEFAULT_CHILDREN = {
|
||||
'post-list': 'post-list-page',
|
||||
permissions: 'permissions-comment',
|
||||
users: 'users-auth',
|
||||
payment: 'payment-recharge',
|
||||
payment: 'payment-knowledge',
|
||||
extensions: 'extensions-ip',
|
||||
tools: 'tools-import-export',
|
||||
'theme-info': 'theme-info-dashboard',
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
import { renderFaIcon, schemaPage, customPage } from './helpers'
|
||||
import { renderFaIcon, schemaPage } from './helpers'
|
||||
|
||||
/**
|
||||
* 支付&付费 — 子菜单按参考主题划分:
|
||||
* 知识付费 / 余额充值 / VIP会员 / 创作分成 / 推广返佣 / 收款接口 / 提现策略。
|
||||
* 收款接口为 schema 表单页(双下拉式接口选择 + 凭据配置 + 前缀配置)。
|
||||
*/
|
||||
export const paymentSection = {
|
||||
key: 'payment',
|
||||
label: '支付&付费',
|
||||
icon: renderFaIcon('fa fas fa-credit-card'),
|
||||
route: '/payment',
|
||||
children: [
|
||||
schemaPage({
|
||||
key: 'payment-knowledge',
|
||||
name: 'PaymentKnowledge',
|
||||
label: '知识付费',
|
||||
route: '/payment/knowledge',
|
||||
status: 'available',
|
||||
title: '知识付费',
|
||||
desc: '免登陆购买、模块显示位置、商品优惠码、客户服务、付费下载与商品预置参数。',
|
||||
canonicalDomain: 'payment',
|
||||
section: 'knowledge',
|
||||
}),
|
||||
schemaPage({
|
||||
key: 'payment-recharge',
|
||||
name: 'PaymentRecharge',
|
||||
@@ -13,7 +29,7 @@ export const paymentSection = {
|
||||
route: '/payment/recharge',
|
||||
status: 'available',
|
||||
title: '余额充值',
|
||||
desc: '余额充值策略、记录和手动确认配置。',
|
||||
desc: '余额功能、充值商品选项、卡密充值、充值卡与余额兑换积分。',
|
||||
canonicalDomain: 'payment',
|
||||
section: 'recharge',
|
||||
}),
|
||||
@@ -24,41 +40,18 @@ export const paymentSection = {
|
||||
route: '/payment/vip',
|
||||
status: 'available',
|
||||
title: 'VIP会员',
|
||||
desc: '会员套餐、会员特权和免扣策略。',
|
||||
desc: '会员等级、会员权益、购买选项、卡密兑换与积分兑换。',
|
||||
canonicalDomain: 'payment',
|
||||
section: 'vip',
|
||||
}),
|
||||
customPage({
|
||||
key: 'payment-gateway',
|
||||
name: 'PaymentGateway',
|
||||
label: '收款接口',
|
||||
route: '/payment/gateway',
|
||||
component: 'PaymentGateway',
|
||||
status: 'available',
|
||||
title: '收款接口',
|
||||
desc: '支付网关适配器、测试网关和手动确认配置。',
|
||||
canonicalDomain: 'payment',
|
||||
section: 'gateway',
|
||||
}),
|
||||
schemaPage({
|
||||
key: 'payment-order',
|
||||
name: 'PaymentOrder',
|
||||
label: '订单设置',
|
||||
route: '/payment/order',
|
||||
status: 'available',
|
||||
title: '订单设置',
|
||||
desc: '订单前缀、有效期、展示字段和状态文案。',
|
||||
canonicalDomain: 'payment',
|
||||
section: 'order',
|
||||
}),
|
||||
schemaPage({
|
||||
key: 'payment-revenue',
|
||||
name: 'PaymentRevenue',
|
||||
label: '创作中心',
|
||||
label: '创作分成',
|
||||
route: '/payment/revenue',
|
||||
status: 'available',
|
||||
title: '创作中心',
|
||||
desc: '独立配置现金与积分分成比例、结算周期和结算备注文案。',
|
||||
title: '创作分成',
|
||||
desc: '现金与积分分成比例、结算周期、分成说明与详情页(分成工单在支付中心)。',
|
||||
canonicalDomain: 'payment',
|
||||
section: 'revenue',
|
||||
}),
|
||||
@@ -69,20 +62,20 @@ export const paymentSection = {
|
||||
route: '/payment/affiliate',
|
||||
status: 'available',
|
||||
title: '推广返佣',
|
||||
desc: '返佣规则、邀请链路和结算策略(返佣工单在支付中心)。',
|
||||
desc: '推广识别模式、分档返佣规则、文案与海报(返佣工单在支付中心)。',
|
||||
canonicalDomain: 'payment',
|
||||
section: 'affiliate',
|
||||
}),
|
||||
schemaPage({
|
||||
key: 'payment-card-pass',
|
||||
name: 'PaymentCardPass',
|
||||
label: '卡密设置',
|
||||
route: '/payment/card-pass',
|
||||
key: 'payment-gateway',
|
||||
name: 'PaymentGateway',
|
||||
label: '收款接口',
|
||||
route: '/payment/gateway',
|
||||
status: 'available',
|
||||
title: '卡密设置',
|
||||
desc: '充值卡类型、卡号规则与兑换文案(卡密工单在支付中心)。',
|
||||
title: '收款接口',
|
||||
desc: '微信/支付宝接口选择、PayPal/Stripe 开关、货币结算、显示顺序与订单编号前缀。',
|
||||
canonicalDomain: 'payment',
|
||||
section: 'card-pass',
|
||||
section: 'gateway',
|
||||
}),
|
||||
schemaPage({
|
||||
key: 'payment-withdrawal',
|
||||
@@ -91,7 +84,7 @@ export const paymentSection = {
|
||||
route: '/payment/withdrawal',
|
||||
status: 'available',
|
||||
title: '提现策略',
|
||||
desc: '最低提现、手续费与说明文案(提现工单在支付中心)。',
|
||||
desc: '提现门槛、手续费、每日限制、收款渠道与提现协议(提现工单在支付中心)。',
|
||||
canonicalDomain: 'payment',
|
||||
section: 'withdrawal',
|
||||
}),
|
||||
|
||||
@@ -1,529 +0,0 @@
|
||||
<template>
|
||||
<div class="aksrt-page-container">
|
||||
<PageHeader title="充值卡管理" desc="按状态和时间筛选充值卡,支持单张创建、批量生成、详情查看和当前筛选导出。" />
|
||||
|
||||
<div class="aksrt-charge-card-page__stats">
|
||||
<n-card v-for="card in statCards" :key="card.key" size="small">
|
||||
<n-statistic :label="card.label" :value="stats[card.key] || 0" />
|
||||
</n-card>
|
||||
</div>
|
||||
|
||||
<n-card size="small" title="操作台">
|
||||
<n-space justify="space-between" wrap>
|
||||
<n-space>
|
||||
<n-button type="primary" @click="createVisible = true">创建充值卡</n-button>
|
||||
<n-button @click="batchVisible = true">批量生成</n-button>
|
||||
</n-space>
|
||||
<n-button quaternary @click="handleExport">导出当前筛选</n-button>
|
||||
</n-space>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-charge-card-page__card" size="small" title="充值卡台账">
|
||||
<n-space vertical :size="12">
|
||||
<n-grid cols="1 s:2 m:4" :x-gap="12" :y-gap="12" responsive="screen">
|
||||
<n-grid-item>
|
||||
<n-input v-model:value="filters.keyword" clearable placeholder="搜索卡号、备注、使用人" />
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<n-select v-model:value="filters.status" :options="statusOptions" placeholder="状态" />
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<n-date-picker v-model:value="filters.dateRange" type="daterange" clearable style="width: 100%;" />
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<n-space>
|
||||
<n-button type="primary" @click="handleSearch">筛选</n-button>
|
||||
<n-button @click="resetFilters">重置</n-button>
|
||||
</n-space>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
|
||||
<n-data-table
|
||||
:columns="columns"
|
||||
:data="rows"
|
||||
:loading="loading"
|
||||
:bordered="false"
|
||||
:pagination="pagination"
|
||||
:row-key="(row) => row.id"
|
||||
/>
|
||||
</n-space>
|
||||
</n-card>
|
||||
|
||||
<n-modal v-model:show="createVisible" preset="card" title="创建充值卡" style="max-width: 520px;">
|
||||
<n-form :model="createForm" label-placement="top">
|
||||
<n-form-item label="卡类型">
|
||||
<n-select v-model:value="createForm.type" :options="typeOptions" />
|
||||
</n-form-item>
|
||||
<n-form-item label="卡密">
|
||||
<n-input v-model:value="createForm.password" placeholder="手动填写唯一卡密" />
|
||||
</n-form-item>
|
||||
<n-form-item v-if="createForm.type === 'balance'" label="面额">
|
||||
<n-input v-model:value="createForm.amount" inputmode="decimal" placeholder="例如 10.00" />
|
||||
</n-form-item>
|
||||
<n-grid v-if="createForm.type === 'vip'" cols="2" :x-gap="12">
|
||||
<n-grid-item>
|
||||
<n-form-item label="VIP 等级">
|
||||
<n-select v-model:value="createForm.level" :options="levelOptions" />
|
||||
</n-form-item>
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<n-form-item label="天数(0=永久)">
|
||||
<n-input-number v-model:value="createForm.days" :min="0" :max="3650" style="width: 100%;" />
|
||||
</n-form-item>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
<n-form-item v-if="createForm.type === 'credits'" label="积分">
|
||||
<n-input-number v-model:value="createForm.points" :min="1" style="width: 100%;" />
|
||||
</n-form-item>
|
||||
<n-form-item label="过期时间">
|
||||
<n-date-picker v-model:value="createForm.expiresAt" type="datetime" clearable style="width: 100%;" />
|
||||
</n-form-item>
|
||||
<n-form-item label="备注">
|
||||
<n-input v-model:value="createForm.note" type="textarea" :rows="3" />
|
||||
</n-form-item>
|
||||
<n-space justify="end">
|
||||
<n-button @click="createVisible = false">取消</n-button>
|
||||
<n-button type="primary" :loading="submittingCreate" @click="handleCreate">创建</n-button>
|
||||
</n-space>
|
||||
</n-form>
|
||||
</n-modal>
|
||||
|
||||
<n-modal v-model:show="batchVisible" preset="card" title="批量生成充值卡" style="max-width: 520px;">
|
||||
<n-form :model="batchForm" label-placement="top">
|
||||
<n-form-item label="卡类型">
|
||||
<n-select v-model:value="batchForm.type" :options="typeOptions" />
|
||||
</n-form-item>
|
||||
<n-grid cols="2" :x-gap="12">
|
||||
<n-grid-item>
|
||||
<n-form-item label="数量">
|
||||
<n-input-number v-model:value="batchForm.count" :min="1" :max="100" style="width: 100%;" />
|
||||
</n-form-item>
|
||||
</n-grid-item>
|
||||
<n-grid-item v-if="batchForm.type === 'balance'">
|
||||
<n-form-item label="面额">
|
||||
<n-input v-model:value="batchForm.amount" inputmode="decimal" placeholder="例如 10.00" />
|
||||
</n-form-item>
|
||||
</n-grid-item>
|
||||
<n-grid-item v-if="batchForm.type === 'credits'">
|
||||
<n-form-item label="积分">
|
||||
<n-input-number v-model:value="batchForm.points" :min="1" style="width: 100%;" />
|
||||
</n-form-item>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
<n-grid v-if="batchForm.type === 'vip'" cols="2" :x-gap="12">
|
||||
<n-grid-item>
|
||||
<n-form-item label="VIP 等级">
|
||||
<n-select v-model:value="batchForm.level" :options="levelOptions" />
|
||||
</n-form-item>
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<n-form-item label="天数(0=永久)">
|
||||
<n-input-number v-model:value="batchForm.days" :min="0" :max="3650" style="width: 100%;" />
|
||||
</n-form-item>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
<n-grid cols="2" :x-gap="12">
|
||||
<n-grid-item>
|
||||
<n-form-item label="前缀">
|
||||
<n-input v-model:value="batchForm.prefix" placeholder="AK" />
|
||||
</n-form-item>
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<n-form-item label="随机长度">
|
||||
<n-input-number v-model:value="batchForm.length" :min="8" :max="32" style="width: 100%;" />
|
||||
</n-form-item>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
<n-form-item label="过期时间">
|
||||
<n-date-picker v-model:value="batchForm.expiresAt" type="datetime" clearable style="width: 100%;" />
|
||||
</n-form-item>
|
||||
<n-form-item label="备注">
|
||||
<n-input v-model:value="batchForm.note" type="textarea" :rows="3" />
|
||||
</n-form-item>
|
||||
<n-space justify="end">
|
||||
<n-button @click="batchVisible = false">取消</n-button>
|
||||
<n-button type="primary" :loading="submittingBatch" @click="handleBatchCreate">批量生成</n-button>
|
||||
</n-space>
|
||||
</n-form>
|
||||
</n-modal>
|
||||
|
||||
<ChargeCardDetailDrawer v-model:show="drawerVisible" :card-id="currentCardId" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, h, onMounted, reactive, ref } from 'vue'
|
||||
import { NButton, NSpace, NTag, useDialog, useMessage } from 'naive-ui'
|
||||
import api from '@/api'
|
||||
import PageHeader from '@/components/PageHeader.vue'
|
||||
import ChargeCardDetailDrawer from './components/ChargeCardDetailDrawer.vue'
|
||||
|
||||
const message = useMessage()
|
||||
const dialog = useDialog()
|
||||
const loading = ref(false)
|
||||
const rows = ref([])
|
||||
const createVisible = ref(false)
|
||||
const batchVisible = ref(false)
|
||||
const drawerVisible = ref(false)
|
||||
const currentCardId = ref(null)
|
||||
const submittingCreate = ref(false)
|
||||
const submittingBatch = ref(false)
|
||||
|
||||
const stats = reactive({
|
||||
total: 0,
|
||||
active: 0,
|
||||
used: 0,
|
||||
total_amount: 0,
|
||||
used_amount: 0,
|
||||
})
|
||||
|
||||
const filters = reactive({
|
||||
keyword: '',
|
||||
status: '',
|
||||
dateRange: null,
|
||||
})
|
||||
|
||||
const createForm = reactive({
|
||||
type: 'balance',
|
||||
password: '',
|
||||
amount: '10.00',
|
||||
level: 1,
|
||||
days: 30,
|
||||
points: 100,
|
||||
expiresAt: null,
|
||||
note: '',
|
||||
})
|
||||
|
||||
const batchForm = reactive({
|
||||
type: 'balance',
|
||||
count: 20,
|
||||
amount: '10.00',
|
||||
level: 1,
|
||||
days: 30,
|
||||
points: 100,
|
||||
prefix: 'AK',
|
||||
length: 12,
|
||||
expiresAt: null,
|
||||
note: '',
|
||||
})
|
||||
|
||||
const typeOptions = [
|
||||
{ label: '余额卡', value: 'balance' },
|
||||
{ label: '会员卡', value: 'vip' },
|
||||
{ label: '积分卡', value: 'credits' },
|
||||
]
|
||||
|
||||
const levelOptions = [
|
||||
{ label: 'VIP1', value: 1 },
|
||||
{ label: 'VIP2', value: 2 },
|
||||
]
|
||||
|
||||
const pager = reactive({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
itemCount: 0,
|
||||
})
|
||||
|
||||
const optionsState = reactive({
|
||||
statuses: [],
|
||||
})
|
||||
|
||||
const statCards = [
|
||||
{ key: 'total', label: '总卡数' },
|
||||
{ key: 'active', label: '未使用' },
|
||||
{ key: 'used', label: '已使用' },
|
||||
{ key: 'total_amount', label: '总面额' },
|
||||
{ key: 'used_amount', label: '已兑换金额' },
|
||||
]
|
||||
|
||||
const statusOptions = computed(() => optionsState.statuses.length
|
||||
? optionsState.statuses
|
||||
: [
|
||||
{ label: '全部状态', value: '' },
|
||||
{ label: '未使用', value: 'active' },
|
||||
{ label: '已使用', value: 'used' },
|
||||
{ label: '已过期', value: 'expired' },
|
||||
{ label: '已禁用', value: 'disabled' },
|
||||
])
|
||||
|
||||
const pagination = computed(() => ({
|
||||
page: pager.page,
|
||||
pageSize: pager.pageSize,
|
||||
itemCount: pager.itemCount,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
onUpdatePage: (page) => {
|
||||
pager.page = page
|
||||
loadData()
|
||||
},
|
||||
onUpdatePageSize: (pageSize) => {
|
||||
pager.pageSize = pageSize
|
||||
pager.page = 1
|
||||
loadData()
|
||||
},
|
||||
}))
|
||||
|
||||
const columns = [
|
||||
{ title: 'ID', key: 'id', width: 70 },
|
||||
{ title: '卡密', key: 'password', minWidth: 170, ellipsis: { tooltip: true } },
|
||||
{
|
||||
title: '类型',
|
||||
key: 'type_label',
|
||||
width: 90,
|
||||
render: (row) => row.type_label || row.type || '-',
|
||||
},
|
||||
{
|
||||
title: '面额/权益',
|
||||
key: 'amount',
|
||||
width: 120,
|
||||
render: (row) => {
|
||||
if (row.type === 'vip') {
|
||||
const days = Number(row.days || 0)
|
||||
return `VIP${row.level || 1} / ${days > 0 ? `${days}天` : '永久'}`
|
||||
}
|
||||
if (row.type === 'credits') return `${row.points || row.amount || 0} 积分`
|
||||
return row.amount
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status_label',
|
||||
width: 100,
|
||||
render: (row) => {
|
||||
const typeMap = { active: 'success', used: 'warning', expired: 'default', disabled: 'error' }
|
||||
return h(NTag, { size: 'small', round: true, type: typeMap[row.status] || 'default' }, () => row.status_label)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '使用人',
|
||||
key: 'used_by_user',
|
||||
width: 160,
|
||||
render: (row) => row.used_by_user?.display_name || '-',
|
||||
},
|
||||
{ title: '创建时间', key: 'created_at', width: 170 },
|
||||
{
|
||||
title: '过期时间',
|
||||
key: 'expires_at',
|
||||
width: 170,
|
||||
render: (row) => row.expires_at || '-',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
key: 'note',
|
||||
minWidth: 160,
|
||||
ellipsis: { tooltip: true },
|
||||
render: (row) => row.note || '-',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 140,
|
||||
render: (row) => h(NSpace, { size: 'small' }, () => {
|
||||
const actions = [
|
||||
h(NButton, {
|
||||
size: 'tiny',
|
||||
type: 'primary',
|
||||
quaternary: true,
|
||||
onClick: () => openDrawer(row.id),
|
||||
}, () => '详情'),
|
||||
]
|
||||
|
||||
if (row.status === 'active') {
|
||||
actions.push(h(NButton, {
|
||||
size: 'tiny',
|
||||
type: 'warning',
|
||||
quaternary: true,
|
||||
onClick: () => handleDisable(row),
|
||||
}, () => '禁用'))
|
||||
}
|
||||
|
||||
return actions
|
||||
}),
|
||||
},
|
||||
]
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
|
||||
function formatDateValue(value) {
|
||||
const date = new Date(value)
|
||||
const year = date.getFullYear()
|
||||
const month = `${date.getMonth() + 1}`.padStart(2, '0')
|
||||
const day = `${date.getDate()}`.padStart(2, '0')
|
||||
const hours = `${date.getHours()}`.padStart(2, '0')
|
||||
const minutes = `${date.getMinutes()}`.padStart(2, '0')
|
||||
const seconds = `${date.getSeconds()}`.padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
}
|
||||
|
||||
function buildParams(page = pager.page, pageSize = pager.pageSize) {
|
||||
const params = {
|
||||
page,
|
||||
per_page: pageSize,
|
||||
}
|
||||
if (filters.keyword) params.keyword = filters.keyword
|
||||
if (filters.status) params.status = filters.status
|
||||
if (Array.isArray(filters.dateRange) && filters.dateRange.length === 2) {
|
||||
const [start, end] = filters.dateRange
|
||||
params.date_from = formatDateValue(start).slice(0, 10)
|
||||
params.date_to = formatDateValue(end).slice(0, 10)
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await api.request('/payment/charge-cards', { params: buildParams() })
|
||||
const data = res?.data || {}
|
||||
rows.value = data.items || []
|
||||
pager.itemCount = data.total || 0
|
||||
Object.assign(stats, data.stats || {})
|
||||
optionsState.statuses = data.options?.statuses || []
|
||||
} catch (error) {
|
||||
message.error(error?.message || '加载充值卡台账失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
pager.page = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
function resetFilters() {
|
||||
filters.keyword = ''
|
||||
filters.status = ''
|
||||
filters.dateRange = null
|
||||
pager.page = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
async function handleCreate() {
|
||||
submittingCreate.value = true
|
||||
try {
|
||||
await api.request('/payment/charge-cards', {
|
||||
method: 'POST',
|
||||
data: {
|
||||
type: createForm.type,
|
||||
password: createForm.password,
|
||||
amount: String(createForm.amount ?? ''),
|
||||
level: createForm.level,
|
||||
days: createForm.days,
|
||||
points: createForm.points,
|
||||
expires_at: createForm.expiresAt ? formatDateValue(createForm.expiresAt) : '',
|
||||
note: createForm.note,
|
||||
},
|
||||
})
|
||||
message.success('充值卡已创建')
|
||||
createVisible.value = false
|
||||
createForm.password = ''
|
||||
createForm.note = ''
|
||||
await loadData()
|
||||
} catch (error) {
|
||||
message.error(error?.message || '创建失败')
|
||||
} finally {
|
||||
submittingCreate.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleBatchCreate() {
|
||||
submittingBatch.value = true
|
||||
try {
|
||||
await api.request('/payment/charge-cards/batch', {
|
||||
method: 'POST',
|
||||
data: {
|
||||
type: batchForm.type,
|
||||
count: batchForm.count,
|
||||
amount: String(batchForm.amount ?? ''),
|
||||
level: batchForm.level,
|
||||
days: batchForm.days,
|
||||
points: batchForm.points,
|
||||
prefix: batchForm.prefix,
|
||||
length: batchForm.length,
|
||||
expires_at: batchForm.expiresAt ? formatDateValue(batchForm.expiresAt) : '',
|
||||
note: batchForm.note,
|
||||
},
|
||||
})
|
||||
message.success('充值卡已批量生成')
|
||||
batchVisible.value = false
|
||||
await loadData()
|
||||
} catch (error) {
|
||||
message.error(error?.message || '批量生成失败')
|
||||
} finally {
|
||||
submittingBatch.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleDisable(row) {
|
||||
dialog.warning({
|
||||
title: '确认禁用',
|
||||
content: `确定要禁用充值卡 ${row.password} 吗?`,
|
||||
positiveText: '禁用',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
try {
|
||||
await api.request(`/payment/charge-cards/${row.id}/disable`, { method: 'POST' })
|
||||
message.success('充值卡已禁用')
|
||||
await loadData()
|
||||
} catch (error) {
|
||||
message.error(error?.message || '禁用失败')
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function openDrawer(id) {
|
||||
currentCardId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
async function handleExport() {
|
||||
try {
|
||||
const res = await api.request('/payment/charge-cards', { params: buildParams(1, 200) })
|
||||
const items = res?.data?.items || []
|
||||
const header = ['ID', '卡号', '面额', '余额', '状态', '使用人', '创建时间', '过期时间', '备注']
|
||||
const lines = items.map((row) => [
|
||||
row.id,
|
||||
row.password,
|
||||
row.amount,
|
||||
row.balance,
|
||||
row.status_label,
|
||||
row.used_by_user?.display_name || '',
|
||||
row.created_at || '',
|
||||
row.expires_at || '',
|
||||
row.note || '',
|
||||
])
|
||||
downloadCsv('charge-cards.csv', [header, ...lines])
|
||||
message.success('导出完成')
|
||||
} catch (error) {
|
||||
message.error(error?.message || '导出失败')
|
||||
}
|
||||
}
|
||||
|
||||
function downloadCsv(filename, rows) {
|
||||
const csv = `\uFEFF${rows.map((row) => row.map((cell) => `"${String(cell ?? '').replaceAll('"', '""')}"`).join(',')).join('\n')}`
|
||||
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = filename
|
||||
link.click()
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.aksrt-charge-card-page__stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.aksrt-charge-card-page__card {
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,131 +0,0 @@
|
||||
<template>
|
||||
<PaymentSectionPage
|
||||
section="gateway"
|
||||
title="收款接口"
|
||||
desc="管理余额、支付宝、微信、PayPal、Stripe、易支付与卡密兑换通道。"
|
||||
note="以渠道开关与凭据是否齐全为准;异步回调由各网关验签。卡密为兑换入口,不进入收银台。"
|
||||
:links="[
|
||||
{ label: '余额充值', path: '/payment/recharge' },
|
||||
{ label: '充值卡', path: '/payment/card-pass' },
|
||||
]"
|
||||
>
|
||||
<template #before-form>
|
||||
<n-spin :show="loading">
|
||||
<n-card class="aksrt-section-card" title="网关状态">
|
||||
<n-grid :cols="3" :x-gap="12" :y-gap="12">
|
||||
<n-grid-item v-for="gw in gateways" :key="gw.id">
|
||||
<div class="aksrt-gateway__card">
|
||||
<div class="aksrt-gateway__icon" :class="gw.configured ? 'aksrt-gateway__icon--active' : ''">
|
||||
<i :class="iconFor(gw.id)"></i>
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ gw.name }}</strong>
|
||||
<p>{{ gw.configured ? '已配置' : '未配置' }}</p>
|
||||
</div>
|
||||
<n-tag :type="gw.configured ? 'success' : 'default'" size="small" round>
|
||||
{{ gw.configured ? '可用' : '不可用' }}
|
||||
</n-tag>
|
||||
</div>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
<n-empty v-if="!loading && gateways.length === 0" description="暂无已注册的支付网关" />
|
||||
</n-card>
|
||||
|
||||
<n-card v-if="overview.help_text" class="aksrt-section-card" title="运行说明">
|
||||
<p class="aksrt-gateway__help">{{ overview.help_text }}</p>
|
||||
</n-card>
|
||||
</n-spin>
|
||||
</template>
|
||||
</PaymentSectionPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import PaymentSectionPage from '@/pages/payment/PaymentSectionPage.vue'
|
||||
import api from '@/api'
|
||||
|
||||
const message = useMessage()
|
||||
const gateways = ref([])
|
||||
const overview = ref({})
|
||||
const loading = ref(false)
|
||||
|
||||
function iconFor(id) {
|
||||
if (id === 'balance') return 'fa fas fa-credit-card'
|
||||
if (id === 'alipay' || id === 'epay_alipay') return 'fa fas fa-credit-card'
|
||||
if (id === 'wechatpay' || id === 'epay_wxpay') return 'fa fab fa-weixin'
|
||||
if (id === 'paypal') return 'fa fab fa-paypal'
|
||||
if (id === 'stripe') return 'fa fab fa-cc-stripe'
|
||||
return 'fa fas fa-credit-card'
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const [gwRes, ovRes] = await Promise.all([
|
||||
api.request('/payment/overview').catch(() => null),
|
||||
])
|
||||
if (gwRes?.data?.gateway?.gateways) {
|
||||
const gwList = Object.entries(gwRes.data.gateway.gateways).map(([id, enabled]) => ({ id, name: id, configured: !!enabled }))
|
||||
gateways.value = gwList
|
||||
}
|
||||
if (ovRes?.data?.gateway) {
|
||||
overview.value = ovRes.data.gateway
|
||||
} else if (ovRes?.data) {
|
||||
overview.value = ovRes.data
|
||||
}
|
||||
} catch {
|
||||
message.warning('部分支付接口暂不可用')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.aksrt-gateway__card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 14px;
|
||||
border-radius: 8px;
|
||||
background: var(--surface-2, #fafafa);
|
||||
border: 1px solid var(--color-line, #f0f0f0);
|
||||
}
|
||||
|
||||
.aksrt-gateway__icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--surface-3, #f0f0f0);
|
||||
color: var(--aksrt-color-muted);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.aksrt-gateway__icon--active {
|
||||
background: var(--color-primary-soft, #f0f5ff);
|
||||
color: var(--aksrt-color-primary);
|
||||
}
|
||||
|
||||
.aksrt-gateway__card strong {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: var(--aksrt-color-text);
|
||||
}
|
||||
|
||||
.aksrt-gateway__card p {
|
||||
margin: 2px 0 0;
|
||||
font-size: 12px;
|
||||
color: var(--aksrt-color-muted);
|
||||
}
|
||||
|
||||
.aksrt-gateway__help {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: var(--color-ink-3, #5c6370);
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
@@ -1,319 +0,0 @@
|
||||
<template>
|
||||
<PaymentSectionPage
|
||||
section="revenue"
|
||||
title="创作中心"
|
||||
desc="查看现金与积分收益统计及结算运营入口。"
|
||||
:links="[
|
||||
{ label: 'VIP 会员', path: '/payment/vip' },
|
||||
{ label: '推广返佣', path: '/payment/affiliate' },
|
||||
{ label: '创作分成运营', path: '/payment/income' },
|
||||
]"
|
||||
>
|
||||
<template #before-form>
|
||||
<n-card class="aksrt-section-card" title="收益统计">
|
||||
<n-grid :cols="4" :x-gap="12" :y-gap="12">
|
||||
<n-grid-item v-for="item in revenueSummary" :key="item.key">
|
||||
<div class="aksrt-revenue__stat">
|
||||
<strong>{{ item.value }}</strong>
|
||||
<p>{{ item.label }}</p>
|
||||
</div>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card" title="收益趋势">
|
||||
<template #header-extra>
|
||||
<n-space size="small">
|
||||
<n-button :type="granularity === 'day' ? 'primary' : 'default'" size="tiny" quaternary @click="setGranularity('day')">日</n-button>
|
||||
<n-button :type="granularity === 'week' ? 'primary' : 'default'" size="tiny" quaternary @click="setGranularity('week')">周</n-button>
|
||||
<n-button :type="granularity === 'month' ? 'primary' : 'default'" size="tiny" quaternary @click="setGranularity('month')">月</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<div v-if="hasTimeSeries" class="aksrt-revenue__charts">
|
||||
<div v-for="group in timeSeriesGroups" :key="group.key" class="aksrt-revenue__chart">
|
||||
<h4>{{ group.label }}</h4>
|
||||
<div v-if="group.items.length > 0" class="aksrt-revenue__chart-bars">
|
||||
<div
|
||||
v-for="(item, index) in group.items"
|
||||
:key="`${group.key}-${index}`"
|
||||
class="aksrt-revenue__chart-bar-wrap"
|
||||
:title="`${item.date}: ${formatAmount(item.amount, group.key)} (${item.count}笔)`"
|
||||
>
|
||||
<div
|
||||
class="aksrt-revenue__chart-bar"
|
||||
:style="{ height: getBarHeight(item.amount, group.items) + '%' }"
|
||||
/>
|
||||
<span class="aksrt-revenue__chart-label">{{ formatLabel(item.date) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<n-empty v-else size="small" description="暂无数据" />
|
||||
<div class="aksrt-revenue__chart-summary">
|
||||
<span>期间总计: <strong>{{ formatAmount(group.total, group.key) }}</strong></span>
|
||||
<span>共 {{ group.count }} 笔</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<n-empty v-else description="暂无趋势数据" />
|
||||
</n-card>
|
||||
|
||||
<n-card class="aksrt-section-card" title="提现统计">
|
||||
<n-grid :cols="6" :x-gap="12" :y-gap="12">
|
||||
<n-grid-item>
|
||||
<div class="aksrt-revenue__stat">
|
||||
<strong>{{ withdrawalStats.total }}</strong>
|
||||
<p>提现笔数</p>
|
||||
</div>
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<div class="aksrt-revenue__stat">
|
||||
<strong>{{ withdrawalStats.pending }}</strong>
|
||||
<p>待处理</p>
|
||||
</div>
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<div class="aksrt-revenue__stat">
|
||||
<strong>{{ withdrawalStats.processing }}</strong>
|
||||
<p>处理中</p>
|
||||
</div>
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<div class="aksrt-revenue__stat">
|
||||
<strong>{{ withdrawalStats.completed }}</strong>
|
||||
<p>已完成</p>
|
||||
</div>
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<div class="aksrt-revenue__stat">
|
||||
<strong>{{ withdrawalStats.rejected }}</strong>
|
||||
<p>已拒绝</p>
|
||||
</div>
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<div class="aksrt-revenue__stat">
|
||||
<strong>{{ formatAmount(withdrawalStats.total_paid, 'cash') }}</strong>
|
||||
<p>已提现 (元)</p>
|
||||
</div>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
</template>
|
||||
</PaymentSectionPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import PaymentSectionPage from '@/pages/payment/PaymentSectionPage.vue'
|
||||
import api from '@/api'
|
||||
|
||||
const emptyCurrencyStats = (currency) => ({
|
||||
total: currency === 'cash' ? '0.00' : 0,
|
||||
pending: currency === 'cash' ? '0.00' : 0,
|
||||
settled: currency === 'cash' ? '0.00' : 0,
|
||||
count: 0,
|
||||
})
|
||||
const revenueStats = ref({ cash: emptyCurrencyStats('cash'), points: emptyCurrencyStats('points') })
|
||||
const withdrawalStats = ref({ total: 0, pending: 0, processing: 0, completed: 0, rejected: 0, total_paid: '0.00' })
|
||||
const timeSeries = ref({ cash: [], points: [] })
|
||||
const granularity = ref('day')
|
||||
|
||||
const revenueSummary = computed(() => [
|
||||
{ key: 'cash-total', value: `${formatAmount(revenueStats.value.cash.total, 'cash')} 元`, label: '现金总收益' },
|
||||
{ key: 'cash-pending', value: `${formatAmount(revenueStats.value.cash.pending, 'cash')} 元`, label: '现金待结算' },
|
||||
{ key: 'cash-settled', value: `${formatAmount(revenueStats.value.cash.settled, 'cash')} 元`, label: '现金已结算' },
|
||||
{ key: 'cash-count', value: revenueStats.value.cash.count, label: '现金收益笔数' },
|
||||
{ key: 'points-total', value: `${formatAmount(revenueStats.value.points.total, 'points')}`, label: '积分总收益' },
|
||||
{ key: 'points-pending', value: `${formatAmount(revenueStats.value.points.pending, 'points')}`, label: '积分待结算' },
|
||||
{ key: 'points-settled', value: `${formatAmount(revenueStats.value.points.settled, 'points')}`, label: '积分已结算' },
|
||||
{ key: 'points-count', value: revenueStats.value.points.count, label: '积分收益笔数' },
|
||||
])
|
||||
|
||||
const timeSeriesGroups = computed(() => [
|
||||
{
|
||||
key: 'cash',
|
||||
label: '现金收益',
|
||||
items: Array.isArray(timeSeries.value.cash) ? timeSeries.value.cash : [],
|
||||
},
|
||||
{
|
||||
key: 'points',
|
||||
label: '积分收益',
|
||||
items: Array.isArray(timeSeries.value.points) ? timeSeries.value.points : [],
|
||||
},
|
||||
].map((group) => {
|
||||
const totalMinor = group.items.reduce((sum, item) => sum + amountMinor(item.amount, group.key), 0n)
|
||||
return {
|
||||
...group,
|
||||
total: formatMinor(totalMinor, group.key),
|
||||
count: group.items.reduce((sum, item) => sum + Number(item.count || 0), 0),
|
||||
}
|
||||
}))
|
||||
|
||||
const hasTimeSeries = computed(() => timeSeriesGroups.value.some((group) => group.items.length > 0))
|
||||
|
||||
function formatAmount(amount, currency) {
|
||||
return currency === 'points'
|
||||
? `${formatMinor(amountMinor(amount, currency), currency)} 积分`
|
||||
: `¥${formatMinor(amountMinor(amount, currency), currency)}`
|
||||
}
|
||||
|
||||
function amountMinor(amount, currency) {
|
||||
const raw = String(amount ?? '').trim()
|
||||
if (currency === 'points') return /^\d+$/.test(raw) ? BigInt(raw) : 0n
|
||||
const match = /^(0|[1-9]\d*)(?:\.(\d{1,2}))?$/.exec(raw)
|
||||
return match ? (BigInt(match[1]) * 100n) + BigInt((match[2] || '').padEnd(2, '0') || '0') : 0n
|
||||
}
|
||||
|
||||
function formatMinor(minor, currency) {
|
||||
if (currency === 'points') return String(minor)
|
||||
return `${minor / 100n}.${String(minor % 100n).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
function getBarHeight(amount, items) {
|
||||
const currency = items === timeSeries.value.points ? 'points' : 'cash'
|
||||
const maxAmount = items.reduce((max, item) => {
|
||||
const current = amountMinor(item.amount, currency)
|
||||
return current > max ? current : max
|
||||
}, 0n)
|
||||
if (maxAmount <= 0n) return 2
|
||||
const scaled = Number((amountMinor(amount, currency) * 10000n) / maxAmount) / 100
|
||||
return Math.max(2, scaled)
|
||||
}
|
||||
|
||||
function formatLabel(date) {
|
||||
return String(date || '').slice(5)
|
||||
}
|
||||
|
||||
function setGranularity(nextGranularity) {
|
||||
granularity.value = nextGranularity
|
||||
loadRevenue()
|
||||
}
|
||||
|
||||
async function loadRevenue() {
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
time_series: '1',
|
||||
granularity: granularity.value,
|
||||
})
|
||||
const res = await api.request(`/payment/revenue?${params}`)
|
||||
if (res?.data) {
|
||||
if (res.data.revenue) {
|
||||
revenueStats.value = {
|
||||
cash: { ...emptyCurrencyStats('cash'), ...(res.data.revenue.cash || {}) },
|
||||
points: { ...emptyCurrencyStats('points'), ...(res.data.revenue.points || {}) },
|
||||
}
|
||||
}
|
||||
if (res.data.withdrawal) {
|
||||
withdrawalStats.value = {
|
||||
total: Number(res.data.withdrawal.total || 0),
|
||||
pending: Number(res.data.withdrawal.pending || 0),
|
||||
processing: Number(res.data.withdrawal.processing || 0),
|
||||
completed: Number(res.data.withdrawal.completed || 0),
|
||||
rejected: Number(res.data.withdrawal.rejected || 0),
|
||||
total_paid: String(res.data.withdrawal.total_paid || '0.00'),
|
||||
}
|
||||
}
|
||||
timeSeries.value = {
|
||||
cash: res.data.time_series?.cash || [],
|
||||
points: res.data.time_series?.points || [],
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// silent
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadRevenue)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.aksrt-revenue__stat {
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
background: var(--surface-2, #fafafa);
|
||||
}
|
||||
|
||||
.aksrt-revenue__stat strong {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: var(--aksrt-color-text);
|
||||
}
|
||||
|
||||
.aksrt-revenue__stat p {
|
||||
margin: 4px 0 0;
|
||||
font-size: 12px;
|
||||
color: var(--aksrt-color-muted);
|
||||
}
|
||||
|
||||
.aksrt-revenue__charts {
|
||||
display: grid;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.aksrt-revenue__chart {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.aksrt-revenue__chart h4 {
|
||||
margin: 0 0 10px;
|
||||
color: var(--aksrt-color-text);
|
||||
}
|
||||
|
||||
.aksrt-revenue__chart-bars {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 2px;
|
||||
height: 180px;
|
||||
padding: 0 4px;
|
||||
border-bottom: 1px solid var(--color-line, #e5e7eb);
|
||||
}
|
||||
|
||||
.aksrt-revenue__chart-bar-wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.aksrt-revenue__chart-bar {
|
||||
width: 100%;
|
||||
max-width: 32px;
|
||||
min-height: 2px;
|
||||
background: var(--color-primary, #88abda);
|
||||
border-radius: 3px 3px 0 0;
|
||||
transition: height 0.3s ease;
|
||||
}
|
||||
|
||||
.aksrt-revenue__chart-bar-wrap:hover .aksrt-revenue__chart-bar {
|
||||
background: var(--color-primary-hover, #7099cc);
|
||||
}
|
||||
|
||||
.aksrt-revenue__chart-label {
|
||||
font-size: 10px;
|
||||
color: var(--aksrt-color-muted);
|
||||
margin-top: 4px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.aksrt-revenue__chart-summary {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 12px;
|
||||
font-size: 12px;
|
||||
color: var(--color-ink-3, #525866);
|
||||
}
|
||||
|
||||
.aksrt-revenue__chart-summary strong {
|
||||
color: var(--aksrt-color-text);
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -1,103 +0,0 @@
|
||||
<template>
|
||||
<div class="aksrt-page-container">
|
||||
<PageHeader :title="title" :desc="desc">
|
||||
<template #actions>
|
||||
<n-space v-if="links.length">
|
||||
<n-button v-for="link in links" :key="link.path" quaternary @click="go(link.path)">
|
||||
{{ link.label }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<n-card v-if="note" class="aksrt-section-card">
|
||||
<n-alert type="info" :show-icon="false">
|
||||
{{ note }}
|
||||
</n-alert>
|
||||
</n-card>
|
||||
|
||||
<slot name="before-form" />
|
||||
|
||||
<AksFormRenderer
|
||||
:schema="pageMeta.groups"
|
||||
:form-data="formData"
|
||||
:dirty="dirty"
|
||||
:loading="loading"
|
||||
:saving="saving"
|
||||
:load-error="loadError"
|
||||
:show-search="false"
|
||||
@update:form-data="formData = $event"
|
||||
@save="handleSave"
|
||||
@reload="handleReload"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import api from '@/api'
|
||||
import PageHeader from '@/components/PageHeader.vue'
|
||||
import AksFormRenderer from '@/components/form/AksFormRenderer.vue'
|
||||
import { useSectionForm } from '@/composables/useSectionForm'
|
||||
import { getSchema } from '@/schema'
|
||||
|
||||
const props = defineProps({
|
||||
section: { type: String, required: true },
|
||||
title: { type: String, required: true },
|
||||
desc: { type: String, default: '' },
|
||||
note: { type: String, default: '' },
|
||||
links: { type: Array, default: () => [] },
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
const message = useMessage()
|
||||
|
||||
const pageMeta = computed(() => getSchema('payment', props.section) || { groups: [] })
|
||||
const defaults = computed(() => {
|
||||
const result = {}
|
||||
;(pageMeta.value.groups || []).forEach((group) => {
|
||||
;(group.fields || []).forEach((field) => {
|
||||
if (field.default !== undefined) {
|
||||
result[field.key] = field.default
|
||||
}
|
||||
})
|
||||
})
|
||||
return result
|
||||
})
|
||||
|
||||
const {
|
||||
formData,
|
||||
loading,
|
||||
saving,
|
||||
loadError,
|
||||
dirty,
|
||||
save: doSave,
|
||||
reload,
|
||||
} = useSectionForm({
|
||||
fetch: () => api.getSchemaPage('payment', props.section),
|
||||
submit: (data) => api.saveSchemaPage('payment', props.section, data),
|
||||
defaults: defaults.value,
|
||||
onSaved: () => {
|
||||
message.success('设置已保存')
|
||||
},
|
||||
})
|
||||
|
||||
function go(path) {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
try {
|
||||
await doSave()
|
||||
} catch (error) {
|
||||
message.error(error.message || '保存失败')
|
||||
}
|
||||
}
|
||||
|
||||
function handleReload() {
|
||||
reload()
|
||||
message.info('已放弃更改')
|
||||
}
|
||||
</script>
|
||||
@@ -1,89 +0,0 @@
|
||||
<template>
|
||||
<n-drawer :show="show" :width="680" @update:show="$emit('update:show', $event)">
|
||||
<n-drawer-content title="充值卡详情" closable>
|
||||
<n-spin :show="loading">
|
||||
<template v-if="detail.card">
|
||||
<n-descriptions bordered :column="1" size="small" label-placement="left">
|
||||
<n-descriptions-item label="卡密">{{ detail.card.password || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="面额">{{ detail.card.amount || 0 }}</n-descriptions-item>
|
||||
<n-descriptions-item label="余额">{{ detail.card.balance || 0 }}</n-descriptions-item>
|
||||
<n-descriptions-item label="状态">{{ detail.card.status_label || detail.card.status }}</n-descriptions-item>
|
||||
<n-descriptions-item label="创建时间">{{ detail.card.created_at || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="过期时间">{{ detail.card.expires_at || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="使用时间">{{ detail.card.used_at || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="使用者">
|
||||
{{ detail.card.used_by_user?.display_name || '-' }}
|
||||
</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
|
||||
<div class="aksrt-charge-drawer__section">
|
||||
<div class="aksrt-charge-drawer__title">备注</div>
|
||||
<n-alert type="info" :show-icon="false">
|
||||
{{ detail.card.note || '暂无备注' }}
|
||||
</n-alert>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<n-empty v-else description="暂无充值卡详情" />
|
||||
</n-spin>
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import api from '@/api'
|
||||
|
||||
const props = defineProps({
|
||||
show: { type: Boolean, default: false },
|
||||
cardId: { type: [Number, String], default: null },
|
||||
})
|
||||
|
||||
defineEmits(['update:show'])
|
||||
|
||||
const message = useMessage()
|
||||
const loading = ref(false)
|
||||
const detail = ref({ card: null })
|
||||
|
||||
watch(
|
||||
() => [props.show, props.cardId],
|
||||
async ([visible, id]) => {
|
||||
if (visible && id) {
|
||||
await loadDetail()
|
||||
}
|
||||
if (!visible) {
|
||||
detail.value = { card: null }
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
async function loadDetail() {
|
||||
if (!props.cardId) {
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await api.request(`/payment/charge-cards/${props.cardId}`)
|
||||
detail.value = res?.data || { card: null }
|
||||
} catch (error) {
|
||||
message.error(error?.message || '加载充值卡详情失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.aksrt-charge-drawer__section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.aksrt-charge-drawer__title {
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
}
|
||||
</style>
|
||||
@@ -1,13 +1,11 @@
|
||||
import { corePageLoaders } from './core'
|
||||
import { usersPageLoaders } from './users'
|
||||
import { paymentPageLoaders } from './payment'
|
||||
import { toolsPageLoaders } from './tools'
|
||||
import { extensionsPageLoaders } from './extensions'
|
||||
|
||||
export const componentLoaders = {
|
||||
...corePageLoaders,
|
||||
...usersPageLoaders,
|
||||
...paymentPageLoaders,
|
||||
...toolsPageLoaders,
|
||||
...extensionsPageLoaders,
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export const paymentPageLoaders = {
|
||||
PaymentGateway: () => import('@/pages/payment/PaymentGateway.vue'),
|
||||
}
|
||||
@@ -72,12 +72,10 @@ function createSectionRedirectRoutes() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Old SPA ops routes → native WP admin pages (Phase 4 transition).
|
||||
* Old SPA ops routes → native WP admin pages.
|
||||
* Bookmarks under order-related paths land on NativeOpsRedirect.
|
||||
*/
|
||||
const NATIVE_OPS_REDIRECTS = [
|
||||
{ path: '/payment/charge-card', title: '充值卡', nativeAdminPath: 'admin.php?page=aksrt_charge_cards' },
|
||||
{ path: '/payment/income', title: '创作分成运营', nativeAdminPath: 'admin.php?page=aksrt_income' },
|
||||
{ path: '/users/messages', title: '消息中心', nativeAdminPath: 'users.php?page=aksrt_messages' },
|
||||
{ path: '/users/referral-codes', title: '推广码运营', nativeAdminPath: 'users.php?page=aksrt_referral_codes' },
|
||||
{ path: '/users/ban', title: '禁封管理', nativeAdminPath: 'users.php?page=aksrt_user_ban&tab=restrictions' },
|
||||
|
||||
@@ -1,38 +1,183 @@
|
||||
/**
|
||||
* 推广返佣 — 参考主题结构:开关、识别模式、自己推广自己、按用户类型分档的
|
||||
* 返佣规则(tab)、文案、推广海报与推广让利标签。
|
||||
*/
|
||||
export const affiliateSchema = {
|
||||
key: 'payment.affiliate',
|
||||
title: '推广返佣',
|
||||
icon: 'fa fas fa-share-alt',
|
||||
description: '配置推广归因、分级佣金比例和结算规则。',
|
||||
icon: 'fa fas fa-btc',
|
||||
description: '推广识别模式、分档返佣规则、返佣文案、推广海报与让利标签。',
|
||||
defaults: {
|
||||
affiliate_enabled: false,
|
||||
affiliate_ratio_user: 5,
|
||||
affiliate_ratio_vip_1: 5,
|
||||
affiliate_ratio_vip_2: 5,
|
||||
affiliate_order_types: ['membership', 'content'],
|
||||
pay_rebate_s: false,
|
||||
pay_rebate_judgment: 'all',
|
||||
pay_rebate_show_users: true,
|
||||
pay_rebate_self_s: true,
|
||||
pay_rebate_user_s: [],
|
||||
pay_rebate_ratio: 5,
|
||||
pay_rebate_user_s_1: ['all'],
|
||||
pay_rebate_ratio_vip_1: 10,
|
||||
pay_rebate_user_s_2: ['all'],
|
||||
pay_rebate_ratio_vip_2: 20,
|
||||
affiliate_settlement_days: 7,
|
||||
affiliate_promotion_text: '',
|
||||
pay_rebate_text_desc: '加入分享计划,获得高额奖励',
|
||||
pay_rebate_text_details_title: '返佣详解',
|
||||
pay_rebate_text_details: '<p>通过您的推广链接打开本站后,在本站购买商品即可获得佣金</p><p>通过您的推广链接注册后的用户今后购买的商品均可获得佣金</p><p>申请提现后,需后台人工处理,一般2-3小时,请耐心等待</p>',
|
||||
pay_rebate_poster_s: true,
|
||||
pay_rebate_poster_img: '',
|
||||
pay_rebate_poster_title: '发现一个精品网站,快来看一看',
|
||||
pay_rebate_poster_desc: '网站有超多精品资源和优惠福利,扫码进入网站还可享受诸多优惠哦',
|
||||
pay_rebate_text_discount: '推荐购买 下单再减%discount%',
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
key: 'policy',
|
||||
title: '返佣策略',
|
||||
icon: 'fa fas fa-sitemap',
|
||||
key: 'notice',
|
||||
title: '说明',
|
||||
icon: 'fa fas fa-info-circle',
|
||||
fields: [
|
||||
{ key: 'affiliate_enabled', label: '启用推广返佣', type: 'switch', default: false },
|
||||
{ key: 'affiliate_ratio_user', label: '普通会员佣金比例(%)', type: 'number', default: 5, min: 0, max: 100, dependsOn: { key: 'affiliate_enabled', value: true } },
|
||||
{ key: 'affiliate_ratio_vip_1', label: '一级会员佣金比例(%)', type: 'number', default: 5, min: 0, max: 100, dependsOn: { key: 'affiliate_enabled', value: true } },
|
||||
{ key: 'affiliate_ratio_vip_2', label: '二级会员佣金比例(%)', type: 'number', default: 5, min: 0, max: 100, dependsOn: { key: 'affiliate_enabled', value: true } },
|
||||
{ key: 'affiliate_order_types', label: '适用订单类型', type: 'checkbox', default: ['membership', 'content'], sanitize_type: 'array', options: [{ label: '会员订单', value: 'membership' }, { label: '付费内容', value: 'content' }, { label: '购买积分', value: 'credits' }], dependsOn: { key: 'affiliate_enabled', value: true } },
|
||||
{ key: 'affiliate_settlement_days', label: '结算观察期(天)', type: 'number', default: 7, min: 0, max: 90, dependsOn: { key: 'affiliate_enabled', value: true } },
|
||||
{
|
||||
key: 'affiliate_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
content: '<ul style="margin:0 0 0 18px;line-height:1.9"><li>识别模式「绑定注册」:用户只要是通过任意推广链接注册的,今后此用户消费均会给推荐人返佣</li><li>识别模式「仅推广链接」:只有通过推广链接购买的才会给推荐人返佣,不会考虑是否是推荐注册的</li><li>开启的订单类型如果一个都不勾选,则代表该用户类型不参与此功能</li><li>您可以根据网站的VIP设置相互配合设置相应的规则</li><li>下面的设置为全局设置,还可以单独为每一个用户设置规则和比例</li></ul>',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'switch',
|
||||
title: '推广返佣',
|
||||
icon: 'fa fas fa-toggle-on',
|
||||
fields: [
|
||||
{ key: 'pay_rebate_s', label: '启用推广返佣', type: 'switch', default: false },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'mode',
|
||||
title: '推广识别模式',
|
||||
icon: 'fa fas fa-link',
|
||||
fields: [
|
||||
{
|
||||
key: 'pay_rebate_judgment',
|
||||
label: '推广识别模式',
|
||||
type: 'radio',
|
||||
default: 'all',
|
||||
dependsOn: { key: 'pay_rebate_s', value: true },
|
||||
options: [
|
||||
{ label: '绑定注册', value: 'all' },
|
||||
{ label: '仅推广链接', value: 'link' },
|
||||
],
|
||||
},
|
||||
{ key: 'pay_rebate_show_users', label: '用户中心显示我推荐的用户', type: 'switch', default: true, dependsOn: { key: 'pay_rebate_judgment', value: 'all' } },
|
||||
{ key: 'pay_rebate_self_s', label: '自己推广自己', type: 'switch', default: true, dependsOn: { key: 'pay_rebate_s', value: true }, help: '允许使用自己的推广链接购买商品并获得佣金' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'rules',
|
||||
title: '返佣规则',
|
||||
icon: 'fa fas fa-table',
|
||||
fields: [
|
||||
{
|
||||
key: 'rebate_rule',
|
||||
label: '为不同用户类型设置返佣规则',
|
||||
type: 'tabbed',
|
||||
tabs: [
|
||||
{
|
||||
key: 'ordinary',
|
||||
title: '普通用户',
|
||||
fields: [
|
||||
{
|
||||
key: 'pay_rebate_user_s',
|
||||
label: '返佣订单',
|
||||
type: 'checkbox',
|
||||
default: [],
|
||||
dependsOn: { key: 'pay_rebate_s', value: true },
|
||||
options: rebateTypeOptions,
|
||||
help: '普通用户返利的订单类型,全部关闭,则代表普通用户不参与推广返佣',
|
||||
},
|
||||
{ key: 'pay_rebate_ratio', label: '普通用户返佣比例(%)', type: 'number', default: 5, min: 0, max: 100, dependsOn: { key: 'pay_rebate_s', value: true } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'vip1',
|
||||
title: '一级会员',
|
||||
fields: [
|
||||
{
|
||||
key: 'pay_rebate_user_s_1',
|
||||
label: '返佣订单',
|
||||
type: 'checkbox',
|
||||
default: ['all'],
|
||||
dependsOn: { key: 'pay_rebate_s', value: true },
|
||||
options: rebateTypeOptions,
|
||||
help: '一级会员返利的订单类型,全部关闭,则代表一级会员用户不参与推广返佣',
|
||||
},
|
||||
{ key: 'pay_rebate_ratio_vip_1', label: '一级会员返佣比例(%)', type: 'number', default: 10, min: 0, max: 100, dependsOn: { key: 'pay_rebate_s', value: true } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'vip2',
|
||||
title: '二级会员',
|
||||
fields: [
|
||||
{
|
||||
key: 'pay_rebate_user_s_2',
|
||||
label: '返佣订单',
|
||||
type: 'checkbox',
|
||||
default: ['all'],
|
||||
dependsOn: { key: 'pay_rebate_s', value: true },
|
||||
options: rebateTypeOptions,
|
||||
help: '二级会员返利的订单类型,全部关闭,则代表二级会员用户不参与推广返佣',
|
||||
},
|
||||
{ key: 'pay_rebate_ratio_vip_2', label: '二级会员返佣比例(%)', type: 'number', default: 20, min: 0, max: 100, dependsOn: { key: 'pay_rebate_s', value: true } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ key: 'affiliate_settlement_days', label: '结算观察期(天)', type: 'number', default: 7, min: 0, max: 90, dependsOn: { key: 'pay_rebate_s', value: true }, help: '订单完成后经过观察期,佣金才会结算到可提现余额' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'text',
|
||||
title: '文案设置',
|
||||
icon: 'fa fas fa-pen',
|
||||
title: '返佣文案',
|
||||
icon: 'fa fas fa-file-alt',
|
||||
fields: [
|
||||
{ key: 'affiliate_promotion_text', label: '推广说明', type: 'textarea', default: '', dependsOn: { key: 'affiliate_enabled', value: true } },
|
||||
{ key: 'pay_rebate_text_desc', label: '返佣文案(一句话简介)', type: 'input', default: '加入分享计划,获得高额奖励', dependsOn: { key: 'pay_rebate_s', value: true } },
|
||||
{ key: 'pay_rebate_text_details_title', label: '返佣详情标题', type: 'input', default: '返佣详解', dependsOn: { key: 'pay_rebate_s', value: true } },
|
||||
{ key: 'pay_rebate_text_details', label: '返佣详情内容', type: 'textarea', sanitize_type: 'html', default: '<p>通过您的推广链接打开本站后,在本站购买商品即可获得佣金</p><p>通过您的推广链接注册后的用户今后购买的商品均可获得佣金</p><p>申请提现后,需后台人工处理,一般2-3小时,请耐心等待</p>', rows: 6, dependsOn: { key: 'pay_rebate_s', value: true }, help: '返佣详情介绍,建议为规则介绍或者其他说明,支持HTML代码' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'poster',
|
||||
title: '推广海报',
|
||||
icon: 'fa fas fa-image',
|
||||
fields: [
|
||||
{ key: 'pay_rebate_poster_s', label: '推广海报', type: 'switch', default: true, dependsOn: { key: 'pay_rebate_s', value: true }, help: '在用户中心显示包含二维码的推广海报' },
|
||||
{ key: 'pay_rebate_poster_img', label: '推广海报顶部图片', type: 'media', default: '', dependsOn: { key: 'pay_rebate_poster_s', value: true } },
|
||||
{ key: 'pay_rebate_poster_title', label: '推广海报标题(20-30个字)', type: 'input', default: '发现一个精品网站,快来看一看', dependsOn: { key: 'pay_rebate_poster_s', value: true } },
|
||||
{ key: 'pay_rebate_poster_desc', label: '推广海报简介(40-60个字)', type: 'input', default: '网站有超多精品资源和优惠福利,扫码进入网站还可享受诸多优惠哦', dependsOn: { key: 'pay_rebate_poster_s', value: true } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'discount',
|
||||
title: '推广让利标签',
|
||||
icon: 'fa fas fa-tags',
|
||||
fields: [
|
||||
{
|
||||
key: 'discount_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
dependsOn: { key: 'pay_rebate_s', value: true },
|
||||
content: '<p><b>推广让利标签配置详解:</b></p><ul style="margin:6px 0 0 18px;line-height:1.9"><li>可使用的变量:折扣 <code>%discount%</code>、推荐人姓名 <code>%referrer_name%</code></li><li>使用变量示例:<code>%referrer_name%推荐购买 下单再减%discount%元</code></li></ul>',
|
||||
},
|
||||
{ key: 'pay_rebate_text_discount', label: '推广让利标签', type: 'input', default: '推荐购买 下单再减%discount%', dependsOn: { key: 'pay_rebate_s', value: true }, help: '显示在购买模块,推广让利的标签文案' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/** 返佣订单类型(与运行时 membership/content/credits 对齐)。 */
|
||||
const rebateTypeOptions = [
|
||||
{ label: '全部订单', value: 'all' },
|
||||
{ label: '购买会员', value: 'membership' },
|
||||
{ label: '付费内容', value: 'content' },
|
||||
{ label: '购买积分', value: 'credits' },
|
||||
]
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
export const cardPassSchema = {
|
||||
key: 'payment.card-pass',
|
||||
title: '充值卡',
|
||||
icon: 'fa fas fa-ticket-alt',
|
||||
description: '配置充值卡类型、卡号规则、批量生成和兑换逻辑。',
|
||||
defaults: {
|
||||
card_pass_enabled: false,
|
||||
card_pass_desc: '',
|
||||
card_pass_type: 'balance',
|
||||
card_pass_prefix: 'AK',
|
||||
card_pass_length: 16,
|
||||
card_pass_batch_size: 50,
|
||||
card_pass_max_redeem: 1,
|
||||
card_pass_expiry: 0,
|
||||
card_pass_amount: '10.00',
|
||||
card_pass_vip_days: 30,
|
||||
card_pass_credits: 0,
|
||||
card_pass_redeem_page: '',
|
||||
card_pass_success_msg: '兑换成功!',
|
||||
card_pass_fail_msg: '卡号无效或已过期',
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
key: 'basic',
|
||||
title: '充值卡设置',
|
||||
icon: 'fa fas fa-id-card',
|
||||
fields: [
|
||||
{ key: 'card_pass_enabled', label: '启用充值卡', type: 'switch', default: false },
|
||||
{ key: 'card_pass_desc', label: '充值卡说明', type: 'textarea', default: '', dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_type', label: '充值卡类型', type: 'select', default: 'balance', options: [{ label: '余额卡', value: 'balance' }, { label: 'VIP卡', value: 'vip' }, { label: '积分卡', value: 'credits' }], dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_prefix', label: '卡号前缀', type: 'input', default: 'AK', dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_length', label: '卡号长度', type: 'number', default: 16, min: 8, max: 32, dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_batch_size', label: '批量生成数量', type: 'number', default: 50, min: 1, max: 500, dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'redeem',
|
||||
title: '兑换设置',
|
||||
icon: 'fa fas fa-exchange-alt',
|
||||
fields: [
|
||||
{ key: 'card_pass_max_redeem', label: '单卡最大兑换次数', type: 'number', default: 1, min: 1, max: 100, dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_expiry', label: '有效期(天)', type: 'number', default: 0, min: 0, help: '0为永久有效', dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_amount', label: '面额(元)', type: 'input', default: '10.00', min: 1, max: 9999, dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_vip_days', label: 'VIP天数', type: 'number', default: 30, min: 1, max: 3650, dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_credits', label: '赠送积分', type: 'number', default: 0, min: 0, max: 99999, dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'display',
|
||||
title: '展示设置',
|
||||
icon: 'fa fas fa-desktop',
|
||||
fields: [
|
||||
{ key: 'card_pass_redeem_page', label: '兑换页面', type: 'input', default: '', dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_success_msg', label: '兑换成功提示', type: 'input', default: '兑换成功!', dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_fail_msg', label: '兑换失败提示', type: 'input', default: '卡号无效或已过期', dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,42 +1,56 @@
|
||||
/**
|
||||
* 收款接口 — 参考主题双下拉式重构:微信/支付宝接口选择(官方/易支付/关闭)、
|
||||
* PayPal/Stripe 独立开关、货币与结算、订单与编号(含三个前缀)、
|
||||
* 收款接口显示顺序,以及 5 个凭据配置块(支付宝/微信/PayPal/Stripe/易支付)。
|
||||
*/
|
||||
export const gatewaySchema = {
|
||||
key: 'payment.gateway',
|
||||
title: '收款接口',
|
||||
icon: 'fa fas fa-credit-card',
|
||||
description: '管理已支持的收款通道:余额、支付宝、微信、PayPal、Stripe、易支付与卡密兑换。开启渠道后展开填写凭据。',
|
||||
description: '配置收款通道:微信与支付宝可在官方/易支付之间选择,PayPal 与 Stripe 独立开关。订单前缀、流水号前缀、提现单号前缀在本页配置。',
|
||||
defaults: {
|
||||
payment_wechat_enabled: false,
|
||||
payment_alipay_enabled: false,
|
||||
payment_paypal_enabled: false,
|
||||
payment_stripe_enabled: false,
|
||||
payment_epay_enabled: false,
|
||||
payment_balance_enabled: true,
|
||||
payment_card_pass_enabled: false,
|
||||
wechat_merchantid: '',
|
||||
wechat_appid: '',
|
||||
wechat_payment_type: 'native',
|
||||
wechat_api_v3_key: '',
|
||||
wechat_private_key_path: '',
|
||||
wechat_cert_path: '',
|
||||
pay_wechat_sdk_options: 'null',
|
||||
pay_alipay_sdk_options: 'null',
|
||||
pay_paypal_sdk_s: false,
|
||||
pay_stripe_sdk_s: false,
|
||||
pay_currency_mark: '¥',
|
||||
pay_currency_unit: '元',
|
||||
pay_wechat_settle: { settle_rate: '1', settle_mark: '¥', settle_unit: '元' },
|
||||
pay_alipay_settle: { settle_rate: '1', settle_mark: '¥', settle_unit: '元' },
|
||||
pay_sdk_custom_title_s: false,
|
||||
pay_sdk_custom_title: '',
|
||||
payment_order_prefix: 'AKSRT',
|
||||
payment_flow_prefix: 'AK',
|
||||
payment_withdrawal_prefix: 'TX',
|
||||
payment_order_expiry: 30,
|
||||
pay_wait_float_s: true,
|
||||
pay_sdk_order: {},
|
||||
alipay_appid: '',
|
||||
alipay_privatekey: '',
|
||||
alipay_payment_type: 'page',
|
||||
alipay_app_public_cert_path: '',
|
||||
alipay_public_cert_path: '',
|
||||
alipay_root_cert_path: '',
|
||||
wechat_merchantid: '',
|
||||
wechat_appid: '',
|
||||
wechat_payment_type: 'native',
|
||||
wechat_api_v3_key: '',
|
||||
wechat_private_key_path: '',
|
||||
wechat_cert_path: '',
|
||||
paypal_client_id: '',
|
||||
paypal_client_secret: '',
|
||||
paypal_currency: 'USD',
|
||||
paypal_sandbox: true,
|
||||
paypal_rates: '0.14',
|
||||
paypal_sandbox: false,
|
||||
stripe_secret_key: '',
|
||||
stripe_publishable_key: '',
|
||||
stripe_webhook_secret: '',
|
||||
stripe_currency: 'usd',
|
||||
stripe_test_mode: true,
|
||||
stripe_currency: 'USD',
|
||||
stripe_rates: '0.14',
|
||||
stripe_test_mode: false,
|
||||
epay_apiurl: '',
|
||||
epay_pid: '',
|
||||
epay_key: '',
|
||||
epay_channel_alipay: true,
|
||||
epay_channel_wxpay: true,
|
||||
epay_device: 'auto',
|
||||
},
|
||||
groups: [
|
||||
@@ -46,122 +60,293 @@ export const gatewaySchema = {
|
||||
icon: 'fa fas fa-info-circle',
|
||||
fields: [
|
||||
{
|
||||
key: 'payment_gateway_notice',
|
||||
label: '通道说明',
|
||||
type: 'notice',
|
||||
alertType: 'info',
|
||||
alertContent: '以渠道开关 + 凭据是否齐全为准。异步回调由各网关验签(支付宝/微信/Stripe/易支付等),无需额外口令。卡密为兑换入口,不出现在收银台支付方式列表。',
|
||||
key: 'gateway_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
content: '<ul style="margin:0 0 0 18px;line-height:1.9"><li>以下收款接口,主题仅提供API接入服务,收款平台的可靠性请自行斟酌!</li><li>涉及到资金及信息安全,有相关执照的商家推荐使用官方接口</li><li>微信 / 支付宝接口可在「官方」与「易支付」之间选择;PayPal 与 Stripe 独立开关</li><li>异步回调由各网关验签,无需额外口令;回调地址固定为 /wp-json/aksrt/v1/payment/{gateway}/{notify|return}</li></ul>',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'gateways',
|
||||
title: '支付渠道开关',
|
||||
key: 'channels',
|
||||
title: '收款接口选择',
|
||||
icon: 'fa fas fa-toggle-on',
|
||||
description: '先开启渠道,下方对应配置卡片会展开;填写凭据后网关状态变为可用。余额与卡密无第三方凭据,仅开关即可。',
|
||||
description: '微信与支付宝各选一种接入方式;选「易支付」后需在下方易支付配置块填写接口信息。',
|
||||
fields: [
|
||||
{ key: 'payment_balance_enabled', label: '余额支付', type: 'switch', default: true, help: '需同时在「余额充值」启用余额功能。关闭后用户中心仍可有钱包,但结账不可用余额支付。' },
|
||||
{ key: 'payment_alipay_enabled', label: '支付宝', type: 'switch', default: false },
|
||||
{ key: 'payment_wechat_enabled', label: '微信支付', type: 'switch', default: false },
|
||||
{ key: 'payment_paypal_enabled', label: 'PayPal', type: 'switch', default: false },
|
||||
{ key: 'payment_stripe_enabled', label: 'Stripe', type: 'switch', default: false },
|
||||
{ key: 'payment_epay_enabled', label: '易支付', type: 'switch', default: false, help: '兼容彩虹易支付协议的聚合收银台,可同时开启支付宝/微信子通道。' },
|
||||
{ key: 'payment_card_pass_enabled', label: '卡密充值', type: 'switch', default: false, help: '仅控制卡密兑换入口是否开放;卡密规则与批量发卡在「充值卡」页。不进入结账支付方式列表。' },
|
||||
{
|
||||
key: 'pay_wechat_sdk_options',
|
||||
label: '微信收款接口',
|
||||
type: 'select',
|
||||
default: 'null',
|
||||
options: [
|
||||
{ label: '微信官方', value: 'official_wechat' },
|
||||
{ label: '易支付-微信', value: 'epay' },
|
||||
{ label: '关闭微信收款', value: 'null' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'pay_alipay_sdk_options',
|
||||
label: '支付宝收款接口',
|
||||
type: 'select',
|
||||
default: 'null',
|
||||
options: [
|
||||
{ label: '支付宝企业支付/当面付', value: 'official_alipay' },
|
||||
{ label: '易支付-支付宝', value: 'epay' },
|
||||
{ label: '关闭支付宝收款', value: 'null' },
|
||||
],
|
||||
},
|
||||
{ key: 'pay_paypal_sdk_s', label: 'PayPal支付(贝宝)', type: 'switch', default: false, help: '启用后请在下方配置参数' },
|
||||
{ key: 'pay_stripe_sdk_s', label: 'Stripe支付', type: 'switch', default: false, help: '启用后请在下方配置参数' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'currency',
|
||||
title: '货币与结算',
|
||||
icon: 'fa fas fa-coins',
|
||||
fields: [
|
||||
{ key: 'pay_currency_mark', label: '本地货币符号', type: 'input', default: '¥', help: '显示在金额前面(例如 $ 或 ¥)' },
|
||||
{ key: 'pay_currency_unit', label: '本地货币单位名称', type: 'input', default: '元', help: '金额后缀,例如:人民币填「元」、美元填「dollars」' },
|
||||
{
|
||||
key: 'pay_wechat_settle',
|
||||
label: '微信结算配置',
|
||||
type: 'fieldset',
|
||||
default: { settle_rate: '1', settle_mark: '¥', settle_unit: '元' },
|
||||
fields: [
|
||||
{ key: 'settle_rate', label: '结算汇率', type: 'input', default: '1' },
|
||||
{ key: 'settle_mark', label: '结算货币符号', type: 'input', default: '¥' },
|
||||
{ key: 'settle_unit', label: '结算货币单位', type: 'input', default: '元' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'pay_alipay_settle',
|
||||
label: '支付宝结算配置',
|
||||
type: 'fieldset',
|
||||
default: { settle_rate: '1', settle_mark: '¥', settle_unit: '元' },
|
||||
fields: [
|
||||
{ key: 'settle_rate', label: '结算汇率', type: 'input', default: '1' },
|
||||
{ key: 'settle_mark', label: '结算货币符号', type: 'input', default: '¥' },
|
||||
{ key: 'settle_unit', label: '结算货币单位', type: 'input', default: '元', help: '如果接入的收款方式与本地的货币类型不同,则在此配置结算汇率及单位' },
|
||||
],
|
||||
},
|
||||
{ key: 'pay_sdk_custom_title_s', label: '自定义收款标题', type: 'switch', default: false, help: '默认为付款类型加网站名,如需自定义请开启' },
|
||||
{ key: 'pay_sdk_custom_title', label: '自定义收款标题', type: 'input', default: '', dependsOn: { key: 'pay_sdk_custom_title_s', value: true }, help: '可使用变量:订单类型 %order_type%,例如:%order_type%-AKSRT;注意控制总长度,太长会导致部分接口报错' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'order',
|
||||
title: '订单与编号',
|
||||
icon: 'fa fas fa-hashtag',
|
||||
description: '订单号、流水号与提现单号的前缀配置(原名配置项保留于此)。',
|
||||
fields: [
|
||||
{ key: 'payment_order_prefix', label: '订单前缀', type: 'input', default: 'AKSRT', help: '订单号生成前缀' },
|
||||
{ key: 'payment_flow_prefix', label: '流水号前缀', type: 'input', sanitize_type: 'business_prefix', default: 'AK', maxlength: 12, help: '仅允许字母、数字、下划线和短横线,最多 12 位。' },
|
||||
{ key: 'payment_withdrawal_prefix', label: '提现单号前缀', type: 'input', sanitize_type: 'business_prefix', default: 'TX', maxlength: 12, help: '仅允许字母、数字、下划线和短横线,最多 12 位。' },
|
||||
{ key: 'payment_order_expiry', label: '订单支付时效(分钟)', type: 'number', default: 30, min: 5, max: 1440, help: '用户下单后,最大支付时间,超过后订单将自动关闭(不能低于5分钟)' },
|
||||
{ key: 'pay_wait_float_s', label: '浮窗显示待支付订单', type: 'switch', default: true, help: '将用户待支付订单一直显示在右上角,提醒用户付款' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'sdk-order',
|
||||
title: '收款接口显示顺序',
|
||||
icon: 'fa fas fa-sort-amount-down',
|
||||
fields: [
|
||||
{
|
||||
key: 'pay_sdk_order',
|
||||
label: '显示顺序',
|
||||
type: 'sortable',
|
||||
default: {},
|
||||
items: [
|
||||
{ key: 'wechat', title: '微信', fields: [] },
|
||||
{ key: 'alipay', title: '支付宝', fields: [] },
|
||||
{ key: 'paypal', title: 'PayPal(贝宝)', fields: [] },
|
||||
{ key: 'stripe', title: 'Stripe', fields: [] },
|
||||
{ key: 'balance', title: '余额', fields: [] },
|
||||
{ key: 'card_pass', title: '卡密', fields: [] },
|
||||
],
|
||||
help: '收银台支付方式显示顺序;仅显示已开启的接口',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'alipay',
|
||||
title: '支付宝官方',
|
||||
icon: 'fa fas fa-credit-card',
|
||||
description: '支付宝官方接口(当面付 / 网站应用)。',
|
||||
collapsible: true,
|
||||
expandWhen: 'pay_alipay_sdk_options',
|
||||
dependsOn: { key: 'pay_alipay_sdk_options', value: 'official_alipay' },
|
||||
fields: [
|
||||
{
|
||||
key: 'alipay_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
content: '<ul style="margin:0 0 0 18px;line-height:1.9"><li>支付宝公钥为必填项目(公钥错误可以成功付款,但会回调失败)</li><li>回调地址固定为 /wp-json/aksrt/v1/payment/alipay/{notify|return}</li><li>同时填写了企业支付以及当面付参数,则优先使用当面付</li></ul>',
|
||||
},
|
||||
{ key: 'alipay_appid', label: '当面付:APPID', type: 'input', default: '' },
|
||||
{ key: 'alipay_privatekey', label: '当面付:应用私钥', type: 'textarea', sanitize_type: 'code', default: '', rows: 4 },
|
||||
{
|
||||
key: 'alipay_payment_type',
|
||||
label: '支付方式',
|
||||
type: 'select',
|
||||
default: 'page',
|
||||
options: [
|
||||
{ label: '电脑网站支付(web)', value: 'page' },
|
||||
{ label: '手机网站支付(wap)', value: 'wap' },
|
||||
],
|
||||
},
|
||||
{ key: 'alipay_app_public_cert_path', label: '应用公钥证书路径', type: 'input', default: '', help: 'appCertPublicKey.crt 绝对路径。' },
|
||||
{ key: 'alipay_public_cert_path', label: '支付宝公钥证书路径', type: 'input', default: '', help: 'alipayCertPublicKey_RSA2.crt 绝对路径。' },
|
||||
{ key: 'alipay_root_cert_path', label: '支付宝根证书路径', type: 'input', default: '', help: 'alipayRootCert.crt 绝对路径。' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'wechat',
|
||||
title: '微信支付配置',
|
||||
title: '微信官方',
|
||||
icon: 'fa fab fa-weixin',
|
||||
description: '开启「微信支付」后填写商户号与密钥。',
|
||||
description: '微信企业支付官方接口。',
|
||||
collapsible: true,
|
||||
expandWhen: 'payment_wechat_enabled',
|
||||
dependsOn: { key: 'payment_wechat_enabled', value: true },
|
||||
expandWhen: 'pay_wechat_sdk_options',
|
||||
dependsOn: { key: 'pay_wechat_sdk_options', value: 'official_wechat' },
|
||||
fields: [
|
||||
{ key: 'wechat_merchantid', label: '商户号', type: 'input', default: '' },
|
||||
{ key: 'wechat_appid', label: 'AppID', type: 'input', default: '' },
|
||||
{ key: 'wechat_payment_type', label: '支付方式', type: 'select', default: 'native', options: [
|
||||
{ label: '扫码支付(Native)', value: 'native' },
|
||||
{ label: 'H5支付', value: 'h5' },
|
||||
] },
|
||||
{ key: 'wechat_api_v3_key', label: 'APIv3密钥', type: 'input', default: '', help: '微信商户平台设置的 APIv3 密钥(32位)。' },
|
||||
{
|
||||
key: 'wechat_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
content: '<ul style="margin:0 0 0 18px;line-height:1.9"><li>微信企业支付:官方接口,商家可申请,有年费,申请较为复杂</li><li>回调地址固定为 /wp-json/aksrt/v1/payment/wechatpay/{notify|return},请配置 Native 支付回调链接</li></ul>',
|
||||
},
|
||||
{ key: 'wechat_merchantid', label: '商户号 PartnerID', type: 'input', default: '' },
|
||||
{ key: 'wechat_appid', label: '授权绑定的AppID', type: 'input', default: '' },
|
||||
{
|
||||
key: 'wechat_payment_type',
|
||||
label: '支付方式',
|
||||
type: 'select',
|
||||
default: 'native',
|
||||
options: [
|
||||
{ label: '扫码支付(Native)', value: 'native' },
|
||||
{ label: 'H5支付', value: 'h5' },
|
||||
],
|
||||
},
|
||||
{ key: 'wechat_api_v3_key', label: '支付APIv3密钥', type: 'input', default: '', help: '微信商户平台设置的 APIv3 密钥(32位)。' },
|
||||
{ key: 'wechat_private_key_path', label: '商户私钥路径', type: 'input', default: '', help: 'apiclient_key.pem 绝对路径,用于签名。' },
|
||||
{ key: 'wechat_cert_path', label: '商户API证书路径', type: 'input', default: '', help: 'apiclient_cert.pem 绝对路径。' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'alipay',
|
||||
title: '支付宝配置',
|
||||
icon: 'fa fas fa-credit-card',
|
||||
description: '开启「支付宝」后填写应用与证书。',
|
||||
collapsible: true,
|
||||
expandWhen: 'payment_alipay_enabled',
|
||||
dependsOn: { key: 'payment_alipay_enabled', value: true },
|
||||
fields: [
|
||||
{ key: 'alipay_appid', label: 'AppID', type: 'input', default: '' },
|
||||
{ key: 'alipay_privatekey', label: '应用私钥', type: 'textarea', rows: 3, default: '' },
|
||||
{ key: 'alipay_payment_type', label: '支付方式', type: 'select', default: 'page', options: [
|
||||
{ label: '电脑网站支付(web)', value: 'page' },
|
||||
{ label: '手机网站支付(wap)', value: 'wap' },
|
||||
] },
|
||||
{ key: 'alipay_app_public_cert_path', label: '应用公钥证书路径', type: 'input', default: '', help: 'appCertPublicKey.crt 绝对路径。' },
|
||||
{ key: 'alipay_public_cert_path', label: '支付宝公钥证书路径', type: 'input', default: '', help: 'alipayCertPublicKey_RSA2.crt 绝对路径。' },
|
||||
{ key: 'alipay_root_cert_path', label: '支付宝根证书路径', type: 'input', default: '', help: 'alipayRootCert.crt 绝对路径。' },
|
||||
{ key: 'alipay_notify_help', label: '回调地址说明', type: 'notice', alertType: 'info', alertContent: '异步通知 / 同步返回已固定为 /wp-json/aksrt/v1/payment/alipay/{notify|return},无需在此填写。' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'paypal',
|
||||
title: 'PayPal 配置',
|
||||
title: 'PayPal(贝宝)',
|
||||
icon: 'fa fab fa-paypal',
|
||||
description: '开启「PayPal」后填写 Client 凭据。',
|
||||
description: '常见的海外支付接口。',
|
||||
collapsible: true,
|
||||
expandWhen: 'payment_paypal_enabled',
|
||||
dependsOn: { key: 'payment_paypal_enabled', value: true },
|
||||
expandWhen: 'pay_paypal_sdk_s',
|
||||
dependsOn: { key: 'pay_paypal_sdk_s', value: true },
|
||||
fields: [
|
||||
{
|
||||
key: 'paypal_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
content: '<ul style="margin:0 0 0 18px;line-height:1.9"><li>PayPal(贝宝)是常见的海外支付接口</li><li>如果您的收款账户为中国大陆地区,则账户类型必须是企业,且只能收取非中国大陆区的PayPal付款</li><li>REST 应用申请:https://developer.paypal.com/dashboard/applications</li></ul>',
|
||||
},
|
||||
{ key: 'paypal_client_id', label: 'Client ID', type: 'input', default: '' },
|
||||
{ key: 'paypal_client_secret', label: 'Client Secret', type: 'input', default: '' },
|
||||
{ key: 'paypal_currency', label: '货币', type: 'input', default: 'USD' },
|
||||
{ key: 'paypal_sandbox', label: '沙箱模式', type: 'switch', default: true },
|
||||
{ key: 'paypal_currency', label: '结算货币', type: 'select', default: 'USD', options: currencyOptions },
|
||||
{ key: 'paypal_rates', label: '货币汇率', type: 'input', default: '0.14', help: '根据你选择的货币类型与您定价货币的汇率进行填写,例如1元人民币兑换0.137美元(USD),则此处填0.137' },
|
||||
{ key: 'paypal_sandbox', label: '沙盒调试模式', type: 'switch', default: false, help: '沙箱模式(Sandbox)是PayPal支付的测试环境。开启后请使用沙箱环境生成的收款账户和付款账户进行测试。' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'stripe',
|
||||
title: 'Stripe 配置',
|
||||
title: 'Stripe',
|
||||
icon: 'fa fab fa-cc-stripe',
|
||||
description: '开启「Stripe」后填写 API Key 与 Webhook。',
|
||||
description: '常见的海外信用卡收款接口。',
|
||||
collapsible: true,
|
||||
expandWhen: 'payment_stripe_enabled',
|
||||
dependsOn: { key: 'payment_stripe_enabled', value: true },
|
||||
expandWhen: 'pay_stripe_sdk_s',
|
||||
dependsOn: { key: 'pay_stripe_sdk_s', value: true },
|
||||
fields: [
|
||||
{ key: 'stripe_secret_key', label: 'Secret Key', type: 'input', default: '' },
|
||||
{
|
||||
key: 'stripe_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
content: '<ul style="margin:0 0 0 18px;line-height:1.9"><li>Stripe 是常见的海外信用卡收款接口</li><li>API 密钥申请:https://dashboard.stripe.com/apikeys</li><li>Webhook 事件:checkout.session.completed</li><li>测试环境请使用 sk_test_ 开头的 Secret Key</li></ul>',
|
||||
},
|
||||
{ key: 'stripe_secret_key', label: '私钥Secret Key', type: 'input', default: '', help: 'Stripe Dashboard → Developers → API keys → Secret key' },
|
||||
{ key: 'stripe_publishable_key', label: 'Publishable Key', type: 'input', default: '' },
|
||||
{ key: 'stripe_webhook_secret', label: 'Webhook Secret', type: 'input', default: '', help: 'Stripe Dashboard 配置 Webhook 后获得,用于验签。' },
|
||||
{ key: 'stripe_currency', label: '货币', type: 'input', default: 'usd' },
|
||||
{ key: 'stripe_test_mode', label: '测试模式', type: 'switch', default: true },
|
||||
{ key: 'stripe_webhook_secret', label: '签名密钥Webhook Secret', type: 'input', default: '', help: 'Developers → Webhooks → 端点 → Signing secret(whsec_ 开头)' },
|
||||
{ key: 'stripe_currency', label: '结算货币', type: 'select', default: 'USD', options: currencyOptions },
|
||||
{ key: 'stripe_rates', label: '货币汇率', type: 'input', default: '0.14', help: '根据你选择的货币类型与您定价货币的汇率进行填写,例如1元人民币兑换0.137美元(USD),则此处填0.137' },
|
||||
{ key: 'stripe_test_mode', label: '测试模式', type: 'switch', default: false, help: '如果使用的测试开放模式,则请使用 sk_test_ 测试密钥与 Stripe 测试卡进行配置' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'epay',
|
||||
title: '易支付配置',
|
||||
title: '易支付',
|
||||
icon: 'fa fas fa-university',
|
||||
description: '开启「易支付」后填写接口地址与商户信息,并选择支付宝/微信子通道。',
|
||||
description: '易支付(彩虹协议)聚合收银台;通道由上方微信/支付宝接口选择控制。',
|
||||
collapsible: true,
|
||||
expandWhen: 'payment_epay_enabled',
|
||||
dependsOn: { key: 'payment_epay_enabled', value: true },
|
||||
expandWhen: 'pay_wechat_sdk_options',
|
||||
dependsOn: {
|
||||
any: [
|
||||
{ key: 'pay_wechat_sdk_options', value: 'epay' },
|
||||
{ key: 'pay_alipay_sdk_options', value: 'epay' },
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{ key: 'epay_apiurl', label: '接口地址', type: 'input', default: '', help: '易支付站点根地址,如 https://pay.example.com/(末尾斜杠可选)。' },
|
||||
{ key: 'epay_pid', label: '商户ID', type: 'input', default: '' },
|
||||
{ key: 'epay_key', label: '商户密钥', type: 'input', default: '', help: 'MD5 通信密钥。' },
|
||||
{ key: 'epay_channel_alipay', label: '启用支付宝通道', type: 'switch', default: true },
|
||||
{ key: 'epay_channel_wxpay', label: '启用微信通道', type: 'switch', default: true },
|
||||
{ key: 'epay_device', label: '下单终端', type: 'select', default: 'auto', options: [
|
||||
{ label: '自动(按 UA)', value: 'auto' },
|
||||
{ label: '电脑端 submit', value: 'pc' },
|
||||
{ label: '移动端 mapi', value: 'mobile' },
|
||||
] },
|
||||
{ key: 'epay_notify_help', label: '回调地址说明', type: 'notice', alertType: 'info', alertContent: '异步/同步回调固定为 /wp-json/aksrt/v1/payment/epay_alipay/{notify|return} 与 /payment/epay_wxpay/{notify|return},请在易支付后台放行对应 URL。' },
|
||||
{
|
||||
key: 'epay_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
content: '<ul style="margin:0 0 0 18px;line-height:1.9"><li>易支付是一个常见的支付系统源码,任何人都可以下载并搭建</li><li>同时市面上多数的小规模支付平台都是由易支付修改而来,例如部分码支付、源支付等等</li><li>由于此接口服务商众多,主题只负责技术接入,平台可靠性请自行斟酌</li></ul>',
|
||||
},
|
||||
{ key: 'epay_apiurl', label: 'API接口网址', type: 'input', default: '', help: '请填写接口地址,例如:https://pay.xxxxxxx.cn/' },
|
||||
{ key: 'epay_pid', label: '商户号', type: 'input', default: '' },
|
||||
{ key: 'epay_key', label: '商户秘钥', type: 'input', default: '', help: 'MD5 通信密钥。' },
|
||||
{
|
||||
key: 'epay_device',
|
||||
label: '下单终端',
|
||||
type: 'select',
|
||||
default: 'auto',
|
||||
options: [
|
||||
{ label: '自动(按 UA)', value: 'auto' },
|
||||
{ label: '电脑端 submit', value: 'pc' },
|
||||
{ label: '移动端 mapi', value: 'mobile' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
/** PayPal / Stripe 结算货币选项(参考主题同款 26 种)。 */
|
||||
const currencyOptions = [
|
||||
{ label: '澳元 (AUD)', value: 'AUD' },
|
||||
{ label: '巴西雷亚尔 (BRL)', value: 'BRL' },
|
||||
{ label: '加拿大元 (CAD)', value: 'CAD' },
|
||||
{ label: '人民币 (CNY)', value: 'CNY' },
|
||||
{ label: '克朗 (CZK)', value: 'CZK' },
|
||||
{ label: '丹麦克朗 (DKK)', value: 'DKK' },
|
||||
{ label: '欧元 (EUR)', value: 'EUR' },
|
||||
{ label: '港币 (HKD)', value: 'HKD' },
|
||||
{ label: '匈牙利福林 (HUF)', value: 'HUF' },
|
||||
{ label: '印度卢比 (INR)', value: 'INR' },
|
||||
{ label: '以色列新谢克尔 (ILS)', value: 'ILS' },
|
||||
{ label: '日元 (JPY)', value: 'JPY' },
|
||||
{ label: '马来西亚林吉特 (MYR)', value: 'MYR' },
|
||||
{ label: '墨西哥比索 (MXN)', value: 'MXN' },
|
||||
{ label: '新台币 (TWD)', value: 'TWD' },
|
||||
{ label: '新西兰元 (NZD)', value: 'NZD' },
|
||||
{ label: '挪威克朗 (NOK)', value: 'NOK' },
|
||||
{ label: '菲律宾比索 (PHP)', value: 'PHP' },
|
||||
{ label: '波兰兹罗提 (PLN)', value: 'PLN' },
|
||||
{ label: '英镑 (GBP)', value: 'GBP' },
|
||||
{ label: '俄罗斯卢布 (RUB)', value: 'RUB' },
|
||||
{ label: '新加坡元 (SGD)', value: 'SGD' },
|
||||
{ label: '瑞典克朗 (SEK)', value: 'SEK' },
|
||||
{ label: '瑞士法郎 (CHF)', value: 'CHF' },
|
||||
{ label: '泰铢 (THB)', value: 'THB' },
|
||||
{ label: '美元 (USD)', value: 'USD' },
|
||||
]
|
||||
|
||||
@@ -2,23 +2,20 @@
|
||||
* Payment & Paid domain schemas.
|
||||
*/
|
||||
|
||||
import { knowledgeSchema } from './knowledge.js'
|
||||
import { rechargeSchema } from './recharge.js'
|
||||
import { vipSchema } from './vip.js'
|
||||
import { revenueSchema } from './revenue.js'
|
||||
import { affiliateSchema } from './affiliate.js'
|
||||
import { gatewaySchema } from './gateway.js'
|
||||
import { orderSchema } from './order.js'
|
||||
import { cardPassSchema } from './cardPass.js'
|
||||
import { withdrawalSchema } from './withdrawal.js'
|
||||
|
||||
export const paymentSchemas = {
|
||||
knowledge: knowledgeSchema,
|
||||
recharge: rechargeSchema,
|
||||
vip: vipSchema,
|
||||
revenue: revenueSchema,
|
||||
affiliate: affiliateSchema,
|
||||
gateway: gatewaySchema,
|
||||
order: orderSchema,
|
||||
'card-pass': cardPassSchema,
|
||||
cardPass: cardPassSchema,
|
||||
withdrawal: withdrawalSchema,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* 知识付费 — 1:1 复刻参考主题对应页面(免登陆购买 / 模块位置 / 优惠码 /
|
||||
* 客户服务 / 付费下载 / 商品预置参数),并保留 AKSRT 订单展示与状态文案。
|
||||
*/
|
||||
export const knowledgeSchema = {
|
||||
key: 'payment.knowledge',
|
||||
title: '知识付费',
|
||||
icon: 'fa fas fa-shopping-bag',
|
||||
description: '付费内容总开关、免登陆购买、模块显示、优惠码与商品预置参数配置。',
|
||||
defaults: {
|
||||
pay_no_logged_in: true,
|
||||
pay_cookie_day: 15,
|
||||
pay_no_logged_remind: '您当前未登录!建议登陆后购买,可保存购买订单',
|
||||
pay_box_position: 'top',
|
||||
pay_free_logged_show: true,
|
||||
pay_show_user: true,
|
||||
pay_show_paycount: true,
|
||||
coupon_post_s: false,
|
||||
coupon_desc: '',
|
||||
pay_mark: '付费',
|
||||
payment_show_ip: false,
|
||||
payment_show_device: false,
|
||||
payment_show_order_no: true,
|
||||
payment_show_create_time: true,
|
||||
payment_show_pay_time: true,
|
||||
payment_order_status_pending: '待支付',
|
||||
payment_order_status_paid: '已支付',
|
||||
payment_order_status_refunding: '退款中',
|
||||
payment_order_status_refunded: '已退款',
|
||||
payment_order_status_closed: '已关闭',
|
||||
pay_service: [],
|
||||
down_alone_page: false,
|
||||
down_user_log: 50,
|
||||
down_post_log: 50,
|
||||
down_remote_to_local_s: true,
|
||||
down_remote_to_local_size: 2048,
|
||||
down_remote_to_local_ext: 'zip,rar,7z,text,png,jpg,jpeg,gif,mp3,mp4,avi,wmv,flv,swf,doc,docx,xls,xlsx,ppt,pptx,pdf',
|
||||
down_verify_nonce: true,
|
||||
pay_modo_default: '0',
|
||||
points_price_default: 0,
|
||||
vip_1_points_default: 0,
|
||||
vip_2_points_default: 0,
|
||||
pay_price_default: '0.01',
|
||||
pay_original_price_default: '',
|
||||
pay_promotion_tag_default: '<i class="fa fa-fw fa-bolt"></i> 限时特惠',
|
||||
vip_1_price_default: '0',
|
||||
vip_2_price_default: '0',
|
||||
pay_rebate_discount: '0',
|
||||
pay_cuont_default: { min: 0, max: 0 },
|
||||
pay_details_default: '',
|
||||
pay_extra_hide_default: '',
|
||||
pay_attributes_default: [],
|
||||
pay_gallery_show_default: 1,
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
key: 'notice',
|
||||
title: '说明',
|
||||
icon: 'fa fas fa-info-circle',
|
||||
fields: [
|
||||
{
|
||||
key: 'knowledge_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
content: '<p><b>知识付费</b></p><ul style="margin:6px 0 0 18px;line-height:1.9"><li>知识付费功能主要分为文章付费、付费会员、余额系统、推广返佣、创作分成等功能组成,您可以根据您的需要开启对应功能</li><li>使用支付功能请先在「收款接口」页配置好收款通道</li></ul>',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'guest-buy',
|
||||
title: '免登陆购买',
|
||||
icon: 'fa fas fa-user-clock',
|
||||
fields: [
|
||||
{ key: 'pay_no_logged_in', label: '免登陆购买', type: 'switch', default: true, help: '开启后如果用户未登录则使用浏览器缓存验证是否购买' },
|
||||
{ key: 'pay_cookie_day', label: 'Cookie时间(天)', type: 'number', default: 15, min: 1, max: 31, dependsOn: { key: 'pay_no_logged_in', value: true }, help: '免登陆购买的浏览器缓存有效时间' },
|
||||
{ key: 'pay_no_logged_remind', label: '未登录提醒', type: 'textarea', default: '您当前未登录!建议登陆后购买,可保存购买订单', rows: 2, dependsOn: { key: 'pay_no_logged_in', value: true } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'box-position',
|
||||
title: '模块显示位置',
|
||||
icon: 'fa fas fa-arrows-alt',
|
||||
fields: [
|
||||
{
|
||||
key: 'pay_box_position',
|
||||
label: '购买模块位置',
|
||||
type: 'radio',
|
||||
default: 'top',
|
||||
options: [
|
||||
{ label: '文章模块上方', value: 'box_top' },
|
||||
{ label: '文章内容顶部', value: 'top' },
|
||||
{ label: '文章内容底部', value: 'bottom' },
|
||||
{ label: '文章模块下方', value: 'box_bottom' },
|
||||
],
|
||||
help: '在文章页面中购买模块的显示位置',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'display-basic',
|
||||
title: '基础显示',
|
||||
icon: 'fa fas fa-eye',
|
||||
fields: [
|
||||
{ key: 'pay_free_logged_show', label: '免费资源登录可见', type: 'switch', default: true, help: '免费资源必须登录后才能查看' },
|
||||
{ key: 'pay_show_user', label: '订单数据', type: 'switch', default: true, help: '在用户中心显示订单数据' },
|
||||
{ key: 'pay_show_paycount', label: '销量显示', type: 'switch', default: true, help: '商品详情显示销售数量' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'coupon',
|
||||
title: '商品优惠码',
|
||||
icon: 'fa fas fa-ticket-alt',
|
||||
fields: [
|
||||
{ key: 'coupon_post_s', label: '为所有商品启用优惠券功能', type: 'switch', default: false, help: '开启后将为所有商品显示输入优惠码的功能;也可以单独给任意一个商品开启' },
|
||||
{ key: 'coupon_desc', label: '优惠码默认说明', type: 'textarea', default: '', rows: 1, dependsOn: { key: 'coupon_post_s', value: true }, help: '用户填写优惠码时展示的提醒内容,支持HTML代码' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'display',
|
||||
title: '展示设置',
|
||||
icon: 'fa fas fa-list-alt',
|
||||
fields: [
|
||||
{ key: 'pay_mark', label: '付费标记', type: 'input', default: '付费' },
|
||||
{ key: 'payment_show_ip', label: '显示用户IP', type: 'switch', default: false },
|
||||
{ key: 'payment_show_device', label: '显示设备信息', type: 'switch', default: false },
|
||||
{ key: 'payment_show_order_no', label: '显示订单号', type: 'switch', default: true },
|
||||
{ key: 'payment_show_create_time', label: '显示创建时间', type: 'switch', default: true },
|
||||
{ key: 'payment_show_pay_time', label: '显示支付时间', type: 'switch', default: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
title: '状态设置',
|
||||
icon: 'fa fas fa-tags',
|
||||
fields: [
|
||||
{ key: 'payment_order_status_pending', label: '待支付状态文案', type: 'input', default: '待支付' },
|
||||
{ key: 'payment_order_status_paid', label: '已支付状态文案', type: 'input', default: '已支付' },
|
||||
{ key: 'payment_order_status_refunding', label: '退款中状态文案', type: 'input', default: '退款中' },
|
||||
{ key: 'payment_order_status_refunded', label: '已退款状态文案', type: 'input', default: '已退款' },
|
||||
{ key: 'payment_order_status_closed', label: '已关闭状态文案', type: 'input', default: '已关闭' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'service',
|
||||
title: '客户服务',
|
||||
icon: 'fa fas fa-headphones',
|
||||
fields: [
|
||||
{
|
||||
key: 'pay_service',
|
||||
label: '用户服务内容',
|
||||
type: 'group',
|
||||
button_title: '添加服务项',
|
||||
default: [],
|
||||
fields: [
|
||||
{ key: 'value', label: '服务内容', type: 'input', default: '' },
|
||||
{ key: 'icon', label: '自定义图标', type: 'icon', default: '' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'feature',
|
||||
title: '商品功能配置',
|
||||
icon: 'fa fas fa-cubes',
|
||||
fields: [
|
||||
{
|
||||
key: 'pay_type_option',
|
||||
label: '商品功能配置',
|
||||
type: 'tabbed',
|
||||
tabs: [
|
||||
{
|
||||
key: 'download',
|
||||
title: '付费下载',
|
||||
fields: [
|
||||
{ key: 'down_alone_page', label: '独立下载页面', type: 'switch', default: false, help: '开启后,付费资源的下载链接会跳转独立页面显示' },
|
||||
{ key: 'down_user_log', label: '保存用户下载记录(条)', type: 'number', default: 50, min: 0, help: '填0则不保存,注意记录量太大可能会影响网站性能' },
|
||||
{ key: 'down_post_log', label: '保存文章下载记录(条)', type: 'number', default: 50, min: 0, help: '填0则不保存,注意记录量太大可能会影响网站性能' },
|
||||
{ key: 'down_remote_to_local_s', label: '远程文件转本地文件下载', type: 'switch', default: true, help: '当下载地址为外链文件时,先将远程文件下载到本地服务器,再提供给用户下载,可完全隐藏远程下载地址' },
|
||||
{ key: 'down_remote_to_local_size', label: '文件大小限制(Kb)', type: 'number', default: 2048, min: 0 },
|
||||
{ key: 'down_remote_to_local_ext', label: '文件格式限制', type: 'input', default: 'zip,rar,7z,text,png,jpg,jpeg,gif,mp3,mp4,avi,wmv,flv,swf,doc,docx,xls,xlsx,ppt,pptx,pdf', help: '限制使用此功能的文件格式,多个用英文逗号隔开' },
|
||||
{ key: 'down_verify_nonce', label: '下载前检测用户环境', type: 'switch', default: true, help: '防止机器批量扫描下载,有效防止跨站攻击' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'presets',
|
||||
title: '商品预置参数配置',
|
||||
icon: 'fa fas fa-sliders-h',
|
||||
fields: [
|
||||
{
|
||||
key: 'presets_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
content: '在下方配置的参数新建文章时候会自动填入,方便新建文章的设置。最终以文章配置为准',
|
||||
},
|
||||
{
|
||||
key: 'pay_modo_default',
|
||||
label: '支付类型',
|
||||
type: 'radio',
|
||||
default: '0',
|
||||
options: [
|
||||
{ label: '普通商品(金钱购买)', value: '0' },
|
||||
{ label: '积分商品(积分购买,依赖于用户积分功能)', value: 'points' },
|
||||
],
|
||||
},
|
||||
{ key: 'points_price_default', label: '积分售价(积分)', type: 'number', default: 0, min: 0 },
|
||||
{ key: 'vip_1_points_default', label: '一级会员积分售价(积分)', type: 'number', default: 0, min: 0 },
|
||||
{ key: 'vip_2_points_default', label: '二级会员积分售价(积分)', type: 'number', default: 0, min: 0 },
|
||||
{ key: 'pay_price_default', label: '执行价', type: 'input', sanitize_type: 'money_decimal', default: '0.01', suffix: '元' },
|
||||
{ key: 'pay_original_price_default', label: '原价', type: 'input', sanitize_type: 'money_decimal', default: '', suffix: '元', help: '显示在执行价格前面,并划掉' },
|
||||
{ key: 'pay_promotion_tag_default', label: '促销标签', type: 'textarea', sanitize_type: 'html', default: '<i class="fa fa-fw fa-bolt"></i> 限时特惠', rows: 1 },
|
||||
{ key: 'vip_1_price_default', label: '一级会员价格', type: 'input', sanitize_type: 'money_decimal', default: '0', suffix: '元', help: '填0则为一级会员免费' },
|
||||
{ key: 'vip_2_price_default', label: '二级会员价格', type: 'input', sanitize_type: 'money_decimal', default: '0', suffix: '元', help: '填0则为二级会员免费' },
|
||||
{ key: 'pay_rebate_discount', label: '推广折扣', type: 'input', sanitize_type: 'money_decimal', default: '0', suffix: '元', help: '通过推广链接购买,额外优惠的金额;需开启推广返佣功能,且不能超过实际购买价' },
|
||||
{ key: 'pay_cuont_default', label: '销量浮动', type: 'between_number', default: { min: 0, max: 0 }, help: '为真实销量增加或减少的数量,系统会根据此处设置的区间获取随机值作为预置参数' },
|
||||
{ key: 'pay_details_default', label: '更多详情', type: 'textarea', sanitize_type: 'html', default: '', rows: 3, help: '可插入任意的HTML代码' },
|
||||
{ key: 'pay_extra_hide_default', label: '额外隐藏内容', type: 'textarea', sanitize_type: 'html', default: '', rows: 3, help: '可插入任意的HTML代码' },
|
||||
{
|
||||
key: 'pay_attributes_default',
|
||||
label: '资源属性',
|
||||
type: 'group',
|
||||
button_title: '添加属性',
|
||||
default: [],
|
||||
fields: [
|
||||
{ key: 'key', label: '属性名称', type: 'input', default: '' },
|
||||
{ key: 'value', label: '属性内容', type: 'input', default: '' },
|
||||
],
|
||||
},
|
||||
{ key: 'pay_gallery_show_default', label: '付费图片免费查看数量(张)', type: 'number', default: 1, min: 0, help: '设置付费图片可免费查看前几张图片的数量的默认值' },
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
export const orderSchema = {
|
||||
key: 'payment.order',
|
||||
title: '订单设置',
|
||||
icon: 'fa fas fa-file-alt',
|
||||
description: '配置订单前缀、有效期、展示字段和状态文案。',
|
||||
defaults: {
|
||||
payment_order_prefix: 'AKSRT',
|
||||
payment_flow_prefix: 'AK',
|
||||
payment_withdrawal_prefix: 'TX',
|
||||
payment_order_expiry: 30,
|
||||
pay_show_user: true,
|
||||
pay_show_paycount: true,
|
||||
pay_no_logged_in: true,
|
||||
pay_cookie_day: 15,
|
||||
pay_no_logged_remind: '您当前未登录!建议登陆后购买,可保存购买订单',
|
||||
pay_wait_float_s: true,
|
||||
payment_order_max_minutes: 30,
|
||||
pay_mark: '付费',
|
||||
pay_box_position: 'top',
|
||||
payment_show_ip: false,
|
||||
payment_show_device: false,
|
||||
payment_show_order_no: true,
|
||||
payment_show_create_time: true,
|
||||
payment_show_pay_time: true,
|
||||
payment_order_status_pending: '待支付',
|
||||
payment_order_status_paid: '已支付',
|
||||
payment_order_status_refunding: '退款中',
|
||||
payment_order_status_refunded: '已退款',
|
||||
payment_order_status_closed: '已关闭',
|
||||
pay_service: '',
|
||||
pay_service_icon: '',
|
||||
pay_service_value: '',
|
||||
pay_service_qq: '',
|
||||
pay_service_wechat: '',
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
key: 'basic',
|
||||
title: '订单基础',
|
||||
icon: 'fa fas fa-cog',
|
||||
fields: [
|
||||
{ key: 'payment_order_prefix', label: '订单前缀', type: 'input', default: 'AKSRT' },
|
||||
{ key: 'payment_flow_prefix', label: '流水号前缀', type: 'input', sanitize_type: 'business_prefix', default: 'AK', maxlength: 12, help: '仅允许字母、数字、下划线和短横线,最多 12 位。' },
|
||||
{ key: 'payment_withdrawal_prefix', label: '提现单号前缀', type: 'input', sanitize_type: 'business_prefix', default: 'TX', maxlength: 12, help: '仅允许字母、数字、下划线和短横线,最多 12 位。' },
|
||||
{ key: 'payment_order_expiry', label: '订单有效期(分钟)', type: 'number', default: 30, min: 5, max: 1440 },
|
||||
{ key: 'pay_show_user', label: '显示用户信息', type: 'switch', default: true },
|
||||
{ key: 'pay_show_paycount', label: '显示购买次数', type: 'switch', default: true },
|
||||
{ key: 'pay_no_logged_in', label: '未登录提示', type: 'switch', default: true },
|
||||
{ key: 'pay_cookie_day', label: 'Cookie有效期(天)', type: 'number', default: 15, min: 1, max: 31, help: '未登录购买 Cookie 有效时间,1-31 天' },
|
||||
{ key: 'pay_no_logged_remind', label: '未登录提醒文案', type: 'textarea', default: '您当前未登录!建议登陆后购买,可保存购买订单', help: '未登录用户查看付费内容时的提醒文案' },
|
||||
{ key: 'pay_wait_float_s', label: '订单等待浮动', type: 'switch', default: true },
|
||||
{ key: 'payment_order_max_minutes', label: '订单最大支付时间(分钟)', type: 'number', default: 30, min: 5, max: 1440 },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'display',
|
||||
title: '展示设置',
|
||||
icon: 'fa fas fa-eye',
|
||||
fields: [
|
||||
{ key: 'pay_mark', label: '付费标记', type: 'input', default: '付费' },
|
||||
{ key: 'pay_box_position', label: '付费框位置', type: 'radio', default: 'top', options: [{ label: '文章框顶部(封面后)', value: 'box_top' }, { label: '正文顶部', value: 'top' }, { label: '正文底部', value: 'bottom' }, { label: '文章框底部(相关前)', value: 'box_bottom' }] },
|
||||
{ key: 'payment_show_ip', label: '显示用户IP', type: 'switch', default: false },
|
||||
{ key: 'payment_show_device', label: '显示设备信息', type: 'switch', default: false },
|
||||
{ key: 'payment_show_order_no', label: '显示订单号', type: 'switch', default: true },
|
||||
{ key: 'payment_show_create_time', label: '显示创建时间', type: 'switch', default: true },
|
||||
{ key: 'payment_show_pay_time', label: '显示支付时间', type: 'switch', default: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
title: '状态设置',
|
||||
icon: 'fa fas fa-tags',
|
||||
fields: [
|
||||
{ key: 'payment_order_status_pending', label: '待支付状态文案', type: 'input', default: '待支付' },
|
||||
{ key: 'payment_order_status_paid', label: '已支付状态文案', type: 'input', default: '已支付' },
|
||||
{ key: 'payment_order_status_refunding', label: '退款中状态文案', type: 'input', default: '退款中' },
|
||||
{ key: 'payment_order_status_refunded', label: '已退款状态文案', type: 'input', default: '已退款' },
|
||||
{ key: 'payment_order_status_closed', label: '已关闭状态文案', type: 'input', default: '已关闭' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'service',
|
||||
title: '服务设置',
|
||||
icon: 'fa fas fa-headphones',
|
||||
fields: [
|
||||
{ key: 'pay_service', label: '客服设置', type: 'textarea', default: '' },
|
||||
{ key: 'pay_service_icon', label: '客服图标', type: 'icon', default: '' },
|
||||
{ key: 'pay_service_value', label: '客服内容', type: 'input', default: '' },
|
||||
{ key: 'pay_service_qq', label: 'QQ客服', type: 'input', default: '' },
|
||||
{ key: 'pay_service_wechat', label: '微信客服', type: 'media', default: '' },
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,24 +1,44 @@
|
||||
/**
|
||||
* 余额充值 — 参考主题结构:用户余额(含余额提现)、转账、充值商品选项、
|
||||
* 卡密充值、充值卡(原「充值卡」页并入)与余额兑换积分。
|
||||
*/
|
||||
export const rechargeSchema = {
|
||||
key: 'payment.recharge',
|
||||
title: '余额充值',
|
||||
icon: 'fa fas fa-bolt',
|
||||
description: '配置余额充值套餐、转账与积分兑换。',
|
||||
description: '配置余额功能、充值商品选项、卡密充值、充值卡规则与余额兑换积分。',
|
||||
defaults: {
|
||||
pay_balance_s: true,
|
||||
pay_balance_withdraw_s: false,
|
||||
pay_balance_desc: '',
|
||||
pay_balance_product: '30|30|入门包\n98|108|推荐包',
|
||||
pay_balance_product_name: '',
|
||||
pay_balance_price: 0,
|
||||
pay_balance_tag: '元',
|
||||
pay_balance_tag_class: '',
|
||||
pay_balance_product_custom_s: true,
|
||||
pay_balance_product_custom_limit: 9999,
|
||||
pay_balance_transfer_s: false,
|
||||
pay_balance_transfer_service_charge: 0,
|
||||
pay_balance_transfer_desc: '',
|
||||
pay_balance_product: [
|
||||
{ price: 50, pay_price: 0, tag: '', tag_class: '' },
|
||||
{ price: 100, pay_price: 98, tag: '', tag_class: '' },
|
||||
{ price: 200, pay_price: 188, tag: '推荐', tag_class: 'jb-cyan' },
|
||||
{ price: 500, pay_price: 468, tag: '特惠', tag_class: 'jb-pink' },
|
||||
],
|
||||
pay_balance_product_custom_s: true,
|
||||
pay_balance_product_custom_limit: 9999,
|
||||
pay_balance_pass_charge_s: false,
|
||||
pay_balance_pass_charge_desc: '',
|
||||
pay_balance_pass_charge_only_password: false,
|
||||
card_pass_enabled: false,
|
||||
card_pass_desc: '',
|
||||
card_pass_type: 'balance',
|
||||
card_pass_prefix: 'AK',
|
||||
card_pass_length: 16,
|
||||
card_pass_batch_size: 50,
|
||||
card_pass_max_redeem: 1,
|
||||
card_pass_expiry: 0,
|
||||
card_pass_amount: '10.00',
|
||||
card_pass_vip_days: 30,
|
||||
card_pass_credits: 0,
|
||||
card_pass_redeem_page: '',
|
||||
card_pass_success_msg: '兑换成功!',
|
||||
card_pass_fail_msg: '卡号无效或已过期',
|
||||
pay_balance_points_exchange_s: false,
|
||||
pay_balance_points_exchange_rate: 100,
|
||||
pay_balance_points_exchange_min_points: 100,
|
||||
@@ -26,34 +46,28 @@ export const rechargeSchema = {
|
||||
pay_balance_points_exchange_desc: '',
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
key: 'notice',
|
||||
title: '说明',
|
||||
icon: 'fa fas fa-info-circle',
|
||||
fields: [
|
||||
{
|
||||
key: 'recharge_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
content: '<p><b>用户余额功能:</b></p><ul style="margin:6px 0 0 18px;line-height:1.9"><li>开启后用户可充值到余额,余额可用于全站消费</li><li>在下方设置默认的分成比例,同时支持单独为每一个用户设置独立的分成比例</li><li>可以在用户管理中,为某一个用户手动赠送余额</li></ul>',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'basic',
|
||||
title: '充值开关',
|
||||
title: '用户余额',
|
||||
icon: 'fa fas fa-credit-card',
|
||||
fields: [
|
||||
{ key: 'pay_balance_s', label: '启用余额功能', type: 'switch', default: true, help: '开启后用户中心显示现金钱包,并默认允许余额支付(可在收款接口单独关闭)。充值需另配支付宝/微信等外部网关。' },
|
||||
{ key: 'pay_balance_desc', label: '充值说明', type: 'textarea', default: '', dependsOn: { key: 'pay_balance_s', value: true } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'packages',
|
||||
title: '充值套餐',
|
||||
icon: 'fa fas fa-folder-open',
|
||||
fields: [
|
||||
{ key: 'pay_balance_product', label: '套餐列表', type: 'textarea', default: '30|30|入门包\n98|108|推荐包', help: '每行一个套餐:支付金额|到账金额|标题', dependsOn: { key: 'pay_balance_s', value: true } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'product',
|
||||
title: '充值产品',
|
||||
icon: 'fa fas fa-archive',
|
||||
fields: [
|
||||
{ key: 'pay_balance_product_name', label: '充值产品', type: 'input', default: '', dependsOn: { key: 'pay_balance_s', value: true } },
|
||||
{ key: 'pay_balance_price', label: '充值价格', type: 'number', default: 0, min: 0, max: 99999, dependsOn: { key: 'pay_balance_s', value: true } },
|
||||
{ key: 'pay_balance_tag', label: '价格标签', type: 'input', default: '元', dependsOn: { key: 'pay_balance_s', value: true } },
|
||||
{ key: 'pay_balance_tag_class', label: '标签样式', type: 'input', default: '', dependsOn: { key: 'pay_balance_s', value: true } },
|
||||
{ key: 'pay_balance_product_custom_s', label: '自定义金额', type: 'switch', default: true, dependsOn: { key: 'pay_balance_s', value: true } },
|
||||
{ key: 'pay_balance_product_custom_limit', label: '自定义金额限制', type: 'number', default: 9999, min: 1, max: 99999, dependsOn: { key: 'pay_balance_s', value: true } },
|
||||
{ key: 'pay_balance_s', label: '启用余额充值/支付功能', type: 'switch', default: true, help: '开启后用户中心显示现金钱包,并可作为收银台支付方式' },
|
||||
{ key: 'pay_balance_withdraw_s', label: '余额提现', type: 'switch', default: false, dependsOn: { key: 'pay_balance_s', value: true }, help: '允许将余额提现' },
|
||||
{ key: 'pay_balance_desc', label: '充值说明', type: 'textarea', default: '', rows: 3, dependsOn: { key: 'pay_balance_s', value: true }, help: '用户充值时显示的内容,建议为提醒事项、充值协议等,支持HTML代码' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -61,23 +75,83 @@ export const rechargeSchema = {
|
||||
title: '余额转账',
|
||||
icon: 'fa fas fa-exchange-alt',
|
||||
fields: [
|
||||
{ key: 'pay_balance_transfer_s', label: '启用余额转账', type: 'switch', default: false },
|
||||
{ key: 'pay_balance_transfer_s', label: '启用余额转账', type: 'switch', default: false, help: '允许用户转账给其他用户' },
|
||||
{ key: 'pay_balance_transfer_service_charge', label: '转账手续费(%)', type: 'number', default: 0, min: 0, max: 50, dependsOn: { key: 'pay_balance_transfer_s', value: true } },
|
||||
{ key: 'pay_balance_transfer_desc', label: '转账说明', type: 'textarea', default: '', dependsOn: { key: 'pay_balance_transfer_s', value: true } },
|
||||
{ key: 'pay_balance_transfer_desc', label: '转账说明', type: 'textarea', default: '', rows: 2, dependsOn: { key: 'pay_balance_transfer_s', value: true }, help: '用户转账时候显示的内容,建议为提醒事项、转账协议等,支持HTML代码' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'products',
|
||||
title: '充值商品选项',
|
||||
icon: 'fa fas fa-archive',
|
||||
fields: [
|
||||
{
|
||||
key: 'pay_balance_product',
|
||||
label: '充值金额选项',
|
||||
type: 'group',
|
||||
button_title: '添加充值金额选项',
|
||||
default: [
|
||||
{ price: 50, pay_price: 0, tag: '', tag_class: '' },
|
||||
{ price: 100, pay_price: 98, tag: '', tag_class: '' },
|
||||
{ price: 200, pay_price: 188, tag: '推荐', tag_class: 'jb-cyan' },
|
||||
{ price: 500, pay_price: 468, tag: '特惠', tag_class: 'jb-pink' },
|
||||
],
|
||||
help: '用户充值时候,可快速选择的充值金额,同时支持设置折扣',
|
||||
fields: [
|
||||
{ key: 'price', label: '充值金额', type: 'number', default: 100, min: 0, suffix: '元' },
|
||||
{ key: 'pay_price', label: '销售金额', type: 'number', default: 0, min: 0, suffix: '元', help: '选填,设置不同的销售金额实现充值折扣的功能,设置为0则与充值金额一致' },
|
||||
{ key: 'tag', label: '小标签', type: 'input', default: '', help: '支持HTML,请注意控制长度' },
|
||||
{ key: 'tag_class', label: '标签颜色类', type: 'input', default: '', help: '主题标签颜色类,如 jb-cyan / jb-pink' },
|
||||
],
|
||||
},
|
||||
{ key: 'pay_balance_product_custom_s', label: '自定义充值金额', type: 'switch', default: true, help: '允许用户手动输入充值金额' },
|
||||
{ key: 'pay_balance_product_custom_limit', label: '自定义充值金额上限', type: 'number', default: 9999, min: 1, max: 99999, dependsOn: { key: 'pay_balance_product_custom_s', value: true } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'pass-charge',
|
||||
title: '充值卡免扣',
|
||||
title: '卡密充值',
|
||||
icon: 'fa fas fa-ticket-alt',
|
||||
fields: [
|
||||
{ key: 'pay_balance_pass_charge_s', label: '启用余额卡密兑换', type: 'switch', default: false },
|
||||
{ key: 'pay_balance_pass_charge_desc', label: '卡密兑换说明', type: 'textarea', default: '', dependsOn: { key: 'pay_balance_pass_charge_s', value: true } },
|
||||
{ key: 'pay_balance_pass_charge_only_password', label: '仅密码兑换', type: 'switch', default: false, dependsOn: { key: 'pay_balance_pass_charge_s', value: true } },
|
||||
{ key: 'pay_balance_pass_charge_s', label: '启用卡密充值余额功能', type: 'switch', default: false },
|
||||
{ key: 'pay_balance_pass_charge_desc', label: '卡密充值说明', type: 'textarea', default: '', rows: 2, dependsOn: { key: 'pay_balance_pass_charge_s', value: true }, help: '用户使用卡密充值时显示的内容,建议为提醒事项、充值协议等,支持HTML代码' },
|
||||
{ key: 'pay_balance_pass_charge_only_password', label: '单密码模式', type: 'switch', default: false, dependsOn: { key: 'pay_balance_pass_charge_s', value: true }, help: '仅需输入密码,不需要卡号' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'exchange',
|
||||
key: 'charge-card',
|
||||
title: '充值卡',
|
||||
icon: 'fa fas fa-id-card',
|
||||
fields: [
|
||||
{ key: 'card_pass_enabled', label: '启用充值卡', type: 'switch', default: false, help: '充值卡规则与批量发卡配置;卡密兑换入口由上方「卡密充值」控制' },
|
||||
{ key: 'card_pass_desc', label: '充值卡说明', type: 'textarea', default: '', dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{
|
||||
key: 'card_pass_type',
|
||||
label: '充值卡类型',
|
||||
type: 'select',
|
||||
default: 'balance',
|
||||
dependsOn: { key: 'card_pass_enabled', value: true },
|
||||
options: [
|
||||
{ label: '余额卡', value: 'balance' },
|
||||
{ label: 'VIP卡', value: 'vip' },
|
||||
{ label: '积分卡', value: 'credits' },
|
||||
],
|
||||
},
|
||||
{ key: 'card_pass_prefix', label: '卡号前缀', type: 'input', default: 'AK', dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_length', label: '卡号长度', type: 'number', default: 16, min: 8, max: 32, dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_batch_size', label: '批量生成数量', type: 'number', default: 50, min: 1, max: 500, dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_max_redeem', label: '单卡最大兑换次数', type: 'number', default: 1, min: 1, max: 100, dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_expiry', label: '有效期(天)', type: 'number', default: 0, min: 0, help: '0为永久有效', dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_amount', label: '面额(元)', type: 'input', sanitize_type: 'money_decimal', default: '10.00', min: 1, max: 9999, dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_vip_days', label: 'VIP天数', type: 'number', default: 30, min: 1, max: 3650, dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_credits', label: '赠送积分', type: 'number', default: 0, min: 0, max: 99999, dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_redeem_page', label: '兑换页面', type: 'input', default: '', dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_success_msg', label: '兑换成功提示', type: 'input', default: '兑换成功!', dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
{ key: 'card_pass_fail_msg', label: '兑换失败提示', type: 'input', default: '卡号无效或已过期', dependsOn: { key: 'card_pass_enabled', value: true } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'points-exchange',
|
||||
title: '余额兑换积分',
|
||||
icon: 'fa fas fa-database',
|
||||
fields: [
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/**
|
||||
* 创作分成 — 现金/积分分成比例 + 创作分成说明与详情页配置。
|
||||
*/
|
||||
export const revenueSchema = {
|
||||
key: 'payment.revenue',
|
||||
title: '创作中心',
|
||||
title: '创作分成',
|
||||
icon: 'fa fas fa-chart-line',
|
||||
description: '分别配置现金收益和积分收益的会员分成比例,结算周期结束后自动入账。',
|
||||
defaults: {
|
||||
@@ -13,8 +16,25 @@ export const revenueSchema = {
|
||||
payment_revenue_points_ratio_user: 0,
|
||||
payment_revenue_points_ratio_vip_1: 0,
|
||||
payment_revenue_points_ratio_vip_2: 0,
|
||||
pay_income_desc_details: '<p>在本站发布付费内容,获得的收益将参与收入分成</p><p>普通用户分成比例为:60%</p><p>一级会员分成比例为:70%</p><p>二级会员分成比例为:80%</p><p>分成比例的计算方式为:(实收现金-推广佣金)x 分成比例</p><p>申请提现后,需后台人工处理,一般2-3小时,请耐心等待</p>',
|
||||
pay_income_desc_page_s: false,
|
||||
pay_income_desc_page: 0,
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
key: 'notice',
|
||||
title: '说明',
|
||||
icon: 'fa fas fa-info-circle',
|
||||
fields: [
|
||||
{
|
||||
key: 'revenue_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
content: '<ul style="margin:0 0 0 18px;line-height:1.9"><li>开启创作分成后,用户发布的付费内容则按设置的比例收入与用户分成</li><li>在下方设置默认的分成比例,同时支持单独为每一个用户设置独立的分成比例</li><li>用户分成比例是按照订单实收现金并扣除推广佣金后,再按设置的比例分成</li><li>禁用此功能后,所有收款将不会与作者分成</li></ul>',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'basic',
|
||||
title: '创作中心设置',
|
||||
@@ -30,7 +50,7 @@ export const revenueSchema = {
|
||||
title: '现金收益分成',
|
||||
icon: 'fa fas fa-credit-card',
|
||||
fields: [
|
||||
{ key: 'payment_revenue_cash_ratio_user', label: '普通用户分成比例(%)', type: 'number', default: 60, min: 0, max: 100, dependsOn: { key: 'pay_income_s', value: true } },
|
||||
{ key: 'payment_revenue_cash_ratio_user', label: '普通用户分成比例(%)', type: 'number', default: 60, min: 0, max: 100, dependsOn: { key: 'pay_income_s', value: true }, help: '为0则不参与分成' },
|
||||
{ key: 'payment_revenue_cash_ratio_vip_1', label: '一级会员分成比例(%)', type: 'number', default: 70, min: 0, max: 100, dependsOn: { key: 'pay_income_s', value: true } },
|
||||
{ key: 'payment_revenue_cash_ratio_vip_2', label: '二级会员分成比例(%)', type: 'number', default: 80, min: 0, max: 100, dependsOn: { key: 'pay_income_s', value: true } },
|
||||
],
|
||||
@@ -40,10 +60,20 @@ export const revenueSchema = {
|
||||
title: '积分收益分成',
|
||||
icon: 'fa fas fa-database',
|
||||
fields: [
|
||||
{ key: 'payment_revenue_points_ratio_user', label: '普通用户分成比例(%)', type: 'number', default: 0, min: 0, max: 100, dependsOn: { key: 'pay_income_s', value: true } },
|
||||
{ key: 'payment_revenue_points_ratio_user', label: '普通用户分成比例(%)', type: 'number', default: 0, min: 0, max: 100, dependsOn: { key: 'pay_income_s', value: true }, help: '为0则不参与分成(用户采用积分支付的订单给与作者的分成比例)' },
|
||||
{ key: 'payment_revenue_points_ratio_vip_1', label: '一级会员分成比例(%)', type: 'number', default: 0, min: 0, max: 100, dependsOn: { key: 'pay_income_s', value: true } },
|
||||
{ key: 'payment_revenue_points_ratio_vip_2', label: '二级会员分成比例(%)', type: 'number', default: 0, min: 0, max: 100, dependsOn: { key: 'pay_income_s', value: true } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'detail',
|
||||
title: '分成说明',
|
||||
icon: 'fa fas fa-file-alt',
|
||||
fields: [
|
||||
{ key: 'pay_income_desc_details', label: '创作分成说明', type: 'textarea', sanitize_type: 'html', default: '<p>在本站发布付费内容,获得的收益将参与收入分成</p><p>普通用户分成比例为:60%</p><p>一级会员分成比例为:70%</p><p>二级会员分成比例为:80%</p><p>分成比例的计算方式为:(实收现金-推广佣金)x 分成比例</p><p>申请提现后,需后台人工处理,一般2-3小时,请耐心等待</p>', rows: 8, dependsOn: { key: 'pay_income_s', value: true }, help: '显示在用户中心的说明,建议为发布协议或者其他说明(必填项,内容不易太少),支持HTML代码' },
|
||||
{ key: 'pay_income_desc_page_s', label: '创作分成详情页面', type: 'switch', default: false, dependsOn: { key: 'pay_income_s', value: true }, help: '您可以新建一个页面写入更加详细的创作分成说明及协议内容,再开启此项并选择对应的页面' },
|
||||
{ key: 'pay_income_desc_page', label: '选择页面', type: 'page_select', default: 0, dependsOn: { key: 'pay_income_desc_page_s', value: true } },
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
/**
|
||||
* VIP会员 — 参考主题结构:会员等级、会员权益(普通/一级/二级 tab)、
|
||||
* 会员购买选项(一级/二级/续费/升级 tab)、卡密兑换与积分兑换。
|
||||
*/
|
||||
export const vipSchema = {
|
||||
key: 'payment.vip',
|
||||
title: 'VIP会员',
|
||||
icon: 'fa fas fa-gem',
|
||||
description: '对齐 legacy 双级会员:分档套餐、权益、续费/升级与积分兑换。',
|
||||
description: '双级会员:等级名称、权益(下载次数与限速)、套餐、续费/升级与积分兑换。',
|
||||
defaults: {
|
||||
nav_pay_vip: true,
|
||||
nav_user_pay_vip: true,
|
||||
@@ -10,27 +14,24 @@ export const vipSchema = {
|
||||
pay_user_vip_1_name: '一级会员',
|
||||
pay_user_vip_1_desc: '尊享一级会员特权',
|
||||
pay_user_vip_1_equity: '<li>免费下载部分资源</li><li>会员专属身份标识</li><li>付费内容会员价</li>',
|
||||
vip_1_product_s: true,
|
||||
payment_vip_1_products: '30|59|1|month|限时|月卡\n98|199|3|month|特惠|季卡\n365|599|12|month|超值|年卡\n999|1299|0|day|永久|永久卡',
|
||||
vip_1_img_icon: '',
|
||||
pay_user_vip_2_s: true,
|
||||
pay_user_vip_2_name: '二级会员',
|
||||
pay_user_vip_2_desc: '尊享二级会员特权',
|
||||
pay_user_vip_2_equity: '<li>更高免费下载额度</li><li>更高等级身份标识</li><li>更多付费内容会员价</li>',
|
||||
vip_2_product_s: true,
|
||||
payment_vip_2_products: '58|99|1|month|限时|月卡\n168|299|3|month|特惠|季卡\n598|999|12|month|超值|年卡\n1299|1999|0|day|永久|永久卡',
|
||||
vip_2_img_icon: '',
|
||||
pay_user_vip_desc: '',
|
||||
pay_user_vip_more: '',
|
||||
pay_download_limit: 0,
|
||||
pay_download_speed: 0,
|
||||
pay_download_limit_vip_1: 0,
|
||||
pay_download_speed_vip_1: 0,
|
||||
pay_download_limit_vip_2: 0,
|
||||
pay_vip_pass_charge_s: false,
|
||||
pay_vip_pass_charge_desc: '',
|
||||
pay_vip_pass_charge_only_password: true,
|
||||
pay_vip_points_exchange_s: false,
|
||||
pay_vip_points_exchange_desc: '',
|
||||
payment_vip_points_packages: '1|500|30|积分月卡\n1|5000|0|积分永久',
|
||||
pay_download_speed_vip_2: 0,
|
||||
vip_1_product_s: true,
|
||||
payment_vip_1_products: '30|59|1|month|限时|月卡\n98|199|3|month|特惠|季卡\n365|599|12|month|超值|年卡\n999|1299|0|day|永久|永久卡',
|
||||
vip_2_product_s: true,
|
||||
payment_vip_2_products: '58|99|1|month|限时|月卡\n168|299|3|month|特惠|季卡\n598|999|12|month|超值|年卡\n1299|1999|0|day|永久|永久卡',
|
||||
vip_renew: true,
|
||||
vip_renew_desc: '',
|
||||
vip_renew_price_type: 'discount',
|
||||
@@ -39,8 +40,28 @@ export const vipSchema = {
|
||||
vip_upgrade: true,
|
||||
vip_upgrade_desc: '',
|
||||
vip_upgrade_product: 'unit|1|按剩余天数升级\njump|199|升级永久二级会员\npermanent|299|永久一级升永久二级',
|
||||
pay_vip_pass_charge_s: false,
|
||||
pay_vip_pass_charge_desc: '',
|
||||
pay_vip_pass_charge_only_password: true,
|
||||
pay_vip_points_exchange_s: false,
|
||||
payment_vip_points_packages: '1|500|30|积分月卡\n1|5000|0|积分永久',
|
||||
pay_vip_points_exchange_desc: '',
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
key: 'notice',
|
||||
title: '说明',
|
||||
icon: 'fa fas fa-info-circle',
|
||||
fields: [
|
||||
{
|
||||
key: 'vip_notice',
|
||||
label: '',
|
||||
type: 'content',
|
||||
ui_only: true,
|
||||
content: '<ul style="margin:0 0 0 18px;line-height:1.9"><li>开启付费会员功能之前,请先配置好收款接口,确保网站收款正常</li><li>会员功能的设置项目较多,请仔细核对,避免出现价格、时间的问题</li><li>配合设置名称、有效期等可搭配出不同类型的会员</li><li>管理员可在后台为用户单独开启会员</li></ul>',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'basic',
|
||||
title: '会员等级',
|
||||
@@ -49,12 +70,12 @@ export const vipSchema = {
|
||||
{ key: 'nav_pay_vip', label: '导航栏购买按钮', type: 'switch', default: true, help: '在顶部导航栏显示开通会员按钮;请注意顶部导航的整体宽度。' },
|
||||
{ key: 'nav_user_pay_vip', label: '用户框购买按钮', type: 'switch', default: true, help: '在导航栏用户框内显示开通会员按钮。' },
|
||||
{ key: 'pay_user_vip_1_s', label: '启用一级会员', type: 'switch', default: true },
|
||||
{ key: 'pay_user_vip_1_name', label: '一级会员名称', type: 'input', default: '一级会员', dependsOn: { key: 'pay_user_vip_1_s', value: true } },
|
||||
{ key: 'pay_user_vip_1_name', label: '一级会员显示名称', type: 'input', default: '一级会员', dependsOn: { key: 'pay_user_vip_1_s', value: true } },
|
||||
{ key: 'pay_user_vip_1_desc', label: '一级会员一句话简介', type: 'input', default: '尊享一级会员特权', dependsOn: { key: 'pay_user_vip_1_s', value: true } },
|
||||
{ key: 'pay_user_vip_1_equity', label: '一级会员权益 HTML', type: 'textarea', default: '<li>免费下载部分资源</li><li>会员专属身份标识</li><li>付费内容会员价</li>', dependsOn: { key: 'pay_user_vip_1_s', value: true }, help: '建议使用 <li> 列表项' },
|
||||
{ key: 'vip_1_img_icon', label: '一级会员图标 URL', type: 'input', default: '', dependsOn: { key: 'pay_user_vip_1_s', value: true }, help: '留空使用内置 vip-1.svg 图标' },
|
||||
{ key: 'pay_user_vip_2_s', label: '启用二级会员', type: 'switch', default: true },
|
||||
{ key: 'pay_user_vip_2_name', label: '二级会员名称', type: 'input', default: '二级会员', dependsOn: { key: 'pay_user_vip_2_s', value: true } },
|
||||
{ key: 'pay_user_vip_2_name', label: '二级会员显示名称', type: 'input', default: '二级会员', dependsOn: { key: 'pay_user_vip_2_s', value: true } },
|
||||
{ key: 'pay_user_vip_2_desc', label: '二级会员一句话简介', type: 'input', default: '尊享二级会员特权', dependsOn: { key: 'pay_user_vip_2_s', value: true } },
|
||||
{ key: 'pay_user_vip_2_equity', label: '二级会员权益 HTML', type: 'textarea', default: '<li>更高免费下载额度</li><li>更高等级身份标识</li><li>更多付费内容会员价</li>', dependsOn: { key: 'pay_user_vip_2_s', value: true } },
|
||||
{ key: 'vip_2_img_icon', label: '二级会员图标 URL', type: 'input', default: '', dependsOn: { key: 'pay_user_vip_2_s', value: true }, help: '留空使用内置 vip-2.svg 图标' },
|
||||
@@ -63,32 +84,137 @@ export const vipSchema = {
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'packages',
|
||||
title: '分档套餐',
|
||||
icon: 'fa fas fa-gem',
|
||||
key: 'benefit',
|
||||
title: '会员权益',
|
||||
icon: 'fa fas fa-gift',
|
||||
fields: [
|
||||
{ key: 'vip_1_product_s', label: '允许购买一级会员', type: 'switch', default: true, dependsOn: { key: 'pay_user_vip_1_s', value: true } },
|
||||
{
|
||||
key: 'payment_vip_1_products',
|
||||
label: '一级会员套餐',
|
||||
type: 'textarea',
|
||||
default: '30|59|1|month|限时|月卡\n98|199|3|month|特惠|季卡\n365|599|12|month|超值|年卡\n999|1299|0|day|永久|永久卡',
|
||||
dependsOn: { key: 'vip_1_product_s', value: true },
|
||||
help: '每行:执行价|原价|时长|单位(day|month)|标签|标题;时长 0=永久',
|
||||
},
|
||||
{ key: 'vip_2_product_s', label: '允许购买二级会员', type: 'switch', default: true, dependsOn: { key: 'pay_user_vip_2_s', value: true } },
|
||||
{
|
||||
key: 'payment_vip_2_products',
|
||||
label: '二级会员套餐',
|
||||
type: 'textarea',
|
||||
default: '58|99|1|month|限时|月卡\n168|299|3|month|特惠|季卡\n598|999|12|month|超值|年卡\n1299|1999|0|day|永久|永久卡',
|
||||
dependsOn: { key: 'vip_2_product_s', value: true },
|
||||
help: '每行:执行价|原价|时长|单位(day|month)|标签|标题;时长 0=永久',
|
||||
key: 'vip_benefit',
|
||||
label: '会员权益',
|
||||
type: 'tabbed',
|
||||
tabs: [
|
||||
{
|
||||
key: 'ordinary',
|
||||
title: '普通用户',
|
||||
fields: [
|
||||
{ key: 'pay_download_limit', label: '付费资源:免费资源每日可下载(个)', type: 'number', default: 0, min: 0, max: 9999, help: '普通用户每日最多可下载几个免费资源(为0则不限制)' },
|
||||
{ key: 'pay_download_speed', label: '付费资源:下载限速(KB/s)', type: 'number', default: 0, min: 0, help: '付费下载限制最大下载速度(为0则不限制);只对本地服务器文件有效' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'vip1',
|
||||
title: '一级会员',
|
||||
fields: [
|
||||
{ key: 'pay_download_limit_vip_1', label: '付费资源:免费资源每日可下载(个)', type: 'number', default: 0, min: 0, max: 9999, dependsOn: { key: 'pay_user_vip_1_s', value: true }, help: '一级会员每日最多可下载几个免费资源(为0则不限制)' },
|
||||
{ key: 'pay_download_speed_vip_1', label: '付费资源:下载限速(KB/s)', type: 'number', default: 0, min: 0, dependsOn: { key: 'pay_user_vip_1_s', value: true }, help: '付费下载限制最大下载速度(为0则不限制);只对本地服务器文件有效' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'vip2',
|
||||
title: '二级会员',
|
||||
fields: [
|
||||
{ key: 'pay_download_limit_vip_2', label: '付费资源:免费资源每日可下载(个)', type: 'number', default: 0, min: 0, max: 9999, dependsOn: { key: 'pay_user_vip_2_s', value: true }, help: '二级会员每日最多可下载几个免费资源(为0则不限制)' },
|
||||
{ key: 'pay_download_speed_vip_2', label: '付费资源:下载限速(KB/s)', type: 'number', default: 0, min: 0, dependsOn: { key: 'pay_user_vip_2_s', value: true }, help: '付费下载限制最大下载速度(为0则不限制);只对本地服务器文件有效' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'points_exchange',
|
||||
key: 'opt',
|
||||
title: '会员购买选项',
|
||||
icon: 'fa fas fa-shopping-cart',
|
||||
fields: [
|
||||
{
|
||||
key: 'vip_opt',
|
||||
label: '会员购买选项',
|
||||
type: 'tabbed',
|
||||
tabs: [
|
||||
{
|
||||
key: 'vip1',
|
||||
title: '一级会员',
|
||||
fields: [
|
||||
{ key: 'vip_1_product_s', label: '允许购买一级会员', type: 'switch', default: true, dependsOn: { key: 'pay_user_vip_1_s', value: true } },
|
||||
{
|
||||
key: 'payment_vip_1_products',
|
||||
label: '一级会员套餐',
|
||||
type: 'textarea',
|
||||
default: '30|59|1|month|限时|月卡\n98|199|3|month|特惠|季卡\n365|599|12|month|超值|年卡\n999|1299|0|day|永久|永久卡',
|
||||
dependsOn: { key: 'vip_1_product_s', value: true },
|
||||
help: '每行:执行价|原价|时长|单位(day|month)|标签|标题;时长 0=永久',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'vip2',
|
||||
title: '二级会员',
|
||||
fields: [
|
||||
{ key: 'vip_2_product_s', label: '允许购买二级会员', type: 'switch', default: true, dependsOn: { key: 'pay_user_vip_2_s', value: true } },
|
||||
{
|
||||
key: 'payment_vip_2_products',
|
||||
label: '二级会员套餐',
|
||||
type: 'textarea',
|
||||
default: '58|99|1|month|限时|月卡\n168|299|3|month|特惠|季卡\n598|999|12|month|超值|年卡\n1299|1999|0|day|永久|永久卡',
|
||||
dependsOn: { key: 'vip_2_product_s', value: true },
|
||||
help: '每行:执行价|原价|时长|单位(day|month)|标签|标题;时长 0=永久',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'renew',
|
||||
title: '会员续费',
|
||||
fields: [
|
||||
{ key: 'vip_renew', label: '允许续费', type: 'switch', default: true, help: '关闭后则不能续费,只能会员到期之后再购买。永久会员不需要续费' },
|
||||
{ key: 'vip_renew_desc', label: '续费说明', type: 'textarea', default: '', dependsOn: { key: 'vip_renew', value: true } },
|
||||
{
|
||||
key: 'vip_renew_price_type',
|
||||
label: '续费价格类型',
|
||||
type: 'radio',
|
||||
default: 'discount',
|
||||
dependsOn: { key: 'vip_renew', value: true },
|
||||
options: [
|
||||
{ label: '相同价格', value: 'same' },
|
||||
{ label: '折扣价格', value: 'discount' },
|
||||
{ label: '优惠立减', value: 'reduce' },
|
||||
],
|
||||
},
|
||||
{ key: 'vip_renew_discount', label: '续费折扣(折,如 8=八折)', type: 'number', default: 8, min: 1, max: 100, dependsOn: { key: 'vip_renew_price_type', value: 'discount' } },
|
||||
{ key: 'vip_renew_reduce', label: '续费减免金额', type: 'input', sanitize_type: 'money_decimal', default: '20.00', min: 0, max: 9999, dependsOn: { key: 'vip_renew_price_type', value: 'reduce' } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'upgrade',
|
||||
title: '会员升级',
|
||||
fields: [
|
||||
{ key: 'vip_upgrade', label: '允许升级', type: 'switch', default: true },
|
||||
{ key: 'vip_upgrade_desc', label: '升级说明', type: 'textarea', default: '', dependsOn: { key: 'vip_upgrade', value: true } },
|
||||
{
|
||||
key: 'vip_upgrade_product',
|
||||
label: '升级产品',
|
||||
type: 'textarea',
|
||||
default: 'unit|1|按剩余天数升级\njump|199|升级永久二级会员\npermanent|299|永久一级升永久二级',
|
||||
dependsOn: { key: 'vip_upgrade', value: true },
|
||||
help: '每行一条。永久VIP1升级:permanent|价格|标题;按剩余天:unit|每日单价|标题;限时跳转永久:jump|价格|标题',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'pass-charge',
|
||||
title: '卡密兑换会员',
|
||||
icon: 'fa fas fa-ticket-alt',
|
||||
fields: [
|
||||
{ key: 'pay_vip_pass_charge_s', label: '启用卡密兑换会员功能', type: 'switch', default: false },
|
||||
{ key: 'pay_vip_pass_charge_desc', label: '卡密兑换说明', type: 'textarea', default: '', dependsOn: { key: 'pay_vip_pass_charge_s', value: true }, help: '用户使用卡密兑换时显示的内容,建议为提醒事项、充值协议等,支持HTML代码' },
|
||||
{ key: 'pay_vip_pass_charge_only_password', label: '单密码模式', type: 'switch', default: true, dependsOn: { key: 'pay_vip_pass_charge_s', value: true }, help: '仅需输入密码,不需要卡号' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'points-exchange',
|
||||
title: '积分兑换会员',
|
||||
icon: 'fa fas fa-database',
|
||||
fields: [
|
||||
@@ -104,47 +230,5 @@ export const vipSchema = {
|
||||
{ key: 'pay_vip_points_exchange_desc', label: '兑换说明', type: 'textarea', default: '', dependsOn: { key: 'pay_vip_points_exchange_s', value: true } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'benefit',
|
||||
title: '下载权益',
|
||||
icon: 'fa fas fa-gift',
|
||||
fields: [
|
||||
{ key: 'pay_download_limit', label: '普通用户下载限制', type: 'number', default: 0, min: 0, max: 9999, help: '0为不限制' },
|
||||
{ key: 'pay_download_limit_vip_1', label: '一级会员下载限制', type: 'number', default: 0, min: 0, max: 9999, dependsOn: { key: 'pay_user_vip_1_s', value: true } },
|
||||
{ key: 'pay_download_limit_vip_2', label: '二级会员下载限制', type: 'number', default: 0, min: 0, max: 9999, dependsOn: { key: 'pay_user_vip_2_s', value: true } },
|
||||
{ key: 'pay_vip_pass_charge_s', label: '会员卡密兑换', type: 'switch', default: false },
|
||||
{ key: 'pay_vip_pass_charge_desc', label: '卡密兑换说明', type: 'textarea', default: '', dependsOn: { key: 'pay_vip_pass_charge_s', value: true } },
|
||||
{ key: 'pay_vip_pass_charge_only_password', label: '仅密码兑换', type: 'switch', default: true, dependsOn: { key: 'pay_vip_pass_charge_s', value: true }, help: '开启后只需输入卡密;关闭则需卡号+卡密。' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'renew',
|
||||
title: '续费设置',
|
||||
icon: 'fa fas fa-sync-alt',
|
||||
fields: [
|
||||
{ key: 'vip_renew', label: '允许续费', type: 'switch', default: true },
|
||||
{ key: 'vip_renew_desc', label: '续费说明', type: 'textarea', default: '', dependsOn: { key: 'vip_renew', value: true } },
|
||||
{ key: 'vip_renew_price_type', label: '续费价格类型', type: 'radio', default: 'discount', options: [{ label: '相同价格', value: 'same' }, { label: '折扣价格', value: 'discount' }, { label: '优惠立减', value: 'reduce' }], dependsOn: { key: 'vip_renew', value: true } },
|
||||
{ key: 'vip_renew_discount', label: '续费折扣(折,如 8=八折)', type: 'number', default: 8, min: 1, max: 100, dependsOn: { key: 'vip_renew_price_type', value: 'discount' } },
|
||||
{ key: 'vip_renew_reduce', label: '续费减免金额', type: 'input', sanitize_type: 'money_decimal', default: '20.00', min: 0, max: 9999, dependsOn: { key: 'vip_renew_price_type', value: 'reduce' } },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'upgrade',
|
||||
title: '升级设置',
|
||||
icon: 'fa fas fa-arrow-up',
|
||||
fields: [
|
||||
{ key: 'vip_upgrade', label: '允许升级', type: 'switch', default: true },
|
||||
{ key: 'vip_upgrade_desc', label: '升级说明', type: 'textarea', default: '', dependsOn: { key: 'vip_upgrade', value: true } },
|
||||
{
|
||||
key: 'vip_upgrade_product',
|
||||
label: '升级产品',
|
||||
type: 'textarea',
|
||||
default: 'unit|1|按剩余天数升级\njump|199|升级永久二级会员\npermanent|299|永久一级升永久二级',
|
||||
dependsOn: { key: 'vip_upgrade', value: true },
|
||||
help: '每行一条。永久VIP1升级:permanent|价格|标题;按剩余天:unit|每日单价|标题;限时跳转永久:jump|价格|标题',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,29 +1,55 @@
|
||||
/**
|
||||
* 提现策略 — 提现门槛、每日限制、收款渠道与提现协议。
|
||||
*/
|
||||
export const withdrawalSchema = {
|
||||
key: 'payment.withdrawal',
|
||||
title: '提现策略',
|
||||
icon: 'fa fas fa-money-bill',
|
||||
description: '配置提现门槛、手续费比例与每日限制。',
|
||||
description: '配置提现门槛、手续费比例、每日限制、收款渠道与提现协议。',
|
||||
defaults: {
|
||||
affiliate_min_withdrawal: '50.00',
|
||||
affiliate_min_withdrawal: '50.00',
|
||||
affiliate_withdrawal_fee: 5,
|
||||
affiliate_daily_withdrawal_count: 3,
|
||||
affiliate_daily_withdrawal_limit: '500.00',
|
||||
affiliate_daily_withdrawal_limit: '500.00',
|
||||
affiliate_withdrawal_reset_time: '00:00',
|
||||
affiliate_payout_channels: ['alipay', 'wechat'],
|
||||
pay_rebate_withdraw_text_details: '<div>可提现金额达到50元后即可发起提现</div><div>申请提现需后台人工处理,一般2-3小时,请耐心等待</div><div>如有其它疑问,请与客服联系</div>',
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
key: 'basic',
|
||||
title: '提现设置',
|
||||
title: '提现门槛',
|
||||
icon: 'fa fas fa-sliders-h',
|
||||
fields: [
|
||||
{ key: 'affiliate_min_withdrawal', label: '最低提现金额', type: 'input', sanitize_type: 'money_decimal', default: '50.00', help: '仅接受最多两位小数的十进制字符串。' },
|
||||
{ key: 'affiliate_min_withdrawal', label: '提现最低金额限制', type: 'input', sanitize_type: 'money_decimal', default: '50.00', help: '当用户可提现总金额(推广佣金、余额、创作分成)高于多少时候,才能发起提现' },
|
||||
{ key: 'affiliate_withdrawal_fee', label: '提现手续费(%)', type: 'number', default: 5, min: 0, max: 100 },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'limits',
|
||||
title: '每日限制',
|
||||
icon: 'fa fas fa-clock',
|
||||
fields: [
|
||||
{ key: 'affiliate_daily_withdrawal_count', label: '每日可提现次数', type: 'number', default: 3, min: 0, max: 99999 },
|
||||
{ key: 'affiliate_daily_withdrawal_limit', label: '每日提现上限', type: 'input', sanitize_type: 'money_decimal', default: '500.00', help: '仅接受最多两位小数的十进制字符串。' },
|
||||
{ key: 'affiliate_daily_withdrawal_limit', label: '每日提现上限', type: 'input', sanitize_type: 'money_decimal', default: '500.00' },
|
||||
{ key: 'affiliate_withdrawal_reset_time', label: '重置时间', type: 'input', default: '00:00', help: '按站点 WordPress 时区重置,格式 HH:MM。' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'channels',
|
||||
title: '收款渠道',
|
||||
icon: 'fa fas fa-qrcode',
|
||||
fields: [
|
||||
{ key: 'affiliate_payout_channels', label: '可用收款渠道', type: 'checkbox', default: ['alipay', 'wechat'], sanitize_type: 'array', options: [{ label: '支付宝收款码', value: 'alipay' }, { label: '微信收款码', value: 'wechat' }] },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'agreement',
|
||||
title: '提现协议',
|
||||
icon: 'fa fas fa-file-contract',
|
||||
fields: [
|
||||
{ key: 'pay_rebate_withdraw_text_details', label: '提现协议', type: 'textarea', sanitize_type: 'html', default: '<div>可提现金额达到50元后即可发起提现</div><div>申请提现需后台人工处理,一般2-3小时,请耐心等待</div><div>如有其它疑问,请与客服联系</div>', rows: 4, help: '用户申请提现时展示的内容,建议为提现须知等,支持HTML代码' },
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user