feat: actions & views (#18)
* feat: 视图新增支持 associated 的情况 * fix: 解决关系数据的视图问题 * 表单数据初始化 * feat: sortable table * 排序 * 表格细节调整 * feat: field actions
This commit is contained in:
parent
b0ba1472bb
commit
eb93f0734f
@ -33,12 +33,14 @@
|
|||||||
"@nocobase/server": "^0.3.0-alpha.0",
|
"@nocobase/server": "^0.3.0-alpha.0",
|
||||||
"@umijs/preset-react": "1.x",
|
"@umijs/preset-react": "1.x",
|
||||||
"@umijs/test": "^3.2.23",
|
"@umijs/test": "^3.2.23",
|
||||||
|
"array-move": "^3.0.1",
|
||||||
"concurrently": "^5.3.0",
|
"concurrently": "^5.3.0",
|
||||||
"lint-staged": "^10.0.7",
|
"lint-staged": "^10.0.7",
|
||||||
"nodemon": "^2.0.6",
|
"nodemon": "^2.0.6",
|
||||||
"prettier": "^1.19.1",
|
"prettier": "^1.19.1",
|
||||||
"react": "^16.12.0",
|
"react": "^16.12.0",
|
||||||
"react-dom": "^16.12.0",
|
"react-dom": "^16.12.0",
|
||||||
|
"react-sortable-hoc": "^1.11.0",
|
||||||
"styled-components": "^5.2.1",
|
"styled-components": "^5.2.1",
|
||||||
"umi": "^3.2.23",
|
"umi": "^3.2.23",
|
||||||
"yorkie": "^2.0.0"
|
"yorkie": "^2.0.0"
|
||||||
|
@ -43,7 +43,7 @@ const data = {
|
|||||||
path: '/',
|
path: '/',
|
||||||
type: 'layout',
|
type: 'layout',
|
||||||
template: 'TopMenuLayout',
|
template: 'TopMenuLayout',
|
||||||
order: 1,
|
sort: 10,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: '仪表盘',
|
title: '仪表盘',
|
||||||
@ -51,7 +51,7 @@ const data = {
|
|||||||
path: '/dashboard',
|
path: '/dashboard',
|
||||||
icon: 'dashboard',
|
icon: 'dashboard',
|
||||||
template: 'page1',
|
template: 'page1',
|
||||||
order: 2,
|
sort: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '数据',
|
title: '数据',
|
||||||
@ -59,7 +59,7 @@ const data = {
|
|||||||
path: '/collections',
|
path: '/collections',
|
||||||
icon: 'dashboard',
|
icon: 'dashboard',
|
||||||
template: 'SideMenuLayout',
|
template: 'SideMenuLayout',
|
||||||
order: 3,
|
sort: 30,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: '页面3',
|
title: '页面3',
|
||||||
@ -67,7 +67,7 @@ const data = {
|
|||||||
path: '/collections/page3',
|
path: '/collections/page3',
|
||||||
icon: 'dashboard',
|
icon: 'dashboard',
|
||||||
template: 'page3',
|
template: 'page3',
|
||||||
order: 5,
|
sort: 40,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '页面4',
|
title: '页面4',
|
||||||
@ -75,17 +75,17 @@ const data = {
|
|||||||
path: '/collections/page4',
|
path: '/collections/page4',
|
||||||
icon: 'dashboard',
|
icon: 'dashboard',
|
||||||
template: 'page4',
|
template: 'page4',
|
||||||
order: 6,
|
sort: 50,
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '页面5',
|
|
||||||
type: 'collection',
|
|
||||||
path: '/collections/collection1',
|
|
||||||
icon: 'dashboard',
|
|
||||||
template: 'collection',
|
|
||||||
collection: 'collection1',
|
|
||||||
order: 7,
|
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// title: '页面5',
|
||||||
|
// type: 'collection',
|
||||||
|
// path: '/collections/collection1',
|
||||||
|
// icon: 'dashboard',
|
||||||
|
// template: 'collection',
|
||||||
|
// collection: 'collection1',
|
||||||
|
// sort: 60,
|
||||||
|
// },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -94,7 +94,7 @@ const data = {
|
|||||||
path: '/users',
|
path: '/users',
|
||||||
icon: 'dashboard',
|
icon: 'dashboard',
|
||||||
template: 'SideMenuLayout',
|
template: 'SideMenuLayout',
|
||||||
order: 3,
|
sort: 70,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: '用户管理',
|
title: '用户管理',
|
||||||
@ -103,7 +103,7 @@ const data = {
|
|||||||
icon: 'dashboard',
|
icon: 'dashboard',
|
||||||
template: 'collection',
|
template: 'collection',
|
||||||
collection: 'users',
|
collection: 'users',
|
||||||
order: 1,
|
sort: 80,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -113,7 +113,7 @@ const data = {
|
|||||||
path: '/settings',
|
path: '/settings',
|
||||||
icon: 'dashboard',
|
icon: 'dashboard',
|
||||||
template: 'SideMenuLayout',
|
template: 'SideMenuLayout',
|
||||||
order: 4,
|
sort: 90,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: '页面与菜单',
|
title: '页面与菜单',
|
||||||
@ -121,7 +121,7 @@ const data = {
|
|||||||
collection: 'pages',
|
collection: 'pages',
|
||||||
path: '/settings/pages',
|
path: '/settings/pages',
|
||||||
icon: 'dashboard',
|
icon: 'dashboard',
|
||||||
order: 1,
|
sort: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '数据表配置',
|
title: '数据表配置',
|
||||||
@ -129,7 +129,7 @@ const data = {
|
|||||||
collection: 'collections',
|
collection: 'collections',
|
||||||
path: '/settings/collections',
|
path: '/settings/collections',
|
||||||
icon: 'dashboard',
|
icon: 'dashboard',
|
||||||
order: 2,
|
sort: 110,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '权限配置',
|
title: '权限配置',
|
||||||
@ -137,7 +137,7 @@ const data = {
|
|||||||
collection: 'roles',
|
collection: 'roles',
|
||||||
path: '/settings/roles',
|
path: '/settings/roles',
|
||||||
icon: 'dashboard',
|
icon: 'dashboard',
|
||||||
order: 3,
|
sort: 120,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -173,117 +173,121 @@ const data = {
|
|||||||
// console.log(options);
|
// console.log(options);
|
||||||
const associations: any = {};
|
const associations: any = {};
|
||||||
if (options.fields) {
|
if (options.fields) {
|
||||||
associations['fields'] = options.fields.map(item => ({
|
associations['fields'] = options.fields.map((item, sort) => ({
|
||||||
...item,
|
...item,
|
||||||
options: item,
|
options: item,
|
||||||
|
sort,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
if (options.tabs) {
|
if (options.tabs) {
|
||||||
associations['tabs'] = options.tabs.map(item => ({
|
associations['tabs'] = options.tabs.map((item, sort) => ({
|
||||||
...item,
|
...item,
|
||||||
options: item,
|
options: item,
|
||||||
|
sort,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
if (options.actions) {
|
if (options.actions) {
|
||||||
associations['actions'] = options.actions.map(item => ({
|
associations['actions'] = options.actions.map((item, sort) => ({
|
||||||
...item,
|
...item,
|
||||||
options: item,
|
options: item,
|
||||||
|
sort,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
if (options.views) {
|
if (options.views) {
|
||||||
associations['views'] = options.views.map(item => ({
|
associations['views'] = options.views.map((item, sort) => ({
|
||||||
...item,
|
...item,
|
||||||
options: item,
|
options: item,
|
||||||
|
sort,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
await collection.updateAssociations(associations);
|
await collection.updateAssociations(associations);
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = await Action.findAll();
|
// const actions = await Action.findAll();
|
||||||
|
|
||||||
for (const action of actions) {
|
// for (const action of actions) {
|
||||||
const viewName = action.options.viewName;
|
// const viewName = action.options.viewName;
|
||||||
console.log({viewName});
|
// console.log({viewName});
|
||||||
if (viewName) {
|
// if (viewName) {
|
||||||
const view = await View.findOne({
|
// const view = await View.findOne({
|
||||||
where: {
|
// where: {
|
||||||
name: viewName,
|
// name: viewName,
|
||||||
collection_name: action.collection_name
|
// collection_name: action.collection_name
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
if (view) {
|
// if (view) {
|
||||||
action.options.viewId = view.id;
|
// action.options.viewId = view.id;
|
||||||
console.log(action.options);
|
// console.log(action.options);
|
||||||
action.setDataValue('options', action.options);
|
// action.setDataValue('options', action.options);
|
||||||
action.changed('options', true);
|
// action.changed('options', true);
|
||||||
await action.save();
|
// await action.save();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
const tabs = await Tab.findAll();
|
// const tabs = await Tab.findAll();
|
||||||
|
|
||||||
for (const tab of tabs) {
|
// for (const tab of tabs) {
|
||||||
const viewName = tab.options.viewName;
|
// const viewName = tab.options.viewName;
|
||||||
if (!viewName) {
|
// if (!viewName) {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
let view: any;
|
// let view: any;
|
||||||
if (tab.type === 'association') {
|
// if (tab.type === 'association') {
|
||||||
view = await View.findOne({
|
// view = await View.findOne({
|
||||||
where: {
|
// where: {
|
||||||
name: viewName,
|
// name: viewName,
|
||||||
collection_name: tab.options.association,
|
// collection_name: tab.options.association,
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
} else {
|
// } else {
|
||||||
view = await View.findOne({
|
// view = await View.findOne({
|
||||||
where: {
|
// where: {
|
||||||
name: viewName,
|
// name: viewName,
|
||||||
collection_name: tab.collection_name,
|
// collection_name: tab.collection_name,
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
if (view) {
|
// if (view) {
|
||||||
tab.options.viewId = view.id;
|
// tab.options.viewId = view.id;
|
||||||
tab.setDataValue('options', tab.options);
|
// tab.setDataValue('options', tab.options);
|
||||||
tab.changed('options', true);
|
// tab.changed('options', true);
|
||||||
await tab.save();
|
// await tab.save();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
const views = await View.findAll();
|
// const views = await View.findAll();
|
||||||
for (const view of views) {
|
// for (const view of views) {
|
||||||
const detailsViewName = view.options.detailsViewName;
|
// const detailsViewName = view.options.detailsViewName;
|
||||||
if (detailsViewName) {
|
// if (detailsViewName) {
|
||||||
const v = await View.findOne({
|
// const v = await View.findOne({
|
||||||
where: {
|
// where: {
|
||||||
name: detailsViewName,
|
// name: detailsViewName,
|
||||||
collection_name: view.collection_name
|
// collection_name: view.collection_name
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
if (v) {
|
// if (v) {
|
||||||
view.options.detailsViewId = v.id;
|
// view.options.detailsViewId = v.id;
|
||||||
view.setDataValue('options', view.options);
|
// view.setDataValue('options', view.options);
|
||||||
view.changed('options', true);
|
// view.changed('options', true);
|
||||||
await view.save();
|
// await view.save();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
const updateViewName = view.options.updateViewName;
|
// const updateViewName = view.options.updateViewName;
|
||||||
if (updateViewName) {
|
// if (updateViewName) {
|
||||||
const v = await View.findOne({
|
// const v = await View.findOne({
|
||||||
where: {
|
// where: {
|
||||||
name: updateViewName,
|
// name: updateViewName,
|
||||||
collection_name: view.collection_name
|
// collection_name: view.collection_name
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
if (v) {
|
// if (v) {
|
||||||
view.options.updateViewId = v.id;
|
// view.options.updateViewId = v.id;
|
||||||
view.setDataValue('options', view.options);
|
// view.setDataValue('options', view.options);
|
||||||
view.changed('options', true);
|
// view.changed('options', true);
|
||||||
await view.save();
|
// await view.save();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
console.log({detailsViewName, updateViewName});
|
// console.log({detailsViewName, updateViewName});
|
||||||
}
|
// }
|
||||||
|
|
||||||
// for (let table of tables) {
|
// for (let table of tables) {
|
||||||
// const options = table.getOptions();
|
// const options = table.getOptions();
|
||||||
|
@ -4,28 +4,29 @@ import ViewFactory from '@/components/views';
|
|||||||
|
|
||||||
export function Create(props) {
|
export function Create(props) {
|
||||||
console.log(props);
|
console.log(props);
|
||||||
const { title, viewCollectionName, viewName } = props.schema;
|
const { title, viewName, collection_name } = props.schema;
|
||||||
const { activeTab = {}, item = {} } = props;
|
const { activeTab = {}, item = {} } = props;
|
||||||
const { association, collection_name } = activeTab;
|
const { association } = activeTab;
|
||||||
|
|
||||||
const params = {};
|
const params = {};
|
||||||
|
|
||||||
if (association) {
|
if (association) {
|
||||||
params['resourceName'] = association;
|
params['resourceName'] = association;
|
||||||
params['associatedName'] = collection_name;
|
params['associatedName'] = activeTab.collection_name;
|
||||||
params['associatedKey'] = item.itemId;
|
params['associatedKey'] = item.itemId;
|
||||||
} else {
|
} else {
|
||||||
params['resourceName'] = collection_name;
|
params['resourceName'] = collection_name;
|
||||||
params['resourceKey'] = item.itemId;
|
params['resourceKey'] = item.itemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(params);
|
||||||
|
|
||||||
const drawerRef = useRef<any>();
|
const drawerRef = useRef<any>();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewFactory
|
<ViewFactory
|
||||||
{...props}
|
{...props}
|
||||||
reference={drawerRef}
|
reference={drawerRef}
|
||||||
viewCollectionName={viewCollectionName}
|
|
||||||
viewName={viewName}
|
viewName={viewName}
|
||||||
{...params}
|
{...params}
|
||||||
/>
|
/>
|
||||||
|
@ -4,15 +4,15 @@ import ViewFactory from '@/components/views';
|
|||||||
|
|
||||||
export function Update(props) {
|
export function Update(props) {
|
||||||
console.log(props);
|
console.log(props);
|
||||||
const { title, viewCollectionName, viewName } = props.schema;
|
const { title, viewName, resourceName, collection_name } = props.schema;
|
||||||
const { activeTab = {}, item = {} } = props;
|
const { activeTab = {}, item = {} } = props;
|
||||||
const { association, collection_name } = activeTab;
|
const { association } = activeTab;
|
||||||
|
|
||||||
const params = {};
|
const params = {};
|
||||||
|
|
||||||
if (association) {
|
if (association) {
|
||||||
params['resourceName'] = association;
|
params['resourceName'] = association;
|
||||||
params['associatedName'] = collection_name;
|
params['associatedName'] = resourceName;
|
||||||
params['associatedKey'] = item.itemId;
|
params['associatedKey'] = item.itemId;
|
||||||
} else {
|
} else {
|
||||||
params['resourceName'] = collection_name;
|
params['resourceName'] = collection_name;
|
||||||
@ -22,15 +22,15 @@ export function Update(props) {
|
|||||||
const drawerRef = useRef<any>();
|
const drawerRef = useRef<any>();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewFactory
|
<ViewFactory
|
||||||
{...props}
|
{...props}
|
||||||
reference={drawerRef}
|
reference={drawerRef}
|
||||||
viewCollectionName={viewCollectionName}
|
|
||||||
viewName={viewName}
|
viewName={viewName}
|
||||||
{...params}
|
{...params}
|
||||||
/>
|
/>
|
||||||
<Button type={'primary'} onClick={() => {
|
<Button type={'primary'} onClick={() => {
|
||||||
drawerRef.current.setVisible(true);
|
drawerRef.current.setVisible(true);
|
||||||
|
drawerRef.current.getData(item.itemId);
|
||||||
}}>{title}</Button>
|
}}>{title}</Button>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -29,8 +29,8 @@ export function CollectionIndex(props) {
|
|||||||
// }
|
// }
|
||||||
/>
|
/>
|
||||||
<div className={'collection-content'}>
|
<div className={'collection-content'}>
|
||||||
<ViewFactory {...props}
|
<ViewFactory
|
||||||
viewCollectionName={collection}
|
{...props}
|
||||||
viewName={viewName||defaultViewName}
|
viewName={viewName||defaultViewName}
|
||||||
resourceName={collection}
|
resourceName={collection}
|
||||||
/>
|
/>
|
||||||
|
@ -4,6 +4,7 @@ import { CollectionTabPane } from './CollectionTabPane';
|
|||||||
import { getPathName, redirectTo } from './utils';
|
import { getPathName, redirectTo } from './utils';
|
||||||
import api from '@/api-client';
|
import api from '@/api-client';
|
||||||
import { useRequest } from 'umi';
|
import { useRequest } from 'umi';
|
||||||
|
import { Spin } from '@nocobase/client';
|
||||||
|
|
||||||
export function CollectionSingle(props) {
|
export function CollectionSingle(props) {
|
||||||
console.log(props);
|
console.log(props);
|
||||||
@ -11,13 +12,16 @@ export function CollectionSingle(props) {
|
|||||||
const { tabs = [] } = props.collection;
|
const { tabs = [] } = props.collection;
|
||||||
const activeTab = tabs.find(tab => tab.name == item.tabName)||{};
|
const activeTab = tabs.find(tab => tab.name == item.tabName)||{};
|
||||||
console.log(activeTab);
|
console.log(activeTab);
|
||||||
const { data = {} } = useRequest(() => activeTab && api.resource(activeTab.collection_name).getPageInfo({
|
const { data = {}, loading } = useRequest(() => activeTab && api.resource(activeTab.collection_name).getPageInfo({
|
||||||
resourceKey: item.itemId,
|
resourceKey: item.itemId,
|
||||||
}));
|
}));
|
||||||
console.log(data);
|
console.log(data);
|
||||||
if (!activeTab) {
|
if (!activeTab) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (loading) {
|
||||||
|
return <Spin/>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
|
@ -5,7 +5,7 @@ import { useRequest, request, Spin } from '@nocobase/client';
|
|||||||
|
|
||||||
export function CollectionTabPane(props) {
|
export function CollectionTabPane(props) {
|
||||||
const { pageInfo = {}, activeTab = {}, item = {} } = props;
|
const { pageInfo = {}, activeTab = {}, item = {} } = props;
|
||||||
const { viewCollectionName, viewName, association, collection_name, field = {} } = activeTab;
|
const { viewName, association, collection_name, field = {} } = activeTab;
|
||||||
const { sourceKey = 'id' } = field;
|
const { sourceKey = 'id' } = field;
|
||||||
|
|
||||||
const params = {};
|
const params = {};
|
||||||
@ -25,7 +25,6 @@ export function CollectionTabPane(props) {
|
|||||||
<div>
|
<div>
|
||||||
<ViewFactory
|
<ViewFactory
|
||||||
{...props}
|
{...props}
|
||||||
viewCollectionName={viewCollectionName}
|
|
||||||
viewName={viewName}
|
viewName={viewName}
|
||||||
{...params}
|
{...params}
|
||||||
/>
|
/>
|
||||||
|
@ -34,7 +34,7 @@ export function CollectionLoader(props: any) {
|
|||||||
return (
|
return (
|
||||||
<div className={'collection'}>
|
<div className={'collection'}>
|
||||||
<div className={'collection-index'}>
|
<div className={'collection-index'}>
|
||||||
<CollectionIndex collection={data.data||{}} {...props}/>
|
{ items.length === 0 && <CollectionIndex collection={data.data||{}} {...props}/> }
|
||||||
</div>
|
</div>
|
||||||
{items.length > 0 && (
|
{items.length > 0 && (
|
||||||
<div className={'collection-item'}>
|
<div className={'collection-item'}>
|
||||||
|
20
packages/app/src/components/pages/Page.tsx
Normal file
20
packages/app/src/components/pages/Page.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { PageHeader } from 'antd';
|
||||||
|
import './style.less';
|
||||||
|
|
||||||
|
export function Page(props: any) {
|
||||||
|
const { children, ...restProps } = props;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PageHeader
|
||||||
|
ghost={false}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
<div className={'page-content'}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
@ -4,3 +4,4 @@ export { CollectionLoader } from './CollectionLoader';
|
|||||||
export { PageLoader as default } from './PageLoader';
|
export { PageLoader as default } from './PageLoader';
|
||||||
export { SideMenuLayout } from './SideMenuLayout/index';
|
export { SideMenuLayout } from './SideMenuLayout/index';
|
||||||
export { TopMenuLayout } from './TopMenuLayout/index';
|
export { TopMenuLayout } from './TopMenuLayout/index';
|
||||||
|
export { Page } from './Page';
|
||||||
|
3
packages/app/src/components/pages/style.less
Normal file
3
packages/app/src/components/pages/style.less
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.page-content {
|
||||||
|
margin: 24px;
|
||||||
|
}
|
39
packages/app/src/components/views/Details.tsx
Normal file
39
packages/app/src/components/views/Details.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Card, Descriptions, Button } from 'antd';
|
||||||
|
import { Actions } from '@/components/actions';
|
||||||
|
import api from '@/api-client';
|
||||||
|
import { useRequest } from 'umi';
|
||||||
|
import { Spin } from '@nocobase/client';
|
||||||
|
|
||||||
|
export function Details(props: any) {
|
||||||
|
const {
|
||||||
|
activeTab = {},
|
||||||
|
pageInfo = {},
|
||||||
|
schema,
|
||||||
|
resourceName,
|
||||||
|
associatedName,
|
||||||
|
associatedKey,
|
||||||
|
resourceKey,
|
||||||
|
} = props;
|
||||||
|
const { data = {}, loading } = useRequest(() => {
|
||||||
|
const name = associatedName ? `${associatedName}.${resourceName}` : resourceName;
|
||||||
|
return api.resource(name).get({
|
||||||
|
resourceKey,
|
||||||
|
associatedKey,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
console.log(props);
|
||||||
|
const { actions = [], fields = [] } = props.schema;
|
||||||
|
return (
|
||||||
|
<Card bordered={false}>
|
||||||
|
<Actions {...props} style={{ marginBottom: 14 }} actions={actions}/>
|
||||||
|
{loading ? <Spin/> : (
|
||||||
|
<Descriptions bordered column={1}>
|
||||||
|
{fields.map((field: any) => (
|
||||||
|
<Descriptions.Item label={field.title||field.name}>{data[field.name]}</Descriptions.Item>
|
||||||
|
))}
|
||||||
|
</Descriptions>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
@ -14,15 +14,37 @@ import {
|
|||||||
setValidationLanguage,
|
setValidationLanguage,
|
||||||
} from '@formily/antd';
|
} from '@formily/antd';
|
||||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { useRequest } from 'umi';
|
||||||
|
import api from '@/api-client';
|
||||||
|
import { Spin } from '@nocobase/client';
|
||||||
|
|
||||||
export const DrawerForm = forwardRef((props: any, ref) => {
|
export const DrawerForm = forwardRef((props: any, ref) => {
|
||||||
console.log(props);
|
console.log(props);
|
||||||
|
const {
|
||||||
|
activeTab = {},
|
||||||
|
pageInfo = {},
|
||||||
|
schema,
|
||||||
|
resourceName,
|
||||||
|
associatedName,
|
||||||
|
associatedKey,
|
||||||
|
} = props;
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
|
const { data, run, loading } = useRequest((resourceKey) => {
|
||||||
|
const name = associatedName ? `${associatedName}.${resourceName}` : resourceName;
|
||||||
|
return api.resource(name).get({
|
||||||
|
resourceKey,
|
||||||
|
associatedKey,
|
||||||
|
});
|
||||||
|
}, {
|
||||||
|
manual: true,
|
||||||
|
});
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
setVisible,
|
setVisible,
|
||||||
|
getData: run,
|
||||||
}));
|
}));
|
||||||
const actions = createAsyncFormActions();
|
const actions = createAsyncFormActions();
|
||||||
const { title } = props.schema||{};
|
const { title, fields: properties ={} } = props.schema||{};
|
||||||
|
console.log({properties});
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
{...props}
|
{...props}
|
||||||
@ -39,37 +61,33 @@ export const DrawerForm = forwardRef((props: any, ref) => {
|
|||||||
}}>提交</Button>
|
}}>提交</Button>
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SchemaForm
|
{loading ? <Spin/> : (
|
||||||
colon={true}
|
<SchemaForm
|
||||||
layout={'vertical'}
|
colon={true}
|
||||||
initialValues={{}}
|
layout={'vertical'}
|
||||||
actions={actions}
|
initialValues={data}
|
||||||
schema={{
|
actions={actions}
|
||||||
type: 'object',
|
schema={{
|
||||||
properties: {
|
type: 'object',
|
||||||
title: {
|
properties,
|
||||||
type: 'string',
|
}}
|
||||||
title: '标题',
|
expressionScope={{
|
||||||
required: true,
|
text(...args: any[]) {
|
||||||
|
return React.createElement('span', {}, ...args)
|
||||||
},
|
},
|
||||||
},
|
tooltip(title: string, offset = 3) {
|
||||||
}}
|
return (
|
||||||
expressionScope={{
|
<Tooltip title={title}>
|
||||||
text(...args: any[]) {
|
<QuestionCircleOutlined
|
||||||
return React.createElement('span', {}, ...args)
|
style={{ margin: '0 3px', cursor: 'default', marginLeft: offset }}
|
||||||
},
|
/>
|
||||||
tooltip(title: string, offset = 3) {
|
</Tooltip>
|
||||||
return (
|
);
|
||||||
<Tooltip title={title}>
|
},
|
||||||
<QuestionCircleOutlined
|
}}
|
||||||
style={{ margin: '0 3px', cursor: 'default', marginLeft: offset }}
|
>
|
||||||
/>
|
</SchemaForm>
|
||||||
</Tooltip>
|
)}
|
||||||
);
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
</SchemaForm>
|
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,38 +1,11 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { Table as AntdTable, Card } from 'antd';
|
import { Table as AntdTable, Card } from 'antd';
|
||||||
import { redirectTo } from '@/components/pages/CollectionLoader/utils';
|
|
||||||
import { Actions } from '@/components/actions';
|
import { Actions } from '@/components/actions';
|
||||||
import ViewFactory from '@/components/views';
|
import ViewFactory from '@/components/views';
|
||||||
import { request, useRequest } from 'umi';
|
import { useRequest } from 'umi';
|
||||||
import api from '@/api-client';
|
import api from '@/api-client';
|
||||||
|
import get from 'lodash/get';
|
||||||
const dataSource = [];
|
import { components, fields2columns } from './SortableTable';
|
||||||
for (let i = 0; i < 46; i++) {
|
|
||||||
dataSource.push({
|
|
||||||
id: i,
|
|
||||||
name: `Edward King ${i}`,
|
|
||||||
age: 32,
|
|
||||||
address: `London, Park Lane no. ${i}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: '姓名',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '年龄',
|
|
||||||
dataIndex: 'age',
|
|
||||||
key: 'age',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '住址',
|
|
||||||
dataIndex: 'address',
|
|
||||||
key: 'address',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export interface SimpleTableProps {
|
export interface SimpleTableProps {
|
||||||
schema?: any;
|
schema?: any;
|
||||||
@ -53,23 +26,46 @@ export function SimpleTable(props: SimpleTableProps) {
|
|||||||
associatedName,
|
associatedName,
|
||||||
associatedKey,
|
associatedKey,
|
||||||
} = props;
|
} = props;
|
||||||
const { fields, viewCollectionName, rowViewName, actions = [] } = schema;
|
const { rowKey = 'id', fields = [], rowViewName, actions = [] } = schema;
|
||||||
const { sourceKey = 'id' } = activeTab.field||{};
|
const { sourceKey = 'id' } = activeTab.field||{};
|
||||||
const drawerRef = useRef<any>();
|
const drawerRef = useRef<any>();
|
||||||
const name = associatedName ? `${associatedName}.${resourceName}` : resourceName;
|
const name = associatedName ? `${associatedName}.${resourceName}` : resourceName;
|
||||||
const { data } = useRequest(() => api.resource(name).list({
|
const { data, mutate } = useRequest(() => api.resource(name).list({
|
||||||
associatedKey,
|
associatedKey,
|
||||||
}));
|
}));
|
||||||
console.log(activeTab);
|
console.log(activeTab);
|
||||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||||
|
const onChange = (selectedRowKeys: React.ReactText[]) => {
|
||||||
|
setSelectedRowKeys(selectedRowKeys);
|
||||||
|
}
|
||||||
|
const tableProps: any = {};
|
||||||
|
if (actions.length) {
|
||||||
|
tableProps.rowSelection = {
|
||||||
|
selectedRowKeys,
|
||||||
|
onChange,
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Card bordered={false}>
|
<Card bordered={false}>
|
||||||
<Actions {...props} style={{ marginBottom: 14 }} actions={actions}/>
|
<Actions {...props} style={{ marginBottom: 14 }} actions={actions}/>
|
||||||
<ViewFactory reference={drawerRef} viewCollectionName={viewCollectionName} viewName={rowViewName}/>
|
<ViewFactory
|
||||||
<AntdTable dataSource={data} onRow={(data) => ({
|
{...props}
|
||||||
onClick: () => {
|
viewName={rowViewName}
|
||||||
drawerRef.current.setVisible(true);
|
reference={drawerRef}
|
||||||
},
|
/>
|
||||||
})} columns={fields} />
|
<AntdTable
|
||||||
|
columns={fields2columns(fields)}
|
||||||
|
dataSource={data}
|
||||||
|
rowKey={rowKey}
|
||||||
|
components={components({data, mutate})}
|
||||||
|
onRow={(record) => ({
|
||||||
|
onClick: () => {
|
||||||
|
drawerRef.current.setVisible(true);
|
||||||
|
drawerRef.current.getData(record[rowKey]);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
{...tableProps}
|
||||||
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
54
packages/app/src/components/views/SortableTable/index.tsx
Normal file
54
packages/app/src/components/views/SortableTable/index.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import React from 'react';
|
||||||
|
// @ts-ignore
|
||||||
|
import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc';
|
||||||
|
import { MenuOutlined } from '@ant-design/icons';
|
||||||
|
import arrayMove from 'array-move';
|
||||||
|
import findIndex from 'lodash/findIndex';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
import './style.less';
|
||||||
|
|
||||||
|
export const SortableItem = sortableElement(props => <tr {...props} />);
|
||||||
|
export const SortableContainer = sortableContainer(props => <tbody {...props} />);
|
||||||
|
|
||||||
|
export const DragHandle = sortableHandle(() => (
|
||||||
|
<MenuOutlined style={{ cursor: 'pointer', color: '#999' }} />
|
||||||
|
));
|
||||||
|
|
||||||
|
export const components = ({data, mutate}) => {
|
||||||
|
return {
|
||||||
|
body: {
|
||||||
|
wrapper: props => (
|
||||||
|
<SortableContainer
|
||||||
|
useDragHandle
|
||||||
|
helperClass="row-dragging"
|
||||||
|
onSortEnd={({ oldIndex, newIndex }) => {
|
||||||
|
if (oldIndex !== newIndex) {
|
||||||
|
const newData = arrayMove([].concat(data), oldIndex, newIndex).filter(el => !!el);
|
||||||
|
mutate(newData);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
row: ({ className, style, ...restProps }) => {
|
||||||
|
// function findIndex base on Table rowKey props and should always be a right array index
|
||||||
|
const index = findIndex(data, (x: any) => x.id === restProps['data-row-key']);
|
||||||
|
return <SortableItem index={index} {...restProps} />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function fields2columns(fields) {
|
||||||
|
const columns: any[] = fields.map(field => {
|
||||||
|
const type = get(field, 'component.type');
|
||||||
|
if (type === 'sort') {
|
||||||
|
field.render = () => <DragHandle/>;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...field,
|
||||||
|
...(field.component||{}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return columns;
|
||||||
|
}
|
14
packages/app/src/components/views/SortableTable/style.less
Normal file
14
packages/app/src/components/views/SortableTable/style.less
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.row-dragging {
|
||||||
|
background: #fafafa;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
z-index: 99999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-dragging td {
|
||||||
|
padding: 16px;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-dragging .drag-visible {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Table as AntdTable, Card } from 'antd';
|
import { Table as AntdTable, Card } from 'antd';
|
||||||
import { redirectTo } from '@/components/pages/CollectionLoader/utils';
|
import { redirectTo } from '@/components/pages/CollectionLoader/utils';
|
||||||
import { Actions } from '@/components/actions';
|
import { Actions } from '@/components/actions';
|
||||||
import { request, useRequest } from 'umi';
|
import { request, useRequest } from 'umi';
|
||||||
import api from '@/api-client';
|
import api from '@/api-client';
|
||||||
|
import { components, fields2columns } from './SortableTable';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@ -40,28 +41,45 @@ export function Table(props: TableProps) {
|
|||||||
associatedName,
|
associatedName,
|
||||||
associatedKey,
|
associatedKey,
|
||||||
} = props;
|
} = props;
|
||||||
const { defaultTabId, fields, defaultTabName, rowKey = 'id', actions = [] } = schema;
|
const { fields, defaultTabName, rowKey = 'id', actions = [] } = schema;
|
||||||
const name = associatedName ? `${associatedName}.${resourceName}` : resourceName;
|
const name = associatedName ? `${associatedName}.${resourceName}` : resourceName;
|
||||||
const { data } = useRequest(() => api.resource(name).list({
|
const { data, mutate } = useRequest(() => api.resource(name).list({
|
||||||
associatedKey,
|
associatedKey,
|
||||||
}));
|
}));
|
||||||
const { sourceKey = 'id' } = activeTab.field||{};
|
const { sourceKey = 'id' } = activeTab.field||{};
|
||||||
console.log(data);
|
console.log(props);
|
||||||
console.log(activeTab);
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||||
|
const onChange = (selectedRowKeys: React.ReactText[]) => {
|
||||||
|
setSelectedRowKeys(selectedRowKeys);
|
||||||
|
}
|
||||||
|
const tableProps: any = {};
|
||||||
|
if (actions.length) {
|
||||||
|
tableProps.rowSelection = {
|
||||||
|
selectedRowKeys,
|
||||||
|
onChange,
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Card bordered={false}>
|
<Card bordered={false}>
|
||||||
<Actions {...props} style={{ marginBottom: 14 }} actions={actions}/>
|
<Actions {...props} style={{ marginBottom: 14 }} actions={actions}/>
|
||||||
<AntdTable dataSource={data} onRow={(data) => ({
|
<AntdTable
|
||||||
onClick: () => {
|
dataSource={data}
|
||||||
redirectTo({
|
rowKey={rowKey}
|
||||||
...props.match.params,
|
columns={fields2columns(fields)}
|
||||||
[activeTab ? 'newItem' : 'lastItem']: {
|
components={components({data, mutate})}
|
||||||
itemId: data[rowKey]||data.id,
|
onRow={(data) => ({
|
||||||
tabName: defaultTabName,
|
onClick: () => {
|
||||||
},
|
redirectTo({
|
||||||
});
|
...props.match.params,
|
||||||
},
|
[activeTab ? 'newItem' : 'lastItem']: {
|
||||||
})} columns={fields} />
|
itemId: data[rowKey]||data.id,
|
||||||
|
tabName: defaultTabName,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
{...tableProps}
|
||||||
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,38 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Card, Descriptions, Button } from 'antd';
|
|
||||||
import { Actions } from '@/components/actions';
|
|
||||||
|
|
||||||
export function Details(props: any) {
|
|
||||||
console.log(props);
|
|
||||||
const { actions = [], fields = [] } = props.schema;
|
|
||||||
return (
|
|
||||||
<Card bordered={false}>
|
|
||||||
<Actions {...props} style={{ marginBottom: 14 }} actions={actions}/>
|
|
||||||
<Descriptions bordered column={1}>
|
|
||||||
<Descriptions.Item label="Product">Cloud Database</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="Billing Mode">Prepaid</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="Automatic Renewal">YES</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="Order time">2018-04-24 18:00:00</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="Usage Time" >
|
|
||||||
2019-04-24 18:00:00
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="Negotiated Amount">$80.00</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="Discount">$20.00</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="Official Receipts">$60.00</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="Config Info">
|
|
||||||
Data disk type: MongoDB
|
|
||||||
<br />
|
|
||||||
Database version: 3.4
|
|
||||||
<br />
|
|
||||||
Package: dds.mongo.mid
|
|
||||||
<br />
|
|
||||||
Storage space: 10 GB
|
|
||||||
<br />
|
|
||||||
Replication factor: 3
|
|
||||||
<br />
|
|
||||||
Region: East China 1<br />
|
|
||||||
</Descriptions.Item>
|
|
||||||
</Descriptions>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,11 +1,12 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Form, DrawerForm } from './Form/index';
|
|
||||||
import { Table } from './Table';
|
|
||||||
import { Details } from './Details';
|
|
||||||
import { useRequest, request, Spin } from '@nocobase/client';
|
|
||||||
import { SimpleTable } from './SimpleTable';
|
|
||||||
import api from '@/api-client';
|
import api from '@/api-client';
|
||||||
|
import { useRequest } from 'umi';
|
||||||
|
import { Spin } from '@nocobase/client';
|
||||||
|
import { SimpleTable } from './SimpleTable';
|
||||||
|
import { Table } from './Table';
|
||||||
|
import { Form, DrawerForm } from './Form/index';
|
||||||
|
import { Details } from './Details';
|
||||||
|
|
||||||
const TEMPLATES = new Map<string, any>();
|
const TEMPLATES = new Map<string, any>();
|
||||||
|
|
||||||
@ -24,19 +25,37 @@ registerView('Table', Table);
|
|||||||
registerView('SimpleTable', SimpleTable);
|
registerView('SimpleTable', SimpleTable);
|
||||||
registerView('Details', Details);
|
registerView('Details', Details);
|
||||||
|
|
||||||
export default function ViewFactory(props) {
|
export interface ViewProps {
|
||||||
const { activeTab, viewCollectionName, viewName, reference } = props;
|
resourceName: string;
|
||||||
console.log({viewCollectionName, viewName});
|
resourceKey?: string | number;
|
||||||
const { data = {}, error, loading, run } = useRequest(() => api.resource(viewCollectionName).getView({
|
associatedName?: string;
|
||||||
resourceKey: viewName,
|
associatedKey?: string | number;
|
||||||
}), {
|
viewName?: string;
|
||||||
refreshDeps: [viewCollectionName, viewName],
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ViewFactory(props: ViewProps) {
|
||||||
|
const {
|
||||||
|
associatedName,
|
||||||
|
associatedKey,
|
||||||
|
resourceName,
|
||||||
|
viewName,
|
||||||
|
reference,
|
||||||
|
} = props;
|
||||||
|
const { data = {}, loading } = useRequest(() => {
|
||||||
|
const params = {
|
||||||
|
resourceKey: viewName,
|
||||||
|
associatedName: associatedName,
|
||||||
|
};
|
||||||
|
return api.resource(resourceName).getView(params);
|
||||||
|
}, {
|
||||||
|
refreshDeps: [associatedName, resourceName, viewName],
|
||||||
});
|
});
|
||||||
console.log(activeTab);
|
console.log(data);
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Spin/>;
|
return <Spin/>;
|
||||||
}
|
}
|
||||||
const { template } = data.data;
|
const { template } = data;
|
||||||
const Template = getViewTemplate(template);
|
const Template = getViewTemplate(template);
|
||||||
return Template && <Template {...props} ref={reference} schema={data.data}/>;
|
return Template && <Template {...props} ref={reference} schema={data}/>;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,38 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'umi';
|
import { Link } from 'umi';
|
||||||
|
import { Statistic, Card, Row, Col } from 'antd';
|
||||||
|
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
|
||||||
|
import { Page } from '@/components/pages';
|
||||||
|
|
||||||
export default (props: any) => {
|
export default (props: any) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Page title={'页面标题'}>
|
||||||
Page1
|
<Row gutter={16}>
|
||||||
<Link to={'/page2'}>Page2</Link>
|
<Col span={12}>
|
||||||
</div>
|
<Card>
|
||||||
|
<Statistic
|
||||||
|
title="Active"
|
||||||
|
value={11.28}
|
||||||
|
precision={2}
|
||||||
|
valueStyle={{ color: '#3f8600' }}
|
||||||
|
prefix={<ArrowUpOutlined />}
|
||||||
|
suffix="%"
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Card>
|
||||||
|
<Statistic
|
||||||
|
title="Idle"
|
||||||
|
value={9.3}
|
||||||
|
precision={2}
|
||||||
|
valueStyle={{ color: '#cf1322' }}
|
||||||
|
prefix={<ArrowDownOutlined />}
|
||||||
|
suffix="%"
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
@ -1,11 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'umi';
|
import { Link } from 'umi';
|
||||||
|
import { Page } from '@/components/pages';
|
||||||
|
|
||||||
export default (props: any) => {
|
export default (props: any) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Page title={'页面标题'}>
|
||||||
Page2
|
Page2
|
||||||
<Link to={'/page1'}>Page1</Link>
|
<Link to={'/page1'}>Page1</Link>
|
||||||
</div>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
@ -1,11 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'umi';
|
import { Link } from 'umi';
|
||||||
|
import { Page } from '@/components/pages';
|
||||||
|
|
||||||
export default (props: any) => {
|
export default (props: any) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Page title={'页面标题'}>
|
||||||
Page3
|
Page3
|
||||||
<Link to={'/page1'}>Page1</Link>
|
<Link to={'/page1'}>Page1</Link>
|
||||||
</div>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
@ -1,11 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'umi';
|
import { Link } from 'umi';
|
||||||
|
import { Page } from '@/components/pages';
|
||||||
|
|
||||||
export default (props: any) => {
|
export default (props: any) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Page title={'页面标题'}>
|
||||||
Page4
|
Page4
|
||||||
<Link to={'/page1'}>Page1</Link>
|
<Link to={'/page1'}>Page1</Link>
|
||||||
</div>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
@ -4,10 +4,24 @@ export default {
|
|||||||
name: 'actions',
|
name: 'actions',
|
||||||
title: '操作配置',
|
title: '操作配置',
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'integer',
|
||||||
|
name: 'sort',
|
||||||
|
title: '排序',
|
||||||
|
component: {
|
||||||
|
type: 'sort',
|
||||||
|
className: 'drag-visible',
|
||||||
|
width: 60,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'type',
|
name: 'type',
|
||||||
title: '类型',
|
title: '类型',
|
||||||
|
component: {
|
||||||
|
type: 'string',
|
||||||
|
className: 'drag-visible',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -6,6 +6,16 @@ export default {
|
|||||||
title: '数据表配置',
|
title: '数据表配置',
|
||||||
model: CollectionModel,
|
model: CollectionModel,
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'integer',
|
||||||
|
name: 'sort',
|
||||||
|
title: '排序',
|
||||||
|
component: {
|
||||||
|
type: 'sort',
|
||||||
|
className: 'drag-visible',
|
||||||
|
width: 60,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'title',
|
name: 'title',
|
||||||
@ -14,6 +24,7 @@ export default {
|
|||||||
required: true,
|
required: true,
|
||||||
component: {
|
component: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
className: 'drag-visible',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -48,21 +59,41 @@ export default {
|
|||||||
type: 'hasMany',
|
type: 'hasMany',
|
||||||
name: 'fields',
|
name: 'fields',
|
||||||
sourceKey: 'name',
|
sourceKey: 'name',
|
||||||
|
actions: {
|
||||||
|
list: {
|
||||||
|
sort: 'sort',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'hasMany',
|
type: 'hasMany',
|
||||||
name: 'actions',
|
name: 'actions',
|
||||||
sourceKey: 'name',
|
sourceKey: 'name',
|
||||||
|
actions: {
|
||||||
|
list: {
|
||||||
|
sort: 'sort',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'hasMany',
|
type: 'hasMany',
|
||||||
name: 'tabs',
|
name: 'tabs',
|
||||||
sourceKey: 'name',
|
sourceKey: 'name',
|
||||||
|
actions: {
|
||||||
|
list: {
|
||||||
|
sort: 'sort',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'hasMany',
|
type: 'hasMany',
|
||||||
name: 'views',
|
name: 'views',
|
||||||
sourceKey: 'name',
|
sourceKey: 'name',
|
||||||
|
actions: {
|
||||||
|
list: {
|
||||||
|
sort: 'sort',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
|
@ -4,10 +4,24 @@ export default {
|
|||||||
name: 'fields',
|
name: 'fields',
|
||||||
title: '字段配置',
|
title: '字段配置',
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'integer',
|
||||||
|
name: 'sort',
|
||||||
|
title: '排序',
|
||||||
|
component: {
|
||||||
|
type: 'sort',
|
||||||
|
className: 'drag-visible',
|
||||||
|
width: 60,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'type',
|
name: 'type',
|
||||||
title: '类型',
|
title: '类型',
|
||||||
|
component: {
|
||||||
|
type: 'string',
|
||||||
|
className: 'drag-visible',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
@ -19,6 +33,16 @@ export default {
|
|||||||
name: 'title',
|
name: 'title',
|
||||||
title: '名称',
|
title: '名称',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'boolean',
|
||||||
|
name: 'showInListAction',
|
||||||
|
title: '显示在表格里',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'boolean',
|
||||||
|
name: 'showInGetAction',
|
||||||
|
title: '显示在详情里',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'json',
|
type: 'json',
|
||||||
name: 'options',
|
name: 'options',
|
||||||
@ -26,6 +50,7 @@ export default {
|
|||||||
{
|
{
|
||||||
type: 'belongsTo',
|
type: 'belongsTo',
|
||||||
name: 'collection',
|
name: 'collection',
|
||||||
|
title: '所属数据表',
|
||||||
target: 'collections',
|
target: 'collections',
|
||||||
targetKey: 'name',
|
targetKey: 'name',
|
||||||
},
|
},
|
||||||
|
@ -4,10 +4,24 @@ export default {
|
|||||||
name: 'tabs',
|
name: 'tabs',
|
||||||
title: '标签配置',
|
title: '标签配置',
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'integer',
|
||||||
|
name: 'sort',
|
||||||
|
title: '排序',
|
||||||
|
component: {
|
||||||
|
type: 'sort',
|
||||||
|
className: 'drag-visible',
|
||||||
|
width: 60,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'type',
|
name: 'type',
|
||||||
title: '类型',
|
title: '类型',
|
||||||
|
component: {
|
||||||
|
type: 'string',
|
||||||
|
className: 'drag-visible',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -4,10 +4,24 @@ export default {
|
|||||||
name: 'views',
|
name: 'views',
|
||||||
title: '视图配置',
|
title: '视图配置',
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'integer',
|
||||||
|
name: 'sort',
|
||||||
|
title: '排序',
|
||||||
|
component: {
|
||||||
|
type: 'sort',
|
||||||
|
className: 'drag-visible',
|
||||||
|
width: 60,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'type',
|
name: 'type',
|
||||||
title: '类型',
|
title: '类型',
|
||||||
|
component: {
|
||||||
|
type: 'string',
|
||||||
|
className: 'drag-visible',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -20,7 +20,9 @@ export default async (ctx, next) => {
|
|||||||
});
|
});
|
||||||
collection.setDataValue('defaultViewId', get(views, [0, 'id']));
|
collection.setDataValue('defaultViewId', get(views, [0, 'id']));
|
||||||
collection.setDataValue('defaultViewName', get(views, [0, 'name']));
|
collection.setDataValue('defaultViewName', get(views, [0, 'name']));
|
||||||
const tabs = await collection.getTabs() as Model[];
|
const tabs = await collection.getTabs({
|
||||||
|
order: [['sort', 'asc']],
|
||||||
|
}) as Model[];
|
||||||
const tabItems = [];
|
const tabItems = [];
|
||||||
for (const tab of tabs) {
|
for (const tab of tabs) {
|
||||||
const itemTab = {
|
const itemTab = {
|
||||||
|
@ -34,7 +34,9 @@ function pages2routes(pages: Array<any>) {
|
|||||||
export default async function getRoutes(ctx, next) {
|
export default async function getRoutes(ctx, next) {
|
||||||
const database: Database = ctx.database;
|
const database: Database = ctx.database;
|
||||||
const Page = database.getModel('pages');
|
const Page = database.getModel('pages');
|
||||||
let pages = await Page.findAll({});
|
let pages = await Page.findAll({
|
||||||
|
order: [['sort', 'asc']],
|
||||||
|
});
|
||||||
const data = flatToTree(pages.map(row => row.toJSON()), {
|
const data = flatToTree(pages.map(row => row.toJSON()), {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
parentId: 'parent_id',
|
parentId: 'parent_id',
|
||||||
|
@ -15,15 +15,14 @@ const transforms = {
|
|||||||
return arr;
|
return arr;
|
||||||
},
|
},
|
||||||
form: async (fields: Model[]) => {
|
form: async (fields: Model[]) => {
|
||||||
const arr = [];
|
const schema = {};
|
||||||
for (const field of fields) {
|
for (const field of fields) {
|
||||||
arr.push({
|
schema[field.name] = {
|
||||||
...field.toJSON(),
|
type: 'string',
|
||||||
...field.options,
|
title: field.title||field.name,
|
||||||
dataIndex: field.name,
|
};
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return arr;
|
return schema;
|
||||||
},
|
},
|
||||||
details: async (fields: Model[]) => {
|
details: async (fields: Model[]) => {
|
||||||
const arr = [];
|
const arr = [];
|
||||||
@ -50,8 +49,16 @@ export default async (ctx, next) => {
|
|||||||
// },
|
// },
|
||||||
}));
|
}));
|
||||||
const collection = await view.getCollection();
|
const collection = await view.getCollection();
|
||||||
const fields = await collection.getFields();
|
const fields = await collection.getFields({
|
||||||
const actions = await collection.getActions();
|
order: [
|
||||||
|
['sort', 'asc'],
|
||||||
|
]
|
||||||
|
});
|
||||||
|
const actions = await collection.getActions({
|
||||||
|
order: [
|
||||||
|
['sort', 'asc'],
|
||||||
|
]
|
||||||
|
});
|
||||||
const actionNames = view.options.actionNames||[];
|
const actionNames = view.options.actionNames||[];
|
||||||
console.log(view.options);
|
console.log(view.options);
|
||||||
if (view.type === 'table') {
|
if (view.type === 'table') {
|
||||||
|
@ -6,10 +6,12 @@ export default {
|
|||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
name: 'parent_id',
|
name: 'sort',
|
||||||
title: '父级页面',
|
title: '排序',
|
||||||
component: {
|
component: {
|
||||||
type: 'number',
|
type: 'sort',
|
||||||
|
className: 'drag-visible',
|
||||||
|
width: 60,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -20,6 +22,15 @@ export default {
|
|||||||
isMainTitle: true,
|
isMainTitle: true,
|
||||||
component: {
|
component: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
className: 'drag-visible',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'integer',
|
||||||
|
name: 'parent_id',
|
||||||
|
title: '父级页面',
|
||||||
|
component: {
|
||||||
|
type: 'number',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -140,10 +151,6 @@ export default {
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'integer',
|
|
||||||
name: 'order',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'hasMany',
|
type: 'hasMany',
|
||||||
name: 'children',
|
name: 'children',
|
||||||
|
@ -7,6 +7,7 @@ export default {
|
|||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'username',
|
name: 'username',
|
||||||
|
title: '用户名',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
|
@ -44,6 +44,7 @@ export function middleware(options: MiddlewareOptions = {}) {
|
|||||||
const field = table.getField(names[0]) as BelongsTo | HasMany | BelongsToMany | HasOne;
|
const field = table.getField(names[0]) as BelongsTo | HasMany | BelongsToMany | HasOne;
|
||||||
if (names.length == 0 || field) {
|
if (names.length == 0 || field) {
|
||||||
let resourceType = 'single';
|
let resourceType = 'single';
|
||||||
|
let actions = {};
|
||||||
if (field) {
|
if (field) {
|
||||||
if (field instanceof HasOne) {
|
if (field instanceof HasOne) {
|
||||||
resourceType = 'hasOne';
|
resourceType = 'hasOne';
|
||||||
@ -54,10 +55,14 @@ export function middleware(options: MiddlewareOptions = {}) {
|
|||||||
} else if (field instanceof BelongsToMany) {
|
} else if (field instanceof BelongsToMany) {
|
||||||
resourceType = 'belongsToMany';
|
resourceType = 'belongsToMany';
|
||||||
}
|
}
|
||||||
|
if (field.options.actions) {
|
||||||
|
actions = field.options.actions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
resourcer.define({
|
resourcer.define({
|
||||||
type: resourceType as any,
|
type: resourceType as any,
|
||||||
name: resourceName,
|
name: resourceName,
|
||||||
|
actions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user