Jelajahi Sumber

添加数据对接

bill 3 tahun lalu
induk
melakukan
1f61dcf2a7

+ 3 - 2
src/api/constant.ts

@@ -58,8 +58,9 @@ export const DELETE_GUIDE_PATH = `/fusion/fusionGuidePath/delete`
 
 // 屏幕录制
 export const RECORD_LIST = `/fusion/caseVideoFolder/allList`
-export const INSERT_RECORD = `/fusion/caseVideo/add`
-export const MERGE_RECORD = `/fusion/caseVideo/add`
+export const RECORD_STATUS = `/fusion/caseVideo/uploadAddVideoProgress`
+export const INSERT_RECORD = `/fusion/caseVideo/uploadAddVideo`
+export const MERGE_RECORD = `/fusion/caseVideo/uploadAddVideo`
 export const UPDATE_RECORD = `/fusion/caseVideoFolder/updateNameOrSort`
 export const DELETE_RECORD = `/fusion/caseVideoFolder/delete`
 

+ 1 - 1
src/api/mesasure.ts

@@ -46,7 +46,7 @@ const toService = (measure: Measure, isUpdate = true): PartialProps<ServiceMeasu
 
 export type Measures = Measure[]
 
-export const fetchMeasures = async (fusionId: { fusionId: Measure['fusionId'] }) => {
+export const fetchMeasures = async (fusionId: Measure['fusionId']) => {
   const data = await axios.get<ServiceMeasure[]>(MESASURE_LIST, { params: { fusionId } })
   return data.map(toLocal)
 }

+ 17 - 6
src/api/record.ts

@@ -1,8 +1,10 @@
 import axios from './instance'
-import { RECORD_LIST, DELETE_RECORD, UPDATE_RECORD, MERGE_RECORD } from './constant'
+import { RECORD_LIST, DELETE_RECORD, UPDATE_RECORD, MERGE_RECORD, RECORD_STATUS, UPLOAD_HEADS } from './constant'
 import { params } from '@/env'
+import { jsonToForm } from '@/utils'
 
 export enum RecordStatus {
+  UN = -2,
   ERR = -1,
   RUN = 0,
   SUCCESS = 1
@@ -36,7 +38,7 @@ const toLocal = (serviceRecord: ServiceRecord) : Record => ({
 })
 
 const toService = (record: Record, isUpdate = true): PartialProps<ServiceRecord, 'videoFolderId'> => ({
-  videoFolderId: isUpdate ? Number(record.id): undefined,
+  ...(isUpdate ? { videoFolderId: Number(record.id) } : {}),
   videoFolderName: record.title,
   videoFolderCover: record.cover,
   videoMergeUrl: record.url,
@@ -52,6 +54,10 @@ export const fetchRecords = async () => {
   return data.map(toLocal)
 }
 
+export const fetchRecordStatus = async (recordId: Record['id']) => {
+  return await axios.get<RecordStatus>(RECORD_STATUS, { params: { recordId } })
+}
+
 export const postAddRecord = async (record: Record, files: File[]) => {
   const serviceRecord = await postMegerRecord(files)
   return toLocal(serviceRecord)
@@ -63,10 +69,15 @@ export const postUpdateRecord = async (record: Record) => {
 }
 
 export const postMegerRecord = (files: File[], recordId?: Record['id']) => {
-  return axios.post<ServiceRecord>(MERGE_RECORD, {
-    folderId: recordId && Number(recordId),
-    caseId: params.caseId,
-    files
+  return axios<ServiceRecord>({
+    method: 'POST',
+    url: MERGE_RECORD,
+    headers: { ...UPLOAD_HEADS },
+    data: jsonToForm({
+      ...(recordId ? { folderId: recordId && Number(recordId) } : {}),
+      caseId: params.caseId,
+      files
+    })
   })
 }
 

+ 8 - 10
src/api/view.ts

@@ -10,7 +10,7 @@ export type View = {
   title: string
   sort: number
   flyData: string,
-} & ({ isFuse: true } | { isFuse: false, sceneId: Scene['id'] })
+} & ({ fusionId: number } | { num: string, numType: Scene['type'] })
 
 type ServiceView = {
   viewId: number
@@ -18,7 +18,7 @@ type ServiceView = {
   viewPoint:	string	
   viewImg:	string	
   sort:	number	
-}& ({ isFuse: true } | { isFuse: false, sceneId: Scene['id'] })
+} & ({ fusionId: number } | { num: string, numType: Scene['type'] })
 
 const toLocal = (serviceView: ServiceView) : View => {
   const base = {
@@ -27,12 +27,11 @@ const toLocal = (serviceView: ServiceView) : View => {
     title: serviceView.viewTitle,
     sort: serviceView.sort,
     flyData: JSON.parse(serviceView.viewPoint),
-    isFuse: serviceView.isFuse,
   }
-  if (!serviceView.isFuse) {
-    return { ...base, sceneId: serviceView.sceneId }
+  if ('fusionId' in serviceView) {
+    return { ...base, fusionId: serviceView.fusionId }
   } else {
-    return base as View
+    return { ...base, num: serviceView.num, numType: serviceView.numType }
   }
 }
 
@@ -43,12 +42,11 @@ const toService = (view: View, isUpdate = true): PartialProps<ServiceView, 'view
     viewPoint: JSON.stringify(view.flyData),
     viewImg: view.cover,
     sort:	view.sort,
-    isFuse: view.isFuse
   }
-  if (!view.isFuse) {
-    return { ...base, sceneId: view.sceneId } as ServiceView
+  if ('fusionId' in view) {
+    return { ...base, fusionId: view.fusionId } as ServiceView
   } else {
-    return base
+    return { ...base, num: view.num, numType: view.numType } as ServiceView
   }
 }
 

+ 4 - 2
src/layout/edit/fuse-edit.vue

@@ -22,7 +22,8 @@ import {
   save, 
   initialTaggingStyles, 
   initialTaggings, 
-  initialGuides 
+  initialGuides, 
+  initialMeasures
 } from '@/store'
 
 import Header from './header/index.vue'
@@ -31,7 +32,8 @@ const loaded = ref(false)
 Promise.all([
   initialTaggingStyles(),
   initialTaggings(),
-  initialGuides()
+  initialGuides(),
+  initialMeasures()
 ])
 .then(() => loaded.value = true)
 

+ 2 - 1
src/store/index.ts

@@ -8,4 +8,5 @@ export * from './guide-path'
 export * from './tagging-positions'
 export * from './measure'
 export * from './record'
-export * from './view'
+export * from './view'
+export * from './record-fragment'

+ 7 - 4
src/store/measure.ts

@@ -20,9 +20,9 @@ import {
 import type { Measure, Measures } from '@/api'
 
 export const MeasureTypeMeta = {
-  [MeasureType.area]: { icon: 'v-l', desc: '自由', unit: '长度' },
-  [MeasureType.free]: { icon: 'f-l', desc: '垂直', unit: '长度' },
-  [MeasureType.vertical]: { icon: 'h-r', desc: '面积', unit: '面积' }
+  [MeasureType.area]: { icon: 'v-l', desc: '自由', unitDesc: '长度', unit: 'm' },
+  [MeasureType.free]: { icon: 'f-l', desc: '垂直', unitDesc: '长度', unit: 'm' },
+  [MeasureType.vertical]: { icon: 'h-r', desc: '面积', unitDesc: '面积', unit: 'm²' }
 }
 
 export const measures = ref<Measures>([])
@@ -46,7 +46,7 @@ export const backupMeasures = () => {
 export const recoverMeasures = recoverStoreItems(measures, getBackupMeasures)
 
 
-export const initialMeasure = fetchStoreItems(measures, () => fetchMeasures(fuseModels.value[0]), backupMeasures)
+export const initialMeasures = fetchStoreItems(measures, () => fetchMeasures(fuseModels.value[0].fusionId), backupMeasures)
 export const addMeasure = addStoreItem(measures, postAddMeasure)
 export const updateMeasure = updateStoreItem(measures, postUpdateMeasure)
 export const deleteMeasure = deleteStoreItem(measures, measure => postDeleteMeasure(measure.id))
@@ -64,3 +64,6 @@ export const autoSaveMeasures = autoSetModeCallback(measures, {
   recovery: recoverMeasures,
   save: saveMeasures
 })
+
+export type { Measure, Measures } from '@/api'
+export { MeasureType }

+ 9 - 3
src/store/record-fragment.ts

@@ -6,22 +6,28 @@ import { fetchRecordFragments, postDeleteRecordFragment } from '@/api'
 import type { RecordFragment as SRecordFragment } from '@/api'
 import type { Record } from './record'
 
-export type RecordFragment = LocalMode<SRecordFragment, 'url'> & { recordId: Record['id'] }
+export type RecordFragment = Omit<SRecordFragment, 'url'> & { url: string | Blob } & { recordId: Record['id'] }
 export type RecordFragments = RecordFragment[]
 
 export const recordFragments = ref<RecordFragments>([])
 
-export const createRecordFragment = (): RecordFragment => ({
+export const createRecordFragment = (recordFragment: Partial<RecordFragment> = {}): RecordFragment => ({
   id: createTemploraryID(),
   recordId: '',
   cover: '',
   url: '',
   sort: Math.min(...recordFragments.value.map(item => item.sort)) + 1,
+  ...recordFragment
 })
 
 export const getRecordFragments = (record: Record) => 
   recordFragments.value.filter(fragment => fragment.recordId === record.id)
 
+
+export const getRecordFragmentBlobs = (record: Record) => getRecordFragments(record)
+  .filter(fragment => typeof fragment.url !== 'string')
+  .map(fragment => fragment.url as Blob)
+
 let bcRecordFragments: RecordFragments = []
 export const getBackupRecordFragments = () => bcRecordFragments
 export const backupRecordFragments = () => {
@@ -45,7 +51,7 @@ export const saveRecordFragments = saveStoreItems(
   getBackupRecordFragments,
   { delete: deleteRecordFragment }
 )
-export const autoSaveViews = autoSetModeCallback(recordFragments, {
+export const autoSaveRecordFragments = autoSetModeCallback(recordFragments, {
   backup: backupRecordFragments,
   recovery: recoverRecordFragments,
   save: saveRecordFragments

+ 32 - 14
src/store/record.ts

@@ -1,5 +1,5 @@
-import { ref } from "vue";
-import { autoSetModeCallback, createTemploraryID } from './sys'
+import { ref, watchEffect } from "vue";
+import { autoSetModeCallback, createTemploraryID, unSetModelUpdate } from './sys'
 import { 
   addStoreItem, 
   deleteStoreItem, 
@@ -18,12 +18,12 @@ import {
   RecordStatus,
   postMegerRecord,
   blobToFile,
-  uploadFile
+  uploadFile,
+  fetchRecordStatus
 } from '@/api'
 import { 
   getRecordFragments, 
   initRecordFragmentsByRecord,
-  deleteRecordFragment,
   recordFragments,
   backupRecordFragments,
   recoverRecordFragments,
@@ -37,13 +37,14 @@ export type Records = Record[]
 
 export const records = ref<Records>([])
 
-export const createRecord = (): Record => ({
+export const createRecord = (record: Partial<Record> = {}): Record => ({
   id: createTemploraryID(),
   title: '讲解视频',
   cover: '',
   url: '',
-  status: RecordStatus.SUCCESS,
-  sort: Math.min(...records.value.map(item => item.sort)) - 1
+  status: RecordStatus.UN,
+  sort: Math.min(...records.value.map(item => item.sort)) - 1,
+  ...record
 })
 
 
@@ -54,19 +55,34 @@ export const backupRecords = () => {
 }
 export const recoverRecords = recoverStoreItems(records, getBackupRecords)
 
+const refreshRecordStatus = async (record: Record) => {
+  const status = await fetchRecordStatus(record.id)
+  if (status === RecordStatus.SUCCESS) {
+    unSetModelUpdate(() => record.status = RecordStatus.SUCCESS)
+  } else {
+    setTimeout(refreshRecordStatus.bind(null, record), 3000)
+  }
+}
+
+
 const getRecordMergeFiles = (record: Record) => {
   const fragments = getRecordFragments(record)
   const files = fragments
-    .filter(fragment => typeof fragment.cover !== 'string')
-    .map(fragment => blobToFile((fragment.url as LocalFile).blob, '.mp4'))
+    .filter(fragment => typeof fragment.url !== 'string')
+    .map(fragment => blobToFile((fragment.url as Blob), '.mp4'))
   return files
 }
 
-export const initialRecords = fetchStoreItems(records, async () => {
-  const records = await fetchRecords()
-  await Promise.all(records.map(initRecordFragmentsByRecord))
-  return records
-}, getBackupRecords)
+export const initialRecords = async () => {
+  records.value = await fetchRecords()
+  await Promise.all(records.value.map(initRecordFragmentsByRecord))
+  for (const record of records.value) {
+    if (record.status === RecordStatus.RUN) {
+      refreshRecordStatus(record)
+    }
+  }
+  getBackupRecords()
+}
 
 export const addRecord = addStoreItem(records, async (record) => {
   const cover = await uploadFile(record.cover)
@@ -109,3 +125,5 @@ export const autoSaveRecords = autoSetModeCallback(
     }
   }
 )
+
+export { RecordStatus }

+ 2 - 1
src/store/view.ts

@@ -1,5 +1,6 @@
 import { ref } from "vue";
 import { autoSetModeCallback, createTemploraryID } from './sys'
+import { fuseModels } from './fuse-model'
 import { 
   addStoreItem, 
   deleteStoreItem, 
@@ -28,7 +29,7 @@ export const createView = (): View => ({
   title: '视图',
   cover: 'https://4dkk.4dage.com/scene_view_data/KK-t-F8e5M46wcQ/images/floor_0.png?t=1659422513133?v=0&rnd=0.9219648338739086&x-oss-process=image/resize,m_fill,w_80,h_60/quality,q_70&rnd=0.25420557086595965',
   flyData: '',
-  isFuse: true,
+  fusionId: fuseModels.value[0].fusionId,
   sort: Math.min(...views.value.map(item => item.sort)) - 1,
 })
 

+ 7 - 1
src/utils/index.ts

@@ -49,7 +49,13 @@ export const asyncTimeout = (mis: number = 0) => new Promise(resolve => setTimeo
 export const jsonToForm = (data: { [key in string]: any }) => {
   const formData = new FormData()
   for (const [key, val] of Object.entries(data)) {
-    formData.append(key, val)
+    if (Array.isArray(val)) {
+      for (let i = 0; i < val.length; i++) {
+        formData.append(`${key}`, val[i])  
+      }
+    } else {
+      formData.append(key, val)
+    }
   }
   return formData
 }

+ 6 - 5
src/views/measure/edit.vue

@@ -3,8 +3,8 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, reactive } from 'vue'
-import { enterEdit, useAutoSetMode } from '@/store'
+import { ref, reactive, watch } from 'vue'
+import { enterEdit, enterOld, sysBus } from '@/store'
 import { useViewStack } from '@/hook'
 import { togetherCallback } from '@/utils'
 import { showRightCtrlPanoStack, showRightPanoStack, } from '@/env'
@@ -23,14 +23,15 @@ useViewStack(() => togetherCallback([
   showRightCtrlPanoStack.push(ref(false)),
   showRightPanoStack.push(ref(false)),
 ]))
-useAutoSetMode(measure, {
-  save: () => emit('submit', measure)
-})
+sysBus.on('save', () => emit('submit', measure), { pre: true })
+watch(measure, () => enterOld(), { deep: true })
 
 setTimeout(() => {
   measure.positions = [
     {x: 1, y: 1, z: 1},
     {x: 1, y: 1, z: 1},
   ]
+  measure.title = '123123'
+  measure.desc = '20'
 }, 2000)
 </script>

+ 2 - 1
src/views/measure/index.vue

@@ -46,7 +46,7 @@ import EditMeasure from './edit.vue'
 import { RightFillPano } from '@/layout'
 import { computed, ref } from 'vue';
 import { custom, showMeasuresStack } from '@/env'
-import { measures, MeasureTypeMeta, MeasureType, createMeasure } from '@/store'
+import { measures, MeasureTypeMeta, MeasureType, createMeasure, autoSaveMeasures } from '@/store'
 import { useViewStack } from '@/hook'
 
 import type { Measure } from '@/store'
@@ -84,4 +84,5 @@ const deleteMeasure = (measure: Measure) => {
 }
 
 useViewStack(() => showMeasuresStack.push(ref(true)))
+useViewStack(autoSaveMeasures)
 </script>

+ 2 - 2
src/views/measure/sign.vue

@@ -7,8 +7,8 @@
     <div class="info">
       <ui-icon :type="MeasureTypeMeta[measure.type].icon" class="type" />
       <div>
-        <p>{{ measure.desc }}</p>
-        <span>{{ MeasureTypeMeta[measure.type].unit }}</span>
+        <p>{{ measure.desc }} {{ MeasureTypeMeta[measure.type].unit }}</p>
+        <span>{{ MeasureTypeMeta[measure.type].unitDesc }}</span>
       </div>
     </div>
     <div class="actions" @click.stop>

+ 5 - 3
src/views/record/index.vue

@@ -34,13 +34,14 @@ import { ref, watch } from 'vue'
 import { showMeasuresStack, showTaggingsStack } from '@/env'
 import { useViewStack } from '@/hook'
 import { diffArrayChange, togetherCallback } from '@/utils'
-import { isTemploraryID } from '@/store'
 import { RecordProcess } from './help'
-import { records, createRecord, Record } from '@/store/record'
+import { records, createRecord, Record, RecordStatus, autoSaveRecords, initialRecords } from '@/store'
 import { RightFillPano } from '@/layout'
 import Draggable from 'vuedraggable'
 import Sign from './sign.vue'
 
+initialRecords()
+
 const start = () => records.value.push(createRecord())
 const deleteRecord = (record: Record) => {
   const index = records.value.indexOf(record)
@@ -51,7 +52,7 @@ const deleteRecord = (record: Record) => {
 
 const getSignRecord = (record: Record): RecordProcess => ({
   ...record,
-  immediately: !record.blobs.length && isTemploraryID(record.id)
+  immediately: record.status === RecordStatus.UN
 })
 
 const setOptions = [
@@ -80,6 +81,7 @@ useViewStack(() => {
     pop()
   }
 })
+useViewStack(autoSaveRecords)
 </script>
 
 <style lang="scss" src="./style.scss" scoped>

+ 4 - 4
src/views/record/shot.vue

@@ -41,7 +41,7 @@ import { VideoRecorder } from '@simaq/core';
 import { sdk } from '@/sdk'
 import { getVideoCover, togetherCallback } from '@/utils'
 import { MediaType, Preview } from '@/components/static-preview/index.vue'
-import { Record } from '@/store/record'
+import { Record, getRecordFragmentBlobs } from '@/store'
 import { 
   getResource, 
   showRightCtrlPanoStack, 
@@ -102,10 +102,9 @@ export default defineComponent({
       clearInterval(interval)
     }
 
-    const blobs: Blob[] = shallowReactive([])
+    const blobs: File[] = shallowReactive([])
     videoRecorder.off('*')
     videoRecorder.on('record', blob => {
-      console.log('完成录屏')
       blobs.push(new File([blob], '录屏.mp4', { type: 'video/mp4; codecs=h264' }))
     })
     videoRecorder.on('cancelRecord', pause)
@@ -118,7 +117,8 @@ export default defineComponent({
       if (props.record.url) {
         existsVideos.push(getResource(props.record.url))
       }
-      existsVideos.push(...props.record.blobs, ...blobs)
+      const fragmentBlobs = getRecordFragmentBlobs(props.record)
+      existsVideos.push(...fragmentBlobs, ...blobs)
       for (const blob of existsVideos) {
         if (videoList.some(item => item.origin === blob)) {
           continue

+ 16 - 6
src/views/record/sign.vue

@@ -2,13 +2,13 @@
   <ui-group-option class="sign">
     <div class="content">
       <span class="cover">
-        <img :src="getResource(record.cover)" alt="" v-if="record.cover">
+        <img :src="getResource(getFileUrl(record.cover))" alt="" v-if="record.cover">
         <ui-icon 
           type="preview" 
           ctrl 
           class="preview" 
           @click="actions.play()"  
-          v-if="record.status === RecordStatus.SUCCESS && !record.blobs.length"
+          v-if="record.status === RecordStatus.SUCCESS"
         />
       </span>
       <ui-input 
@@ -36,7 +36,7 @@
     <Shot 
       v-if="isShot" 
       @close="closeHandler"
-      @append="(blobs: Blob[]) => record.blobs.push(...blobs)" 
+      @append="appendFragment" 
       @updateCover="(cover: string) => $emit('updateCover', cover)" 
       @deleteRecord="$emit('delete')"
       :record="record" />
@@ -51,8 +51,9 @@
 
 <script lang="ts">
 import { defineComponent, ref, computed } from 'vue'
+import { getFileUrl } from '@/utils'
 import { useFocus } from 'bill/hook/useFocus'
-import { RecordStatus } from '@/store/record'
+import { RecordStatus, createRecordFragment, getRecordFragmentBlobs, recordFragments } from '@/store'
 import { saveAs, loadPack } from '@/utils'
 import { MediaType, Preview } from '@/components/static-preview/index.vue'
 import { getResource } from '@/env'
@@ -106,12 +107,19 @@ export default defineComponent({
     props.record.immediately && actions.continue()
 
     const closeHandler = () => {
-      if (props.record.blobs.length === 0 && isTemploraryID(props.record.id)) {
+      console.log(getRecordFragmentBlobs(props.record).length)
+      if (getRecordFragmentBlobs(props.record).length === 0 && isTemploraryID(props.record.id)) {
         emit('delete')
       }
       isShot.value = false
     }
 
+    const appendFragment = (blobs: Blob[]) => {
+      recordFragments.value.push(
+        ...blobs.map(blob => createRecordFragment({ url: blob, recordId: props.record.id }))
+      )
+    }
+
     return {
       menus,
       actions,
@@ -122,7 +130,9 @@ export default defineComponent({
       RecordStatus,
       MediaType,
       isPlayVideo,
-      getResource
+      getResource,
+      getFileUrl,
+      appendFragment
     }
   },
   components: {

+ 3 - 2
src/views/view/index.vue

@@ -24,10 +24,11 @@
 </template>
 
 <script lang="ts" setup>
-import { views, createView } from '@/store'
+import { views, createView, autoSaveViews } from '@/store'
 import { RightFillPano } from '@/layout'
 import Draggable from 'vuedraggable'
 import Sign from './sign.vue'
+import { useViewStack } from '@/hook'
 
 import type { View } from '@/store'
 
@@ -38,7 +39,7 @@ const deleteView = (record: View) => {
     views.value.splice(index, 1)
   }
 }
-
+useViewStack(autoSaveViews)
 </script>
 
 <style lang="scss" src="./style.scss" scoped>

+ 3 - 1
src/views/view/sign.vue

@@ -2,7 +2,7 @@
   <ui-group-option class="sign">
     <div class="content">
       <span class="cover">
-        <img :src="getResource(view.cover)" alt="">
+        <img :src="getResource(getFileUrl(view.cover))" alt="">
       </span>
       <ui-input 
         type="text" 
@@ -33,6 +33,7 @@ import { defineComponent, ref, computed } from 'vue'
 import { useFocus } from 'bill/hook/useFocus'
 import { Preview } from '@/components/static-preview/index.vue'
 import { getResource } from '@/env'
+import { getFileUrl } from '@/utils'
 
 import type { PropType } from 'vue'
 import type { View } from '@/store'
@@ -66,6 +67,7 @@ export default defineComponent({
       menus,
       actions,
       isEditTitle,
+      getFileUrl,
       inputRef,
       getResource
     }