Browse Source

fix: 对接onSelect

bill 2 years ago
parent
commit
1f993fab17

+ 0 - 1
package.json

@@ -23,7 +23,6 @@
     "classnames": "^2.3.1",
     "craco-less": "^2.0.0",
     "dom-to-image": "^2.6.0",
-    "html2canvas": "^1.4.1",
     "icons": "link:@types/@ant-design/icons",
     "js-base64": "^3.7.2",
     "lodash": "^4.17.21",

+ 2 - 1
src/api/index.ts

@@ -25,4 +25,5 @@ export * from './user'
 export * from './example'
 export * from './files'
 export * from './sys'
-export * from './board'
+export * from './board'
+export * from './tagging'

+ 17 - 0
src/api/tagging.ts

@@ -0,0 +1,17 @@
+import { TAGGING_LIST } from 'constant'
+import axios from './instance'
+
+export interface Tagging {
+  "hotIconId": number,
+  "hotIconUrl": string,
+  "getMethod": string,
+  "getUser": string,
+  "tagId": number,
+  "tagImgUrl": string,
+  "leaveBehind": string,
+  "tagDescribe": string,
+  "tagTitle": string,
+}
+
+export const fetchTaggings = async (caseId: string) => 
+  axios.get<Tagging[]>(TAGGING_LIST, { params: { caseId } })

BIN
src/assets/svg.zip


+ 5 - 1
src/constant/api.ts

@@ -47,4 +47,8 @@ export const DELETE_DRAW_FILE = '/fusion/caseFiles/draw'
 export const UPDATE_DRAW_FILE = '/fusion/caseFiles/draw'
 
 // 上传文件
-export const UPLOAD_FILE = `/fusion/upload/file`
+export const UPLOAD_FILE = `/fusion/upload/file`
+
+
+// 标注列表
+export const TAGGING_LIST = `/fusion/caseTag/allList`

+ 9 - 0
src/public.scss

@@ -68,3 +68,12 @@ html, body, #root {
     line-height: 24px;
   }
 }
+
+
+.icon {
+  cursor: pointer;
+  transition: color .3s ease;
+  &:hover {
+    color: #26559B;
+  }
+}

+ 1 - 0
src/utils/index.ts

@@ -8,6 +8,7 @@ export * from './sys'
 export * from './url'
 export * from './file-serve'
 export * from './only-open'
+export * from './transform'
 
 // 字符串转params对象
 export const strToParams = (str: string) => {

+ 19 - 0
src/utils/transform.ts

@@ -0,0 +1,19 @@
+export const base64ToBlob = (base64Data: string) => {
+  let arr = base64Data.split(",");
+  let matchs = arr[0].match(/:(.*?);/);
+  if (!matchs) {
+    return null
+  }
+
+  let fileType = matchs[1]
+  let bstr = atob(arr[1]),
+    l = bstr.length,
+    u8Arr = new Uint8Array(l);
+
+  while (l--) {
+    u8Arr[l] = bstr.charCodeAt(l);
+  }
+  return new Blob([u8Arr], {
+    type: fileType,
+  });
+};

+ 28 - 11
src/views/draw-file/board/index.js

@@ -76,9 +76,26 @@ export const create = (store, canvas) => {
     // refs.bus.emit('selectShape', null)
   })
 
+  const onSelect = (type, data = {}) => {
+    console.log(type, data)
+    refs.bus.emit('selectShape', {
+      data: { type, ...data },
+      setColor(color) {
+        layer.uiControl.setAttributes(type, 'color', color)
+      },
+      setFontSize(fontSize) {
+        layer.uiControl.setAttributes(type, 'fontSize', fontSize)
+      },
+      setText(text) {
+        layer.uiControl.setAttributes(type, 'text', text)
+      }
+    })
+  }
+
   const layer = new Layer()
   layer.start(canvas, store)
-  console.log(layer)
+  layer.uiControl.uiSelectCallback = onSelect
+  layer.uiControl.setAttributes()
 
   const board = {
     bus: refs.bus,
@@ -94,16 +111,16 @@ export const create = (store, canvas) => {
       layer.uiControl.selectUI = shapeType
       layer.uiControl.updateEventNameForSelectUI()
 
-      const definePosition = ev => {
-        const shape = createShape(refs, { type: shapeType, pos: { x: ev.offsetX, y: ev.offsetY } })
-        cleaup()
-        onFine()
-        refs.bus.emit('storeChange')
-        refs.bus.emit('selectShape', shape)
-      }
-      canvas.addEventListener('click',definePosition)
-      const cleaup = () => canvas.removeEventListener('click',definePosition)
-      return cleaup
+      // const definePosition = ev => {
+      //   const shape = createShape(refs, { type: shapeType, pos: { x: ev.offsetX, y: ev.offsetY } })
+      //   cleaup()
+      //   onFine()
+      //   refs.bus.emit('storeChange')
+      //   refs.bus.emit('selectShape', shape)
+      // }
+      // canvas.addEventListener('click',definePosition)
+      // const cleaup = () => canvas.removeEventListener('click',definePosition)
+      // return cleaup
     },
     setImage(url) {
       refs.baseMap.changeImage(url)

+ 3 - 3
src/views/draw-file/header.tsx

@@ -48,7 +48,7 @@ const Header = ({ board, type }: HeaderProps) => {
   return (
     <>
       <div className={style['df-header-left']}>
-        <span className={`${style['df-header-back']} ${style['icon']}`} onClick={() => navigate(-1)}>
+        <span className={`${style['df-header-back']} icon`} onClick={() => navigate(-1)}>
           <ArrowLeftOutlined />
           返回
         </span>
@@ -59,11 +59,11 @@ const Header = ({ board, type }: HeaderProps) => {
       <div className={style['df-header-right']}>
         <div className={style['df-header-action']}>
           <ArrowLeftOutlined 
-            className={!status.canBack ? 'disabled':  style['icon']} 
+            className={!status.canBack ? 'disabled':  'icon'} 
             onClick={() => dispatch({ type: 'board/backoff' })} 
           />
           <ArrowRightOutlined 
-            className={!status.canForward ? 'disabled': style['icon']} 
+            className={!status.canForward ? 'disabled': 'icon'} 
             onClick={() => dispatch({ type: 'board/forward' })} 
           />
         </div>

+ 96 - 93
src/views/draw-file/modal.tsx

@@ -1,77 +1,28 @@
-import { Input, Modal } from 'antd'
-import { useEffect, useRef, useState } from 'react'
+import { Input, Modal, Transfer } from 'antd'
+import { useEffect, useMemo, useRef, useState } from 'react'
 import AMapLoader from '@amap/amap-jsapi-loader';
 import style from './style.module.scss'
-// import html2canvas from 'html2canvas'
 import { SceneType, SceneTypeDomain, SceneTypePaths } from 'constant';
-import { getHref } from 'utils';
+import { base64ToBlob, getHref } from 'utils';
 import { asyncLoading } from 'components/loading';
+import { RedoOutlined } from '@ant-design/icons';
+import { fetchTaggings } from 'api'
 
+import type { Tagging } from 'api'
 
-const domScreenshot = async (dom: HTMLElement, foreignObjectRendering: boolean) => {
+const domScreenshot = async (dom: HTMLElement) => {
   const canvas = (dom.tagName.toUpperCase() === 'CANVAS' ? dom : dom.querySelector('canvas')) as HTMLCanvasElement
   return new Promise<Blob | null>(resolve => {
     if (!canvas) {
-      resolve(null)
+      return resolve(null)
     }
-    canvas.toBlob((blob) => {
-      console.log(blob)
-      if (blob) {
-        window.open(URL.createObjectURL(blob))
-      }
-      resolve(blob)
-    })
+    canvas.toBlob(resolve)
   })
-  // const imgs = Array.from(dom.querySelectorAll('img'))
-  // try {
-  //   await Promise.all(
-  //     imgs.map(img => {
-  //       if (!img.src) {
-  //         return null;
-  //       }
-  //       const req = fetch(img.src, {
-  //         method: 'get'
-  //       })
-  //       return req
-  //         .then(res => res.blob())
-  //         .then(blob => {
-  //           const render = new FileReader()
-  //           return new Promise<void>(resolve => {
-  //             render.onload = e => {
-  //               if (e.target?.result) {
-  //                 img.src = e.target?.result as string
-  //               }
-  //               resolve()
-  //             }
-  //             render.readAsDataURL(blob)
-  //           })
-  //         })
-  //     }) 
-  //   )
-  // } catch {
-  // }
-
-  // const canvas = await html2canvas(dom, {
-  //   allowTaint: true,
-  //   useCORS: true,
-  //   imageTimeout: 0,
-  //   removeContainer: false,
-  //   foreignObjectRendering,
-  //   width: dom.offsetWidth,
-  //   height: dom.offsetHeight
-  // })
-
-  // return new Promise<Blob | null>(resolve => {
-  //   canvas.toBlob((blob) => {
-  //     resolve(blob)
-  //   })
-  // })
 }
 
-
 type SelectImageProps = {
   onClose: () => void
-  onSave: (url: Blob) => void
+  onSave: (url: Blob | null, tagging: Tagging[]) => void
 }
 
 
@@ -93,11 +44,9 @@ export const SelectMap = (props: SelectImageProps) => {
 
   const onSubmit = async () => {
     if (mapEle.current) {
-      const blob = await domScreenshot(mapEle.current, false)
-      if (blob) {
-        await props.onSave(blob)
-        setOpen(false)
-      }
+      const blob = await domScreenshot(mapEle.current)
+      await props.onSave(blob, [])
+      setOpen(false)
     }
   }
 
@@ -180,42 +129,72 @@ export const SelectMap = (props: SelectImageProps) => {
 const getFuseUrl = (caseId: number) =>
   `${getHref(SceneTypeDomain[SceneType.SWMX]!, SceneTypePaths[SceneType.SWMX][0], { caseId: caseId.toString() })}&share=1#show/summary`
 
-export const SelectFuse = (props: SelectImageProps & {caseId: number}) => {
-  const url = getFuseUrl(props.caseId)
+
+const getFuseImage = async (iframe: HTMLIFrameElement) => {
+  const iframeElement = iframe.contentWindow?.document.documentElement
+  if (!iframeElement) {
+    return null
+  }
+  const extIframe = iframeElement.querySelector('.external') as HTMLIFrameElement
+  const targetIframe = extIframe || iframe
+  const targetWindow: any = targetIframe.contentWindow
+  const fuseCnavas = targetWindow.document.querySelector('.scene-canvas > canvas') as HTMLElement
+  
+  if (fuseCnavas) {
+    return domScreenshot(fuseCnavas)
+  }
+  const isLaser = targetWindow.document.querySelector('.laser-layer')
+
+  if (isLaser) {
+    const sdk = await targetWindow.__sdk
+    return new Promise<Blob | null>(resolve => {
+      sdk.scene.screenshot(900, 900).done((data: string) => {
+        resolve(base64ToBlob(data))
+      })
+    })
+  } else {
+    const sdk = targetWindow.__sdk
+    return new Promise<Blob | null>(resolve => {
+      sdk.Camera.screenshot([ {width: 2048, height: 1024, name: '2k' }],false).then((result: any)=>{
+        resolve(base64ToBlob(result[0].data))
+      })
+    })
+  }
+}
+
+
+export const SelectFuse = (props: SelectImageProps & { caseId: number }) => {
   const [open, setOpen] = useState(true)
+  const [blob, setBlob] = useState<Blob | null>(null)
+  const [tags, setTags] = useState<Tagging[]>([])
+  const [selectTags, setSelectTags] = useState<string[]>([])
+  const [addTags, setAddTags] = useState<string[]>([])
   const iframeRef = useRef<HTMLIFrameElement>(null)
+  const coverUrl = useMemo(() => blob && URL.createObjectURL(blob), [blob])
+  const url = useMemo(() => getFuseUrl(props.caseId), [props.caseId])
+  const mockData = useMemo(() => tags.map(tag => ({ 
+    data: tag, 
+    key: tag.tagId.toString()
+  })), [tags])
+
+
+  useEffect(() => {
+    fetchTaggings(props.caseId.toString()).then(setTags)
+  }, [props.caseId])
 
   const onSubmit = async () => {
-    if (iframeRef.current?.contentWindow) {
-      const iframeElement = iframeRef.current.contentWindow.document.documentElement
-
-      let fuseBody: Element | null = null
-      if (!fuseBody) {
-        const iframe = iframeElement.querySelector('.external') as HTMLIFrameElement
-        const childElement = iframe?.contentWindow?.document.documentElement
-        if (childElement) {
-          fuseBody = childElement.querySelector('.scene-canvas > canvas') || 
-            childElement.querySelector('.player[name="main"] > canvas') || 
-            childElement
-        }
-        console.log(fuseBody)
-      }
-      if (!fuseBody) {
-        fuseBody = iframeElement.querySelector('.scene-canvas > canvas')
-      }
-      if (fuseBody) {
-        const blob = await domScreenshot(fuseBody as HTMLElement, true)
-        if (blob) {
-          window.open(URL.createObjectURL(blob))
-          // setOpen(false)
-        }
-      }
+    props.onSave(blob, tags.filter(tag => addTags.includes(tag.tagId.toString())))
+  }
+  const getCover = async () => {
+    if (iframeRef.current) {
+      const blob = await getFuseImage(iframeRef.current)
+      setBlob(blob)
     }
   }
 
   return (
     <Modal 
-      width="700px"
+      width="1060px"
       title="选择户型图" 
       open={open} 
       onCancel={() => setOpen(false)}
@@ -224,8 +203,32 @@ export const SelectFuse = (props: SelectImageProps & {caseId: number}) => {
       okText="确定"
       cancelText="取消"
     >
-      <div className={style['iframe-layout']}>
-        <iframe src={url} ref={iframeRef} title="fuce-code" />
+      <div className={style['house-layout']}>
+        <div className={style['iframe-layout']}>
+          <iframe src={url} ref={iframeRef} title="fuce-code" />
+        </div>
+        <div className={style['content-layout']}>
+          <div className={style['house-tags']}>
+            <h4>请选择要同步到现场图的标注:</h4>  
+            <div className={style['tagging-transfer']}>
+              <Transfer
+                dataSource={mockData}
+                titles={['所有', '需要']}
+                targetKeys={addTags}
+                selectedKeys={selectTags}
+                onChange={setAddTags}
+                onSelectChange={(sKeys, tKeys) => setSelectTags([...sKeys, ...tKeys])}
+                render={(item) => item.data.tagTitle}
+              />
+            </div>
+          </div>  
+          <div>
+            <h4>户型图:<RedoOutlined className='icon' onClick={getCover} /></h4>
+            <div className={style['house-image']}>
+              { coverUrl && <img src={coverUrl} alt="预览图" /> }
+            </div>
+          </div>  
+        </div>
       </div>
     </Modal>
   )

+ 7 - 3
src/views/draw-file/slider.tsx

@@ -8,6 +8,7 @@ import style from './style.module.scss'
 
 import type { ShapeType, Board } from './board'
 import type { RefObject } from 'react'
+import type { Tagging } from 'api'
 
 type SliderProps = {
   board: RefObject<Board>
@@ -30,9 +31,12 @@ export const DfSlider = ({ board, type, caseId }: SliderProps) => {
       </div>
     )
   }
-  const setBoardImage = async (blob: Blob) => {
-    const url = URL.createObjectURL(blob)
-    board.current?.setImage(url)
+  const setBoardImage = async (blob: Blob | null, taggings: Tagging[]) => {
+    console.log(taggings)
+    if (blob) {
+      const url = URL.createObjectURL(blob)
+      board.current?.setImage(url)
+    }
   }
   const SelectImage = type  === BoardType.map ? SelectMap : SelectFuse
   const renderSelect = selectMode && <SelectImage

+ 52 - 8
src/views/draw-file/style.module.scss

@@ -33,13 +33,6 @@
   margin-right: 18px;
 }
 
-.icon {
-  cursor: pointer;
-  transition: color .3s ease;
-  &:hover {
-    color: #26559B;
-  }
-}
 
 .df-header-left,
 .df-header-right {
@@ -242,8 +235,13 @@ body {
   }
 }
 
+.house-layout {
+  display: flex;
+}
+
 .iframe-layout {
-  height: 500px;
+  min-height: 500px;
+  flex: 1;
   iframe {
     border: none;
     width: 100%;
@@ -251,6 +249,52 @@ body {
   }
 }
 
+
+.content-layout {
+  flex: none;
+  width: 360px;
+  display: flex;
+  flex-direction: column;
+  margin-left: 40px;
+
+  > div {
+    flex: 1 1 0;
+    display: flex;
+    flex-direction: column;
+
+    > div {
+      flex: 1;
+    }
+    h4 {
+      font-size: 14px;
+      font-weight: 400;
+      color: rgba(0,0,0,0.85);
+      line-height: 22px;
+      margin-bottom: 15px;
+      flex: none;
+
+      span {
+        float: right
+      }
+    }
+
+    &:not(:first-of-type) {
+      margin-top: 20px;
+    }
+  }
+
+  
+}
+
+.house-image {
+  border: 1px solid #D9D9D9;
+  img {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+  }
+}
+
 .add-table-row {
   text-align: center;
   margin: 10px;