@quansitech/antd-admin 1.0.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 (38) hide show
  1. package/components/Column/Cascader.tsx +79 -0
  2. package/components/Column/File.tsx +168 -0
  3. package/components/Column/Image.tsx +77 -0
  4. package/components/Column/Readonly/Cascader.tsx +51 -0
  5. package/components/Column/Readonly/File.tsx +54 -0
  6. package/components/Column/Readonly/Image.tsx +78 -0
  7. package/components/Column/Readonly/Option.tsx +58 -0
  8. package/components/Column/Readonly/types.d.ts +9 -0
  9. package/components/Column/Ueditor.tsx +314 -0
  10. package/components/Column/types.d.ts +29 -0
  11. package/components/Form/Action/Button.tsx +125 -0
  12. package/components/Form/Action/types.d.ts +5 -0
  13. package/components/Form/Actions.tsx +35 -0
  14. package/components/Form.tsx +171 -0
  15. package/components/FormContext.ts +8 -0
  16. package/components/Layout.tsx +237 -0
  17. package/components/LayoutContext.ts +26 -0
  18. package/components/ModalContext.ts +16 -0
  19. package/components/Table/Action/Button.tsx +89 -0
  20. package/components/Table/Action/StartEditable.tsx +59 -0
  21. package/components/Table/Action/types.d.ts +7 -0
  22. package/components/Table/Option/Link.tsx +68 -0
  23. package/components/Table/Option/types.d.ts +5 -0
  24. package/components/Table/ToolbarActions.tsx +39 -0
  25. package/components/Table.scss +7 -0
  26. package/components/Table.tsx +279 -0
  27. package/components/TableContext.ts +14 -0
  28. package/components/Tabs.tsx +72 -0
  29. package/components/types.d.ts +0 -0
  30. package/lib/container.ts +82 -0
  31. package/lib/customRule.ts +10 -0
  32. package/lib/global.ts +11 -0
  33. package/lib/helpers.tsx +150 -0
  34. package/lib/http.ts +74 -0
  35. package/lib/schemaHandler.ts +122 -0
  36. package/lib/upload.ts +177 -0
  37. package/package.json +35 -0
  38. package/readme.md +128 -0
@@ -0,0 +1,237 @@
1
+ import {MenuDataItem, PageContainer, ProLayout} from "@ant-design/pro-components";
2
+ import {App, 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 global from "../lib/global";
13
+ import assign from "lodash/assign";
14
+
15
+ export default function ({children}: {
16
+ children: React.ReactNode
17
+ }) {
18
+
19
+ const pageProps = usePage<{
20
+ layoutProps: LayoutProps,
21
+ }>().props
22
+
23
+ const layoutProps = pageProps.layoutProps
24
+ const {modal, notification, message} = App.useApp()
25
+ const contentRef = useRef<HTMLDivElement>(null)
26
+
27
+ const [props, setProps] = useState<LayoutProps>({
28
+ title: '',
29
+ metaTitle: '',
30
+ topMenuActiveKey: '',
31
+ menuActiveKey: '',
32
+ loading: false,
33
+ topMenu: [],
34
+ menuList: [],
35
+ logo: '',
36
+ userMenu: [],
37
+ })
38
+
39
+ const assignProps = (newProps: LayoutProps) => {
40
+ setProps(assign(props, newProps))
41
+ }
42
+
43
+ const headerContentRender = () => {
44
+ return <>
45
+ <Menu items={props.topMenu}
46
+ mode={'horizontal'}
47
+ activeKey={props.topMenuActiveKey}
48
+ />
49
+ </>
50
+ }
51
+
52
+ const [openKeys, setOpenKeys] = useState<string[]>([])
53
+ const [pageTitle, setPageTitle] = useState<string>('')
54
+ const [route, setRoute] = useState<Route>()
55
+
56
+ useEffect(() => {
57
+ function findKeyPath(key: string, list: MenuDataItem[]): string[] {
58
+ for (let i = 0; i < list.length; i++) {
59
+ const item = list[i];
60
+ if (item.key === key) {
61
+ return [item.key]
62
+ } else if (item.children?.length) {
63
+ const path = findKeyPath(key, item.children)
64
+ if (path?.length) {
65
+ return [item.key as string, ...path]
66
+ }
67
+ }
68
+ }
69
+ return []
70
+ }
71
+
72
+ if (!props.menuActiveKey) {
73
+ return
74
+ }
75
+ setOpenKeys(findKeyPath(props.menuActiveKey, props.menuList || []))
76
+
77
+ }, [props.menuActiveKey]);
78
+
79
+ useEffect(() => {
80
+ if (pageProps.layoutProps?.menuActiveKey) {
81
+ assignProps({
82
+ menuActiveKey: pageProps.layoutProps.menuActiveKey
83
+ })
84
+ }
85
+
86
+ const title = props.title || layoutProps.title
87
+ if (pageProps.layoutProps?.metaTitle) {
88
+ setPageTitle(pageProps.layoutProps.metaTitle + ' - ' + title)
89
+ assignProps({
90
+ metaTitle: pageProps.layoutProps.metaTitle + ' - ' + title
91
+ })
92
+ } else {
93
+ setPageTitle(title as string)
94
+ assignProps({
95
+ metaTitle: title
96
+ })
97
+ }
98
+ }, [pageProps]);
99
+
100
+
101
+ useEffect(() => {
102
+ console.log(pageProps)
103
+
104
+ global.modal = modal
105
+ global.notification = notification
106
+ global.message = message
107
+
108
+ setProps({
109
+ title: layoutProps.title || '',
110
+ metaTitle: '',
111
+ topMenuActiveKey: layoutProps.topMenuActiveKey,
112
+ menuActiveKey: layoutProps.menuActiveKey,
113
+ loading: false,
114
+ topMenu: layoutProps.topMenu,
115
+ menuList: layoutProps.menuList,
116
+ logo: layoutProps.logo,
117
+ userMenu: layoutProps.userMenu,
118
+ })
119
+
120
+ const r = {
121
+ key: '/',
122
+ routes: layoutProps.menuList?.map(menu => {
123
+ return {
124
+ name: menu.name,
125
+ key: menu.key,
126
+ children: menu.children?.map(child => {
127
+ return {
128
+ name: child.name,
129
+ key: child.key
130
+ }
131
+ })
132
+ }
133
+ })
134
+ }
135
+
136
+ setRoute(r)
137
+
138
+ // 设置内容高度
139
+ if (contentRef.current) {
140
+ contentRef.current.style.minHeight = (window.innerHeight - 200) + 'px'
141
+ }
142
+
143
+ }, [])
144
+
145
+
146
+ const onMenuClick = (info: MenuInfo) => {
147
+ const keyPath = info.keyPath.reverse()
148
+ let menu: MenuDataItem | undefined = props.menuList?.find(menu => menu.key === keyPath[0]);
149
+ for (let i = 1; i < keyPath.length; i++) {
150
+ menu = menu?.children?.find(m => m.key === keyPath[i])
151
+ }
152
+ if (menu?.path) {
153
+ assignProps({
154
+ loading: true
155
+ })
156
+ routerNavigateTo(menu.path, {
157
+ onSuccess() {
158
+ assignProps({
159
+ menuActiveKey: info.key
160
+ })
161
+ },
162
+ onFinish() {
163
+ assignProps({
164
+ loading: false
165
+ })
166
+ }
167
+ })
168
+ }
169
+ }
170
+
171
+ return <>
172
+ <LayoutContext.Provider value={{
173
+ assignProps,
174
+ props,
175
+ }}>
176
+ <ProLayout title={props.title}
177
+ loading={props.loading}
178
+ layout="mix"
179
+ route={route}
180
+ fixSiderbar={true}
181
+ logo={props.logo}
182
+ headerContentRender={headerContentRender}
183
+ pageTitleRender={p => pageTitle}
184
+ footerRender={() => <>
185
+ <Space>
186
+ <a href="https://www.quansitech.com/" target={'_blank'}>全思科技</a>
187
+ <a href="https://github.com/quansitech/" target={'_blank'}>Github</a>
188
+ </Space>
189
+ </>}
190
+ avatarProps={{
191
+ title: 'admin',
192
+ render(p, dom) {
193
+ return <>
194
+ <Dropdown menu={{
195
+ items: props.userMenu?.map(menu => {
196
+ return {
197
+ label: menu.title,
198
+ key: menu.url,
199
+ onClick() {
200
+ switch (menu.type) {
201
+ case 'open':
202
+ window.open(menu.url)
203
+ break;
204
+ case 'nav':
205
+ routerNavigateTo(menu.url)
206
+ break
207
+ case 'ajax':
208
+ http.get(menu.url).then(() => {
209
+ window.location.reload()
210
+ })
211
+ break
212
+ }
213
+ }
214
+ }
215
+ }) || [],
216
+ }}>
217
+ {dom}
218
+ </Dropdown>
219
+ </>
220
+ }
221
+ }}
222
+ menuProps={{
223
+ activeKey: props.menuActiveKey as string,
224
+ selectedKeys: [props.menuActiveKey as string],
225
+ openKeys: openKeys,
226
+ onClick: onMenuClick,
227
+ onOpenChange: setOpenKeys
228
+ }}
229
+ >
230
+
231
+ <PageContainer title={props.metaTitle}>
232
+ <div ref={contentRef}>{children}</div>
233
+ </PageContainer>
234
+ </ProLayout>
235
+ </LayoutContext.Provider>
236
+ </>
237
+ }
@@ -0,0 +1,26 @@
1
+ import {createContext} from "react";
2
+ import {MenuDataItem} from "@ant-design/pro-components";
3
+
4
+ declare type LayoutContextValue = {
5
+ assignProps: (props: LayoutProps) => void,
6
+ props: LayoutProps,
7
+ }
8
+
9
+ export declare type LayoutProps = {
10
+ title?: string,
11
+ metaTitle?: string,
12
+ topMenuActiveKey?: string,
13
+ menuActiveKey?: string,
14
+ loading?: boolean,
15
+ topMenu?: { name: string, key: string }[],
16
+ menuList?: MenuDataItem[],
17
+ logo?: string,
18
+ userMenu?: {
19
+ title: string,
20
+ url: string,
21
+ type: string,
22
+
23
+ }[]
24
+ }
25
+
26
+ export const LayoutContext = createContext<LayoutContextValue>({} as LayoutContextValue)
@@ -0,0 +1,16 @@
1
+ import {createContext} from "react";
2
+
3
+ export type ModalContextProps = {
4
+ inModal: boolean,
5
+ closeModal: () => void,
6
+ contexts?: Record<string, any>
7
+ setAfterClose(callback: () => void): void
8
+ }
9
+
10
+ export const ModalContext = createContext<ModalContextProps>({
11
+ inModal: false,
12
+ closeModal: () => {
13
+ },
14
+ setAfterClose: (fn: Function) => {
15
+ },
16
+ })
@@ -0,0 +1,89 @@
1
+ import {Button, Popconfirm} from "antd";
2
+ import {useContext, useEffect, useState} from "react";
3
+ import {TableContext} from "../../TableContext";
4
+ import http from "../../../lib/http";
5
+ import {modal, routerNavigateTo} from "../../../lib/helpers";
6
+ import {TableActionProps} from "./types";
7
+
8
+ export default function (props: TableActionProps & {
9
+ props: Record<string, any>,
10
+ link?: {
11
+ url: string,
12
+ },
13
+ request?: RequestOptions,
14
+ modal?: ModalOptions,
15
+ }) {
16
+ const tableContext = useContext(TableContext)
17
+
18
+ const onClick = async () => {
19
+ const rowKey = tableContext.getTableProps().rowKey
20
+ if (props.link) {
21
+ routerNavigateTo(props.link.url)
22
+ return
23
+ }
24
+
25
+ if (props.request) {
26
+ setLoading(true)
27
+ const data = props.request.data || {}
28
+ if (props.relateSelection) {
29
+ data.selection = props.selectedRows?.map(item => item[rowKey])
30
+ for (const key in data) {
31
+ if (typeof data[key] !== 'string') {
32
+ continue
33
+ }
34
+ const matches = data[key].match(/^__(\w+)__$/)
35
+ if (!matches) {
36
+ continue
37
+ }
38
+ data[key] = props.selectedRows?.map(item => item[matches[1]])
39
+ }
40
+ }
41
+ try {
42
+ await http({
43
+ url: props.request.url,
44
+ method: props.request.method,
45
+ headers: props.request.headers || {},
46
+ data: data,
47
+ })
48
+ } finally {
49
+ setLoading(false)
50
+ }
51
+ return
52
+ }
53
+
54
+ if (props.modal) {
55
+ await modal(props.modal)
56
+ return
57
+ }
58
+
59
+ await tableContext.actionRef?.reload()
60
+ }
61
+
62
+ const [loading, setLoading] = useState(false)
63
+
64
+ const [disabled, setDisabled] = useState(props.props.disabled)
65
+
66
+ useEffect(() => {
67
+ if (!props.relateSelection) {
68
+ return
69
+ }
70
+ setDisabled(props.selectedRows?.length === 0)
71
+
72
+ }, [props.selectedRows]);
73
+
74
+
75
+ const ButtonComponent = () => {
76
+ if (props.request?.confirm) {
77
+ return <Popconfirm title={props.request.confirm} onConfirm={onClick}>
78
+ <Button loading={loading} {...props.props} disabled={disabled}>{props.title}</Button>
79
+ </Popconfirm>
80
+ } else {
81
+ return <Button loading={loading} onClick={onClick} {...props.props}
82
+ disabled={disabled}>{props.title}</Button>
83
+ }
84
+ }
85
+
86
+ return <>
87
+ <ButtonComponent></ButtonComponent>
88
+ </>
89
+ }
@@ -0,0 +1,59 @@
1
+ import {Button, ButtonProps, Space} from "antd";
2
+ import {TableContext} from "../../TableContext";
3
+ import {useContext, useState} from "react";
4
+ import http from "../../../lib/http";
5
+ import {TableActionProps} from "./types";
6
+
7
+ export default function (props: TableActionProps & {
8
+ props: ButtonProps,
9
+ saveRequest: {
10
+ url: string,
11
+ method: string,
12
+ },
13
+ }) {
14
+ const tableContext = useContext(TableContext)
15
+
16
+ const onStartClick = () => {
17
+ const rowKey = tableContext.getTableProps().rowKey
18
+ tableContext.getTableProps().dataSource.map(item => {
19
+ tableContext.actionRef?.startEditable(item[rowKey], item)
20
+ })
21
+ }
22
+
23
+ const onCancelClick = () => {
24
+ const rowKey = tableContext.getTableProps().rowKey
25
+ tableContext.getTableProps().dataSource.map(item => {
26
+ tableContext.actionRef?.cancelEditable(item[rowKey])
27
+ })
28
+ }
29
+
30
+ const [loading, setLoading] = useState(false)
31
+
32
+ const onSaveClick = async () => {
33
+ setLoading(true)
34
+
35
+ try {
36
+ await http({
37
+ method: props.saveRequest.method,
38
+ url: props.saveRequest.url,
39
+ data: tableContext.getEditedValues(),
40
+ })
41
+
42
+ await tableContext.actionRef?.reload()
43
+ } finally {
44
+ setLoading(false)
45
+ }
46
+ onCancelClick()
47
+ }
48
+
49
+ return <>
50
+ {tableContext.editableKeys.length > 0
51
+ ?
52
+ <Space>
53
+ <Button loading={loading} type={'primary'} onClick={onSaveClick}>保存</Button>
54
+ <Button danger={true} onClick={onCancelClick}>取消</Button>
55
+ </Space>
56
+ : <Button {...props.props} onClick={onStartClick}>{props.title}</Button>
57
+ }
58
+ </>
59
+ }
@@ -0,0 +1,7 @@
1
+ export type TableActionProps = {
2
+ type: string,
3
+ title: string,
4
+ props?: Record<string, any>,
5
+ relateSelection?: boolean,
6
+ selectedRows?: any[],
7
+ }
@@ -0,0 +1,68 @@
1
+ import React, {useContext, useState} from "react";
2
+ import {modal, replaceParams, replaceUrl, routerNavigateTo} from "../../../lib/helpers";
3
+ import {Button, Popconfirm, Spin} from "antd";
4
+ import {TableContext} from "../../TableContext";
5
+ import http from "../../../lib/http";
6
+ import {TableColumnOptionProps} from "./types";
7
+
8
+ type Props = TableColumnOptionProps & {
9
+ href?: string,
10
+ request?: RequestOptions,
11
+ modal?: ModalOptions,
12
+ danger?: boolean
13
+ }
14
+
15
+ export default function (props: Props) {
16
+
17
+ const [loading, setLoading] = useState(false)
18
+ const tableContext = useContext(TableContext)
19
+
20
+ const onClick = async (e: any) => {
21
+ setLoading(true)
22
+ try {
23
+ if (props.href) {
24
+ routerNavigateTo(replaceUrl(props.href, props.record))
25
+ return
26
+ }
27
+ if (props.request) {
28
+ await http({
29
+ method: props.request.method,
30
+ url: replaceUrl(props.request.url, props.record),
31
+ headers: props.request.headers || {},
32
+ data: props.request.data ? replaceParams(props.request.data, props.record) : null,
33
+ })
34
+
35
+ await tableContext.actionRef?.reload()
36
+ }
37
+ if (props.modal) {
38
+ let url
39
+ if (props.modal.content.url) {
40
+ url = replaceUrl(props.modal.content.url, props.record)
41
+ }
42
+ await modal({
43
+ ...props.modal,
44
+ contexts: {
45
+ tableContext,
46
+ },
47
+ content: {
48
+ ...props.modal.content,
49
+ url,
50
+ },
51
+ })
52
+ }
53
+ } finally {
54
+ setLoading(false)
55
+ }
56
+ }
57
+
58
+ return <>
59
+ <Spin spinning={loading}>
60
+ {props.request?.confirm
61
+ ? <Popconfirm title={props.request?.confirm} onConfirm={onClick}>
62
+ <Button type={"link"} danger={props.danger} onClick={() => {
63
+ }}>{props.title}</Button>
64
+ </Popconfirm> : <Button type={"link"} danger={props.danger} onClick={onClick}>{props.title}</Button>
65
+ }
66
+ </Spin>
67
+ </>
68
+ }
@@ -0,0 +1,5 @@
1
+ export type TableColumnOptionProps = {
2
+ type: string,
3
+ title: string,
4
+ record: Record<string, any>,
5
+ }
@@ -0,0 +1,39 @@
1
+ import {lazy, useEffect, useState} from "react";
2
+ import container from "../../lib/container";
3
+ import {ReactComponentLike} from "prop-types";
4
+ import {Space} from "antd";
5
+ import {TableActionProps} from "./Action/types";
6
+ import upperFirst from "lodash/upperFirst";
7
+
8
+ export default function ({
9
+ actions,
10
+ selectedRows,
11
+ }: {
12
+ actions: TableActionProps[],
13
+ selectedRows?: any[]
14
+ }) {
15
+ const [components, setComponents] = useState<{
16
+ Component: ReactComponentLike,
17
+ props: TableActionProps,
18
+ }[]>([])
19
+
20
+ useEffect(() => {
21
+ setComponents(actions.map(a => {
22
+ return {
23
+ Component: lazy(container.get('Table.Action.' + upperFirst(a.type))),
24
+ props: {
25
+ ...a,
26
+ },
27
+ }
28
+ }))
29
+
30
+ }, []);
31
+
32
+ return <>
33
+ <Space wrap={true}>
34
+ {components.map(c => (
35
+ <c.Component key={c.props.title} {...c.props} selectedRows={selectedRows}></c.Component>
36
+ ))}
37
+ </Space>
38
+ </>
39
+ }
@@ -0,0 +1,7 @@
1
+ //.qs-antd-table {
2
+ // table thead {
3
+ // position: sticky;
4
+ // top: 50px;
5
+ // z-index: 1;
6
+ // }
7
+ //}