@wzyjs/uis 0.3.26 → 0.3.28

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wzyjs/uis",
3
- "version": "0.3.26",
3
+ "version": "0.3.28",
4
4
  "description": "description",
5
5
  "author": "wzy",
6
6
  "sideEffects": false,
@@ -27,6 +27,7 @@
27
27
  "prismjs": "^1.29.0",
28
28
  "react": "^19.0.0",
29
29
  "react-beautiful-dnd": "^13.1.1",
30
+ "react-grid-layout": "^1.5.1",
30
31
  "react-json-view": "^1.21.3",
31
32
  "react-markdown": "^10.1.0",
32
33
  "react-syntax-highlighter": "^15.5.0",
@@ -42,7 +43,11 @@
42
43
  "@wzyjs/utils": "^0.2.37",
43
44
  "antd": "^6.0.0"
44
45
  },
45
- "gitHead": "cf770d4d9e94b1000eab84ce34f91a4e6ee912f6",
46
+ "devDependencies": {
47
+ "@types/js-beautify": "^1.14.3",
48
+ "@types/react-grid-layout": "1.3.5"
49
+ },
50
+ "gitHead": "cfb708a071a9f7f0e2c819760c95427d7196623b",
46
51
  "publishConfig": {
47
52
  "access": "public"
48
53
  }
@@ -0,0 +1,135 @@
1
+ 'use client'
2
+
3
+ import React from 'react'
4
+ import { Tabs, type TabsProps } from 'antd'
5
+
6
+ type SyncMode = 'replace' | 'push'
7
+
8
+ export interface TabsProProps extends TabsProps {
9
+ syncToUrl?: boolean
10
+ queryKey?: string
11
+ historyMode?: SyncMode
12
+ }
13
+
14
+ const getItemKeys = (items?: TabsProps['items']): string[] => {
15
+ return (items || [])
16
+ .map(item => item?.key)
17
+ .filter((key): key is string => typeof key === 'string' && !!key)
18
+ }
19
+
20
+ const getUrlTabKey = (queryKey: string): string | undefined => {
21
+ if (typeof window === 'undefined') {
22
+ return undefined
23
+ }
24
+
25
+ const value = new URLSearchParams(window.location.search).get(queryKey)
26
+ return value || undefined
27
+ }
28
+
29
+ const updateUrlTabKey = (queryKey: string, key: string, mode: SyncMode) => {
30
+ if (typeof window === 'undefined') {
31
+ return
32
+ }
33
+
34
+ const url = new URL(window.location.href)
35
+ url.searchParams.set(queryKey, key)
36
+
37
+ const nextUrl = `${url.pathname}${url.search}${url.hash}`
38
+ if (mode === 'push') {
39
+ window.history.pushState(window.history.state, '', nextUrl)
40
+ return
41
+ }
42
+
43
+ window.history.replaceState(window.history.state, '', nextUrl)
44
+ }
45
+
46
+ export const TabsPro = (props: TabsProProps) => {
47
+ const {
48
+ syncToUrl = false,
49
+ queryKey = 'tab',
50
+ historyMode = 'replace',
51
+ activeKey,
52
+ defaultActiveKey,
53
+ items,
54
+ onChange,
55
+ ...rest
56
+ } = props
57
+
58
+ const itemKeys = React.useMemo(() => getItemKeys(items), [items])
59
+
60
+ const initialKey = React.useMemo(() => {
61
+ const urlKey = syncToUrl ? getUrlTabKey(queryKey) : undefined
62
+ if (urlKey && itemKeys.includes(urlKey)) {
63
+ return urlKey
64
+ }
65
+
66
+ if (typeof defaultActiveKey === 'string' && itemKeys.includes(defaultActiveKey)) {
67
+ return defaultActiveKey
68
+ }
69
+
70
+ return itemKeys[0]
71
+ }, [defaultActiveKey, itemKeys, queryKey, syncToUrl])
72
+
73
+ const [innerActiveKey, setInnerActiveKey] = React.useState<string | undefined>(initialKey)
74
+
75
+ const mergedActiveKey = typeof activeKey === 'string' ? activeKey : innerActiveKey
76
+
77
+ React.useEffect(() => {
78
+ if (typeof activeKey === 'string') {
79
+ return
80
+ }
81
+
82
+ setInnerActiveKey(prev => {
83
+ if (prev && itemKeys.includes(prev)) {
84
+ return prev
85
+ }
86
+ return initialKey
87
+ })
88
+ }, [activeKey, initialKey, itemKeys])
89
+
90
+ React.useEffect(() => {
91
+ if (!syncToUrl || !mergedActiveKey) {
92
+ return
93
+ }
94
+
95
+ updateUrlTabKey(queryKey, mergedActiveKey, historyMode)
96
+ }, [historyMode, mergedActiveKey, queryKey, syncToUrl])
97
+
98
+ React.useEffect(() => {
99
+ if (!syncToUrl) {
100
+ return
101
+ }
102
+
103
+ const onPopState = () => {
104
+ const urlKey = getUrlTabKey(queryKey)
105
+ if (!urlKey || !itemKeys.includes(urlKey)) {
106
+ return
107
+ }
108
+
109
+ if (typeof activeKey !== 'string') {
110
+ setInnerActiveKey(urlKey)
111
+ }
112
+
113
+ onChange?.(urlKey)
114
+ }
115
+
116
+ window.addEventListener('popstate', onPopState)
117
+ return () => {
118
+ window.removeEventListener('popstate', onPopState)
119
+ }
120
+ }, [activeKey, itemKeys, onChange, queryKey, syncToUrl])
121
+
122
+ return (
123
+ <Tabs
124
+ {...rest}
125
+ items={items}
126
+ activeKey={mergedActiveKey}
127
+ onChange={key => {
128
+ if (typeof activeKey !== 'string') {
129
+ setInnerActiveKey(key)
130
+ }
131
+ onChange?.(key)
132
+ }}
133
+ />
134
+ )
135
+ }
@@ -6,5 +6,6 @@ export * from './Image'
6
6
  export * from './Input'
7
7
  export * from './Radio'
8
8
  export * from './Space'
9
+ export * from './Tabs'
9
10
  export * from './Typography'
10
11
  export * from './Popconfirm'
@@ -1,13 +1,50 @@
1
+ 'use client'
2
+
3
+ import { useMemo, useState } from 'react'
1
4
  import { Button, type DrawerProps, type ModalProps } from 'antd'
2
5
  import { PlusOutlined, EditOutlined } from '@ant-design/icons'
3
6
  import { BetaSchemaForm } from '@ant-design/pro-components'
4
7
 
5
- import type { CreateUpdateProps } from '../../types'
8
+ import type { CreateUpdateProps, ProColumnsPro } from '../../types'
9
+
10
+ const isActionColumn = (column: ProColumnsPro) => (
11
+ column.valueType === 'option'
12
+ || column.key === 'option'
13
+ || column.key === 'action'
14
+ || column.title === '操作'
15
+ )
16
+
17
+ const getFormColumns = (columns: ProColumnsPro[]) => {
18
+ return columns
19
+ .filter(column => !column.hideInForm && !isActionColumn(column) && column.key !== '__drag_sort__')
20
+ .map((column) => {
21
+ const {
22
+ fixed,
23
+ sorter,
24
+ sortOrder,
25
+ defaultSortOrder,
26
+ filters,
27
+ onFilter,
28
+ onCell,
29
+ onHeaderCell,
30
+ ellipsis,
31
+ copyable,
32
+ render,
33
+ renderText,
34
+ width,
35
+ ...formColumn
36
+ } = column as ProColumnsPro & Record<string, unknown>
37
+
38
+ return formColumn as ProColumnsPro
39
+ })
40
+ }
6
41
 
7
42
  export const CreateUpdate = (props: CreateUpdateProps) => {
8
43
  const { crud, columns, config, record } = props
9
44
 
10
45
  const isEdit = !!record
46
+ const [open, setOpen] = useState(false)
47
+ const [formRendered, setFormRendered] = useState(false)
11
48
 
12
49
  const {
13
50
  mode = 'drawer',
@@ -19,20 +56,49 @@ export const CreateUpdate = (props: CreateUpdateProps) => {
19
56
  width,
20
57
  } = config
21
58
 
22
- const { drawerProps, modalProps, ...formSafeProps } = formProps as unknown as { drawerProps?: DrawerProps; modalProps?: ModalProps } & Record<string, unknown>
59
+ const {
60
+ drawerProps,
61
+ modalProps,
62
+ initialValues,
63
+ onOpenChange,
64
+ onVisibleChange,
65
+ open: _open,
66
+ visible: _visible,
67
+ ...formSafeProps
68
+ } = formProps as unknown as {
69
+ drawerProps?: DrawerProps
70
+ modalProps?: ModalProps
71
+ initialValues?: Record<string, unknown>
72
+ onOpenChange?: (open: boolean) => void
73
+ onVisibleChange?: (open: boolean) => void
74
+ open?: boolean
75
+ visible?: boolean
76
+ } & Record<string, unknown>
23
77
 
24
78
  const layoutType = mode === 'modal' ? 'ModalForm' : 'DrawerForm'
25
79
 
26
- // const finalDrawerProps: DrawerProps | undefined = layoutType === 'DrawerForm'
27
- // ? { forceRender: false, ...drawerProps }
28
- // : undefined
29
- //
30
- // const finalModalProps: ModalProps | undefined = layoutType === 'ModalForm'
31
- // ? { forceRender: false, ...modalProps }
32
- // : undefined
80
+ const formColumns = useMemo(() => getFormColumns(columns), [columns])
81
+
82
+ const handleOpenChange = (nextOpen: boolean) => {
83
+ setOpen(nextOpen)
84
+ if (nextOpen) {
85
+ setFormRendered(true)
86
+ }
87
+ onOpenChange?.(nextOpen)
88
+ onVisibleChange?.(nextOpen)
89
+ }
90
+
91
+ const openForm = (event: any) => {
92
+ buttonProps.onClick?.(event)
93
+ if (event.defaultPrevented) {
94
+ return
95
+ }
96
+ setFormRendered(true)
97
+ setOpen(true)
98
+ }
33
99
 
34
100
  const onFinish = async (values: Record<string, unknown>) => {
35
- const finalValues = transformData ? transformData(values, record) : values
101
+ const finalValues = transformData ? await transformData(values, record) : values
36
102
 
37
103
  if (isEdit) {
38
104
  await crud.updateState.mutateAsync(record.id, finalValues)
@@ -47,7 +113,6 @@ export const CreateUpdate = (props: CreateUpdateProps) => {
47
113
  return record.id
48
114
  }
49
115
 
50
- const initialValues: unknown = (formSafeProps as { initialValues?: unknown }).initialValues
51
116
  if (!initialValues) {
52
117
  return 'create'
53
118
  }
@@ -63,37 +128,52 @@ export const CreateUpdate = (props: CreateUpdateProps) => {
63
128
  }
64
129
  })()
65
130
 
66
- return (
67
- <BetaSchemaForm
68
- key={formKey}
69
- layoutType={layoutType as any}
70
- title={title}
71
- width={width}
72
- columns={columns as any}
73
- onFinish={onFinish}
74
- layout='horizontal'
75
- labelCol={{ span: 4 }}
76
- initialValues={{
77
- ...(record ?? {}),
78
- ...(coverData ? coverData(record) : {}),
79
- }}
80
- {...(drawerProps ? { drawerProps } : {})}
81
- {...(modalProps ? { modalProps } : {})}
82
- trigger={
83
- isEdit ? (
84
- <Button
85
- type='link'
86
- size='small'
87
- icon={<EditOutlined />}
88
- {...buttonProps}
89
- />
90
- ) : (
91
- <Button type='primary' icon={<PlusOutlined />} {...buttonProps}>
92
- 新建
93
- </Button>
94
- )
95
- }
96
- {...formSafeProps}
131
+ const finalInitialValues = {
132
+ ...(record ?? {}),
133
+ ...(initialValues ?? {}),
134
+ ...(coverData ? coverData(record) : {}),
135
+ }
136
+
137
+ const trigger = isEdit ? (
138
+ <Button
139
+ type='link'
140
+ size='small'
141
+ icon={<EditOutlined />}
142
+ {...buttonProps}
143
+ onClick={openForm}
97
144
  />
145
+ ) : (
146
+ <Button
147
+ type='primary'
148
+ icon={<PlusOutlined />}
149
+ {...buttonProps}
150
+ onClick={openForm}
151
+ >
152
+ 新建
153
+ </Button>
154
+ )
155
+
156
+ return (
157
+ <>
158
+ {trigger}
159
+ {formRendered ? (
160
+ <BetaSchemaForm
161
+ key={formKey}
162
+ layoutType={layoutType as any}
163
+ title={title}
164
+ width={width}
165
+ columns={formColumns as any}
166
+ onFinish={onFinish}
167
+ layout='horizontal'
168
+ labelCol={{ span: 4 }}
169
+ initialValues={finalInitialValues}
170
+ open={open}
171
+ onOpenChange={handleOpenChange}
172
+ {...(drawerProps ? { drawerProps } : {})}
173
+ {...(modalProps ? { modalProps } : {})}
174
+ {...formSafeProps}
175
+ />
176
+ ) : null}
177
+ </>
98
178
  )
99
179
  }
@@ -1,6 +1,9 @@
1
- import { type PropsWithChildren, useContext } from 'react'
1
+ 'use client'
2
+
3
+ import { type PropsWithChildren, useContext, useMemo } from 'react'
2
4
  import { Tag, Switch } from 'antd'
3
5
  import { ProProvider, ProFormSelect } from '@ant-design/pro-components'
6
+ import type { ProRenderFieldPropsType } from '@ant-design/pro-provider'
4
7
 
5
8
  import type { Crud } from '../../types'
6
9
 
@@ -19,52 +22,59 @@ export const Provider = (props: ProviderProps) => {
19
22
  }
20
23
  }
21
24
 
25
+ const valueTypeMap = useMemo<Record<string, ProRenderFieldPropsType>>(() => ({
26
+ ...defaultContext.valueTypeMap,
27
+ tags: {
28
+ renderFormItem: (_: unknown, props: any) => {
29
+ const { options, ...restFieldProps } = props.fieldProps || {}
30
+
31
+ let processedOptions = options
32
+
33
+ if (Array.isArray(options) && options.length > 0 && typeof options[0] === 'string') {
34
+ processedOptions = options.map((item: string) => ({
35
+ label: item,
36
+ value: item,
37
+ }))
38
+ }
39
+
40
+ return (
41
+ <ProFormSelect
42
+ placeholder='请输入标签'
43
+ mode='tags'
44
+ options={processedOptions}
45
+ {...restFieldProps}
46
+ />
47
+ )
48
+ },
49
+ render: (tags: string[]) => {
50
+ return (
51
+ <>
52
+ {tags?.map((tag: string) => (
53
+ <Tag key={tag}>{tag}</Tag>
54
+ ))}
55
+ </>
56
+ )
57
+ },
58
+ },
59
+ enabled: {
60
+ render: (enabled: boolean, props: any) => {
61
+ return (
62
+ <Switch
63
+ loading={crud?.updateState.isLoading}
64
+ checked={enabled}
65
+ onChange={enabled => onSwitchChange(props.record?.id, enabled)}
66
+ disabled={!crud?.updateState}
67
+ />
68
+ )
69
+ },
70
+ },
71
+ }), [crud?.updateState, defaultContext.valueTypeMap])
72
+
22
73
  return (
23
74
  <ProProvider.Provider
24
75
  value={{
25
76
  ...defaultContext,
26
- valueTypeMap: {
27
- tags: {
28
- renderFormItem: (_: unknown, props) => {
29
- const { options, ...restFieldProps } = props.fieldProps || {}
30
-
31
- let processedOptions = options
32
-
33
- if (Array.isArray(options) && options.length > 0 && typeof options[0] === 'string') {
34
- processedOptions = options.map((item: string) => ({
35
- label: item,
36
- value: item,
37
- }))
38
- }
39
-
40
- return (
41
- <ProFormSelect
42
- placeholder='请输入标签'
43
- mode='tags'
44
- options={processedOptions}
45
- {...restFieldProps}
46
- />
47
- )
48
- },
49
- render: (tags: string[]) => {
50
- return tags?.map((tag: string) => (
51
- <Tag key={tag}>{tag}</Tag>
52
- ))
53
- },
54
- },
55
- enabled: {
56
- render: (enabled: boolean, props: any) => {
57
- return (
58
- <Switch
59
- loading={crud?.updateState.isLoading}
60
- checked={enabled}
61
- onChange={enabled => onSwitchChange(props.record?.id, enabled)}
62
- disabled={!crud?.updateState}
63
- />
64
- )
65
- },
66
- },
67
- },
77
+ valueTypeMap,
68
78
  }}
69
79
  >
70
80
  {children}