Преглед на файлове

feat(多语言): 添加确认操作相关文案并优化交互组件

refactor(样式): 将确认弹窗样式移至全局样式文件
fix(多语言): 统一选择题文案为"选择"
lsc преди 1 месец
родител
ревизия
86cc5ef29b
променени са 6 файла, в които са добавени 308 реда и са изтрити 107 реда
  1. 102 75
      src/App.vue
  2. 195 2
      src/views/Editor/CanvasTool/index2.vue
  3. 0 25
      src/views/Student/components/DialoguePanel.vue
  4. 4 2
      src/views/lang/cn.json
  5. 3 1
      src/views/lang/en.json
  6. 4 2
      src/views/lang/hk.json

+ 102 - 75
src/App.vue

@@ -30,132 +30,133 @@
 
 
 
 
 <script lang="ts" setup>
 <script lang="ts" setup>
-import { onMounted, ref, provide } from "vue";
-import { storeToRefs } from "pinia";
+import { onMounted, ref, provide } from 'vue'
+import { storeToRefs } from 'pinia'
 import {
 import {
   useScreenStore,
   useScreenStore,
   useMainStore,
   useMainStore,
   useSnapshotStore,
   useSnapshotStore,
   useSlidesStore,
   useSlidesStore,
-} from "@/store";
-import { lang } from "@/main";
-import { LOCALSTORAGE_KEY_DISCARDED_DB } from "@/configs/storage";
-import { deleteDiscardedDB } from "@/utils/database";
-import { isPC } from "@/utils/common";
-import api from "@/services";
-
-import Editor from "./views/Editor/index.vue";
-import Editor2 from "./views/Editor/index2.vue";
-import Editor3 from "./views/Editor/index3.vue";
-import Screen from "./views/Screen/index.vue";
-import Mobile from "./views/Mobile/index.vue";
-import Student from "./views/Student/index.vue";
-import FullscreenSpin from "@/components/FullscreenSpin.vue";
-
-const _isPC = isPC();
-
-const mainStore = useMainStore();
-const slidesStore = useSlidesStore();
-const snapshotStore = useSnapshotStore();
-const { databaseId } = storeToRefs(mainStore);
-const { slides } = storeToRefs(slidesStore);
-const { screening } = storeToRefs(useScreenStore());
+} from '@/store'
+import { lang } from '@/main'
+import { LOCALSTORAGE_KEY_DISCARDED_DB } from '@/configs/storage'
+import { deleteDiscardedDB } from '@/utils/database'
+import { isPC } from '@/utils/common'
+import api from '@/services'
+
+import Editor from './views/Editor/index.vue'
+import Editor2 from './views/Editor/index2.vue'
+import Editor3 from './views/Editor/index3.vue'
+import Screen from './views/Screen/index.vue'
+import Mobile from './views/Mobile/index.vue'
+import Student from './views/Student/index.vue'
+import FullscreenSpin from '@/components/FullscreenSpin.vue'
+
+const _isPC = isPC()
+
+const mainStore = useMainStore()
+const slidesStore = useSlidesStore()
+const snapshotStore = useSnapshotStore()
+const { databaseId } = storeToRefs(mainStore)
+const { slides } = storeToRefs(slidesStore)
+const { screening } = storeToRefs(useScreenStore())
 
 
 // 视图模式:'editor', 'student', 'screen'
 // 视图模式:'editor', 'student', 'screen'
 // 支持通过URL参数直接访问学生模式
 // 支持通过URL参数直接访问学生模式
 const getInitialViewMode = () => {
 const getInitialViewMode = () => {
   // 检查URL参数
   // 检查URL参数
-  const urlParams = new URLSearchParams(window.location.search);
-  const modeFromUrl = urlParams.get("mode");
-  console.log(modeFromUrl);
-  if (modeFromUrl === "student") {
-    return "student";
+  const urlParams = new URLSearchParams(window.location.search)
+  const modeFromUrl = urlParams.get('mode')
+  console.log(modeFromUrl)
+  if (modeFromUrl === 'student') {
+    return 'student'
   }
   }
 
 
-  if (modeFromUrl === "editor2") {
-    return "editor2";
+  if (modeFromUrl === 'editor2') {
+    return 'editor2'
   }
   }
 
 
-  if (modeFromUrl === "editor3") {
-    return "editor3";
+  if (modeFromUrl === 'editor3') {
+    return 'editor3'
   }
   }
   // 检查localStorage
   // 检查localStorage
-  const modeFromStorage = localStorage.getItem("viewMode");
+  const modeFromStorage = localStorage.getItem('viewMode')
   if (modeFromStorage) {
   if (modeFromStorage) {
-    return modeFromStorage;
+    return modeFromStorage
   }
   }
 
 
   // 默认返回编辑模式
   // 默认返回编辑模式
-  return "editor";
-};
+  return 'editor'
+}
 
 
 // 获取URL参数中的courseid和type
 // 获取URL参数中的courseid和type
 const getUrlParams = () => {
 const getUrlParams = () => {
-  const urlParams = new URLSearchParams(window.location.search);
+  const urlParams = new URLSearchParams(window.location.search)
   return {
   return {
-    courseid: urlParams.get("courseid"),
-    userid: urlParams.get("userid"),
-    oid: urlParams.get("oid"),
-    org: urlParams.get("org"),
-    cid: urlParams.get("cid"),
-    type: urlParams.get("type"),
-  };
-};
+    courseid: urlParams.get('courseid'),
+    userid: urlParams.get('userid'),
+    oid: urlParams.get('oid'),
+    org: urlParams.get('org'),
+    cid: urlParams.get('cid'),
+    type: urlParams.get('type'),
+  }
+}
 
 
-const urlParams = getUrlParams();
+const urlParams = getUrlParams()
 
 
-const viewMode = ref(getInitialViewMode());
+const viewMode = ref(getInitialViewMode())
 
 
 // 全局切换视图模式的函数
 // 全局切换视图模式的函数
 const switchViewMode = (mode: string) => {
 const switchViewMode = (mode: string) => {
-  viewMode.value = mode;
-  localStorage.setItem("viewMode", mode);
+  viewMode.value = mode
+  localStorage.setItem('viewMode', mode)
 
 
   // 更新URL参数
   // 更新URL参数
-  const url = new URL(window.location.href);
-  if (mode === "student") {
-    url.searchParams.set("mode", "student");
-  } else {
-    url.searchParams.delete("mode");
+  const url = new URL(window.location.href)
+  if (mode === 'student') {
+    url.searchParams.set('mode', 'student')
+  }
+  else {
+    url.searchParams.delete('mode')
   }
   }
 
 
   // 使用 history.pushState 更新URL,不刷新页面
   // 使用 history.pushState 更新URL,不刷新页面
-  window.history.pushState({}, "", url.toString());
-};
+  window.history.pushState({}, '', url.toString())
+}
 
 
 // 使用provide提供切换函数,供子组件调用
 // 使用provide提供切换函数,供子组件调用
-provide("switchViewMode", switchViewMode);
+provide('switchViewMode', switchViewMode)
 
 
-if (import.meta.env.MODE !== "development") {
-  window.onbeforeunload = () => false;
+if (import.meta.env.MODE !== 'development') {
+  window.onbeforeunload = () => false
 }
 }
 
 
 onMounted(async () => {
 onMounted(async () => {
-  const slides = await api.getFileData("slides");
-  console.log(slides);
-  slidesStore.setSlides(slides);
+  const slides = await api.getFileData('slides')
+  console.log(slides)
+  slidesStore.setSlides(slides)
   // 初始化快照数据库
   // 初始化快照数据库
   // await deleteDiscardedDB()
   // await deleteDiscardedDB()
   // snapshotStore.initSnapshotDatabase()
   // snapshotStore.initSnapshotDatabase()
 
 
   // 监听视图模式切换事件
   // 监听视图模式切换事件
-  window.addEventListener("viewModeChanged", (event: any) => {
+  window.addEventListener('viewModeChanged', (event: any) => {
     if (event.detail) {
     if (event.detail) {
-      switchViewMode(event.detail);
+      switchViewMode(event.detail)
     }
     }
-  });
-});
+  })
+})
 
 
 // 应用注销时向 localStorage 中记录下本次 indexedDB 的数据库ID,用于之后清除数据库
 // 应用注销时向 localStorage 中记录下本次 indexedDB 的数据库ID,用于之后清除数据库
-window.addEventListener("beforeunload", () => {
-  const discardedDB = localStorage.getItem(LOCALSTORAGE_KEY_DISCARDED_DB);
-  const discardedDBList: string[] = discardedDB ? JSON.parse(discardedDB) : [];
+window.addEventListener('beforeunload', () => {
+  const discardedDB = localStorage.getItem(LOCALSTORAGE_KEY_DISCARDED_DB)
+  const discardedDBList: string[] = discardedDB ? JSON.parse(discardedDB) : []
 
 
-  discardedDBList.push(databaseId.value);
+  discardedDBList.push(databaseId.value)
 
 
-  const newDiscardedDB = JSON.stringify(discardedDBList);
-  localStorage.setItem(LOCALSTORAGE_KEY_DISCARDED_DB, newDiscardedDB);
-});
+  const newDiscardedDB = JSON.stringify(discardedDBList)
+  localStorage.setItem(LOCALSTORAGE_KEY_DISCARDED_DB, newDiscardedDB)
+})
 </script>
 </script>
 
 
 <style lang="scss">
 <style lang="scss">
@@ -219,4 +220,30 @@ window.addEventListener("beforeunload", () => {
   user-select: none;
   user-select: none;
   will-change: transform;
   will-change: transform;
 }
 }
+
+// 清空聊天记录确认弹窗样式
+.clear-confirm {
+  padding-top: 4px;
+
+  &__title {
+    font-size: 16px;
+    font-weight: 600;
+    margin-bottom: 12px;
+    color: #333;
+  }
+
+  &__content {
+    font-size: 14px;
+    color: #666;
+    line-height: 1.6;
+    margin-bottom: 20px;
+  }
+
+  &__footer {
+    display: flex;
+    justify-content: flex-end;
+    gap: 8px;
+  }
+}
+
 </style>
 </style>

+ 195 - 2
src/views/Editor/CanvasTool/index2.vue

@@ -1,6 +1,36 @@
 <template>
 <template>
   <div class="canvas-tool">
   <div class="canvas-tool">
     <div class="left-handler">
     <div class="left-handler">
+      <Popover trigger="click" v-model:value="toolVisible"
+        style="height: 100%;display: flex;align-items: center;" :offset="10" v-if="hasInteractiveTool">
+        <template #content>
+          <div class="popover-item" @click="editContent(45)" v-if="frametype != 45">
+            <svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <circle cx="12" cy="12" r="10" />
+              <path d="M12 16v-4m0-4h.01" />
+            </svg>
+            <span>{{ lang.ssChoiceQ }}</span>
+          </div>
+          <div class="popover-item" @click="editContent(15)" v-if="frametype != 15">
+            <svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" />
+            </svg>
+            <span>{{ lang.ssQandA }}</span>
+          </div>
+        </template>
+        <div class="handler-item">
+          <span class="svg-icon">
+            <svg v-if="frametype == 45" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <circle cx="12" cy="12" r="10" />
+              <path d="M12 16v-4m0-4h.01" />
+            </svg>
+            <svg v-if="frametype == 15" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" />
+            </svg>
+          </span>
+          <span>{{ iframeLabel }}</span>
+        </div>
+      </Popover>
       <Popover trigger="click" v-model:value="textTypeSelectVisible"
       <Popover trigger="click" v-model:value="textTypeSelectVisible"
         style="height: 100%;display: flex;align-items: center;" :offset="10" v-if="!isFrame">
         style="height: 100%;display: flex;align-items: center;" :offset="10" v-if="!isFrame">
         <template #content>
         <template #content>
@@ -73,6 +103,27 @@
       <LaTeXEditor @close="latexEditorVisible = false"
       <LaTeXEditor @close="latexEditorVisible = false"
         @update="data => { createLatexElement(data); latexEditorVisible = false }" />
         @update="data => { createLatexElement(data); latexEditorVisible = false }" />
     </Modal>
     </Modal>
+
+    <!-- 确认对话框 -->
+    <Modal
+      :visible="confirmDialogVisible"
+      :width="420"
+      :closeButton="true"
+      :closeOnClickMask="false"
+      :closeOnEsc="false"
+      @update:visible="val => confirmDialogVisible = val"
+    >
+      <div class="clear-confirm">
+        <div class="clear-confirm__title">{{ lang.ssConfirmOperation }}</div>
+        <div class="clear-confirm__content">
+          {{ lang.ssClearToolContent }}
+        </div>
+        <div class="clear-confirm__footer">
+          <Button type="default" @click="handleCancel">{{ lang.ssCancel }}</Button>
+          <Button type="primary" @click="handleConfirm">{{ lang.ssApply }}</Button>
+        </div>
+      </div>
+    </Modal>
   </div>
   </div>
 </template>
 </template>
 
 
@@ -101,6 +152,7 @@ import Modal from '@/components/Modal.vue'
 import Divider from '@/components/Divider.vue'
 import Divider from '@/components/Divider.vue'
 import Popover from '@/components/Popover.vue'
 import Popover from '@/components/Popover.vue'
 import PopoverMenuItem from '@/components/PopoverMenuItem.vue'
 import PopoverMenuItem from '@/components/PopoverMenuItem.vue'
+import Button from '@/components/Button.vue'
 
 
 const mainStore = useMainStore()
 const mainStore = useMainStore()
 const slidesStore = useSlidesStore()
 const slidesStore = useSlidesStore()
@@ -128,6 +180,29 @@ const hasInteractiveTool = computed(() => {
   return elements.some((el: any) => el.type === 'frame' && (el.toolType === 45 || el.toolType === 15))
   return elements.some((el: any) => el.type === 'frame' && (el.toolType === 45 || el.toolType === 15))
 })
 })
 
 
+const iframeLabel = computed(() => {
+  return getTypeLabel(frametype.value)
+})
+
+const frametype = computed(() => {
+  const elements = currentSlide.value?.elements || []
+  return hasInteractiveTool.value ? elements[0].type === 'frame' ? elements[0]?.toolType || 0 : 0 : 0
+})
+
+// 获取类型标签
+const getTypeLabel = (type: number) => {
+  const typeMap: Record<number, string> = {
+    45: lang.ssChoiceQ,
+    15: lang.ssQATest,
+    72: lang.ssAiApp,
+    73: lang.ssHPage,
+    74: lang.ssVideo,
+    75: lang.ssBiliVideo,
+    76: lang.ssCreative
+  }
+  return typeMap[type] || lang.ssUnknown
+}
+
 const isFrame = computed(() => {
 const isFrame = computed(() => {
   const elements = currentSlide.value?.elements || []
   const elements = currentSlide.value?.elements || []
   return elements.some((el: any) => el.type === 'frame')
   return elements.some((el: any) => el.type === 'frame')
@@ -180,7 +255,7 @@ const insertImageElement = async (files: FileList) => {
   if (!imageFile) return
   if (!imageFile) return
   const url = await uploadFileToS3(imageFile)
   const url = await uploadFileToS3(imageFile)
   console.log(url)
   console.log(url)
-  
+
   // getImageDataURL(imageFile).then(dataURL => createImageElement(dataURL))
   // getImageDataURL(imageFile).then(dataURL => createImageElement(dataURL))
   createImageElement(url)
   createImageElement(url)
   picturePoolVisible.value = false
   picturePoolVisible.value = false
@@ -232,6 +307,7 @@ const handleInsertLearningContent = () => {
 }
 }
 const latexEditorVisible = ref(false)
 const latexEditorVisible = ref(false)
 const textTypeSelectVisible = ref(false)
 const textTypeSelectVisible = ref(false)
+const toolVisible = ref(false)
 const shapeMenuVisible = ref(false)
 const shapeMenuVisible = ref(false)
 const moreVisible = ref(false)
 const moreVisible = ref(false)
 
 
@@ -282,9 +358,119 @@ const toggleSraechPanel = () => {
 const toggleNotesPanel = () => {
 const toggleNotesPanel = () => {
   mainStore.setNotesPanelState(!showNotesPanel.value)
   mainStore.setNotesPanelState(!showNotesPanel.value)
 }
 }
+
+interface ContentItem {
+  tool?: number
+  title?: string
+  url?: string
+  id?: string
+}
+
+const addContent = (data: ContentItem, type: number) => {
+  // contentList.value.push(data)
+  if (type === 2) {
+    const elements = currentSlide.value?.elements || []
+    const frameElement = elements.find((el: any) => el.type === 'frame' && (el.toolType === 45 || el.toolType === 15))
+    if (frameElement) {
+      slidesStore.updateElement({
+        id: frameElement.id,
+        props: { url: data.url }
+      })
+    }
+  }
+}
+
+Object.assign(window, { addContent })
+
+// 确认对话框状态
+const confirmDialogVisible = ref(false)
+const confirmDialogCallback = ref<(() => void) | null>(null)
+
+const showConfirmDialog = (callback: () => void) => {
+  confirmDialogCallback.value = callback
+  confirmDialogVisible.value = true
+}
+
+const handleConfirm = () => {
+  confirmDialogCallback.value?.()
+  confirmDialogVisible.value = false
+  confirmDialogCallback.value = null
+}
+
+const handleCancel = () => {
+  confirmDialogVisible.value = false
+  confirmDialogCallback.value = null
+}
+
+const editContent = (toolType: number) => {
+  showConfirmDialog(() => {
+    interface ParentWindowWithToolList extends Window {
+      toolBtn2?: (action: number, id: string, toolType: number) => void;
+    }
+    const parentWindow = window.parent as ParentWindowWithToolList
+    const elements = currentSlide.value?.elements || []
+    const frameElement = elements.find((el: any) => el.type === 'frame' && (el.toolType === 45 || el.toolType === 15))
+    parentWindow?.toolBtn2?.(0, frameElement?.id || '', toolType)
+  })
+}
+
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
+.confirm-dialog-content {
+  padding: 20px;
+  text-align: center;
+
+  h3 {
+    margin: 0 0 16px;
+    font-size: 18px;
+    font-weight: 600;
+    color: #333;
+  }
+
+  p {
+    margin: 0 0 24px;
+    font-size: 14px;
+    color: #666;
+    line-height: 1.5;
+  }
+
+  .confirm-dialog-buttons {
+    display: flex;
+    justify-content: center;
+    gap: 16px;
+
+    button {
+      padding: 8px 24px;
+      border-radius: 6px;
+      font-size: 14px;
+      font-weight: 500;
+      cursor: pointer;
+      transition: all 0.3s;
+
+      &.cancel-btn {
+        background: #f5f5f5;
+        color: #666;
+        border: 1px solid #e0e0e0;
+
+        &:hover {
+          background: #e8e8e8;
+        }
+      }
+
+      &.confirm-btn {
+        background: #FF9300;
+        color: white;
+        border: 1px solid #FF9300;
+
+        &:hover {
+          background: #e68a00;
+        }
+      }
+    }
+  }
+}
+
 .canvas-tool {
 .canvas-tool {
   position: relative;
   position: relative;
   border-bottom: 1px solid $borderColor;
   border-bottom: 1px solid $borderColor;
@@ -317,6 +503,11 @@ const toggleNotesPanel = () => {
     .icon {
     .icon {
       margin-right: 5px;
       margin-right: 5px;
     }
     }
+
+    .svg-icon {
+      margin-right: 5px;
+      display: flex;
+    }
   }
   }
 }
 }
 
 
@@ -385,6 +576,7 @@ const toggleNotesPanel = () => {
     width: 1em;
     width: 1em;
     height: 1em;
     height: 1em;
   }
   }
+
   &.center {
   &.center {
     text-align: center;
     text-align: center;
   }
   }
@@ -392,7 +584,8 @@ const toggleNotesPanel = () => {
   &:hover {
   &:hover {
     background-color: #f3f4f6;
     background-color: #f3f4f6;
   }
   }
-  & + .popover-menu-item {
+
+  &+.popover-menu-item {
     margin-top: 2px;
     margin-top: 2px;
   }
   }
 }
 }

+ 0 - 25
src/views/Student/components/DialoguePanel.vue

@@ -727,31 +727,6 @@ onMounted(() => {
   }
   }
 }
 }
 
 
-// 清空聊天记录确认弹窗样式
-.clear-confirm {
-  padding-top: 4px;
-
-  &__title {
-    font-size: 16px;
-    font-weight: 600;
-    margin-bottom: 12px;
-    color: #333;
-  }
-
-  &__content {
-    font-size: 14px;
-    color: #666;
-    line-height: 1.6;
-    margin-bottom: 20px;
-  }
-
-  &__footer {
-    display: flex;
-    justify-content: flex-end;
-    gap: 8px;
-  }
-}
-
 @keyframes spin {
 @keyframes spin {
   0% { transform: rotate(0deg); }
   0% { transform: rotate(0deg); }
   100% { transform: rotate(360deg); }
   100% { transform: rotate(360deg); }

+ 4 - 2
src/views/lang/cn.json

@@ -160,7 +160,7 @@
   "ssVideo": "视频",
   "ssVideo": "视频",
   "ssCreative": "创作空间",
   "ssCreative": "创作空间",
   "ssContentList": "内容列表",
   "ssContentList": "内容列表",
-  "ssChoiceQ": "选择",
+  "ssChoiceQ": "选择",
   "ssQandA": "问答",
   "ssQandA": "问答",
   "ssNoLearn": "暂无学习内容",
   "ssNoLearn": "暂无学习内容",
   "ssNeedUpload": "请先上传或创建学习内容",
   "ssNeedUpload": "请先上传或创建学习内容",
@@ -707,5 +707,7 @@
   "ssFileParseCancelled": "文件解析已取消",
   "ssFileParseCancelled": "文件解析已取消",
   "ssFileParseFailed": "文件解析失败",
   "ssFileParseFailed": "文件解析失败",
   "ssFileParseFailedRetry": "文件解析失败,请重试",
   "ssFileParseFailedRetry": "文件解析失败,请重试",
-  "ssParseCancelled": "解析已取消"
+  "ssParseCancelled": "解析已取消",
+  "ssConfirmOperation": "确认操作",
+  "ssClearToolContent": "该操作将清除当前工具的编辑内容,是否继续?"
 }
 }

+ 3 - 1
src/views/lang/en.json

@@ -707,5 +707,7 @@
   "ssFileParseCancelled": "File parsing cancelled",
   "ssFileParseCancelled": "File parsing cancelled",
   "ssFileParseFailed": "File parsing failed",
   "ssFileParseFailed": "File parsing failed",
   "ssFileParseFailedRetry": "File parsing failed, please retry",
   "ssFileParseFailedRetry": "File parsing failed, please retry",
-  "ssParseCancelled": "Parsing cancelled"
+  "ssParseCancelled": "Parsing cancelled",
+  "ssConfirmOperation": "Confirm Operation",
+  "ssClearToolContent": "This operation will clear the current tool's editing content. Continue?"
 }
 }

+ 4 - 2
src/views/lang/hk.json

@@ -318,7 +318,7 @@
   "ssIgnoreWebfont": "忽略在線字體:",
   "ssIgnoreWebfont": "忽略在線字體:",
   "ssIgnoreWebfontTip": "匯出時預設忽略在線字體。若在幻燈片中使用了在線字體且希望匯出後保留樣式,可關閉【忽略在線字體】選項,但會增加匯出用時。",
   "ssIgnoreWebfontTip": "匯出時預設忽略在線字體。若在幻燈片中使用了在線字體且希望匯出後保留樣式,可關閉【忽略在線字體】選項,但會增加匯出用時。",
   "ssExporting": "正在匯出...",
   "ssExporting": "正在匯出...",
-  "ssChoiceQ": "選擇",
+  "ssChoiceQ": "選擇",
   "ssEssayQ": "問答題",
   "ssEssayQ": "問答題",
   "ssAIApp": "AI應用",
   "ssAIApp": "AI應用",
   "ssH5Page": "H5頁面",
   "ssH5Page": "H5頁面",
@@ -707,5 +707,7 @@
   "ssFileParseCancelled": "文件解析已取消",
   "ssFileParseCancelled": "文件解析已取消",
   "ssFileParseFailed": "文件解析失敗",
   "ssFileParseFailed": "文件解析失敗",
   "ssFileParseFailedRetry": "文件解析失敗,請重試",
   "ssFileParseFailedRetry": "文件解析失敗,請重試",
-  "ssParseCancelled": "解析已取消"
+  "ssParseCancelled": "解析已取消",
+  "ssConfirmOperation": "確認操作",
+  "ssClearToolContent": "該操作將清除當前工具的編輯內容,是否繼續?"
 }
 }