// Complete ejectable AdminList source. Generated by npm run source:admin-list. // After copying this directory into an application it is fully application-owned. import type { AdminListDataSource, AdminListDataViewSourceOptions, AdminListExportInput, AdminListExportTask, AdminListFormSourceOptions, AdminListFunctionSourceOptions, AdminListQuery, AdminListResult, } from "./types" import { createAdminListPreferenceStore } from "./preferences" import type { PageBinaryResponse } from "openxiangda/runtime" const getHeaderValue = ( headers: PageBinaryResponse["headers"], name: string, ) => { const match = Object.entries(headers || {}).find( ([key]) => key.toLowerCase() === name.toLowerCase(), ) return match?.[1] || "" } const decodeFileName = (value: string) => { try { return decodeURIComponent(value) } catch { return value } } const parseContentDispositionFileName = (value: string) => { const utf8Match = value.match(/filename\*=UTF-8''([^;]+)/i) if (utf8Match?.[1]) return decodeFileName(utf8Match[1].trim()) const plainMatch = value.match(/filename="?([^";]+)"?/i) return plainMatch?.[1] ? decodeFileName(plainMatch[1].trim()) : "" } const resolveExportFileName = ( response: PageBinaryResponse, fallbackName: string, ) => { const disposition = getHeaderValue(response.headers, "content-disposition") || (response.fileName?.includes("filename") ? response.fileName : "") const candidate = parseContentDispositionFileName(disposition) || response.fileName || fallbackName return candidate.split(/[\\/]/).pop() || fallbackName } const saveExportBlob = ( response: PageBinaryResponse, fallbackName: string, ) => { if (typeof window === "undefined" || typeof document === "undefined") return const objectUrl = window.URL.createObjectURL(response.blob) const anchor = document.createElement("a") anchor.href = objectUrl anchor.download = resolveExportFileName(response, fallbackName) document.body.appendChild(anchor) anchor.click() anchor.remove() window.URL.revokeObjectURL(objectUrl) } const nonEmpty = (value: unknown) => value !== undefined && value !== null && value !== "" && (!Array.isArray(value) || value.length > 0) const toFilterExpression = ( filters: Record, operators: Record = {}, ) => ({ logic: "AND" as const, rules: Object.entries(filters) .filter(([, value]) => nonEmpty(value)) .map(([key, value]) => ({ key, operator: operators[key] || (Array.isArray(value) ? "IN" : "EQ"), value, })), }) const toOrder = (query: AdminListQuery) => query.sorts.map((sort) => ({ id: sort.field, isAsc: (sort.direction === "ascend" ? "y" : "n") as "y" | "n", })) const listRowKeys = ["rows", "data", "records", "list", "items"] as const const isObjectEnvelope = (value: unknown): value is Record => Boolean(value) && typeof value === "object" && !Array.isArray(value) const resolveListEnvelope = (result: unknown) => { const chain: Record[] = [] const seen = new Set>() let current: unknown = result while (isObjectEnvelope(current) && !seen.has(current)) { const envelope = current seen.add(envelope) chain.push(envelope) if (listRowKeys.some((key) => Array.isArray(envelope[key]))) { break } current = isObjectEnvelope(envelope.result) ? envelope.result : isObjectEnvelope(envelope.data) ? envelope.data : undefined } const value = chain[chain.length - 1] || {} const metadataSources = [value, ...chain.slice(0, -1).reverse()] const readMetadata = (...keys: string[]) => { for (const source of metadataSources) { for (const key of keys) { if (source[key] !== undefined && source[key] !== null) return source[key] } } return undefined } return { value, readMetadata } } const normalizeList = (result: unknown): AdminListResult => { const { value, readMetadata } = resolveListEnvelope(result) const rows = listRowKeys .map((key) => value[key]) .find((candidate): candidate is Row[] => Array.isArray(candidate)) return { rows: rows || [], total: Number(readMetadata("total", "totalCount") ?? 0), currentPage: readMetadata("currentPage") as number | undefined, pageSize: readMetadata("pageSize") as number | undefined, allowedColumnKeys: readMetadata("allowedColumnKeys") as string[] | undefined, hiddenFieldsCount: readMetadata("hiddenFieldsCount") as number | undefined, } } const createExportClient = ( options: | AdminListFormSourceOptions | AdminListDataViewSourceOptions | AdminListFunctionSourceOptions, source: Record, ) => { const appType = options.appType || options.sdk.context.app.appType || "" const requestedFileNames = new Map() const basePath = `/openxiangda-api/v1/apps/${encodeURIComponent( appType, )}/admin-list/exports` return { createExportTask: async (input: AdminListExportInput) => { const normalizedInput = { ...input, rowIds: input.rowIds?.map((rowId) => typeof rowId === "number" ? rowId : String(rowId), ), } let task: AdminListExportTask if (options.sdk.export?.create) { const response = await options.sdk.export.create({ appType, exportKey: options.listKey, source: source as any, ...normalizedInput, definitionCode: options.exportDefinitionCode, definitionInput: options.exportDefinitionInput, }) if (!response.result) { throw new Error("创建导出任务失败:服务端未返回任务") } task = response.result } else { const response = await options.sdk.request({ path: basePath, method: "post", body: { listKey: options.listKey, source, ...normalizedInput, }, }) if (!response.result) { throw new Error("创建导出任务失败:服务端未返回任务") } task = response.result } if (input.fileName) requestedFileNames.set(task.id, input.fileName) return task }, getExportTask: async (taskId: string) => { if (options.sdk.export?.get) { const response = await options.sdk.export.get(taskId, { appType }) if (!response.result) { throw new Error("查询导出任务失败:任务不存在") } return response.result } const response = await options.sdk.request({ path: `${basePath}/${encodeURIComponent(taskId)}`, method: "get", }) if (!response.result) { throw new Error("查询导出任务失败:任务不存在") } return response.result }, downloadExportTask: async ( task: AdminListExportTask, downloadOptions: { fileName?: string } = {}, ) => { if (!task.downloadUrl) { throw new Error("导出任务未返回下载地址") } const response = await options.sdk.transport.download({ path: task.downloadUrl, method: "get", }) const fallbackName = downloadOptions.fileName || requestedFileNames.get(task.id) || `${options.listKey}.xlsx` saveExportBlob(response, fallbackName) requestedFileNames.delete(task.id) }, } } export function createFormAdminListSource( options: AdminListFormSourceOptions, ): AdminListDataSource { const appType = options.appType || options.sdk.context.app.appType || "" const exportClient = createExportClient(options, { type: "form", formUuid: options.formUuid, idField: options.idField || "formInstId", filterOperators: options.filterOperators || {}, }) return { key: `form:${appType}:${options.formUuid}:${options.listKey}`, preferences: createAdminListPreferenceStore(options.sdk, appType), async query(query) { const filters = { ...(options.fixedFilters || {}), ...(query.fixedFilters || {}), ...query.filters, } const response = await options.sdk.form.advancedSearch({ appType, formUuid: options.formUuid, currentPage: query.currentPage, pageSize: query.pageSize, filters: toFilterExpression(filters, options.filterOperators), order: toOrder(query), }) return normalizeList(response.result) }, ...exportClient, } } export function createDataViewAdminListSource( options: AdminListDataViewSourceOptions, ): AdminListDataSource { const appType = options.appType || options.sdk.context.app.appType || "" const exportClient = createExportClient(options, { type: "dataView", dataViewCode: options.dataViewCode, fields: options.fields, idField: options.idField || "id", filterOperators: options.filterOperators || {}, }) return { key: `data-view:${appType}:${options.dataViewCode}:${options.listKey}`, preferences: createAdminListPreferenceStore(options.sdk, appType), async query(query) { const filters = { ...(options.fixedFilters || {}), ...(query.fixedFilters || {}), ...query.filters, } const response = await options.sdk.dataView.query( options.dataViewCode, { appType, fields: options.fields, currentPage: query.currentPage, pageSize: query.pageSize, filters: toFilterExpression(filters, options.filterOperators), order: toOrder(query), }, ) return normalizeList(response.result) }, ...exportClient, } } export function createFunctionAdminListSource( options: AdminListFunctionSourceOptions, ): AdminListDataSource { const appType = options.appType || options.sdk.context.app.appType || "" const exportClient = createExportClient(options, { type: "function", functionCode: options.functionCode, }) return { key: `function:${appType}:${options.functionCode}:${options.listKey}`, preferences: createAdminListPreferenceStore(options.sdk, appType), async query(query) { const response = await options.sdk.function.invoke< AdminListResult, Record >(options.functionCode, { appType, input: { contract: "admin_list_v1", query: { ...query, fixedFilters: { ...(options.fixedFilters || {}), ...(query.fixedFilters || {}), }, }, }, }) const invocation = response.result if (!invocation) { throw new Error("AdminList App Function 未返回结果") } return normalizeList(invocation.result || invocation.output) }, ...exportClient, } }