updates
This commit is contained in:
parent
06fbd8c51b
commit
8bfb5cf85c
2
.github/workflows/vercel.yml
vendored
2
.github/workflows/vercel.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
|
||||
- run: npm install
|
||||
- run: npm run bootstrap
|
||||
- run: echo "API_URL=${{secrets.API_URL}}" >> .env
|
||||
- run: echo "API_URL=${{ secrets.API_URL }}" >> .env
|
||||
- run: npm run build-docs
|
||||
|
||||
- name: Deploy
|
||||
|
@ -43,7 +43,7 @@ const plugins = [
|
||||
'@nocobase/plugin-ui-schema',
|
||||
// '@nocobase/plugin-action-logs',
|
||||
// '@nocobase/plugin-pages',
|
||||
// '@nocobase/plugin-users',
|
||||
'@nocobase/plugin-users',
|
||||
// '@nocobase/plugin-file-manager',
|
||||
// '@nocobase/plugin-permissions',
|
||||
// '@nocobase/plugin-automations',
|
||||
|
@ -17,6 +17,11 @@ import * as uiSchema from './ui-schema';
|
||||
// tables: ['collections', 'fields', 'actions', 'views', 'tabs'],
|
||||
});
|
||||
|
||||
const config = require('@nocobase/plugin-users/src/collections/users').default;
|
||||
const Collection = database.getModel('collections');
|
||||
const collection = await Collection.create(config);
|
||||
await collection.updateAssociations(config);
|
||||
|
||||
const Route = database.getModel('routes');
|
||||
|
||||
const data = [
|
||||
|
0
packages/app/src/app.ts
Normal file
0
packages/app/src/app.ts
Normal file
@ -1,10 +1,8 @@
|
||||
import 'antd/dist/antd.css'
|
||||
import 'antd/dist/antd.css';
|
||||
import { useRequest } from 'ahooks';
|
||||
import { Spin } from 'antd';
|
||||
import React, { useMemo } from 'react';
|
||||
import {
|
||||
MemoryRouter as Router,
|
||||
} from 'react-router-dom';
|
||||
import { MemoryRouter as Router } from 'react-router-dom';
|
||||
import {
|
||||
createRouteSwitch,
|
||||
AdminLayout,
|
||||
@ -19,8 +17,6 @@ const request = extend({
|
||||
timeout: 1000,
|
||||
});
|
||||
|
||||
// console.log = () => {}
|
||||
|
||||
const RouteSwitch = createRouteSwitch({
|
||||
components: {
|
||||
AdminLayout,
|
||||
@ -35,7 +31,7 @@ const App = () => {
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return <Spin/>
|
||||
return <Spin />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -1,8 +1,29 @@
|
||||
import { SchemaRenderer } from '../../';
|
||||
import React from 'react';
|
||||
import { FormItem } from '@formily/antd';
|
||||
import { useCollectionContext } from '../../schemas';
|
||||
import { action } from '@formily/reactive';
|
||||
|
||||
const useAsyncDataSource = (service: any) => (field: any) => {
|
||||
field.loading = true;
|
||||
service(field).then(
|
||||
action((data: any) => {
|
||||
field.dataSource = data;
|
||||
field.loading = false;
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
export default () => {
|
||||
const { data, loading } = useCollectionContext();
|
||||
|
||||
const loadCollections = async (field: any) => {
|
||||
return data.map((item: any) => ({
|
||||
label: item.title,
|
||||
value: item.name,
|
||||
}));
|
||||
};
|
||||
|
||||
const schema = {
|
||||
type: 'array',
|
||||
name: 'collections',
|
||||
@ -33,5 +54,11 @@ export default () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
return <SchemaRenderer components={{ FormItem }} schema={schema} />;
|
||||
return (
|
||||
<SchemaRenderer
|
||||
scope={{ loadCollections, useAsyncDataSource }}
|
||||
components={{ FormItem }}
|
||||
schema={schema}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -29,7 +29,7 @@ import { useRequest } from 'ahooks';
|
||||
import './style.less';
|
||||
|
||||
import { uid } from '@formily/shared';
|
||||
import { ISchema } from '@formily/react';
|
||||
import { ISchema, Schema } from '@formily/react';
|
||||
import Database from './datatable';
|
||||
import { HighlightOutlined } from '@ant-design/icons';
|
||||
import { useCookieState } from 'ahooks';
|
||||
@ -51,14 +51,20 @@ function DesignableToggle() {
|
||||
);
|
||||
}
|
||||
|
||||
function LayoutWithMenu({ schema }) {
|
||||
interface LayoutWithMenuProps {
|
||||
schema: Schema;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
function LayoutWithMenu(props: LayoutWithMenuProps) {
|
||||
const { schema, defaultSelectedKeys } = props;
|
||||
const match = useRouteMatch<any>();
|
||||
const location = useLocation();
|
||||
const sideMenuRef = useRef();
|
||||
const history = useHistory();
|
||||
const [activeKey, setActiveKey] = useState(match.params.name);
|
||||
const [, setPageTitle] = usePageTitleContext();
|
||||
const onSelect = (info) => {
|
||||
console.log('LayoutWithMenu', info);
|
||||
if (!info.schema) {
|
||||
setActiveKey(null);
|
||||
} else if (info.schema['x-component'] === 'Menu.SubMenu') {
|
||||
@ -66,12 +72,13 @@ function LayoutWithMenu({ schema }) {
|
||||
setActiveKey(null);
|
||||
} else {
|
||||
setActiveKey(info.schema.key);
|
||||
history.push(`/admin/${info.schema.key}`);
|
||||
if (info.schema.title) {
|
||||
setPageTitle(info.schema.title);
|
||||
}
|
||||
}
|
||||
};
|
||||
console.log({ match });
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Layout.Header style={{ display: 'flex' }}>
|
||||
@ -80,7 +87,7 @@ function LayoutWithMenu({ schema }) {
|
||||
scope={{
|
||||
sideMenuRef,
|
||||
onSelect,
|
||||
selectedKeys: [activeKey].filter(Boolean),
|
||||
selectedKeys: defaultSelectedKeys.filter(Boolean),
|
||||
}}
|
||||
/>
|
||||
<Database />
|
||||
@ -124,16 +131,42 @@ export function AdminLayout({ route }: any) {
|
||||
formatResult: (result) => result?.data,
|
||||
},
|
||||
);
|
||||
const match = useRouteMatch<any>();
|
||||
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
|
||||
const findProperties = (schema: Schema): Schema[] => {
|
||||
if (!schema) {
|
||||
return [];
|
||||
}
|
||||
return schema.reduceProperties((items, current) => {
|
||||
if (current['key'] == match.params.name) {
|
||||
return [...items, current];
|
||||
}
|
||||
return [...items, ...findProperties(current)];
|
||||
}, []);
|
||||
}
|
||||
const current = findProperties(new Schema(data)).shift();
|
||||
const defaultSelectedKeys = [current?.name];
|
||||
let parent = current?.parent;
|
||||
while(parent) {
|
||||
if (parent['x-component'] === 'Menu') {
|
||||
break;
|
||||
}
|
||||
defaultSelectedKeys.unshift(parent.name);
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
console.log('current?.title', current, current?.title, defaultSelectedKeys);
|
||||
|
||||
return (
|
||||
<SwithDesignableContextProvider>
|
||||
<CollectionContextProvider>
|
||||
<PageTitleContextProvider>
|
||||
<LayoutWithMenu schema={data} />
|
||||
{/* @ts-ignore */}
|
||||
<PageTitleContextProvider defaultPageTitle={current?.title}>
|
||||
<LayoutWithMenu defaultSelectedKeys={defaultSelectedKeys} current={current} schema={data} />
|
||||
</PageTitleContextProvider>
|
||||
</CollectionContextProvider>
|
||||
</SwithDesignableContextProvider>
|
||||
|
@ -51,7 +51,7 @@ import { CardItem } from '../../schemas/card-item';
|
||||
import { DragAndDrop } from '../../schemas/drag-and-drop';
|
||||
import { TreeSelect } from '../../schemas/tree-select';
|
||||
import { Page } from '../../schemas/page';
|
||||
import { Chart } from '../../schemas/chart';
|
||||
// import { Chart } from '../../schemas/chart';
|
||||
import { useCollectionContext, useSwithDesignableContext } from '../../schemas';
|
||||
|
||||
export const BlockContext = createContext({ dragRef: null });
|
||||
@ -72,7 +72,7 @@ export const SchemaField = createSchemaField({
|
||||
Div,
|
||||
Space,
|
||||
Page,
|
||||
Chart,
|
||||
// Chart,
|
||||
|
||||
ArrayCollapse,
|
||||
ArrayTable,
|
||||
|
@ -882,8 +882,9 @@ AddNew.PaneItem = observer((props: any) => {
|
||||
placement={'bottomCenter'}
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.SubMenu title={'新建卡片'}>
|
||||
<Menu.ItemGroup title={'数据区块'}>
|
||||
<Menu.Item
|
||||
icon={<IconPicker type={'FileOutlined'} />}
|
||||
onClick={async () => {
|
||||
let data: ISchema = {
|
||||
type: 'void',
|
||||
@ -927,9 +928,10 @@ AddNew.PaneItem = observer((props: any) => {
|
||||
}}
|
||||
style={{ minWidth: 150 }}
|
||||
>
|
||||
阅读模式
|
||||
详情
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
icon={<IconPicker type={'FormOutlined'} />}
|
||||
onClick={async () => {
|
||||
let data: ISchema = {
|
||||
type: 'void',
|
||||
@ -972,47 +974,50 @@ AddNew.PaneItem = observer((props: any) => {
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
编辑模式
|
||||
表单
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu title={'要展示的相关数据'}>
|
||||
<Menu.Item style={{ minWidth: 150 }}>日志</Menu.Item>
|
||||
<Menu.Item>评论</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.Item
|
||||
onClick={async () => {
|
||||
let data: ISchema = {
|
||||
key: uid(),
|
||||
type: 'void',
|
||||
default: '这是一段演示文字,**支持使用 Markdown 语法**',
|
||||
'x-designable-bar': 'Markdown.Void.DesignableBar',
|
||||
'x-decorator': 'CardItem',
|
||||
'x-read-pretty': true,
|
||||
'x-component': 'Markdown.Void',
|
||||
};
|
||||
if (isGridBlock(schema)) {
|
||||
path.pop();
|
||||
path.pop();
|
||||
data = generateGridBlock(data);
|
||||
} else if (isGrid(schema)) {
|
||||
data = generateGridBlock(data);
|
||||
}
|
||||
if (data) {
|
||||
let s;
|
||||
if (isGrid(schema)) {
|
||||
s = appendChild(data, [...path]);
|
||||
} else if (defaultAction === 'insertAfter') {
|
||||
s = insertAfter(data, [...path]);
|
||||
} else {
|
||||
s = insertBefore(data, [...path]);
|
||||
</Menu.ItemGroup>
|
||||
<Menu.ItemGroup title={'相关数据区块'}>
|
||||
<Menu.Item style={{ minWidth: 150 }} icon={<IconPicker type={'HistoryOutlined'} />}>日志</Menu.Item>
|
||||
<Menu.Item icon={<IconPicker type={'CommentOutlined'} />}>评论</Menu.Item>
|
||||
</Menu.ItemGroup>
|
||||
<Menu.ItemGroup title={'多媒体区块'}>
|
||||
<Menu.Item
|
||||
icon={<IconPicker type={'FileMarkdownOutlined'} />}
|
||||
onClick={async () => {
|
||||
let data: ISchema = {
|
||||
key: uid(),
|
||||
type: 'void',
|
||||
default: '这是一段演示文字,**支持使用 Markdown 语法**',
|
||||
'x-designable-bar': 'Markdown.Void.DesignableBar',
|
||||
'x-decorator': 'CardItem',
|
||||
'x-read-pretty': true,
|
||||
'x-component': 'Markdown.Void',
|
||||
};
|
||||
if (isGridBlock(schema)) {
|
||||
path.pop();
|
||||
path.pop();
|
||||
data = generateGridBlock(data);
|
||||
} else if (isGrid(schema)) {
|
||||
data = generateGridBlock(data);
|
||||
}
|
||||
await createSchema(s);
|
||||
}
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
添加说明文字
|
||||
</Menu.Item>
|
||||
if (data) {
|
||||
let s;
|
||||
if (isGrid(schema)) {
|
||||
s = appendChild(data, [...path]);
|
||||
} else if (defaultAction === 'insertAfter') {
|
||||
s = insertAfter(data, [...path]);
|
||||
} else {
|
||||
s = insertBefore(data, [...path]);
|
||||
}
|
||||
await createSchema(s);
|
||||
}
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
Markdown
|
||||
</Menu.Item>
|
||||
</Menu.ItemGroup>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
|
@ -1,111 +1,111 @@
|
||||
import React, { useContext } from 'react';
|
||||
import {
|
||||
Column,
|
||||
ColumnConfig,
|
||||
Line,
|
||||
LineConfig,
|
||||
Pie,
|
||||
PieConfig,
|
||||
Bar,
|
||||
BarConfig,
|
||||
} from '@ant-design/charts';
|
||||
import {
|
||||
connect,
|
||||
mapProps,
|
||||
observer,
|
||||
useField,
|
||||
useFieldSchema,
|
||||
mapReadPretty,
|
||||
} from '@formily/react';
|
||||
import { Button, Dropdown, Input as AntdInput, Menu, Space } from 'antd';
|
||||
import { InputProps, TextAreaProps } from 'antd/lib/input';
|
||||
import { Display } from '../display';
|
||||
import { LoadingOutlined, MenuOutlined, DragOutlined } from '@ant-design/icons';
|
||||
import micromark from 'micromark';
|
||||
import { useDesignable } from '../../components/schema-renderer';
|
||||
import { useState } from 'react';
|
||||
import AddNew from '../add-new';
|
||||
import cls from 'classnames';
|
||||
import { DraggableBlockContext } from '../../components/drag-and-drop';
|
||||
import { uid } from '@formily/shared';
|
||||
import { removeSchema, updateSchema } from '..';
|
||||
import { isGridRowOrCol } from '../grid';
|
||||
// import React, { useContext } from 'react';
|
||||
// import {
|
||||
// Column,
|
||||
// ColumnConfig,
|
||||
// Line,
|
||||
// LineConfig,
|
||||
// Pie,
|
||||
// PieConfig,
|
||||
// Bar,
|
||||
// BarConfig,
|
||||
// } from '@ant-design/charts';
|
||||
// import {
|
||||
// connect,
|
||||
// mapProps,
|
||||
// observer,
|
||||
// useField,
|
||||
// useFieldSchema,
|
||||
// mapReadPretty,
|
||||
// } from '@formily/react';
|
||||
// import { Button, Dropdown, Input as AntdInput, Menu, Space } from 'antd';
|
||||
// import { InputProps, TextAreaProps } from 'antd/lib/input';
|
||||
// import { Display } from '../display';
|
||||
// import { LoadingOutlined, MenuOutlined, DragOutlined } from '@ant-design/icons';
|
||||
// import micromark from 'micromark';
|
||||
// import { useDesignable } from '../../components/schema-renderer';
|
||||
// import { useState } from 'react';
|
||||
// import AddNew from '../add-new';
|
||||
// import cls from 'classnames';
|
||||
// import { DraggableBlockContext } from '../../components/drag-and-drop';
|
||||
// import { uid } from '@formily/shared';
|
||||
// import { removeSchema, updateSchema } from '..';
|
||||
// import { isGridRowOrCol } from '../grid';
|
||||
|
||||
export const Chart: any = {};
|
||||
// export const Chart: any = {};
|
||||
|
||||
Chart.Column = observer((props: any) => {
|
||||
return <Column {...props.config} />;
|
||||
});
|
||||
// Chart.Column = observer((props: any) => {
|
||||
// return <Column {...props.config} />;
|
||||
// });
|
||||
|
||||
Chart.Line = observer((props: any) => {
|
||||
return <Line {...props.config} />;
|
||||
});
|
||||
// Chart.Line = observer((props: any) => {
|
||||
// return <Line {...props.config} />;
|
||||
// });
|
||||
|
||||
Chart.Pie = observer((props: any) => {
|
||||
return <Pie {...props.config} />;
|
||||
});
|
||||
// Chart.Pie = observer((props: any) => {
|
||||
// return <Pie {...props.config} />;
|
||||
// });
|
||||
|
||||
Chart.Bar = observer((props: any) => {
|
||||
return <Bar {...props.config} />;
|
||||
});
|
||||
// Chart.Bar = observer((props: any) => {
|
||||
// return <Bar {...props.config} />;
|
||||
// });
|
||||
|
||||
Chart.DesignableBar = observer((props) => {
|
||||
const field = useField();
|
||||
const { designable, schema, refresh, deepRemove } = useDesignable();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { dragRef } = useContext(DraggableBlockContext);
|
||||
if (!designable) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className={cls('designable-bar', { active: visible })}>
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
className={cls('designable-bar-actions', { active: visible })}
|
||||
>
|
||||
<Space size={'small'}>
|
||||
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
|
||||
{dragRef && <DragOutlined ref={dragRef} />}
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
visible={visible}
|
||||
onVisibleChange={(visible) => {
|
||||
setVisible(visible);
|
||||
}}
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
key={'update'}
|
||||
onClick={() => {
|
||||
field.readPretty = false;
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
修改文本段
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
key={'delete'}
|
||||
onClick={async () => {
|
||||
const removed = deepRemove();
|
||||
// console.log({ removed })
|
||||
const last = removed.pop();
|
||||
if (isGridRowOrCol(last)) {
|
||||
await removeSchema(last);
|
||||
}
|
||||
}}
|
||||
>
|
||||
删除当前文本
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<MenuOutlined />
|
||||
</Dropdown>
|
||||
</Space>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
// Chart.DesignableBar = observer((props) => {
|
||||
// const field = useField();
|
||||
// const { designable, schema, refresh, deepRemove } = useDesignable();
|
||||
// const [visible, setVisible] = useState(false);
|
||||
// const { dragRef } = useContext(DraggableBlockContext);
|
||||
// if (!designable) {
|
||||
// return null;
|
||||
// }
|
||||
// return (
|
||||
// <div className={cls('designable-bar', { active: visible })}>
|
||||
// <span
|
||||
// onClick={(e) => {
|
||||
// e.stopPropagation();
|
||||
// }}
|
||||
// className={cls('designable-bar-actions', { active: visible })}
|
||||
// >
|
||||
// <Space size={'small'}>
|
||||
// <AddNew.CardItem defaultAction={'insertAfter'} ghost />
|
||||
// {dragRef && <DragOutlined ref={dragRef} />}
|
||||
// <Dropdown
|
||||
// trigger={['click']}
|
||||
// visible={visible}
|
||||
// onVisibleChange={(visible) => {
|
||||
// setVisible(visible);
|
||||
// }}
|
||||
// overlay={
|
||||
// <Menu>
|
||||
// <Menu.Item
|
||||
// key={'update'}
|
||||
// onClick={() => {
|
||||
// field.readPretty = false;
|
||||
// setVisible(false);
|
||||
// }}
|
||||
// >
|
||||
// 修改文本段
|
||||
// </Menu.Item>
|
||||
// <Menu.Divider />
|
||||
// <Menu.Item
|
||||
// key={'delete'}
|
||||
// onClick={async () => {
|
||||
// const removed = deepRemove();
|
||||
// // console.log({ removed })
|
||||
// const last = removed.pop();
|
||||
// if (isGridRowOrCol(last)) {
|
||||
// await removeSchema(last);
|
||||
// }
|
||||
// }}
|
||||
// >
|
||||
// 删除当前文本
|
||||
// </Menu.Item>
|
||||
// </Menu>
|
||||
// }
|
||||
// >
|
||||
// <MenuOutlined />
|
||||
// </Dropdown>
|
||||
// </Space>
|
||||
// </span>
|
||||
// </div>
|
||||
// );
|
||||
// });
|
@ -26,7 +26,7 @@ import {
|
||||
message,
|
||||
Spin,
|
||||
} from 'antd';
|
||||
import { options, interfaces } from './interfaces';
|
||||
import { options, interfaces, getDefaultFields } from './interfaces';
|
||||
import {
|
||||
DeleteOutlined,
|
||||
DatabaseOutlined,
|
||||
@ -53,16 +53,21 @@ export const DatabaseCollection = observer((props) => {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const form = useForm();
|
||||
const [newValue, setNewValue] = useState('');
|
||||
const { refresh } = useCollectionContext();
|
||||
const { loading, refresh, data } = useCollectionContext();
|
||||
|
||||
const { run, loading } = useRequest('collections:findAll', {
|
||||
formatResult: (result) => result?.data,
|
||||
onSuccess(data) {
|
||||
field.setValue(data);
|
||||
console.log('onSuccess', data);
|
||||
},
|
||||
manual: true,
|
||||
});
|
||||
useEffect(() => {
|
||||
field.setValue(data);
|
||||
console.log('onSuccess', data);
|
||||
}, [data]);
|
||||
|
||||
// const { run, loading } = useRequest('collections:findAll', {
|
||||
// formatResult: (result) => result?.data,
|
||||
// onSuccess(data) {
|
||||
// // field.setValue(data);
|
||||
// // console.log('onSuccess', data);
|
||||
// },
|
||||
// manual: true,
|
||||
// });
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -74,12 +79,12 @@ export const DatabaseCollection = observer((props) => {
|
||||
}}
|
||||
onClick={async () => {
|
||||
setVisible(true);
|
||||
await run();
|
||||
// await run();
|
||||
if (field.value?.length === 0) {
|
||||
field.push({
|
||||
name: `t_${uid()}`,
|
||||
unsaved: true,
|
||||
fields: [],
|
||||
fields: getDefaultFields(),
|
||||
});
|
||||
}
|
||||
}}
|
||||
@ -127,7 +132,7 @@ export const DatabaseCollection = observer((props) => {
|
||||
const data = {
|
||||
name: `t_${uid()}`,
|
||||
title: value,
|
||||
fields: [],
|
||||
fields: getDefaultFields(),
|
||||
};
|
||||
field.push(data);
|
||||
setActiveIndex(field.value.length - 1);
|
||||
@ -160,28 +165,30 @@ export const DatabaseCollection = observer((props) => {
|
||||
>
|
||||
{item.title || '未命名'}{' '}
|
||||
{item.unsaved ? '(未保存)' : ''}
|
||||
<DeleteOutlined
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
field.remove(index);
|
||||
if (field.value?.length === 0) {
|
||||
field.push({
|
||||
name: `t_${uid()}`,
|
||||
unsaved: true,
|
||||
fields: [],
|
||||
});
|
||||
}
|
||||
if (activeIndex === index) {
|
||||
setActiveIndex(0);
|
||||
} else if (activeIndex > index) {
|
||||
setActiveIndex(activeIndex - 1);
|
||||
}
|
||||
if (item.name) {
|
||||
await deleteCollection(item.name);
|
||||
await refresh();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{item.privilege !== 'undelete' && (
|
||||
<DeleteOutlined
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
field.remove(index);
|
||||
if (field.value?.length === 0) {
|
||||
field.push({
|
||||
name: `t_${uid()}`,
|
||||
unsaved: true,
|
||||
fields: getDefaultFields(),
|
||||
});
|
||||
}
|
||||
if (activeIndex === index) {
|
||||
setActiveIndex(0);
|
||||
} else if (activeIndex > index) {
|
||||
setActiveIndex(activeIndex - 1);
|
||||
}
|
||||
if (item.name) {
|
||||
await deleteCollection(item.name);
|
||||
await refresh();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Select.Option>
|
||||
);
|
||||
@ -206,24 +213,20 @@ export const DatabaseCollection = observer((props) => {
|
||||
} catch (error) {}
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<Spin />
|
||||
) : (
|
||||
<FormLayout layout={'vertical'}>
|
||||
<RecursionField
|
||||
name={activeIndex}
|
||||
schema={
|
||||
new Schema({
|
||||
type: 'object',
|
||||
properties: schema.properties,
|
||||
})
|
||||
}
|
||||
/>
|
||||
{/* <FormConsumer>
|
||||
<FormLayout layout={'vertical'}>
|
||||
<RecursionField
|
||||
name={activeIndex}
|
||||
schema={
|
||||
new Schema({
|
||||
type: 'object',
|
||||
properties: schema.properties,
|
||||
})
|
||||
}
|
||||
/>
|
||||
{/* <FormConsumer>
|
||||
{(form) => <pre>{JSON.stringify(form.values, null, 2)}</pre>}
|
||||
</FormConsumer> */}
|
||||
</FormLayout>
|
||||
)}
|
||||
</FormLayout>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
@ -237,7 +240,6 @@ export const DatabaseField: any = observer((props) => {
|
||||
}
|
||||
}, []);
|
||||
const [activeKey, setActiveKey] = useState(null);
|
||||
console.log('DatabaseField', field);
|
||||
return (
|
||||
<div>
|
||||
<Collapse
|
||||
@ -249,13 +251,19 @@ export const DatabaseField: any = observer((props) => {
|
||||
accordion
|
||||
>
|
||||
{field.value?.map((item, index) => {
|
||||
if (!item.interface) {
|
||||
return;
|
||||
}
|
||||
const schema = cloneDeep(interfaces.get(item.interface));
|
||||
if (!schema) {
|
||||
console.error('schema invalid');
|
||||
return;
|
||||
}
|
||||
const path = field.address.concat(index);
|
||||
const errors = field.form.queryFeedbacks({
|
||||
type: 'error',
|
||||
address: `*(${path},${path}.*)`,
|
||||
});
|
||||
console.log('item.key', item.key);
|
||||
return (
|
||||
<Collapse.Panel
|
||||
header={
|
||||
@ -263,22 +271,30 @@ export const DatabaseField: any = observer((props) => {
|
||||
{(item.uiSchema && item.uiSchema.title) || (
|
||||
<i style={{ color: 'rgba(0, 0, 0, 0.25)' }}>未命名</i>
|
||||
)}{' '}
|
||||
<Tag>{schema.title}</Tag>
|
||||
<Tag
|
||||
className={item.privilege ? cls(item.privilege) : undefined}
|
||||
>
|
||||
{schema.title}
|
||||
</Tag>
|
||||
<span style={{ color: 'rgba(0, 0, 0, 0.25)', fontSize: 14 }}>
|
||||
{item.name}
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
extra={[
|
||||
<Badge key={'1'} count={errors.length} />,
|
||||
<DeleteOutlined
|
||||
key={'2'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
field.remove(index);
|
||||
}}
|
||||
/>,
|
||||
]}
|
||||
extra={
|
||||
item.privilege === 'undelete'
|
||||
? []
|
||||
: [
|
||||
<Badge key={'1'} count={errors.length} />,
|
||||
<DeleteOutlined
|
||||
key={'2'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
field.remove(index);
|
||||
}}
|
||||
/>,
|
||||
]
|
||||
}
|
||||
key={item.key}
|
||||
>
|
||||
<RecursionField
|
||||
@ -325,6 +341,9 @@ export const DatabaseField: any = observer((props) => {
|
||||
name: `f_${uid()}`,
|
||||
interface: info.key,
|
||||
};
|
||||
if (schema.initialize) {
|
||||
schema.initialize(data);
|
||||
}
|
||||
field.push(data);
|
||||
setActiveKey(data.key);
|
||||
console.log('info.key', field.value);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { omit } from 'lodash';
|
||||
import { defaultProps } from './properties';
|
||||
|
||||
export const checkbox: ISchema = {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { defaultProps } from './properties';
|
||||
import { dateTimeProps, defaultProps } from './properties';
|
||||
|
||||
export const createdAt: ISchema = {
|
||||
name: 'createdAt',
|
||||
@ -13,7 +13,7 @@ export const createdAt: ISchema = {
|
||||
// name,
|
||||
uiSchema: {
|
||||
type: 'datetime',
|
||||
// title,
|
||||
title: '创建时间',
|
||||
'x-component': 'DatePicker',
|
||||
'x-component-props': {},
|
||||
'x-read-pretty': true,
|
||||
@ -23,6 +23,7 @@ export const createdAt: ISchema = {
|
||||
},
|
||||
properties: {
|
||||
...defaultProps,
|
||||
...dateTimeProps,
|
||||
},
|
||||
operations: [
|
||||
{ label: '等于', value: 'eq' },
|
||||
|
@ -14,7 +14,7 @@ export const createdBy: ISchema = {
|
||||
// name,
|
||||
uiSchema: {
|
||||
type: 'object',
|
||||
// title,
|
||||
title: '创建人',
|
||||
'x-component': 'Select.Drawer',
|
||||
'x-component-props': {},
|
||||
'x-decorator': 'FormItem',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { defaultProps } from './properties';
|
||||
import { dateTimeProps, defaultProps } from './properties';
|
||||
|
||||
export const datetime: ISchema = {
|
||||
name: 'datetime',
|
||||
@ -14,13 +14,16 @@ export const datetime: ISchema = {
|
||||
type: 'datetime',
|
||||
// title,
|
||||
'x-component': 'DatePicker',
|
||||
'x-component-props': {},
|
||||
'x-component-props': {
|
||||
showTime: false,
|
||||
},
|
||||
'x-decorator': 'FormItem',
|
||||
'x-designable-bar': 'DatePicker.DesignableBar',
|
||||
} as ISchema,
|
||||
},
|
||||
properties: {
|
||||
...defaultProps,
|
||||
...dateTimeProps,
|
||||
},
|
||||
operations: [
|
||||
{ label: '等于', value: 'eq' },
|
||||
|
@ -1,12 +1,26 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { set } from 'lodash';
|
||||
import { cloneDeep, set } from 'lodash';
|
||||
import * as types from './types';
|
||||
import { uid } from '@formily/shared';
|
||||
|
||||
export const interfaces = new Map<string, ISchema>();
|
||||
|
||||
const fields = {};
|
||||
const groupLabels = {};
|
||||
|
||||
export function getDefaultFields() {
|
||||
const defaults = ['createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
||||
return defaults.map(key => {
|
||||
return {
|
||||
interface: key,
|
||||
key: uid(),
|
||||
name: uid(),
|
||||
privilege: 'undelete',
|
||||
...cloneDeep(interfaces.get(key)?.default),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function registerField(group: string, type: string, schema) {
|
||||
fields[group] = fields[group] || {};
|
||||
set(fields, [group, type], schema);
|
||||
@ -25,6 +39,7 @@ Object.keys(types).forEach((type) => {
|
||||
registerGroupLabel('basic', '基本类型');
|
||||
registerGroupLabel('choices', '选择类型');
|
||||
registerGroupLabel('media', '多媒体类型');
|
||||
registerGroupLabel('datetime', '日期和时间');
|
||||
registerGroupLabel('relation', '关系类型');
|
||||
registerGroupLabel('systemInfo', '系统信息');
|
||||
registerGroupLabel('others', '其他类型');
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { defaultProps } from './properties';
|
||||
import { uid } from '@formily/shared';
|
||||
|
||||
export const linkTo: ISchema = {
|
||||
name: 'linkTo',
|
||||
@ -19,8 +20,41 @@ export const linkTo: ISchema = {
|
||||
'x-designable-bar': 'Select.Drawer.DesignableBar',
|
||||
} as ISchema,
|
||||
},
|
||||
initialize: (values: any) => {
|
||||
if (values.dataType === 'belongsToMany') {
|
||||
if (!values.through) {
|
||||
values.through = `t_${uid()}`;
|
||||
}
|
||||
if (!values.foreignKey) {
|
||||
values.foreignKey = `f_${uid()}`;
|
||||
}
|
||||
if (!values.otherKey) {
|
||||
values.otherKey = `f_${uid()}`;
|
||||
}
|
||||
if (!values.sourceKey) {
|
||||
values.sourceKey = 'id';
|
||||
}
|
||||
if (!values.targetKey) {
|
||||
values.targetKey = 'id';
|
||||
}
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
...defaultProps,
|
||||
target: {
|
||||
type: 'string',
|
||||
title: '要关联的数据表',
|
||||
required: true,
|
||||
'x-reactions': ['{{useAsyncDataSource(loadCollections)}}'],
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
},
|
||||
'uiSchema.x-component-props.multiple': {
|
||||
type: 'boolean',
|
||||
'x-content': '允许关联多条记录',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
},
|
||||
},
|
||||
operations: [
|
||||
{ label: '等于', value: 'eq' },
|
||||
|
@ -14,7 +14,9 @@ export const multipleSelect: ISchema = {
|
||||
type: 'array',
|
||||
// title,
|
||||
'x-component': 'Select',
|
||||
'x-component-props': {},
|
||||
'x-component-props': {
|
||||
mode: 'multiple',
|
||||
},
|
||||
'x-decorator': 'FormItem',
|
||||
'x-designable-bar': 'Select.DesignableBar',
|
||||
enum: [],
|
||||
|
@ -14,12 +14,31 @@ export const number: ISchema = {
|
||||
type: 'number',
|
||||
// title,
|
||||
'x-component': 'InputNumber',
|
||||
'x-component-props': {
|
||||
stringMode: true,
|
||||
step: '0',
|
||||
},
|
||||
'x-decorator': 'FormItem',
|
||||
'x-designable-bar': 'InputNumber.DesignableBar',
|
||||
} as ISchema,
|
||||
},
|
||||
properties: {
|
||||
...defaultProps,
|
||||
'uiSchema.x-component-props.step': {
|
||||
type: 'string',
|
||||
title: '精度',
|
||||
'x-component': 'Select',
|
||||
'x-decorator': 'FormItem',
|
||||
default: '0',
|
||||
enum: [
|
||||
{ value: '0', label: '1' },
|
||||
{ value: '0.1', label: '1.0' },
|
||||
{ value: '0.01', label: '1.00' },
|
||||
{ value: '0.001', label: '1.000' },
|
||||
{ value: '0.0001', label: '1.0000' },
|
||||
{ value: '0.00001', label: '1.00000' },
|
||||
],
|
||||
},
|
||||
},
|
||||
operations: [
|
||||
{ label: '等于', value: 'eq' },
|
||||
|
@ -14,12 +14,31 @@ export const percent: ISchema = {
|
||||
type: 'string',
|
||||
// title,
|
||||
'x-component': 'InputNumber',
|
||||
'x-component-props': {
|
||||
stringMode: true,
|
||||
step: '0',
|
||||
},
|
||||
'x-decorator': 'FormItem',
|
||||
'x-designable-bar': 'InputNumber.DesignableBar',
|
||||
} as ISchema,
|
||||
},
|
||||
properties: {
|
||||
...defaultProps
|
||||
...defaultProps,
|
||||
'uiSchema.x-component-props.step': {
|
||||
type: 'string',
|
||||
title: '精度',
|
||||
'x-component': 'Select',
|
||||
'x-decorator': 'FormItem',
|
||||
default: '0',
|
||||
enum: [
|
||||
{ value: '0', label: '1' },
|
||||
{ value: '0.1', label: '1.0' },
|
||||
{ value: '0.01', label: '1.00' },
|
||||
{ value: '0.001', label: '1.000' },
|
||||
{ value: '0.0001', label: '1.0000' },
|
||||
{ value: '0.00001', label: '1.00000' },
|
||||
]
|
||||
},
|
||||
},
|
||||
operations: [
|
||||
{ label: '等于', value: 'eq' },
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { ISchema } from "@formily/react";
|
||||
import { ISchema } from '@formily/react';
|
||||
|
||||
export const dataType: ISchema = {
|
||||
type: 'string',
|
||||
title: '数据类型',
|
||||
required: true,
|
||||
'x-disabled': true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
enum: [
|
||||
@ -24,7 +25,61 @@ export const dataType: ISchema = {
|
||||
{ label: 'BelongsTo', value: 'belongsTo' },
|
||||
{ label: 'BelongsToMany', value: 'belongsToMany' },
|
||||
],
|
||||
}
|
||||
};
|
||||
|
||||
export const dateTimeProps: { [key: string]: ISchema } = {
|
||||
dateFormat: {
|
||||
type: 'string',
|
||||
title: '日期格式',
|
||||
'x-component': 'Radio.Group',
|
||||
'x-decorator': 'FormItem',
|
||||
default: 'YYYY-MM-DD',
|
||||
enum: [
|
||||
{
|
||||
label: '年/月/日',
|
||||
value: 'YYYY/MM/DD',
|
||||
},
|
||||
{
|
||||
label: '年-月-日',
|
||||
value: 'YYYY-MM-DD',
|
||||
},
|
||||
{
|
||||
label: '日/月/年',
|
||||
value: 'DD/MM/YYYY',
|
||||
},
|
||||
],
|
||||
},
|
||||
showTime: {
|
||||
type: 'boolean',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
'x-content': '显示时间',
|
||||
'x-reactions': [
|
||||
`{{(field) => {
|
||||
field.query('..[].timeFormat').take(f => {
|
||||
f.display = field.value ? 'visible' : 'none';
|
||||
});
|
||||
}}}`,
|
||||
],
|
||||
},
|
||||
timeFormat: {
|
||||
type: 'string',
|
||||
title: '时间格式',
|
||||
'x-component': 'Radio.Group',
|
||||
'x-decorator': 'FormItem',
|
||||
default: 'HH:mm:ss',
|
||||
enum: [
|
||||
{
|
||||
label: '24小时制',
|
||||
value: 'HH:mm:ss',
|
||||
},
|
||||
{
|
||||
label: '12小时制',
|
||||
value: 'hh:mm:ss a',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const dataSource: ISchema = {
|
||||
type: 'array',
|
||||
@ -53,7 +108,7 @@ export const dataSource: ISchema = {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayTable.Column',
|
||||
'x-component-props': { title: '选项值' },
|
||||
"x-hidden": true,
|
||||
'x-hidden': true,
|
||||
properties: {
|
||||
value: {
|
||||
type: 'string',
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { defaultProps } from './properties';
|
||||
import { uid } from '@formily/shared';
|
||||
|
||||
export const subTable: ISchema = {
|
||||
name: 'subTable',
|
||||
@ -20,6 +21,14 @@ export const subTable: ISchema = {
|
||||
enum: [],
|
||||
} as ISchema,
|
||||
},
|
||||
initialize: (values: any) => {
|
||||
if (!values.target) {
|
||||
values.target = `t_${uid()}`;
|
||||
}
|
||||
if (!values.foreignKey) {
|
||||
values.foreignKey = `f_${uid()}`;
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
...defaultProps,
|
||||
'children': {
|
||||
|
@ -20,6 +20,23 @@ export const time: ISchema = {
|
||||
},
|
||||
properties: {
|
||||
...defaultProps,
|
||||
'uiSchema.x-component-props.format': {
|
||||
type: 'string',
|
||||
title: '时间格式',
|
||||
'x-component': 'Radio.Group',
|
||||
'x-decorator': 'FormItem',
|
||||
default: 'HH:mm:ss',
|
||||
enum: [
|
||||
{
|
||||
label: '24小时制',
|
||||
value: 'HH:mm:ss',
|
||||
},
|
||||
{
|
||||
label: '12小时制',
|
||||
value: 'hh:mm:ss a',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
operations: [
|
||||
{ label: '等于', value: 'eq' },
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { defaultProps } from './properties';
|
||||
import { dateTimeProps, defaultProps } from './properties';
|
||||
|
||||
export const updatedAt: ISchema = {
|
||||
name: 'updatedAt',
|
||||
@ -13,7 +13,7 @@ export const updatedAt: ISchema = {
|
||||
// name,
|
||||
uiSchema: {
|
||||
type: 'datetime',
|
||||
// title,
|
||||
title: '最后更新时间',
|
||||
'x-component': 'DatePicker',
|
||||
'x-component-props': {},
|
||||
'x-read-pretty': true,
|
||||
@ -23,6 +23,7 @@ export const updatedAt: ISchema = {
|
||||
},
|
||||
properties: {
|
||||
...defaultProps,
|
||||
...dateTimeProps,
|
||||
},
|
||||
operations: [
|
||||
{ label: '等于', value: 'eq' },
|
||||
|
@ -14,7 +14,7 @@ export const updatedBy: ISchema = {
|
||||
// name,
|
||||
uiSchema: {
|
||||
type: 'object',
|
||||
// title,
|
||||
title: '最后修改人',
|
||||
'x-component': 'Select.Drawer',
|
||||
'x-component-props': {},
|
||||
'x-decorator': 'FormItem',
|
||||
|
@ -21,3 +21,8 @@
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tag.disabled {
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
cursor: not-allowed;
|
||||
}
|
@ -55,7 +55,7 @@ export const FieldDesignableBar = observer((props) => {
|
||||
className={cls('designable-bar-actions', { active: visible })}
|
||||
>
|
||||
<Space size={'small'}>
|
||||
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
|
||||
<AddNew.FormItem defaultAction={'insertAfter'} ghost />
|
||||
{dragRef && <DragOutlined ref={dragRef} />}
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
|
@ -49,8 +49,8 @@ export function useVisible(name, defaultValue = false) {
|
||||
|
||||
export const DesignableBarContext = createContext(null);
|
||||
|
||||
const [PageTitleContextProvider, usePageTitleContext] = constate(() => {
|
||||
return useState(null);
|
||||
const [PageTitleContextProvider, usePageTitleContext] = constate(({ defaultPageTitle }) => {
|
||||
return useState(defaultPageTitle);
|
||||
});
|
||||
|
||||
export { PageTitleContextProvider, usePageTitleContext };
|
||||
|
@ -72,7 +72,7 @@ import { onFieldChange } from '@formily/core';
|
||||
export const MenuModeContext = createContext(null);
|
||||
|
||||
const SideMenu = (props: any) => {
|
||||
const { selectedKey, onSelect, path } = props;
|
||||
const { selectedKey, defaultSelectedKeys, onSelect, path } = props;
|
||||
const { schema } = useDesignable();
|
||||
if (!selectedKey) {
|
||||
return null;
|
||||
@ -101,11 +101,11 @@ const SideMenu = (props: any) => {
|
||||
};
|
||||
|
||||
export const Menu: any = observer((props: any) => {
|
||||
const { mode, onSelect, sideMenuRef, ...others } = props;
|
||||
const { mode, onSelect, sideMenuRef, defaultSelectedKeys = [], ...others } = props;
|
||||
const { designable, schema } = useDesignable();
|
||||
const fieldSchema = useFieldSchema();
|
||||
console.log('Menu.schema', schema, fieldSchema);
|
||||
const [selectedKey, setSelectedKey] = useState(null);
|
||||
const [selectedKey, setSelectedKey] = useState(defaultSelectedKeys[0]||null);
|
||||
const ref = useRef();
|
||||
const path = useSchemaPath();
|
||||
const child = schema.properties && schema.properties[selectedKey];
|
||||
@ -133,6 +133,7 @@ export const Menu: any = observer((props: any) => {
|
||||
return (
|
||||
<MenuModeContext.Provider value={mode}>
|
||||
<AntdMenu
|
||||
defaultSelectedKeys={defaultSelectedKeys}
|
||||
{...others}
|
||||
mode={mode === 'mix' ? 'horizontal' : mode}
|
||||
onSelect={(info) => {
|
||||
@ -163,12 +164,13 @@ export const Menu: any = observer((props: any) => {
|
||||
<SideMenu
|
||||
path={path}
|
||||
onSelect={(info) => {
|
||||
const keyPath = [selectedKey, ...info.keyPath];
|
||||
const keyPath = [selectedKey, ...[...info.keyPath].reverse()];
|
||||
const selectedSchema = findPropertyByPath(schema, keyPath);
|
||||
console.log('keyPath', keyPath, selectedSchema);
|
||||
onSelect &&
|
||||
onSelect({ ...info, keyPath, schema: selectedSchema });
|
||||
}}
|
||||
defaultSelectedKeys={defaultSelectedKeys||[]}
|
||||
selectedKey={selectedKey}
|
||||
sideMenuRef={sideMenuRef}
|
||||
/>
|
||||
|
@ -423,10 +423,11 @@ export abstract class Relation extends Field {
|
||||
|
||||
public targetTableInit() {
|
||||
const { target, fields = [] } = this.options;
|
||||
if (target && fields.length) {
|
||||
const children = fields.concat(this.options.children || []);
|
||||
if (target && children.length) {
|
||||
this.context.database.table({
|
||||
name: target,
|
||||
fields,
|
||||
fields: children,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ export const create = async (ctx: actions.Context, next: actions.Next) => {
|
||||
await actions.common.create(ctx, async () => {});
|
||||
const { associated } = ctx.action.params;
|
||||
await associated.migrate();
|
||||
console.log('associated.migrate');
|
||||
// console.log('associated.migrate');
|
||||
await next();
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ export const createOrUpdate = async (ctx: actions.Context, next: actions.Next) =
|
||||
await collection.updateAssociations(values);
|
||||
await collection.migrate();
|
||||
} catch (error) {
|
||||
console.log('error.errors', error.errors)
|
||||
// console.log('error.errors', error.errors)
|
||||
throw error;
|
||||
}
|
||||
ctx.body = collection;
|
||||
|
@ -20,6 +20,10 @@ export default {
|
||||
name: 'title',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'privilege',
|
||||
},
|
||||
{
|
||||
type: 'json',
|
||||
name: 'options',
|
||||
@ -30,5 +34,10 @@ export default {
|
||||
name: 'fields',
|
||||
sourceKey: 'name',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'uiSchema',
|
||||
target: 'ui_schemas',
|
||||
},
|
||||
],
|
||||
} as TableOptions;
|
||||
|
@ -31,6 +31,10 @@ export default {
|
||||
type: 'string',
|
||||
name: 'dataType',
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'privilege',
|
||||
},
|
||||
{
|
||||
type: 'hasMany',
|
||||
name: 'children',
|
||||
@ -49,7 +53,6 @@ export default {
|
||||
type: 'belongsTo',
|
||||
name: 'uiSchema',
|
||||
target: 'ui_schemas',
|
||||
defaultValue: {},
|
||||
},
|
||||
{
|
||||
type: 'json',
|
||||
|
@ -41,7 +41,9 @@ export class Collection extends Model {
|
||||
}
|
||||
|
||||
async getNestedFields() {
|
||||
const fields = await this.getFields();
|
||||
const fields = await this.getFields({
|
||||
order: [['sort', 'asc']],
|
||||
});
|
||||
const items = [];
|
||||
for (const field of fields) {
|
||||
items.push(await field.toProps());
|
||||
@ -58,7 +60,7 @@ export class Collection extends Model {
|
||||
*/
|
||||
async loadTableOptions(opts: any = {}) {
|
||||
const options = await this.toProps();
|
||||
console.log(JSON.stringify(options, null, 2));
|
||||
// console.log(JSON.stringify(options, null, 2));
|
||||
const table = this.database.table(options);
|
||||
return table;
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import { Model } from '@nocobase/database';
|
||||
|
||||
export class Field extends Model {
|
||||
static async create(value?: any, options?: any): Promise<any> {
|
||||
// console.log({ value });
|
||||
const attributes = this.toAttributes(value);
|
||||
// @ts-ignore
|
||||
const model: Model = await super.create(attributes, options);
|
||||
@ -11,13 +10,16 @@ export class Field extends Model {
|
||||
}
|
||||
|
||||
static toAttributes(value = {}): any {
|
||||
const data = _.cloneDeep(value);
|
||||
const data: any = _.cloneDeep(value);
|
||||
const keys = [
|
||||
...Object.keys(this.rawAttributes),
|
||||
...Object.keys(this.associations),
|
||||
];
|
||||
if (!data.dataType && data.type) {
|
||||
data.dataType = data.type;
|
||||
}
|
||||
const attrs = _.pick(data, keys);
|
||||
const options = _.omit(data, keys);
|
||||
const options = _.omit(data, [...keys, 'type']);
|
||||
return { ...attrs, options };
|
||||
}
|
||||
|
||||
|
@ -11,10 +11,86 @@ export default async function (this: Application, options = {}) {
|
||||
database.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
database.getModel('fields').beforeCreate((model) => {
|
||||
const [Collection, Field] = database.getModels(['collections', 'fields']);
|
||||
Field.beforeCreate(async (model) => {
|
||||
if (!model.get('name')) {
|
||||
model.set('name', model.get('key'));
|
||||
}
|
||||
if (!model.get('collection_name') && model.get('parentKey')) {
|
||||
const field = await Field.findByPk(model.get('parentKey'));
|
||||
if (field) {
|
||||
const { target } = field.get('options') || {};
|
||||
if (target) {
|
||||
model.set('collection_name', target);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Field.beforeUpdate(async (model) => {
|
||||
if (!model.get('collection_name') && model.get('parentKey')) {
|
||||
const field = await Field.findByPk(model.get('parentKey'));
|
||||
if (field) {
|
||||
const { target } = field.get('options') || {};
|
||||
if (target) {
|
||||
model.set('collection_name', target);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Field.afterCreate(async (model) => {
|
||||
console.log('afterCreate');
|
||||
if (model.get('interface') !== 'subTable') {
|
||||
return;
|
||||
}
|
||||
const { target } = model.get('options') || {};
|
||||
// const uiSchemaKey = model.get('ui_schema_key');
|
||||
// console.log({ uiSchemaKey })
|
||||
try {
|
||||
let collection = await Collection.findOne({
|
||||
where: {
|
||||
name: target,
|
||||
},
|
||||
});
|
||||
if (!collection) {
|
||||
collection = await Collection.create({
|
||||
name: target,
|
||||
// ui_schema_key: uiSchemaKey,
|
||||
});
|
||||
}
|
||||
// if (model.get('ui_schema_key')) {
|
||||
// collection.set('ui_schema_key', model.get('ui_schema_key'));
|
||||
// await collection.save({ hooks: false });
|
||||
// }
|
||||
await collection.migrate();
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
Field.afterUpdate(async (model) => {
|
||||
console.log('afterUpdate');
|
||||
if (model.get('interface') !== 'subTable') {
|
||||
return;
|
||||
}
|
||||
const { target } = model.get('options') || {};
|
||||
try {
|
||||
let collection = await Collection.findOne({
|
||||
where: {
|
||||
name: target,
|
||||
},
|
||||
});
|
||||
if (!collection) {
|
||||
collection = await Collection.create({
|
||||
name: target,
|
||||
});
|
||||
}
|
||||
// if (model.get('ui_schema_key')) {
|
||||
// collection.set('ui_schema_key', model.get('ui_schema_key'));
|
||||
// await collection.save({ hooks: false });
|
||||
// }
|
||||
await collection.migrate();
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
this.resourcer.registerActionHandler('collections.fields:create', create);
|
||||
this.resourcer.registerActionHandler('collections:findAll', findAll);
|
||||
|
@ -1,12 +1,12 @@
|
||||
import _ from 'lodash';
|
||||
import { Model } from '@nocobase/database';
|
||||
import deepmerge from 'deepmerge';
|
||||
import { merge } from '../utils';
|
||||
|
||||
export class UISchema extends Model {
|
||||
static async create(value?: any, options?: any): Promise<any> {
|
||||
// console.log({ value });
|
||||
const attributes = this.toAttributes(_.cloneDeep(value));
|
||||
console.log({ attributes })
|
||||
// console.log({ attributes })
|
||||
// @ts-ignore
|
||||
const model: Model = await super.create(attributes, options);
|
||||
if (!attributes.children) {
|
||||
@ -34,7 +34,9 @@ export class UISchema extends Model {
|
||||
];
|
||||
const attrs = _.pick(data, keys);
|
||||
const options = _.omit(data, keys);
|
||||
return { ...attrs, options: deepmerge(opts, options) };
|
||||
return {
|
||||
...attrs, options: merge(opts, options)
|
||||
};
|
||||
}
|
||||
|
||||
static properties2children(properties = []) {
|
||||
|
@ -7,165 +7,67 @@ export default {
|
||||
// internal: true,
|
||||
createdBy: false,
|
||||
updatedBy: false,
|
||||
privilege: 'undelete',
|
||||
fields: [
|
||||
{
|
||||
interface: 'string',
|
||||
type: 'string',
|
||||
name: 'username',
|
||||
title: '用户名',
|
||||
unique: true,
|
||||
required: true,
|
||||
createOnly: true,
|
||||
component: {
|
||||
name: 'nickname',
|
||||
uiSchema: {
|
||||
type: 'string',
|
||||
showInTable: true,
|
||||
showInDetail: true,
|
||||
showInForm: true,
|
||||
title: '昵称',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
},
|
||||
{
|
||||
interface: 'email',
|
||||
type: 'string',
|
||||
name: 'email',
|
||||
title: '邮箱',
|
||||
unique: true,
|
||||
required: true,
|
||||
createOnly: true,
|
||||
component: {
|
||||
privilege: 'undelete',
|
||||
uiSchema: {
|
||||
type: 'string',
|
||||
showInTable: true,
|
||||
showInDetail: true,
|
||||
showInForm: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
interface: 'string',
|
||||
type: 'string',
|
||||
name: 'nickname',
|
||||
title: '昵称',
|
||||
required: true,
|
||||
component: {
|
||||
type: 'string',
|
||||
showInTable: true,
|
||||
showInDetail: true,
|
||||
showInForm: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
interface: 'phone',
|
||||
type: 'string',
|
||||
name: 'phone',
|
||||
unique: true,
|
||||
title: '手机号',
|
||||
component: {
|
||||
type: 'string',
|
||||
showInTable: true,
|
||||
showInDetail: true,
|
||||
showInForm: true,
|
||||
title: '邮箱',
|
||||
'x-component': 'Input',
|
||||
require: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
interface: 'password',
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
title: '密码',
|
||||
hidden: true,
|
||||
component: {
|
||||
type: 'password',
|
||||
showInForm: true,
|
||||
privilege: 'undelete',
|
||||
uiSchema: {
|
||||
type: 'string',
|
||||
title: '密码',
|
||||
'x-component': 'Password',
|
||||
},
|
||||
},
|
||||
{
|
||||
interface: 'string',
|
||||
interface: 'password',
|
||||
type: 'string',
|
||||
name: 'token',
|
||||
title: 'Token',
|
||||
unique: true,
|
||||
hidden: true,
|
||||
filterable: false,
|
||||
developerMode: true,
|
||||
privilege: 'undelete',
|
||||
uiSchema: {
|
||||
type: 'string',
|
||||
title: 'Token',
|
||||
'x-component': 'Password',
|
||||
},
|
||||
},
|
||||
{
|
||||
interface: 'string',
|
||||
interface: 'password',
|
||||
type: 'string',
|
||||
name: 'reset_token',
|
||||
title: 'Reset Token',
|
||||
unique: true,
|
||||
hidden: true,
|
||||
filterable: false,
|
||||
developerMode: true,
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'list',
|
||||
title: '查看',
|
||||
},
|
||||
{
|
||||
type: 'destroy',
|
||||
name: 'destroy',
|
||||
title: '删除',
|
||||
},
|
||||
{
|
||||
type: 'create',
|
||||
name: 'create',
|
||||
title: '新增',
|
||||
viewName: 'form',
|
||||
},
|
||||
{
|
||||
type: 'update',
|
||||
name: 'update',
|
||||
title: '编辑',
|
||||
viewName: 'form',
|
||||
},
|
||||
],
|
||||
views_v2: [
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'table',
|
||||
name: 'table',
|
||||
title: '全部数据',
|
||||
labelField: 'nickname',
|
||||
actions: [
|
||||
{
|
||||
name: 'create',
|
||||
type: 'create',
|
||||
title: '新增',
|
||||
viewName: 'form',
|
||||
},
|
||||
{
|
||||
name: 'destroy',
|
||||
type: 'destroy',
|
||||
title: '删除',
|
||||
},
|
||||
],
|
||||
fields: ['email', 'nickname', 'phone', 'roles'],
|
||||
detailsOpenMode: 'drawer', // window
|
||||
details: ['form'],
|
||||
sort: ['id'],
|
||||
},
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'descriptions',
|
||||
name: 'descriptions',
|
||||
title: '详情',
|
||||
fields: ['email', 'nickname', 'phone', 'roles'],
|
||||
actions: [
|
||||
{
|
||||
name: 'update',
|
||||
type: 'update',
|
||||
title: '编辑',
|
||||
viewName: 'form',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'form',
|
||||
name: 'form',
|
||||
title: '表单',
|
||||
fields: ['email', 'nickname', 'phone', 'password', 'roles'],
|
||||
privilege: 'undelete',
|
||||
uiSchema: {
|
||||
type: 'string',
|
||||
title: 'Reset Token',
|
||||
'x-component': 'Password',
|
||||
},
|
||||
},
|
||||
],
|
||||
} as TableOptions;
|
||||
|
Loading…
Reference in New Issue
Block a user