feat: divide the fields into general and system

This commit is contained in:
chenos 2021-08-07 23:14:14 +08:00
parent 2f880c6cd0
commit c34d82d180
9 changed files with 105 additions and 262 deletions

View File

@ -45,7 +45,7 @@ export default () => {
'x-component': 'Input', 'x-component': 'Input',
'x-read-pretty': true, 'x-read-pretty': true,
}, },
fields: { generalFields: {
type: 'array', type: 'array',
title: '数据表字段', title: '数据表字段',
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',

View File

@ -23,7 +23,7 @@ export function useCollection(props: CollectionProviderProps) {
collection = collections.find((item) => item.name === collectionName); collection = collections.find((item) => item.name === collectionName);
} }
if (collection) { if (collection) {
fields = collection?.fields || []; fields = collection?.generalFields || [];
} }
let sortableField = collection?.sortable; let sortableField = collection?.sortable;
if (collection?.sortable && typeof collection?.sortable === 'object') { if (collection?.sortable && typeof collection?.sortable === 'object') {

View File

@ -24,7 +24,8 @@ import {
Input, Input,
Badge, Badge,
message, message,
Spin, Drawer,
Space,
} from 'antd'; } from 'antd';
import { options, interfaces, getDefaultFields } from './interfaces'; import { options, interfaces, getDefaultFields } from './interfaces';
import { import {
@ -127,11 +128,12 @@ export const DatabaseCollection = observer((props) => {
> >
<DatabaseOutlined /> <DatabaseOutlined />
</Button> </Button>
<Modal <Drawer
bodyStyle={{ width={'50%'}
overflow: 'auto', // bodyStyle={{
maxHeight: 'calc(100vh - 300px)', // overflow: 'auto',
}} // maxHeight: 'calc(100vh - 300px)',
// }}
title={ title={
<div style={{ textAlign: 'center' }}> <div style={{ textAlign: 'center' }}>
<DndContext <DndContext
@ -249,21 +251,49 @@ export const DatabaseCollection = observer((props) => {
</div> </div>
} }
visible={visible} visible={visible}
onCancel={() => { onClose={() => {
setVisible(false); setVisible(false);
}} }}
okText={'保存'} footer={
cancelText={'关闭'} <Space>
onOk={async () => { <Button
try { onClick={() => {
form.clearErrors(); setVisible(false);
await form.validate(`${field.address.entire}.${activeIndex}.*`); }}
delete field.value[activeIndex]['unsaved']; >
await createOrUpdateCollection(field.value[activeIndex]);
await refresh(); </Button>
message.success('保存成功'); <Button
} catch (error) {} type={'primary'}
}} onClick={async () => {
try {
form.clearErrors();
await form.validate(
`${field.address.entire}.${activeIndex}.*`,
);
delete field.value[activeIndex]['unsaved'];
await createOrUpdateCollection(field.value[activeIndex]);
await refresh();
message.success('保存成功');
} catch (error) {}
}}
>
</Button>
</Space>
}
// okText={'保存'}
// cancelText={'关闭'}
// onOk={async () => {
// try {
// form.clearErrors();
// await form.validate(`${field.address.entire}.${activeIndex}.*`);
// delete field.value[activeIndex]['unsaved'];
// await createOrUpdateCollection(field.value[activeIndex]);
// await refresh();
// message.success('保存成功');
// } catch (error) {}
// }}
> >
<FormLayout layout={'vertical'}> <FormLayout layout={'vertical'}>
<RecursionField <RecursionField
@ -279,7 +309,7 @@ export const DatabaseCollection = observer((props) => {
{(form) => <pre>{JSON.stringify(form.values, null, 2)}</pre>} {(form) => <pre>{JSON.stringify(form.values, null, 2)}</pre>}
</FormConsumer> */} </FormConsumer> */}
</FormLayout> </FormLayout>
</Modal> </Drawer>
</div> </div>
); );
}); });

View File

@ -3,65 +3,27 @@ import { TableOptions } from '@nocobase/database';
export default { export default {
name: 'action_changes', name: 'action_changes',
title: '变动值', title: '变动值',
developerMode: true,
internal: true,
createdBy: false, createdBy: false,
updatedBy: false, updatedBy: false,
createdAt: false, createdAt: false,
updatedAt: false, updatedAt: false,
logging: false,
fields: [ fields: [
{ {
interface: 'linkTo',
type: 'belongsTo', type: 'belongsTo',
name: 'log', name: 'log',
target: 'action_logs', target: 'action_logs',
title: '所属操作'
}, },
{ {
type: 'jsonb', type: 'jsonb',
name: 'field', name: 'field',
title: '字段信息',
component: {
type: 'logs.field',
showInTable: true,
},
}, },
{ {
type: 'jsonb', type: 'jsonb',
name: 'before', name: 'before',
title: '操作前',
component: {
type: 'logs.fieldValue',
showInTable: true,
},
}, },
{ {
type: 'jsonb', type: 'jsonb',
name: 'after', name: 'after',
title: '操作后',
component: {
type: 'logs.fieldValue',
showInTable: true,
},
}
],
actions: [
{
type: 'list',
name: 'list',
title: '查看'
}
],
views_v2: [
{
type: 'table',
name: 'table',
fields: [
'field',
'before',
'after',
],
} }
], ],
} as TableOptions; } as TableOptions;

View File

@ -4,217 +4,37 @@ import { LOG_TYPE_CREATE, LOG_TYPE_UPDATE, LOG_TYPE_DESTROY } from '../constants
export default { export default {
name: 'action_logs', name: 'action_logs',
title: '操作记录', title: '操作记录',
developerMode: true,
internal: true,
createdBy: false, createdBy: false,
updatedBy: false, updatedBy: false,
updatedAt: false, updatedAt: false,
logging: false,
fields: [ fields: [
{ {
interface: 'createdAt',
name: 'created_at',
type: 'date', type: 'date',
title: '操作时间', name: 'created_at',
showTime: true,
component: {
showInTable: true,
showInDetail: true,
},
}, },
{ {
interface: 'linkTo',
type: 'belongsTo', type: 'belongsTo',
name: 'user', name: 'user',
target: 'users', target: 'users',
title: '操作用户',
appends: true,
labelField: 'nickname',
component: {
showInTable: true,
showInDetail: true,
},
}, },
{ {
interface: 'linkTo',
type: 'belongsTo', type: 'belongsTo',
name: 'collection', name: 'collection',
target: 'collections', target: 'collections',
title: '数据表',
targetKey: 'name', targetKey: 'name',
appends: true,
labelField: 'title',
component: {
showInTable: true,
showInDetail: true,
},
}, },
{ {
interface: 'select',
type: 'string', type: 'string',
name: 'type', name: 'type',
title: '操作类型',
filterable: true,
dataSource: [
{ value: LOG_TYPE_CREATE, label: '新增', color: 'blue' },
{ value: LOG_TYPE_UPDATE, label: '更新', color: 'volcano' },
{ value: LOG_TYPE_DESTROY, label: '删除', color: 'magenta' },
],
component: {
showInTable: true,
showInDetail: true,
},
}, },
{ {
type: 'integer', type: 'integer',
name: 'index', name: 'index',
title: '数据ID',
component: {
showInTable: true,
},
}, },
{ {
interface: 'subTable',
type: 'hasMany', type: 'hasMany',
name: 'changes', name: 'changes',
target: 'action_changes', target: 'action_changes',
title: '数据变动',
component: {
showInDetail: true,
},
} }
], ],
actions: [
{
type: 'filter',
name: 'filter',
title: '筛选'
},
{
type: 'list',
name: 'list',
title: '查看',
// sort: '-created_at'
},
],
views_v2: [
{
developerMode: true,
type: 'table',
name: 'table',
title: '全部数据',
labelField: 'created_at',
actions: [
{
name: 'filter',
type: 'filter',
title: '过滤',
},
],
fields: ['created_at', 'user', 'collection', 'type', 'index'],
detailsOpenMode: 'drawer', // window
details: ['descriptions'],
sort: ['-created_at'],
},
{
developerMode: true,
type: 'table',
name: 'create',
title: '新增数据',
labelField: 'created_at',
actions: [
{
name: 'filter',
type: 'filter',
title: '过滤',
},
],
fields: ['created_at', 'user', 'collection', 'type', 'index'],
detailsOpenMode: 'drawer', // window
details: ['descriptions'],
filter: {
and: [
{
type: 'create',
}
],
},
sort: ['-created_at'],
},
{
developerMode: true,
type: 'table',
name: 'update',
title: '更新数据',
labelField: 'created_at',
actions: [
{
name: 'filter',
type: 'filter',
title: '过滤',
},
],
fields: ['created_at', 'user', 'collection', 'type', 'index'],
detailsOpenMode: 'drawer', // window
details: ['descriptions'],
filter: {
and: [
{
type: 'update',
}
],
},
sort: ['-created_at'],
},
{
developerMode: true,
type: 'table',
name: 'destroy',
title: '删除数据',
labelField: 'created_at',
actions: [
{
name: 'filter',
type: 'filter',
title: '过滤',
},
],
fields: ['created_at', 'user', 'collection', 'type', 'index'],
detailsOpenMode: 'drawer', // window
details: ['descriptions'],
filter: {
and: [
{
type: 'destroy',
}
],
},
sort: ['-created_at'],
},
{
type: 'table',
name: 'table2',
title: '操作日志列表',
labelField: 'created_at',
actions: [
{
name: 'filter',
type: 'filter',
title: '过滤',
},
],
fields: ['created_at', 'user', 'type', 'index'],
detailsOpenMode: 'drawer', // window
details: ['descriptions'],
sort: ['-created_at'],
},
{
developerMode: true,
type: 'descriptions',
name: 'descriptions',
title: '详情',
fields: ['created_at', 'user', 'collection', 'type', 'index', 'changes'],
actions: [],
},
],
} as TableOptions; } as TableOptions;

View File

@ -12,7 +12,7 @@ export default async function() {
// 为所有的表都加上日志的 hooks // 为所有的表都加上日志的 hooks
database.addHook('afterTableInit', (table) => { database.addHook('afterTableInit', (table) => {
if (table.options.logging === false) { if (!table.options.logging) {
return; return;
} }
addAll(database.getModel(table.options.name)); addAll(database.getModel(table.options.name));
@ -20,7 +20,7 @@ export default async function() {
const Collection = database.getModel('collections'); const Collection = database.getModel('collections');
Collection.addHook('afterCreate', async (model, options) => { Collection.addHook('afterCreate', async (model, options) => {
if (model.get('logging') === false) { if (!model.get('logging')) {
return; return;
} }
@ -28,20 +28,21 @@ export default async function() {
const exists = await model.countFields({ const exists = await model.countFields({
where: { where: {
type: { [Op.iLike]: 'hasMany' }, dataType: { [Op.iLike]: 'hasMany' },
name: 'action_logs' name: 'action_logs'
}, },
transaction transaction
}); });
if (!exists) { if (!exists) {
await model.createField({ await model.createSystemField({
interface: 'linkTo', interface: 'linkTo',
type: 'hasMany', dataType: 'hasMany',
name: 'action_logs', name: 'action_logs',
target: 'action_logs', target: 'action_logs',
title: '数据动态', title: '数据动态',
foreignKey: 'index', foreignKey: 'index',
state: 0,
scope: { scope: {
collection_name: model.get('name') collection_name: model.get('name')
}, },

View File

@ -6,16 +6,17 @@ export default {
model: 'Collection', model: 'Collection',
sortable: 'sort', sortable: 'sort',
fields: [ fields: [
// {
// type: 'sort',
// name: 'sort',
// },
{ {
type: 'uid', type: 'uid',
name: 'name', name: 'name',
primaryKey: true, primaryKey: true,
prefix: 't_', prefix: 't_',
}, },
{
type: 'boolean',
name: 'logging',
defaultValue: true,
},
{ {
type: 'string', type: 'string',
name: 'title', name: 'title',
@ -38,8 +39,29 @@ export default {
{ {
type: 'hasMany', type: 'hasMany',
name: 'fields', name: 'fields',
target: 'fields',
sourceKey: 'name', sourceKey: 'name',
}, },
{
type: 'hasMany',
name: 'generalFields',
target: 'fields',
foreignKey: 'collection_name',
sourceKey: 'name',
scope: {
state: 1,
},
},
{
type: 'hasMany',
name: 'systemFields',
target: 'fields',
foreignKey: 'collection_name',
sourceKey: 'name',
scope: {
state: 0,
},
},
{ {
type: 'belongsTo', type: 'belongsTo',
name: 'uiSchema', name: 'uiSchema',

View File

@ -11,18 +11,11 @@ export default {
scope: ['parentKey'], scope: ['parentKey'],
}, },
fields: [ fields: [
// {
// type: 'sort',
// name: 'sort',
// scope: ['parentKey'],
// },
{ {
type: 'uid', type: 'uid',
name: 'key', name: 'key',
primaryKey: true, primaryKey: true,
prefix: 'f_', prefix: 'f_',
// autoIncrement: false,
// defaultValue: DataTypes.UUIDV4,
}, },
{ {
type: 'string', type: 'string',
@ -40,6 +33,11 @@ export default {
type: 'string', type: 'string',
name: 'privilege', name: 'privilege',
}, },
{
type: 'integer',
name: 'state',
default: 1,
},
{ {
type: 'hasMany', type: 'hasMany',
name: 'children', name: 'children',

View File

@ -36,12 +36,22 @@ export class Collection extends Model {
const json = this.toJSON(); const json = this.toJSON();
const data: any = _.omit(json, ['options', 'created_at', 'updated_at']); const data: any = _.omit(json, ['options', 'created_at', 'updated_at']);
const options = json['options'] || {}; const options = json['options'] || {};
const fields = await this.getNestedFields(); const generalFields = await this.getNestedFields({
return { ...data, ...options, fields } where: {
state: 1,
}
});
const systemFields = await this.getNestedFields({
where: {
state: 0,
}
});
return { ...data, ...options, generalFields, systemFields }
} }
async getNestedFields() { async getNestedFields(options) {
const fields = await this.getFields({ const fields = await this.getFields({
...options,
order: [['sort', 'asc']], order: [['sort', 'asc']],
}); });
const items = []; const items = [];
@ -60,8 +70,8 @@ export class Collection extends Model {
*/ */
async loadTableOptions(opts: any = {}) { async loadTableOptions(opts: any = {}) {
const options = await this.toProps(); const options = await this.toProps();
// console.log(JSON.stringify(options, null, 2)); const { generalFields = [], systemFields = [], ...others } = options;
const table = this.database.table(options); const table = this.database.table({ ...others, fields: generalFields.concat(systemFields) });
return table; return table;
} }