diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/blocks/GroupBlock.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/blocks/GroupBlock.tsx index db3223c63..d8b485011 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/blocks/GroupBlock.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/schema-components/blocks/GroupBlock.tsx @@ -1,9 +1,10 @@ import { useField, useFieldSchema } from '@formily/react'; import { findFilterTargets, useAPIClient, useBlockRequestContext, useFilterBlock } from '@nocobase/client'; -import { Descriptions, DescriptionsProps, Spin } from 'antd'; +import { Descriptions, DescriptionsProps, Space, Spin } from 'antd'; import { transformers } from './GroupBlockConfigure/transformers'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useAsyncEffect } from 'ahooks'; +import { GroupConfigure } from './GroupBlockConfigure/GroupConfigure'; type DataItem = { labels: string[]; @@ -20,11 +21,8 @@ export const GroupBlock = (props) => { const params = fieldSchema.parent['x-decorator-props'].params; const { resource, service } = useBlockRequestContext(); const { getDataBlocks } = useFilterBlock(); - const { targets = [], uid } = findFilterTargets(fieldSchema); - const api = useAPIClient(); - const [result, setResult] = useState([]); - const items: DescriptionsProps['items'] = []; const Blocks = getDataBlocks(); + const [group, setGroup] = useState([]); if (!service.params.length) { getDataBlocks().map((block) => { if (Object.keys(block.defaultFilter).length) { @@ -37,84 +35,35 @@ export const GroupBlock = (props) => { }); } - useAsyncEffect(async () => { - const filter = service?.params[0] ? service.params[0].filter : service?.params; - setResult( - await Promise.all( - params?.config?.request?.map(async (requeatItem) => { - return [ - await api.request({ - url: requeatItem.fieldFormat.requestUrl, - method: 'POST', - data: { - filter: { ...filter }, - collection: params.collection, - }, - }), - requeatItem, - ]; - }) ?? [], - ), - ); - }, [Blocks, service.params, service.params[0]]); - if (service.loading && !field.loaded) { return ; } - const data = service.data?.data[0] ? { ...service.data?.data[0] } : {}; - - params?.config?.measures?.forEach((measuresItem) => { - const value = measuresItem.fieldFormat.fieldValue; - if (!data[value]) data[value] = 0; - data[value] = fieldTransformers(measuresItem, data[value], api); - }); - - for (const key in data) { - params?.config?.measures?.forEach((value) => { - if (value.field[0] === key) { - if (value.display) { - items.push({ - key: key, - label: value.label, - children: data[key], - }); + return ( + <> +

汇总:

+ {params?.config.map((configItem, index) => { + if (configItem) { + return ; } - } - }); - } - - result.forEach(([data, requeatItem]) => { - data?.data?.data?.forEach((requestData: requestData) => { - const dataItem = { key: requestData.label, label: requestData.label, children: '' }; - if (typeof requestData.value === 'object') { - requestData.value.labels.forEach((labelItem, index) => { - dataItem.children = - dataItem.children + - ` ${labelItem} ${fieldTransformers(requeatItem, requestData.value['values'][index], api)}`; - }); - } else { - dataItem.children = fieldTransformers(requeatItem, requestData.value.toString(), api); - } - items.push(dataItem); - }); - }); - return ; + })} + + ); }; -const fieldTransformers = (item, data, api) => { +export const fieldTransformers = (item, data, api) => { const { option: tOption } = transformers; const locale = api.auth.getLocale(); - if (item.fieldFormat) { - const option = item.fieldFormat.option; - const decimal = item.fieldFormat.decimal; - if (option && option !== 'decimal') { - const component = tOption.filter((tValue) => tValue.value === option)[0].component; + if (item) { + const format = item.format; + const digits = item?.digits; + if (format && format !== 'decimal') { + const component = tOption.find((tValue) => tValue.value === format).component; data = String(data).includes(',') ? String(data).replace(/,/g, '') : data; return component(data, locale); - } else if (option && option === 'decimal') { + } else if (format && format === 'decimal' && digits) { const component = tOption .filter((tValue) => tValue.value === 'decimal')[0] - .childrens.filter((decimalOption) => decimalOption.value === decimal)[0].component; + .childrens.filter((decimalOption) => decimalOption.value === digits)[0].component; data = String(data).includes(',') ? String(data).replace(/,/g, '') : data; return component(data); } diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/blocks/GroupBlockConfigure/GroupBlockConfigure.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/blocks/GroupBlockConfigure/GroupBlockConfigure.tsx index 7c4fcd866..2f2475711 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/blocks/GroupBlockConfigure/GroupBlockConfigure.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/schema-components/blocks/GroupBlockConfigure/GroupBlockConfigure.tsx @@ -1,21 +1,205 @@ -import { SchemaSettingsItem } from '@nocobase/client'; +import { + SchemaComponentOptions, + SchemaSettingsItem, + SchemaSettingsModalItem, + useBlockRequestContext, + useDesignable, +} from '@nocobase/client'; import React, { useContext } from 'react'; import { useTranslation } from '../../../locale'; -import { Modal } from 'antd'; import { GroupBlockContext } from '../../../schema-initializer/blocks/GroupBlockInitializer'; +import { transformers } from './transformers'; +import { ISchema, connect, useFieldSchema } from '@formily/react'; +import { ArrayItems, FormItem, Space } from '@formily/antd-v5'; +import { PullRequestOutlined } from '@ant-design/icons'; -export const GroupBlockConfigure = (props) => { +export const GroupBlockConfigure = connect((props) => { const { t } = useTranslation(); - const { setVisible, visible } = useContext(GroupBlockContext); + const fieldSchema = useFieldSchema(); + const { dn } = useDesignable(); + const { service } = useBlockRequestContext(); + + const params = fieldSchema['x-decorator-props']?.params; + const measures = params?.measures; + const decimal = transformers.option.filter((item) => item.value === 'decimal')[0].childrens; + const valueOption = measures.map((item) => { + return { + label: item.label, + value: item.field[0], + }; + }); + const { scope } = useContext(GroupBlockContext); + const schema = modalSchema(t, params, valueOption, decimal); return ( - { - setVisible(true); - }} - > - {t('Configure')} - + + { + fieldSchema['x-decorator-props'].params['config'] = [...fieldOptionItem.configField]; + dn.emit('patch', { + schema: { + 'x-uid': fieldSchema['x-uid'], + 'x-decorator-props': fieldSchema['x-decorator-props'], + }, + }); + dn.refresh(); + service?.refresh(); + }} + schema={schema as ISchema} + /> + ); +}); + +const fieldType = [ + { + label: '字段配置', + value: 'field', + }, + { + label: '请求配置', + value: 'custom', + }, +]; + +const styleOption = [ + { + label: '描述', + value: 'describe', + }, + { + label: '表格', + value: 'table', + }, +]; + +const modalSchema = (t, params, valueOption, decimal) => { + const schema = { + title: t('Configure Group'), + type: 'object', + properties: { + configField: { + title: 'Config Field', + type: 'array', + 'x-decorator': 'FormItem', + 'x-component': 'ArrayItems', + default: params.config, + items: { + type: 'object', + 'x-decorator': 'ArrayItems.Item', + properties: { + space: { + type: 'void', + 'x-component': 'Space', + properties: { + sort: { + type: 'void', + 'x-decorator': 'FormItem', + 'x-component': 'ArrayItems.SortHandle', + }, + type: { + type: 'string', + 'x-decorator': 'FormItem', + 'x-component': 'Select', + 'x-component-props': { + placeholder: '{{t("type")}}', + }, + enum: fieldType, + require: true, + }, + field: { + type: 'string', + 'x-decorator': 'FormItem', + 'x-component': 'Select', + 'x-component-props': { + placeholder: '{{t("field")}}', + }, + 'x-visbile': false, + enum: valueOption, + 'x-reactions': { + dependencies: ['.type'], + fulfill: { + schema: { + 'x-visible': "{{$deps[0]==='field'}}", + }, + }, + }, + }, + reqUrl: { + type: 'string', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-component-props': { + placeholder: '{{t("reqUrl")}}', + addonBefore: , + }, + 'x-visible': false, + 'x-reactions': { + dependencies: ['.type'], + fulfill: { + schema: { + 'x-visible': "{{$deps[0]==='custom'}}", + }, + }, + }, + }, + format: { + type: 'string', + 'x-decorator': 'FormItem', + 'x-component': 'Select', + 'x-component-props': { + placeholder: '{{t("format")}}', + }, + enum: transformers.option, + require: true, + }, + digits: { + type: 'string', + 'x-decorator': 'FormItem', + 'x-component': 'Select', + 'x-component-props': { + placeholder: '{{t("digits")}}', + }, + enum: decimal, + 'x-visible': false, + 'x-reactions': { + dependencies: ['.format'], + fulfill: { + schema: { + 'x-visible': "{{$deps[0]==='decimal'}}", + }, + }, + }, + }, + style: { + type: 'string', + 'x-decorator': 'FormItem', + 'x-component': 'Select', + 'x-component-props': { + placeholder: '{{t("style")}}', + }, + enum: styleOption, + require: true, + }, + remove: { + type: 'void', + 'x-decorator': 'FormItem', + 'x-component': 'ArrayItems.Remove', + }, + }, + }, + }, + }, + properties: { + add: { + type: 'void', + title: 'Add', + 'x-component': 'ArrayItems.Addition', + }, + }, + }, + }, + }; + return schema; }; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/blocks/GroupBlockConfigure/GroupConfigure.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/blocks/GroupBlockConfigure/GroupConfigure.tsx index 3f7a3f391..639f05448 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/blocks/GroupBlockConfigure/GroupConfigure.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/schema-components/blocks/GroupBlockConfigure/GroupConfigure.tsx @@ -1,231 +1,132 @@ import { useField, useFieldSchema } from '@formily/react'; -import { useDesignable, useFieldNames, useFilterBlock } from '@nocobase/client'; -import { App, Button, Flex, Input, Modal, Select } from 'antd'; -import React, { useContext, useEffect, useState } from 'react'; -import { useTranslation } from '../../../locale'; -import { GroupBlockContext } from '../../../schema-initializer/blocks/GroupBlockInitializer'; -import { transformers } from './transformers'; -import { DeleteOutlined, PullRequestOutlined } from '@ant-design/icons'; -import { uid } from '@formily/shared'; +import { useAPIClient, useFilterBlock, useRequest } from '@nocobase/client'; +import { Descriptions, DescriptionsProps, Spin, Table } from 'antd'; +import React, { useState } from 'react'; +import { useAsyncEffect } from 'ahooks'; +import { fieldTransformers } from '../GroupBlock'; -export type SelectedField = { - field: string | string[]; - alias?: string; -}; -export type fieldOptionType = { - label: string; - fieldFormat: { - fieldValue: string; - option: string; - type: string; - decimal?: string; - requestUrl?: string; - }; - aggregation?: string; - field?: [string]; - display?: boolean; +type reqData = { + labels: any[]; + values: any[]; }; export const GroupConfigure = (props) => { - const { visible, setVisible } = useContext(GroupBlockContext); - const { t } = useTranslation(); - const { dn } = useDesignable(); - const { modal } = App.useApp(); + const { configItem, service } = props; const fieldSchema = useFieldSchema(); - const measures = fieldSchema['x-decorator-props']?.params?.measures; - const option: fieldOptionType[] = []; - const params = fieldSchema['x-decorator-props']?.params; - if (params['config']) { - if (params['config']['measures']) { - option.push(...params['config']['measures']); + const field = useField(); + const params = fieldSchema.parent['x-decorator-props'].params; + const [result, setResult] = useState({}); + const { getDataBlocks } = useFilterBlock(); + const Blocks = getDataBlocks(); + const api = useAPIClient(); + useAsyncEffect(async () => { + const filter = service?.params[0] ? service.params[0].filter : service?.params; + if (configItem.reqUrl) { + setResult( + (await api.request({ + url: configItem.reqUrl, + method: 'POST', + data: { + filter: { ...filter }, + collection: params.collection, + }, + })) ?? {}, + ); } - if (params['config']['request']) { - option.push(...params['config']['request']); + }, [Blocks, service.params, service.params[0]]); + if (configItem.style === 'describe') { + const item: DescriptionsProps['items'] = describeItem(configItem, result, service, params, api); + return ; + } else if (configItem.style === 'table') { + const { columns, options } = tableItem(configItem, result, service, params, api); + return ; + } +}; + +const describeItem = (configItem, result, service, params, api) => { + const item = []; + if (configItem.type === 'field') { + const measuresData = service.data?.data; + if (measuresData) { + let data = measuresData.map((value) => { + return value[configItem.field]; + })[0]; + data = fieldTransformers(configItem, data, api); + const label = params.measures.find((value) => value.field[0] === configItem.field).label; + item.push({ + key: configItem.field, + label, + children: data, + }); + } + } else if (configItem.type === 'custom') { + if (Object.keys(result).length && result?.['data']?.data) { + const data: reqData = { ...result?.['data']?.data }; + const label = data.labels; + data.values.forEach((valueItem, index) => { + let childrenText = ''; + label.forEach((value, index) => { + if (index === 0) return; + childrenText += `${value}${fieldTransformers(configItem, valueItem[index], api)} `; + }); + item.push({ + key: index, + label: valueItem[0], + children: childrenText, + }); + }); } } + return item; +}; - const [fieldOption, setFieldOption] = useState(JSON.parse(JSON.stringify(option))); - const fieldType = [ - { - label: '字段配置', - value: 'field', - }, - { - label: '请求配置', - value: 'custom', - }, - ]; - const valueOption = measures.map((item) => { - return { - label: item.label, - value: item.field[0], - }; - }); - - const decimal = transformers.option.filter((item) => item.value === 'decimal')[0].childrens; - useEffect(() => { - if (!visible) { - return; - } - }, [visible]); - return ( - { - const request = []; - const configMeasures = []; - fieldOption.forEach((fieldOptionItem) => { - if (fieldOptionItem.fieldFormat.type === 'field') { - const { measureItem, index } = measures - .map((measureItem, index) => { - if (measureItem.field[0] === fieldOptionItem?.fieldFormat?.fieldValue) { - return { measureItem, index }; - } else { - return {}; - } - }) - .filter((value) => value.measureItem)[0]; - if (measureItem) { - measureItem['fieldFormat'] = { ...fieldOptionItem.fieldFormat }; - configMeasures.push(measureItem); - } - } else if (fieldOptionItem.fieldFormat.type === 'custom') { - request.push({ ...fieldOptionItem, label: '自定义请求', field: [uid()] }); - } - }); - fieldSchema['x-decorator-props']['params']['config'] = { - measures: configMeasures, - request, +const tableItem = (configItem, result, service, params, api) => { + const columns = []; + const options = []; + if (configItem.type === 'custom') { + if (Object.keys(result).length && result?.['data']?.data) { + const data: reqData = { ...result?.['data']?.data }; + data.labels.forEach((value, index) => { + columns.push({ title: value, dataIndex: 'value' + index, key: index }); + }); + data.values.forEach((value, index) => { + const item = { + key: index, }; - dn.emit('patch', { - schema: fieldSchema, + columns.forEach((colItem, index) => { + const data = isNaN(value[index]) ? value[index] : fieldTransformers(configItem, value[index], api); + item[colItem.dataIndex] = data; }); - setVisible(false); - dn.refresh(); - }} - onCancel={() => { - modal.confirm({ - title: t('Are you sure to cancel?'), - content: t('You changes are not saved. If you click OK, your changes will be lost.'), - okButtonProps: { - danger: true, - }, - onOk: () => { - setVisible(false); - }, - }); - }} - width={'30%'} - > -
- {fieldOption.map((item, index) => { - return ( -
- { - fieldConfig('field', fieldOption, setFieldOption, index, v); - }} - /> - ) : null} - {item?.fieldFormat?.type === 'custom' ? ( - } - placeholder="RequestUrl" - allowClear - style={{ marginRight: '5px', marginTop: '5px', marginBottom: '5px' }} - defaultValue={item?.fieldFormat?.requestUrl ? item?.fieldFormat?.requestUrl : undefined} - onChange={(v) => { - fieldConfig('url', fieldOption, setFieldOption, index, v.currentTarget.value); - }} - /> - ) : null} - fieldConfig('decimal', fieldOption, setFieldOption, index, v)} - /> - ) : null} - { - fieldConfig('del', fieldOption, setFieldOption, index); - }} - /> -
- ); - })} -
- -
- ); -}; - -const fieldConfig = (type, fieldOption, setFieldOption, index?, record?) => { - const option = [...fieldOption]; - if (type === 'add') { - option.push({}); - } else if (type === 'del') { - option.splice(index, 1); - } else if (type === 'type') { - option[index]['fieldFormat'] = { - ...option[index]['fieldFormat'], - type: record, - }; - } else if (type === 'url') { - option[index]['fieldFormat'] = { - ...option[index]['fieldFormat'], - requestUrl: record, - }; - } else if (type === 'field') { - option[index]['fieldFormat'] = { - ...option[index]['fieldFormat'], - fieldValue: record, - }; - } else if (type === 'format') { - option[index]['fieldFormat'] = { - ...option[index]['fieldFormat'], - option: record, - }; - } else if (type === 'decimal') { - option[index]['fieldFormat'] = { - ...option[index]['fieldFormat'], - decimal: record, - }; + options.push(item); + }); + } + } else if (configItem.type === 'field') { + const measuresData = service.data?.data; + if (measuresData) { + let data = measuresData.map((value) => { + return value[configItem.field]; + })[0]; + data = fieldTransformers(configItem, data, api); + const label = params.measures.find((value) => value.field[0] === configItem.field).label; + columns.push( + { + title: '名称', + dataIndex: 'name', + key: 'name', + }, + { + title: '数量', + dataIndex: 'number', + key: 'number', + }, + ); + options.push({ + key: label, + name: label, + number: data, + }); + } } - setFieldOption(option); + + return { columns, options }; }; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-initializer/blocks/GroupBlockInitializer.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-initializer/blocks/GroupBlockInitializer.tsx index cd17dceab..f945d55aa 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-initializer/blocks/GroupBlockInitializer.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/schema-initializer/blocks/GroupBlockInitializer.tsx @@ -17,7 +17,6 @@ import React, { createContext, useState } from 'react'; import { uid } from '@nocobase/utils/client'; import { Checkbox, Spin } from 'antd'; import { GroupBlockConfigure } from '../../schema-components/blocks/GroupBlockConfigure/GroupBlockConfigure'; -import { GroupConfigure } from '../../schema-components/blocks/GroupBlockConfigure/GroupConfigure'; export const GroupBlockContext = createContext({}); @@ -43,7 +42,6 @@ const InternalGroupBlockProvider = (props) => { }} > {props.children} - ); }; diff --git a/packages/plugins/@hera/plugin-rental/src/server/actions/records-controller.ts b/packages/plugins/@hera/plugin-rental/src/server/actions/records-controller.ts index 3410850b4..5819c123e 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/actions/records-controller.ts +++ b/packages/plugins/@hera/plugin-rental/src/server/actions/records-controller.ts @@ -80,15 +80,14 @@ export class RecordPreviewController { items[product.name].total -= count; } }); - ctx.body = _.toArray(items) + const result = { + labels: ['名称', '出库数量', '入库数量', '小计'], + values: [], + }; + _.toArray(items) .sort((a, b) => a.sort - b.sort) - .map((item) => ({ - label: item.name, - value: { - labels: ['出库数量', '入库数量', '小计'], - values: [item.out, item.in, item.total], - }, - })); + .forEach((item) => result.values.push([item.name, item.out, item.in, item.total])); + ctx.body = { ...result }; } } @Action('allweight') @@ -106,15 +105,10 @@ export class RecordPreviewController { const inNum = records.find((item) => item.movement === Movement.in)?.weight ?? 0; const outNum = records.find((item) => item.movement === Movement.out)?.weight ?? 0; - const data = [ - { - label: '实际重量(吨)', - value: { - labels: ['出库数量(吨)', '入库数量(吨)', '小计(吨)'], - values: [outNum, inNum, inNum - outNum], - }, - }, - ]; + const data = { + labels: ['名称', '出库数量(吨)', '入库数量(吨)', '小计(吨)'], + values: [['实际重量(吨)', outNum, inNum, inNum - outNum]], + }; ctx.body = data; } @Action('allprice') @@ -132,15 +126,10 @@ export class RecordPreviewController { const inNum = records.find((item) => item.movement === Movement.in)?.all_price ?? 0; const outNum = records.find((item) => item.movement === Movement.out)?.all_price ?? 0; - const data = [ - { - label: '总金额', - value: { - labels: ['收入', '支出', '小计'], - values: [outNum, inNum, outNum - inNum], - }, - }, - ]; + const data = { + labels: ['名称', '收入', '支出', '小计'], + values: [['总金额', outNum, inNum, outNum - inNum]], + }; ctx.body = data; }