| ... | ... | @@ -3,7 +3,7 @@ import { QxWidgetIcon } from '@qx/common'; | 
| 3 | 3 | import { Collapse, Dropdown, Empty, Tag } from 'antd'; | 
| 4 | 4 | import cls from 'classnames'; | 
| 5 | 5 | import { cloneDeep } from 'lodash-es'; | 
| 6 |  | -import React, { useEffect, useMemo, useState } from 'react'; | 
|  | 6 | +import React, { useEffect, useState } from 'react'; | 
| 7 | 7 | import { QxBaseIcon } from '../qx-base-icon'; | 
| 8 | 8 | import { request } from '../utils'; | 
| 9 | 9 | import './index.less'; | 
| ... | ... | @@ -68,6 +68,8 @@ interface NodeFieldDisPlay { | 
| 68 | 68 | node?: INode; | 
| 69 | 69 | nodes?: INode[]; | 
| 70 | 70 | limitTypes?: string[]; | 
|  | 71 | +  value?: string; | 
|  | 72 | +  subset?: boolean; | 
| 71 | 73 | } | 
| 72 | 74 |  | 
| 73 | 75 | const icon = (icon: any) => { | 
| ... | ... | @@ -85,52 +87,18 @@ export const useNodeFieldDisplay = ({ | 
| 85 | 87 | node, | 
| 86 | 88 | nodes, | 
| 87 | 89 | limitTypes, | 
|  | 90 | +  value, | 
|  | 91 | +  subset = true, | 
| 88 | 92 | }: NodeFieldDisPlay) => { | 
| 89 |  | -  const sourceParentNodes = | 
|  | 93 | +  const sourceParentNodes = cloneDeep( | 
| 90 | 94 | getParentNodes(node, nodes).filter( | 
| 91 | 95 | (node) => !['default_DF_BRANCH'].includes(node.type), | 
| 92 |  | -    ) || []; | 
| 93 |  | - | 
| 94 |  | -  const optionalNodes = useMemo(() => { | 
| 95 |  | -    const targetParentNodes = cloneDeep(sourceParentNodes); | 
| 96 |  | -    if (!limitTypes) return targetParentNodes; | 
| 97 |  | - | 
| 98 |  | -    function getEffectiveNodes(nodes: INode[]) { | 
| 99 |  | -      return nodes.reduce<INode[]>((pre, cur) => { | 
| 100 |  | -        const curNode = cur; | 
| 101 |  | - | 
| 102 |  | -        if (Array.isArray(curNode.data?.result)) { | 
| 103 |  | -          const resultNodes = (curNode.data.result as FiledType[]).filter( | 
| 104 |  | -            (i) => { | 
| 105 |  | -              if (i.child && Array.isArray(i.child)) { | 
| 106 |  | -                // eslint-disable-next-line @typescript-eslint/no-use-before-define | 
| 107 |  | -                i.child = getEffectiveResult(i.child); | 
| 108 |  | -              } | 
| 109 |  | -              return limitTypes!.includes(i.type); | 
| 110 |  | -            }, | 
| 111 |  | -          ); | 
| 112 |  | - | 
| 113 |  | -          if (resultNodes?.length) { | 
| 114 |  | -            curNode.data.result = resultNodes; | 
| 115 |  | -            pre.push(curNode); | 
| 116 |  | -          } | 
| 117 |  | -        } | 
| 118 |  | - | 
| 119 |  | -        return pre; | 
| 120 |  | -      }, []); | 
| 121 |  | -    } | 
| 122 |  | - | 
| 123 |  | -    function getEffectiveResult(result: FiledType[]) { | 
| 124 |  | -      return result.filter((item) => { | 
| 125 |  | -        if (item.child && Array.isArray(item.child)) { | 
| 126 |  | -          item.child = getEffectiveResult(item.child); | 
| 127 |  | -        } | 
| 128 |  | -        return limitTypes?.includes(item.fieldType); | 
| 129 |  | -      }); | 
| 130 |  | -    } | 
|  | 96 | +    ) || [], | 
|  | 97 | +  ); | 
| 131 | 98 |  | 
| 132 |  | -    return getEffectiveNodes(targetParentNodes); | 
| 133 |  | -  }, [limitTypes, sourceParentNodes]); | 
|  | 99 | +  // const [targetParentNodes, setTargetParentNodes] = useState(sourceParentNodes) | 
|  | 100 | +  const [inputDisplay, setInputDisplay] = useState<React.ReactNode>(); | 
|  | 101 | +  const [optionalNodes, setOptionalNodes] = useState<INode[]>([]); // 根据 fieldType 过滤后的 nodes | 
| 134 | 102 |  | 
| 135 | 103 | const getId = (val?: string) => { | 
| 136 | 104 | if (!val) return; | 
| ... | ... | @@ -150,11 +118,12 @@ export const useNodeFieldDisplay = ({ | 
| 150 | 118 | let index = 0; | 
| 151 | 119 | while (n && index <= 20) { | 
| 152 | 120 | displayConfig = []; | 
| 153 |  | -      const curNode = optionalNodes[index] || {}; | 
|  | 121 | +      // eslint-disable-next-line @typescript-eslint/no-use-before-define | 
|  | 122 | +      const curNode = cloneDeep(optionalNodes[index]) || {}; | 
| 154 | 123 | displayConfig.push({ | 
| 155 | 124 | title: curNode.name, | 
| 156 | 125 | icon: curNode.icon, | 
| 157 |  | -        ...(optionalNodes[index] || {}), | 
|  | 126 | +        ...curNode, | 
| 158 | 127 | }); | 
| 159 | 128 | // eslint-disable-next-line @typescript-eslint/no-use-before-define | 
| 160 | 129 | recursionNodeResult(curNode.data?.result); | 
| ... | ... | @@ -187,20 +156,13 @@ export const useNodeFieldDisplay = ({ | 
| 187 | 156 | return ( | 
| 188 | 157 | <> | 
| 189 | 158 | {displayConfig?.map((item, idx) => ( | 
| 190 |  | -          <div | 
| 191 |  | -            key={idx} | 
| 192 |  | -            className='qx-node-select-input__content-item' | 
| 193 |  | -          > | 
|  | 159 | +          <div key={idx} className="qx-node-select-input__content-item"> | 
| 194 | 160 | {item.icon && ( | 
| 195 |  | -              <span | 
| 196 |  | -                className='qx-node-select-input__content-item__icon' | 
| 197 |  | -              > | 
|  | 161 | +              <span className="qx-node-select-input__content-item__icon"> | 
| 198 | 162 | {icon(item.icon)} | 
| 199 | 163 | </span> | 
| 200 | 164 | )} | 
| 201 |  | -            <span | 
| 202 |  | -              className='qx-node-select-input__content-item__text' | 
| 203 |  | -            > | 
|  | 165 | +            <span className="qx-node-select-input__content-item__text"> | 
| 204 | 166 | {item.type && | 
| 205 | 167 | FileTypeMap[item.type] && | 
| 206 | 168 | !item.icon && | 
| ... | ... | @@ -208,9 +170,7 @@ export const useNodeFieldDisplay = ({ | 
| 208 | 170 | {item.title} | 
| 209 | 171 | </span> | 
| 210 | 172 | {idx !== displayConfig.length - 1 && ( | 
| 211 |  | -              <span | 
| 212 |  | -                className='qx-node-select-input__content-item__arrow' | 
| 213 |  | -              > | 
|  | 173 | +              <span className="qx-node-select-input__content-item__arrow"> | 
| 214 | 174 | <svg | 
| 215 | 175 | xmlns="http://www.w3.org/2000/svg" | 
| 216 | 176 | width="1em" | 
| ... | ... | @@ -227,9 +187,137 @@ export const useNodeFieldDisplay = ({ | 
| 227 | 187 | ); | 
| 228 | 188 | }; | 
| 229 | 189 |  | 
|  | 190 | +  const renderInputDisplay = (val: string = value || '') => { | 
|  | 191 | +    setInputDisplay( | 
|  | 192 | +      <Tag bordered={false} className="qx-node-select-input__content"> | 
|  | 193 | +        {genDisplayDom(val)} | 
|  | 194 | +      </Tag>, | 
|  | 195 | +    ); | 
|  | 196 | +  }; | 
|  | 197 | + | 
|  | 198 | +  /*** | 
|  | 199 | +   * 查找有 formId 的 result | 
|  | 200 | +   */ | 
|  | 201 | +  const findResultByFormId = () => { | 
|  | 202 | +    const forms: FiledType[] = []; | 
|  | 203 | +    sourceParentNodes.forEach((node) => { | 
|  | 204 | +      if (Array.isArray(node.data?.result)) { | 
|  | 205 | +        // eslint-disable-next-line @typescript-eslint/no-use-before-define | 
|  | 206 | +        recursionNodeResult(node.data?.result); | 
|  | 207 | +      } | 
|  | 208 | +    }); | 
|  | 209 | + | 
|  | 210 | +    function recursionNodeResult(result: FiledType[] | FiledType) { | 
|  | 211 | +      if (!Array.isArray(result) && result) { | 
|  | 212 | +        if (result.qxProps && result.qxProps?.formId) { | 
|  | 213 | +          forms.push(result); | 
|  | 214 | +        } | 
|  | 215 | +      } | 
|  | 216 | +      if (Array.isArray(result)) { | 
|  | 217 | +        result.forEach((i) => { | 
|  | 218 | +          if (i.qxProps && i.qxProps?.formId) { | 
|  | 219 | +            forms.push(i); | 
|  | 220 | +          } else if (i.child) { | 
|  | 221 | +            recursionNodeResult(i.child); | 
|  | 222 | +          } | 
|  | 223 | +        }); | 
|  | 224 | +      } | 
|  | 225 | +    } | 
|  | 226 | + | 
|  | 227 | +    return forms; | 
|  | 228 | +  }; | 
|  | 229 | + | 
|  | 230 | +  /** | 
|  | 231 | +   * 查询所有 formId 的字段,并给 result 添加 child | 
|  | 232 | +   */ | 
|  | 233 | +  const handleGetAppsFields = async () => { | 
|  | 234 | +    const forms = findResultByFormId(); | 
|  | 235 | +    const ids = forms.map((item) => item.qxProps?.formId); | 
|  | 236 | +    if (Array.isArray(ids) && ids.length && subset) { | 
|  | 237 | +      try { | 
|  | 238 | +        const data = await getAppsFields(ids as any[]); | 
|  | 239 | +        Object.keys(data).forEach((id) => { | 
|  | 240 | +          const form = forms.find((item) => item.qxProps?.formId === id); | 
|  | 241 | +          if (!form) return; | 
|  | 242 | + | 
|  | 243 | +          const childItem = data[id].map((item: FiledType) => ({ | 
|  | 244 | +            ...item, | 
|  | 245 | +            icon: ( | 
|  | 246 | +              <span className="qx-node-select-item__icon"> | 
|  | 247 | +                <QxWidgetIcon widgetName={item.extract?.widget || 'qxInput'} /> | 
|  | 248 | +              </span> | 
|  | 249 | +            ), | 
|  | 250 | +            title: item.name, | 
|  | 251 | +            id: item.code, | 
|  | 252 | +            type: item.extract?.fieldType, | 
|  | 253 | +          })); | 
|  | 254 | + | 
|  | 255 | +          if (Array.isArray(form.child)) { | 
|  | 256 | +            form.child.push(childItem); | 
|  | 257 | +          } else { | 
|  | 258 | +            form.child = childItem; | 
|  | 259 | +          } | 
|  | 260 | +        }); | 
|  | 261 | +        // console.log(2222222, targetParentNodes, forms); | 
|  | 262 | +      } catch (error) { | 
|  | 263 | +        // appsFields = {}; | 
|  | 264 | +      } finally { | 
|  | 265 | +        renderInputDisplay(); | 
|  | 266 | +      } | 
|  | 267 | +    } | 
|  | 268 | +    return sourceParentNodes; | 
|  | 269 | +  }; | 
|  | 270 | + | 
|  | 271 | +  const getOptionalNodes = async () => { | 
|  | 272 | +    const targetParentNodes = await handleGetAppsFields(); | 
|  | 273 | +    if (!limitTypes) return setOptionalNodes(targetParentNodes); | 
|  | 274 | + | 
|  | 275 | +    function getEffectiveResult(result: FiledType[]) { | 
|  | 276 | +      const newResult = []; | 
|  | 277 | +      for (let i = 0; i < result.length; i++) { | 
|  | 278 | +        const resultItem = result[i] || {}; | 
|  | 279 | +        if (resultItem.child) { | 
|  | 280 | +          resultItem.child = getEffectiveResult(resultItem.child); | 
|  | 281 | +          if ( | 
|  | 282 | +            (Array.isArray(resultItem.child) && resultItem.child.length) || | 
|  | 283 | +            limitTypes?.includes(resultItem.type) | 
|  | 284 | +          ) { | 
|  | 285 | +            newResult.push(resultItem); | 
|  | 286 | +          } | 
|  | 287 | +        } else if (limitTypes?.includes(resultItem.type)) { | 
|  | 288 | +          newResult.push(resultItem); | 
|  | 289 | +        } | 
|  | 290 | +      } | 
|  | 291 | + | 
|  | 292 | +      return newResult; | 
|  | 293 | +    } | 
|  | 294 | + | 
|  | 295 | +    function getEffectiveNodes(nodes: INode[]) { | 
|  | 296 | +      for (let i = 0; i <= nodes.length; i++) { | 
|  | 297 | +        const node = nodes[i] || {}; | 
|  | 298 | +        const nodeResult = node.data?.result; | 
|  | 299 | +        if (Array.isArray(nodeResult) && nodeResult.length) { | 
|  | 300 | +          node.data.result = getEffectiveResult(nodeResult); | 
|  | 301 | +        } | 
|  | 302 | +      } | 
|  | 303 | + | 
|  | 304 | +      return nodes; | 
|  | 305 | +    } | 
|  | 306 | + | 
|  | 307 | +    const newNodes = getEffectiveNodes(targetParentNodes); | 
|  | 308 | +    setOptionalNodes([...newNodes]); | 
|  | 309 | +  }; | 
|  | 310 | + | 
|  | 311 | +  useEffect(() => { | 
|  | 312 | +    getOptionalNodes(); | 
|  | 313 | +  }, []); | 
|  | 314 | + | 
| 230 | 315 | return { | 
| 231 | 316 | genDisplayDom, | 
| 232 | 317 | optionalNodes, | 
|  | 318 | +    // targetParentNodes, | 
|  | 319 | +    renderInputDisplay, | 
|  | 320 | +    inputDisplay, | 
| 233 | 321 | }; | 
| 234 | 322 | }; | 
| 235 | 323 |  | 
| ... | ... | @@ -266,7 +354,7 @@ const SelectItem = (props: any) => { | 
| 266 | 354 | ); | 
| 267 | 355 | } | 
| 268 | 356 |  | 
| 269 |  | -  if (!props.child) { | 
|  | 357 | +  if (!props.child || !props.child.length) { | 
| 270 | 358 | return ( | 
| 271 | 359 | <div | 
| 272 | 360 | className={cls('qx-node-select-item')} | 
| ... | ... | @@ -317,14 +405,10 @@ export const QxFlowNodeFieldSelector: React.FC<NodeFieldSelectProps> = ( | 
| 317 | 405 |  | 
| 318 | 406 | const [visible, setVisible] = useState(false); | 
| 319 | 407 |  | 
| 320 |  | -  const [inputDisplay, setInputDisplay] = useState<React.ReactNode>(); | 
| 321 |  | - | 
| 322 |  | -  const { optionalNodes, genDisplayDom } = useNodeFieldDisplay(props); | 
|  | 408 | +  const { optionalNodes, renderInputDisplay, inputDisplay } = | 
|  | 409 | +    useNodeFieldDisplay(props); | 
| 323 | 410 |  | 
| 324 | 411 | const getOptions = () => { | 
| 325 |  | -    if (!optionalNodes.length) { | 
| 326 |  | -      return <Empty />; | 
| 327 |  | -    } | 
| 328 | 412 | return optionalNodes.map((node) => ({ | 
| 329 | 413 | label: ( | 
| 330 | 414 | <div className={cls('qx-node-select-dropdown-header')}> | 
| ... | ... | @@ -352,96 +436,6 @@ export const QxFlowNodeFieldSelector: React.FC<NodeFieldSelectProps> = ( | 
| 352 | 436 | })); | 
| 353 | 437 | }; | 
| 354 | 438 |  | 
| 355 |  | -  const getForms = () => { | 
| 356 |  | -    const forms: FiledType[] = []; | 
| 357 |  | -    optionalNodes.forEach((node) => { | 
| 358 |  | -      if (Array.isArray(node.data?.result)) { | 
| 359 |  | -        // eslint-disable-next-line @typescript-eslint/no-use-before-define | 
| 360 |  | -        recursionNodeResult(node.data?.result); | 
| 361 |  | -      } | 
| 362 |  | -    }); | 
| 363 |  | - | 
| 364 |  | -    function recursionNodeResult(result: FiledType[] | FiledType) { | 
| 365 |  | -      if (!Array.isArray(result) && result) { | 
| 366 |  | -        if (result.qxProps && result.qxProps?.formId) { | 
| 367 |  | -          forms.push(result); | 
| 368 |  | -        } | 
| 369 |  | -      } | 
| 370 |  | -      if (Array.isArray(result)) { | 
| 371 |  | -        result.forEach((i) => { | 
| 372 |  | -          if (i.qxProps && i.qxProps?.formId) { | 
| 373 |  | -            forms.push(i); | 
| 374 |  | -          } else if (i.child) { | 
| 375 |  | -            recursionNodeResult(i.child); | 
| 376 |  | -          } | 
| 377 |  | -        }); | 
| 378 |  | -      } | 
| 379 |  | -    } | 
| 380 |  | - | 
| 381 |  | -    return forms; | 
| 382 |  | -  }; | 
| 383 |  | - | 
| 384 |  | -  const renderInputDisplay = (val: string = props.value || '') => { | 
| 385 |  | -    setInputDisplay( | 
| 386 |  | -      <Tag bordered={false} className="qx-node-select-input__content"> | 
| 387 |  | -        {genDisplayDom(val)} | 
| 388 |  | -      </Tag>, | 
| 389 |  | -    ); | 
| 390 |  | -  }; | 
| 391 |  | - | 
| 392 |  | -  const handleGetAppsFields = async () => { | 
| 393 |  | -    const forms = getForms(); | 
| 394 |  | -    const ids = forms.map( | 
| 395 |  | -      (item) => item.qxProps?.formId && !props.limitTypes?.includes(item.type), | 
| 396 |  | -    ); | 
| 397 |  | -    console.log(ids, 'ids') | 
| 398 |  | -    if (Array.isArray(ids) && ids.length) { | 
| 399 |  | -      try { | 
| 400 |  | -        const data = await getAppsFields(ids as any[]); | 
| 401 |  | -        Object.keys(data).forEach((id) => { | 
| 402 |  | -          forms.forEach((i) => { | 
| 403 |  | -            if (i.qxProps?.formId === id) { | 
| 404 |  | -              if (Array.isArray(i.child)) { | 
| 405 |  | -                i.child.push( | 
| 406 |  | -                  data[id].map((item: FiledType) => ({ | 
| 407 |  | -                    icon: ( | 
| 408 |  | -                      <span className="qx-node-select-item__icon"> | 
| 409 |  | -                        <QxWidgetIcon | 
| 410 |  | -                          widgetName={item.extract?.widget || 'qxInput'} | 
| 411 |  | -                        /> | 
| 412 |  | -                      </span> | 
| 413 |  | -                    ), | 
| 414 |  | -                    title: item.name, | 
| 415 |  | -                    code: item.code, | 
| 416 |  | -                    id: item.code, | 
| 417 |  | -                    type: item.extract?.fieldType, | 
| 418 |  | -                  })), | 
| 419 |  | -                ); | 
| 420 |  | -              } else { | 
| 421 |  | -                i.child = data[id].map((item: FiledType) => ({ | 
| 422 |  | -                  icon: ( | 
| 423 |  | -                    <span className="qx-node-select-item__icon"> | 
| 424 |  | -                      <QxWidgetIcon | 
| 425 |  | -                        widgetName={item.extract?.widget || 'qxInput'} | 
| 426 |  | -                      /> | 
| 427 |  | -                    </span> | 
| 428 |  | -                  ), | 
| 429 |  | -                  title: item.name, | 
| 430 |  | -                  code: item.code, | 
| 431 |  | -                  id: item.code, | 
| 432 |  | -                  type: item.extract?.fieldType, | 
| 433 |  | -                })); | 
| 434 |  | -              } | 
| 435 |  | -            } | 
| 436 |  | -          }); | 
| 437 |  | -        }); | 
| 438 |  | -        renderInputDisplay(); | 
| 439 |  | -      } catch (error) { | 
| 440 |  | -        // appsFields = {}; | 
| 441 |  | -      } | 
| 442 |  | -    } | 
| 443 |  | -  }; | 
| 444 |  | - | 
| 445 | 439 | const handleItemClick = (nodeKey: string, item: any) => { | 
| 446 | 440 | const newValue = '${' + `${nodeKey}|${item.id}` + '}'; | 
| 447 | 441 | props.onChange?.(newValue, item); | 
| ... | ... | @@ -452,23 +446,27 @@ export const QxFlowNodeFieldSelector: React.FC<NodeFieldSelectProps> = ( | 
| 452 | 446 | }; | 
| 453 | 447 |  | 
| 454 | 448 | useEffect(() => { | 
| 455 |  | -    handleGetAppsFields(); | 
| 456 |  | -  }, []); | 
| 457 |  | - | 
| 458 |  | -  useEffect(() => { | 
| 459 | 449 | setVisible(props.open ?? false); | 
| 460 | 450 | }, [props.open]); | 
| 461 | 451 |  | 
|  | 452 | +  const dropdownRender = () => { | 
|  | 453 | +    const items = getOptions(); | 
|  | 454 | +    if (Array.isArray(items) && items.length) { | 
|  | 455 | +      return <Collapse ghost expandIconPosition="end" items={items} />; | 
|  | 456 | +    } else { | 
|  | 457 | +      return <Empty />; | 
|  | 458 | +    } | 
|  | 459 | +  }; | 
|  | 460 | + | 
| 462 | 461 | return ( | 
| 463 | 462 | <div className={cls('qx-node-select')}> | 
| 464 | 463 | <Dropdown | 
|  | 464 | +        destroyPopupOnHide | 
| 465 | 465 | trigger={['click']} | 
| 466 | 466 | overlayStyle={{ width: `${props?.width}px` }} | 
| 467 | 467 | overlayClassName={cls('qx-node-select-dropdown')} | 
| 468 | 468 | open={visible} | 
| 469 |  | -        dropdownRender={() => ( | 
| 470 |  | -          <Collapse ghost expandIconPosition="end" items={getOptions()} /> | 
| 471 |  | -        )} | 
|  | 469 | +        dropdownRender={dropdownRender} | 
| 472 | 470 | onOpenChange={(open) => { | 
| 473 | 471 | if (mode === 'select') { | 
| 474 | 472 | setVisible(open); | 
| ... | ... | @@ -505,6 +503,7 @@ export interface NodeFieldSelectProps { | 
| 505 | 503 | width?: number; | 
| 506 | 504 | mode?: 'select' | 'variable'; | 
| 507 | 505 | open?: boolean; | 
|  | 506 | +  subset?: boolean; | 
| 508 | 507 | } | 
| 509 | 508 |  | 
| 510 | 509 | export interface FiledType { | 
... | ... |  |