From 230c1807f3654b606e2ada9b3fff733130b75760 Mon Sep 17 00:00:00 2001 From: zjt Date: Tue, 15 Mar 2022 16:31:50 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20=E8=AF=A6=E6=83=85=E8=A1=A8?= =?UTF-8?q?=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/detail/common.tsx | 8 + src/components/detail/index.tsx | 37 +- .../detail/table/common/columnStyle.tsx | 20 + src/components/detail/table/index.tsx | 770 ++++++++++++++++++ src/index.tsx | 1 + src/steps/detail/index.tsx | 18 +- 6 files changed, 831 insertions(+), 23 deletions(-) create mode 100644 src/components/detail/table/common/columnStyle.tsx create mode 100644 src/components/detail/table/index.tsx diff --git a/src/components/detail/common.tsx b/src/components/detail/common.tsx index c095ba3..69560e3 100644 --- a/src/components/detail/common.tsx +++ b/src/components/detail/common.tsx @@ -3,6 +3,8 @@ import { ColumnsConfig, ParamConfig } from '../../interface' import { DetailFieldConfigs as getFieldConfigs } from './' import ParamHelper from '../../util/param' +import { CCMSConfig } from '../../main' + /** * 详情页表单项基类配置文件格式定义 * - field: 表单项字段名 @@ -88,6 +90,12 @@ export interface DetailFieldProps { onValueListSplice: (path: string, index: number, count: number, validation: true | DetailFieldError[]) => Promise baseRoute: string, loadDomain: (domain: string) => Promise + loadPageConfig?: (pageID: any) => Promise + handlePageRedirect?: (path: string) => void + checkPageAuth?: (pageID: any) => Promise + onUnmount?: (reload?: boolean, data?: any) => Promise + loadPageURL?: (pageID: any) => Promise + loadPageFrameURL?: (pageID: any) => Promise } /** diff --git a/src/components/detail/index.tsx b/src/components/detail/index.tsx index d67c1e3..f590834 100644 --- a/src/components/detail/index.tsx +++ b/src/components/detail/index.tsx @@ -1,4 +1,3 @@ - import TextField, { TextFieldConfig } from './text' import EnumDetail, { EnumDetailConfig } from './enum' import StatementDetail, { StatementDetailConfig } from './statement' @@ -7,28 +6,31 @@ import GroupField, { GroupFieldConfig } from './group' import ImportSubformField, { ImportSubformFieldConfig } from './importSubform' import InfoDetail, { InfoDetailConfig } from './detailInfo' import ColorDetail, { ColorDetailConfig } from './detailColor' +import TableField, { TableFieldConfig } from './table' /** * 详情步骤内详情项配置文件格式定义 - 枚举 */ export type DetailFieldConfigs = - TextFieldConfig | - EnumDetailConfig | - StatementDetailConfig | - GroupFieldConfig | - ImportSubformFieldConfig | - InfoDetailConfig | - ColorDetailConfig - + TextFieldConfig | + EnumDetailConfig | + StatementDetailConfig | + GroupFieldConfig | + ImportSubformFieldConfig | + InfoDetailConfig | + ColorDetailConfig | + TableFieldConfig export type componentType = - 'text' | - 'group' | - 'detail_enum' | - 'statement' | - 'import_subform' | - 'detail_info' | - 'detail_color' + 'text' | + 'group' | + 'detail_enum' | + 'statement' | + 'import_subform' | + 'detail_info' | + 'detail_color' | + 'table' + export default { group: GroupField, @@ -37,5 +39,6 @@ export default { detail_enum: EnumDetail, statement: StatementDetail, detail_info: InfoDetail, - detail_color: ColorDetail + detail_color: ColorDetail, + table: TableField } diff --git a/src/components/detail/table/common/columnStyle.tsx b/src/components/detail/table/common/columnStyle.tsx new file mode 100644 index 0000000..a0d9e22 --- /dev/null +++ b/src/components/detail/table/common/columnStyle.tsx @@ -0,0 +1,20 @@ +import React, { Component } from 'react' + +type Props = { + style: any + addfix: boolean +}; + +export default class ColumnStyleComponent extends Component { + render () { + const { style, addfix = true } = this.props + const reSetStyle = Object.assign({}, { fontSize: style?.fontSize, color: style?.color }, style?.customStyle) + return addfix + ?
+ {style?.prefix || ''}{this.props.children}{style?.postfix || ''} +
+ :
+ {this.props.children} +
+ } +} diff --git a/src/components/detail/table/index.tsx b/src/components/detail/table/index.tsx new file mode 100644 index 0000000..17b409d --- /dev/null +++ b/src/components/detail/table/index.tsx @@ -0,0 +1,770 @@ +import React from 'react' +import queryString from 'query-string' +import { getParam, getParamText, getValue } from '../../../util/value' +import { getBoolean } from '../../../util/value' +import Step, { StepConfig, StepProps } from '../../../steps/common' +import { DetailField, DetailFieldConfig, DetailFieldError, IDetailField } from '../common' +import getALLComponents, { ColumnConfigs } from '../../tableColumns' +import CCMS, { CCMSConfig } from '../../../main' +import InterfaceHelper, { InterfaceConfig } from '../../../util/interface' +import ConditionHelper, { ConditionConfig } from '../../../util/condition' +import { ParamConfig } from '../../../interface' +import { DetailFieldProps } from '../common' +import { cloneDeep, get, set } from 'lodash' +import ColumnStyleComponent from './common/columnStyle' + + +export interface TableFieldConfig extends DetailFieldConfig { + type: 'table' + field: string + label: string + primary: string + tableColumns: ColumnConfigs[] + operations?: { + rowOperations?: Array + } + pagination?: { + mode: 'none' | 'client' | 'server' + current?: string + pageSize?: string + total?: string + } +} + +/** + * 表格步骤-操作配置文件格式 + */ +export interface TableOperationGroupConfig { + type: 'group' + label?: string + level?: 'normal' | 'primary' | 'danger' + operations: Array +} + +/** + * 表格步骤-操作配置文件格式 + */ +export interface TableOperationConfig { + type: 'button' + label: string + level?: 'normal' | 'primary' | 'danger' + check?: { enable: false } | TableOperationCheckConfig + confirm?: { enable: false } | TableOperationConfirmConfig + handle: TableCCMSOperationConfig | TableLinkOperationConfig + condition?: ConditionConfig +} + +export interface TableCCMSOperationConfig { + type: 'ccms' + page: any + target: 'current' | 'page' | 'open' | 'handle' + targetURL: string + data: { [key: string]: ParamConfig } + params?: { field: string, data: ParamConfig }[] + callback?: boolean + debug?: boolean +} + +export interface TableLinkOperationConfig { + type: 'link' + target: '_blank' | '_self' + targetURL: string + params?: { field: string, data: ParamConfig }[] + callback?: boolean + debug?: boolean +} + +interface TableOperationCheckConfig { + enable: true + interface: InterfaceConfig +} + +interface TableOperationConfirmConfig { + enable: true + titleText: string + titleParams?: Array<{ field: string, data: ParamConfig }> + okText: string + cancelText: string +} + +/** + * 表格步骤组件 - 列 - UI渲染方法 - 入参 + */ +export interface ITableColumn { + field: string + label: string + align: 'left' | 'center' | 'right' + render: (value: any, record: { [type: string]: any }, index: number) => React.ReactNode +} + +/** + * 表格步骤组件 - 操作 - UI渲染方法 - 入参 + */ +export interface ITableStepRowOperation { + children: (React.ReactNode | undefined)[] +} + +/** + * 表格步骤组件 - 操作 - UI渲染方法 - 入参 + */ +export interface ITableStepTableOperation { + children: (React.ReactNode | undefined)[] +} + +/** + * 表格步骤组件 - 操作按钮 - UI渲染方法 - 入参 + */ +export interface ITableStepRowOperationButton { + label: string + level: 'normal' | 'primary' | 'danger' + disabled?: boolean + onClick: () => Promise +} + +/** + * 表格步骤组件 - 操作按钮组 - UI渲染方法 - 入参 + */ +export interface ITableStepRowOperationGroup { + label?: string + children: React.ReactNode[] +} + +/** + * 表格步骤组件 - 操作按钮组元素 - UI渲染方法 - 入参 + */ +export interface ITableStepRowOperationGroupItem { + label: string + level: 'normal' | 'primary' | 'danger' + disabled?: boolean + onClick: () => Promise +} + +/** + * 表格步骤组件 - 操作按钮 - UI渲染方法 - 入参 + */ +export interface ITableStepTableOperationButton { + label: string + level: 'normal' | 'primary' | 'danger' + disabled?: boolean + onClick: () => Promise +} + +/** + * 表格步骤组件 - 操作按钮组 - UI渲染方法 - 入参 + */ +export interface ITableStepTableOperationGroup { + label?: string + children: React.ReactNode[] +} + +/** + * 表格步骤组件 - 操作按钮组元素 - UI渲染方法 - 入参 + */ +export interface ITableStepTableOperationGroupItem { + label: string + level: 'normal' | 'primary' | 'danger' + disabled?: boolean + onClick: () => Promise +} + +/** + * 表格步骤组件 - 操作 - 二次确认 - UI渲染方法 - 入参 + */ +export interface ITableStepOperationConfirm { + title: string + okText: string + cancelText: string + onOk: () => void + onCancel: () => void +} + +export interface ITableStepOperationModal { + title: string + visible: boolean + width: string + children: React.ReactNode + onClose: () => void +} + +interface TableState { + operation: { + enable: boolean + target: 'current' | 'handle' + title: string + visible: boolean + config: CCMSConfig + data: any + callback?: boolean + } + pageAuth: { [page: string]: boolean } +} + +/** + * 表格步骤组件 - UI渲染方法 - 入参 + * - data: 数据 + */ +export interface ITableField { + title: string | null + primary: string + data: { [field: string]: any }[] + tableColumns: ITableColumn[] + pagination?: { + current: number + pageSize: number + total: number + onChange: (page: number, pageSize: number) => void + } + // tableOperations: React.ReactNode | null + // multirowOperations: React.ReactNode | null, + description?: { + type: 'text' | 'tooltip' | 'modal' + label: string | undefined + content: React.ReactNode + showIcon: boolean + } +} + +export default class TableField extends DetailField implements IDetailField { + state: TableState + CCMS = CCMS + getALLComponents = (type: any) => getALLComponents[type] + interfaceHelper = new InterfaceHelper() + /** + * 页面权限获取状态 + * fulfilled |pending + */ + pageAuth: { [page: string]: boolean } = {} + /* 服务端分页情况下页码溢出标识:页码溢出时退回重新请求,此标识符用于防止死循环判断 */ + pageOverflow: boolean = false + + constructor(props: DetailFieldProps) { + super(props) + + this.state = { + operation: { + enable: false, + target: 'current', + title: '', + visible: false, + config: {}, + data: {}, + callback: false + }, + pageAuth: {} + } + } + + /** + * 执行操作 + * @param operation + * @param record + * @returns + */ + handleRowOperation = async (operation: TableOperationConfig, record: { [field: string]: any }) => { + const { + data, + step + } = this.props + if (operation.check && operation.check.enable) { + const checkResult = await this.interfaceHelper.request( + operation.check.interface, + {}, + { record, data, step }, + { loadDomain: this.props.loadDomain } + ) + if (!checkResult) { + return false + } + } + + if (operation.confirm && operation.confirm.enable) { + const title = operation.confirm.titleParams ? (await getParamText(operation.confirm.titleText, operation.confirm.titleParams, { record, data, step })) : operation.confirm.titleText + const showConfirm = () => { + return new Promise((resolve, reject) => { + if (operation.confirm && operation.confirm.enable) { + this.renderOperationConfirm({ + title, + okText: operation.confirm.okText, + cancelText: operation.confirm.cancelText, + onOk: () => { resolve(true) }, + onCancel: () => { reject(new Error('用户取消')) } + }) + } + }) + } + try { + await showConfirm() + } catch (e) { + return false + } + } + + if (operation.handle.type === 'ccms') { + const params = {} + if (operation.handle.params === undefined) { + for (const [field, param] of Object.entries(operation.handle.data || {})) { + const value = getParam(param, { record, data, step }) + set(params, field, value) + } + } else { + for (const { field, data: dataConfig } of operation.handle.params) { + const value = getParam(dataConfig, { record, data, step }) + set(params, field, value) + } + } + if (operation.handle.debug) { + console.log('CCMS debug: operation - params', params) + } + if (operation.handle.target === 'current' || operation.handle.target === 'handle') { + if (this.props.loadPageConfig) { + const operationConfig = await this.props.loadPageConfig(operation.handle.page) + + this.setState({ + operation: { + enable: true, + target: operation.handle.target, + title: operation.label, + visible: true, + config: operationConfig, + data: params, + callback: operation.handle.callback + } + }) + } + } else if (operation.handle.target === 'page') { + if (this.props.loadPageURL) { + const sourceURL = await this.props.loadPageURL(operation.handle.page) + const { url, query } = queryString.parseUrl(sourceURL, { arrayFormat: 'bracket' }) + const targetURL = operation.handle.targetURL || '' + const targetKey = queryString.stringifyUrl({ url, query: { ...query, ...params } }, { arrayFormat: 'bracket' }) || '' + if (this.props.handlePageRedirect) { + this.props.handlePageRedirect(`${targetURL}${targetKey}`) + } else { + window.location.href = `${targetURL}${targetKey}` + } + } + } else if (operation.handle.target === 'open') { + if (this.props.loadPageFrameURL) { + const sourceURL = await this.props.loadPageFrameURL(operation.handle.page) + const { url, query } = queryString.parseUrl(sourceURL, { arrayFormat: 'bracket' }) + const targetURL = operation.handle.targetURL || '' + const targetKey = queryString.stringifyUrl({ url, query: { ...query, ...params } }, { arrayFormat: 'bracket' }) || '' + window.open(`${targetURL}${targetKey}`) + } + } + } + + // 当按钮的响应类型是第三方链接时 + if (operation.handle.type === 'link') { + const params = {} + if (operation.handle.params !== undefined) { + for (const { field, data: dataConfig } of operation.handle.params) { + const value = getParam(dataConfig, { record, data, step }) + set(params, field, value) + } + + if (operation.handle.debug) { + console.log('CCMS debug: operation - operation.handle.type === link', params) + } + + // 提取跳转URL地址,合并URL参数拼接 + const targetURL = operation.handle.targetURL || '' + const urlParams = this.queryUrlParams(targetURL) + const query = urlParams.paramsResult + const hash = urlParams.hashResult.hash?.replace('#/', '') + const url = targetURL?.split('#')[0]?.split('?')[0] + // const { url, query, fragmentIdentifier } = queryString.parseUrl(targetURL, { arrayFormat: 'bracket', parseFragmentIdentifier: true }) + const jumpUrl = queryString.stringifyUrl({ url, query: { ...query, ...params }, fragmentIdentifier: hash }, { arrayFormat: 'bracket' }) || '' + + this.urlJumpHandler(jumpUrl, operation.handle.target) + } + } + } + + /** + * 获取URL中的路由和参数 + * @param url url地址 + */ + queryUrlParams = (url: string) => { + const paramsResult: any = {} + const hashResult: any = {} + const paramReg = /([^?=&#]+)=([^?=&#]+)/g + const hashReg = /#[^?=&#]+/g + // eslint-disable-next-line no-return-assign + url.replace(paramReg, (n, x, y) => paramsResult[x] = y) + // eslint-disable-next-line no-return-assign + url.replace(hashReg, (n) => hashResult.hash = n) + return { paramsResult, hashResult } + } + + /** + * 链接跳转处理方法 + * @param url 跳转地址 + * @param target 跳转方式 + */ + urlJumpHandler = (url: string, target: string) => { + const a = document.createElement('a') + document.body.appendChild(a) + a.href = url + a.target = target + a.click() + document.body.removeChild(a) + } + + /** + * 渲染 操作二次确认弹窗 + * @param props + */ + renderOperationConfirm = (props: ITableStepOperationConfirm) => { + const mask = document.createElement('DIV') + mask.style.position = 'fixed' + mask.style.left = '0px' + mask.style.top = '0px' + mask.style.width = '100%' + mask.style.height = '100%' + mask.style.backgroundColor = 'white' + mask.innerText = '您当前使用的UI版本没有实现Table的OperationConfirm组件。' + mask.onclick = () => { + mask.remove() + props.onOk() + } + + document.body.appendChild(mask) + } + + checkPageAuth = (page: string) => { + if (!this.pageAuth[page]) { + this.pageAuth[page] = true + this.props.checkPageAuth && this.props.checkPageAuth(page).then((auth) => { + const pageAuth = cloneDeep(this.state.pageAuth) + pageAuth[page] = auth + this.setState({ pageAuth }) + }) + } + } + + + /** + * 渲染 表格 + * @param props + * @returns + */ + renderComponent = (props: ITableField) => { + return + 您当前使用的UI版本没有实现Table组件。 +
+
+
+ } + + renderRowOperationComponent = (props: ITableStepRowOperation) => { + return + 您当前使用的UI版本没有实现Table组件的OperationButton部分。 + + } + + renderRowOperationButtonComponent = (props: ITableStepRowOperationButton) => { + return + 您当前使用的UI版本没有实现Table组件的OperationButton部分。 + + } + + renderRowOperationGroupComponent = (props: ITableStepRowOperationGroup) => { + return + 您当前使用的UI版本没有实现Table组件的OperationGroup部分。 + + } + + renderRowOperationGroupItemComponent = (props: ITableStepRowOperationGroupItem) => { + return + 您当前使用的UI版本没有实现Table组件的OperationGroupItem部分。 + + } + + renderTableOperationComponent = (props: ITableStepTableOperation) => { + return + 您当前使用的UI版本没有实现Table组件的OperationButton部分。 + + } + + renderTableOperationButtonComponent = (props: ITableStepTableOperationButton) => { + return + 您当前使用的UI版本没有实现Table组件的OperationButton部分。 + + } + + renderTableOperationGroupComponent = (props: ITableStepTableOperationGroup) => { + return + 您当前使用的UI版本没有实现Table组件的OperationGroup部分。 + + } + + renderTableOperationGroupItemComponent = (props: ITableStepTableOperationGroupItem) => { + return + 您当前使用的UI版本没有实现Table组件的OperationGroupItem部分。 + + } + + renderOperationModal = (props: ITableStepOperationModal) => { + const mask = document.createElement('DIV') + mask.style.position = 'fixed' + mask.style.left = '0px' + mask.style.top = '0px' + mask.style.width = props.width || '100%' + mask.style.height = '100%' + mask.style.backgroundColor = 'white' + mask.innerText = '您当前使用的UI版本没有实现Table的OperationModal组件。' + mask.onclick = () => { + mask.remove() + props.onClose() + } + + document.body.appendChild(mask) + } + + + render = () => { + const { + config: { + field, + label, + // width, + primary, + tableColumns, + operations, + pagination, + // description + }, + data, + step, + onUnmount, + value + } = this.props + + const { + operation: { + enable: operationEnable, + target: operationTarget, + title: operationTitle, + visible: operationVisible, + config: operationConfig, + data: operationData, + callback: operationCallback + }, + pageAuth + } = this.state + + // let getDate = field ? getValue(data[step], field) : data[step] + let getDate = value || [] + if (Object.prototype.toString.call(getDate) !== '[object Array]') { + getDate = [] + } + const props: ITableField = { + title: label, + primary, + data: getDate as { [field: string]: any; }[], + tableColumns: (tableColumns || []).filter((column) => column.field !== undefined && column.field !== '').map((column, index) => { + const field = column.field.split('.')[0] + return { + field, + label: column.label, + align: column.align, + render: (value: any, record: { [field: string]: any }) => { + if (value && Object.prototype.toString.call(value) === '[object Object]') { + value = getValue(value, column.field.replace(field, '').slice(1)) + } + + const Column = this.getALLComponents(column.type) + if (Column) { + const addfix = ['multirowText'].some((val) => val !== column.field) + return + + + } + } + } + }) + } + if (pagination && pagination.mode === 'server') { + const paginationCurrent = Number((pagination.current === undefined || pagination.current === '') ? data[step] : get(data[step], pagination.current, 1)) + const paginationPageSize = Number((pagination.pageSize === undefined || pagination.pageSize === '') ? data[step] : get(data[step], pagination.pageSize, 10)) + const paginationTotal = Number((pagination.total === undefined || pagination.total === '') ? data[step] : get(data[step], pagination.total, 0)) + + props.pagination = { + current: Number.isNaN(paginationCurrent) ? 1 : paginationCurrent, + pageSize: Number.isNaN(paginationPageSize) ? 10 : paginationPageSize, + total: Number.isNaN(paginationTotal) ? 0 : paginationTotal, + onChange: (page, pageSize) => { + this.props.onUnmount && this.props.onUnmount(true, { + [pagination.current || '']: page, + [pagination.pageSize || '']: pageSize + }) + } + } + + if (!this.pageOverflow && props.pagination.current > 1 && (props.pagination.current - 1) * props.pagination.pageSize >= props.pagination.total) { + this.pageOverflow = true + this.props.onUnmount && this.props.onUnmount(true, { + [pagination.current || '']: 1, + [pagination.pageSize || '']: props.pagination.pageSize + }) + } + } + + if (operations && operations.rowOperations && operations.rowOperations.length > 0) { + props.tableColumns.push({ + field: 'ccms-table-rowOperation', + label: '操作', + align: 'left', + render: (_value: any, record: { [field: string]: any }) => { + if (operations.rowOperations) { + return this.renderRowOperationComponent({ + children: (operations.rowOperations || []).map((operation, index) => { + if (operation.type === 'button') { + if (!ConditionHelper(operation.condition, { record, data, step })) { + return null + } + + let hidden = false + if (operation.handle && operation.handle.type === 'ccms') { + hidden = operation.handle.page === undefined || !false + operation.handle.page !== undefined && this.checkPageAuth(operation.handle.page.toString()) + } + + return ( + + {hidden + ? null + : this.renderRowOperationButtonComponent({ + label: operation.label, + level: operation.level || 'normal', + onClick: async () => { await this.handleRowOperation(operation, record) } + })} + + ) + } else if (operation.type === 'group') { + return ( + + {this.renderRowOperationGroupComponent({ + label: operation.label, + children: (operation.operations || []).map((operation) => { + if (!ConditionHelper(operation.condition, { record, data, step })) { + return null + } + + let hidden = false + if (operation.handle && operation.handle.type === 'ccms') { + hidden = operation.handle.page === undefined || !false + operation.handle.page !== undefined && this.checkPageAuth(operation.handle.page.toString()) + } + + return hidden + ? null + : this.renderRowOperationGroupItemComponent({ + label: operation.label, + level: operation.level || 'normal', + onClick: async () => { await this.handleRowOperation(operation, record) } + }) + }) + })} + + ) + } else { + return + } + }) + }) + } else { + return + } + } + }) + } + + const CCMS = this.CCMS + console.log('props:', props); + return ( + + {this.renderComponent(props)} + {operationEnable && ( + operationTarget === 'current' + ? ( + this.renderOperationModal({ + title: operationTitle, + width: '500', + visible: operationVisible, + children: ( + Promise} + loadPageURL={this.props.loadPageURL as (pageID: any) => Promise} + loadPageFrameURL={this.props.loadPageFrameURL as (pageID: any) => Promise} + loadPageConfig={this.props.loadPageConfig as (pageID: any) => Promise} + loadDomain={this.props.loadDomain} + handlePageRedirect={this.props.handlePageRedirect} + onMount={() => { + const { operation } = this.state + operation.visible = true + this.setState({ operation }) + }} + callback={() => { + const { operation } = this.state + operation.enable = false + operation.visible = false + this.setState({ operation }) + + if ((operationCallback && operationCallback === true) || Boolean(operationCallback)) { + onUnmount && onUnmount(true) + } + }} + /> + ), + onClose: () => { + const { operation } = this.state + operation.enable = false + operation.visible = false + this.setState({ operation }) + } + }) + ) + : ( + true} + loadPageURL={this.props.loadPageURL as (pageID: any) => Promise} + loadPageFrameURL={this.props.loadPageFrameURL as (pageID: any) => Promise} + loadPageConfig={this.props.loadPageConfig as (pageID: any) => Promise} + loadDomain={this.props.loadDomain} + handlePageRedirect={this.props.handlePageRedirect} + onMount={() => { + const { operation } = this.state + operation.visible = true + this.setState({ operation }) + }} + callback={() => { + const { operation } = this.state + operation.enable = false + operation.visible = false + this.setState({ operation }) + + if ((operationCallback && operationCallback === true) || Boolean(operationCallback)) { + onUnmount && onUnmount(true) + } + }} + /> + ) + )} + + ) + } +} diff --git a/src/index.tsx b/src/index.tsx index af50ce4..93425aa 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -60,6 +60,7 @@ export { default as DetailTextField } from './components/detail/text' export { default as DetailImportSubformField } from './components/detail/importSubform' export { default as DetailInfoField } from './components/detail/detailInfo' export { default as DetailColorField } from './components/detail/detailColor' +export { default as DetailTableField } from './components/detail/table' export { default as HeaderStep } from './steps/header' diff --git a/src/steps/detail/index.tsx b/src/steps/detail/index.tsx index e877424..8733659 100644 --- a/src/steps/detail/index.tsx +++ b/src/steps/detail/index.tsx @@ -434,12 +434,12 @@ export default class DetailStep extends Step { // message: detailFieldConfig.field !== undefined ? getValue(detailData, detailFieldConfig.field, {}).message || '' : '', columns: config.columns?.enable ? { - type: detailFieldConfig.columns?.type || config.columns?.type || 'span', - value: detailFieldConfig.columns?.value || config.columns?.value || 1, - wrap: detailFieldConfig.columns?.wrap || config.columns?.wrap || false, - gap: detailFieldConfig.columns?.gap || config.columns?.gap || 0, - rowGap: detailFieldConfig.columns?.rowGap || config.columns?.rowGap || 0 - } + type: detailFieldConfig.columns?.type || config.columns?.type || 'span', + value: detailFieldConfig.columns?.value || config.columns?.value || 1, + wrap: detailFieldConfig.columns?.wrap || config.columns?.wrap || false, + gap: detailFieldConfig.columns?.gap || config.columns?.gap || 0, + rowGap: detailFieldConfig.columns?.rowGap || config.columns?.rowGap || 0 + } : undefined, layout, styles: detailFieldConfig.styles || {}, @@ -447,6 +447,12 @@ export default class DetailStep extends Step { fieldType: detailFieldConfig.type, children: ( this.props.handlePageRedirect} key={detailFieldIndex} ref={(detailField: DetailField | null) => { if (detailFieldIndex !== null) { -- Gitee From 7174325d60b06967f99b53cb2fa58c1adf44150b Mon Sep 17 00:00:00 2001 From: zjt Date: Wed, 30 Mar 2022 10:23:55 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/detail/common.tsx | 12 +- src/components/detail/group/index.tsx | 65 +++++---- src/components/detail/table/index.tsx | 191 +++++++++++++++----------- 3 files changed, 156 insertions(+), 112 deletions(-) diff --git a/src/components/detail/common.tsx b/src/components/detail/common.tsx index 69560e3..ac8a055 100644 --- a/src/components/detail/common.tsx +++ b/src/components/detail/common.tsx @@ -90,12 +90,12 @@ export interface DetailFieldProps { onValueListSplice: (path: string, index: number, count: number, validation: true | DetailFieldError[]) => Promise baseRoute: string, loadDomain: (domain: string) => Promise - loadPageConfig?: (pageID: any) => Promise - handlePageRedirect?: (path: string) => void - checkPageAuth?: (pageID: any) => Promise - onUnmount?: (reload?: boolean, data?: any) => Promise - loadPageURL?: (pageID: any) => Promise - loadPageFrameURL?: (pageID: any) => Promise + loadPageConfig: (pageID: any) => Promise + handlePageRedirect: (path: string) => void + checkPageAuth: (pageID: any) => Promise + onUnmount: (reload?: boolean, data?: any) => Promise + loadPageURL: (pageID: any) => Promise + loadPageFrameURL: (pageID: any) => Promise } /** diff --git a/src/components/detail/group/index.tsx b/src/components/detail/group/index.tsx index c7dd58d..3bbf31a 100644 --- a/src/components/detail/group/index.tsx +++ b/src/components/detail/group/index.tsx @@ -30,7 +30,7 @@ export default class GroupField extends DetailField | null> = [] detailFieldsMounted: Array = [] - constructor (props: DetailFieldProps) { + constructor(props: DetailFieldProps) { super(props) this.state = { @@ -250,40 +250,47 @@ export default class GroupField extends DetailField | null) => { - if (detailFieldIndex !== null) { - this.detailFields[detailFieldIndex] = detailField - this.handleMount(detailFieldIndex) - } - }} - formLayout={formLayout} - value={getValue(value, detailFieldConfig.field)} - record={record} - data={cloneDeep(data)} - step={step} - config={detailFieldConfig} - onChange={async (value: any) => { await this.handleChange(detailFieldIndex, value) }} - onValueSet={async (path, value, validation) => this.handleValueSet(detailFieldIndex, path, value, validation)} - onValueUnset={async (path, validation) => this.handleValueUnset(detailFieldIndex, path, validation)} - onValueListAppend={async (path, value, validation) => this.handleValueListAppend(detailFieldIndex, path, value, validation)} - onValueListSplice={async (path, index, count, validation) => this.handleValueListSplice(detailFieldIndex, path, index, count, validation)} - baseRoute={this.props.baseRoute} - loadDomain={async (domain: string) => await this.props.loadDomain(domain)} - /> + | null) => { + if (detailFieldIndex !== null) { + this.detailFields[detailFieldIndex] = detailField + this.handleMount(detailFieldIndex) + } + }} + formLayout={formLayout} + value={getValue(value, detailFieldConfig.field)} + record={record} + data={cloneDeep(data)} + step={step} + config={detailFieldConfig} + detail={this.props.detail} + onChange={async (value: any) => { await this.handleChange(detailFieldIndex, value) }} + onValueSet={async (path, value, options) => this.handleValueSet(detailFieldIndex, path, value, options)} + onValueUnset={async (path, options) => this.handleValueUnset(detailFieldIndex, path, options)} + onValueListAppend={async (path, value, options) => this.handleValueListAppend(detailFieldIndex, path, value, options)} + onValueListSplice={async (path, index, count, options) => this.handleValueListSplice(detailFieldIndex, path, index, count, options)} + baseRoute={this.props.baseRoute} + loadDomain={async (domain: string) => await this.props.loadDomain(domain)} + /> ) } // 渲染表单项容器 diff --git a/src/components/detail/table/index.tsx b/src/components/detail/table/index.tsx index 17b409d..3a31698 100644 --- a/src/components/detail/table/index.tsx +++ b/src/components/detail/table/index.tsx @@ -2,7 +2,6 @@ import React from 'react' import queryString from 'query-string' import { getParam, getParamText, getValue } from '../../../util/value' import { getBoolean } from '../../../util/value' -import Step, { StepConfig, StepProps } from '../../../steps/common' import { DetailField, DetailFieldConfig, DetailFieldError, IDetailField } from '../common' import getALLComponents, { ColumnConfigs } from '../../tableColumns' import CCMS, { CCMSConfig } from '../../../main' @@ -12,6 +11,7 @@ import { ParamConfig } from '../../../interface' import { DetailFieldProps } from '../common' import { cloneDeep, get, set } from 'lodash' import ColumnStyleComponent from './common/columnStyle' +import Column from '../../tableColumns/common' export interface TableFieldConfig extends DetailFieldConfig { @@ -21,7 +21,7 @@ export interface TableFieldConfig extends DetailFieldConfig { primary: string tableColumns: ColumnConfigs[] operations?: { - rowOperations?: Array + rowOperations?: Array } pagination?: { mode: 'none' | 'client' | 'server' @@ -31,6 +31,12 @@ export interface TableFieldConfig extends DetailFieldConfig { } } + +/** + * 表格步骤-菜单配置 + */ +export type TableOperationsType = TableOperationConfig | TableOperationGroupConfig | TableOperationDropdownConfig + /** * 表格步骤-操作配置文件格式 */ @@ -41,6 +47,16 @@ export interface TableOperationGroupConfig { operations: Array } +/** + * 表格步骤-操作配置文件下拉菜单 + */ +export interface TableOperationDropdownConfig { + type: 'dropdown' + label?: string + level?: 'normal' | 'primary' | 'danger' + operations: Array +} + /** * 表格步骤-操作配置文件格式 */ @@ -100,21 +116,21 @@ export interface ITableColumn { /** * 表格步骤组件 - 操作 - UI渲染方法 - 入参 */ -export interface ITableStepRowOperation { +export interface ITableDetailRowOperation { children: (React.ReactNode | undefined)[] } /** * 表格步骤组件 - 操作 - UI渲染方法 - 入参 */ -export interface ITableStepTableOperation { +export interface ITableDetailTableOperation { children: (React.ReactNode | undefined)[] } /** * 表格步骤组件 - 操作按钮 - UI渲染方法 - 入参 */ -export interface ITableStepRowOperationButton { +export interface ITableDetailRowOperationButton { label: string level: 'normal' | 'primary' | 'danger' disabled?: boolean @@ -124,7 +140,7 @@ export interface ITableStepRowOperationButton { /** * 表格步骤组件 - 操作按钮组 - UI渲染方法 - 入参 */ -export interface ITableStepRowOperationGroup { +export interface ITableDetailRowOperationGroup { label?: string children: React.ReactNode[] } @@ -132,7 +148,7 @@ export interface ITableStepRowOperationGroup { /** * 表格步骤组件 - 操作按钮组元素 - UI渲染方法 - 入参 */ -export interface ITableStepRowOperationGroupItem { +export interface ITableDetailRowOperationGroupItem { label: string level: 'normal' | 'primary' | 'danger' disabled?: boolean @@ -142,7 +158,7 @@ export interface ITableStepRowOperationGroupItem { /** * 表格步骤组件 - 操作按钮 - UI渲染方法 - 入参 */ -export interface ITableStepTableOperationButton { +export interface ITableDetailTableOperationButton { label: string level: 'normal' | 'primary' | 'danger' disabled?: boolean @@ -152,7 +168,7 @@ export interface ITableStepTableOperationButton { /** * 表格步骤组件 - 操作按钮组 - UI渲染方法 - 入参 */ -export interface ITableStepTableOperationGroup { +export interface ITableDetailTableOperationGroup { label?: string children: React.ReactNode[] } @@ -160,7 +176,7 @@ export interface ITableStepTableOperationGroup { /** * 表格步骤组件 - 操作按钮组元素 - UI渲染方法 - 入参 */ -export interface ITableStepTableOperationGroupItem { +export interface ITableDetailTableOperationGroupItem { label: string level: 'normal' | 'primary' | 'danger' disabled?: boolean @@ -170,7 +186,7 @@ export interface ITableStepTableOperationGroupItem { /** * 表格步骤组件 - 操作 - 二次确认 - UI渲染方法 - 入参 */ -export interface ITableStepOperationConfirm { +export interface ITableDetailOperationConfirm { title: string okText: string cancelText: string @@ -178,7 +194,7 @@ export interface ITableStepOperationConfirm { onCancel: () => void } -export interface ITableStepOperationModal { +export interface ITableDetailOperationModal { title: string visible: boolean width: string @@ -224,10 +240,9 @@ export interface ITableField { } } -export default class TableField extends DetailField implements IDetailField { - state: TableState +export default class TableField extends DetailField implements IDetailField { CCMS = CCMS - getALLComponents = (type: any) => getALLComponents[type] + getALLComponents = (type: any): typeof Column => getALLComponents[type] interfaceHelper = new InterfaceHelper() /** * 页面权限获取状态 @@ -315,42 +330,38 @@ export default class TableField extends DetailField { + renderOperationConfirm = (props: ITableDetailOperationConfirm) => { const mask = document.createElement('DIV') mask.style.position = 'fixed' mask.style.left = '0px' @@ -435,6 +446,7 @@ export default class TableField extends DetailField { if (!this.pageAuth[page]) { this.pageAuth[page] = true + this.props.checkPageAuth && this.props.checkPageAuth(page).then((auth) => { const pageAuth = cloneDeep(this.state.pageAuth) pageAuth[page] = auth @@ -457,55 +469,43 @@ export default class TableField extends DetailField } - renderRowOperationComponent = (props: ITableStepRowOperation) => { + renderRowOperationComponent = (props: ITableDetailRowOperation) => { return 您当前使用的UI版本没有实现Table组件的OperationButton部分。 } - renderRowOperationButtonComponent = (props: ITableStepRowOperationButton) => { + renderRowOperationButtonComponent = (props: ITableDetailRowOperationButton) => { return 您当前使用的UI版本没有实现Table组件的OperationButton部分。 } - renderRowOperationGroupComponent = (props: ITableStepRowOperationGroup) => { + renderRowOperationGroupComponent = (props: ITableDetailRowOperationGroup) => { return 您当前使用的UI版本没有实现Table组件的OperationGroup部分。 } - renderRowOperationGroupItemComponent = (props: ITableStepRowOperationGroupItem) => { + renderRowOperationGroupItemComponent = (props: ITableDetailRowOperationGroupItem) => { return 您当前使用的UI版本没有实现Table组件的OperationGroupItem部分。 } - renderTableOperationComponent = (props: ITableStepTableOperation) => { + renderRowOperationDropdownComponent = (props: ITableDetailRowOperationGroup) => { return - 您当前使用的UI版本没有实现Table组件的OperationButton部分。 + 您当前使用的UI版本没有实现Table组件的OperationDropdown部分。 } - renderTableOperationButtonComponent = (props: ITableStepTableOperationButton) => { + renderRowOperationDropdownItemComponent = (props: ITableDetailRowOperationGroupItem) => { return - 您当前使用的UI版本没有实现Table组件的OperationButton部分。 + 您当前使用的UI版本没有实现Table组件的OperationDropdownItem部分。 } - renderTableOperationGroupComponent = (props: ITableStepTableOperationGroup) => { - return - 您当前使用的UI版本没有实现Table组件的OperationGroup部分。 - - } - - renderTableOperationGroupItemComponent = (props: ITableStepTableOperationGroupItem) => { - return - 您当前使用的UI版本没有实现Table组件的OperationGroupItem部分。 - - } - - renderOperationModal = (props: ITableStepOperationModal) => { + renderOperationModal = (props: ITableDetailOperationModal) => { const mask = document.createElement('DIV') mask.style.position = 'fixed' mask.style.left = '0px' @@ -559,10 +559,11 @@ export default class TableField extends DetailField column.field !== undefined && column.field !== '').map((column, index) => { const field = column.field.split('.')[0] return { @@ -579,11 +580,15 @@ export default class TableField extends DetailField val !== column.field) return { }} record={record} value={value} data={data} step={step} config={column} + column={this} + baseRoute={this.props.baseRoute} + loadDomain={async (domain: string) => await this.props.loadDomain(domain)} /> } @@ -632,11 +637,12 @@ export default class TableField extends DetailField