bill 2 vuotta sitten
vanhempi
commit
04b8cad265

+ 1 - 0
package.json

@@ -18,6 +18,7 @@
     "@types/react-dom": "^18.0.6",
     "@types/react-grid-layout": "^1.3.2",
     "antd": "^4.21.7",
+    "antd-img-crop": "^4.12.1",
     "axios": "^0.27.2",
     "canvas-nest.js": "^2.0.4",
     "classnames": "^2.3.1",

+ 51 - 6
pnpm-lock.yaml

@@ -16,6 +16,7 @@ specifiers:
   '@types/react-dom': ^18.0.6
   '@types/react-grid-layout': ^1.3.2
   antd: ^4.21.7
+  antd-img-crop: ^4.12.1
   axios: ^0.27.2
   canvas-nest.js: ^2.0.4
   classnames: ^2.3.1
@@ -57,6 +58,7 @@ dependencies:
   '@types/react-dom': 18.0.9
   '@types/react-grid-layout': 1.3.2
   antd: 4.24.4_biqbaboplfbrettd7655fr4n2y
+  antd-img-crop: 4.12.1_mmr7wo2mwcqxydifh6vjmnf2sq
   axios: 0.27.2
   canvas-nest.js: 2.0.4
   classnames: 2.3.2
@@ -3245,6 +3247,21 @@ packages:
     engines: {node: '>=10'}
     dev: false
 
+  /antd-img-crop/4.12.1_mmr7wo2mwcqxydifh6vjmnf2sq:
+    resolution: {integrity: sha512-JoYzTw7e49xLWcUBwYvazLd8B+F0+r77eB6nHfppboETEl74lQrTG5RHBdl32J9jtFZ0Ag2YaPvrrttqIgjPjA==}
+    peerDependencies:
+      antd: '>=4.0.0'
+      react: '>=16.8.0'
+      react-dom: '>=16.8.0'
+    dependencies:
+      antd: 4.24.4_biqbaboplfbrettd7655fr4n2y
+      compare-versions: 6.0.0-rc.1
+      react: 18.2.0
+      react-dom: 18.2.0_react@18.2.0
+      react-easy-crop: 4.7.4_biqbaboplfbrettd7655fr4n2y
+      tslib: 2.5.0
+    dev: false
+
   /antd/4.24.4_biqbaboplfbrettd7655fr4n2y:
     resolution: {integrity: sha512-XKZYMCKTQV+z0kZkAodvWMph9EhnNLon4JrWOoi2rjBzsYKKOVNEceXi3TZy/wdF6GcawjBduL3gd6NtWcTe4A==}
     peerDependencies:
@@ -3806,7 +3823,7 @@ packages:
     resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
     dependencies:
       pascal-case: 3.1.2
-      tslib: 2.4.1
+      tslib: 2.5.0
     dev: false
 
   /camelcase-css/2.0.1:
@@ -4021,6 +4038,10 @@ packages:
     resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
     dev: false
 
+  /compare-versions/6.0.0-rc.1:
+    resolution: {integrity: sha512-cFhkjbGY1jLFWIV7KegECbfuyYPxSGvgGkdkfM+ibboQDoPwg2FRHm5BSNTOApiauRBzJIQH7qvOJs2sW5ueKQ==}
+    dev: false
+
   /compressible/2.0.18:
     resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==}
     engines: {node: '>= 0.6'}
@@ -4717,7 +4738,7 @@ packages:
     resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
     dependencies:
       no-case: 3.0.4
-      tslib: 2.4.1
+      tslib: 2.5.0
     dev: false
 
   /dotenv-expand/5.1.0:
@@ -7239,7 +7260,7 @@ packages:
   /lower-case/2.0.2:
     resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
     dependencies:
-      tslib: 2.4.1
+      tslib: 2.5.0
     dev: false
 
   /lru-cache/6.0.0:
@@ -7467,7 +7488,7 @@ packages:
     resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
     dependencies:
       lower-case: 2.0.2
-      tslib: 2.4.1
+      tslib: 2.5.0
     dev: false
 
   /node-forge/1.3.1:
@@ -7498,6 +7519,10 @@ packages:
     engines: {node: '>=10'}
     dev: false
 
+  /normalize-wheel/1.0.1:
+    resolution: {integrity: sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==}
+    dev: false
+
   /npm-run-path/4.0.1:
     resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
     engines: {node: '>=8'}
@@ -7716,7 +7741,7 @@ packages:
     resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
     dependencies:
       dot-case: 3.0.4
-      tslib: 2.4.1
+      tslib: 2.5.0
     dev: false
 
   /parent-module/1.0.1:
@@ -7754,7 +7779,7 @@ packages:
     resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
     dependencies:
       no-case: 3.0.4
-      tslib: 2.4.1
+      tslib: 2.5.0
     dev: false
 
   /path-exists/3.0.0:
@@ -9365,6 +9390,18 @@ packages:
       react-dom: 18.2.0_react@18.2.0
     dev: false
 
+  /react-easy-crop/4.7.4_biqbaboplfbrettd7655fr4n2y:
+    resolution: {integrity: sha512-oDi1375Jo/zuPUvo3oauxnNbfy8L4wsbmHD1KB2vT55fdgu+q8/K0w/rDWzy9jz4jfQ94Q9+3Yu366sDDFVmiA==}
+    peerDependencies:
+      react: '>=16.4.0'
+      react-dom: '>=16.4.0'
+    dependencies:
+      normalize-wheel: 1.0.1
+      react: 18.2.0
+      react-dom: 18.2.0_react@18.2.0
+      tslib: 2.0.1
+    dev: false
+
   /react-edit-table/0.1.1-experimental.4:
     resolution: {integrity: sha512-P3rHAb4O8uXubgfgG5hWr3JGqc0dAjIk2S27+Xl/OSXcbxSogwnOfaml2VRUAPfnJg9mUpfyDRQ3HDXKUgHSrA==}
     dependencies:
@@ -10723,10 +10760,18 @@ packages:
     resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
     dev: false
 
+  /tslib/2.0.1:
+    resolution: {integrity: sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==}
+    dev: false
+
   /tslib/2.4.1:
     resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==}
     dev: false
 
+  /tslib/2.5.0:
+    resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==}
+    dev: false
+
   /tsutils/3.21.0_typescript@4.9.3:
     resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
     engines: {node: '>= 6'}

+ 4 - 0
src/react-app-env.d.ts

@@ -1,5 +1,9 @@
 /// <reference types="react-scripts" />
 
+declare module 'antd-img-crop' {
+  export default any
+}
+
 type OmitBasic<T, U> = T extends U ? never : T
 
 type ExcludeObject<T, U> = {

+ 1 - 1
src/version.json

@@ -2,5 +2,5 @@
   "env": "dev",
   "main": 1.2,
   "append": "alpha",
-  "version": 16
+  "version": 17
 }

+ 47 - 0
src/views/draw-file/modal.tsx

@@ -1,4 +1,6 @@
 import { Empty, Input, Modal } from 'antd'
+import { Button, Upload, message } from 'antd'
+import ImgCrop from 'antd-img-crop'
 import { useEffect, useMemo, useRef, useState } from 'react'
 import AMapLoader from '@amap/amap-jsapi-loader';
 import style from './style.module.scss'
@@ -8,8 +10,10 @@ import { asyncLoading } from 'components/loading';
 import { RedoOutlined } from '@ant-design/icons';
 import { fetchTaggings } from 'api'
 import { SortTransfer } from './sort-transfer'
+import { BoardType, BoardTypeDesc } from 'api'
 
 import type { Tagging } from 'api'
+import type { UploadProps } from 'antd'
 
 const domScreenshot = async (dom: HTMLElement) => {
   const canvas = (dom.tagName.toUpperCase() === 'CANVAS' ? dom : dom.querySelector('canvas')) as HTMLCanvasElement
@@ -312,4 +316,47 @@ export const SelectFuse = (props: SelectImageProps & { caseId: number }) => {
       </div>
     </Modal>
   )
+}
+
+
+
+type DfUploadCropProp = Partial<SelectImageProps> & {
+  type: BoardType
+  caseId: number
+}
+export const DfUploadCrop = ({ type, caseId, onClose, onSave }: DfUploadCropProp) => {
+  const onUpload: UploadProps['beforeUpload'] = async file => {
+    const filename = file.name
+    
+    const ext = filename.substring(filename.lastIndexOf('.'))
+    const isImg = ['.png', '.jpg'].includes(ext.toLocaleLowerCase())
+    if (!isImg) {
+      message.error('只能上传png或jpg文件')
+      return Upload.LIST_IGNORE
+    } else if (file.size > 100 * 1024 * 1024) {
+      message.error('大小在100MB以内')
+      return Upload.LIST_IGNORE
+    }
+    const img = new Image();
+    img.src = URL.createObjectURL(file);
+    await new Promise(resolve => {
+      img.onload = resolve
+    })
+    const $canvas = document.createElement("canvas");
+    $canvas.width = 540;
+    $canvas.height = 390;
+    const ctx = $canvas.getContext("2d")
+    ctx?.drawImage(img, 0, 0, 540, 390);
+    const blob = await domScreenshot($canvas);
+    onSave && onSave(blob, [])
+    return Upload.LIST_IGNORE
+  }
+
+  return (
+    <ImgCrop rotationSlider modalTitle={"裁剪" + BoardTypeDesc[type]} aspect={540 / 390} minZoom={1} maxZoom={5}>
+      <Upload beforeUpload={onUpload}  multiple={false} accept="png">
+          <Button type="primary" ghost block>上传{ BoardTypeDesc[type] }</Button>
+      </Upload>
+    </ImgCrop>
+  )
 }

+ 8 - 2
src/views/draw-file/slider.tsx

@@ -2,13 +2,15 @@ import { Button } from 'antd'
 import { useEffect, useState } from 'react'
 import { metas, labels, images } from './board'
 import { BoardType, BoardTypeDesc } from 'api'
-import { SelectMap, SelectFuse } from './modal'
+import { SelectMap, SelectFuse, DfUploadCrop } from './modal'
 import shapes from './shapes'
 import style from './style.module.scss'
 
 import type { MetaShapeType, Board } from './board'
 import type { Tagging } from 'api'
 
+
+
 type SliderProps = {
   board: Board
   type: BoardType
@@ -80,7 +82,11 @@ export const DfSlider = ({ board, type, caseId }: SliderProps) => {
     <div className={style['df-slide-content']}>
       { renderSelect }
       <h3>户型图</h3>
-      <Button type="primary" block ghost  onClick={() => setSelectMode(true)}>设置{ BoardTypeDesc[type] }</Button>
+      <div className={style['def-image-set']}>
+        <Button type="primary" ghost  onClick={() => setSelectMode(true)}>设置{ BoardTypeDesc[type] }</Button>
+        <DfUploadCrop caseId={caseId} type={type} onSave={setBoardImage} />
+        
+      </div>
       <h3>标注</h3>
       <div className={style['df-shape-layout']}>{ labels.map(getEle) }</div>
       <h3>图例</h3>

+ 12 - 0
src/views/draw-file/style.module.scss

@@ -360,4 +360,16 @@ body {
 
 :global(.body-container .row-container:first-child:last-child .cell:last-child .delete-container) {
   display: none;
+}
+
+.def-image-set {
+  display: flex;
+  justify-content: space-between;
+
+  > *  {
+    width: 45%;
+    :global(.ant-upload) {
+      display: block;
+    }
+  }
 }

+ 1 - 2
src/views/scene/header.tsx

@@ -34,10 +34,9 @@ export const SceneHeader = memo(({ readonly, type, onSearch, onDataChange }: Lis
     return Upload.LIST_IGNORE
   }
 
-  // /b3dm
   const renderUpload = readonly || (type === SceneType.SWMX && (
     <Upload beforeUpload={onUpload} multiple={false} accept="application/zip">
-      <Popover content="请上传zip文件(支持obj/ply/las格式的数据),大小在1GB以内">
+      <Popover content="请上传zip文件(支持obj/ply/las/osgb/b3dm格式的数据),大小在1GB以内">
         <Button type="primary" children="上传数据" />
       </Popover>
     </Upload>