@quansitech/antd-admin 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/components/Column/Cascader.tsx +78 -78
  2. package/components/Column/File.tsx +165 -167
  3. package/components/Column/Image.tsx +76 -76
  4. package/components/{Table/Option → Column/Readonly/Action}/Link.tsx +77 -67
  5. package/components/{Table/Option → Column/Readonly/Action}/types.d.ts +5 -4
  6. package/components/Column/Readonly/Action.tsx +80 -0
  7. package/components/Column/Readonly/Cascader.tsx +50 -50
  8. package/components/Column/Readonly/File.tsx +52 -53
  9. package/components/Column/Readonly/Image.tsx +38 -77
  10. package/components/Column/Readonly/Ueditor.tsx +18 -0
  11. package/components/Column/Readonly/types.d.ts +9 -8
  12. package/components/Column/Ueditor.tsx +313 -313
  13. package/components/Column/types.d.ts +29 -28
  14. package/components/Form/Action/Button.tsx +128 -124
  15. package/components/Form/Action/types.d.ts +5 -4
  16. package/components/Form/Actions.tsx +38 -34
  17. package/components/Form.tsx +176 -170
  18. package/components/FormContext.ts +8 -7
  19. package/components/Layout/New.tsx +252 -0
  20. package/components/Layout.tsx +52 -237
  21. package/components/LayoutContext.ts +25 -25
  22. package/components/ModalContext.ts +15 -15
  23. package/components/Table/Action/Button.tsx +88 -88
  24. package/components/Table/Action/StartEditable.tsx +58 -58
  25. package/components/Table/Action/types.d.ts +7 -6
  26. package/components/Table/ToolbarActions.tsx +43 -38
  27. package/components/Table.scss +4 -7
  28. package/components/Table.tsx +280 -279
  29. package/components/TableContext.ts +14 -13
  30. package/components/Tabs.tsx +71 -71
  31. package/lib/container.ts +83 -81
  32. package/lib/customRule.ts +9 -9
  33. package/lib/global.ts +10 -10
  34. package/lib/helpers.tsx +145 -149
  35. package/lib/http.ts +73 -73
  36. package/lib/schemaHandler.ts +121 -121
  37. package/lib/upload.ts +177 -177
  38. package/lib/writeExtra.js +31 -0
  39. package/package.json +2 -6
  40. package/readme.md +151 -128
  41. package/components/Column/Readonly/Option.tsx +0 -58
@@ -1,171 +1,177 @@
1
- import {BetaSchemaForm, ProFormColumnsType, ProFormInstance, ProSkeleton} from "@ant-design/pro-components";
2
- import type {FormSchema} from "@ant-design/pro-form/es/components/SchemaForm/typing";
3
- import React, {lazy, Suspense, useContext, useEffect, useRef, useState} from "react";
4
- import upperFirst from "lodash/upperFirst";
5
- import container from "../lib/container";
6
- import {FormActionType} from "./Form/Action/types";
7
- import Actions from "./Form/Actions";
8
- import {FormContext} from "./FormContext";
9
- import {Col, Row, Spin} from "antd";
10
- import http from "../lib/http";
11
- import {RuleObject} from "rc-field-form/lib/interface";
12
- import customRule from "../lib/customRule";
13
- import cloneDeep from "lodash/cloneDeep";
14
- import {ModalContext} from "./ModalContext";
15
- import {TableContext} from "./TableContext";
16
- import {commonHandler} from "../lib/schemaHandler";
17
-
18
- type SubmitRequestType = {
19
- url: string,
20
- method?: string,
21
- data?: any,
22
- afterSubmit?: () => void,
23
- headers?: Record<string, string>
24
- afterAction?: string[],
25
- }
26
-
27
-
28
- export default function (props: FormSchema & {
29
- actions?: FormActionType[]
30
- metaTitle?: string,
31
- columns?: ProFormColumnsType[],
32
- submitRequest?: SubmitRequestType
33
- }) {
34
-
35
- const [columns, setColumns] = useState<ProFormColumnsType[]>([])
36
- const formRef = useRef<ProFormInstance>()
37
- const [initialized, setInitialized] = useState(false)
38
- const [loading, setLoading] = useState(false)
39
-
40
- useEffect(() => {
41
- setColumns((cloneDeep(props.columns)?.map((c: ProFormColumnsType & {
42
- formItemProps?: {
43
- rules?: (RuleObject & {
44
- customType?: string
45
- })[]
46
- }
47
- }) => {
48
- // rules
49
- if (!c.formItemProps) {
50
- c.formItemProps = {}
51
- }
52
- if (!c.formItemProps?.rules) {
53
- c.formItemProps.rules = []
54
- }
55
- c.formItemProps.rules = c.formItemProps.rules.map(rule => {
56
- if (rule.customType) {
57
- if (customRule[rule.customType]) {
58
- rule.validator = customRule[rule.customType]
59
- }
60
- }
61
- return rule
62
- })
63
-
64
- // item render
65
- const formItemComponent = 'Column.' + upperFirst(c.valueType as string)
66
- if (container.check(formItemComponent)) {
67
- const Component = lazy(container.get(formItemComponent))
68
- c.renderFormItem = (schema, config, form) =>
69
- <Suspense fallback={<Spin/>}>
70
- <Component config={config}
71
- form={form}
72
- fieldProps={c.fieldProps}
73
- key={c.title as string}
74
- rules={c.formItemProps?.rules}
75
- dataIndex={c.dataIndex}
76
- ></Component>
77
- </Suspense>
78
- }
79
- // readonly render
80
- const readonlyComponent = 'Column.Readonly.' + upperFirst(c.valueType as string)
81
- if (container.check(readonlyComponent)) {
82
- const Component = lazy(container.get(readonlyComponent))
83
- c.render = (dom, entity, index, action, schema) =>
84
- <Suspense fallback={<Spin/>}>
85
- <Component key={c.title as string}
86
- entity={entity}
87
- index={index}
88
- action={action}
89
- schema={schema}
90
- dom={dom}
91
- ></Component>
92
- </Suspense>
93
- }
94
-
95
- commonHandler(c)
96
- if (container.schemaHandler[c.valueType as string]) {
97
- return container.schemaHandler[c.valueType as string](c)
98
- }
99
-
100
- return c
101
- }) || []) as ProFormColumnsType[])
102
-
103
- setInitialized(true)
104
- }, []);
105
-
106
- const modalContext = useContext(ModalContext)
107
- const tableContext = useContext(TableContext)
108
-
109
- const handleAfterAction = async (req?: SubmitRequestType) => {
110
- if (req?.afterAction?.includes('tableReload')) {
111
- if (modalContext.contexts) {
112
- modalContext.setAfterClose(() => {
113
- modalContext.contexts?.tableContext?.actionRef.reload()
114
- })
115
- }
116
- if (tableContext.actionRef) {
117
- await tableContext.actionRef.reload()
118
- }
119
- }
120
- if (req?.afterAction?.includes('closeModal') && modalContext.inModal) {
121
- modalContext.closeModal()
122
- }
123
- }
124
-
125
- const onFinish = async (values: any) => {
126
- if (props.submitRequest) {
127
- setLoading(true)
128
- try {
129
- await http({
130
- method: props.submitRequest.method,
131
- url: props.submitRequest.url,
132
- data: Object.assign({}, props.submitRequest.data, values),
133
- headers: props.submitRequest.headers,
134
- })
135
-
136
- handleAfterAction(props.submitRequest)
137
- } finally {
138
- setLoading(false)
139
- }
140
- }
141
- }
142
-
143
- return <>
144
- <Row justify={'center'}>
145
- <Col sm={24} md={22} lg={20}>
146
- <FormContext.Provider value={{
147
- formRef: formRef,
148
- }}>
149
- {!initialized
150
- ? <ProSkeleton type={"list"} list={2}></ProSkeleton>
151
- : <BetaSchemaForm columns={columns}
152
- colProps={props.colProps}
153
- readonly={props.readonly}
154
- grid={true}
155
- loading={loading}
156
- formRef={formRef}
157
- initialValues={props.initialValues}
158
- onFinish={onFinish}
159
- submitter={{
160
- render: () => [
161
- <Actions key={'actions'} loading={loading}
162
- actions={props.actions}></Actions>
163
- ]
164
- }}
165
- ></BetaSchemaForm>
166
- }
167
- </FormContext.Provider>
168
- </Col>
169
- </Row>
170
- </>
1
+ import {BetaSchemaForm, ProFormColumnsType, ProFormInstance, ProSkeleton} from "@ant-design/pro-components";
2
+ import type {FormSchema} from "@ant-design/pro-form/es/components/SchemaForm/typing";
3
+ import React, {lazy, Suspense, useContext, useEffect, useRef, useState} from "react";
4
+ import {cloneDeep, upperFirst} from "lodash";
5
+ import container from "../lib/container";
6
+ import {FormActionType} from "./Form/Action/types";
7
+ import Actions from "./Form/Actions";
8
+ import {FormContext} from "./FormContext";
9
+ import {Col, Row, Spin} from "antd";
10
+ import http from "../lib/http";
11
+ import customRule from "../lib/customRule";
12
+ import {ModalContext} from "./ModalContext";
13
+ import {TableContext} from "./TableContext";
14
+ import {commonHandler} from "../lib/schemaHandler";
15
+ import {Rule} from "antd/es/form";
16
+
17
+ type SubmitRequestType = {
18
+ url: string,
19
+ method?: string,
20
+ data?: any,
21
+ afterSubmit?: () => void,
22
+ headers?: Record<string, string>
23
+ afterAction?: string[],
24
+ }
25
+
26
+
27
+ export default function (props: FormSchema & {
28
+ actions?: FormActionType[]
29
+ metaTitle?: string,
30
+ columns?: ProFormColumnsType[],
31
+ submitRequest?: SubmitRequestType,
32
+ extraRenderValues?: Record<string, any>,
33
+ }) {
34
+
35
+ const [columns, setColumns] = useState<ProFormColumnsType[]>([])
36
+ const formRef = useRef<ProFormInstance>()
37
+ const [initialized, setInitialized] = useState(false)
38
+ const [loading, setLoading] = useState(false)
39
+ const hiddenField = useRef<Record<string, any>>({})
40
+
41
+ useEffect(() => {
42
+ setColumns((cloneDeep(props.columns)?.map((c: ProFormColumnsType & {
43
+ formItemProps?: {
44
+ rules?: (Rule & {
45
+ customType?: string
46
+ })[]
47
+ }
48
+ }) => {
49
+ // rules
50
+ if (!c.formItemProps) {
51
+ c.formItemProps = {}
52
+ }
53
+ if (!c.formItemProps?.rules) {
54
+ c.formItemProps.rules = []
55
+ }
56
+ c.formItemProps.rules = c.formItemProps.rules.map(rule => {
57
+ if (rule.customType && customRule[rule.customType]) {
58
+ rule.validator = customRule[rule.customType]
59
+ }
60
+ return rule
61
+ })
62
+
63
+ // hideInForm时增加
64
+ if (c.hideInForm) {
65
+ hiddenField.current[c.dataIndex as string] = props.initialValues?.[c.dataIndex as string]
66
+ return null
67
+ }
68
+
69
+ // item render
70
+ const formItemComponent = 'Column.' + upperFirst(c.valueType as string)
71
+ if (container.check(formItemComponent)) {
72
+ const Component = lazy(container.get(formItemComponent))
73
+ c.renderFormItem = (schema, config, form) =>
74
+ <Suspense fallback={<Spin/>}>
75
+ <Component config={config}
76
+ form={form}
77
+ fieldProps={c.fieldProps}
78
+ key={c.title as string}
79
+ rules={c.formItemProps?.rules}
80
+ dataIndex={c.dataIndex}
81
+ ></Component>
82
+ </Suspense>
83
+ }
84
+ // readonly render
85
+ const readonlyComponent = 'Column.Readonly.' + upperFirst(c.valueType as string)
86
+ if (container.check(readonlyComponent)) {
87
+ const Component = lazy(container.get(readonlyComponent))
88
+ c.render = (dom, entity, index, action, schema) =>
89
+ <Suspense fallback={<Spin/>}>
90
+ <Component key={c.title as string}
91
+ entity={entity}
92
+ index={index}
93
+ action={action}
94
+ schema={schema}
95
+ dom={dom}
96
+ ></Component>
97
+ </Suspense>
98
+ }
99
+
100
+ commonHandler(c)
101
+ if (container.schemaHandler[c.valueType as string]) {
102
+ return container.schemaHandler[c.valueType as string](c)
103
+ }
104
+
105
+ return c
106
+ }).filter(c => !!c) || []) as ProFormColumnsType[])
107
+
108
+ setInitialized(true)
109
+ }, []);
110
+
111
+ const modalContext = useContext(ModalContext)
112
+ const tableContext = useContext(TableContext)
113
+
114
+ const handleAfterAction = async (req?: SubmitRequestType) => {
115
+ if (req?.afterAction?.includes('tableReload')) {
116
+ if (modalContext.contexts) {
117
+ modalContext.setAfterClose(() => {
118
+ modalContext.contexts?.tableContext?.actionRef.reload()
119
+ })
120
+ }
121
+ if (tableContext.actionRef) {
122
+ await tableContext.actionRef.reload()
123
+ }
124
+ }
125
+ if (req?.afterAction?.includes('closeModal') && modalContext.inModal) {
126
+ modalContext.closeModal()
127
+ }
128
+ }
129
+
130
+ const onFinish = async (values: any) => {
131
+ if (props.submitRequest) {
132
+ setLoading(true)
133
+ try {
134
+ await http({
135
+ method: props.submitRequest.method,
136
+ url: props.submitRequest.url,
137
+ data: Object.assign({}, hiddenField.current, props.submitRequest.data, values),
138
+ headers: props.submitRequest.headers,
139
+ })
140
+
141
+ handleAfterAction(props.submitRequest)
142
+ } finally {
143
+ setLoading(false)
144
+ }
145
+ }
146
+ }
147
+
148
+ return <>
149
+ <Row justify={'center'}>
150
+ <Col sm={24} md={22} lg={20}>
151
+ <FormContext.Provider value={{
152
+ formRef: formRef,
153
+ extraRenderValues: props.extraRenderValues,
154
+ }}>
155
+ {!initialized
156
+ ? <ProSkeleton type={"list"} list={2}></ProSkeleton>
157
+ : <BetaSchemaForm columns={columns}
158
+ colProps={props.colProps}
159
+ readonly={props.readonly}
160
+ grid={true}
161
+ loading={loading}
162
+ formRef={formRef}
163
+ initialValues={props.initialValues}
164
+ onFinish={onFinish}
165
+ submitter={{
166
+ render: () => [
167
+ <Actions key={'actions'} loading={loading}
168
+ actions={props.actions}></Actions>
169
+ ]
170
+ }}
171
+ ></BetaSchemaForm>
172
+ }
173
+ </FormContext.Provider>
174
+ </Col>
175
+ </Row>
176
+ </>
171
177
  }
@@ -1,8 +1,9 @@
1
- import {ProFormInstance} from "@ant-design/pro-components";
2
- import {createContext, MutableRefObject} from "react";
3
-
4
- export type FormContextProps = {
5
- formRef?: MutableRefObject<ProFormInstance | undefined>,
6
- }
7
-
1
+ import {ProFormInstance} from "@ant-design/pro-components";
2
+ import {createContext, MutableRefObject} from "react";
3
+
4
+ export type FormContextProps = {
5
+ formRef?: MutableRefObject<ProFormInstance | undefined>,
6
+ extraRenderValues?: Record<string, any>
7
+ }
8
+
8
9
  export const FormContext = createContext<FormContextProps>({})
@@ -0,0 +1,252 @@
1
+ import {MenuDataItem, PageContainer, ProConfigProvider, ProLayout} from "@ant-design/pro-components";
2
+ import {Button, Dropdown, Menu, Space} from "antd";
3
+ import type {LayoutProps} from "../LayoutContext";
4
+ import {LayoutContext} from "../LayoutContext";
5
+ import {useEffect, useRef, useState} from "react";
6
+ import {usePage} from "@inertiajs/react";
7
+ import {routerNavigateTo} from "../../lib/helpers";
8
+ import {MenuInfo} from "rc-menu/lib/interface";
9
+ import http from "../../lib/http";
10
+ // @ts-ignore
11
+ import {Route} from '@ant-design/pro-layout/lib/typing';
12
+ import {assign} from "lodash";
13
+ import {MoonOutlined, SunOutlined} from "@ant-design/icons";
14
+
15
+ export default function ({children, pageTitle, siteTitle}: {
16
+ children: React.ReactNode,
17
+ pageTitle: string,
18
+ siteTitle: string,
19
+ }) {
20
+
21
+ const pageProps = usePage<{
22
+ layoutProps: LayoutProps,
23
+ }>().props
24
+
25
+ const layoutProps = pageProps.layoutProps
26
+ const contentRef = useRef<HTMLDivElement>(null)
27
+
28
+ const [props, setProps] = useState<LayoutProps>({
29
+ title: '',
30
+ metaTitle: '',
31
+ topMenuActiveKey: '',
32
+ menuActiveKey: '',
33
+ loading: false,
34
+ topMenu: [],
35
+ menuList: [],
36
+ logo: '',
37
+ userMenu: [],
38
+ })
39
+ const [theme, setTheme] = useState<'light' | 'realDark'>('light')
40
+
41
+ const assignProps = (newProps: LayoutProps) => {
42
+ setProps(assign(props, newProps))
43
+ }
44
+
45
+ const headerContentRender = () => {
46
+ return <>
47
+ <Menu items={props.topMenu}
48
+ mode={'horizontal'}
49
+ activeKey={props.topMenuActiveKey}
50
+ />
51
+ </>
52
+ }
53
+
54
+ const [openKeys, setOpenKeys] = useState<string[]>([])
55
+ const [route, setRoute] = useState<Route>()
56
+
57
+ useEffect(() => {
58
+ function findKeyPath(key: string, list: MenuDataItem[]): string[] {
59
+ for (let i = 0; i < list.length; i++) {
60
+ const item = list[i];
61
+ if (item.key === key) {
62
+ return [item.key]
63
+ } else if (item.children?.length) {
64
+ const path = findKeyPath(key, item.children)
65
+ if (path?.length) {
66
+ return [item.key as string, ...path]
67
+ }
68
+ }
69
+ }
70
+ return []
71
+ }
72
+
73
+ if (!props.menuActiveKey) {
74
+ return
75
+ }
76
+ setOpenKeys(findKeyPath(props.menuActiveKey, props.menuList || []))
77
+
78
+ }, [props.menuActiveKey]);
79
+
80
+ useEffect(() => {
81
+ if (pageProps.layoutProps?.menuActiveKey) {
82
+ assignProps({
83
+ menuActiveKey: pageProps.layoutProps.menuActiveKey
84
+ })
85
+ }
86
+
87
+ const title = props.title || layoutProps.title
88
+ if (pageProps.layoutProps?.metaTitle) {
89
+ assignProps({
90
+ metaTitle: pageProps.layoutProps.metaTitle + ' | ' + title
91
+ })
92
+ } else {
93
+ assignProps({
94
+ metaTitle: title
95
+ })
96
+ }
97
+ }, [pageProps.layoutProps]);
98
+
99
+
100
+ useEffect(() => {
101
+
102
+ setProps({
103
+ title: layoutProps.title || '',
104
+ metaTitle: '',
105
+ topMenuActiveKey: layoutProps.topMenuActiveKey,
106
+ menuActiveKey: layoutProps.menuActiveKey,
107
+ loading: false,
108
+ topMenu: layoutProps.topMenu,
109
+ menuList: layoutProps.menuList,
110
+ logo: layoutProps.logo,
111
+ userMenu: layoutProps.userMenu,
112
+ })
113
+
114
+ const r = {
115
+ key: '/',
116
+ routes: layoutProps.menuList?.map(menu => {
117
+ return {
118
+ name: menu.name,
119
+ key: menu.key,
120
+ children: menu.children?.map(child => {
121
+ return {
122
+ name: child.name,
123
+ key: child.key
124
+ }
125
+ })
126
+ }
127
+ })
128
+ }
129
+
130
+ setRoute(r)
131
+
132
+ // 设置内容高度
133
+ function onResize() {
134
+ if (contentRef.current) {
135
+ contentRef.current.style.minHeight = Math.max(window.innerHeight - 200, 200) + 'px'
136
+ }
137
+ }
138
+
139
+ onResize()
140
+ window.addEventListener('resize', onResize)
141
+ return () => {
142
+ window.removeEventListener('resize', onResize)
143
+ }
144
+
145
+ }, [])
146
+
147
+
148
+ const onMenuClick = (info: MenuInfo) => {
149
+ const keyPath = info.keyPath.reverse()
150
+ let menu: MenuDataItem | undefined = props.menuList?.find(menu => menu.key === keyPath[0]);
151
+ for (let i = 1; i < keyPath.length; i++) {
152
+ menu = menu?.children?.find(m => m.key === keyPath[i])
153
+ }
154
+ if (menu?.path) {
155
+ assignProps({
156
+ loading: true
157
+ })
158
+ routerNavigateTo(menu.path, {
159
+ onSuccess() {
160
+ assignProps({
161
+ menuActiveKey: info.key
162
+ })
163
+ },
164
+ onFinish() {
165
+ assignProps({
166
+ loading: false
167
+ })
168
+ }
169
+ })
170
+ }
171
+ }
172
+
173
+ const actionsRender = () => <>
174
+ <Space>
175
+ <Button type={'text'} onClick={() => {
176
+ setTheme(theme === 'light' ? 'realDark' : 'light')
177
+ }}>
178
+ {theme === 'realDark' ? <MoonOutlined/> : <SunOutlined/>}
179
+ </Button>
180
+ </Space>
181
+ </>
182
+
183
+ return <>
184
+ <LayoutContext.Provider value={{
185
+ assignProps,
186
+ props,
187
+ }}>
188
+ <ProConfigProvider dark={theme === 'realDark'}>
189
+ <ProLayout title={siteTitle}
190
+ loading={props.loading}
191
+ layout="mix"
192
+ actionsRender={actionsRender}
193
+ route={route}
194
+ fixSiderbar={true}
195
+ logo={props.logo}
196
+ headerContentRender={headerContentRender}
197
+ pageTitleRender={p => `${pageTitle} | ${siteTitle} 后台管理`}
198
+ footerRender={() => <>
199
+ <Space>
200
+ <a href="https://www.quansitech.com/" target={'_blank'}>全思科技</a>
201
+ <a href="https://github.com/quansitech/" target={'_blank'}>Github</a>
202
+ </Space>
203
+ </>}
204
+ avatarProps={{
205
+ title: 'admin',
206
+ render(p, dom) {
207
+ return <>
208
+ <Dropdown menu={{
209
+ items: props.userMenu?.map(menu => {
210
+ return {
211
+ label: menu.title,
212
+ key: menu.url,
213
+ onClick() {
214
+ switch (menu.type) {
215
+ case 'open':
216
+ window.open(menu.url)
217
+ break;
218
+ case 'nav':
219
+ routerNavigateTo(menu.url)
220
+ break
221
+ case 'ajax':
222
+ http.get(menu.url).then(() => {
223
+ window.location.reload()
224
+ })
225
+ break
226
+ }
227
+ }
228
+ }
229
+ }) || [],
230
+ }}>
231
+ {dom}
232
+ </Dropdown>
233
+ </>
234
+ }
235
+ }}
236
+ menuProps={{
237
+ activeKey: props.menuActiveKey as string,
238
+ selectedKeys: [props.menuActiveKey as string],
239
+ openKeys: openKeys,
240
+ onClick: onMenuClick,
241
+ onOpenChange: setOpenKeys
242
+ }}
243
+ >
244
+
245
+ <PageContainer title={pageTitle}>
246
+ <div ref={contentRef}>{children}</div>
247
+ </PageContainer>
248
+ </ProLayout>
249
+ </ProConfigProvider>
250
+ </LayoutContext.Provider>
251
+ </>
252
+ }