@wzyjs/components 0.2.41
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 +42 -0
- package/src/BottomBar/index.tsx +28 -0
- package/src/CodeView/index.tsx +85 -0
- package/src/Collapse/index.tsx +26 -0
- package/src/Com2Canvas/index.tsx +60 -0
- package/src/CompileHtml/index.tsx +26 -0
- package/src/Crud/components/create.tsx +77 -0
- package/src/Crud/components/remove.tsx +33 -0
- package/src/Crud/components/update.tsx +75 -0
- package/src/Crud/index.tsx +144 -0
- package/src/Crud/powers/option.tsx +60 -0
- package/src/Crud/powers/validator.ts +73 -0
- package/src/Crud/types.ts +36 -0
- package/src/Crud/utils.ts +17 -0
- package/src/DateSwitcher/index.module.scss +10 -0
- package/src/DateSwitcher/index.tsx +75 -0
- package/src/DownloadLink/index.tsx +36 -0
- package/src/DragSort/index.tsx +77 -0
- package/src/FetchSelect/index.tsx +57 -0
- package/src/Fold/index.tsx +52 -0
- package/src/FormPro/index.tsx +28 -0
- package/src/HtmlPro/index.tsx +18 -0
- package/src/IframePro/index.tsx +52 -0
- package/src/JsonView/index.tsx +21 -0
- package/src/MultiImageDisplay/index.tsx +63 -0
- package/src/TablePro/index.tsx +66 -0
- package/src/Video/index.tsx +37 -0
- package/src/index.ts +17 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { getRequire } from '../utils'
|
|
4
|
+
import { Apis, Column } from '../types'
|
|
5
|
+
|
|
6
|
+
interface ValidatorProps<I> {
|
|
7
|
+
columns: Column<I>[],
|
|
8
|
+
findApi?: Apis<I>['find']
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const validatorValue = <I>(item: Column<I>, findApi: Apis<I>['find']): any[] => {
|
|
12
|
+
const { validator, title } = item
|
|
13
|
+
|
|
14
|
+
if (!validator?.length) {
|
|
15
|
+
return []
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return validator.map(item => {
|
|
19
|
+
if (!item) {
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const [type, option] = Array.isArray(item) ? item : [item]
|
|
24
|
+
|
|
25
|
+
switch (type) {
|
|
26
|
+
case 'require':
|
|
27
|
+
return getRequire(title as string)
|
|
28
|
+
|
|
29
|
+
case 'unique':
|
|
30
|
+
if (!findApi) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
asyncValidator: async ({ field }: { field: keyof I }, value: string) => {
|
|
36
|
+
if (!value || !findApi) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const { data } = await findApi({ [field]: { type: 'eq', value } } as any)
|
|
41
|
+
if (data) {
|
|
42
|
+
return Promise.reject(option)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return Promise.resolve()
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
default:
|
|
50
|
+
return {}
|
|
51
|
+
}
|
|
52
|
+
}).filter(item => item)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default <I>(props: ValidatorProps<I>): Column<I>[] => {
|
|
56
|
+
const { columns, findApi } = props
|
|
57
|
+
|
|
58
|
+
return columns.map(item => {
|
|
59
|
+
if (item?.validator) {
|
|
60
|
+
item.formItemProps = {
|
|
61
|
+
...(item.formItemProps || {}),
|
|
62
|
+
rules: [
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
...(item.rules || []),
|
|
65
|
+
...validatorValue(item, findApi),
|
|
66
|
+
].flat(),
|
|
67
|
+
}
|
|
68
|
+
delete item.validator
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return item
|
|
72
|
+
})
|
|
73
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { TableProColumns } from '../TablePro'
|
|
2
|
+
import { RequestRes, Pagination, Where, Order } from '@wzyjs/types'
|
|
3
|
+
|
|
4
|
+
// 扩展了 ProColumns 的能力
|
|
5
|
+
export type Column<I, ValueType = 'text'> = TableProColumns<I, ValueType> & {
|
|
6
|
+
hideInCreate?: true, // 创建表单里隐藏
|
|
7
|
+
hideInUpdate?: true, // 编辑表单里隐藏
|
|
8
|
+
validator?: ('require' | null | ['unique', string])[], // 使用内置的校验规则
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type ColumnContent = {
|
|
12
|
+
type: 'create' | 'update' | 'list',
|
|
13
|
+
isCreate?: boolean,
|
|
14
|
+
isUpdate?: boolean,
|
|
15
|
+
isList?: boolean,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type Columns<I, ValueType = 'text'> = Column<I, ValueType>[] | ((content: ColumnContent) => Column<I>[])
|
|
19
|
+
|
|
20
|
+
export type Apis<I> = {
|
|
21
|
+
list: (params: {
|
|
22
|
+
where?: Where,
|
|
23
|
+
order?: Order,
|
|
24
|
+
pagination?: Pagination
|
|
25
|
+
}) => Promise<RequestRes<{ data: I[], total: number }>>,
|
|
26
|
+
find?: (params: I) => Promise<RequestRes<unknown>>,
|
|
27
|
+
create?: (data: Partial<I>) => Promise<RequestRes<unknown>>,
|
|
28
|
+
update?: (id: string, data: Partial<I>) => Promise<RequestRes<unknown>>,
|
|
29
|
+
remove?: (id: string) => Promise<RequestRes<unknown>>,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 调用各个接口时的附加参数
|
|
33
|
+
export type ApiParams = {
|
|
34
|
+
list?: Where,
|
|
35
|
+
create?: Where,
|
|
36
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { isFunction, cloneDeep } from '@wzyjs/utils'
|
|
2
|
+
import { Column, Columns, ColumnContent } from './types'
|
|
3
|
+
|
|
4
|
+
export const getRequireFormProps = (label?: string) => ({
|
|
5
|
+
rules: [
|
|
6
|
+
getRequire(label),
|
|
7
|
+
],
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
export const getRequire = (label?: string) => ({
|
|
11
|
+
required: true,
|
|
12
|
+
message: `${label + ' ' || '此项'}为必填项`,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export const handleColumns = <I>(columns: Columns<I>, content: ColumnContent, mids: any[]): Column<I>[] => {
|
|
16
|
+
return mids.reduce((columns, mid) => mid(columns), isFunction(columns) ? columns(content) : cloneDeep(columns))
|
|
17
|
+
}
|
|
@@ -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'
|
|
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,
|
|
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 direction={direction} wrap style={{ width: '100%' }}>
|
|
51
|
+
{list?.map((item, index) => (
|
|
52
|
+
<Draggable key={item.id} 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,57 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useImperativeHandle } from 'react'
|
|
4
|
+
import { Select, SelectProps, Spin } from 'antd'
|
|
5
|
+
import { useRequest } from 'ahooks'
|
|
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,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'
|
|
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
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import ReactJson, { ReactJsonViewProps } from 'react-json-view'
|
|
5
|
+
|
|
6
|
+
export type JsonViewProps = ReactJsonViewProps
|
|
7
|
+
|
|
8
|
+
export const JsonView = (props: JsonViewProps) => {
|
|
9
|
+
const { style } = props
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<ReactJson
|
|
13
|
+
enableClipboard={false}
|
|
14
|
+
collapsed={true}
|
|
15
|
+
displayObjectSize={false}
|
|
16
|
+
displayDataTypes={false}
|
|
17
|
+
{...props}
|
|
18
|
+
style={{ overflow: 'auto', ...style }}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { Row, Col, Image } from 'antd'
|
|
5
|
+
|
|
6
|
+
export const MultiImageDisplay = ({ images = [], preview = false }) => {
|
|
7
|
+
const renderImages = () => {
|
|
8
|
+
const count = images.length
|
|
9
|
+
|
|
10
|
+
if (count === 1) {
|
|
11
|
+
return (
|
|
12
|
+
<Image src={images[0]} preview={preview} style={{ width: '100%', height: 'auto' }} />
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (count === 2) {
|
|
17
|
+
return (
|
|
18
|
+
<Row gutter={8}>
|
|
19
|
+
{images.map((img, index) => (
|
|
20
|
+
<Col span={12} key={index}>
|
|
21
|
+
<Image src={img} preview={preview} style={{ width: '100%', height: 'auto' }} />
|
|
22
|
+
</Col>
|
|
23
|
+
))}
|
|
24
|
+
</Row>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (count === 3) {
|
|
29
|
+
return (
|
|
30
|
+
<div>
|
|
31
|
+
<Row gutter={8}>
|
|
32
|
+
{images.slice(0, 2).map((img, index) => (
|
|
33
|
+
<Col span={12} key={index}>
|
|
34
|
+
<Image src={img} preview={preview} style={{ width: '100%', height: 'auto' }} />
|
|
35
|
+
</Col>
|
|
36
|
+
))}
|
|
37
|
+
</Row>
|
|
38
|
+
<Row gutter={8} style={{ marginTop: '8px' }}>
|
|
39
|
+
<Col span={24}>
|
|
40
|
+
<Image src={images[2]} preview={preview} style={{ width: '100%', height: 'auto' }} />
|
|
41
|
+
</Col>
|
|
42
|
+
</Row>
|
|
43
|
+
</div>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (count >= 4) {
|
|
48
|
+
return (
|
|
49
|
+
<Row>
|
|
50
|
+
{images.map((img, index) => (
|
|
51
|
+
<Col span={12} key={index}>
|
|
52
|
+
<Image src={img} preview={preview} style={{ width: '100%', height: 'auto' }} />
|
|
53
|
+
</Col>
|
|
54
|
+
))}
|
|
55
|
+
</Row>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return <div>{renderImages()}</div>
|
|
63
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { ReactNode, useImperativeHandle, useRef } from 'react'
|
|
4
|
+
import { _ } from '@wzyjs/utils'
|
|
5
|
+
import {
|
|
6
|
+
ActionType,
|
|
7
|
+
ParamsType,
|
|
8
|
+
ProColumns,
|
|
9
|
+
ProFormInstance,
|
|
10
|
+
ProTable,
|
|
11
|
+
ProTableProps,
|
|
12
|
+
} from '@ant-design/pro-components'
|
|
13
|
+
|
|
14
|
+
export type { ActionType, ProColumns } from '@ant-design/pro-components'
|
|
15
|
+
|
|
16
|
+
export type TableProColumns<T, ValueType> = ProColumns<T, ValueType> & {
|
|
17
|
+
summary?: (data: readonly T[]) => ReactNode,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type TableProProps<T, U, ValueType = 'text'> = Omit<ProTableProps<T, U, ValueType>, 'columns'> & {
|
|
21
|
+
columns: TableProColumns<T, ValueType>[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const defaultTableProps = {
|
|
25
|
+
rowKey: 'id',
|
|
26
|
+
cardBordered: true,
|
|
27
|
+
options: false,
|
|
28
|
+
dateFormatter: 'string',
|
|
29
|
+
headerTitle: '列表',
|
|
30
|
+
scroll: {
|
|
31
|
+
x: 'max-content',
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const TablePro = <T extends Record<string, any>, U extends ParamsType, ValueType = 'text'>(props: TableProProps<T, U, ValueType>) => {
|
|
36
|
+
const tableProps = _.merge(_.cloneDeep(defaultTableProps), props)
|
|
37
|
+
|
|
38
|
+
const actionRef = useRef<ActionType>(undefined)
|
|
39
|
+
useImperativeHandle(props.actionRef, () => actionRef?.current, [actionRef])
|
|
40
|
+
|
|
41
|
+
const formRef = useRef<ProFormInstance>(undefined)
|
|
42
|
+
useImperativeHandle(props.formRef, () => formRef?.current, [formRef])
|
|
43
|
+
|
|
44
|
+
const defaultsSummary = (data: readonly T[]) => {
|
|
45
|
+
return (
|
|
46
|
+
<ProTable.Summary.Row>
|
|
47
|
+
{tableProps.columns?.map((item: any, index: number) => {
|
|
48
|
+
return (
|
|
49
|
+
<ProTable.Summary.Cell key={index} index={index}>
|
|
50
|
+
{item.summary?.(data) || ''}
|
|
51
|
+
</ProTable.Summary.Cell>
|
|
52
|
+
)
|
|
53
|
+
})}
|
|
54
|
+
</ProTable.Summary.Row>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<ProTable
|
|
60
|
+
{...tableProps}
|
|
61
|
+
formRef={formRef}
|
|
62
|
+
actionRef={actionRef}
|
|
63
|
+
summary={tableProps.summary || defaultsSummary}
|
|
64
|
+
/>
|
|
65
|
+
)
|
|
66
|
+
}
|