|
@@ -252,10 +252,18 @@
|
|
|
<div class="c_t15_c_i_top">
|
|
<div class="c_t15_c_i_top">
|
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
|
<div>{{ item.name }}</div>
|
|
<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>
|
|
</div>
|
|
|
<div class="c_t15_c_i_bottom">
|
|
<div class="c_t15_c_i_bottom">
|
|
@@ -368,10 +376,18 @@
|
|
|
<div class="c_t15_c_i_top">
|
|
<div class="c_t15_c_i_top">
|
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
|
<div>{{ item.name }}</div>
|
|
<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>
|
|
</div>
|
|
|
<div class="c_t73_c_i_bottom" v-if="item.content && item.content.fileList.length > 0">
|
|
<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">
|
|
<div class="c_t15_c_i_top">
|
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
|
<div>{{ item.name }}</div>
|
|
<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>
|
|
</div>
|
|
|
<div class="bottom_btn" v-if="likeValue">
|
|
<div class="bottom_btn" v-if="likeValue">
|
|
@@ -568,10 +592,18 @@
|
|
|
<div class="c_t72_c_i_top">
|
|
<div class="c_t72_c_i_top">
|
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
|
<div>{{ item.name }}</div>
|
|
<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>
|
|
</div>
|
|
|
<div class="bottom_btn" v-if="likeValue">
|
|
<div class="bottom_btn" v-if="likeValue">
|
|
@@ -699,10 +731,18 @@
|
|
|
<div class="c_t73_c_i_top">
|
|
<div class="c_t73_c_i_top">
|
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
|
<div>{{ item.name }}</div>
|
|
<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>
|
|
</div>
|
|
|
<div class="c_t73_c_i_bottom">
|
|
<div class="c_t73_c_i_bottom">
|
|
@@ -756,10 +796,18 @@
|
|
|
<div class="c_t72_c_i_top">
|
|
<div class="c_t72_c_i_top">
|
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
|
<div>{{ item.name }}</div>
|
|
<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>
|
|
</div>
|
|
|
<div class="bottom_btn" v-if="likeValue">
|
|
<div class="bottom_btn" v-if="likeValue">
|
|
@@ -906,10 +954,18 @@
|
|
|
<div class="c_t72_c_i_top">
|
|
<div class="c_t72_c_i_top">
|
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
<span>{{ item.name.charAt(0) }}</span>
|
|
|
<div>{{ item.name }}</div>
|
|
<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>
|
|
</div>
|
|
|
<div class="bottom_btn" v-if="likeValue">
|
|
<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 { chat_stream, chat_no_stream } from '@/tools/aiChat'
|
|
|
import axios from '@/services/config'
|
|
import axios from '@/services/config'
|
|
|
import Switch from '@/components/Switch.vue'
|
|
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<{
|
|
const props = defineProps<{
|
|
|
visible: number[];
|
|
visible: number[];
|
|
@@ -1087,6 +1145,7 @@ const emit = defineEmits<{
|
|
|
(e: 'setIsResultArray', v: boolean, key: string): void;
|
|
(e: 'setIsResultArray', v: boolean, key: string): void;
|
|
|
(e: 'successLike'): void;
|
|
(e: 'successLike'): void;
|
|
|
(e: 'sendMessage', v: any): void;
|
|
(e: 'sendMessage', v: any): void;
|
|
|
|
|
+ (e: 'regetWorks'): void;
|
|
|
}>()
|
|
}>()
|
|
|
|
|
|
|
|
const visible = computed({
|
|
const visible = computed({
|
|
@@ -1488,6 +1547,29 @@ const exitCastScreen = () => {
|
|
|
lookWork('')
|
|
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 调用
|
|
// 暴露给父级(Student/index.vue)用 ref 调用
|
|
|
defineExpose({ castScreen, exitCastScreen })
|
|
defineExpose({ castScreen, exitCastScreen })
|
|
|
|
|
|
|
@@ -1892,10 +1974,14 @@ watch(
|
|
|
|
|
|
|
|
// 查看未提交学生
|
|
// 查看未提交学生
|
|
|
const viewUnsubmittedStudents = () => {
|
|
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生成选择题分析 选择题
|
|
// ai生成选择题分析 选择题
|
|
@@ -3748,7 +3834,7 @@ onUnmounted(() => {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
height: auto;
|
|
height: auto;
|
|
|
display: grid;
|
|
display: grid;
|
|
|
- grid-template-columns: repeat(4, 1fr);
|
|
|
|
|
|
|
+ grid-template-columns: repeat(3, 1fr);
|
|
|
gap: 20px;
|
|
gap: 20px;
|
|
|
margin-top: 40px;
|
|
margin-top: 40px;
|
|
|
|
|
|
|
@@ -4777,4 +4863,29 @@ onUnmounted(() => {
|
|
|
background: #2563eb;
|
|
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>
|
|
</style>
|