bill 2 anos atrás
pai
commit
f142aa29b0

+ 10 - 3
src/api/files.ts

@@ -3,10 +3,12 @@ import {
   EXAMPLE_FILE_TYPE_LIST,
   EXAMPLE_FILE_LIST,
   INSERT_EXAMPLE_FILE,
-  DELETE_EXAMPLE_FILE
+  DELETE_EXAMPLE_FILE,
+  UPLOAD_HEADS
  } from 'constant'
 
 import type { Example } from './example'
+import { jsonToForm } from 'utils'
 
 export interface ExampleFileType {
   filesTypeId: number,
@@ -38,9 +40,14 @@ export type GetExampleFilesProps = { caseId: Example['caseId'], filesTypeId?: Ex
 export const getExampleFiles = (props: GetExampleFilesProps) => 
   axios.get<ExampleFiles>(EXAMPLE_FILE_LIST, { params: props })
 
-export type AddExampleFilesProps = Pick<ExampleFile, 'caseId' | 'filesTitle' | 'filesUrl' | 'filesTypeId'>
+export type AddExampleFilesProps = Pick<ExampleFile, 'caseId' | 'filesTitle' | 'filesTypeId'> & { file: File }
 export const addExampleFile = (props: AddExampleFilesProps) => 
-  axios.post<ExampleFiles>(INSERT_EXAMPLE_FILE, props)
+  axios<ExampleFiles>({
+    url: INSERT_EXAMPLE_FILE, 
+    method: 'POST',
+    headers: UPLOAD_HEADS,
+    data: jsonToForm(props),
+  })
 
 export type DeleteExampleFileProps = Pick<ExampleFile, 'caseId' | 'filesId'>
 export const deleteExampleFile = (props: DeleteExampleFileProps) => 

+ 2 - 1
src/api/instance.ts

@@ -23,7 +23,8 @@ export const {
 } = instance
 
 addReqErrorHandler(err => {
-  message.error(err.message)
+  hideLoading()
+  // message.error(err.message)
 })
 
 addResErrorHandler(

+ 18 - 2
src/api/scene.ts

@@ -16,7 +16,8 @@ import {
   MODEL_SCENE_LIST, 
   UPLOAD_MODEL,
   DELETE_MODEL,
-  GET_MODEL_SCENE_STATUS
+  GET_MODEL_SCENE_STATUS,
+  CANCEL_MODEL_HANDER
 } from 'constant'
 
 
@@ -87,7 +88,12 @@ export const getModelSceneStatus = async (sceneId: Scene['id']) => {
   const progress = await axios.get<number>(GET_MODEL_SCENE_STATUS, { params: { modelId: sceneId } })
   return {
     id: sceneId,
-    status: progress >= 100 ? ModelSceneStatus.SUCCESS : ModelSceneStatus.RUN
+    progress,
+    status: progress >= 100 
+      ? ModelSceneStatus.SUCCESS 
+      : progress < 0 
+        ? ModelSceneStatus.ERR 
+        : ModelSceneStatus.RUN
   }
 }
 
@@ -102,6 +108,16 @@ export const uploadModelScene = ({ file, progressCallback }: UploadModelScenePro
     onUploadProgress: progressCallback && uploadProgressFactory(progressCallback)
   })
 
+// 取消上传
+export const cancelUploadModelScene = async (modelId: ServeModelScene['modelId']) => {
+  await axios.get<undefined>(CANCEL_MODEL_HANDER, { params: { modelId } })
+
+  return {
+    id: modelId,
+  }
+}
+
+
 // 删除模型文件
 export const deleteModelScene = (modelId: ServeModelScene['modelId']) => 
   axios.post<undefined>(DELETE_MODEL, { modelId })

+ 1 - 0
src/constant/api.ts

@@ -26,6 +26,7 @@ export const MODEL_SCENE_LIST = `/fusion/model/list`
 export const UPLOAD_MODEL = `/fusion/model/uploadObj`
 export const DELETE_MODEL = `/fusion/model/delete`
 export const GET_MODEL_SCENE_STATUS = `/fusion/model/uploadObjProgress`
+export const CANCEL_MODEL_HANDER = `/fusion/model/cancelUpload`
 
 // 案件
 export const EXAMPLE_LIST = `/fusion/case/list`

+ 3 - 1
src/constant/scene.ts

@@ -55,11 +55,13 @@ export enum ModelSceneStatus {
   ERR = -1,
   RUN = 0,
   SUCCESS = 1,
+  CANCEL = -2
 }
 
 
 export const ModelSceneStatusDesc: { [key in ModelSceneStatus]: string }  = {
-  [ModelSceneStatus.ERR]: '失败',
+  [ModelSceneStatus.CANCEL]: '已取消',
+  [ModelSceneStatus.ERR]: '上传失败',
   [ModelSceneStatus.RUN]: '上传中',
   [ModelSceneStatus.SUCCESS]: '成功',
 }

+ 1 - 1
src/layout/header/index.tsx

@@ -26,7 +26,7 @@ export const HeaderContent = () => {
     <>
       <h2 className={style.title}>{title}</h2>
       <div className={style.avatar}>
-        <Dropdown overlay={<Menu style={{width: '100px'}} items={items} />} className={style['logout-drop']}>
+        <Dropdown overlay={<Menu style={{width: '100px'}} items={items} />} className={style['logout-drop']} placement="bottomCenter">
           <div>
             <Avatar src={user.head} />
             <span className={style.username}>{user.nickName}</span>

+ 0 - 4
src/layout/header/style.module.scss

@@ -14,8 +14,4 @@
   .username {
     margin-left: 10px;
   }
-}
-
-.logout-drop {
-  max-width: 200px;
 }

+ 15 - 2
src/store/scene.ts

@@ -6,7 +6,8 @@ import {
   uploadModelScene as uploadModelSceneApi,
   deleteModelScene as deleteModelSceneApi,
   getModelSceneStatus,
-  ModelScene
+  ModelScene,
+  cancelUploadModelScene
 } from 'api'
 import { 
   ModelSceneStatus, 
@@ -50,13 +51,24 @@ const sceneSlice = createSlice({
 
     builder.addCase(checkSceneStatus.fulfilled, (state, data) => {
       const scene = state.value.find(scene => scene.id === data.payload.id) as ModelScene
-      if (scene) {
+      if (scene && scene.status === ModelSceneStatus.RUN) {
         state.value[state.value.indexOf(scene)] = {
           ...scene,
+          progress: data.payload.progress,
           status: data.payload.status
         }
       }
     })
+
+    builder.addCase(cancelUploadScene.fulfilled, (state, data) => {
+      const scene = state.value.find(scene => scene.id === data.payload.id) as ModelScene
+      if (scene) {
+        state.value[state.value.indexOf(scene)] = {
+          ...scene,
+          status: ModelSceneStatus.CANCEL
+        }
+      }
+    })
   }
 })
 
@@ -75,6 +87,7 @@ export const fetchScenes = createAsyncThunk('fetch/scenes', getSceneByType)
 export const uploadModelScene = createAsyncThunk('upload/modelScene', uploadModelSceneApi)
 export const deleteModelScene = createAsyncThunk('delete/modelScene', deleteModelSceneApi)
 export const checkSceneStatus = createAsyncThunk('fetch/scene', getModelSceneStatus)
+export const cancelUploadScene = createAsyncThunk('cancel/uploadScene', cancelUploadModelScene)
 
 export enum SceneLinkFlag { query, edit }
 export const getSceneLink = (scene: Scene, flag: SceneLinkFlag): URL => {

+ 19 - 1
src/utils/index.ts

@@ -6,4 +6,22 @@ export * from './setState'
 export * from './serve'
 export * from './sys'
 export * from './url'
-export * from './only-open'
+export * from './only-open'
+
+// 字符串转params对象
+export const strToParams = (str: string) => {
+  if (str[0] === '?') {
+      str = str.substr(1)
+  }
+
+  const result: { [key: string]: string } = {}
+  const splitRG = /([^=&]+)(?:=([^&]*))?&?/
+
+  let rgRet
+  while ((rgRet = str.match(splitRG))) {
+    result[rgRet[1]] = rgRet[2] === undefined ? '' : rgRet[2]
+    str = str.substr(rgRet[0].length)
+  }
+
+  return result
+}

+ 38 - 9
src/views/example/files/list.tsx

@@ -1,7 +1,7 @@
 import { useEffect, useRef, useState } from 'react'
 import { ActionsButton } from 'components'
 import style from '../style.module.scss'
-import { AntUploadProps, addExampleFile, deleteExampleFile } from 'api'
+import { addExampleFile, deleteExampleFile } from 'api'
 import { UploadOutlined } from '@ant-design/icons'
 import { confirm, onlyOpenWindow } from 'utils'
 import { 
@@ -108,8 +108,7 @@ export const ExampleFiles = (props: ExampleScenesProps) => {
       visible={true} 
       onOk={props.onClose} 
       onCancel={props.onClose}
-      okText="确定"
-      cancelText="取消"
+      footer={null}
     >
       {renderAddFile}
       <div className={style['model-header']}>
@@ -130,6 +129,17 @@ export const ExampleFiles = (props: ExampleScenesProps) => {
   )
 }
 
+const FileTitleInput = (props: { value?: string, onChange?: (value: string) => void;}) => 
+  <Input 
+    onChange={ev => {
+      const value = ev.target.value.trim()
+      if (value.length <= 50) {
+        props.onChange && props.onChange(value)
+      }
+    }} 
+    value={props.value} 
+  />
+
 export type AddExampleFileProps = Pick<Example, 'caseId'> & { onClose: () => void,}
 export const AddExampleFile = (props: AddExampleFileProps) => {
   const dispatch = useDispatch()
@@ -144,8 +154,9 @@ export const AddExampleFile = (props: AddExampleFileProps) => {
     }
     await addExampleFile({
       ...values,
+      filesUrl: void 0,
       caseId: props.caseId,
-      filesUrl: values.filesUrl.fileList[0].response
+      file: values.filesUrl.file.originFileObj
     })
     props.onClose()
     dispatch(fetchExampleFiles({ caseId: props.caseId }))
@@ -153,13 +164,31 @@ export const AddExampleFile = (props: AddExampleFileProps) => {
   const onSubmit = () => {
     from.current?.submit()
   }
+  const accpets = [".pdf", ".word", ".jpg"]
 
   const uploadProps: UploadProps = {
-    ...AntUploadProps,
+    async customRequest(option) {
+      const file = option.file as File
+      const filename = file.name
+      const ext = filename.substring(filename.lastIndexOf('.'))
+      const isSuper = accpets.includes(ext)
+      if (!isSuper) {
+        message.error(`只能${accpets.join(',')}上传文件`)
+        option.onError &&  option.onError(new Error(`只能${accpets.join(',')}上传文件`))
+        return 
+      } else if (file.size > 100 * 1024 * 1024) {
+        message.error(`上传文件不能超过100M`)
+        option.onError &&  option.onError(new Error(`上传文件不能超过100M`))
+        return
+      }
+      option.onSuccess &&  option.onSuccess(file.name)
+    },
     listType: 'picture',
+    multiple: false,
+    accept: accpets.join(","),
     maxCount: 1,
     onPreview(file) {
-      onlyOpenWindow(file.response)
+      onlyOpenWindow(URL.createObjectURL(file.originFileObj!))
     }
   };
 
@@ -187,11 +216,11 @@ export const AddExampleFile = (props: AddExampleFileProps) => {
         </Form.Item>
         <Form.Item name="filesUrl" label="上传附件" rules={[{ required: true, message: '附件文件不能为空' }]}>
           <Upload {...uploadProps}>
-            <Button icon={<UploadOutlined />}>请上传pdf/word/jpg格式文件</Button>
+            <Button icon={<UploadOutlined />}>请上传{accpets.join('/')}格式文件</Button>
           </Upload>
         </Form.Item>
-        <Form.Item name="filesTitle" label="附件标题" rules={[{ required: true, message: '附件标题不能为空' }]}>
-          <Input />
+        <Form.Item name="filesTitle" label="附件标题" rules={[{ required: true, message: '附件标题不能为空' }]} >
+          <FileTitleInput />
         </Form.Item>
       </Form>
     </Modal>

+ 11 - 2
src/views/login/index.tsx

@@ -6,8 +6,9 @@ import { useDispatch, postLogin } from 'store'
 import style from './style.module.scss'
 import { RoutePath, useNavigate } from 'router'
 import { useLoading } from 'hook'
+import { strToParams } from 'utils'
 
-import type { LoginParams } from 'api'
+import { getToken, LoginParams } from 'api'
 import type { FormItemProps } from 'antd'
 import type { ComponentType } from 'react'
 
@@ -91,14 +92,22 @@ export const Login = () => {
       <Form.Item {...props} key={i} children={<Node />} />
     ))
   const loginHandler = async (data: LoginInfo) => {
+    const params = strToParams(window.location.search)
     await setPromise(dispatch(postLogin(data)).unwrap())
-    navigate(RoutePath.home, { replace: true })
 
     if (data.markpsw) {
       setCache(data)
     } else {
       delCache()
     }
+    if ('redirect' in params) {
+      const url = new URL(unescape(params.redirect))
+      url.searchParams.delete('token')
+      url.searchParams.append('token', getToken()!)
+      window.location.replace(url)
+    } else {
+      navigate(RoutePath.home, { replace: true })
+    }
   }
 
   return (

+ 35 - 12
src/views/scene/content.tsx

@@ -13,7 +13,8 @@ import {
   fetchScenes,
   deleteModelScene,
   useDispatch,
-  checkSceneStatus
+  checkSceneStatus,
+  cancelUploadScene
 } from 'store'
 import { useEffect, useState } from 'react'
 import { updateModelSceneTitle } from 'api'
@@ -77,6 +78,15 @@ export const SceneTabContent = ({type}: {type: SceneType}) => {
     } 
   }, [scenes, type, dispatch])
 
+
+  useEffect(() => {
+    const runScenes = scenes.filter(scene => scene.status === ModelSceneStatus.SUCCESS && scene.progress)
+    if (type === SceneType.SWMX && runScenes.length) {
+      refresh()
+    } 
+  }, [scenes, type, refresh])
+
+
   const actionColumn: SceneColumn = {
     title: '操作',
     key: 'action',
@@ -85,17 +95,30 @@ export const SceneTabContent = ({type}: {type: SceneType}) => {
       
       if (scene.type === SceneType.SWKK) {
         // actions.push({ text: '仿真', action: () => {} })
-      } else if (scene.type === SceneType.SWMX && scene.status !== ModelSceneStatus.RUN) {
-        actions.push({ 
-          text: '删除', 
-          action: async () => {
-            if (await confirm('确定要删除此数据?')) {
-              await dispatch(deleteModelScene(scene.id)).unwrap()
-              await refresh()
-            }
-          }, 
-          bind: { danger: true } 
-        })
+      } else if (scene.type === SceneType.SWMX) {
+        if (scene.status !== ModelSceneStatus.RUN) {
+          actions.push({ 
+            text: '删除', 
+            action: async () => {
+              if (await confirm('确定要删除此数据?')) {
+                await dispatch(deleteModelScene(scene.id)).unwrap()
+                await refresh()
+              }
+            }, 
+            bind: { danger: true } 
+          })
+        } else {
+          actions.push({ 
+            text: '取消上传', 
+            action: async () => {
+              if (await confirm('确定要取消上传吗?')) {
+                await dispatch(cancelUploadScene(scene.id)).unwrap()
+                await refresh()
+              }
+            }, 
+            bind: { danger: true } 
+          })
+        }
       }
       return <ActionsButton actions={actions} /> 
     }