fix(pay): 修复后台收款接口显示顺序不可用并接入收银台排序

后台「收款接口 → 收款接口显示顺序」有三处缺陷叠加:
1) sortable 渲染组件读取 item.label,而 schema 写的是 title,导致 6 行
   只剩上下箭头;
2) SchemaProvider 的 sortable 分支遍历 items 定义重建数组,而该字段的
   键顺序本身就是配置值,用户排序被静默改回 schema 静态顺序;
3) 真正构建收银台支付方式列表的 aksrt_list_cash_payment_methods() 使用
   硬编码顺序,Gateway::get_available()/apply_sdk_order() 那套排序逻辑
   无任何调用方,配置对前台毫无影响。

改为:schema 项统一使用 label(JS 与 PHP 两处对齐);sortable 持久化按
提交键顺序重建,items 定义仅作白名单;新增唯一排序实现
aksrt_sort_payment_methods()(语义对齐参考主题 zibpay_get_payment_methods
+ pay_sdk_order 键顺序),并接入内容收银台、余额充值、购买积分与会员
购买四个入口。卡密输入盒改为按渠道存在与否判断,不再假定卡密固定在末位。

清理死代码:无调用方的 Gateway::get_available/apply_sdk_order/
get_status_summary、VipServicePackages 中不可达的支付方式列表回退分支、
零引用的 settings-defaults 模块及 schema/index.js 未使用的导出、支付
sortable 的 fields 死元数据、无拖拽实现却声明的 cursor:move。

未配置 pay_sdk_order 时顺序保持不变;未写数据迁移。

验证:php -l 8 个改动文件全部通过;新增 pay-sdk-order-contract 契约测试
(6 项);node --test tests/widget-system/*.test.cjs 301 项全部通过;
后台 SPA 已重新构建。
This commit is contained in:
2026-09-18 19:01:19 +08:00
parent 530a5b998f
commit d2447f8aa6
14 changed files with 223 additions and 181 deletions
@@ -498,8 +498,8 @@
<n-button text size="tiny" :disabled="index === sortableItems.length - 1" @click="moveSortable(index, 1)">↓</n-button>
</span>
</div>
<div class="aksrt-field__sortable-body">
<AksField v-for="field in item.fields || []" :key="field.key" :schema="field" :model-value="modelValue?.[item.key]?.[field.key] ?? field.default" @update:model-value="updateSortable(item.key, field.key, $event)" />
<div v-if="item.fields && item.fields.length" class="aksrt-field__sortable-body">
<AksField v-for="field in item.fields" :key="field.key" :schema="field" :model-value="modelValue?.[item.key]?.[field.key] ?? field.default" @update:model-value="updateSortable(item.key, field.key, $event)" />
</div>
</div>
</div>
@@ -1365,7 +1365,6 @@ function confirmRepeaterModal() {
justify-content: space-between;
padding: 10px 12px;
background: var(--aksrt-color-canvas);
cursor: move;
}
.aksrt-field__sortable-body {
@@ -1,28 +0,0 @@
import { ALL_SCHEMAS, CANONICAL_SCHEMA_DOMAINS, normalizeSchemaDomain } from '@/schema/index.js'
function cloneDefaults(defaults = {}) {
return JSON.parse(JSON.stringify(defaults))
}
export function getSectionDefaults(domain, section) {
const domainKey = normalizeSchemaDomain(domain)
const sectionSchema = ALL_SCHEMAS[domainKey]?.[section]
return cloneDefaults(sectionSchema?.defaults || {})
}
export function getDomainDefaults(domain) {
const domainKey = normalizeSchemaDomain(domain)
const sections = Object.values(ALL_SCHEMAS[domainKey] || {})
return sections.reduce((defaults, schema) => {
return {
...defaults,
...cloneDefaults(schema?.defaults || {}),
}
}, {})
}
export const SETTINGS_DEFAULTS_BY_DOMAIN = CANONICAL_SCHEMA_DOMAINS.reduce((defaults, domain) => {
defaults[domain] = getDomainDefaults(domain)
return defaults
}, {})
-39
View File
@@ -43,17 +43,6 @@ export function normalizeSchemaDomain(domain = '') {
return value.replace(/-/g, '_')
}
export const CANONICAL_SCHEMA_DOMAINS = [
'global',
'page_display',
'post_list',
'permissions',
'users',
'payment',
'extensions',
'tools',
]
/** All schemas organized by domain */
export const ALL_SCHEMAS = Object.freeze({
global: globalSchemas,
@@ -77,31 +66,3 @@ export function getSchema(domain, section) {
if (!domainSchemas) return null
return domainSchemas[section] || null
}
/**
* Get all schemas for a domain.
* @param {string} domain
* @returns {Object}
*/
export function getDomainSchemas(domain) {
return ALL_SCHEMAS[normalizeSchemaDomain(domain)] || {}
}
export function getSchemaSectionKeys(domain) {
return Object.keys(getDomainSchemas(domain))
}
export function getSchemaDefaults(domain, section = '') {
const domainKey = normalizeSchemaDomain(domain)
if (section) {
return { ...(getSchema(domainKey, section)?.defaults || {}) }
}
return Object.values(getDomainSchemas(domainKey)).reduce((defaults, schema) => {
return {
...defaults,
...(schema?.defaults || {}),
}
}, {})
}
@@ -187,12 +187,12 @@ export const gatewaySchema = {
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: [] },
{ key: 'wechat', label: '微信' },
{ key: 'alipay', label: '支付宝' },
{ key: 'paypal', label: 'PayPal(贝宝)' },
{ key: 'stripe', label: 'Stripe' },
{ key: 'balance', label: '余额' },
{ key: 'card_pass', label: '卡密' },
],
help: '收银台支付方式显示顺序;仅显示已开启的接口',
},