123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- import { useCallback, useEffect, useMemo, useState } from "react"
- import { Button, Form, Modal, Input } from 'antd'
- import { CloseOutlined, PlusOutlined, RotateRightOutlined } from '@ant-design/icons'
- import style from './style.module.scss'
- import { title, compass } from './board'
- import ReactEditeTable, { InputEditor } from 'react-edit-table'
- import { alert } from 'utils'
- import type { Board, BoardShape, ShapeType, ExtractShape } from "./board"
- import type { ComponentType } from 'react'
- import { useSelector } from "store"
- import { usePathData } from "router"
- // import { Select } from 'antd'
- // import InputColor from 'react-input-color';
- // const ColorInput = ({ shape }: { shape: BoardShape }) => (
- // <Form.Item label="颜色" className={style['def-color-item']}>
- // <InputColor
- // initialValue={shape.data.color || '#000'}
- // onChange={(color) => shape.setColor(color.rgba)}
- // />
- // </Form.Item>
- // )
- // const sizeOptions = [6,7,8,9,10,11,12,13,14,16,18,20,28,36,48,72]
- // .map(size => ({ label: `${size}px`, value: size }))
- // const FontSizeInput = ({ shape }: { shape: ExtractShape<'fontSize'> }) => (
- // <Form.Item label="字号">
- // <Select
- // defaultValue={shape.data.fontSize}
- // style={{ width: 80 }}
- // onChange={(size) => shape.setFontSize(size)}
- // options={sizeOptions}
- // />
- // </Form.Item>
- // )
- const TextInput = ({ shape }: { shape: ExtractShape<'text'> }) => {
- const [text, setText] = useState(shape.data.text)
- const onChang = () => {
- shape.setText(text)
- // if (text !== shape.data.text) {
- // shape.setText(text)
- // }
- }
- return (
- <Form.Item label="内容">
- <Input.Group compact>
- <Input
- maxLength={50}
- style={{ width: 120 }}
- value={text}
- onKeyDown={
- ev => {
- ev.key === 'Enter' && onChang()
- ev.stopPropagation()
- }
- }
- onBlur={onChang}
- onChange={ev => setText(ev.target.value)}
- />
- <Button type="primary" onClick={onChang}>确定</Button>
- </Input.Group>
-
- </Form.Item>
- )
- }
- const ContentInput = ({ shape }: { shape: ExtractShape<'content'> }) => {
- const [edit, setEdit] = useState(false)
- const [content, setContent] = useState(shape.data.content)
- const refer = content.filter(item => item.rowIndex === 0)
- const tableAttrs = useMemo(() => {
- const dataSource: string[][] = []
- content.forEach(item => {
- let columns = dataSource[item.rowIndex]
- if (!columns) {
- columns = dataSource[item.rowIndex] = []
- }
- columns[item.colIndex] = item.value
- })
- return {
- columns: refer.map((item, i) => ({
- title: `列${i + 1}`,
- dataIndex: i,
- key: i,
- editor: {
- type: 'input',
- component: InputEditor
- }
- })),
- dataSource
- }
- }, [content, refer])
- const sortContent = () =>
- content.sort((a, b) => {
- const rowDiff: number = a.rowIndex - b.rowIndex
- if (rowDiff) {
- return rowDiff
- } else {
- return a.colIndex - b.colIndex
- }
- })
- const onChange = (data: any) => {
- const newContent = [...content]
- const item = newContent.find(item => item.rowIndex === data.rowIndex && item.colIndex === data.key)
- item!.value = data.newValue
- setContent(newContent)
- }
- const onDelete = (data: any) => {
- if (content.length <= 2) {
- return alert("表格最少需要保留一行!")
- }
- const newContent = sortContent()
- const startIndex = newContent.findIndex(item => item.rowIndex === data.rowIndex)
- const endIndex = startIndex + refer.length
- setContent([
- ...newContent.slice(0, startIndex),
- ...newContent.slice(endIndex).map(item => ({...item, rowIndex: --item.rowIndex}))
- ])
- }
- const onInsert = () => {
- const maxRow = Math.max(...content.map(item => item.rowIndex))
- setContent([
- ...content,
- ...refer.map(item => ({ ...item, value: '', rowIndex: maxRow + 1 }))
- ])
- }
- const onSubmit = useCallback(() => {
- const rowEls = Array.from(document.querySelectorAll('#edit-table .body-container .row-container')) as HTMLDivElement[]
- const bound = rowEls.map((row, rowIndex) => {
- const cells = Array.from(row.querySelectorAll('.cell')) as HTMLDivElement[]
- return cells.slice(0, -1).map((cell, colIndex) => ({
- width: cell.offsetWidth,
- height: row.offsetHeight - 1,
- value: content.find(item => item.rowIndex === rowIndex && item.colIndex === colIndex)!.value,
- colIndex,
- rowIndex
- }))
- }).flat()
- setContent(bound)
- shape.setContent(bound)
- console.log(bound)
- setEdit(false)
- }, [content, shape])
- useEffect(() => {
- if (!edit) {
- setContent(shape.data.content || [['', '']])
- }
- }, [edit, shape.data.content])
- useEffect(() => {
- if (shape.autoSet) {
- setEdit(true)
- setTimeout(onSubmit, 100)
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [])
- return (
- <Form.Item label="内容">
- <Button type="primary" onClick={() => setEdit(true)}>编辑</Button>
- <Modal open={edit} onCancel={() => setEdit(false)} onOk={onSubmit} width="430px" className="edit-table-layout">
- <div id="edit-table" onKeyDown={ev => ev.stopPropagation()}>
- { tableAttrs.dataSource.length &&
- <ReactEditeTable
- {...tableAttrs}
- onDelete={onDelete}
- onChange={onChange}
- />
- }
-
- <div className={style['add-table-row']}>
- <Button onClick={onInsert} type="primary">
- <PlusOutlined className="icon" /> 行
- </Button>
- </div>
- </div>
- </Modal>
- </Form.Item>
- )
- }
- const RotateInput = ({ shape }: { shape: ExtractShape<'rotate'> }) => (
- <Button
- onClick={() => shape.setRotate((shape.data.rotate + 90) % 360)}
- type="primary"
- style={{marginRight: '10px'}}
- >
- <RotateRightOutlined /> 旋转
- </Button>
- )
- const shapeCompontes: { [key in ShapeType]?: ComponentType<{ shape: any }> } = {
- Tag: TextInput,
- Table: ContentInput,
- Compass: RotateInput,
- Title: TextInput
- }
- export type EShapeProps = {
- board: Board
- }
- export const EShape = ({ board }: EShapeProps) => {
- const [shape, setShape] = useState<BoardShape | null>(null)
- const Edit = shape && shapeCompontes[shape.data.type]
- const disabledDelete: boolean = ([title, compass] as any).includes(shape?.data.type)
- const renderDelete = !disabledDelete && (
- <Form.Item label="删除">
- <Button type="primary" onClick={() => shape!.delete()}>删除</Button>
- </Form.Item>
- )
- useEffect(() => {
- board.bus.on('selectShape', setShape)
- return () => {
- board.bus.off('selectShape', setShape)
- }
- }, [board])
- useEffect(() => {
- const keydownHandler = (ev: KeyboardEvent) => {
- if (['Backspace', 'Delete'].includes(ev.key) && shape && !disabledDelete) {
- shape.delete()
- }
- }
- window.addEventListener('keydown', keydownHandler)
- return () => {
- window.removeEventListener('keydown', keydownHandler)
- }
- }, [board, shape, disabledDelete])
- const path = usePathData()
- const user = useSelector(store => store.user.value)
- useEffect(() => {
- if (board && path?.id === '-1') {
- board.calcTableShape([
- ["案发时间", ""],
- ["案发地点", ""],
- ["绘图单位", ""],
- ["绘图人", ""],
- ["绘图时间", ""]
- ]).then(data => {
- board.setDefaultTable(data.content, null)
- board.initHistory()
- })
- } else {
- board.initHistory()
- }
- }, [user, board, path?.id])
- return shape ? (
- <div className={style['def-shape-edit']} style={{ visibility: shape.autoSet ? "hidden" : 'visible' }}>
- { Edit && <Edit shape={shape} /> }
- { renderDelete }
- <div
- className={`ant-form-item ${style['def-close-shape-edit']}`}
- onClick={() => setShape(null)}
- >
- <CloseOutlined className={`${style['icon']}`} onClick={() => board.unSelectShape()} />
- </div>
- </div>
- ) : <></>
- }
- export default EShape
|