Commit 6eeb558c authored by 田翔's avatar 田翔

fix: 更换拖拽组件

parent 192384e4
{ {
"name": "panda-xform", "name": "panda-xform",
"version": "3.10.5", "version": "3.10.6",
"description": "3.10.5: 版本升级", "description": "3.10.6: 更换拖拽组件",
"keywords": [ "keywords": [
"panda-xform" "panda-xform"
], ],
...@@ -30,19 +30,17 @@ ...@@ -30,19 +30,17 @@
"@wisdom-components/empty": "^1.4.7", "@wisdom-components/empty": "^1.4.7",
"antd": "4.20.7", "antd": "4.20.7",
"antd-img-crop": "^3.14.1", "antd-img-crop": "^3.14.1",
"array-move": "^4.0.0",
"form-render": "1.14.7", "form-render": "1.14.7",
"fr-generator": "2.8.3", "fr-generator": "2.8.3",
"http-proxy-middleware": "^1.0.6", "http-proxy-middleware": "^1.0.6",
"moment": "^2.29.1", "moment": "^2.29.1",
"qrcode.react": "^3.1.0", "qrcode.react": "^3.1.0",
"react": "17.0.2", "react": "17.0.2",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-file-viewer": "^1.2.1", "react-file-viewer": "^1.2.1",
"react-sign2": "^0.0.3", "react-sign2": "^0.0.3",
"react-sortable-hoc": "^2.0.0",
"react-sortablejs": "^6.1.4",
"react-svg": "15.1.9", "react-svg": "15.1.9",
"sortablejs": "^1.15.0",
"yarn": "^1.22.17" "yarn": "^1.22.17"
}, },
"devDependencies": { "devDependencies": {
...@@ -50,7 +48,6 @@ ...@@ -50,7 +48,6 @@
"@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-jsx": "^7.12.13", "@babel/plugin-syntax-jsx": "^7.12.13",
"@babel/plugin-transform-modules-amd": "^7.14.5", "@babel/plugin-transform-modules-amd": "^7.14.5",
"@types/sortablejs": "^1.15.1",
"@umijs/fabric": "^2.0.7", "@umijs/fabric": "^2.0.7",
"@wisdom-map/amap": "1.1.0-beta.45", "@wisdom-map/amap": "1.1.0-beta.45",
"@wisdom-map/arcgismap": "1.4.0-124", "@wisdom-map/arcgismap": "1.4.0-124",
......
import React, { useState, useRef, useEffect, useContext, useMemo, useLayoutEffect } from 'react' import React, { useState, useEffect, useRef } from 'react';
import { MenuOutlined } from '@ant-design/icons' import { Table } from 'antd';
import { Table, Input, Form, Popover, Popconfirm } from 'antd'
import { arrayMoveImmutable } from 'array-move' import { HTML5Backend } from 'react-dnd-html5-backend';
import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc' import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { CheckOutlined, DeleteOutlined } from '@ant-design/icons' import classnames from 'classnames';
import styles from './index.less' import styles from './index.less';
const colors = [ const DraggableBodyRow = ({
// '#ffccc7', '#ffe7ba', '#fffb8f', '#d9f7be', '#b5f5ec', name,
// '#bae7ff', '#d6e4ff', '#efdbff', '#ffd6e7', '#e8e8e8', index,
// '#ff4d4f', '#ffa940', '#fbce00', '#73d13d', '#36cfc9', moveRow,
// '#40a9ff', '#597ef7', '#9254de', '#f759ab', '#8c8c8c', className,
'rgb(255, 77, 79)', style,
'rgb(255, 169, 64)', tableType,
'rgb(251, 206, 0)', ItemTypes,
'rgba(115, 209, 61)', ...restProps
'rgba(54, 207, 201)', }) => {
'rgba(64, 169, 255)', const [type, setType] = useState('');
'rgba(89, 126, 247)', const ref = useRef();
'rgba(146, 84, 222)', useEffect(() => {
'rgba(247, 89, 171)', if (ItemTypes) {
'rgba(140, 140, 140)', setType(ItemTypes);
// 'rgba(255, 204, 199)',
// 'rgba(255, 231, 186)',
// 'rgba(255, 251, 143)',
// 'rgba(217, 247, 190)',
// 'rgba(181, 245, 236)',
// 'rgba(186, 231, 255)',
// 'rgba(214, 228, 255)',
// 'rgba(239, 219, 255)',
// 'rgba(255, 214, 231)',
// 'rgba(232, 232, 232)',
]
let newData = []
const EditableContext = React.createContext(null)
const DragHandle = SortableHandle(() => (
<MenuOutlined
style={{
cursor: 'grab',
color: '#999',
}}
/>
))
const SortableItem = SortableElement((props) => {
const [form] = Form.useForm()
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
)
})
const SortableBody = SortableContainer((props) => <tbody {...props} />)
// const InputItem = (props) => {
// const inputRef = useRef(null)
// useLayoutEffect(() => {
// inputRef.current.focus()
// }, [])
// return (
// <Input ref={inputRef} {...props} />
// )
// }
const DragTable = (props) => {
const { dataSource, setDataSource, color } = props
const onSortEnd = ({ oldIndex, newIndex }) => {
if (oldIndex !== newIndex) {
const data = arrayMoveImmutable(dataSource.slice(), oldIndex, newIndex).filter(
(el) => !!el,
)
setDataSource(data)
} }
} }, [ItemTypes]);
const [{ isOver, dropClassName }, drop] = useDrop({
const DraggableContainer = (props) => ( accept: type,
<SortableBody collect: monitor => {
useDragHandle const { index: dragIndex } = monitor.getItem() || {};
disableAutoscroll if (dragIndex === index) {
helperClass="row-dragging" return {};
onSortEnd={onSortEnd}
{...props}
/>
)
const DraggableBodyRow = ({ className, style, ...restProps }) => {
// const index = dataSource.findIndex((x) => x.index === restProps['data-row-key'])
// return <SortableItem key={index} index={index} {...restProps} />
let key = Number(restProps['data-row-key'])
return <SortableItem key={key} index={key} {...restProps} />
}
const onPressEnter = (i) => {
let length = [...dataSource].length
let array = [...dataSource]
array.splice(i + 1, 0, { key: `${length + 1}`, index: length + 1, value: '' })
setDataSource(array)
}
const inputChange = (value, i) => {
let array = []
dataSource.forEach((v, index) => {
if (index === i) {
v.value = value
}
array.push({ ...v })
})
newData = array
}
const colorClick = (value, i) => {
let array = []
dataSource.forEach((v, index) => {
if (index === i) {
v.color = value
} }
array.push({ ...v }) return {
}) isOver: monitor.isOver(),
setDataSource(array) canDrop: monitor.canDrop(),
} dropClassName: dragIndex < index ? 'drop-over-downward' : 'drop-over-upward',
};
const onBlur = () => { },
if (Array.isArray(newData) && newData.length) { drop: item => {
setDataSource(newData) moveRow(item.index, index, tableType);
},
});
const [, drag] = useDrag({
type,
item: { index },
collect: monitor => ({
isDragging: monitor.isDragging(),
}),
});
drop(drag(ref));
return (
<tr
ref={ref}
className={classnames({
[className]: true,
[styles[dropClassName]]: isOver,
})}
style={{ cursor: 'move', ...style }}
{...restProps}
/>
);
};
const DragTable = props => {
const { columns, dataSource, dragCallBack, ItemTypes } = props;
// const [data, setData] = useState(null); // 分组后的数组
const components = {
body: {
row: DraggableBodyRow,
},
};
// useEffect(() => {
// if (dataSource.length > 0) {
// setData(dataSource);
// }
// }, [dataSource]);
// 每次拖拽后返回
// useEffect(() => {
// dragCallBack(data);
// }, [data]);
// 移动数组元素到指定位置
const moveInArray = (arr, from, to) => {
// 确保是有效数组
if (Object.prototype.toString.call(arr) !== '[object Array]') {
throw new Error('Please provide a valid array');
} }
} // 删除当前的位置
let item = arr.splice(from, 1);
const confirm = (index) => { // 确保还剩有元素移动
let array = [] if (!item.length) {
dataSource.forEach((v, i) => { throw new Error(`There is no item in the array at index ${from}`);
if (i !== index) {
array.push({ ...v })
}
})
newData = array
setDataSource(array)
}
const rest = (index) => {
let array = []
dataSource.forEach((v, i) => {
if (index === i) {
v.color = ''
}
array.push({ ...v })
})
setDataSource(array)
}
const columns = useMemo(() => {
let columns = [
{
title: 'value',
dataIndex: 'value',
render: (r, _, i) => {
return (
<Input
onPressEnter={() => onPressEnter(i)}
onChange={(e) => inputChange(e.target.value, i)}
defaultValue={r}
onBlur={() => onBlur()}
/>
)
}
},
{
title: 'color',
dataIndex: 'color',
width: 30,
render: (r, _, index) => {
const content = (
<div>
<div className={styles.colorTop}>
<div className={styles.topLeft}>请选择颜色</div>
<div className={styles.topRight} onClick={() => rest(index)}>重置</div>
</div>
<div className={styles.colorBox}>
{
colors.map((v, i) => {
return (
<div className={styles.colors} style={{ background: v }} onClick={() => colorClick(v, index)}>
<CheckOutlined style={{ fontSize: '11px', marginLeft: '5px', display: r === v ? 'inline-block' : 'none' }} />
</div>
)
})
}
</div>
</div>
)
return (
<Popover
content={content}
trigger='click'
>
<div className={styles.colors} style={{ background: r || '#f2f4f5' }}></div>
</Popover>
)
}
},
{
width: 30,
render: (r, _, i) => {
return (
<Popconfirm
title='确定删除?'
onConfirm={() => confirm(i)}
onCancel={() => { }}
>
<DeleteOutlined style={{ color: 'rgb(153, 153, 153)' }} />
</Popconfirm>
)
},
},
{
title: 'Sort',
dataIndex: 'sort',
width: 30,
className: 'drag-visible',
render: () => <DragHandle />,
},
]
if (!color) {
return columns.filter(v => v.dataIndex !== 'color')
} }
return columns // 移动元素到指定位置
}, [dataSource, color]) arr.splice(to, 0, item[0]);
return arr;
};
// 拖拽表格
const moveRow = (dragIndex, hoverIndex) => {
// setData(val => {
// let newData = JSON.parse(JSON.stringify(val));
// newData = moveInArray(newData, dragIndex, hoverIndex);
// return newData;
// });
let newData = JSON.parse(JSON.stringify(dataSource))
newData = moveInArray(newData, dragIndex, hoverIndex)
dragCallBack(newData)
}
return ( return (
<Table <DndProvider backend={HTML5Backend}>
showHeader={false} <Table
pagination={false} size='small'
dataSource={dataSource} columns={columns}
columns={columns} dataSource={dataSource}
rowKey="key" components={components}
size='small' onRow={(record, index) => ({
components={{ index,
body: { ItemTypes,
wrapper: DraggableContainer, moveRow,
row: DraggableBodyRow, onClick: () => props.onClick && props.onClick(record),
}, })}
}} {...props}
/> />
</DndProvider>
) )
} }
export default DragTable export default DragTable;
\ No newline at end of file
.colorTop { tr.drop-over-downward td {
display: flex; border-bottom: 2px dashed #1890ff;
justify-content: space-between;
.topLeft {
font-size: 15px;
font-weight: 700;
color: #707070;
}
.topRight {
font-size: 12px;
color: #41b3ec;
&:hover {
cursor: pointer;
}
}
} }
.colorBox { tr.drop-over-upward td {
width: 130px; border-top: 2px dashed #1890ff;
display: flex;
flex-wrap: wrap;
}
.colors {
width: 20px;
height: 20px;
border-radius: 50%;
margin-top: 6px;
margin-left: 6px;
&:hover {
cursor: pointer;
}
} }
\ No newline at end of file
...@@ -263,6 +263,7 @@ const RelationForm = (props) => { ...@@ -263,6 +263,7 @@ const RelationForm = (props) => {
} }
const getTableData = async (addFieldGroup) => { const getTableData = async (addFieldGroup) => {
console.log('addFieldGroup', addFieldGroup)
if (!addons) return if (!addons) return
let queryWheres = getQueryWheres(addFieldGroup) let queryWheres = getQueryWheres(addFieldGroup)
if (!queryWheres.length) { if (!queryWheres.length) {
......
import React, { useState, useRef, useContext, useEffect } from 'react' import React, { useState, useRef, useContext, useEffect, useMemo } from 'react'
import styles from './index.less' import styles from './index.less'
import { Switch, Input, Table, Form } from 'antd' import { Switch, Input, Table, Form, Popconfirm, Popover } from 'antd'
import Drag from './../../../../components/Drag' import Drag from './../../../../components/Drag'
import DragTable from '../../../../components/DragTable' import DragTable from '../../../../components/DragTable'
import { CheckOutlined, DeleteOutlined, MenuOutlined } from '@ant-design/icons'
const colors = [
// '#ffccc7', '#ffe7ba', '#fffb8f', '#d9f7be', '#b5f5ec',
// '#bae7ff', '#d6e4ff', '#efdbff', '#ffd6e7', '#e8e8e8',
// '#ff4d4f', '#ffa940', '#fbce00', '#73d13d', '#36cfc9',
// '#40a9ff', '#597ef7', '#9254de', '#f759ab', '#8c8c8c',
'rgb(255, 77, 79)',
'rgb(255, 169, 64)',
'rgb(251, 206, 0)',
'rgba(115, 209, 61)',
'rgba(54, 207, 201)',
'rgba(64, 169, 255)',
'rgba(89, 126, 247)',
'rgba(146, 84, 222)',
'rgba(247, 89, 171)',
'rgba(140, 140, 140)',
// 'rgba(255, 204, 199)',
// 'rgba(255, 231, 186)',
// 'rgba(255, 251, 143)',
// 'rgba(217, 247, 190)',
// 'rgba(181, 245, 236)',
// 'rgba(186, 231, 255)',
// 'rgba(214, 228, 255)',
// 'rgba(239, 219, 255)',
// 'rgba(255, 214, 231)',
// 'rgba(232, 232, 232)',
]
const SimpleList = (props) => { const SimpleList = (props) => {
...@@ -10,15 +38,158 @@ const SimpleList = (props) => { ...@@ -10,15 +38,158 @@ const SimpleList = (props) => {
const { color } = addons.formData const { color } = addons.formData
const [visible, setVisible] = useState(false) const [visible, setVisible] = useState(false)
const [text, setText] = useState('') const [text, setText] = useState('')
const [loading, setLoading] = useState(true)
useEffect(() => {
setTimeout(() => {
setLoading(false)
}, 0)
}, [])
const onPressEnter = (i) => {
let length = [...value].length
let array = [...value]
array.splice(i + 1, 0, { key: `${length + 1}`, index: length + 1, color: colors[length % 10], value: '' })
onChange(array)
}
const inputChange = (item, i) => {
let array = []
value.forEach((v, index) => {
if (index === i) {
v.value = item
}
array.push({ ...v })
})
onChange(array)
}
const confirm = (index) => {
let array = []
value.forEach((v, i) => {
if (i !== index) {
array.push({ ...v })
}
})
onChange(array)
}
const colorClick = (item, i) => {
let array = []
value.forEach((v, index) => {
if (index === i) {
v.color = item
}
array.push({ ...v })
})
onChange(array)
}
const rest = (index) => {
let array = []
value.forEach((v, i) => {
if (index === i) {
v.color = colors[index % 10]
}
array.push({ ...v })
})
onChange(array)
}
const columns = useMemo(() => {
let columns = [
{
title: 'value',
dataIndex: 'value',
render: (r, _, i) => {
return (
<Input
onPressEnter={() => onPressEnter(i)}
onChange={(e) => inputChange(e.target.value, i)}
defaultValue={r}
/>
)
}
},
{
title: 'color',
dataIndex: 'color',
width: 30,
render: (r, _, index) => {
const content = (
<div>
<div className={styles.colorTop}>
<div className={styles.topLeft}>请选择颜色</div>
<div className={styles.topRight} onClick={() => rest(index)}>重置</div>
</div>
<div className={styles.colorBox}>
{
colors.map((v, i) => {
return (
<div className={styles.colors} style={{ background: v }} onClick={() => colorClick(v, index)}>
<CheckOutlined style={{ fontSize: '11px', marginLeft: '5px', display: r === v ? 'inline-block' : 'none' }} />
</div>
)
})
}
</div>
</div>
)
return (
<Popover
content={content}
trigger='click'
>
<div className={styles.colors} style={{ background: r || '#f2f4f5' }}></div>
</Popover>
)
}
},
{
width: 30,
render: (r, _, i) => {
return (
<Popconfirm
title='确定删除?'
onConfirm={() => confirm(i)}
onCancel={() => { }}
>
<DeleteOutlined style={{ color: 'rgb(153, 153, 153)' }} />
</Popconfirm>
)
},
},
{
title: 'Sort',
dataIndex: 'sort',
width: 30,
className: 'drag-visible',
render: () => <MenuOutlined style={{ cursor: 'grab', color: '#999', }} />,
},
]
if (!color) {
return columns.filter(v => v.dataIndex !== 'color')
}
return columns
}, [value, color])
const switchChange = (checked) => { const switchChange = (checked) => {
addons.setValue('color', checked) addons.setValue('color', checked)
if (checked) {
if (Array.isArray(value)) {
let array = []
value.forEach((v, i) => {
array.push({ ...v, color: colors[i % 10] })
})
onChange(array)
}
}
} }
const add = () => { const add = () => {
let length = [...value].length let length = [...value].length
let array = [...value] let array = [...value]
array.push({ key: `${length + 1}`, index: length + 1, value: '' }) array.push({ key: `${length + 1}`, index: length + 1, color: colors[length % 10], value: '' })
onChange(array) onChange(array)
} }
...@@ -28,11 +199,17 @@ const SimpleList = (props) => { ...@@ -28,11 +199,17 @@ const SimpleList = (props) => {
} }
const onOk = () => { const onOk = () => {
let array = text.split('\n').map((v, i) => { return { key: `${i}`, index: i, value: v } }) let array = text.split('\n').map((v, i) => { return { key: `${i}`, index: i, value: v, color: colors[i % 10] } })
onChange(array) onChange(array)
setVisible(false) setVisible(false)
} }
const dragCallBack = (array) => {
if (array) {
onChange(array)
}
}
return ( return (
<div className={styles.simpleList}> <div className={styles.simpleList}>
<div className={styles.title}> <div className={styles.title}>
...@@ -43,11 +220,19 @@ const SimpleList = (props) => { ...@@ -43,11 +220,19 @@ const SimpleList = (props) => {
</div> </div>
</div> </div>
<div className={styles.box}> <div className={styles.box}>
<DragTable {
dataSource={Array.isArray(value) ? value.map((v, i) => { return { key: `${i}`, index: i, ...v } }) : []} loading ? null :
setDataSource={onChange} <DragTable
color={color} dataSource={Array.isArray(value) ? value.map((v, i) => { return { key: `${i}`, index: i, ...v } }) : []}
/> showHeader={false}
rowKey='key'
ItemTypes='stadingOrder'
color={color}
columns={columns}
pagination={false}
dragCallBack={dragCallBack}
/>
}
</div> </div>
<div className={styles.footer}> <div className={styles.footer}>
<span onClick={add}>添加选项</span> <span onClick={add}>添加选项</span>
......
...@@ -25,4 +25,35 @@ ...@@ -25,4 +25,35 @@
} }
} }
} }
}
.colorTop {
display: flex;
justify-content: space-between;
.topLeft {
font-size: 15px;
font-weight: 700;
color: #707070;
}
.topRight {
font-size: 12px;
color: #41b3ec;
&:hover {
cursor: pointer;
}
}
}
.colorBox {
width: 130px;
display: flex;
flex-wrap: wrap;
}
.colors {
width: 20px;
height: 20px;
border-radius: 50%;
margin-top: 6px;
margin-left: 6px;
&:hover {
cursor: pointer;
}
} }
\ No newline at end of file
...@@ -127,13 +127,13 @@ export const debounce = (fn) => { ...@@ -127,13 +127,13 @@ export const debounce = (fn) => {
} }
//生成随机ID //生成随机ID
export function getNanoid(len = 6) { export function getNanoid(len = 6, left = '分组名称_') {
var orgStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var orgStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let returnStr = ""; let returnStr = "";
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
returnStr += orgStr.charAt(Math.floor((Math.random() * orgStr.length))); returnStr += orgStr.charAt(Math.floor((Math.random() * orgStr.length)));
} }
return `分组名称_${returnStr}` return `${left}${returnStr}`
} }
//手动校验配置信息 //手动校验配置信息
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment