tangning hai 5 meses
pai
achega
81390f4bf7

+ 2 - 1
package.json

@@ -2,7 +2,6 @@
   "name": "public-service",
   "private": true,
   "version": "0.0.0",
-  "type": "module",
   "scripts": {
     "dev": "vite",
     "build": "vite build ./ fire && vite build ./ criminal && vite build ./ xmfire",
@@ -18,6 +17,7 @@
     "echarts": "^5.4.3",
     "element-plus": "^2.3.8",
     "html2canvas": "^1.4.1",
+    "http": "^0.0.1-security",
     "js-base64": "^3.7.5",
     "leaflet": "^1.9.4",
     "mime": "^3.0.0",
@@ -33,6 +33,7 @@
     "vue": "^3.3.4",
     "vue-cropper": "^1.0.9",
     "vue-draggable-plus": "^0.6.0",
+    "vue-i18n": "^11.1.1",
     "vue-router": "^4.2.4",
     "windicss": "^3.5.6"
   },

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1044 - 0
program/lang/weblate/en.json


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1044 - 0
program/lang/weblate/ja.json


+ 215 - 0
program/lang/weblate/kr.json

@@ -0,0 +1,215 @@
+{
+    "program": {
+        "other": {
+            "toWinIng": "컴퓨터로 동기화하고 있어요.",
+            "syncIng": "N 개의 시나리오가 동기화됩니다",
+            "usb": "USB메모리",
+            "usbScene": "USB에 담긴 프로젝트",
+            "gscene": "N 개의 장면",
+            "delTipPost": "삭제하시겠습니까?"
+        },
+        "syncHelp": {
+            "step1": {
+                "desc1": "전용 USB 드라이브를 컴퓨터 USB 포트에 꽂아요.",
+                "desc2": "식별을 기다려요"
+            },
+            "step2": {
+                "desc1": "USB 포트를 교체하고 다시 시도해요.",
+                "desc2": "USB 디스크 이름이 수정되었는지 확인해 주세요."
+            }
+        },
+        "camera": {
+            "sn": "SN 코드",
+            "title": "모든 카메라"
+        },
+        "delete": "삭제",
+        "auth": {
+            "use": "사용 중"
+        },
+        "errMsg": {
+            "genObjTip": "망사 프로젝트는 계산중이니 기다려 주세요."
+        },
+        "time": {
+            "minute": "분"
+        },
+        "sceneDown": {
+            "down": "다운로드",
+            "downSuccess": "성공적으로 다운로드했습니다",
+            "all": "모두 선택",
+            "init": "처음 생성",
+            "delMsg": "삭제한 후에는 복구할 수 없습니다.이 데이터를 삭제하시겠습니까?"
+        },
+        "scene": {
+            "laserObj": "망사 프로젝트"
+        },
+        "errCode": {
+            "204": "중복 전송하지 마십시오. 나중에 다시 시도하십시오"
+        },
+        "exit-msg": "페이지를 닫은 후에도 프로그램은 배경에서 계속 실행됩니다. 종료하려면 오른쪽 하단 트레이를 열고 오른쪽 단추를 클릭하여 종료하십시오."
+    },
+    "common": {
+        "sure": "확정",
+        "downloadsuccess": "다운로드 성공",
+        "cancel": "삭제",
+        "addpicture": "그림 업로드",
+        "save": "저장"
+    },
+    "coord": {
+        "edit": {
+            "gisUpdateLocalUn": "지리적 좌표는 변경되었고 로컬 좌표는 변경되지 않았습니다.",
+            "whySetCtrls": "왜 제어점을 설정해야 하나요?",
+            "localUpdateGisUn": "로컬 좌표는 변경되었고 지리적 좌표는 변경되지 않았습니다.",
+            "movePoint": "이곳으로 이동하다",
+            "localPoint": "현지 좌표",
+            "whyTrapLocalPoint": "지역 좌표는 왜 설정합니까?"
+        }
+    },
+    "view": {
+        "scene": "3D",
+        "side": "측면 보기",
+        "sideRight": "측면 보기(E-W)",
+        "density": {
+            "low": "낮"
+        },
+        "sideLeft": "측면 보기( N-S)",
+        "switchView": "보기 전환",
+        "opacity": "퍼지성"
+    },
+    "dataset": {
+        "recalcJoinDeleteTip": "【{sceneName}】 가 다시 계산되었고 당신이 추가한 【{title}】 데이터셋은 삭제되었습니다",
+        "uploadTitle": "업로드할 데이터셋입니다",
+        "all": "모든 데이터셋",
+        "initial": "초기 데이터셋",
+        "backSearch": "검색결과로 돌아가요.",
+        "upload": "업로드",
+        "model": {
+            "volume": "부피 가"
+        }
+    },
+    "crop": {
+        "pointActions": {
+            "intersect": "상자 내용만 남깁니다",
+            "exclude": "상자 안에서 제거"
+        }
+    },
+    "earthwork": {
+        "unit": {
+            "meter": "미터"
+        },
+        "slamWring": "SLAM 모드에서 촬영한 장면은 당분간 토방량 측정 기능을 지원하지 않습니다.",
+        "export": "보고서 다운로드",
+        "downTitle": "데이터를 다운로드하다",
+        "stop": "그리기 중지"
+    },
+    "epoint": {
+        "closeRTK": "RTK 위치 끄기"
+    },
+    "err": {
+        "serve": {
+            "desc": [
+                "플랫폼 자원을 더 잘 사용할 수 있도록 업그레이드 중입니다. 업그레이드 중에는 액세스할 수 없습니다."
+            ]
+        },
+        "scene": {
+            "webgl": "메모리가 부족합니다. 여러 페이지나 프로그램을 동시에 열지 마십시오. 브라우저를 다시 시작한 후 다시 여십시오."
+        },
+        "preset": "메모리가 부족합니다. 여러 페이지나 프로그램을 동시에 열지 마십시오. 브라우저를 다시 시작한 후 다시 여십시오."
+    },
+    "help": {
+        "prev": "이전 단계",
+        "next": "다음 단계",
+        "tip": "다음 번에 여기에서 자습서를 열 수 있습니다.",
+        "mobile": {
+            "step4": {
+                "title": "점구름/파노라마 전환"
+            }
+        }
+    },
+    "hotspot": {
+        "all": "모든 핫스팟",
+        "meta": {
+            "image": {
+                "place": "그림 업로드"
+            },
+            "video": {
+                "place": "동영상 업로드"
+            },
+            "audio": {
+                "place": "오디오 업로드"
+            }
+        }
+    },
+    "measure": {
+        "unit": {
+            "meter": "미터"
+        },
+        "stop": "측정 중지"
+    },
+    "record": {
+        "all": "모든 비디오"
+    },
+    "resStatus": {
+        "503": "업로드 중 이상이 발생했습니다",
+        "502": "업로드 파일의 크기는 5GB보다 작아야 합니다",
+        "3024": "이 장면을 직접 연출하지 마세요",
+        "3008": "작업 실패",
+        "6006": "업로드한 그림의 크기가 원본과 일치하지 않습니다",
+        "6007": "업로드한 그래픽 데이터가 잘못되었습니다",
+        "6000": "이 데이터셋이 존재하지 않습니다.",
+        "8005": "식별을 기다려요"
+    },
+    "scene": {
+        "objTip": "망사 프로젝트",
+        "notice": {
+            "handerSuccess": "데이터 처리가 완료되었습니다."
+        },
+        "pano": "파노라마",
+        "download": {
+            "format": "다운로드 형식",
+            "cloud": "포인트 클라우드 다운로드"
+        },
+        "spaceModel": {
+            "defaultFloorTitle": "건물"
+        },
+        "floorpan": {
+            "customize": {
+                "steps": [
+                    null,
+                    "업로드, 원본문서형식으로업로드, 크기를수정하지않습니다."
+                ]
+            }
+        },
+        "pose": {
+            "unImage": "전경 모드에서는 위치를 설정할 수 없습니다"
+        }
+    },
+    "sys": {
+        "repeatPwdDiff": "두 번 입력한 비밀번호가 일치하지 않습니다",
+        "haveAccountLogin": "기존 계정으로 로그인하다",
+        "update": "수정",
+        "save": "저장",
+        "close": "닫기",
+        "delete": "삭제",
+        "upload": "업로드",
+        "download": "다운로드",
+        "uploadErr": {
+            "accept": "{accept} 형식만 지원합니다"
+        },
+        "all": "전부",
+        "compatible": {
+            "chrome": "Chrome",
+            "edg": "Microsoft Edge",
+            "safari": "Safari"
+        },
+        "downloadSuccess": "성공적으로 다운로드했습니다",
+        "downloadAndroidSuccess": "다운로드하였습니다. 파일을 가져오려면 관련 폴더를 여십시오",
+        "downloadIosSuccess": "다운로드하였습니다. 파일을 가져오려면 관련 폴더를 여십시오",
+        "time": {
+            "m": "분"
+        },
+        "setting": {
+            "setName": "이름 수정",
+            "public": "공개"
+        }
+    }
+}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1076 - 0
program/lang/weblate/zh.json


+ 19 - 0
src/i18n/constant.ts

@@ -0,0 +1,19 @@
+// 语言支持
+export enum langNameEum {
+  zh = 'zh',
+  en = 'en',
+  ja = 'ja',
+  jp = 'ja',
+  kr = 'kr'
+}
+
+export const langNameDescs = {
+  [langNameEum.zh]: '中文',
+  [langNameEum.en]: 'English',
+  [langNameEum.ja]: '日本語',
+  [langNameEum.kr]: '한국인'
+}
+
+export const deflangName = langNameEum.zh
+
+export const langNames = [langNameEum.en, langNameEum.zh, langNameEum.ja,langNameEum.kr]

+ 85 - 0
src/i18n/index.ts

@@ -0,0 +1,85 @@
+import { App, WritableComputedRef, ref, computed } from 'vue'
+import { createI18n, I18n as BaseI18n } from 'vue-i18n'
+import { deflangName, langNameEum, langNames, langNameDescs } from './constant'
+import { localGetFactory, localSetFactory } from '@/util/store'
+import { paramsToStr, strToParams } from "@/util";
+
+// import zh from './zh'
+// import en from './en'
+// import ja from './ja'
+// import jp from './ja'
+// import kr from './kr'
+// 接入weblate
+import zhProgram from './weblate/zh.json'
+import enProgram from './weblate/en.json'
+import jaProgram from './weblate/ja.json'
+import krProgram from './weblate/kr.json'
+// zh.program = zhProgram.program
+// en.program = enProgram.program
+// ja.program = jaProgram.program
+
+const langList = ref([]);
+
+const localKey = 'lang'
+const local = {
+  get: localGetFactory(str => {
+    if (str) {
+      return str
+    } else {
+      const langs = Object.keys(langNameDescs)
+      const defLang = window?.navigator?.language || 'zh'
+      const navLang = langs.find(lang =>
+        new RegExp(`-?${lang}-?`).test(defLang)
+      )
+      return navLang || langNameEum.en
+    }
+  }),
+  set: localSetFactory((lang) => lang)
+}
+
+const params = strToParams(location.search)
+export const lang = (params.lang || window.lang || local.get(localKey))
+console.log(lang)
+
+if (lang !== local.get(localKey)) {
+  local.set(localKey, lang)
+}
+
+const i18n = createI18n({
+  legacy: false,
+  fallbackLocale: deflangName,
+  availableLocales: langNames,
+  locale: lang,
+  sync: true,
+  silentTranslationWarn: true,
+  missingWarn: false,
+  silentFallbackWarn: true
+})
+
+i18n.global.setLocaleMessage('zh', zhProgram)
+i18n.global.setLocaleMessage('en', enProgram)
+i18n.global.setLocaleMessage('ja', jaProgram)
+i18n.global.setLocaleMessage('kr', krProgram)
+i18n.global.changeLang = (lang, reload = true) => {
+  i18n.global.locale.value = lang
+  local.set(localKey, lang)
+  params.lang = lang
+  if (reload) {
+    location.search = paramsToStr(params)
+  }
+  // location.reload()
+}
+
+export const setupI18n = (app) => {
+  app.config.globalProperties.$t = i18n.global.t
+  app.use(i18n)
+}
+export const changeLang = i18n.global.changeLang
+export const ui18n = i18n.global
+export const useI18n = () => ui18n
+
+
+export const getLangList = computed(() => langList.value)
+export const setLangList = (list) => {
+  langList.value = list
+}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1044 - 0
src/i18n/weblate/en.json


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1044 - 0
src/i18n/weblate/ja.json


+ 215 - 0
src/i18n/weblate/kr.json

@@ -0,0 +1,215 @@
+{
+    "program": {
+        "other": {
+            "toWinIng": "컴퓨터로 동기화하고 있어요.",
+            "syncIng": "N 개의 시나리오가 동기화됩니다",
+            "usb": "USB메모리",
+            "usbScene": "USB에 담긴 프로젝트",
+            "gscene": "N 개의 장면",
+            "delTipPost": "삭제하시겠습니까?"
+        },
+        "syncHelp": {
+            "step1": {
+                "desc1": "전용 USB 드라이브를 컴퓨터 USB 포트에 꽂아요.",
+                "desc2": "식별을 기다려요"
+            },
+            "step2": {
+                "desc1": "USB 포트를 교체하고 다시 시도해요.",
+                "desc2": "USB 디스크 이름이 수정되었는지 확인해 주세요."
+            }
+        },
+        "camera": {
+            "sn": "SN 코드",
+            "title": "모든 카메라"
+        },
+        "delete": "삭제",
+        "auth": {
+            "use": "사용 중"
+        },
+        "errMsg": {
+            "genObjTip": "망사 프로젝트는 계산중이니 기다려 주세요."
+        },
+        "time": {
+            "minute": "분"
+        },
+        "sceneDown": {
+            "down": "다운로드",
+            "downSuccess": "성공적으로 다운로드했습니다",
+            "all": "모두 선택",
+            "init": "처음 생성",
+            "delMsg": "삭제한 후에는 복구할 수 없습니다.이 데이터를 삭제하시겠습니까?"
+        },
+        "scene": {
+            "laserObj": "망사 프로젝트"
+        },
+        "errCode": {
+            "204": "중복 전송하지 마십시오. 나중에 다시 시도하십시오"
+        },
+        "exit-msg": "페이지를 닫은 후에도 프로그램은 배경에서 계속 실행됩니다. 종료하려면 오른쪽 하단 트레이를 열고 오른쪽 단추를 클릭하여 종료하십시오."
+    },
+    "common": {
+        "sure": "확정",
+        "downloadsuccess": "다운로드 성공",
+        "cancel": "삭제",
+        "addpicture": "그림 업로드",
+        "save": "저장"
+    },
+    "coord": {
+        "edit": {
+            "gisUpdateLocalUn": "지리적 좌표는 변경되었고 로컬 좌표는 변경되지 않았습니다.",
+            "whySetCtrls": "왜 제어점을 설정해야 하나요?",
+            "localUpdateGisUn": "로컬 좌표는 변경되었고 지리적 좌표는 변경되지 않았습니다.",
+            "movePoint": "이곳으로 이동하다",
+            "localPoint": "현지 좌표",
+            "whyTrapLocalPoint": "지역 좌표는 왜 설정합니까?"
+        }
+    },
+    "view": {
+        "scene": "3D",
+        "side": "측면 보기",
+        "sideRight": "측면 보기(E-W)",
+        "density": {
+            "low": "낮"
+        },
+        "sideLeft": "측면 보기( N-S)",
+        "switchView": "보기 전환",
+        "opacity": "퍼지성"
+    },
+    "dataset": {
+        "recalcJoinDeleteTip": "【{sceneName}】 가 다시 계산되었고 당신이 추가한 【{title}】 데이터셋은 삭제되었습니다",
+        "uploadTitle": "업로드할 데이터셋입니다",
+        "all": "모든 데이터셋",
+        "initial": "초기 데이터셋",
+        "backSearch": "검색결과로 돌아가요.",
+        "upload": "업로드",
+        "model": {
+            "volume": "부피 가"
+        }
+    },
+    "crop": {
+        "pointActions": {
+            "intersect": "상자 내용만 남깁니다",
+            "exclude": "상자 안에서 제거"
+        }
+    },
+    "earthwork": {
+        "unit": {
+            "meter": "미터"
+        },
+        "slamWring": "SLAM 모드에서 촬영한 장면은 당분간 토방량 측정 기능을 지원하지 않습니다.",
+        "export": "보고서 다운로드",
+        "downTitle": "데이터를 다운로드하다",
+        "stop": "그리기 중지"
+    },
+    "epoint": {
+        "closeRTK": "RTK 위치 끄기"
+    },
+    "err": {
+        "serve": {
+            "desc": [
+                "플랫폼 자원을 더 잘 사용할 수 있도록 업그레이드 중입니다. 업그레이드 중에는 액세스할 수 없습니다."
+            ]
+        },
+        "scene": {
+            "webgl": "메모리가 부족합니다. 여러 페이지나 프로그램을 동시에 열지 마십시오. 브라우저를 다시 시작한 후 다시 여십시오."
+        },
+        "preset": "메모리가 부족합니다. 여러 페이지나 프로그램을 동시에 열지 마십시오. 브라우저를 다시 시작한 후 다시 여십시오."
+    },
+    "help": {
+        "prev": "이전 단계",
+        "next": "다음 단계",
+        "tip": "다음 번에 여기에서 자습서를 열 수 있습니다.",
+        "mobile": {
+            "step4": {
+                "title": "점구름/파노라마 전환"
+            }
+        }
+    },
+    "hotspot": {
+        "all": "모든 핫스팟",
+        "meta": {
+            "image": {
+                "place": "그림 업로드"
+            },
+            "video": {
+                "place": "동영상 업로드"
+            },
+            "audio": {
+                "place": "오디오 업로드"
+            }
+        }
+    },
+    "measure": {
+        "unit": {
+            "meter": "미터"
+        },
+        "stop": "측정 중지"
+    },
+    "record": {
+        "all": "모든 비디오"
+    },
+    "resStatus": {
+        "503": "업로드 중 이상이 발생했습니다",
+        "502": "업로드 파일의 크기는 5GB보다 작아야 합니다",
+        "3024": "이 장면을 직접 연출하지 마세요",
+        "3008": "작업 실패",
+        "6006": "업로드한 그림의 크기가 원본과 일치하지 않습니다",
+        "6007": "업로드한 그래픽 데이터가 잘못되었습니다",
+        "6000": "이 데이터셋이 존재하지 않습니다.",
+        "8005": "식별을 기다려요"
+    },
+    "scene": {
+        "objTip": "망사 프로젝트",
+        "notice": {
+            "handerSuccess": "데이터 처리가 완료되었습니다."
+        },
+        "pano": "파노라마",
+        "download": {
+            "format": "다운로드 형식",
+            "cloud": "포인트 클라우드 다운로드"
+        },
+        "spaceModel": {
+            "defaultFloorTitle": "건물"
+        },
+        "floorpan": {
+            "customize": {
+                "steps": [
+                    null,
+                    "업로드, 원본문서형식으로업로드, 크기를수정하지않습니다."
+                ]
+            }
+        },
+        "pose": {
+            "unImage": "전경 모드에서는 위치를 설정할 수 없습니다"
+        }
+    },
+    "sys": {
+        "repeatPwdDiff": "두 번 입력한 비밀번호가 일치하지 않습니다",
+        "haveAccountLogin": "기존 계정으로 로그인하다",
+        "update": "수정",
+        "save": "저장",
+        "close": "닫기",
+        "delete": "삭제",
+        "upload": "업로드",
+        "download": "다운로드",
+        "uploadErr": {
+            "accept": "{accept} 형식만 지원합니다"
+        },
+        "all": "전부",
+        "compatible": {
+            "chrome": "Chrome",
+            "edg": "Microsoft Edge",
+            "safari": "Safari"
+        },
+        "downloadSuccess": "성공적으로 다운로드했습니다",
+        "downloadAndroidSuccess": "다운로드하였습니다. 파일을 가져오려면 관련 폴더를 여십시오",
+        "downloadIosSuccess": "다운로드하였습니다. 파일을 가져오려면 관련 폴더를 여십시오",
+        "time": {
+            "m": "분"
+        },
+        "setting": {
+            "setName": "이름 수정",
+            "public": "공개"
+        }
+    }
+}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1086 - 0
src/i18n/weblate/zh.json


+ 2 - 0
src/main.ts

@@ -10,6 +10,7 @@ import { router } from "./router";
 import { appConstant } from "./app";
 import 'virtual:windi.css'
 import "@/store/system";
+import { setupI18n } from "@/i18n/index";
 import '@/assets/font/iconfont.css'
 const app = createApp(App);
 
@@ -19,6 +20,7 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
 directiveSetup(app);
 app.use(ElementPlus);
 app.use(router);
+setupI18n(app)
 setApp(app);
 app.mount("#app");
 

+ 5 - 6
src/request/index.ts

@@ -48,12 +48,11 @@ axios.interceptors.request.use(async (config) => {
   const { token, userId } = getAuth();
   let caseId = router.currentRoute.value?.params?.caseId
   console.log("token",localStorage.getItem('token'), token, "userId", userId, config.url);
-  if (!token && !~notLoginUrls.indexOf(config.url)) {
-    // router.replace({ name: RouteName.login });
-    let redirect = encodeURIComponent(`${window.location.href}`);
-    window.location.href = window.location.origin + "/admin/#/login?redirect=" + redirect;
-    throw "用户未登录";
-  }
+  // if (!token && !~notLoginUrls.indexOf(config.url)) {
+  //   let redirect = encodeURIComponent(`${window.location.href}`);
+  //   window.location.href = window.location.origin + "/admin/#/login?redirect=" + redirect;
+  //   throw "用户未登录";
+  // }
   config.headers.token = token;
   config.headers['caseid'] = caseId;
   config.headers['page-type'] = 'edit';

+ 3 - 2
src/request/urls.ts

@@ -250,7 +250,7 @@ export const addmodelByMediaLibrary = "/fusion/model/addByMediaLibrary";
 export const criminalInfo = "/fusion/caseInquestCriminal/info";
 export const saveOrUpdate = "/fusion/caseInquestCriminal/saveOrUpdate";
 export const downDocx = "/fusion/caseInquestCriminal/downDocx";
-export const getSceneList = "/service/manage/scene/list";
+export const getSceneList = "/fusion/scene/list";
 export const getDictFileList = "/service/manage/dict/getByKey/media-library";
 export const delDictFileList = "/service/manage/dictFile/del/media-library";
 export const getListFileList = "/service/manage/dictFile/pageList/media-library";
@@ -267,9 +267,10 @@ export const getByKey = '/fusion/dict/getByKey/media-library';
 export const getpageList = '/fusion/dict/pageList/media-library';
 export const addOrUpdate = '/fusion/dict/addOrUpdate/media-library';
 export const del = '/fusion/dict/del/media-library';
-export const upload = '/fusion/common/upload/fileNew';
+export const uploadUrl = '/fusion/upload/fileNew';
 export const getCaseNumByRyId = '/fusion/case/getCaseNumByRyId';
 export const mediaList = '/fusion/dictFile/pageList/media-library';
+export const fileaddOrUpdate = '/fusion/dictFile/addOrUpdate/media-library';
 
 
 

+ 11 - 0
src/store/case.ts

@@ -42,6 +42,8 @@ import {
   dictFiledel,
   del,
   addOrUpdate,
+  fileaddOrUpdate,
+  uploadUrl,
 } from "@/request";
 import { router } from "@/router";
 import { ModelScene, QuoteScene, Scene, SceneType } from "./scene";
@@ -58,6 +60,7 @@ export type Case = {
 };
 let isloadList = false;
 export const treeList = ref([]);
+export const lang = ref(localStorage.getItem("lang") || "zh");
 export const sceneList = ref([]);
 export const caseInfoData = ref({
   caseTitle: '',
@@ -69,6 +72,10 @@ export const setCaseSharePWD = (params: { caseId: number; randCode: string }) =>
 export const setCaseaddOrUpdate = (params) =>
   axios.post(caseaddOrUpdate, params);
 
+export const setFileaddOrUpdate = (params) =>
+  axios.post(fileaddOrUpdate, params);
+
+
 export const getCaseSharePWD = async (params: { caseId: number }) =>
   (await axios.get<string>(getCasePsw, { params })).data;
 
@@ -195,6 +202,10 @@ export const caseUpdateSort = (list: [CaseImg]) =>
 export const saveOrAndSave = (params) =>
   axios.post(uploadImagesAndSave, { ...params });
 
+export const uploadFile = async (params) => {
+  return (await axios.post<string>(uploadUrl, params)).data;
+};
+
 export const getByTreeFileLists = async () => {
   return treeList.value
 };

+ 7 - 0
src/util/index.ts

@@ -337,6 +337,13 @@ export const strToParams = (str: string) => {
 
   return result;
 };
+// 对象转params
+export const paramsToStr = (params: { [key: string]: string | boolean }) =>
+  '?' +
+  Object.keys(params)
+    .filter(key => params[key] !== undefined)
+    .map(key => `${key}${params[key] == false ? '' : `=${params[key]}`}`)
+    .join('&')
 
 export const getDomMatrix = (dom: HTMLElement) => {
   const str = getComputedStyle(dom, null).getPropertyValue("transform");

+ 107 - 0
src/util/store.ts

@@ -0,0 +1,107 @@
+type Store = typeof localStorage | typeof sessionStorage
+
+type SetTransform<T> = (args: T) => string
+type GetTransform<T> = (args: null | string) => T
+
+export function get(store: Store, name: string): string | null
+export function get<T>(
+  store: Store,
+  name: string,
+  transform: GetTransform<T>
+): T
+export function get(store, name, transform?) {
+  const value = store.getItem(name)
+  if (transform) {
+    return transform(value)
+  } else {
+    return value
+  }
+}
+
+export function set(store: Store, name: string, value: string): string
+export function set<T>(
+  store: Store,
+  name: string,
+  value: T,
+  transform: SetTransform<T>
+): string
+export function set(store, name, value, transform?) {
+  if (transform) {
+    value = transform(value)
+  }
+  store.setItem(name, value)
+  return value
+}
+
+export function getFactory(store: Store): (name: string) => string | null
+export function getFactory<T>(
+  store: Store,
+  transform: GetTransform<T>
+): (name: string | null) => T
+export function getFactory<T>(store: Store, transform?: GetTransform<T>) {
+  return (name: string | null) =>
+    transform ? get(store, name, transform) : get(store, name)
+}
+
+export function setFactory(
+  store: Store
+): (name: string, value: string) => string
+export function setFactory<T>(
+  store: Store,
+  transform: SetTransform<T>
+): (name: string, value: T) => string
+export function setFactory(store, transform?) {
+  return (name: string, value) =>
+    transform ? set(store, name, transform(value)) : set(store, name, value)
+}
+
+export function localGetFactory(): (name: string) => string | null
+export function localGetFactory<T>(
+  transform: GetTransform<T>
+): (name: string | null) => T
+export function localGetFactory(transform?) {
+  return getFactory(localStorage, transform)
+}
+
+export function localSetFactory(): (name: string, value: string) => string
+export function localSetFactory<T>(
+  transform: SetTransform<T>
+): (name: string, value: T) => string
+export function localSetFactory(transform?) {
+  return setFactory(localStorage, transform)
+}
+export function localDel(key) {
+  localStorage.removeItem(key)
+}
+
+export function sessionGetFactory(): (name: string) => string | null
+export function sessionGetFactory<T>(
+  transform: GetTransform<T>
+): (name: string | null) => T
+export function sessionGetFactory(transform?) {
+  return getFactory(sessionStorage, transform)
+}
+
+export function sessionSetFactory(): (name: string, value: string) => string
+export function sessionSetFactory<T>(
+  transform: SetTransform<T>
+): (name: string, value: T) => string
+export function sessionSetFactory(transform?) {
+  return setFactory(sessionStorage, transform)
+}
+
+export function sessionDel(key) {
+  sessionStorage.removeItem(key)
+}
+
+export const local = {
+  get: localGetFactory(),
+  set: localSetFactory(),
+  del: localDel
+}
+
+export const session = {
+  get: sessionGetFactory(),
+  set: sessionSetFactory(),
+  del: sessionDel
+}

+ 17 - 0
src/view/layout/index.vue

@@ -54,6 +54,23 @@ const caseId = computed(() => {
     return Number(caseId);
   }
 });
+const getAllParams = () => {
+  let href = window.location.href;
+  let query = href.substring(href.indexOf("?") + 1);
+  let vars = query.split("&");
+  let obj = {};
+  for (let i = 0; i < vars.length; i++) {
+    let pair = vars[i].split("=");
+    // 将参数名和参数值分别作为对象的属性名和属性值
+    obj[pair[0]] = pair[1];
+  }
+  return obj;
+};
+let querys = getAllParams() || {};
+console.log(querys, 'getAllParams');
+if(querys.lang){
+
+}
 // const qpisceneList = ref(getCaseSceneListData() as any[]);
 const sceneList = ref([]);
 const sceneURL = ref(`/code/index.html?caseId=${caseId.value}#/show`)

+ 1 - 1
src/view/layout/top/index.vue

@@ -2,7 +2,7 @@
   <div class="header-top">
     <div class="title" >
       <div class="span" :title="title">{{ title }}</div>
-      <div class="edit" style="white-space: nowrap;text-overflow: ellipsis;">编辑</div>
+      <div class="edit" style="white-space: nowrap;text-overflow: ellipsis;">{{$t('program.sceneDown.edit')}}</div>
     </div>
     <div class="oper-btns">
       <el-button style="padding: 5px 10px" type="primary" @click="handlemtk">媒体库</el-button>

+ 51 - 10
src/view/mediaLibrary/TableComponent.vue

@@ -57,7 +57,9 @@
     </el-form>
     <!-- 表格 -->
     <el-table
-      size="large"
+    tooltip-effect="dark"
+        ref="tableRef"
+        size="large"
       :data="tableData"
       style="width: 100%"
       :row-class-name="tableRowClassName"
@@ -79,6 +81,7 @@
         v-for="column in columns"
         :key="column.prop"
         :prop="column.prop"
+        show-overflow-tooltip
         align="center"
         :label="column.label"
       ></el-table-column>
@@ -136,7 +139,7 @@
           </el-upload>
         </el-form-item>
         <el-form-item
-          :label="dialogData.title == '' ? '分组' : '修改分组'"
+          :label="dialogData.title == '上传' ? '分组' : '修改分组'"
           :label-width="formLabelWidth"
         >
           <el-select style="width: 180px" v-model="addForm.dictId" placeholder="请选择分组">
@@ -182,15 +185,15 @@
     <el-dialog v-model="dialogData.fzshow" title="分组管理" width="500">
     <el-form :model="form" label-width="50">
       <el-form-item label="分组" :label-width="formLabelWidth">
-        <el-input v-model="dialogData.fzName" style="width: 300px;" />
+        <el-input v-model="dialogData.fzName" maxLength="100" style="width: 300px;" />
         <el-button style="margin-left: 20px" @click="handlefzAdd" type="primary">新增</el-button>
       </el-form-item>
       <div class="itemTitle">
         <p>分组列表</p>
         <div class="itemTitleList">
           <div class="itemTitle-list" v-for="(item, index) in dictIdList" :key="index">
-            <span>{{ item.dictName }}</span>
-            <span @click="hanleFzDle(item)">删除</span>
+            <span class="name">{{ item.dictName }}</span>
+            <span class="del" @click="hanleFzDle(item)">删除</span>
           </div>
         </div>
       </div>
@@ -198,7 +201,7 @@
     <template #footer>
       <div class="dialog-footer">
           <el-button @click="dialogData.fzshow = false">取消</el-button>
-          <el-button type="primary" @click="handleuploadAdd">
+          <el-button type="primary" @click="dialogData.fzshow = false">
             确定
           </el-button>
       </div>
@@ -210,7 +213,7 @@
 <script setup>
 import { Search } from "@element-plus/icons-vue";
 import { ref, watch, onMounted } from "vue";
-import { getByKeyList, getfzdel, getdictFiledel, getaddOrUpdate  } from "@/store/case";
+import { getByKeyList, getfzdel, getdictFiledel, getaddOrUpdate, setFileaddOrUpdate, uploadFile } from "@/store/case";
 import dayjs from "dayjs";
 import obj from "@/assets/images/obj.jpg";
 import osgb from "@/assets/images/osgb.jpg";
@@ -286,6 +289,8 @@ const removeFile = () => {
 };
 const handleAdd = () => {
   addForm.value.dictId = '';
+  addForm.value.dictId = '';
+  fileList.value = [];
   dialogData.value = {
     show: true,
     title: "上传",
@@ -310,11 +315,35 @@ const handleAddfz = () => {
   dialogData.value.fzshow = true;
 };
 
-const handleuploadAdd = () => {
-  if (file.value) {
+const handleuploadAdd = async () => {
+  console.log('formData', dialogData.value.title);
+  if (dialogData.value.title != '上传'){
+    await setFileaddOrUpdate({
+      id: dialogData.value.data?.id,
+      dictId: addForm.value.dictId,
+    })
+    initData();
+    dialogData.value.show = false
+    dialogData.value.title = ''
+    ElMessage({
+      type: "success",
+      message: "操作成功",
+    });
+  } else {
     const formData = new FormData();
-    formData.append("file", file.value);
+    formData.append("file", URL.createObjectURL(fileList.value && fileList.value[0]));
     formData.append("dictId", addForm.value.dictId);
+    uploadFile(formData).then((res) => {
+    console.log(res);
+    if (res) {
+      initData();
+      dialogData.value.show = false;
+      ElMessage({
+        type: "success",
+        message: "上传成功",
+      });
+    }
+  });
     console.log(formData);
   }
 };
@@ -541,6 +570,18 @@ initData();
       line-height: 30px;
       display: flex;
       justify-content: space-between;
+      position: relative;
+      cursor: pointer;
+      .name{
+        width: 350px;
+        overflow: hidden; //超出的文本隐藏
+        text-overflow: ellipsis; //溢出用省略号显示
+        white-space: nowrap; //溢出不换行
+      }
+      .del{
+        color: red;
+        cursor: pointer;
+      }
     }
   }
 }

+ 10 - 9
src/view/vrmodel/index.vue

@@ -1,17 +1,17 @@
 <template>
   <div class="scene">
-    <span class="title1" style="line-height: 32px">多元融合</span>
-    <el-button class="w-full my-4" @click="handleAdddyrh">编辑</el-button>
+    <span class="title1" style="line-height: 32px">{{$t('sceneHome.dyrh')}}</span>
+    <el-button class="w-full my-4" @click="handleAdddyrh">{{$t('program.sceneDown.edit')}}</el-button>
     <div class="scene-list">
       <div class="scene-title flex justify-between content-center">
-        <span class="title1" style="line-height: 32px">场景列表</span>
+        <span class="title1" style="line-height: 32px">{{$t('sceneHome.sceneList')}}</span>
         <!-- <el-switch v-model="active" @change="handleActive" /> -->
       </div>
       <el-button
         class="w-full my-4"
         @click="handleAdd"
         style="margin-left: 0; margin-right: 0"
-        >新增</el-button
+        >{{$t('common.add')}}</el-button
       >
       <div class="list" v-if="active" style="min-height: 630px">
         <div
@@ -20,16 +20,16 @@
           :key="index"
         >
           <span class="anmc" :title="item.name">{{
-            item.name || "多元融合"
+            item.name || $t('sceneHome.dyrh')
           }}</span>
           <div class="cursor-pointer" quaternary type="primary">
             <span
               class="mr-4"
               @click="handlegotoelT(item)"
               :style="{ color: item.inFusion ? '#ccc' : '' }"
-              >移除</span
+              >{{$t('sceneHome.library')}}</span
             >
-            <span @click="handlegotoEdit(item)">编辑</span>
+            <span @click="handlegotoEdit(item)">{{$t('program.sceneDown.edit')}}</span>
           </div>
           <!-- <div @click="handlegotoEdit(item)" class="cursor-pointer" quaternary type="primary">编辑</div> -->
         </div>
@@ -61,6 +61,7 @@ import ModelContent from "./modelContent.vue";
 import { useScenePaggingParams } from "./pagging";
 import { computed, ref, onMounted } from "vue";
 import { tableModelScene } from "./quisk";
+import { ui18n } from '@/i18n'
 const active = ref(true);
 const list = ref([]);
 const isEdit = ref(false);
@@ -92,7 +93,7 @@ async function handlegotoelT(record) {
   //   ElMessage.error("无法移除,场景已加入多元融合,请进入多元融合移除场景后再试");
   //   return
   // }
-  if (record.inFusion || (await confirm("确定要移除当前场景吗?"))) {
+  if (record.inFusion || (await confirm(ui18n.t('sceneHome.ycTips')))) {
     list.value = list.value.filter((item) => item.id !== record.id);
     submitForm();
   }
@@ -109,7 +110,7 @@ async function submitForm() {
   setCaseaddOrUpdate(apiData).then((res) => {
     geiList();
     ElMessage({
-      message: "操作成功",
+      message: ui18n.t('program.resStatus.200'),
       type: "success",
     });
   }).catch((errr) => {

+ 2 - 1
src/view/vrmodel/quisk.ts

@@ -4,13 +4,14 @@ import tableModel from "./tableModel.vue";
 import SceneDownload from "./sceneDownload.vue";
 import { quiskMountFactory } from "@/helper/mount";
 import { axios, checkHasDownload } from "@/request";
+import { ui18n } from '@/i18n'
 
 export const editModelScene = quiskMountFactory(EditModel, {
   title: "编辑模型",
   width: 500,
 });
 export const tableModelScene = quiskMountFactory(tableModel, {
-  title: "添加场景",
+  title: ui18n.t('sceneHome.addScene'),
   width: 1000,
 });
 export type SceneDpwnloadProps = { scene: QuoteScene };

+ 4 - 2
src/view/vrmodel/tableModel.vue

@@ -34,15 +34,17 @@ import ModelContent from "./modelContent.vue";
 import { useScenePaggingParams } from "./pagging";
 import { QuiskExpose } from "@/helper/mount";
 import { computed, ref } from "vue";
+import { ui18n } from '@/i18n'
+
 defineProps<{ numList: Array<string> }>();
 const options = [
   {
     value: '1',
-    label: 'Mesh 场景',
+    label: ui18n.t('program.scene.laserObj'),
   },
   {
     value: '2',
-    label: '点云场景',
+    label: ui18n.t('program.scene.laserClo'),
   },
 ]
 

+ 66 - 0
translate.js

@@ -0,0 +1,66 @@
+const http = require('http')
+const fs = require('fs')
+
+function checkIfPhishing(urlToPrint, path, lang = 'zh') {
+  fs.mkdirSync(path, { recursive: true })
+  const file = fs.createWriteStream(`${path}/${lang}.json`)
+  const request = http.get(urlToPrint, function (response) {
+    response
+      .on('finish', function () {
+        console.log('done')
+        // console.log(fs.readFileSync(`${lang}.json`, { encoding: "utf8" }));
+      })
+      .pipe(file)
+  })
+}
+//  zh
+checkIfPhishing(
+  'http://192.168.0.163:8080/download/4kankan/eletron_laster_local/zh_Hans/',
+  'src/i18n/weblate',
+  'zh'
+)
+//  en
+checkIfPhishing(
+  'http://192.168.0.163:8080/download/4kankan/eletron_laster_local/en/',
+  'src/i18n/weblate',
+  'en'
+)
+//  ja
+checkIfPhishing(
+  'http://192.168.0.163:8080/download/4kankan/eletron_laster_local/ja/',
+  'src/i18n/weblate',
+  'ja'
+)
+
+//  ja
+checkIfPhishing(
+  'http://192.168.0.163:8080/download/4kankan/eletron_laster_local/ko/',
+  'src/i18n/weblate',
+  'kr'
+)
+
+
+
+//  zh
+checkIfPhishing(
+  'http://192.168.0.163:8080/download/4kankan/eletron_laster_local/zh_Hans/',
+  'program/lang/weblate',
+  'zh'
+)
+//  en
+checkIfPhishing(
+  'http://192.168.0.163:8080/download/4kankan/eletron_laster_local/en/',
+  'program/lang/weblate',
+  'en'
+)
+//  ja
+checkIfPhishing(
+  'http://192.168.0.163:8080/download/4kankan/eletron_laster_local/ja/',
+  'program/lang/weblate',
+  'ja'
+)
+checkIfPhishing(
+  'http://192.168.0.163:8080/download/4kankan/eletron_laster_local/ko/',
+  'program/lang/weblate',
+  'kr'
+)

+ 37 - 2
yarn.lock

@@ -177,6 +177,27 @@
   resolved "https://mirrors.cloud.tencent.com/npm/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62"
   integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==
 
+"@intlify/core-base@11.1.1":
+  version "11.1.1"
+  resolved "https://registry.npmmirror.com/@intlify/core-base/-/core-base-11.1.1.tgz#2ab2f21fac40a5a2ceb190e9cbf6c23393e105a2"
+  integrity sha512-bb8gZvoeKExCI2r/NVCK9E4YyOkvYGaSCPxVZe8T0jz8aX+dHEOZWxK06Z/Y9mWRkJfBiCH4aOhDF1yr1t5J8Q==
+  dependencies:
+    "@intlify/message-compiler" "11.1.1"
+    "@intlify/shared" "11.1.1"
+
+"@intlify/message-compiler@11.1.1":
+  version "11.1.1"
+  resolved "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-11.1.1.tgz#07c8640460d3ec4c70411f902fc38c90a3f1d8cf"
+  integrity sha512-4iEsUZ3aF7jXY19CJFN5VP+pPyLITD9FVsjB13z9TU1UxaZLlFsmNhvRxlPDSOfHAP5RpNF2QKKdZ3DHVf4Yzw==
+  dependencies:
+    "@intlify/shared" "11.1.1"
+    source-map-js "^1.0.2"
+
+"@intlify/shared@11.1.1":
+  version "11.1.1"
+  resolved "https://registry.npmmirror.com/@intlify/shared/-/shared-11.1.1.tgz#7cd50bf4dd5162a59d2e27aa1a03b4b68275dfeb"
+  integrity sha512-2kGiWoXaeV8HZlhU/Nml12oTbhv7j2ufsJ5vQaa0VTjzUmZVdd/nmKFRAOJ/FtjO90Qba5AnZDwsrY7ZND5udA==
+
 "@jridgewell/sourcemap-codec@^1.5.0":
   version "1.5.0"
   resolved "https://mirrors.cloud.tencent.com/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
@@ -431,7 +452,7 @@
     "@vue/compiler-dom" "3.5.13"
     "@vue/shared" "3.5.13"
 
-"@vue/devtools-api@^6.6.4":
+"@vue/devtools-api@^6.5.0", "@vue/devtools-api@^6.6.4":
   version "6.6.4"
   resolved "https://mirrors.cloud.tencent.com/npm/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343"
   integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==
@@ -890,6 +911,11 @@ html2canvas@^1.4.1:
     css-line-break "^2.1.0"
     text-segmentation "^1.0.3"
 
+http@^0.0.1-security:
+  version "0.0.1-security"
+  resolved "https://registry.npmmirror.com/http/-/http-0.0.1-security.tgz#3aac09129d12dc2747bbce4157afde20ad1f7995"
+  integrity sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==
+
 immutable@^5.0.2:
   version "5.0.3"
   resolved "https://mirrors.cloud.tencent.com/npm/immutable/-/immutable-5.0.3.tgz#aa037e2313ea7b5d400cd9298fa14e404c933db1"
@@ -1192,7 +1218,7 @@ side-channel@^1.0.6:
     get-intrinsic "^1.2.4"
     object-inspect "^1.13.1"
 
-"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.0, source-map-js@^1.2.1:
+"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2, source-map-js@^1.2.0, source-map-js@^1.2.1:
   version "1.2.1"
   resolved "https://mirrors.cloud.tencent.com/npm/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
   integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
@@ -1313,6 +1339,15 @@ vue-draggable-plus@^0.6.0:
   dependencies:
     "@types/sortablejs" "^1.15.8"
 
+vue-i18n@^11.1.1:
+  version "11.1.1"
+  resolved "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-11.1.1.tgz#8bae0201ac1396d1c77a60ed2a03026396ab4714"
+  integrity sha512-0P6DkKy96R4Wh2sIZJEHw8ivnlD1pnB6Ib/eldoF1SUpQutfKZv6aMqZwICS1gW0rwq24ZSXw7y3jW+PRVYqWA==
+  dependencies:
+    "@intlify/core-base" "11.1.1"
+    "@intlify/shared" "11.1.1"
+    "@vue/devtools-api" "^6.5.0"
+
 vue-router@^4.2.4:
   version "4.5.0"
   resolved "https://mirrors.cloud.tencent.com/npm/vue-router/-/vue-router-4.5.0.tgz#58fc5fe374e10b6018f910328f756c3dae081f14"