mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-24 16:05:44 +08:00
fix(payment): 修复后端返回空supported_types时支付提供商卡片消失的问题
统一处理后端返回的null类型supported_types,将其标准化为空数组,避免调用includes()时报错导致卡片无法显示,同时修复多处相关的类型判断逻辑
This commit is contained in:
@@ -658,7 +658,9 @@ function reset(defaultKey: string) {
|
||||
function loadProvider(provider: ProviderInstance) {
|
||||
form.name = provider.name
|
||||
form.provider_key = provider.provider_key
|
||||
form.supported_types = provider.supported_types
|
||||
form.supported_types = Array.isArray(provider.supported_types)
|
||||
? [...provider.supported_types]
|
||||
: []
|
||||
form.enabled = provider.enabled
|
||||
// Coerce to a valid value for this provider. Guards against stale data
|
||||
// (e.g. "popup" written by an older client) showing up as an unselected
|
||||
|
||||
@@ -104,6 +104,6 @@ const modeLabel = computed(() => {
|
||||
})
|
||||
|
||||
function isSelected(type: string): boolean {
|
||||
return props.provider.supported_types.includes(type)
|
||||
return Array.isArray(props.provider.supported_types) && props.provider.supported_types.includes(type)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -9777,7 +9777,15 @@ async function loadProviders() {
|
||||
providersLoading.value = true;
|
||||
try {
|
||||
const res = await adminAPI.payment.getProviders();
|
||||
providers.value = res.data || [];
|
||||
// Normalize supported_types: backend returns null when the list is empty
|
||||
// (Go nil slice → JSON null). Without this, ProviderCard's isSelected()
|
||||
// throws TypeError on null.includes(), causing the card to vanish.
|
||||
providers.value = (res.data || []).map((p) => ({
|
||||
...p,
|
||||
supported_types: Array.isArray(p.supported_types)
|
||||
? p.supported_types
|
||||
: [],
|
||||
}));
|
||||
} catch (err: unknown) {
|
||||
appStore.showError(extractI18nErrorMessage(err, t, "payment.errors", t("common.error")));
|
||||
} finally {
|
||||
@@ -9871,9 +9879,12 @@ async function handleToggleField(
|
||||
}
|
||||
|
||||
async function handleToggleType(provider: ProviderInstance, type: string) {
|
||||
const updated = provider.supported_types.includes(type)
|
||||
? provider.supported_types.filter((t) => t !== type)
|
||||
: [...provider.supported_types, type];
|
||||
const currentTypes = Array.isArray(provider.supported_types)
|
||||
? provider.supported_types
|
||||
: [];
|
||||
const updated = currentTypes.includes(type)
|
||||
? currentTypes.filter((t) => t !== type)
|
||||
: [...currentTypes, type];
|
||||
const conflict = findProviderEnablementConflict({
|
||||
id: provider.id,
|
||||
provider_key: provider.provider_key,
|
||||
|
||||
@@ -793,6 +793,70 @@ describe("admin SettingsView payment visible method controls", () => {
|
||||
expect(paymentHelpImageUpload?.attributes("data-upload-label")).toBe("上传图片");
|
||||
expect(paymentHelpImageUpload?.attributes("data-remove-label")).toBe("移除");
|
||||
});
|
||||
|
||||
it("normalizes null supported_types from API so provider card stays visible", async () => {
|
||||
// Backend returns null for supported_types when the list is empty
|
||||
// (Go nil slice → JSON null). Without normalization, ProviderCard's
|
||||
// isSelected() throws TypeError on null.includes(), causing the card
|
||||
// to vanish from the list.
|
||||
const providerWithNullTypes = {
|
||||
id: 42,
|
||||
provider_key: "easypay",
|
||||
name: "EasyPay",
|
||||
config: {},
|
||||
supported_types: null as unknown as string[],
|
||||
enabled: true,
|
||||
payment_mode: "",
|
||||
refund_enabled: false,
|
||||
allow_user_refund: false,
|
||||
limits: "",
|
||||
sort_order: 0,
|
||||
};
|
||||
getProviders.mockReset();
|
||||
getProviders.mockResolvedValue({ data: [providerWithNullTypes] });
|
||||
|
||||
let receivedProviders: Array<Record<string, unknown>> = [];
|
||||
const PaymentProviderListCapture = defineComponent({
|
||||
props: {
|
||||
providers: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
receivedProviders = props.providers as Array<Record<string, unknown>>;
|
||||
return () => h("div", { class: "provider-list-capture" });
|
||||
},
|
||||
});
|
||||
|
||||
const wrapper = mount(SettingsView, {
|
||||
global: {
|
||||
stubs: {
|
||||
AppLayout: AppLayoutStub,
|
||||
Select: SelectStub,
|
||||
Toggle: ToggleStub,
|
||||
Icon: true,
|
||||
ConfirmDialog: true,
|
||||
PaymentProviderList: PaymentProviderListCapture,
|
||||
PaymentProviderDialog: true,
|
||||
GroupBadge: true,
|
||||
GroupOptionItem: true,
|
||||
ProxySelector: true,
|
||||
ImageUpload: ImageUploadStub,
|
||||
BackupSettings: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
await openPaymentTab(wrapper);
|
||||
|
||||
// The provider should still be in the list
|
||||
expect(receivedProviders.length).toBe(1);
|
||||
// supported_types should be normalized to an empty array, not null
|
||||
expect(Array.isArray(receivedProviders[0].supported_types)).toBe(true);
|
||||
expect(receivedProviders[0].supported_types).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("admin SettingsView wechat connect controls", () => {
|
||||
|
||||
Reference in New Issue
Block a user