From 20c839bfa9300fd6539865a32cdfcfc4526bfad5 Mon Sep 17 00:00:00 2001 From: chenos Date: Tue, 17 Aug 2021 00:18:42 +0800 Subject: [PATCH] feat: improve code --- .../src/components/admin-layout/More.tsx | 6 + .../src/components/admin-layout/SiteTitle.tsx | 2 +- .../src/components/admin-layout/style.less | 6 + .../src/components/schema-renderer/index.tsx | 3 + .../client/src/schemas/action-logs/index.tsx | 55 +++ packages/client/src/schemas/action/index.tsx | 2 +- packages/client/src/schemas/add-new/index.tsx | 96 ++++- .../client/src/schemas/array-table/index.tsx | 377 +++++++++++++++++- .../src/schemas/cascader/demos/demo2.tsx | 1 + .../client/src/schemas/cascader/index.tsx | 3 +- .../client/src/schemas/checkbox/index.tsx | 11 +- packages/client/src/schemas/display/index.tsx | 316 ++++++++------- .../client/src/schemas/icon-picker/index.tsx | 4 + .../client/src/schemas/input-number/index.tsx | 8 +- .../client/src/schemas/password/index.tsx | 103 ++--- packages/client/src/schemas/radio/index.tsx | 4 + packages/client/src/schemas/select/index.tsx | 10 +- packages/client/src/schemas/table/index.tsx | 2 +- 18 files changed, 775 insertions(+), 234 deletions(-) create mode 100644 packages/client/src/schemas/action-logs/index.tsx diff --git a/packages/client/src/components/admin-layout/More.tsx b/packages/client/src/components/admin-layout/More.tsx index 133ad71d5..4367d2062 100644 --- a/packages/client/src/components/admin-layout/More.tsx +++ b/packages/client/src/components/admin-layout/More.tsx @@ -80,6 +80,12 @@ const schema: ISchema = { // accept: 'jpg,png' }, }, + showLogoOnly: { + type: 'boolean', + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + 'x-content': '只显示 LOGO', + }, }, }, }, diff --git a/packages/client/src/components/admin-layout/SiteTitle.tsx b/packages/client/src/components/admin-layout/SiteTitle.tsx index 76bd7a57a..0667d4a4c 100644 --- a/packages/client/src/components/admin-layout/SiteTitle.tsx +++ b/packages/client/src/components/admin-layout/SiteTitle.tsx @@ -38,7 +38,7 @@ export const SiteTitle = () => { {!loading && data?.logo?.url && ( )} - {!loading && data.title && ( + {!loading && !data.showLogoOnly && data.title && (
{data.title}
)} diff --git a/packages/client/src/components/admin-layout/style.less b/packages/client/src/components/admin-layout/style.less index 8968c303b..6dd5c7b3d 100644 --- a/packages/client/src/components/admin-layout/style.less +++ b/packages/client/src/components/admin-layout/style.less @@ -63,8 +63,14 @@ } } .site-info { + display: flex; + align-items: center; .site-logo { height: 20px; padding: 0 16px; } + .site-title { + color: #fff; + font-size: 20px; + } } \ No newline at end of file diff --git a/packages/client/src/components/schema-renderer/index.tsx b/packages/client/src/components/schema-renderer/index.tsx index 37b887ff9..9dd8c709c 100644 --- a/packages/client/src/components/schema-renderer/index.tsx +++ b/packages/client/src/components/schema-renderer/index.tsx @@ -40,6 +40,7 @@ import { import { DatePicker } from '../../schemas/date-picker'; import { Filter } from '../../schemas/filter'; import { Form } from '../../schemas/form'; +import { ActionLogs } from '../../schemas/action-logs'; import { Grid } from '../../schemas/grid'; import { IconPicker } from '../../schemas/icon-picker'; import { Input } from '../../schemas/input'; @@ -192,6 +193,8 @@ export const SchemaField = createSchemaField({ Tabs, TimePicker, Upload, + + ActionLogs, }, }); diff --git a/packages/client/src/schemas/action-logs/index.tsx b/packages/client/src/schemas/action-logs/index.tsx new file mode 100644 index 000000000..67bf1e867 --- /dev/null +++ b/packages/client/src/schemas/action-logs/index.tsx @@ -0,0 +1,55 @@ +import { Field } from '@formily/core'; +import { + ISchema, + observer, + RecursionField, + Schema, + useField, +} from '@formily/react'; +import React from 'react'; +import { SchemaRenderer } from '../../components/schema-renderer'; +import ArrayTable from '../array-table'; + +export const ActionLogs = () => null; + +ActionLogs.Field = observer((props: any) => { + const { value } = props; + return
{value?.uiSchema?.title || value?.name}
; +}); + +ActionLogs.FieldValue = observer((props: any) => { + const field = useField(); + const array = ArrayTable.useArray(); + const index = ArrayTable.useIndex(); + const collectionField = array.field.value[index]?.field; + console.log('ffffff', collectionField?.uiSchema, field.value); + + if (!collectionField.uiSchema) { + if (!field.value) { + return
N/A
; + } + if (typeof field.value === 'boolean') { + return
{field.value ? 'true' : 'false'}
; + } + if (typeof field.value === 'string' || typeof field.value === 'number') { + return
{field.value}
; + } + return
{JSON.stringify(field.value, null, 2)}
; + } + + return ( + + ); +}); diff --git a/packages/client/src/schemas/action/index.tsx b/packages/client/src/schemas/action/index.tsx index f158120b0..59ac7f4f4 100644 --- a/packages/client/src/schemas/action/index.tsx +++ b/packages/client/src/schemas/action/index.tsx @@ -264,7 +264,7 @@ Action.Dropdown = observer((props: any) => { document.body, )} diff --git a/packages/client/src/schemas/add-new/index.tsx b/packages/client/src/schemas/add-new/index.tsx index e26e18a6b..8f8bd596a 100644 --- a/packages/client/src/schemas/add-new/index.tsx +++ b/packages/client/src/schemas/add-new/index.tsx @@ -397,6 +397,7 @@ function generateCardItemSchema(component) { rowKey: 'id', // dragSort: true, showIndex: true, + defaultSort: ['-id'], defaultAppends: ['user', 'collection'], refreshRequestOnChange: true, pagination: { @@ -459,9 +460,9 @@ function generateCardItemSchema(component) { type: 'string', 'x-component': 'Select', enum: [ - { label: '新增数据', value: 'create' }, - { label: '更新数据', value: 'update' }, - { label: '删除数据', value: 'destroy' }, + { label: '新增', value: 'create', color: 'green' }, + { label: '更新', value: 'update', color: 'blue' }, + { label: '删除', value: 'destroy', color: 'red' }, ], }, }, @@ -521,7 +522,7 @@ function generateCardItemSchema(component) { properties: { created_at: { type: 'string', - title: '创建时间', + title: '操作时间', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-read-pretty': true, @@ -529,6 +530,20 @@ function generateCardItemSchema(component) { format: 'YYYY-MM-DD HH:mm:ss', }, }, + 'user.nickname': { + type: 'string', + title: '操作用户', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-read-pretty': true, + }, + 'collection.title': { + type: 'string', + title: '所属数据表', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-read-pretty': true, + }, type: { type: 'string', title: '操作类型', @@ -536,9 +551,21 @@ function generateCardItemSchema(component) { 'x-component': 'Select', 'x-read-pretty': true, enum: [ - { label: '新增数据', value: 'create' }, - { label: '更新数据', value: 'update' }, - { label: '删除数据', value: 'destroy' }, + { + label: '新增', + value: 'create', + color: 'green', + }, + { + label: '更新', + value: 'update', + color: 'blue', + }, + { + label: '删除', + value: 'destroy', + color: 'red', + }, ], }, changes: { @@ -550,18 +577,33 @@ function generateCardItemSchema(component) { pagination: false, // scroll: { x: '100%' }, }, + // 'x-reactions': ['{{ filterActionLogs }}'], items: { type: 'object', properties: { + column0: { + type: 'void', + 'x-component': 'ArrayTable.Column', + 'x-component-props': { + width: 80, + align: 'center', + }, + properties: { + index: { + type: 'void', + 'x-component': 'ArrayTable.Index', + }, + }, + }, column1: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { title: '字段名称' }, properties: { - 'field.uiSchema.title': { + field: { type: 'string', 'x-decorator': 'FormilyFormItem', - 'x-component': 'Input', + 'x-component': 'ActionLogs.Field', }, }, }, @@ -573,7 +615,7 @@ function generateCardItemSchema(component) { before: { type: 'string', 'x-decorator': 'FormilyFormItem', - 'x-component': 'Values', + 'x-component': 'ActionLogs.FieldValue', }, }, }, @@ -585,7 +627,7 @@ function generateCardItemSchema(component) { after: { type: 'string', 'x-decorator': 'FormilyFormItem', - 'x-component': 'Values', + 'x-component': 'ActionLogs.FieldValue', }, }, }, @@ -604,7 +646,7 @@ function generateCardItemSchema(component) { }, column1: { type: 'void', - title: '创建时间', + title: '操作时间', 'x-component': 'Table.Column', properties: { created_at: { @@ -618,6 +660,30 @@ function generateCardItemSchema(component) { }, }, column2: { + type: 'void', + title: '操作用户', + 'x-component': 'Table.Column', + properties: { + 'user.nickname': { + type: 'string', + 'x-component': 'Input', + 'x-read-pretty': true, + }, + }, + }, + column3: { + type: 'void', + title: '所属数据表', + 'x-component': 'Table.Column', + properties: { + 'collection.title': { + type: 'string', + 'x-component': 'Input', + 'x-read-pretty': true, + }, + }, + }, + column4: { type: 'void', title: '操作类型', 'x-component': 'Table.Column', @@ -627,9 +693,9 @@ function generateCardItemSchema(component) { 'x-component': 'Select', 'x-read-pretty': true, enum: [ - { label: '新增数据', value: 'create' }, - { label: '更新数据', value: 'update' }, - { label: '删除数据', value: 'destroy' }, + { label: '新增', value: 'create', color: 'green' }, + { label: '更新', value: 'update', color: 'blue' }, + { label: '删除', value: 'destroy', color: 'red' }, ], }, }, diff --git a/packages/client/src/schemas/array-table/index.tsx b/packages/client/src/schemas/array-table/index.tsx index ca3ebc04d..66f6edc82 100644 --- a/packages/client/src/schemas/array-table/index.tsx +++ b/packages/client/src/schemas/array-table/index.tsx @@ -1,13 +1,370 @@ -import React from 'react'; -import { ArrayTable as Table } from '@formily/antd'; -import { useField, Schema } from '@formily/react'; -import { Button } from 'antd'; -import cls from 'classnames'; -import { isValid, uid } from '@formily/shared'; -import { PlusOutlined } from '@ant-design/icons'; -import { usePrefixCls } from '@formily/antd/lib/__builtins__'; +// import React from 'react'; +// import { ArrayTable as Table } from '@formily/antd'; +// import { useField, Schema } from '@formily/react'; +// import { Button } from 'antd'; +// import cls from 'classnames'; +// import { isValid, uid } from '@formily/shared'; +// import { PlusOutlined } from '@ant-design/icons'; +// import { usePrefixCls } from '@formily/antd/lib/__builtins__'; -export const ArrayTable = Table; +import React, { Fragment, useState, useRef, useEffect } from 'react'; +import { Table, Pagination, Space, Select, Badge, Button } from 'antd'; +import { PaginationProps } from 'antd/lib/pagination'; +import { TableProps, ColumnProps } from 'antd/lib/table'; +import { SelectProps } from 'antd/lib/select'; +import cls from 'classnames'; +import { SortableContainer, SortableElement } from 'react-sortable-hoc'; +import { GeneralField, FieldDisplayTypes, ArrayField } from '@formily/core'; +import { + useForm, + useField, + observer, + useFieldSchema, + RecursionField, +} from '@formily/react'; +import { FormPath, isArr, isBool, uid, isValid } from '@formily/shared'; +import { Schema } from '@formily/json-schema'; +import { usePrefixCls } from '@formily/antd/lib/__builtins__'; +import { ArrayBase, ArrayBaseMixins } from '@formily/antd/lib/array-base'; +import { PlusOutlined } from '@ant-design/icons'; + +interface ObservableColumnSource { + field: GeneralField; + columnProps: ColumnProps; + schema: Schema; + display: FieldDisplayTypes; + name: string; +} +interface IArrayTablePaginationProps extends PaginationProps { + dataSource?: any[]; + paging?: boolean; + children?: ( + dataSource: any[], + pagination: React.ReactNode, + ) => React.ReactElement; +} + +interface IStatusSelectProps extends SelectProps { + pageSize?: number; +} + +type ComposedArrayTable = React.FC> & + ArrayBaseMixins & { + Column?: React.FC>; + }; + +const SortableRow = SortableElement((props: any) => ); +const SortableBody = SortableContainer((props: any) => ); + +const isColumnComponent = (schema: Schema) => { + return schema['x-component']?.indexOf('Column') > -1; +}; + +const isOperationsComponent = (schema: Schema) => { + return schema['x-component']?.indexOf('Operations') > -1; +}; + +const isAdditionComponent = (schema: Schema) => { + return schema['x-component']?.indexOf('Addition') > -1; +}; + +const useArrayTableSources = () => { + const arrayField = useField(); + const schema = useFieldSchema(); + const parseSources = (schema: Schema): ObservableColumnSource[] => { + if ( + isColumnComponent(schema) || + isOperationsComponent(schema) || + isAdditionComponent(schema) + ) { + if (!schema['x-component-props']?.['dataIndex'] && !schema['name']) + return []; + const name = schema['x-component-props']?.['dataIndex'] || schema['name']; + const field = arrayField.query(arrayField.address.concat(name)).take(); + const columnProps = + field?.component?.[1] || schema['x-component-props'] || {}; + const display = field?.display || schema['x-display']; + return [ + { + name, + display, + field, + schema, + columnProps, + }, + ]; + } else if (schema.properties) { + return schema.reduceProperties((buf, schema) => { + return buf.concat(parseSources(schema)); + }, []); + } + }; + + const parseArrayItems = (schema: Schema['items']) => { + const sources: ObservableColumnSource[] = []; + const items = isArr(schema) ? schema : [schema]; + return items.reduce((columns, schema) => { + const item = parseSources(schema); + if (item) { + return columns.concat(item); + } + return columns; + }, sources); + }; + + if (!schema) throw new Error('can not found schema object'); + + return parseArrayItems(schema.items); +}; + +const useArrayTableColumns = ( + dataSource: any[], + sources: ObservableColumnSource[], +): TableProps['columns'] => { + return sources.reduce((buf, { name, columnProps, schema, display }, key) => { + if (display !== 'visible') return buf; + if (!isColumnComponent(schema)) return buf; + return buf.concat({ + ...columnProps, + key, + dataIndex: name, + render: (value: any, record: any) => { + const index = dataSource.indexOf(record); + const children = ( + + + + ); + return children; + }, + }); + }, []); +}; + +const useAddition = () => { + const schema = useFieldSchema(); + return schema.reduceProperties((addition, schema, key) => { + if (isAdditionComponent(schema)) { + return ; + } + return addition; + }, null); +}; + +const StatusSelect: React.FC = observer((props) => { + const form = useForm(); + const field = useField(); + const prefixCls = usePrefixCls('formily-array-table'); + const errors = form.queryFeedbacks({ + type: 'error', + address: `${field.address}.*`, + }); + const createIndexPattern = (page: number) => { + const pattern = `${field.address}.*[${(page - 1) * props.pageSize}:${ + page * props.pageSize + }].*`; + return FormPath.parse(pattern); + }; + const options = props.options?.map(({ label, value }) => { + const hasError = errors.some(({ address }) => { + return createIndexPattern(value).match(address); + }); + return { + label: hasError ? {label} : label, + value, + }; + }); + + const width = String(options?.length).length * 15; + + return ( +