feat(database): support field get in view preview (#2482)
* feat: support field get in view preview * chore: test * refactor: dbViews fieldtypes * refactor: dbViews fieldtypes * refactor: dbViews fieldtypes * refactor: dbViews fieldtypes --------- Co-authored-by: katherinehhh <katherine_15995@163.com>
This commit is contained in:
		
							parent
							
								
									9d33da0437
								
							
						
					
					
						commit
						a625fc538d
					
				@ -6,6 +6,7 @@ import { EllipsisWithTooltip, useCompile } from '../../../';
 | 
				
			|||||||
import { useAPIClient } from '../../../api-client';
 | 
					import { useAPIClient } from '../../../api-client';
 | 
				
			||||||
import { useCollectionManager } from '../../hooks/useCollectionManager';
 | 
					import { useCollectionManager } from '../../hooks/useCollectionManager';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mapFields = ['lineString', 'point', 'circle', 'polygon'];
 | 
				
			||||||
export const PreviewTable = (props) => {
 | 
					export const PreviewTable = (props) => {
 | 
				
			||||||
  const { databaseView, schema, viewName, fields } = props;
 | 
					  const { databaseView, schema, viewName, fields } = props;
 | 
				
			||||||
  const [previewColumns, setPreviewColumns] = useState([]);
 | 
					  const [previewColumns, setPreviewColumns] = useState([]);
 | 
				
			||||||
@ -20,7 +21,7 @@ export const PreviewTable = (props) => {
 | 
				
			|||||||
    if (databaseView) {
 | 
					    if (databaseView) {
 | 
				
			||||||
      getPreviewData();
 | 
					      getPreviewData();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [databaseView]);
 | 
					  }, [form.values.fields]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    const pColumns = formatPreviewColumns(fields);
 | 
					    const pColumns = formatPreviewColumns(fields);
 | 
				
			||||||
@ -28,10 +29,16 @@ export const PreviewTable = (props) => {
 | 
				
			|||||||
  }, [form.values.fields]);
 | 
					  }, [form.values.fields]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const getPreviewData = () => {
 | 
					  const getPreviewData = () => {
 | 
				
			||||||
 | 
					    const fieldTypes = {};
 | 
				
			||||||
 | 
					    form.values.fields.map((v) => {
 | 
				
			||||||
 | 
					      if (mapFields.includes(v.type)) {
 | 
				
			||||||
 | 
					        fieldTypes[v.name] = v.type;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
    setLoading(true);
 | 
					    setLoading(true);
 | 
				
			||||||
    api
 | 
					    api
 | 
				
			||||||
      .resource(`dbViews`)
 | 
					      .resource(`dbViews`)
 | 
				
			||||||
      .query({ filterByTk: viewName, schema })
 | 
					      .query({ filterByTk: viewName, schema, fieldTypes })
 | 
				
			||||||
      .then(({ data }) => {
 | 
					      .then(({ data }) => {
 | 
				
			||||||
        if (data) {
 | 
					        if (data) {
 | 
				
			||||||
          setLoading(false);
 | 
					          setLoading(false);
 | 
				
			||||||
@ -58,7 +65,13 @@ export const PreviewTable = (props) => {
 | 
				
			|||||||
            const objSchema: any = {
 | 
					            const objSchema: any = {
 | 
				
			||||||
              type: 'object',
 | 
					              type: 'object',
 | 
				
			||||||
              properties: {
 | 
					              properties: {
 | 
				
			||||||
                [item.name]: { ...schema, default: content, 'x-read-pretty': true, title: null },
 | 
					                [item.name]: {
 | 
				
			||||||
 | 
					                  name: `${item.name}`,
 | 
				
			||||||
 | 
					                  'x-component': schema && fieldSource ? 'CollectionField' : 'Input',
 | 
				
			||||||
 | 
					                  'x-read-pretty': true,
 | 
				
			||||||
 | 
					                  'x-collection-field': fieldSource?.join('.'),
 | 
				
			||||||
 | 
					                  default: content,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            return (
 | 
					            return (
 | 
				
			||||||
@ -98,7 +111,7 @@ export const PreviewTable = (props) => {
 | 
				
			|||||||
              columns={previewColumns}
 | 
					              columns={previewColumns}
 | 
				
			||||||
              dataSource={previewData}
 | 
					              dataSource={previewData}
 | 
				
			||||||
              scroll={{ x: 1000, y: 300 }}
 | 
					              scroll={{ x: 1000, y: 300 }}
 | 
				
			||||||
              key={viewName}
 | 
					              key={`${viewName}-preview`}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          </>
 | 
					          </>
 | 
				
			||||||
        )}
 | 
					        )}
 | 
				
			||||||
 | 
				
			|||||||
@ -27,7 +27,7 @@ export const ReadPrettyInternalViewer: React.FC = observer(
 | 
				
			|||||||
  (props: any) => {
 | 
					  (props: any) => {
 | 
				
			||||||
    const fieldSchema = useFieldSchema();
 | 
					    const fieldSchema = useFieldSchema();
 | 
				
			||||||
    const recordCtx = useRecord();
 | 
					    const recordCtx = useRecord();
 | 
				
			||||||
    const { enableLink } = fieldSchema['x-component-props'];
 | 
					    const { enableLink } = fieldSchema['x-component-props'] || {};
 | 
				
			||||||
    // value 做了转换,但 props.value 和原来 useField().value 的值不一致
 | 
					    // value 做了转换,但 props.value 和原来 useField().value 的值不一致
 | 
				
			||||||
    const field = useField();
 | 
					    const field = useField();
 | 
				
			||||||
    const fieldNames = useFieldNames(props);
 | 
					    const fieldNames = useFieldNames(props);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { Database, Repository } from '@nocobase/database';
 | 
					import { Database, Repository, Field, DataTypes } from '@nocobase/database';
 | 
				
			||||||
import { MockServer } from '@nocobase/test';
 | 
					import { MockServer } from '@nocobase/test';
 | 
				
			||||||
import { uid } from '@nocobase/utils';
 | 
					import { uid } from '@nocobase/utils';
 | 
				
			||||||
import { createApp } from '../index';
 | 
					import { createApp } from '../index';
 | 
				
			||||||
@ -54,6 +54,73 @@ SELECT * FROM numbers;
 | 
				
			|||||||
    await app.destroy();
 | 
					    await app.destroy();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should support preview field with getter', async () => {
 | 
				
			||||||
 | 
					    class TestField extends Field {
 | 
				
			||||||
 | 
					      get dataType() {
 | 
				
			||||||
 | 
					        return DataTypes.STRING;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      constructor(options: any, context: any) {
 | 
				
			||||||
 | 
					        const { name } = options;
 | 
				
			||||||
 | 
					        super(
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            get() {
 | 
				
			||||||
 | 
					              return 'test_' + this.getDataValue(name);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            ...options,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          context,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    db.registerFieldTypes({
 | 
				
			||||||
 | 
					      test: TestField,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const C1 = db.collection({
 | 
				
			||||||
 | 
					      name: 'c1',
 | 
				
			||||||
 | 
					      fields: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          type: 'test',
 | 
				
			||||||
 | 
					          name: 'test_field',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await db.sync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await C1.repository.create({
 | 
				
			||||||
 | 
					      values: {
 | 
				
			||||||
 | 
					        test_field: '1',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const c1 = await C1.repository.findOne();
 | 
				
			||||||
 | 
					    expect(c1.get('test_field')).toEqual('test_1');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // create view from C1
 | 
				
			||||||
 | 
					    const viewName = `test_view_${uid(6)}`;
 | 
				
			||||||
 | 
					    await db.sequelize.query(`DROP VIEW IF EXISTS ${viewName}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const createSQL = `CREATE VIEW ${viewName} AS SELECT * FROM ${C1.quotedTableName()}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await db.sequelize.query(createSQL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const response = await agent.resource('dbViews').query({
 | 
				
			||||||
 | 
					      filterByTk: viewName,
 | 
				
			||||||
 | 
					      pageSize: 20,
 | 
				
			||||||
 | 
					      fieldTypes: {
 | 
				
			||||||
 | 
					        test_field: 'test',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expect(response.status).toBe(200);
 | 
				
			||||||
 | 
					    const data = response.body.data;
 | 
				
			||||||
 | 
					    const firstRow = data[0];
 | 
				
			||||||
 | 
					    expect(firstRow.test_field).toEqual('test_1');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should list views', async () => {
 | 
					  it('should list views', async () => {
 | 
				
			||||||
    const response = await agent.resource('dbViews').list();
 | 
					    const response = await agent.resource('dbViews').list();
 | 
				
			||||||
    expect(response.status).toBe(200);
 | 
					    expect(response.status).toBe(200);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import Database, { Repository, ViewCollection, ViewFieldInference } from '@nocobase/database';
 | 
					import Database, { Field, Repository, ViewCollection, ViewFieldInference, DataTypes } from '@nocobase/database';
 | 
				
			||||||
import Application from '@nocobase/server';
 | 
					import Application from '@nocobase/server';
 | 
				
			||||||
import { uid } from '@nocobase/utils';
 | 
					import { uid } from '@nocobase/utils';
 | 
				
			||||||
import { createApp } from '../index';
 | 
					import { createApp } from '../index';
 | 
				
			||||||
 | 
				
			|||||||
@ -56,7 +56,7 @@ export default {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async query(ctx, next) {
 | 
					    async query(ctx, next) {
 | 
				
			||||||
      const { filterByTk, schema = 'public', page = 1, pageSize = 10 } = ctx.action.params;
 | 
					      const { filterByTk, fieldTypes, schema = 'public', page = 1, pageSize = 10 } = ctx.action.params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const offset = (page - 1) * pageSize;
 | 
					      const offset = (page - 1) * pageSize;
 | 
				
			||||||
      const limit = 1 * pageSize;
 | 
					      const limit = 1 * pageSize;
 | 
				
			||||||
@ -66,7 +66,34 @@ export default {
 | 
				
			|||||||
                     ctx.app.db.utils.addSchema(filterByTk, schema),
 | 
					                     ctx.app.db.utils.addSchema(filterByTk, schema),
 | 
				
			||||||
                   )} LIMIT ${limit} OFFSET ${offset}`;
 | 
					                   )} LIMIT ${limit} OFFSET ${offset}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      ctx.body = await ctx.app.db.sequelize.query(sql, { type: 'SELECT' });
 | 
					      const rawValues = await ctx.app.db.sequelize.query(sql, { type: 'SELECT' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (fieldTypes) {
 | 
				
			||||||
 | 
					        for (const raw of rawValues) {
 | 
				
			||||||
 | 
					          const fakeInstance = {
 | 
				
			||||||
 | 
					            dataValues: raw,
 | 
				
			||||||
 | 
					            getDataValue: (key) => raw[key],
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          for (const fieldName of Object.keys(fieldTypes)) {
 | 
				
			||||||
 | 
					            const fieldType = fieldTypes[fieldName];
 | 
				
			||||||
 | 
					            const FieldClass = ctx.app.db.fieldTypes.get(fieldType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const fieldOptions = new FieldClass(
 | 
				
			||||||
 | 
					              { name: fieldName },
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                db: ctx.app.db,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            ).options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (fieldOptions.get) {
 | 
				
			||||||
 | 
					              const newValue = fieldOptions.get.apply(fakeInstance);
 | 
				
			||||||
 | 
					              raw[fieldName] = newValue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      ctx.body = rawValues;
 | 
				
			||||||
      await next();
 | 
					      await next();
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user