index.js 11.8 KB
Newer Older
1
import React, { useRef, useState, forwardRef, useImperativeHandle, useMemo, useLayoutEffect, useEffect } from 'react'
田翔's avatar
田翔 committed
2
import { message, Modal, Button, Popover } from 'antd'
3
import { ExclamationCircleOutlined } from '@ant-design/icons'
田翔's avatar
田翔 committed
4
import QRcode from 'qrcode.react'
田翔's avatar
田翔 committed
5
import Generator, { defaultSettings, defaultCommonSettings, defaultGlobalSettings } from 'fr-generator'
田翔's avatar
田翔 committed
6 7
import { settings, baseSettings, globalSettings } from './config'
import widgets from '../widgets'
8
import { saveTableConfig, GetTableConfigJson, ReloadTableFields } from '../../apis/process'
田翔's avatar
田翔 committed
9
import FormRender from '../FormRender'
10
import { isObject, getVerify, setFieldJson, getNanoid } from '../../utils'
11
import Drag from '../components/Drag'
田翔's avatar
田翔 committed
12
import styles from '../../main.less'
田翔's avatar
田翔 committed
13

14
const FormDesigner = (props, ref) => {
田翔's avatar
田翔 committed
15

田翔's avatar
田翔 committed
16 17 18 19 20
  useImperativeHandle(ref, () => (
    {
      clear,
      preview,
      submit,
田翔's avatar
田翔 committed
21
      initLayout,
田翔's avatar
田翔 committed
22 23 24
    }
  ))

田翔's avatar
田翔 committed
25
  const { tableName, extra } = props
田翔's avatar
田翔 committed
26 27
  const [visible, setVisible] = useState(false)
  const [schema, setSchema] = useState({})
28
  const [fieldName, setFieldName] = useState([])
29
  const [textShow, setTextShow] = useState(false)
田翔's avatar
田翔 committed
30
  const [open, setOpen] = useState(false)
31
  const designerRef = useRef(null)
田翔's avatar
田翔 committed
32
  const formRenderRef = useRef(null)
33

34 35 36 37
  const settingsParent = useMemo(() => {
    let settingsParent = []
    settings.forEach(v => {
      v.widgets.forEach(s => {
田翔's avatar
田翔 committed
38
        s.schema.tableNameParent = tableName
39 40 41 42 43 44
      })
      settingsParent.push(v)
    })
    return settingsParent
  }, [settings, tableName])

45
  const getJSON = (json, fieldName) => {
46
    let nanoid = getNanoid()
田翔's avatar
田翔 committed
47 48
    let properties = json?.properties
    let parent = {}
田翔's avatar
田翔 committed
49
    let paths = []
50
    let title = '(未分组)'
田翔's avatar
田翔 committed
51 52 53
    let ungroupedChild = {}
    if (isObject(properties)) {
      for (let v in properties) {
田翔's avatar
田翔 committed
54 55
        //分组或者描述控件
        if (properties[v].type === 'object' || properties[v].type === 'html') {
田翔's avatar
田翔 committed
56
          parent[v] = properties[v]
57
          if (v !== '(未分组)') {
田翔's avatar
田翔 committed
58
            paths.push(v)
59 60
          } else {
            title = properties[v].title
田翔's avatar
田翔 committed
61 62 63 64
          }
          let child = properties[v]?.properties
          if (isObject(child)) {
            for (let s in child) {
65 66 67 68 69 70
              let IsSystemField = false
              fieldName.forEach(j => {
                if (j.name === s) {
                  IsSystemField = j.IsSystemField
                }
              })
田翔's avatar
田翔 committed
71
              child[s].tableNameParent = tableName
72 73
              child[s].tableTypeParent = json.tableType
              child[s].IsSystemField = IsSystemField
田翔's avatar
田翔 committed
74 75 76 77 78
            }
          }
        } else {
          ungroupedChild[v] = {
            ...properties[v],
79
            tableTypeParent: json.tableType,
田翔's avatar
田翔 committed
80
            tableNameParent: tableName
81 82 83 84
          }
        }
      }
    }
田翔's avatar
田翔 committed
85
    if (JSON.stringify(ungroupedChild) !== '{}') {
86
      parent[nanoid] = {
87
        title: title,
88
        type: 'object',
田翔's avatar
田翔 committed
89 90 91 92 93
        collapsed: false,
        properties: {
          ...ungroupedChild
        }
      }
94
      paths.push(nanoid)
田翔's avatar
田翔 committed
95 96 97 98 99 100
    }
    return {
      ...json,
      paths,
      properties: parent,
    }
101
  }
田翔's avatar
田翔 committed
102

田翔's avatar
田翔 committed
103
  const getTableConfig = async (tableName) => {
田翔's avatar
田翔 committed
104
    const { code, data, msg } = await GetTableConfigJson({ tableName })
105 106 107 108 109 110
    const res = await ReloadTableFields({ tableName })
    if (code === 0 && res.data && res.data.root) {
      if (data && typeof data === 'string' && Array.isArray(res.data.root)) {
        setFieldName(res.data.root)
        let json = getJSON(JSON.parse(data), res.data.root)
        designerRef?.current?.setValue(json)
111
        setSchema(json)
田翔's avatar
田翔 committed
112 113
      }
    } else {
田翔's avatar
田翔 committed
114
      message.error(msg)
田翔's avatar
田翔 committed
115
    }
田翔's avatar
田翔 committed
116 117
  }

118 119 120 121
  useEffect(() => {
    window.designerRef = designerRef
  }, [designerRef])

田翔's avatar
田翔 committed
122 123 124 125
  useLayoutEffect(() => {
    if (tableName) {
      getTableConfig(tableName)
    }
126 127 128 129
    sessionStorage.setItem('FormRender', 'preview')
    return () => {
      sessionStorage.removeItem('FormRender')
    }
田翔's avatar
田翔 committed
130
  }, [tableName])
田翔's avatar
田翔 committed
131

田翔's avatar
田翔 committed
132 133 134 135 136 137 138 139 140
  const clear = () => {
    Modal.confirm({
      title: '确定要清空该表单配置吗?',
      icon: <ExclamationCircleOutlined />,
      centered: true,
      cancelText: '取消',
      okText: '确定',
      onCancel: () => {
        console.log('取消清空')
141
      },
田翔's avatar
田翔 committed
142 143
      onOk: () => {
        const schema = designerRef.current.getValue()
田翔's avatar
田翔 committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
        const { tableName, properties: parent } = schema
        let parentObj = {}
        if (tableName?.includes('设备_台账')) {
          for (let v in parent) {
            if (isObject(parent)) {
              let child = parent[v].properties
              if (isObject(child)) {
                for (let s in child) {
                  if (s === '编码') {
                    parentObj = {
                      [v]: {
                        ...parent[v],
                        properties: {
                          [s]: child[s]
                        }
                      },
                    }
                  }
                }
              }
            }
田翔's avatar
田翔 committed
165
          }
田翔's avatar
田翔 committed
166
        }
田翔's avatar
田翔 committed
167 168 169 170
        let newschema = {
          ...schema,
          properties: parentObj
        }
田翔's avatar
田翔 committed
171
        designerRef.current.setValue(newschema)
田翔's avatar
田翔 committed
172
      }
田翔's avatar
田翔 committed
173 174 175 176
    })
  }

  const preview = () => {
177
    let json = designerRef.current.getValue()
178 179
    let verify = getVerify(json)
    if (verify === true) {
180
      setTextShow(false)
181 182 183
      setVisible(true)
      setSchema(json)
    }
田翔's avatar
田翔 committed
184 185 186
  }

  const submit = async () => {
187
    let json = designerRef.current.getValue()
188 189 190 191 192 193 194 195 196 197 198 199
    let verify = getVerify(json)
    if (verify === true) {
      const { code, data, msg } = await saveTableConfig(json)
      if (code === 0) {
        message.info('保存成功')
      } else {
        message.error(msg)
      }
    }
  }

  const initLayout = () => {
200
    let json = designerRef.current.getValue()
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
    let verify = getVerify(json)
    if (verify === true) {
      Modal.confirm({
        title: '是否确定使用剩余附加字段一键生成表单?',
        icon: <ExclamationCircleOutlined />,
        centered: true,
        cancelText: '取消',
        okText: '确定',
        onCancel: () => {
          console.log('取消一键生成')
        },
        onOk: () => {
          designerRef.current.setValue(setFieldJson(json, fieldName))
        }
      })
田翔's avatar
田翔 committed
216
    }
田翔's avatar
田翔 committed
217 218
  }

田翔's avatar
田翔 committed
219 220 221 222 223 224 225 226 227 228 229
  const extraButtons = useMemo(() => {
    if (extra) {
      return [
        false,
        false,
        true,
        true,
        {
          text: '清空',
          onClick: clear
        },
230 231 232 233
        {
          text: '一键布局',
          onClick: initLayout,
        },
田翔's avatar
田翔 committed
234 235 236 237 238 239 240
        {
          text: '预览',
          onClick: preview
        },
        {
          text: '提交',
          type: 'primary',
241 242
          onClick: submit,
        },
田翔's avatar
田翔 committed
243 244 245
      ]
    }
    return [false, false, false, false]
246
  }, [extra, fieldName])
田翔's avatar
田翔 committed
247

田翔's avatar
田翔 committed
248
  const getFields = (schema) => {
249
    let array = []
250 251 252
    let paths = []
    let inError = false
    let outError = false
田翔's avatar
田翔 committed
253
    let parent = schema?.properties
254
    let parentObj = {}
田翔's avatar
田翔 committed
255 256
    if (isObject(parent)) {
      for (let v in parent) {
257 258 259 260 261 262 263
        if (parent[v].type === 'object') {
          paths.push(v)
          parentObj[v] = parent[v]
          let child = parent[v]?.properties
          if (isObject(child)) {
            let childObj = {}
            for (let s in child) {
田翔's avatar
田翔 committed
264 265 266 267
              if (child[s].type === 'html') {
                outError = true
              }
              if (child[s].type !== 'object' && child[s].type !== 'html') {
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
                array.push({ ...child[s], fieldName: s })
                childObj[s] = child[s]
              } else {
                inError = true
                let sun = child[s]?.properties
                if (isObject(sun)) {
                  for (let k in sun) {
                    array.push({ ...sun[k], fieldName: k })
                    childObj[k] = sun[k]
                  }
                }
              }
            }
            parentObj[v].properties = childObj
          }
田翔's avatar
田翔 committed
283 284
        } else if (parent[v].type === 'html') {
          parentObj[v] = parent[v]
285 286 287 288 289 290 291 292 293 294 295
        } else {
          outError = true
          let nanoid = getNanoid()
          paths.push(nanoid)
          parentObj[nanoid] = {
            title: '(未分组)',
            type: 'object',
            collapsed: false,
            properties: {
              [v]: parent[v]
            }
田翔's avatar
田翔 committed
296 297 298 299
          }
        }
      }
    }
300 301 302 303 304 305 306 307 308 309
    return {
      inError,
      outError,
      fields: array,
      newSchema: {
        ...schema,
        paths,
        properties: parentObj
      }
    }
田翔's avatar
田翔 committed
310 311 312
  }

  const onSchemaChange = (schema) => {
313
    let { inError, outError, fields, newSchema } = getFields(schema)
314
    window.designer = fields
315 316 317
    if (inError || outError) {
      designerRef.current.setValue(newSchema)
    }
318 319
  }

田翔's avatar
田翔 committed
320
  const testSubmit = async () => {
田翔's avatar
田翔 committed
321
    const { formValue, relationForm, errors } = await formRenderRef?.current?.getValues()
322
    console.log(formValue, relationForm)
田翔's avatar
田翔 committed
323
    if (errors.length) {
324
      setTextShow(false)
田翔's avatar
田翔 committed
325
      return message.error('表单校验未通过!')
326 327
    } else {
      setTextShow(true)
田翔's avatar
田翔 committed
328
    }
田翔's avatar
田翔 committed
329 330
  }

331
  const canDelete = (values) => {
田翔's avatar
田翔 committed
332
    const { $id, tableTypeParent, IsSystemField, title, type } = values
333 334
    if (tableTypeParent === '物联设备表' && $id === '编码') {
      message.info(`表类型为:【物联设备表】内部字段【编码】不允许删除!`)
田翔's avatar
田翔 committed
335 336
      return false
    }
337 338 339 340 341 342 343 344 345 346
    // if (tableTypeParent === '物联设备表' && IsSystemField) {
    //   if (fieldName.some(v => v.name === $id)) {
    //     message.info(`表类型为:【物联设备表】内部字段【${$id}】不允许删除!`)
    //     return false
    //   }
    // }
    // if (type === 'object' && ($id === '#/物联数据' || $id === '物联数据')) {
    //   message.info(`表类型为:【物联设备表】包含内部字段不允许删除`)
    //   return false
    // }
347 348 349
    return true
  }

田翔's avatar
田翔 committed
350 351 352 353
  const onOpenChange = (value) => {
    console.log('value', value)
  }

田翔's avatar
田翔 committed
354 355 356 357
  if (!tableName) {
    return null
  }

田翔's avatar
田翔 committed
358
  return (
田翔's avatar
田翔 committed
359
    <div className={styles.pandaXform} style={{ height: '100%' }}>
田翔's avatar
田翔 committed
360
      <div style={{ width: '100%', height: '100%' }}>
361
        <Generator
田翔's avatar
田翔 committed
362
          // configProvider={{ prefixCls: prefixClsPandaXform }}
363
          canDelete={canDelete}
364 365 366
          mapping={{
            object: 'Header',
          }}
367
          validation={false}
368
          ref={designerRef}
369 370
          extraButtons={extraButtons}
          widgets={widgets}
371
          settings={settingsParent}
372 373
          commonSettings={baseSettings}
          globalSettings={globalSettings}
田翔's avatar
田翔 committed
374
          onSchemaChange={onSchemaChange}
375
        />
376
        <Drag
377 378 379 380 381 382
          title={tableName}
          width='80%'
          bodyStyle={{ height: '700px', overflow: 'auto' }}
          onOk={() => setVisible(false)}
          onCancel={() => setVisible(false)}
          visible={visible}
田翔's avatar
田翔 committed
383
          getContainer={false}
384
          destroyOnClose
田翔's avatar
田翔 committed
385 386
          footer={
            <div>
387
              <span style={{ color: '#18b918', fontSize: '14px', paddingRight: '10px', display: textShow ? 'inline-block' : 'none' }}>该表单内容校验通过,可正常使用!</span>
田翔's avatar
田翔 committed
388
              <Button onClick={() => setVisible(false)}>取消</Button>
田翔's avatar
田翔 committed
389 390 391 392 393 394 395 396 397 398 399 400
              <Popover
                trigger="click"
                open={open}
                onOpenChange={onOpenChange}
                content={
                  <div>
                    <QRcode value={`${window.origin}/civ_mobile/product/workflow/formTest?env=dev&tableName=${tableName}`} />
                  </div>
                }
              >
                <Button>移动端预览</Button>
              </Popover>
401
              <Button type='primary' onClick={testSubmit}>测试提交</Button>
田翔's avatar
田翔 committed
402 403
            </div>
          }
404
        >
405 406 407
          <FormRender
            ref={formRenderRef}
            schemaValues={{ formJson: schema }}
408 409 410 411
          // codes={{
          //   工单编号: '12421413431',
          //   事件编号: 'PO230000013',
          // }}
412
          />
413
        </Drag>
414
      </div>
田翔's avatar
田翔 committed
415
    </div>
田翔's avatar
田翔 committed
416 417 418 419
  )

}

420
export default forwardRef(FormDesigner)