import { MoveStyleUtils, RockChildrenConfig, RockEvent, RockEventHandler, RockPageEventSubscriptionConfig, type Rock, type RockConfig, type Framework, } from "@ruiapp/move-style"; import { renderRock } from "@ruiapp/react-renderer"; import RapidEntityListMeta from "./SonicEntityListMeta"; import type { SonicEntityListRockConfig } from "./sonic-entity-list-types"; import { differenceBy, find, get, isArray, isEmpty, isNumber, keyBy, omit, pick, set } from "lodash"; import type { RapidEntityListConfig, RapidEntityListRockConfig } from "../rapid-entity-list/rapid-entity-list-types"; import rapidAppDefinition from "../../rapidAppDefinition"; import { generateRockConfigOfError } from "../../rock-generators/generateRockConfigOfError"; import { RapidEntity } from "@ruiapp/rapid-common"; import { RapidDeleteRecordActionOptions, RapidUpdateRecordActionOptions } from "../../types/rapid-action-types"; import { EntityStore, RapidEntityFormRockConfig, RapidTableColumnConfig, RapidToolbarRockConfig } from "../../mod"; import { useState } from "react"; import moment from "moment"; import { RapidExtStorage } from "../../utils/storage-utility"; import { getColumnUniqueKey, ICacheRapidTableColumn } from "../rapid-entity-list-toolbox/RapidEntityListToolbox"; import { getRapidEntityListFilters, RapidEntityListFilterCache } from "../rapid-entity-search-form/RapidEntitySearchForm"; import { message, Modal } from "antd"; import { getRapidApi } from "../../rapidApi"; import { RapidEntityListToolboxColumnConfig } from "../rapid-entity-list-toolbox/RapidEntityListToolbox"; import { getExtensionLocaleStringResource, getMetaEntityLocaleName, getMetaPropertyLocaleName } from "../../helpers/i18nHelper"; const DEFAULT_PAGE_SIZE = 20; function getToolboxColumns(framework: Framework, mainEntity: RapidEntity, tableColumns: RapidTableColumnConfig[]) { const toolboxColumns: any[] = []; for (const tableColumn of tableColumns) { if (tableColumn.children) { continue; } toolboxColumns.push(getToolboxColumnConfig(framework, mainEntity, tableColumn)); } return toolboxColumns; } function getToolboxColumnConfig(framework: Framework, mainEntity: RapidEntity, column: RapidTableColumnConfig) { let columnTitle = column.title; const fieldName = column.fieldName || column.code; const fieldNameParts = fieldName.split("."); const rpdField = rapidAppDefinition.getEntityFieldByCode(mainEntity, fieldNameParts[0]); if (!columnTitle && rpdField) { columnTitle = getMetaPropertyLocaleName(framework, mainEntity, rpdField); } return { ...pick(column, ["key", "code", "hidden"]), title: columnTitle, }; } export default { onResolveState(props, state) { const [rerenderKey, setRerenderKey] = useState(""); return { rerenderKey, rerenderDom: () => { setRerenderKey(moment().unix()); }, }; }, onReceiveMessage(message, state, props) { if (message.name === "refreshView") { message.page.sendComponentMessage(`${props.$id}-rapidEntityList`, { name: "refreshView", }); } else if (message.name === "submitNewForm") { message.page.sendComponentMessage(`${props.$id}-newForm`, { name: "submit", }); } else if (message.name === "submitEditForm") { message.page.sendComponentMessage(`${props.$id}-editForm`, { name: "submit", }); } else if (message.name === "resetNewForm") { message.page.sendComponentMessage(`${props.$id}-newForm`, { name: "resetFields", }); } else if (message.name === "resetEditForm") { message.page.sendComponentMessage(`${props.$id}-editForm`, { name: "resetFields", }); } else if (message.name === "rerenderDom") { state.rerenderDom(); } }, Renderer(context, props, state) { const { framework } = context; const entities = rapidAppDefinition.getEntities(); const entityCode = props.entityCode; let entityName = props.entityName; const rapidApi = getRapidApi(); let mainEntity: RapidEntity | undefined; if (entityCode) { mainEntity = find(entities, (item) => item.code === entityCode); if (!mainEntity) { return renderRock({ context, rockConfig: generateRockConfigOfError(new Error(`Entity '${entityCode}' not found.`)) }); } if (!entityName) { entityName = getMetaEntityLocaleName(framework, mainEntity); } } const dataSourceCode = props.dataSourceCode || "list"; const pageSize = get(props, "pageSize", DEFAULT_PAGE_SIZE); let entityListRockConfig: RapidEntityListRockConfig = { ...(omit(MoveStyleUtils.omitSystemRockConfigFields(props), ["newForm", "editForm", "style"]) as RapidEntityListConfig), dataSourceCode, pageSize, $type: "rapidEntityList", $id: `${props.$id}-rapidEntityList`, }; // 启用高级查询参数缓存 & 获取并使用参数缓存 let searchFormData: Record = {}; let scopeInitVars: Record = {}; if (props.enabledFilterCache && props.filterCacheName) { const filterCacheData = RapidEntityListFilterCache.get(props.filterCacheName); searchFormData = get(filterCacheData, "formData") || {}; const filters = getRapidEntityListFilters(props.searchForm?.items || [], searchFormData); if (isArray(filters) && !isEmpty(filters)) { set(entityListRockConfig, "cacheFilters", filters); } if (filterCacheData && isNumber(filterCacheData.pageNum)) { scopeInitVars[`stores-${dataSourceCode}-pageNum`] = filterCacheData.pageNum || 1; set(entityListRockConfig, "pageNum", filterCacheData.pageNum || 1); } } // 动态 开启/关闭 树展示 if (get(props, "$exps.convertListToTree")) { set(entityListRockConfig, "$exps.convertListToTree", get(props, "$exps.convertListToTree")); } const toolboxEnabled = !(props.hideToolbox || props.toolbox?.disabled); let toolboxRockConfig: RockConfig | null = null; if (toolboxEnabled) { toolboxRockConfig = { $type: "rapidEntityListToolbox", $id: `${props.$id}_toolbox`, dataSourceCode, columns: getToolboxColumns(framework, mainEntity, props.columns), config: props.toolbox || { columnCacheKey: props.entityCode || props.entityName, }, onRerender: [ { $action: "script", script: () => { state.rerenderDom(); }, }, ], }; const originColumns = props.columns || []; const cacheColumns = RapidExtStorage.get(toolboxRockConfig.config.columnCacheKey); if (isArray(cacheColumns) && !isEmpty(cacheColumns)) { const diffOriginColumns = differenceBy(originColumns, cacheColumns, getColumnUniqueKey); const originByCodeMap = keyBy(originColumns, getColumnUniqueKey) as Record; let sortedColumns: any[] = []; let showColumns: any[] = []; let hideColumnCodes: string[] = []; cacheColumns.forEach((col) => { const uniqueKey = getColumnUniqueKey(col as any); const originColumn = originByCodeMap[uniqueKey]; if (originColumn) { sortedColumns.push(originColumn); if (!col.hidden) { showColumns.push(originColumn); } else { hideColumnCodes.push(col.code); } } }); entityListRockConfig.extraProperties = [...(entityListRockConfig.extraProperties || []), ...hideColumnCodes]; entityListRockConfig.columns = [...showColumns, ...diffOriginColumns]; let toolboxColumns = [...sortedColumns, ...diffOriginColumns].map((column) => getToolboxColumnConfig(framework, mainEntity, column)); toolboxRockConfig.columns = toolboxColumns; } } let toolbarExtraActions: RockConfig[] = props.extraActions || []; if (props.searchForm) { toolbarExtraActions = [ ...toolbarExtraActions, { $id: `${props.$id}-advancedSearchButton`, $type: "anchor", children: [ { $type: "text", text: getExtensionLocaleStringResource(framework, "advancedSearch"), }, { $type: "antdIcon", name: "DownOutlined", $exps: { name: `$scope.vars["searchBoxVisible"] ? "UpOutlined" : "DownOutlined"`, }, }, ], onClick: [ { $action: "script", script: (event: RockEvent) => { event.scope.setVars({ searchBoxVisible: !event.scope.vars["searchBoxVisible"], }); }, }, ], }, ]; } const toolbarRockConfig: RapidToolbarRockConfig = { $id: `${props.$id}-toolbar`, $type: "rapidToolbar", items: props.listActions, extras: toolbarExtraActions, rightExtras: toolboxEnabled ? [toolboxRockConfig] : [], dataSourceCode: props.dataSourceCode, }; const searchFormRockConfig: RockConfig | null = props.searchForm ? { $id: `${props.$id}-searchForm`, $type: "rapidEntitySearchForm", entityCode: entityCode, defaultFormFields: searchFormData, items: props.searchForm.items, formDataAdapter: props.searchForm.formDataAdapter, onValuesChange: props.searchForm.onValuesChange, actionsAlign: "right", onSearch: [ { $action: "script", script: async (event: RockEvent) => { const storeScope = props.useStoreInPageScope ? event.page.scope : event.scope; const store: EntityStore = storeScope.getStore(dataSourceCode); // 设置搜索变量 storeScope.setVars({ [`stores-${dataSourceCode}-pageNum`]: 1, }); // 启用高级查询参数缓存 & 设置参数缓存 if (props.enabledFilterCache && props.filterCacheName) { RapidEntityListFilterCache.set(props.filterCacheName, { formData: event.args[0].formData, pageNum: 1 }); } store.updateConfig({ filters: event.args[0].filters, pagination: pageSize > 0 ? { limit: pageSize, offset: 0, } : undefined, }); // 重新加载数据 store.loadData(); }, }, ], } : null; const onNewFormSubmitSuccess = props.newForm?.onSubmitSuccess || props.newForm?.onSaveSuccess; const onNewFormSubmitError = props.newForm?.onSubmitError || props.newForm?.onSaveError; const newModalRockConfig: RockConfig | null = props.newForm ? { $type: "antdModal", $id: `${props.$id}-newModal`, title: props.newModalTitle || getExtensionLocaleStringResource(framework, "newModalTitle", { entityName, }), okText: getExtensionLocaleStringResource(framework, "ok"), cancelText: getExtensionLocaleStringResource(framework, "cancel"), maskClosable: false, $exps: { open: "!!$scope.vars['modal-newEntity-open']", confirmLoading: "!!$scope.vars['modal-saving']", }, children: [ { $type: "rapidEntityForm", $id: `${props.$id}-newForm`, mode: "new", ...(props.newForm as RapidEntityFormRockConfig), entityCode, beforeSubmit: [ { $action: "setVars", vars: { "modal-saving": true, }, }, ], onSubmitSuccess: [ { $action: "setVars", vars: { "modal-newEntity-open": false, "modal-saving": false, }, }, ...(onNewFormSubmitSuccess ? (Array.isArray(onNewFormSubmitSuccess) ? onNewFormSubmitSuccess : [onNewFormSubmitSuccess]) : []), ...[ { $action: "loadStoreData", storeName: dataSourceCode, }, ], ], onSubmitError: [ { $action: "setVars", vars: { "modal-saving": false, }, }, ...(onNewFormSubmitError ? (Array.isArray(onNewFormSubmitError) ? onNewFormSubmitError : [onNewFormSubmitError]) : []), ], } satisfies RapidEntityFormRockConfig, ], onOk: [ { $action: "sendComponentMessage", componentId: props.$id, message: { name: "submitNewForm", }, }, ], onCancel: [ { $action: "setVars", vars: { "modal-newEntity-open": false, }, }, ], } : null; const onEditFormSubmitSuccess = props.editForm?.onSubmitSuccess || props.editForm?.onSaveSuccess; const onEditFormSubmitError = props.editForm?.onSubmitError || props.editForm?.onSaveError; const editModalRockConfig: RockConfig | null = props.editForm ? { $type: "antdModal", $id: `${props.$id}-editModal`, title: props.editModalTitle || getExtensionLocaleStringResource(framework, "editModalTitle", { entityName, }), okText: getExtensionLocaleStringResource(framework, "ok"), cancelText: getExtensionLocaleStringResource(framework, "cancel"), maskClosable: false, $exps: { open: "!!$scope.vars['modal-editEntity-open']", confirmLoading: "!!$scope.vars['modal-saving']", }, children: [ { $type: "rapidEntityForm", $id: `${props.$id}-editForm`, mode: "edit", ...(props.editForm as RapidEntityFormRockConfig), entityCode, $exps: { entityId: "$scope.vars.activeEntityId", }, beforeSubmit: [ { $action: "setVars", vars: { "modal-saving": true, }, }, ], onSubmitSuccess: [ { $action: "setVars", vars: { "modal-editEntity-open": false, "modal-saving": false, }, }, ...(onEditFormSubmitSuccess ? (Array.isArray(onEditFormSubmitSuccess) ? onEditFormSubmitSuccess : [onEditFormSubmitSuccess]) : []), ...[ { $action: "loadStoreData", storeName: dataSourceCode, }, ], ], onSubmitError: [ { $action: "setVars", vars: { "modal-saving": false, }, }, ...(onEditFormSubmitError ? (Array.isArray(onEditFormSubmitError) ? onEditFormSubmitError : [onEditFormSubmitError]) : []), ], } satisfies RapidEntityFormRockConfig, ], onOk: [ { $action: "sendComponentMessage", componentId: props.$id, message: { name: "submitEditForm", }, }, ], onCancel: [ { $action: "setVars", vars: { "modal-editEntity-open": false, }, }, ], } : null; const childrenConfig: RockChildrenConfig = []; childrenConfig.push(toolbarRockConfig); if (searchFormRockConfig) { childrenConfig.push({ $id: `${props.$id}-searchBox`, $type: "box", style: { backgroundColor: "#f2f2f2", borderRadius: "5px", padding: "24px", paddingBottom: "0", marginBottom: "12px", }, children: searchFormRockConfig, $exps: { "style.display": `$scope.vars["searchBoxVisible"] ? "block" : "none"`, }, }); } childrenConfig.push(entityListRockConfig); if (newModalRockConfig) { childrenConfig.push(newModalRockConfig); } if (editModalRockConfig) { childrenConfig.push(editModalRockConfig); } const footerRockConfig = props.footer; if (footerRockConfig) { if (isArray(footerRockConfig)) { childrenConfig.push(...footerRockConfig); } else { childrenConfig.push(footerRockConfig); } } const rockConfig: RockConfig = { $id: `${props.$id}-scope`, $type: "scope", initialVars: scopeInitVars, stores: props.stores, children: [ { $type: "box", className: "sonic-entity-list", children: childrenConfig, style: props.style, }, ], eventSubscriptions: [ { eventName: "onRefreshButtonClick", handlers: [ { $action: "loadStoreData", storeName: dataSourceCode, }, ], }, { eventName: "onNewEntityButtonClick", handlers: [ { $action: "setVars", vars: { "modal-newEntity-open": true, }, }, { $action: "sendComponentMessage", componentId: props.$id, message: { name: "resetNewForm", }, }, ], }, { eventName: "onEditEntityButtonClick", handlers: [ { $action: "setVars", vars: { "modal-editEntity-open": true, }, $exps: { "vars.activeEntityId": "$event.args[0].recordId", }, }, { $action: "loadStoreData", storeName: "detail", }, { $action: "wait", time: 0, }, { $action: "sendComponentMessage", componentId: props.$id, message: { name: "resetEditForm", }, }, ], }, { eventName: "onUpdateEntityButtonClick", handlers: [ { $action: "script", script: async (event) => { const storeScope = props.useStoreInPageScope ? event.page.scope : event.scope; const recordAction: RapidUpdateRecordActionOptions = event.args[0]; let { confirmTitle, confirmText, recordId, entity } = recordAction; if (confirmText) { Modal.confirm({ title: confirmTitle, content: confirmText, okText: getExtensionLocaleStringResource(framework, "ok"), cancelText: getExtensionLocaleStringResource(framework, "cancel"), onOk: () => { (async () => { try { await rapidApi.patch(`${mainEntity.namespace}/${mainEntity.pluralCode}/${recordId}`, entity); storeScope.loadStoreData(dataSourceCode, null); } catch (err: any) { message.error(getExtensionLocaleStringResource(framework, "updateError", { message: err.message })); } })(); }, }); } else { try { await rapidApi.patch(`${mainEntity.namespace}/${mainEntity.pluralCode}/${recordId}`, entity); storeScope.loadStoreData(dataSourceCode, null); } catch (err: any) { message.error(getExtensionLocaleStringResource(framework, "updateError", { message: err.message })); } } }, }, ], }, { eventName: "onDeleteEntityButtonClick", handlers: [ { $action: "script", script: async (event) => { const storeScope = props.useStoreInPageScope ? event.page.scope : event.scope; const recordAction: RapidDeleteRecordActionOptions = event.args[0]; let { confirmTitle, confirmText, recordId } = recordAction; if (!confirmText) { const entityName = props.entityName || getMetaEntityLocaleName(framework, mainEntity); confirmText = getExtensionLocaleStringResource(framework, "deleteConfirmText", { entityName, }); } Modal.confirm({ title: confirmTitle, content: confirmText, okText: getExtensionLocaleStringResource(framework, "ok"), cancelText: getExtensionLocaleStringResource(framework, "cancel"), onOk: () => { (async () => { try { await rapidApi.delete(`${mainEntity.namespace}/${mainEntity.pluralCode}/${recordId}`); message.info(getExtensionLocaleStringResource(framework, "deleteSuccess")); storeScope.loadStoreData(dataSourceCode, null); } catch (err: any) { message.error(getExtensionLocaleStringResource(framework, "deleteError", { message: err.message })); } })(); }, }); }, }, ], }, { eventName: "onRecordAction", handlers: [ { $action: "script", script: async (event) => { const recordAction = event.args[0]; const { actionName, recordId, record } = recordAction; const actionSubscription = find(props.actionSubscriptions, { actionName }); if (actionSubscription) { await event.page.handleEvent({ context, parentEvent: event, handlers: actionSubscription.handlers, args: [{ recordId, record }], sender: event.sender, }); } }, }, ], }, ] satisfies RockPageEventSubscriptionConfig[], }; return renderRock({ context, rockConfig }); }, ...RapidEntityListMeta, } as Rock;