@wzyjs/uis 0.3.8
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 +49 -0
- package/src/antd/form/CheckboxButton/index.module.scss +24 -0
- package/src/antd/form/CheckboxButton/index.tsx +31 -0
- package/src/antd/form/RadioButton/index.tsx +30 -0
- package/src/antd/form/Upload/index.tsx +65 -0
- package/src/antd/form/UploadImage/index.tsx +338 -0
- package/src/antd/form/index.ts +5 -0
- package/src/antd/index.ts +43 -0
- package/src/antd/pro/Alert/index.tsx +24 -0
- package/src/antd/pro/Button/components/Confirm.tsx +24 -0
- package/src/antd/pro/Button/components/Copy.tsx +46 -0
- package/src/antd/pro/Button/components/Drawer.tsx +37 -0
- package/src/antd/pro/Button/components/Group.tsx +26 -0
- package/src/antd/pro/Button/index.tsx +11 -0
- package/src/antd/pro/Card/index.tsx +92 -0
- package/src/antd/pro/Collapse/components/Item.tsx +30 -0
- package/src/antd/pro/Collapse/index.tsx +27 -0
- package/src/antd/pro/Image/index.tsx +17 -0
- package/src/antd/pro/Input/components/Range.tsx +46 -0
- package/src/antd/pro/Input/index.tsx +61 -0
- package/src/antd/pro/Popconfirm/index.tsx +16 -0
- package/src/antd/pro/Radio/components/Cancel.tsx +30 -0
- package/src/antd/pro/Radio/index.tsx +7 -0
- package/src/antd/pro/Space/index.tsx +15 -0
- package/src/antd/pro/Typography/components/String.tsx +72 -0
- package/src/antd/pro/Typography/index.tsx +9 -0
- package/src/antd/pro/index.ts +10 -0
- package/src/components/BottomBar/index.tsx +28 -0
- package/src/components/CodeView/index.tsx +85 -0
- package/src/components/Collapse/index.tsx +26 -0
- package/src/components/Com2Canvas/index.tsx +60 -0
- package/src/components/CompileHtml/index.tsx +26 -0
- package/src/components/DateSwitcher/index.module.scss +10 -0
- package/src/components/DateSwitcher/index.tsx +75 -0
- package/src/components/DownloadLink/index.tsx +36 -0
- package/src/components/DragSort/index.tsx +77 -0
- package/src/components/DynamicSelect/index.tsx +77 -0
- package/src/components/DynamicSelect/utils.ts +47 -0
- package/src/components/EnumTag/index.tsx +24 -0
- package/src/components/FetchSelect/index.tsx +57 -0
- package/src/components/Fold/index.tsx +52 -0
- package/src/components/FormPro/index.tsx +28 -0
- package/src/components/GroupLayout/index.tsx +45 -0
- package/src/components/HtmlPro/index.tsx +18 -0
- package/src/components/IframePro/index.tsx +52 -0
- package/src/components/JsonRenderer/index.tsx +115 -0
- package/src/components/JsonView/index.tsx +21 -0
- package/src/components/Markdown/index.tsx +152 -0
- package/src/components/Markdown/style.ts +106 -0
- package/src/components/MultiImageDisplay/index.tsx +63 -0
- package/src/components/SectorButton/index.tsx +247 -0
- package/src/components/TextInput/index.tsx +61 -0
- package/src/components/Video/index.tsx +37 -0
- package/src/components/index.ts +22 -0
- package/src/web.ts +2 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useEffect, useRef, ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
import html2canvas from 'html2canvas'
|
|
6
|
+
|
|
7
|
+
import { useDebounceFn } from '@wzyjs/hooks/web'
|
|
8
|
+
import { getRandomColor } from '@wzyjs/utils/web'
|
|
9
|
+
|
|
10
|
+
interface Com2CanvasProps {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
downloadName?: string;
|
|
13
|
+
onChange?: (url: string) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const Com2Canvas = (props: Com2CanvasProps) => {
|
|
17
|
+
const { children, downloadName, onChange } = props
|
|
18
|
+
|
|
19
|
+
const random = useRef(getRandomColor())
|
|
20
|
+
const htmlId = `h2c_html_${random.current}`
|
|
21
|
+
const canvasId = `htc_canvas_${random.current}`
|
|
22
|
+
|
|
23
|
+
const { run } = useDebounceFn(() => {
|
|
24
|
+
const el = document.querySelector(`#${canvasId}`) as HTMLElement
|
|
25
|
+
if (!el) {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
html2canvas(el, {
|
|
30
|
+
allowTaint: true, // 允许污染
|
|
31
|
+
useCORS: true, // 使用跨域(当allowTaint为true时这段代码没什么用)
|
|
32
|
+
scale: 2,
|
|
33
|
+
}).then((canvas: any) => {
|
|
34
|
+
el.innerHTML = ''
|
|
35
|
+
el.appendChild(canvas)
|
|
36
|
+
|
|
37
|
+
onChange?.(canvas.toDataURL('image/png'))
|
|
38
|
+
|
|
39
|
+
if (downloadName) {
|
|
40
|
+
el.onclick = () => {
|
|
41
|
+
const a = document.createElement('a')
|
|
42
|
+
a.download = downloadName
|
|
43
|
+
a.href = canvas.toDataURL('image/png')
|
|
44
|
+
document.body.appendChild(a)
|
|
45
|
+
a.click()
|
|
46
|
+
a.remove()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
}, { wait: 500 })
|
|
51
|
+
|
|
52
|
+
useEffect(run, [children])
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<span style={{ position: 'relative' }}>
|
|
56
|
+
<span id={htmlId}>{children}</span>
|
|
57
|
+
<span id={canvasId} style={{ position: 'absolute', top: 0 }} />
|
|
58
|
+
</span>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useMemo } from 'react'
|
|
4
|
+
import Handlebars from 'handlebars'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
template?: string
|
|
8
|
+
data: any
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default (props: Props) => {
|
|
12
|
+
const { template, data } = props
|
|
13
|
+
|
|
14
|
+
const html = useMemo(() => {
|
|
15
|
+
const html = Handlebars.compile(template || '')(data)
|
|
16
|
+
return html.replaceAll('\n', '').replaceAll('\t', '')
|
|
17
|
+
}, [template, data])
|
|
18
|
+
|
|
19
|
+
if (!html) {
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div dangerouslySetInnerHTML={{ __html: html }} />
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
|
+
|
|
5
|
+
import { DatePicker, Button } from 'antd'
|
|
6
|
+
import { LeftOutlined, RightOutlined } from '@ant-design/icons'
|
|
7
|
+
|
|
8
|
+
import { dayjs, type Dayjs } from '@wzyjs/utils/web'
|
|
9
|
+
|
|
10
|
+
import styles from './index.module.scss'
|
|
11
|
+
|
|
12
|
+
interface DateSwitcherProps {
|
|
13
|
+
value?: Dayjs
|
|
14
|
+
onChange?: (date: Dayjs) => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const DateSwitcher = (props: DateSwitcherProps) => {
|
|
18
|
+
const { value = dayjs(), onChange } = props
|
|
19
|
+
|
|
20
|
+
const [selectedDate, setSelectedDate] = useState(value)
|
|
21
|
+
|
|
22
|
+
const handleDateChange = (date: Dayjs | null) => {
|
|
23
|
+
if (date) {
|
|
24
|
+
setSelectedDate(date)
|
|
25
|
+
onChange?.(date)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const handlePrevDay = () => {
|
|
30
|
+
const newDate = selectedDate.subtract(1, 'day')
|
|
31
|
+
handleDateChange(newDate)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const handleNextDay = () => {
|
|
35
|
+
const newDate = selectedDate.add(1, 'day')
|
|
36
|
+
handleDateChange(newDate)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const isToday = selectedDate.isSame(dayjs(), 'day')
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className={styles.picker}>
|
|
43
|
+
<Button
|
|
44
|
+
type='text'
|
|
45
|
+
icon={<LeftOutlined />}
|
|
46
|
+
onClick={handlePrevDay}
|
|
47
|
+
className='flex items-center justify-center'
|
|
48
|
+
/>
|
|
49
|
+
<DatePicker
|
|
50
|
+
value={selectedDate}
|
|
51
|
+
onChange={handleDateChange}
|
|
52
|
+
allowClear={false}
|
|
53
|
+
suffixIcon={null}
|
|
54
|
+
className='w-40 text-center cursor-pointer'
|
|
55
|
+
style={{ border: 'none' }}
|
|
56
|
+
components={{
|
|
57
|
+
input: props => (
|
|
58
|
+
<div {...props} className={isToday ? 'text-green-600' : ''}>
|
|
59
|
+
<span>{selectedDate.format('YYYY-MM-DD')}</span>
|
|
60
|
+
<span style={{ marginLeft: 5 }}>
|
|
61
|
+
(周{['日', '一', '二', '三', '四', '五', '六'][dayjs(selectedDate).day()]})
|
|
62
|
+
</span>
|
|
63
|
+
</div>
|
|
64
|
+
),
|
|
65
|
+
}}
|
|
66
|
+
/>
|
|
67
|
+
<Button
|
|
68
|
+
type='text'
|
|
69
|
+
icon={<RightOutlined />}
|
|
70
|
+
onClick={handleNextDay}
|
|
71
|
+
className='flex items-center justify-center'
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
|
|
5
|
+
interface DownloadLinkProps {
|
|
6
|
+
url: string
|
|
7
|
+
filename?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const DownloadLink = ({ url, filename }: DownloadLinkProps) => {
|
|
11
|
+
const handleDownload = (e: any) => {
|
|
12
|
+
e.preventDefault()
|
|
13
|
+
fetch(url, { method: 'GET' })
|
|
14
|
+
.then(response => response.blob())
|
|
15
|
+
.then(blob => {
|
|
16
|
+
const downloadUrl = window.URL.createObjectURL(blob)
|
|
17
|
+
const a = document.createElement('a')
|
|
18
|
+
a.href = downloadUrl
|
|
19
|
+
a.download = filename || '下载文件'
|
|
20
|
+
document.body.appendChild(a)
|
|
21
|
+
a.click()
|
|
22
|
+
a.remove()
|
|
23
|
+
window.URL.revokeObjectURL(downloadUrl)
|
|
24
|
+
})
|
|
25
|
+
.catch((err) => {
|
|
26
|
+
console.log(666, err)
|
|
27
|
+
alert('下载失败')
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<a href={url} onClick={handleDownload}>
|
|
33
|
+
下载
|
|
34
|
+
</a>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
|
|
5
|
+
import { Space } from 'antd'
|
|
6
|
+
import {
|
|
7
|
+
DragDropContext,
|
|
8
|
+
Draggable,
|
|
9
|
+
Droppable,
|
|
10
|
+
type OnDragEndResponder,
|
|
11
|
+
type DraggableProvided,
|
|
12
|
+
} from 'react-beautiful-dnd'
|
|
13
|
+
|
|
14
|
+
interface DragSortProps<T> {
|
|
15
|
+
direction?: 'vertical' | 'horizontal'
|
|
16
|
+
droppableId?: string
|
|
17
|
+
list?: T[]
|
|
18
|
+
children: (item: T, provided: DraggableProvided) => React.ReactNode
|
|
19
|
+
lastChildren?: React.ReactNode
|
|
20
|
+
onDragEnd?: OnDragEndResponder
|
|
21
|
+
hasContext?: boolean
|
|
22
|
+
dropType?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { type DropResult, type DraggableProvided } from 'react-beautiful-dnd'
|
|
26
|
+
|
|
27
|
+
export const DragSort = <T extends { id: string }>(props: DragSortProps<T>) => {
|
|
28
|
+
const {
|
|
29
|
+
dropType,
|
|
30
|
+
direction = 'vertical',
|
|
31
|
+
droppableId = 'list',
|
|
32
|
+
list,
|
|
33
|
+
children,
|
|
34
|
+
hasContext = true,
|
|
35
|
+
lastChildren,
|
|
36
|
+
onDragEnd = () => undefined,
|
|
37
|
+
} = props
|
|
38
|
+
|
|
39
|
+
const content = (
|
|
40
|
+
<Droppable
|
|
41
|
+
type={dropType}
|
|
42
|
+
droppableId={droppableId}
|
|
43
|
+
direction={direction}
|
|
44
|
+
isDropDisabled={false}
|
|
45
|
+
isCombineEnabled={false}
|
|
46
|
+
ignoreContainerClipping
|
|
47
|
+
>
|
|
48
|
+
{provided => (
|
|
49
|
+
<div ref={provided.innerRef} {...provided.droppableProps}>
|
|
50
|
+
<Space orientation={direction} wrap style={{ width: '100%' }}>
|
|
51
|
+
{list?.map((item, index) => (
|
|
52
|
+
<Draggable key={item.id} disableInteractiveElementBlocking draggableId={item.id.toString()} index={index}>
|
|
53
|
+
{provided => (
|
|
54
|
+
<div ref={provided.innerRef} {...provided.draggableProps} >
|
|
55
|
+
{children(item, provided)}
|
|
56
|
+
</div>
|
|
57
|
+
)}
|
|
58
|
+
</Draggable>
|
|
59
|
+
))}
|
|
60
|
+
{provided.placeholder}
|
|
61
|
+
{lastChildren}
|
|
62
|
+
</Space>
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
</Droppable>
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if (!hasContext) {
|
|
69
|
+
return content
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<DragDropContext onDragEnd={onDragEnd}>
|
|
74
|
+
{content}
|
|
75
|
+
</DragDropContext>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { AutoComplete, Button, Space } from 'antd'
|
|
3
|
+
import { type Option } from '@wzyjs/types'
|
|
4
|
+
|
|
5
|
+
export * from './utils'
|
|
6
|
+
|
|
7
|
+
interface DynamicSelectProps {
|
|
8
|
+
value?: string[]
|
|
9
|
+
onChange?: (value: string[]) => void
|
|
10
|
+
options?: Option[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const DynamicSelect = (props: DynamicSelectProps) => {
|
|
14
|
+
const { value = [''], onChange, options = [] } = props
|
|
15
|
+
|
|
16
|
+
const removeOption = (index: number) => {
|
|
17
|
+
const newOptions = [...value]
|
|
18
|
+
newOptions.splice(index, 1)
|
|
19
|
+
onChange?.(newOptions)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const handleChange = (index: number, val: string) => {
|
|
23
|
+
const newValue = [...[...value].slice(0, index), val]
|
|
24
|
+
onChange?.(newValue)
|
|
25
|
+
|
|
26
|
+
if (newValue.every(item => item)) {
|
|
27
|
+
onChange?.([...newValue, ''])
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const getOptions = (options: Option[], index: number): Option[] => {
|
|
32
|
+
if (index === 0) {
|
|
33
|
+
return options
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const currentValue = value[value.length - 1 - index]
|
|
37
|
+
if (!currentValue) return []
|
|
38
|
+
|
|
39
|
+
return getOptions(
|
|
40
|
+
options.find(item => item.value === currentValue)?.children || [],
|
|
41
|
+
index - 1,
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Space size={8} align='center'>
|
|
47
|
+
{value.map((val, index) => (
|
|
48
|
+
<Space key={index} size={4} align='center'>
|
|
49
|
+
<AutoComplete
|
|
50
|
+
style={{
|
|
51
|
+
minWidth: '120px',
|
|
52
|
+
maxWidth: '300px',
|
|
53
|
+
width: 'auto',
|
|
54
|
+
}}
|
|
55
|
+
value={val}
|
|
56
|
+
onChange={val => handleChange(index, val)}
|
|
57
|
+
options={getOptions(options, index)}
|
|
58
|
+
placeholder={`请选择第 ${index + 1} 级`}
|
|
59
|
+
/>
|
|
60
|
+
{index !== 0 && (
|
|
61
|
+
<Button
|
|
62
|
+
type='text'
|
|
63
|
+
danger
|
|
64
|
+
size='small'
|
|
65
|
+
onClick={() => removeOption(index)}
|
|
66
|
+
>
|
|
67
|
+
删除
|
|
68
|
+
</Button>
|
|
69
|
+
)}
|
|
70
|
+
{index < value.length - 1 && (
|
|
71
|
+
<span style={{ color: '#999' }}>/</span>
|
|
72
|
+
)}
|
|
73
|
+
</Space>
|
|
74
|
+
))}
|
|
75
|
+
</Space>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Option } from '@wzyjs/types'
|
|
2
|
+
|
|
3
|
+
export const transformOptions = (data: string[][]): Option[] => {
|
|
4
|
+
const result: Option[] = []
|
|
5
|
+
|
|
6
|
+
data.forEach((item = []) => {
|
|
7
|
+
let currentNode: Option | undefined
|
|
8
|
+
let parentNode: Option | undefined
|
|
9
|
+
|
|
10
|
+
for (let i = 0; i < item.length; i++) {
|
|
11
|
+
const currentValue = item[i]
|
|
12
|
+
if (!currentValue) continue
|
|
13
|
+
|
|
14
|
+
if (i === 0) {
|
|
15
|
+
currentNode = result.find(r => r.value === currentValue)
|
|
16
|
+
|
|
17
|
+
if (!currentNode) {
|
|
18
|
+
currentNode = {
|
|
19
|
+
label: currentValue,
|
|
20
|
+
value: currentValue,
|
|
21
|
+
}
|
|
22
|
+
result.push(currentNode)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
parentNode = currentNode
|
|
26
|
+
} else {
|
|
27
|
+
currentNode = parentNode?.children?.find(c => c.value === currentValue)
|
|
28
|
+
|
|
29
|
+
if (!currentNode) {
|
|
30
|
+
currentNode = {
|
|
31
|
+
label: currentValue,
|
|
32
|
+
value: currentValue,
|
|
33
|
+
}
|
|
34
|
+
if (!parentNode) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
parentNode.children = parentNode.children || []
|
|
38
|
+
parentNode.children.push(currentNode)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
parentNode = currentNode
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
return result
|
|
47
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { Tag } from 'antd'
|
|
4
|
+
|
|
5
|
+
type EnumTagProps = {
|
|
6
|
+
value: string
|
|
7
|
+
map: Map<string, { label: string; value: string; extra?: { color?: string } }>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const EnumTag = (props: EnumTagProps) => {
|
|
11
|
+
const { value, map } = props
|
|
12
|
+
|
|
13
|
+
const enumItem = map.get(value)
|
|
14
|
+
|
|
15
|
+
if (!enumItem) {
|
|
16
|
+
return <Tag>{value}</Tag>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Tag color={enumItem.extra?.color}>
|
|
21
|
+
{enumItem.label}
|
|
22
|
+
</Tag>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useImperativeHandle } from 'react'
|
|
4
|
+
import { Select, SelectProps, Spin } from 'antd'
|
|
5
|
+
import { useRequest } from '@wzyjs/hooks/web'
|
|
6
|
+
|
|
7
|
+
export interface FetchSelectProps<I> extends SelectProps {
|
|
8
|
+
selectRef?: any,
|
|
9
|
+
isDetail?: boolean, // 只返回id 还是返回整个信息
|
|
10
|
+
searchApi: (params: { search: string }) => Promise<any>,
|
|
11
|
+
convertData?: (data: I[]) => any[],
|
|
12
|
+
onChange?: (value: any) => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface FetchSelectRef {
|
|
16
|
+
refresh: () => void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const FetchSelect = <I extends { id: string | number }, >(props: FetchSelectProps<I>) => {
|
|
20
|
+
const { searchApi, convertData, onChange, isDetail = false, selectRef, ...other } = props
|
|
21
|
+
|
|
22
|
+
const { data, run, loading, refresh } = useRequest(searchApi, {
|
|
23
|
+
debounceWait: 100,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const onSearch = (value: string) => {
|
|
27
|
+
run({ search: value })
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
useImperativeHandle(selectRef, () => ({
|
|
31
|
+
refresh,
|
|
32
|
+
}))
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Select
|
|
36
|
+
showSearch
|
|
37
|
+
defaultActiveFirstOption={false}
|
|
38
|
+
popupMatchSelectWidth={false}
|
|
39
|
+
suffixIcon={null}
|
|
40
|
+
filterOption={false}
|
|
41
|
+
notFoundContent={loading ? <Spin size='small' /> : null}
|
|
42
|
+
loading={loading}
|
|
43
|
+
{...other}
|
|
44
|
+
|
|
45
|
+
options={convertData ? convertData(data?.data || []) : data?.data}
|
|
46
|
+
onSearch={onSearch}
|
|
47
|
+
onChange={value => {
|
|
48
|
+
if (isDetail) {
|
|
49
|
+
const detail = data?.data?.find(item => item.id === value)
|
|
50
|
+
onChange?.(detail)
|
|
51
|
+
} else {
|
|
52
|
+
onChange?.(value)
|
|
53
|
+
}
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
|
+
import { Button } from 'antd'
|
|
5
|
+
import { DownOutlined, UpOutlined } from '@ant-design/icons'
|
|
6
|
+
|
|
7
|
+
interface FoldProps {
|
|
8
|
+
max: number
|
|
9
|
+
children?: React.ReactNode
|
|
10
|
+
btnStyle?: React.CSSProperties
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Fold = (props: FoldProps) => {
|
|
14
|
+
const { max, children, btnStyle = {} } = props
|
|
15
|
+
|
|
16
|
+
const [collapsed, setCollapsed] = useState<boolean | undefined>(undefined)
|
|
17
|
+
|
|
18
|
+
const divRef = React.useRef<HTMLDivElement>(null)
|
|
19
|
+
|
|
20
|
+
React.useEffect(() => {
|
|
21
|
+
if (divRef.current) {
|
|
22
|
+
const { clientHeight } = divRef.current
|
|
23
|
+
if (clientHeight > max) {
|
|
24
|
+
setCollapsed(true)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}, [divRef.current])
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div ref={divRef} style={{ overflow: 'hidden' }}>
|
|
31
|
+
<div style={{ maxHeight: collapsed ? max : undefined }}>
|
|
32
|
+
{children}
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div style={{ textAlign: 'center', ...btnStyle }}>
|
|
36
|
+
{collapsed === undefined ? null : collapsed ? (
|
|
37
|
+
<Button
|
|
38
|
+
size='small'
|
|
39
|
+
icon={<DownOutlined />}
|
|
40
|
+
onClick={() => setCollapsed(false)}
|
|
41
|
+
/>
|
|
42
|
+
) : (
|
|
43
|
+
<Button
|
|
44
|
+
size='small'
|
|
45
|
+
icon={<UpOutlined />}
|
|
46
|
+
onClick={() => setCollapsed(true)}
|
|
47
|
+
/>
|
|
48
|
+
)}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useImperativeHandle, useRef } from 'react'
|
|
4
|
+
import { BetaSchemaForm, ProFormInstance } from '@ant-design/pro-components'
|
|
5
|
+
import { FormSchema } from '@ant-design/pro-form/es/components/SchemaForm'
|
|
6
|
+
|
|
7
|
+
export type { ProFormColumnsType, ProFormInstance } from '@ant-design/pro-components'
|
|
8
|
+
|
|
9
|
+
export type FormProProps<T, ValueType> = FormSchema<T, ValueType>
|
|
10
|
+
|
|
11
|
+
export const FormPro = <T, ValueType>(props: FormProProps<T, ValueType>) => {
|
|
12
|
+
|
|
13
|
+
const formRef = useRef<ProFormInstance>(null)
|
|
14
|
+
useImperativeHandle(props.formRef, () => formRef?.current, [formRef])
|
|
15
|
+
|
|
16
|
+
// useEffect(() => {
|
|
17
|
+
// if (props.value) {
|
|
18
|
+
// formRef.current?.setFieldsValue(props.value)
|
|
19
|
+
// }
|
|
20
|
+
// }, [JSON.stringify(props.value)])
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<BetaSchemaForm
|
|
24
|
+
{...props}
|
|
25
|
+
formRef={formRef}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { CSSProperties, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import GridLayout, { Layout } from 'react-grid-layout'
|
|
4
|
+
import 'react-grid-layout/css/styles.css'
|
|
5
|
+
|
|
6
|
+
export type { Layout as GridLayoutItem } from 'react-grid-layout'
|
|
7
|
+
|
|
8
|
+
interface GridLayoutProps<I> {
|
|
9
|
+
layout: I[]
|
|
10
|
+
style?: CSSProperties
|
|
11
|
+
onChange?: (layout: I[]) => void
|
|
12
|
+
renderItem?: (item: I) => ReactNode
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const GroupLayout = <I extends Layout>(props: GridLayoutProps<I>) => {
|
|
16
|
+
const { style, layout, onChange, renderItem = item => item.i } = props
|
|
17
|
+
|
|
18
|
+
const onLayoutChange = (items: Layout[]) => {
|
|
19
|
+
onChange?.(items.map(item => ({
|
|
20
|
+
...layout.find(i => i.i === item.i),
|
|
21
|
+
...item,
|
|
22
|
+
}) as I))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<GridLayout
|
|
27
|
+
layout={layout}
|
|
28
|
+
cols={24}
|
|
29
|
+
rowHeight={50}
|
|
30
|
+
width={1200}
|
|
31
|
+
useCSSTransforms={false}
|
|
32
|
+
containerPadding={[0, 0]}
|
|
33
|
+
style={{ width: '100%', height: '100%', userSelect: 'none', ...style }}
|
|
34
|
+
draggableHandle='.drag-handle'
|
|
35
|
+
resizeHandles={['se']}
|
|
36
|
+
onLayoutChange={onLayoutChange}
|
|
37
|
+
>
|
|
38
|
+
{layout.map(item => (
|
|
39
|
+
<div key={item.i}>
|
|
40
|
+
{renderItem(item)}
|
|
41
|
+
</div>
|
|
42
|
+
))}
|
|
43
|
+
</GridLayout>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { CSSProperties } from 'react'
|
|
4
|
+
|
|
5
|
+
export interface HtmlProProps {
|
|
6
|
+
html: string;
|
|
7
|
+
style?: CSSProperties
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const HtmlPro = (props: HtmlProProps) => {
|
|
11
|
+
const { html, style = {} } = props
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
style={{ minWidth: '100%', ...style }}
|
|
15
|
+
dangerouslySetInnerHTML={{ __html: html }}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useEffect, CSSProperties } from 'react'
|
|
4
|
+
import { Spin } from 'antd'
|
|
5
|
+
import { useBoolean } from '@wzyjs/hooks/web'
|
|
6
|
+
|
|
7
|
+
export interface IframeProProps {
|
|
8
|
+
url: string;
|
|
9
|
+
errMessage?: string;
|
|
10
|
+
style?: CSSProperties;
|
|
11
|
+
isShowLoading?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const IframePro = (props: IframeProProps) => {
|
|
15
|
+
const { url, style, errMessage = '', isShowLoading = false } = props
|
|
16
|
+
|
|
17
|
+
const [loading, { setTrue, setFalse }] = useBoolean(true)
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
setTrue()
|
|
21
|
+
}, [url])
|
|
22
|
+
|
|
23
|
+
if (!url) {
|
|
24
|
+
return (
|
|
25
|
+
<span
|
|
26
|
+
style={{
|
|
27
|
+
...style,
|
|
28
|
+
color: '#ccc',
|
|
29
|
+
fontSize: 14,
|
|
30
|
+
display: 'flex',
|
|
31
|
+
flexDirection: 'column',
|
|
32
|
+
justifyContent: 'center',
|
|
33
|
+
alignItems: 'center',
|
|
34
|
+
}}
|
|
35
|
+
>
|
|
36
|
+
{errMessage || '没有url'}
|
|
37
|
+
</span>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Spin spinning={loading && isShowLoading}>
|
|
43
|
+
<iframe
|
|
44
|
+
src={url}
|
|
45
|
+
width='100%'
|
|
46
|
+
height='100%'
|
|
47
|
+
style={style}
|
|
48
|
+
onLoad={setFalse}
|
|
49
|
+
/>
|
|
50
|
+
</Spin>
|
|
51
|
+
)
|
|
52
|
+
}
|