feat: improve plugins (#14)

* feat: delete defined resources

* feat: api client

* feat: view fields & tab field options & page info...

* fix: view type
This commit is contained in:
chenos 2020-11-13 22:01:14 +08:00 committed by GitHub
parent 92160d0fe5
commit 9daae13c68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 235 additions and 135 deletions

View File

@ -0,0 +1,56 @@
import { request } from 'umi';
interface ResourceProxyConstructor {
new <T, H extends object>(target: T, handler: ProxyHandler<H>): H
}
const ResourceProxy = Proxy as ResourceProxyConstructor;
interface Params {
resourceKey?: string | number;
// resourceName?: string;
// associatedName?: string;
associatedKey?: string | number;
fields?: any;
filter?: any;
}
interface Handler {
[name: string]: (params?: Params) => Promise<any>;
}
class APIClient {
resource(name: string) {
return new ResourceProxy<object, Handler>({}, {
get(target, method, receiver) {
return (params: Params = {}) => {
console.log(params);
const { associatedKey, resourceKey, ...restParams } = params;
let url = `/${name}`;
let options: any = {};
if (['list', 'get'].indexOf(method as string) !== -1) {
options.method = 'get';
options.params = restParams;
} else {
options.method = 'post';
options.data = restParams;
}
if (associatedKey) {
url = `/${name.split('.').join(`/${associatedKey}/`)}`;
}
url += `:${method as string}`;
// console.log(name, name.split('.'), associatedKey, name.split('.').join(`/${associatedKey}/`));
if (resourceKey) {
url += `/${resourceKey}`;
}
console.log({url, params});
return request(url, options);
};
}
});
}
}
const api = new APIClient();
export default api;

View File

@ -2,7 +2,8 @@ import Api from '../../../server/src';
import dotenv from 'dotenv';
import path from 'path';
import Database, { Model } from '@nocobase/database';
import { get } from 'lodash';
import actions from '../../../actions/src';
import associated from '../../../actions/src/middlewares/associated';
const sync = {
force: true,
@ -34,6 +35,9 @@ const api = Api.create({
},
});
api.resourcer.use(associated);
api.resourcer.registerHandlers(actions.associate);
const data = {
title: '后台应用',
path: '/',

View File

@ -2,12 +2,19 @@ import React from 'react';
import { PageHeader, Tabs, Button, Statistic, Descriptions } from 'antd';
import { CollectionTabPane } from './CollectionTabPane';
import { getPathName, redirectTo } from './utils';
import api from '@/api-client';
import { useRequest } from 'umi';
export function CollectionSingle(props) {
console.log(props);
const { item = {} } = props;
const { tabs = [] } = props.collection;
const activeTab = tabs.find(tab => tab.name == item.tabName)||{};
console.log(activeTab);
const { data = {} } = useRequest(() => activeTab && api.resource(activeTab.collection_name).getPageInfo({
resourceKey: item.itemId,
}));
console.log(data);
if (!activeTab) {
return null;
}
@ -21,7 +28,7 @@ export function CollectionSingle(props) {
removeLastItem: true,
});
}}
title={'企业信息库'}
title={data.pageTitle}
// subTitle="This is a subtitle"
extra={[
// <Button key="3">Operation</Button>,
@ -47,7 +54,7 @@ export function CollectionSingle(props) {
}
/>
<div className={'collection-content'}>
<CollectionTabPane {...props} activeTab={activeTab}/>
<CollectionTabPane {...props} pageInfo={data} activeTab={activeTab}/>
</div>
</div>
);

View File

@ -4,23 +4,27 @@ import { PageHeader, Tabs, Button, Statistic, Descriptions } from 'antd';
import { useRequest, request, Spin } from '@nocobase/client';
export function CollectionTabPane(props) {
const { activeTab = {}, item = {} } = props;
const { viewCollectionName, viewName, association, collection_name } = activeTab;
const { pageInfo = {}, activeTab = {}, item = {} } = props;
const { viewCollectionName, viewName, association, collection_name, field = {} } = activeTab;
const { sourceKey = 'id' } = field;
const params = {};
if (association) {
params['resourceName'] = association;
params['associatedName'] = collection_name;
params['associatedKey'] = item.itemId;
params['associatedKey'] = pageInfo[sourceKey] || item.itemId;
} else {
params['resourceName'] = collection_name;
params['resourceKey'] = item.itemId;
}
console.log(activeTab);
return (
<div>
<ViewFactory {...props}
<ViewFactory
{...props}
viewCollectionName={viewCollectionName}
viewName={viewName}
{...params}

View File

@ -4,6 +4,7 @@ import CollectionIndex from './CollectionIndex';
import CollectionSingle from './CollectionSingle';
import './style.less';
import { useRequest, request, Spin } from '@nocobase/client';
import api from '@/api-client';
export function CollectionLoader(props: any) {
let { path, pagepath, collection } = props.match.params;
@ -24,7 +25,7 @@ export function CollectionLoader(props: any) {
}));
props.match.params['items'] = items;
console.log(props.match, path);
const { data = {}, error, loading, run } = useRequest(() => request(`/${collection}:getCollection`));
const { data = {}, error, loading, run } = useRequest(() => api.resource(collection).getCollection());
if (loading) {
return <Spin/>;

View File

@ -3,10 +3,11 @@ import React, { useState } from 'react';
import { TemplateLoader } from './TemplateLoader';
import { useRequest, request } from '@nocobase/client';
import templates from '@/templates';
import api from '@/api-client';
export function PageLoader(props: any) {
const { path } = props.match.params;
const { data = {}, error, loading, run } = useRequest(() => request('/pages:getRoutes'));
const { data = {}, error, loading, run } = useRequest(() => api.resource('pages').getRoutes());
const [first, setFirst] = useState(true);
(window as any).routesReload = async () => {
setFirst(false);

View File

@ -1,8 +1,10 @@
import React, { useRef } from 'react';
import React, { useEffect, useRef } from 'react';
import { Table as AntdTable, Card } from 'antd';
import { redirectTo } from '@/components/pages/CollectionLoader/utils';
import { Actions } from '@/components/actions';
import ViewFactory from '@/components/views';
import { request, useRequest } from 'umi';
import api from '@/api-client';
const dataSource = [];
for (let i = 0; i < 46; i++) {
@ -32,20 +34,42 @@ const columns = [
},
];
export function SimpleTable(props: any) {
export interface SimpleTableProps {
schema?: any;
activeTab?: any;
resourceName: string;
associatedName?: string;
associatedKey?: string;
[key: string]: any;
}
export function SimpleTable(props: SimpleTableProps) {
console.log(props);
const { activeTab, schema } = props;
const { viewCollectionName, rowViewName, actions = [] } = schema;
const {
activeTab = {},
pageInfo = {},
schema,
resourceName,
associatedName,
associatedKey,
} = props;
const { fields, viewCollectionName, rowViewName, actions = [] } = schema;
const { sourceKey = 'id' } = activeTab.field||{};
const drawerRef = useRef<any>();
const name = associatedName ? `${associatedName}.${resourceName}` : resourceName;
const { data } = useRequest(() => api.resource(name).list({
associatedKey,
}));
console.log(activeTab);
return (
<Card bordered={false}>
<Actions {...props} style={{ marginBottom: 14 }} actions={actions}/>
<ViewFactory reference={drawerRef} viewCollectionName={viewCollectionName} viewName={rowViewName}/>
<AntdTable dataSource={dataSource} onRow={(data) => ({
<AntdTable dataSource={data} onRow={(data) => ({
onClick: () => {
drawerRef.current.setVisible(true);
},
})} columns={columns} />
})} columns={fields} />
</Card>
);
}

View File

@ -5,6 +5,7 @@ import { Table } from './Table';
import { Details } from './Details';
import { useRequest, request, Spin } from '@nocobase/client';
import { SimpleTable } from './SimpleTable';
import api from '@/api-client';
const TEMPLATES = new Map<string, any>();
@ -24,10 +25,14 @@ registerView('SimpleTable', SimpleTable);
registerView('Details', Details);
export default function ViewFactory(props) {
const { viewCollectionName, viewName, reference } = props;
const { data = {}, error, loading, run } = useRequest(() => request(`/${viewCollectionName}:getView/${viewName}`), {
const { activeTab, viewCollectionName, viewName, reference } = props;
console.log({viewCollectionName, viewName});
const { data = {}, error, loading, run } = useRequest(() => api.resource(viewCollectionName).getView({
resourceKey: viewName,
}), {
refreshDeps: [viewCollectionName, viewName],
});
console.log(activeTab);
if (loading) {
return <Spin/>;
}

View File

@ -3,16 +3,7 @@ import { Table as AntdTable, Card } from 'antd';
import { redirectTo } from '@/components/pages/CollectionLoader/utils';
import { Actions } from '@/components/actions';
import { request, useRequest } from 'umi';
const dataSource = [];
for (let i = 0; i < 46; i++) {
dataSource.push({
id: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
});
}
import api from '@/api-client';
const columns = [
{
@ -32,12 +23,31 @@ const columns = [
},
];
export function Table(props: any) {
console.log(props);
const { activeTab, schema } = props;
const { defaultTabId, defaultTabName, actions = [] } = schema;
const { data } = useRequest(() => request('/collections'));
export interface TableProps {
schema?: any;
activeTab?: any;
resourceName: string;
associatedName?: string;
associatedKey?: string;
[key: string]: any;
}
export function Table(props: TableProps) {
const {
activeTab = {},
schema,
resourceName,
associatedName,
associatedKey,
} = props;
const { defaultTabId, fields, defaultTabName, rowKey = 'id', actions = [] } = schema;
const name = associatedName ? `${associatedName}.${resourceName}` : resourceName;
const { data } = useRequest(() => api.resource(name).list({
associatedKey,
}));
const { sourceKey = 'id' } = activeTab.field||{};
console.log(data);
console.log(activeTab);
return (
<Card bordered={false}>
<Actions {...props} style={{ marginBottom: 14 }} actions={actions}/>
@ -46,12 +56,12 @@ export function Table(props: any) {
redirectTo({
...props.match.params,
[activeTab ? 'newItem' : 'lastItem']: {
itemId: data.id,
itemId: data[rowKey]||data.id,
tabName: defaultTabName,
},
});
},
})} columns={columns} />
})} columns={fields} />
</Card>
);
}

View File

@ -69,7 +69,7 @@ export default {
actionNames: ['update'],
},
{
type: 'simple',
type: 'table',
name: 'simple',
title: '简易模式',
template: 'SimpleTable',

View File

@ -104,7 +104,7 @@ export default {
actionNames: ['update'],
},
{
type: 'simple',
type: 'table',
name: 'simple',
title: '简易模式',
template: 'SimpleTable',

View File

@ -69,7 +69,7 @@ export default {
actionNames: ['update'],
},
{
type: 'simple',
type: 'table',
name: 'simple',
title: '简易模式',
template: 'SimpleTable',

View File

@ -75,7 +75,7 @@ export default {
actionNames: ['update'],
},
{
type: 'simple',
type: 'table',
name: 'simple',
title: '简易模式',
template: 'SimpleTable',

View File

@ -88,7 +88,7 @@ export default {
actionNames: ['update'],
},
{
type: 'simple',
type: 'table',
name: 'simple',
title: '简易模式',
template: 'SimpleTable',

View File

@ -1,5 +0,0 @@
import { ResourceOptions } from '@nocobase/resourcer';
export default {
name: 'actions',
} as ResourceOptions;

View File

@ -1,25 +0,0 @@
import { ResourceOptions } from '@nocobase/resourcer';
export default {
name: 'collections',
actions: {
list: {
fields: {
appends: ['fields'],
}
},
get: {
fields: {
appends: ['fields'],
}
},
// get: {
// handler: async (ctx, next) => {
// ctx.body = {
// get: {}
// }
// await next();
// }
// },
},
} as ResourceOptions;

View File

@ -1,5 +0,0 @@
import { ResourceOptions } from '@nocobase/resourcer';
export default {
name: 'fields',
} as ResourceOptions;

View File

@ -1,5 +0,0 @@
import { ResourceOptions } from '@nocobase/resourcer';
export default {
name: 'tabs',
} as ResourceOptions;

View File

@ -1,5 +0,0 @@
import { ResourceOptions } from '@nocobase/resourcer';
export default {
name: 'views',
} as ResourceOptions;

View File

@ -9,8 +9,4 @@ export default async function (this: any, options = {}) {
database.import({
directory: path.resolve(__dirname, 'collections'),
});
resourcer.import({
directory: path.resolve(__dirname, 'resources'),
});
}

View File

@ -9,8 +9,4 @@ export default async function (options = {}) {
database.import({
directory: path.resolve(__dirname, 'collections'),
});
resourcer.import({
directory: path.resolve(__dirname, 'resources'),
});
}

View File

@ -4,7 +4,7 @@ import { get } from 'lodash';
export default async (ctx, next) => {
const { resourceName, resourceKey } = ctx.action.params;
const [Collection, Tab, View] = ctx.db.getModels(['collections', 'tabs', 'views']) as ModelCtor<Model>[];
const [Collection, Field, Tab, View] = ctx.db.getModels(['collections', 'fields', 'tabs', 'views']) as ModelCtor<Model>[];
const collection = await Collection.findOne(Collection.parseApiJson({
filter: {
name: resourceName,
@ -20,14 +20,33 @@ export default async (ctx, next) => {
});
collection.setDataValue('defaultViewId', get(views, [0, 'id']));
collection.setDataValue('defaultViewName', get(views, [0, 'name']));
const tabs = await collection.getTabs();
ctx.body = {
...collection.toJSON(),
tabs: tabs.map(tab => ({
const tabs = await collection.getTabs() as Model[];
const tabItems = [];
for (const tab of tabs) {
const itemTab = {
...tab.toJSON(),
...tab.options,
viewCollectionName: tab.type == 'association' ? tab.options.association : tab.collection_name,
})),
};
if (itemTab.type == 'association') {
const field = await Field.findOne({
where: {
collection_name: itemTab.collection_name,
name: itemTab.association,
},
});
itemTab.field = field ? {
...field.toJSON(),
...field.options,
} : {};
itemTab.viewCollectionName = itemTab.association;
} else {
itemTab.viewCollectionName = itemTab.collection_name;
}
tabItems.push(itemTab);
}
ctx.body = {
...collection.toJSON(),
tabs: tabItems,
};
await next();
}

View File

@ -0,0 +1,14 @@
import { ResourceOptions } from '@nocobase/resourcer';
import { Model, ModelCtor } from '@nocobase/database';
import { get } from 'lodash';
export default async (ctx, next) => {
const { resourceName, resourceKey } = ctx.action.params;
const M = ctx.db.getModel(resourceName) as ModelCtor<Model>;
const model = await M.findByPk(resourceKey);
ctx.body = {
pageTitle: model.title,
...model.toJSON(),
};
await next();
};

View File

@ -2,6 +2,41 @@ import { ResourceOptions } from '@nocobase/resourcer';
import { Model, ModelCtor } from '@nocobase/database';
import { get } from 'lodash';
const transforms = {
table: async (fields: Model[]) => {
const arr = [];
for (const field of fields) {
arr.push({
...field.toJSON(),
...field.options,
dataIndex: field.name,
});
}
return arr;
},
form: async (fields: Model[]) => {
const arr = [];
for (const field of fields) {
arr.push({
...field.toJSON(),
...field.options,
dataIndex: field.name,
});
}
return arr;
},
details: async (fields: Model[]) => {
const arr = [];
for (const field of fields) {
arr.push({
...field.toJSON(),
...field.options,
});
}
return arr;
},
};
export default async (ctx, next) => {
const { resourceName, resourceKey } = ctx.action.params;
const [View, Field, Action] = ctx.db.getModels(['views', 'fields', 'actions']) as ModelCtor<Model>[];
@ -10,9 +45,9 @@ export default async (ctx, next) => {
collection_name: resourceName,
name: resourceKey,
},
fields: {
appends: ['actions', 'fields'],
},
// fields: {
// appends: ['actions', 'fields'],
// },
}));
const collection = await view.getCollection();
const fields = await collection.getFields();
@ -27,14 +62,14 @@ export default async (ctx, next) => {
});
view.setDataValue('defaultTabName', get(defaultTabs, [0, 'name']));
}
if (view.options.updateViewId) {
if (view.options.updateViewName) {
view.setDataValue('rowViewName', view.options.updateViewName);
}
view.setDataValue('viewCollectionName', view.collection_name);
ctx.body = {
...view.toJSON(),
...(view.options||{}),
fields,
fields: await (transforms[view.type]||transforms.table)(fields),
actions: actions.filter(action => actionNames.includes(action.name)).map(action => ({
...action.toJSON(),
...action.options,

View File

@ -1,9 +0,0 @@
import { ResourceOptions } from '@nocobase/resourcer';
import getRoutes from '../actions/getRoutes';
export default {
name: 'pages',
actions: {
getRoutes,
},
} as ResourceOptions;

View File

@ -3,6 +3,8 @@ import Database from '@nocobase/database';
import Resourcer from '@nocobase/resourcer';
import getCollection from './actions/getCollection';
import getView from './actions/getView';
import getRoutes from './actions/getRoutes';
import getPageInfo from './actions/getPageInfo';
export default async function (options = {}) {
const database: Database = this.database;
@ -14,8 +16,6 @@ export default async function (options = {}) {
resourcer.registerHandler('getCollection', getCollection);
resourcer.registerHandler('getView', getView);
resourcer.import({
directory: path.resolve(__dirname, 'resources'),
});
resourcer.registerHandler('getPageInfo', getPageInfo);
resourcer.registerHandler('pages:getRoutes', getRoutes);
}

View File

@ -1,5 +0,0 @@
import { ResourceOptions } from '@nocobase/resourcer';
export default {
name: 'roles',
} as ResourceOptions;

View File

@ -9,8 +9,4 @@ export default async function (options = {}) {
database.import({
directory: path.resolve(__dirname, 'collections'),
});
resourcer.import({
directory: path.resolve(__dirname, 'resources'),
});
}

View File

@ -1,5 +0,0 @@
import { ResourceOptions } from '@nocobase/resourcer';
export default {
name: 'users',
} as ResourceOptions;

View File

@ -9,8 +9,4 @@ export default async function (options = {}) {
database.import({
directory: path.resolve(__dirname, 'collections'),
});
resourcer.import({
directory: path.resolve(__dirname, 'resources'),
});
}