243 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			243 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { Model, ModelCtor } from '@nocobase/database';
 | ||
| import { get, set } from 'lodash';
 | ||
| import { Op } from 'sequelize';
 | ||
| 
 | ||
| const transforms = {
 | ||
|   table: async (fields: Model[], context?: any) => {
 | ||
|     const arr = [];
 | ||
|     for (const field of fields) {
 | ||
|       if (!field.get('component.showInTable')) {
 | ||
|         continue;
 | ||
|       }
 | ||
|       arr.push({
 | ||
|         ...field.get(),
 | ||
|         sorter: field.get('sortable'),
 | ||
|         dataIndex: field.name.split('.'),
 | ||
|       });
 | ||
|     }
 | ||
|     return arr;
 | ||
|   },
 | ||
|   form: async (fields: Model[], ctx?: any) => {
 | ||
|     const [Field] = ctx.db.getModels(['fields']) as ModelCtor<Model>[];
 | ||
|     const mode = get(ctx.action.params, ['values', 'mode'], ctx.action.params.mode);
 | ||
|     const schema = {};
 | ||
|     for (const field of fields) {
 | ||
|       if (!field.get('component.showInForm')) {
 | ||
|         continue;
 | ||
|       }
 | ||
|       const interfaceType = field.get('interface');
 | ||
|       const type = field.get('component.type') || 'string';
 | ||
|       const prop: any = {
 | ||
|         type,
 | ||
|         title: field.title||field.name,
 | ||
|         ...(field.component||{}),
 | ||
|       }
 | ||
|       if (field.get('name') === 'interface' && ctx.state.developerMode === false) {
 | ||
|         const dataSource = field.get('dataSource').filter(item => item.key !== 'developerMode');
 | ||
|         field.set('dataSource', dataSource);
 | ||
|       }
 | ||
|       if (field.get('name') === 'filter' && field.get('collection_name') === 'views') {
 | ||
|         const { values } = ctx.action.params;
 | ||
|         const all = await Field.findAll({
 | ||
|           where: {
 | ||
|             collection_name: get(values, 'associatedKey'),
 | ||
|             developerMode: ctx.state.developerMode,
 | ||
|           },
 | ||
|           order: [['sort', 'asc']],
 | ||
|         });
 | ||
|         set(prop, 'x-component-props.fields', all.filter(f => f.get('filterable')));
 | ||
|       }
 | ||
|       if (type === 'select') {
 | ||
|         prop.type = 'string'
 | ||
|       }
 | ||
|       if (field.get('component.tooltip')) {
 | ||
|         prop.description = field.get('component.tooltip');
 | ||
|       }
 | ||
|       // if (field.get('name') === 'dataSource') {
 | ||
|       //   set(prop, 'items.properties.value.visible', false);
 | ||
|       // }
 | ||
|       if (['number', 'percent'].includes(interfaceType) && field.get('precision')) {
 | ||
|         set(prop, 'x-component-props.step', field.get('precision'));
 | ||
|       }
 | ||
|       if (field.get('required')) {
 | ||
|         prop.required = true;
 | ||
|       }
 | ||
|       if (mode === 'update' && field.get('createOnly')) {
 | ||
|         set(prop, 'x-component-props.disabled', true);
 | ||
|       }
 | ||
|       if (typeof field.get('showTime') === 'boolean') {
 | ||
|         set(prop, 'x-component-props.showTime', field.get('showTime'));
 | ||
|       }
 | ||
|       const defaultValue = get(field.options, 'defaultValue');
 | ||
|       if (typeof defaultValue !== 'undefined') {
 | ||
|         prop.default = defaultValue;
 | ||
|       }
 | ||
|       if (interfaceType === 'boolean') {
 | ||
|         set(prop, 'x-component-props.children', prop.title);
 | ||
|         delete prop.title;
 | ||
|       }
 | ||
|       if (interfaceType === 'linkTo') {
 | ||
|         set(prop, 'x-component-props.associatedName', field.get('collection_name'));
 | ||
|         set(prop, 'x-component-props.target', field.get('target'));
 | ||
|         set(prop, 'x-component-props.multiple', field.get('multiple'));
 | ||
|         set(prop, 'x-component-props.labelField', field.get('labelField'));
 | ||
|       }
 | ||
|       if (interfaceType === 'multipleSelect') {
 | ||
|         set(prop, 'x-component-props.mode', 'multiple');
 | ||
|       }
 | ||
|       if (interfaceType === 'subTable' && field.get('target')) {
 | ||
|         set(prop, 'x-component-props.target', field.get('target'));
 | ||
|         // resourceName
 | ||
|       }
 | ||
|       if (['radio', 'select', 'multipleSelect', 'checkboxes'].includes(interfaceType)) {
 | ||
|         prop.enum = field.get('dataSource');
 | ||
|       }
 | ||
|       schema[field.name] = {
 | ||
|         ...prop,
 | ||
|       };
 | ||
|     }
 | ||
|     return schema;
 | ||
|   },
 | ||
|   details: async (fields: Model[], context?: any) => {
 | ||
|     const arr = [];
 | ||
|     for (const field of fields) {
 | ||
|       if (!get(field.component, 'showInDetail')) {
 | ||
|         continue;
 | ||
|       }
 | ||
|       const props = {};
 | ||
|       if (field.get('interface') === 'subTable') {
 | ||
|         const children = await field.getChildren();
 | ||
|         props['children'] = children.map(child => ({...child.toJSON(), dataIndex: child.name.split('.')}))
 | ||
|       }
 | ||
|       arr.push({
 | ||
|         ...field.toJSON(),
 | ||
|         ...props,
 | ||
|       });
 | ||
|     }
 | ||
|     return arr;
 | ||
|   },
 | ||
|   filter: async (fields: Model[], ctx?: any) => {
 | ||
|     const properties = {
 | ||
|       filter: {
 | ||
|         type: 'filter',
 | ||
|         'x-component-props': {
 | ||
|           fields: fields.filter(field => field.get('filterable')),
 | ||
|         },
 | ||
|       },
 | ||
|     }
 | ||
|     return properties;
 | ||
|   },
 | ||
| };
 | ||
| 
 | ||
| export default async (ctx, next) => {
 | ||
|   const { resourceName, resourceKey } = ctx.action.params;
 | ||
|   const [View, Collection, Field, Action] = ctx.db.getModels(['views', 'collections', 'fields', 'actions']) as ModelCtor<Model>[];
 | ||
|   let view = await View.findOne(View.parseApiJson({
 | ||
|     filter: {
 | ||
|       collection_name: resourceName,
 | ||
|       name: resourceKey,
 | ||
|     },
 | ||
|     // fields: {
 | ||
|     //   appends: ['actions', 'fields'],
 | ||
|     // },
 | ||
|   }));
 | ||
|   if (!view) {
 | ||
|     // 如果不存在 view,新建一个
 | ||
|     view = new View({type: resourceKey, template: 'FilterForm'});
 | ||
|   }
 | ||
|   const collection = await Collection.findOne({
 | ||
|     where: {
 | ||
|       name: resourceName,
 | ||
|     },
 | ||
|   });
 | ||
|   const where: any = {
 | ||
|     developerMode: ctx.state.developerMode,
 | ||
|   }
 | ||
|   if (!view.get('draggable')) {
 | ||
|     where.type = {
 | ||
|       [Op.not]: 'sort',
 | ||
|     };
 | ||
|   }
 | ||
|   const fields = await collection.getFields({
 | ||
|     where,
 | ||
|     order: [
 | ||
|       ['sort', 'asc'],
 | ||
|     ]
 | ||
|   });
 | ||
|   const actions = await collection.getActions({
 | ||
|     where: {
 | ||
|       developerMode: ctx.state.developerMode,
 | ||
|     },
 | ||
|     order: [
 | ||
|       ['sort', 'asc'],
 | ||
|     ]
 | ||
|   });
 | ||
|   let actionNames = view.get('actionNames') || [];
 | ||
|   if (actionNames.length === 0) {
 | ||
|     actionNames = ['filter', 'create', 'destroy'];
 | ||
|   }
 | ||
|   if (view.get('type') === 'table') {
 | ||
|     const defaultTabs = await collection.getTabs({
 | ||
|       where: {
 | ||
|         default: true,
 | ||
|       },
 | ||
|     });
 | ||
|     view.setDataValue('defaultTabName', get(defaultTabs, [0, 'name']));
 | ||
|   }
 | ||
|   
 | ||
|   if (view.get('type') === 'table') {
 | ||
|     view.setDataValue('rowViewName', 'form');
 | ||
|   }
 | ||
|   if (view.get('updateViewName')) {
 | ||
|     view.setDataValue('rowViewName', view.get('updateViewName'));
 | ||
|   }
 | ||
|   // view.setDataValue('viewCollectionName', view.collection_name);
 | ||
|   let title = collection.get('title');
 | ||
|   const mode = get(ctx.action.params, ['values', 'mode'], ctx.action.params.mode);
 | ||
|   if (mode === 'update') {
 | ||
|     title = `编辑${title}`;
 | ||
|   } else {
 | ||
|     title = `新增${title}`;
 | ||
|   }
 | ||
|   const viewType = view.get('type');
 | ||
|   const actionDefaultParams:any = {};
 | ||
|   const appends = [];
 | ||
|   for (const field of fields) {
 | ||
|     if (!['subTable', 'linkTo', 'attachment'].includes(field.get('interface'))) {
 | ||
|       continue;
 | ||
|     }
 | ||
|     let showInKey;
 | ||
|     switch (viewType) {
 | ||
|       case 'table':
 | ||
|         showInKey = 'showInTable';
 | ||
|         break;
 | ||
|       case 'form':
 | ||
|         showInKey = 'showInForm';
 | ||
|         break;
 | ||
|       case 'details':
 | ||
|         showInKey = 'showInDetail';
 | ||
|         break;
 | ||
|     }
 | ||
|     if (showInKey && field.get(`component.${showInKey}`)) {
 | ||
|       appends.push(field.name);
 | ||
|       if (field.get('interface') === 'attachment') {
 | ||
|         appends.push(`${field.name}.storage`);
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
|   actionDefaultParams['fields[appends]'] = appends.join(',');
 | ||
|   ctx.body = {
 | ||
|     ...view.get(),
 | ||
|     title,
 | ||
|     actionDefaultParams,
 | ||
|     original: fields,
 | ||
|     fields: await (transforms[view.type]||transforms.table)(fields, ctx),
 | ||
|     actions: actions.filter(action => actionNames.includes(action.name)).map(action => ({
 | ||
|       ...action.toJSON(),
 | ||
|       ...action.options,
 | ||
|       // viewCollectionName: action.collection_name,
 | ||
|     })),
 | ||
|   };
 | ||
|   await next();
 | ||
| };
 |