Parcourir la source

feat: 新增作业删除功能及多语言支持

- 新增删除作业的API接口和服务方法
- 新增中英繁三种语言的删除作业相关文本
- 在学生作业列表中添加删除按钮(仅创建者可操作)
- 实现删除确认弹窗、删除成功/失败提示
- 作业删除后刷新本地数据并广播通知跟随模式下的其他客户端
lsc il y a 4 jours
Parent
commit
67fdf68ca7

+ 12 - 0
src/services/course.ts

@@ -264,6 +264,17 @@ export const addPPTOp2 = (params: any): Promise<any> => {
   return axios.post(`${API_URL}addPPTOp2`, [params])
 }
 
+/**
+ * 
+ * 删除课程作业
+ * @param any id 作业id
+ * @returns Promise<any>
+ */
+
+export const deleteCourseWork = (params: any): Promise<any> => {
+  return axios.post(`${API_URL}deleteCourseWork`, [params])
+}
+
 
 export default {
   getCourseDetail,
@@ -289,5 +300,6 @@ export default {
   selectPPTOp,
   addPPTOp,
   addPPTOp2,
+  deleteCourseWork,
 }
 

+ 145 - 34
src/views/Student/components/choiceQuestionDetailDialog.vue

@@ -252,10 +252,18 @@
             <div class="c_t15_c_i_top">
               <span>{{ item.name.charAt(0) }}</span>
               <div>{{ item.name }}</div>
-              <div class="cast_sreen_btn" @click.stop="castScreen(item)"
-                v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
-                <IconCastScreen />
-                {{ lang.ssCastScreen }}
+              <div class="c_item_actions">
+                <div class="cast_sreen_btn" @click.stop="castScreen(item)"
+                  v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
+                  <IconCastScreen />
+                  {{ lang.ssCastScreen }}
+                </div>
+                <IconDelete
+                  v-if="props.isCreator && props.roleType == 1"
+                  class="delete-work-btn"
+                  @click.stop="handleDeleteWork(item)"
+                  :title="lang.ssDeleteWork"
+                />
               </div>
             </div>
             <div class="c_t15_c_i_bottom">
@@ -368,10 +376,18 @@
             <div class="c_t15_c_i_top">
               <span>{{ item.name.charAt(0) }}</span>
               <div>{{ item.name }}</div>
-              <div class="cast_sreen_btn" @click.stop="castScreen(item)"
-                v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
-                <IconCastScreen />
-                {{ lang.ssCastScreen }}
+              <div class="c_item_actions">
+                <div class="cast_sreen_btn" @click.stop="castScreen(item)"
+                  v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
+                  <IconCastScreen />
+                  {{ lang.ssCastScreen }}
+                </div>
+                <IconDelete
+                  v-if="props.isCreator && props.roleType == 1"
+                  class="delete-work-btn"
+                  @click.stop="handleDeleteWork(item)"
+                  :title="lang.ssDeleteWork"
+                />
               </div>
             </div>
             <div class="c_t73_c_i_bottom" v-if="item.content && item.content.fileList.length > 0">
@@ -466,10 +482,18 @@
             <div class="c_t15_c_i_top">
               <span>{{ item.name.charAt(0) }}</span>
               <div>{{ item.name }}</div>
-              <div class="cast_sreen_btn" @click.stop="castScreen(item)"
-                v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
-                <IconCastScreen />
-                {{ lang.ssCastScreen }}
+              <div class="c_item_actions">
+                <div class="cast_sreen_btn" @click.stop="castScreen(item)"
+                  v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
+                  <IconCastScreen />
+                  {{ lang.ssCastScreen }}
+                </div>
+                <IconDelete
+                  v-if="props.isCreator && props.roleType == 1"
+                  class="delete-work-btn"
+                  @click.stop="handleDeleteWork(item)"
+                  :title="lang.ssDeleteWork"
+                />
               </div>
             </div>
             <div class="bottom_btn" v-if="likeValue">
@@ -568,10 +592,18 @@
             <div class="c_t72_c_i_top">
               <span>{{ item.name.charAt(0) }}</span>
               <div>{{ item.name }}</div>
-              <div class="cast_sreen_btn" @click.stop="castScreen(item)"
-                v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
-                <IconCastScreen />
-                {{ lang.ssCastScreen }}
+              <div class="c_item_actions">
+                <div class="cast_sreen_btn" @click.stop="castScreen(item)"
+                  v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
+                  <IconCastScreen />
+                  {{ lang.ssCastScreen }}
+                </div>
+                <IconDelete
+                  v-if="props.isCreator && props.roleType == 1"
+                  class="delete-work-btn"
+                  @click.stop="handleDeleteWork(item)"
+                  :title="lang.ssDeleteWork"
+                />
               </div>
             </div>
             <div class="bottom_btn" v-if="likeValue">
@@ -699,10 +731,18 @@
             <div class="c_t73_c_i_top">
               <span>{{ item.name.charAt(0) }}</span>
               <div>{{ item.name }}</div>
-              <div class="cast_sreen_btn" @click.stop="castScreen(item)"
-                v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
-                <IconCastScreen />
-                {{ lang.ssCastScreen }}
+              <div class="c_item_actions">
+                <div class="cast_sreen_btn" @click.stop="castScreen(item)"
+                  v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
+                  <IconCastScreen />
+                  {{ lang.ssCastScreen }}
+                </div>
+                <IconDelete
+                  v-if="props.isCreator && props.roleType == 1"
+                  class="delete-work-btn"
+                  @click.stop="handleDeleteWork(item)"
+                  :title="lang.ssDeleteWork"
+                />
               </div>
             </div>
             <div class="c_t73_c_i_bottom">
@@ -756,10 +796,18 @@
             <div class="c_t72_c_i_top">
               <span>{{ item.name.charAt(0) }}</span>
               <div>{{ item.name }}</div>
-              <div class="cast_sreen_btn" @click.stop="castScreen(item)"
-                v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
-                <IconCastScreen />
-                {{ lang.ssCastScreen }}
+              <div class="c_item_actions">
+                <div class="cast_sreen_btn" @click.stop="castScreen(item)"
+                  v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
+                  <IconCastScreen />
+                  {{ lang.ssCastScreen }}
+                </div>
+                <IconDelete
+                  v-if="props.isCreator && props.roleType == 1"
+                  class="delete-work-btn"
+                  @click.stop="handleDeleteWork(item)"
+                  :title="lang.ssDeleteWork"
+                />
               </div>
             </div>
             <div class="bottom_btn" v-if="likeValue">
@@ -906,10 +954,18 @@
             <div class="c_t72_c_i_top">
               <span>{{ item.name.charAt(0) }}</span>
               <div>{{ item.name }}</div>
-              <div class="cast_sreen_btn" @click.stop="castScreen(item)"
-                v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
-                <IconCastScreen />
-                {{ lang.ssCastScreen }}
+              <div class="c_item_actions">
+                <div class="cast_sreen_btn" @click.stop="castScreen(item)"
+                  v-if="props.isCreator && props.roleType == 1 && isFollowModeActive && canValue">
+                  <IconCastScreen />
+                  {{ lang.ssCastScreen }}
+                </div>
+                <IconDelete
+                  v-if="props.isCreator && props.roleType == 1"
+                  class="delete-work-btn"
+                  @click.stop="handleDeleteWork(item)"
+                  :title="lang.ssDeleteWork"
+                />
               </div>
             </div>
             <div class="bottom_btn" v-if="likeValue">
@@ -1058,7 +1114,9 @@ import echartsDialog from './echartsDialog.vue'
 import { chat_stream, chat_no_stream } from '@/tools/aiChat'
 import axios from '@/services/config'
 import Switch from '@/components/Switch.vue'
-import { likeWork, addPPTOp2 } from '@/services/course'
+import { likeWork, addPPTOp2, deleteCourseWork } from '@/services/course'
+import message from '@/utils/message'
+import { showConfirmDialog } from '@/utils/confirmDialog'
 
 const props = defineProps<{
   visible: number[];
@@ -1087,6 +1145,7 @@ const emit = defineEmits<{
   (e: 'setIsResultArray', v: boolean, key: string): void;
   (e: 'successLike'): void;
   (e: 'sendMessage', v: any): void;
+  (e: 'regetWorks'): void;
 }>()
 
 const visible = computed({
@@ -1488,6 +1547,29 @@ const exitCastScreen = () => {
   lookWork('')
 }
 
+// 删除作业(仅课程创建人 + 老师端可操作)
+const handleDeleteWork = async (item: any) => {
+  if (!item?.id) return
+  if (!props.isCreator || props.roleType != 1) return
+  const confirmed = await showConfirmDialog({
+    title: lang.ssDeleteConfirmTitle,
+    content: (lang.ssDeleteConfirmContent || '确定删除 {name} 的作业吗?此操作不可撤销。').replace('{name}', item.name || ''),
+    confirmText: lang.ssDeleteConfirm || '确定删除',
+    cancelText: lang.ssCancel || '取消',
+  })
+  if (!confirmed) return
+  try {
+    await deleteCourseWork({ id: item.id })
+    message.success(lang.ssDeleteSuccess || '删除成功')
+    // 如果 selectUserDialog 正开着(查看提交状态场景),关闭它让用户下次打开看到最新数据
+    if (selectUserDialogRef.value) selectUserDialogRef.value.close()
+    emit('regetWorks')
+  }
+  catch (e: any) {
+    message.error(lang.ssDeleteFail || '删除失败:' + (e?.message || e))
+  }
+}
+
 // 暴露给父级(Student/index.vue)用 ref 调用
 defineExpose({ castScreen, exitCastScreen })
 
@@ -1892,10 +1974,14 @@ watch(
 
 // 查看未提交学生
 const viewUnsubmittedStudents = () => {
-  selectUserDialogRef.value.open(lang.ssSubmitStatus, { user: props.unsubmittedStudents.map((item: any) => item.name), notFiledUser: props.unsubmittedStudents.map((item: any) => item.name), isSubmitUser: props.workArray.map((item: any) => item.name) })
-  // if (props.unsubmittedStudents.length > 0) {
-  // unsubmittedStudentsDialogRef.value.open(props.unsubmittedStudents)
-  // }
+  selectUserDialogRef.value.open(lang.ssSubmitStatus, {
+    user: props.unsubmittedStudents.map((item: any) => item.name),
+    notFiledUser: props.unsubmittedStudents.map((item: any) => item.name),
+    // 传完整对象(带 id)用于删除作业
+    isSubmitUser: props.workArray.map((item: any) => ({ id: item.id, name: item.name })),
+    canDeleteWork: props.isCreator && props.roleType == 1,
+    onDeleteWork: handleDeleteWork,
+  })
 }
 
 // ai生成选择题分析 选择题
@@ -3748,7 +3834,7 @@ onUnmounted(() => {
         width: 100%;
         height: auto;
         display: grid;
-        grid-template-columns: repeat(4, 1fr);
+        grid-template-columns: repeat(3, 1fr);
         gap: 20px;
         margin-top: 40px;
 
@@ -4777,4 +4863,29 @@ onUnmounted(() => {
     background: #2563eb;
   }
 }
+
+// 右侧操作区:投屏按钮 + 删除按钮统一管理
+.c_item_actions {
+  margin-left: auto;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  flex-shrink: 0;
+
+  // 覆盖全局 .cast_sreen_btn 的 margin-left: auto,避免在 flex 容器里把自己再推远
+  .cast_sreen_btn {
+    margin-left: 0;
+  }
+
+  .delete-work-btn {
+    font-size: 18px;
+    color: #ff4d4f;
+    opacity: 0.4;
+    cursor: pointer;
+    transition: opacity 0.2s;
+    padding: 0 4px;
+    flex-shrink: 0;
+    &:hover { opacity: 1; }
+  }
+}
 </style>

+ 38 - 6
src/views/Student/components/selectUserDialog.vue

@@ -9,8 +9,14 @@
         {{ item }}
       </div>
       <span v-if="data.isSubmitUser">{{ lang.ssSubStu2 }}:</span>
-      <div v-for="(item, index) in data.isSubmitUser" :key="index" v-if="data.isSubmitUser">
-        {{ item }}
+      <div v-for="(item, index) in data.isSubmitUser" :key="index" v-if="data.isSubmitUser" class="user-item">
+        <span>{{ typeof item === 'string' ? item : item.name }}</span>
+        <IconDelete
+          v-if="data.canDeleteWork && typeof item === 'object' && item.id"
+          class="user-item-delete"
+          @click.stop="handleDeleteWork(item)"
+          :title="lang.ssDeleteWork"
+        />
       </div>
       <div class="line" v-if="data.isSubmitUser && data.isSubmitUser"></div>
       <span v-if="data.notFiledUser">{{ lang.ssUnsubStu2 }}:</span>
@@ -28,14 +34,25 @@ import { lang } from '@/main'
 const show = ref(false)
 
 const data = ref({})
-const title = ref("")
+const title = ref('')
+// 删除作业回调(不能被 JSON.stringify 丢失)
+const onDeleteWorkRef = ref(null)
 
 // 打开
-const open = (tit,value) => {
+const open = (tit, value) => {
   init()
   show.value = true
   title.value = tit
-  data.value = JSON.parse(JSON.stringify(value))
+  // 分离函数和普通数据:JSON 不支持函数,要单独存
+  const { onDeleteWork, ...rest } = value || {}
+  onDeleteWorkRef.value = typeof onDeleteWork === 'function' ? onDeleteWork : null
+  data.value = JSON.parse(JSON.stringify(rest))
+  console.log('selectUseer', value)
+}
+
+// 调用删除回调(让父组件处理真正的删除)
+const handleDeleteWork = (item) => {
+  if (onDeleteWorkRef.value) onDeleteWorkRef.value(item)
 }
 
 // 关闭
@@ -45,8 +62,9 @@ const close = () => {
 }
 
 const init = () => {
-  title.value = ""
+  title.value = ''
   data.value = {}
+  onDeleteWorkRef.value = null
 }
 
 defineExpose({
@@ -109,6 +127,20 @@ defineExpose({
     font-weight: 500;
     font-size: 1rem;
   }
+  &>.user-item{
+    display: inline-flex;
+    align-items: center;
+    gap: 6px;
+  }
+  &>.user-item-delete{
+    font-size: 14px;
+    color: #ff4d4f;
+    opacity: 0.4;
+    cursor: pointer;
+    transition: opacity 0.2s;
+    flex-shrink: 0;
+    &:hover { opacity: 1; }
+  }
   &>span{
     width: 100%;
     display: block;

+ 23 - 1
src/views/Student/index.vue

@@ -106,7 +106,7 @@
           <ScreenSlideList :style="{ width: isFullscreen ? '100%' : slideWidth2 * canvasScale + 'px', height: isFullscreen ? '100%' : slideHeight2 * canvasScale + 'px', margin: '0 auto' }" :slideWidth="isFullscreen ? slideWidth * canvasScale : slideWidth2 * canvasScale" :slideHeight="isFullscreen ? slideHeight * canvasScale : slideHeight2 * canvasScale"
             :animationIndex="0" :turnSlideToId="() => { }" :manualExitFullscreen="() => { }"  :slideIndex="slideIndex" v-show="!choiceQuestionDetailDialogOpenList.includes(slideIndex)"/>
 
-          <choiceQuestionDetailDialog ref="choiceQuestionDetailDialogRef" v-if="choiceQuestionDetailDialogOpenList.includes(slideIndex) && !currentSlideHasEnglishSpeaking" :roleType="props.type" :cid="props.cid" :courseid="props.courseid" :workId="workId" :workUrl="workUrl" :userId="props.userid" :courseDetail="courseDetail" :workArray="workArray" @changeWorkIndex="changeWorkIndex" v-model:visible="choiceQuestionDetailDialogOpenList" :unsubmittedStudents="unsubmittedStudents" :showData="answerTheResultRef" :slideIndex="slideIndex" :workIndex="0" :style="{ width: isFullscreen ? '100%' : slideWidth2 * canvasScale + 'px', height: isFullscreen ? '100%' : slideHeight2 * canvasScale + 'px', margin: '0 auto' }" :slideWidth="isFullscreen ? slideWidth * canvasScale : slideWidth2 * canvasScale" :slideHeight="isFullscreen ? slideHeight * canvasScale : slideHeight2 * canvasScale" :resultArray="currentIsResultArray" @setIsResultArray="setIsResultArray2" :isCreator="isCreator" @successLike="successLike" @sendMessage="sendMessage" :isFollowModeActive="isFollowModeActive"/>
+          <choiceQuestionDetailDialog ref="choiceQuestionDetailDialogRef" v-if="choiceQuestionDetailDialogOpenList.includes(slideIndex) && !currentSlideHasEnglishSpeaking" :roleType="props.type" :cid="props.cid" :courseid="props.courseid" :workId="workId" :workUrl="workUrl" :userId="props.userid" :courseDetail="courseDetail" :workArray="workArray" @changeWorkIndex="changeWorkIndex" v-model:visible="choiceQuestionDetailDialogOpenList" :unsubmittedStudents="unsubmittedStudents" :showData="answerTheResultRef" :slideIndex="slideIndex" :workIndex="0" :style="{ width: isFullscreen ? '100%' : slideWidth2 * canvasScale + 'px', height: isFullscreen ? '100%' : slideHeight2 * canvasScale + 'px', margin: '0 auto' }" :slideWidth="isFullscreen ? slideWidth * canvasScale : slideWidth2 * canvasScale" :slideHeight="isFullscreen ? slideHeight * canvasScale : slideHeight2 * canvasScale" :resultArray="currentIsResultArray" @setIsResultArray="setIsResultArray2" :isCreator="isCreator" @successLike="successLike" @sendMessage="sendMessage" :isFollowModeActive="isFollowModeActive" @regetWorks="handleWorkDeleted"/>
           <SpeakingClassPanel
             v-else-if="choiceQuestionDetailDialogOpenList.includes(slideIndex) && currentSlideToolType === TOPIC_DISCUSSION_TOOL_TYPE"
             ref="speakingPanelRef"
@@ -3113,6 +3113,18 @@ const getWork = async (isUpdate = false) => {
   }
 }
 
+// 作业删除处理:刷新 + 广播(跟随模式)
+const handleWorkDeleted = () => {
+  // 1. 先刷新自己的作业列表
+  getWork(true)
+  // 2. 广播给所有客户端(跟随模式下学生会收到)
+  sendMessage({
+    type: 'work_deleted',
+    courseid: props.courseid,
+    slideIndex: slideIndex.value,
+  })
+}
+
 const selectWorksStudent = async () => {
   studentLoading.value = true
   try {
@@ -3755,6 +3767,16 @@ const getMessages = (msgObj: any) => {
       }
     }, 1000)
   }
+
+  // 处理作业删除消息 - 跟随模式下学生端收到后刷新作业列表
+  if (props.type == '2' && msgObj.type === 'work_deleted' && msgObj.courseid === props.courseid && isFollowModeActive.value) {
+    console.log('收到作业删除消息,重新获取作业数据')
+    setTimeout(() => {
+      if (currentSlideHasIframe.value) {
+        getWork(true)
+      }
+    }, 500)
+  }
   
   // 处理点赞消息 - 当有人点赞时,重新获取点赞数据
   if (msgObj.type === 'like_updated' && msgObj.courseid === props.courseid) {

+ 21 - 1
src/views/Student/index2.vue

@@ -106,7 +106,7 @@
           <ScreenSlideList :style="{ width: isFullscreen ? '100%' : slideWidth2 * canvasScale + 'px', height: isFullscreen ? '100%' : slideHeight2 * canvasScale + 'px', margin: '0 auto' }" :slideWidth="isFullscreen ? slideWidth * canvasScale : slideWidth2 * canvasScale" :slideHeight="isFullscreen ? slideHeight * canvasScale : slideHeight2 * canvasScale"
             :animationIndex="0" :turnSlideToId="() => { }" :manualExitFullscreen="() => { }"  :slideIndex="slideIndex" v-show="!choiceQuestionDetailDialogOpenList.includes(slideIndex)"/>
 
-          <choiceQuestionDetailDialog ref="choiceQuestionDetailDialogRef" v-if="choiceQuestionDetailDialogOpenList.includes(slideIndex) && !currentSlideHasEnglishSpeaking" :roleType="props.type" :cid="props.cid" :courseid="props.courseid" :workId="workId" :workUrl="workUrl" :userId="props.userid" :courseDetail="courseDetail" :workArray="workArray" @changeWorkIndex="changeWorkIndex" v-model:visible="choiceQuestionDetailDialogOpenList" :unsubmittedStudents="unsubmittedStudents" :showData="answerTheResultRef" :slideIndex="slideIndex" :workIndex="0" :style="{ width: isFullscreen ? '100%' : slideWidth2 * canvasScale + 'px', height: isFullscreen ? '100%' : slideHeight2 * canvasScale + 'px', margin: '0 auto' }" :slideWidth="isFullscreen ? slideWidth * canvasScale : slideWidth2 * canvasScale" :slideHeight="isFullscreen ? slideHeight * canvasScale : slideHeight2 * canvasScale" :resultArray="currentIsResultArray" @setIsResultArray="setIsResultArray2" :isCreator="isCreator" @successLike="successLike" @sendMessage="sendMessage" :isFollowModeActive="isFollowModeActive"/>
+          <choiceQuestionDetailDialog ref="choiceQuestionDetailDialogRef" v-if="choiceQuestionDetailDialogOpenList.includes(slideIndex) && !currentSlideHasEnglishSpeaking" :roleType="props.type" :cid="props.cid" :courseid="props.courseid" :workId="workId" :workUrl="workUrl" :userId="props.userid" :courseDetail="courseDetail" :workArray="workArray" @changeWorkIndex="changeWorkIndex" v-model:visible="choiceQuestionDetailDialogOpenList" :unsubmittedStudents="unsubmittedStudents" :showData="answerTheResultRef" :slideIndex="slideIndex" :workIndex="0" :style="{ width: isFullscreen ? '100%' : slideWidth2 * canvasScale + 'px', height: isFullscreen ? '100%' : slideHeight2 * canvasScale + 'px', margin: '0 auto' }" :slideWidth="isFullscreen ? slideWidth * canvasScale : slideWidth2 * canvasScale" :slideHeight="isFullscreen ? slideHeight * canvasScale : slideHeight2 * canvasScale" :resultArray="currentIsResultArray" @setIsResultArray="setIsResultArray2" :isCreator="isCreator" @successLike="successLike" @sendMessage="sendMessage" :isFollowModeActive="isFollowModeActive" @regetWorks="handleWorkDeleted"/>
           <SpeakingClassPanel
             v-else-if="choiceQuestionDetailDialogOpenList.includes(slideIndex) && currentSlideToolType === TOPIC_DISCUSSION_TOOL_TYPE"
             ref="speakingPanelRef"
@@ -2973,6 +2973,16 @@ const getWork = async (isUpdate = false) => {
   }
 }
 
+// 作业删除处理:刷新 + 广播(跟随模式)
+const handleWorkDeleted = () => {
+  getWork(true)
+  sendMessage({
+    type: 'work_deleted',
+    courseid: props.courseid,
+    slideIndex: slideIndex.value,
+  })
+}
+
 const selectWorksStudent = async () => {
   studentLoading.value = true
   try {
@@ -3605,6 +3615,16 @@ const getMessages = (msgObj: any) => {
       }
     }, 1000)
   }
+
+  // 处理作业删除消息 - 跟随模式下学生端收到后刷新作业列表
+  if (props.type == '2' && msgObj.type === 'work_deleted' && msgObj.courseid === props.courseid && isFollowModeActive.value) {
+    console.log('收到作业删除消息,重新获取作业数据')
+    setTimeout(() => {
+      if (currentSlideHasIframe.value) {
+        getWork(true)
+      }
+    }, 500)
+  }
   
   // 处理点赞消息 - 当有人点赞时,重新获取点赞数据
   if (msgObj.type === 'like_updated' && msgObj.courseid === props.courseid) {

+ 6 - 0
src/views/lang/cn.json

@@ -81,6 +81,12 @@
   "ssDelete": "删除",
   "ssReplyInput": "输入回复内容",
   "ssCancel": "取消",
+  "ssDeleteWork": "删除作业",
+  "ssDeleteConfirmTitle": "删除确认",
+  "ssDeleteConfirmContent": "确定删除 {name} 的作业吗?此操作不可撤销。",
+  "ssDeleteConfirm": "确定删除",
+  "ssDeleteSuccess": "删除成功",
+  "ssDeleteFail": "删除失败",
   "ssNoNotes": "本页暂无批注",
   "ssSelEl": "选中元素",
   "ssCurSlide": "当前页幻灯片",

+ 6 - 0
src/views/lang/en.json

@@ -81,6 +81,12 @@
   "ssDelete": "Delete",
   "ssReplyInput": "Enter reply",
   "ssCancel": "Cancel",
+  "ssDeleteWork": "Delete work",
+  "ssDeleteConfirmTitle": "Delete Confirmation",
+  "ssDeleteConfirmContent": "Are you sure you want to delete {name}'s work? This cannot be undone.",
+  "ssDeleteConfirm": "Delete",
+  "ssDeleteSuccess": "Deleted successfully",
+  "ssDeleteFail": "Delete failed",
   "ssNoNotes": "No notes for this slide",
   "ssSelEl": "selected element",
   "ssCurSlide": "current slide",

+ 6 - 0
src/views/lang/hk.json

@@ -81,6 +81,12 @@
   "ssDelete": "刪除",
   "ssReplyInput": "輸入回覆內容",
   "ssCancel": "取消",
+  "ssDeleteWork": "刪除作業",
+  "ssDeleteConfirmTitle": "刪除確認",
+  "ssDeleteConfirmContent": "確定刪除 {name} 的作業嗎?此操作不可撤銷。",
+  "ssDeleteConfirm": "確定刪除",
+  "ssDeleteSuccess": "刪除成功",
+  "ssDeleteFail": "刪除失敗",
   "ssNoNotes": "本頁暫無批註",
   "ssSelEl": "選中元素",
   "ssCurSlide": "當前頁投影片",