Compare commits

..

56 Commits

Author SHA1 Message Date
赵依梦
223e3abe43 教师端1期需求 2025-12-07 11:32:25 +08:00
赵依梦
e7581efddf Merge branch '251114-feature-course-online' of https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/learning-system-portal into 251114-feature-course-online
# Conflicts:
#	src/api/modules/course.js
#	src/data/pages.js
2025-12-07 11:17:47 +08:00
赵依梦
57b233601d 教师端1期需求 2025-12-07 11:03:44 +08:00
huweihang
e53da5d324 feat:新增用户检索功能,更新课程管理页面,添加课程置顶排序组件,优化课程管理列表,支持新课程管理视图。 2025-12-05 17:40:09 +08:00
陈昱达
e6b319bce3 Merge remote-tracking branch 'origin/master' into 251114-feature-course-online
# Conflicts:
#	src/views/portal/course/Index.vue
2025-11-18 10:09:54 +08:00
joshen
bf20eced9b Merge branch 'master' into ebiz-uat-2025-11-06 2025-11-17 18:58:49 +08:00
670788339
8f2da1c736 取消processStatus setup写法 2025-11-17 18:25:29 +08:00
赵依梦
c11fb55ce3 课程列表侧边栏修改 课程详情评分修改 课程详情章节目录修改 2025-11-17 16:11:14 +08:00
670788339
322172edec Merge branch 'master-20251023-tag' into merge-20251113-tag
# Conflicts:
#	src/components/Course/courseTag.vue
2025-11-17 13:28:18 +08:00
670788339
c801dc8a3d 精品课导航样式 2025-11-14 10:32:42 +08:00
670788339
838e704ab0 精品课导航样式 2025-11-14 08:58:42 +08:00
670788339
d3e891e5cc 精品课导航样式 2025-11-14 08:45:27 +08:00
670788339
40ac85f1fe 在线标签提示改版 2025-11-13 18:43:33 +08:00
670788339
6ee8eaca00 课程库样式调整 2025-11-13 14:29:23 +08:00
670788339
d78cc1f97c 标签选择后直接关闭下拉框 2025-11-13 09:00:48 +08:00
670788339
2576174e95 当输入文字与标签匹配时 选择下拉框匹配标签 手动输入文字未直接消失 2025-11-13 08:51:51 +08:00
670788339
7316215809 当输入文字与标签匹配时 选择下拉框匹配标签 手动输入文字未直接消失 2025-11-13 08:47:31 +08:00
670788339
c5e794ef45 Merge branch 'master-20251023-tag' into merge-20251113-tag 2025-11-12 17:16:17 +08:00
670788339
720cff1d1e 提示修改 2025-11-12 14:54:24 +08:00
hz
f3cc59d313 feat: 修复 setup 异常 2025-11-12 12:41:34 +08:00
670788339
dc57becb0d 精品课点击全部未显示选中 2025-11-12 11:20:11 +08:00
670788339
a94d101853 还原 2025-11-12 10:55:35 +08:00
670788339
426ed75bc3 精品课全部切换 2025-11-12 10:51:14 +08:00
670788339
7e8b807825 标签提示样式调整 2025-11-12 10:19:26 +08:00
670788339
bf13c953be 标签提示 2025-11-12 09:17:39 +08:00
670788339
8d07122420 标签提示 2025-11-12 09:05:16 +08:00
670788339
471a790010 课程详情页 标签与x人学习 留点间距 2025-11-11 18:13:01 +08:00
670788339
d39e1e98ef 提示文案更改 2025-11-11 18:06:29 +08:00
670788339
a82a65da8e 课程库样式调整 2025-11-11 16:58:59 +08:00
670788339
2070466786 在线课标签样式 2025-11-11 16:38:51 +08:00
670788339
57d9f9b483 课程库标签高亮显示 2025-11-11 15:42:36 +08:00
670788339
1710e34f89 全部选中样式 2025-11-11 14:26:57 +08:00
670788339
e292a57b20 课程详情页标签样式 2025-11-11 10:44:24 +08:00
670788339
88c83af460 1111修改课程库内容导航底色样式 2025-11-11 09:10:04 +08:00
670788339
a78bac9368 1110修改样式 2025-11-10 19:26:10 +08:00
670788339
f121a2aaf9 1110修改样式 2025-11-10 19:01:19 +08:00
670788339
8228b33cb0 提示 2025-11-10 18:49:54 +08:00
670788339
702255d9d0 精品课标签导航调整样式 2025-11-10 10:29:56 +08:00
670788339
df3e246d25 精品课标签导航取消加粗 2025-11-10 10:24:02 +08:00
670788339
1d20f11861 精品类型加标签 2025-11-10 08:54:17 +08:00
670788339
d5ec4c1833 开发 2025-11-09 16:25:03 +08:00
670788339
89a9be76d4 开发 2025-11-09 15:25:13 +08:00
670788339
73026b0ab5 在线标签无数据显示调整 2025-11-08 14:46:53 +08:00
670788339
9b11cc3f92 课程详情页标签调整 2025-11-08 11:42:43 +08:00
670788339
372a7c22ed 搜索条件取消红色标记 2025-11-08 09:29:40 +08:00
670788339
2678d22302 修改导航区域结构 2025-11-08 09:06:44 +08:00
hz
914b80c374 Merge branch '20250922-cyd' into ebiz-uat-2025-11-06
# Conflicts:
#	src/views/portal/case/Index.vue
#	src/views/portal/case/components/messages.vue
#	src/views/portal/case/components/sendMessage.vue
2025-11-06 09:45:06 +08:00
陈昱达
5d81f72f5f feat(ai-call):优化AI对话组件初始化逻辑
- 添加组件准备就绪状态标识
- 确保组件挂载完成后再执行自动滚动
-重置对话时正确处理组件状态
- 避免未初始化完成时的滚动操作
2025-11-06 17:20:26 +08:00
陈昱达
c9c34501ce feat(ai-call): 实现对话框尺寸和位置状态持久化
- 添加对话框尺寸状态保存与恢复功能
- 支持通过 sessionStorage 存储对话框宽高及坐标
-优化欢迎消息区域高度计算逻辑- 移除旧的状态处理机制,采用事件驱动方式- 确保在无保存状态时使用默认尺寸配置
2025-11-06 16:42:14 +08:00
陈昱达
1812c0901c feat(portal): 调整AI通话弹窗宽度
- 设置AI通话弹窗默认宽度为800px
- 优化弹窗显示效果和用户体验
2025-11-06 16:29:50 +08:00
陈昱达
13281d8a7d feat(portal): 调整AI通话弹窗宽度
- 设置AI通话弹窗默认宽度为800px
- 优化弹窗显示效果和用户体验
2025-11-06 15:53:38 +08:00
陈昱达
5fdf8efedb fix(portal): 调整AI通话弹窗显示逻辑
- 修改弹窗显示条件,确保仅在最大化状态下显示
- 移除冗余的宽度设置
-优化窗口状态控制逻辑
2025-11-06 15:46:11 +08:00
hz
58f517d2fb Merge remote-tracking branch 'origin/20250922-da' into ebiz-uat-2025-11-06 2025-11-06 09:40:28 +08:00
陈昱达
1a475c8612 feat(ai-call):优化对话框拖拽与缩放功能- 增强对话框拖拽逻辑,防止事件冒泡
- 完善对话框缩放功能,动态调整欢迎消息区域高度
- 修正机器人欢迎文本中的错别字
- 调整消息列表区域样式,优化滚动条显示
- 监听对话框可见性变化,确保内容正确渲染
2025-11-04 17:14:38 +08:00
陈昱达
01e4c676fc feat(portal/case): 增强AI对话窗口交互功能
-为sendMessage组件添加textarea输入框,支持多行输入
- 为AI对话窗口添加拖拽和调整大小功能
- 在最小化窗口中添加关闭按钮- 优化窗口样式和布局,提升用户体验
- 添加拖拽手柄和窗口控制按钮
- 实现窗口位置和大小的动态调整
- 引入open.png图标用于最小化窗口操作
2025-11-04 14:54:51 +08:00
陈昱达
2c630eac70 feat(ai-chat): 实现案例专家功能入口权限控制及消息展示优化- 修改AI聊天接口地址为本地开发环境地址
- 新增showCaseAiEntrance接口用于控制案例专家功能入口显示
- 优化消息组件中的案例引用展示逻辑,支持展开/收起功能
- 增加案例引用的上传时间、作者机构等信息展示
- 实现打字机效果的文本逐字显示功能
- 优化AI消息响应处理逻辑,支持think标签内容解析
2025-09-28 11:40:35 +08:00
24 changed files with 5390 additions and 1472 deletions

View File

@@ -1,12 +1,12 @@
<template>
<div id="app">
<div id="app" style="width: 100vw">
<keep-alive :include="['case']">
<router-view />
12312
</keep-alive>
<!-- 添加AI Call组件 -->
<AICall
:dialogVisible="showAICall"
<AICall
:dialogVisible="showAICall"
@close="onCloseAICall"
@restore="onRestoreAICall"
/>
@@ -16,7 +16,7 @@
<script>
import { mapGetters, mapState } from 'vuex';
import AICall from '@/views/portal/case/AICall.vue';
export default{
name: 'App',
components: {
@@ -31,12 +31,12 @@
// 通过Vuex关闭AI Call组件
this.$store.dispatch('app/setShowAICall', false);
},
onRestoreAICall() {
// 通过Vuex显示AI Call组件
this.$store.dispatch('app/setShowAICall', true);
},
// 检查当前路由是否应该显示AI弹窗
checkRouteForAICall() {
const currentRoute = this.$route.name;
@@ -59,7 +59,7 @@
// if(this.userInfo && this.userInfo.name!=''){
// this.$watermark.set(this.userInfo.name+this.userInfo.loginName);
// }
// 初始化检查路由
this.checkRouteForAICall();
},
@@ -87,4 +87,4 @@
border: 1px solid #e7e7e7 !important;
box-shadow: 0px 1px 5px 1px rgba(92,98,111,.3);
}
</style>
</style>

View File

@@ -103,6 +103,14 @@ const getUsersByIds = function(ids) {
return ajax.postJson(baseURL,'/user/getUserMessageToDai',ids);
}
/**
* 根据关键字检索用户(创建人下拉)
* @param {string} keyword
*/
const selectUser = function(keyword = '') {
return ajax.postJson(baseURL,'/user/selectuser',{ keyword });
}
export default {
userParentOrg,
findOrgsByKeyword,
@@ -116,5 +124,6 @@ export default {
getInAudienceIds,
getUsersByIds,
updateUser,
logout
logout,
selectUser
}

File diff suppressed because it is too large Load Diff

View File

@@ -102,6 +102,24 @@ const courseSearch=function(query){
const detailStudy = function(courseId,aid) {
return ajax.get(`/xboe/m/course/portal/detail-study?courseId=${courseId}&aid=${aid}`);
}
/**
* 课程学习进度的详细信息-分页
* @param {Object} data
*/
const detailStudyPage = function(data) {
return ajax.get(`/xboe/m/course/portal/detail-study-page?courseId=${data.courseId}&aid=${data.aid}&pageIndex=${data.pageIndex}&pageSize=${data.pageSize}`);
}
/**
* 资源学习情况列列表-分页
* @param {Object} data
*/
const pageListResource=function(data){
return ajax.post('/xboe/school/study/course/pagelist-resource',data);
}
export default {
list,
pageList,
@@ -112,5 +130,7 @@ export default {
studyCounts,
courseSearch,
detailStudy,
detailPost
detailPost,
detailStudyPage,
pageListResource
}

View File

@@ -165,7 +165,7 @@ const appendStudyTime = function(data) {
* name: 学习人的姓名
*/
const studyRecords = function(data) {
return ajax.post('/xboe/school/study/course/pagelist',data);
return ajax.post('/xboe/school/study/course/pagelistEx',data);
}
/**
@@ -189,7 +189,7 @@ const studyContentRecords = function(data) {
}
*/
const studyExport = function(data) {
return ajax.post('/xboe/school/study/course/export',data);
return ajax.post('/xboe/school/study/course/export',data, { responseType: 'blob' });
}
/**
@@ -402,6 +402,31 @@ const findByIds=function (ids){
return ajax.postJson('/xboe/school/study/es/list-by-ids',ids);
}
const exportSignup=function (data){
return ajax.post('/xboe/school/study/course/export-signup',data, { responseType: 'blob' });
}
// 作业导出
const exportHomework=function (data){
return ajax.post('/xboe/school/study/course/contents-homework-export',data, { responseType: 'blob' });
}
// 考试导出
const exportExam=function (data){
return ajax.post('/xboe/school/study/course/contents-exam-export',data, { responseType: 'blob' });
}
// 评估类型资源
const contentsAssess=function (data){
return ajax.post('/xboe/school/study/course/contents-assess',data);
}
// 考试类型资源
const contentsExam=function (data){
return ajax.post('/xboe/school/study/course/contents-exam',data);
}
export default {
hasSignup,
signup,
@@ -438,5 +463,10 @@ export default {
deleteSignUp,
ids,
followIds,
studyIndexPost
studyIndexPost,
exportSignup,
exportHomework,
exportExam,
contentsAssess,
contentsExam
}

View File

@@ -0,0 +1,234 @@
<template>
<el-dialog
title="置顶排序"
:visible.sync="dialogVisible"
custom-class="g-dialog top-course-sorter-dialog"
width="820px"
:close-on-click-modal="false"
@closed="handleClosed"
append-to-body
>
<div class="top-course-sorter" v-loading="loading">
<div class="top-course-sorter__table" v-if="topList.length">
<div class="sorter-header">
<div class="header-cell header-cell--handle"></div>
<div class="header-cell header-cell--order">排序</div>
<div class="header-cell header-cell--name">课程名称</div>
<div class="header-cell header-cell--teacher">授课教师</div>
</div>
<div
class="sorter-row"
v-for="(item, index) in topList"
:key="item.id"
draggable="true"
@dragstart="handleDragStart(index, $event)"
@dragover.prevent
@drop="handleDrop(index)"
@dragend="handleDragEnd"
:class="{ 'is-dragging': draggingIndex === index }"
>
<div class="row-cell row-cell--handle">
<i class="el-icon-s-operation"></i>
</div>
<div class="row-cell row-cell--order">{{ index + 1 }}</div>
<div class="row-cell row-cell--name" :title="item.name">{{ item.name }}</div>
<div class="row-cell row-cell--teacher" :title="item.teacherName || '-'">
{{ item.teacherName || '-' }}
</div>
</div>
</div>
<el-empty v-else-if="!loading" description="暂无置顶课程"></el-empty>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :disabled="!topList.length" :loading="saving" @click="handleSave">确定</el-button>
</span>
</el-dialog>
</template>
<script>
import apiCourse from '@/api/modules/course.js';
export default {
name: 'TopCourseSorter',
data() {
return {
dialogVisible: false,
loading: false,
saving: false,
topList: [],
draggingIndex: null,
};
},
methods: {
open() {
this.dialogVisible = true;
this.fetchTopList();
},
async fetchTopList() {
this.loading = true;
try {
const res = await apiCourse.fetchTopCourseList();
if (res.status === 200) {
this.topList = Array.isArray(res.result) ? [...res.result] : [];
} else {
this.$message.error(res.message || '获取置顶课程失败');
this.topList = [];
}
} catch (error) {
this.$message.error(error.message || '获取置顶课程失败');
this.topList = [];
} finally {
this.loading = false;
}
},
handleDragStart(index, event) {
this.draggingIndex = index;
if (event && event.dataTransfer) {
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', index);
}
},
handleDrop(targetIndex) {
if (this.draggingIndex === null || this.draggingIndex === targetIndex) {
return;
}
const movingItem = this.topList.splice(this.draggingIndex, 1)[0];
this.topList.splice(targetIndex, 0, movingItem);
this.draggingIndex = targetIndex;
},
handleDragEnd() {
this.draggingIndex = null;
},
async handleSave() {
if (!this.topList.length) {
this.$message.warning('暂无需要保存的排序');
return;
}
const payload = this.topList.map((item, index) => ({
id: item.id,
sortWeight: index,
}));
this.saving = true;
try {
const res = await apiCourse.updateTopCourseSort(payload);
if (res.status === 200) {
this.$message.success('排序更新成功');
this.$emit('sorted');
this.dialogVisible = false;
} else {
throw new Error(res.message || '排序更新失败');
}
} catch (error) {
this.$message.error(error.message || '排序更新失败');
} finally {
this.saving = false;
}
},
handleClosed() {
this.topList = [];
this.draggingIndex = null;
this.loading = false;
this.saving = false;
},
},
};
</script>
<style lang="scss" scoped>
.top-course-sorter {
min-height: 200px;
padding-top: 8px;
}
.top-course-sorter__table {
border: 1px solid #ebeef5;
border-radius: 6px;
overflow: hidden;
}
.sorter-header,
.sorter-row {
display: grid;
grid-template-columns: 60px 80px 1fr 160px;
align-items: center;
}
.sorter-header {
background-color: #f5f7fa;
height: 48px;
font-weight: 600;
color: #303133;
border-bottom: 1px solid #ebeef5;
}
.sorter-row {
min-height: 56px;
border-bottom: 1px solid #f2f6fc;
cursor: move;
transition: background-color 0.2s ease;
}
.sorter-row:last-child {
border-bottom: none;
}
.sorter-row:hover {
background-color: #f9fbff;
}
.sorter-row.is-dragging {
opacity: 0.7;
background-color: #ecf5ff;
}
.header-cell,
.row-cell {
padding: 0 16px;
display: flex;
align-items: center;
}
.header-cell--handle,
.row-cell--handle {
justify-content: center;
}
.header-cell--order,
.row-cell--order {
justify-content: flex-start;
}
.row-cell--name,
.row-cell--teacher {
color: #303133;
}
.row-cell--name {
font-weight: 500;
}
.row-cell--teacher {
color: #666;
}
.row-cell--handle i {
font-size: 20px;
color: #c0c4cc;
}
.dialog-footer {
text-align: right;
}
</style>

View File

@@ -41,6 +41,43 @@
<el-button @click="toInputCourse()" type="primary">确定</el-button>
</span> -->
</el-dialog>
<!-- 蒙层引导组件 -->
<div v-if="showGuidance" class="guidance-overlay" @click="closeGuidance">
<div class="guidance-content">
<div class="guidance-title"></div> <!--新功能引导-->
<div class="guidance-steps">
<div class="guidance-step" :class="{ active: currentStep === 1 }">
<div class="step-number"></div>
<div class="step-content">
<div class="step-title">用标签为课程精准定位吸引更多学员可从以下维度构思</div>
<div class="step-desc" style="padding-left: 20px;"> 讲领域品质管理</div>
<div class="step-desc" style="padding-left: 20px;"> 教技能沟通技巧</div>
<div class="step-desc" style="padding-left: 20px;"> 涉内容5W1H分析法</div>
</div>
</div>
<!-- <div class="guidance-step" :class="{ active: currentStep === 2 }">
<div class="step-number">2</div>
<div class="step-content">
<div class="step-title">添加课程标签</div>
<div class="step-desc">为课程添加相关标签,最多5个便于搜索和分类,可回车创建新标签</div>
</div>
</div>-->
</div>
<!-- <div class="guidance-actions">
<el-button @click="previousStep1" v-if="currentStep > 1">上一步</el-button>
<el-button type="primary" @click="nextStep">
{{ currentStep === 2 ? '完成' : '下一步' }}
</el-button>
<el-button @click="closeGuidance">跳过引导</el-button>
</div>-->
</div>
</div>
<!-- 高亮指引元素 -->
<!-- <div v-if="showGuidance" class="highlight-element" :style="highlightStyle"></div>-->
<!--微课-->
<el-dialog v-if="weike.dlgShow" width="980px" :title="curCourseId == '' ? '新建课程' : '编辑课程'" :visible.sync="weike.dlgShow" :close-on-click-modal="false" custom-class="g-dialog" top="8vh">
<el-form label-width="100px" size="small" class="wei-from" style="min-height: 600px;">
@@ -64,18 +101,29 @@
</span>
</div>
</el-form-item>
<el-form-item label="内容分类" required>
<el-form-item label="内容分类" required >
<el-cascader
placeholder="选择内容分类"
style="width: 100%;"
clearable
v-model="sysTypeList"
:props="{ value: 'id', label: 'name' }"
:options="sysTypeListMap"></el-cascader>
:options="sysTypeListMap"
@focus="onContentTypeFocus"
@change="onContentTypeChange">
</el-cascader>
</el-form-item>
<el-form-item label="标签" required>
<courseTag ref="courseTag" :courseId="curCourseId" :sysTypeList="sysTypeList" :initialTags="courseTags" @change="handleTagsChange"></courseTag>
<el-form-item label="标签" required class="guidance-highlight" data-step="1" style="display: flex" >
<courseTag ref="courseTag" :courseId="curCourseId" :sysTypeList="sysTypeList"
:initialTags="courseTags" @change="handleTagsChange"
@focus="onTagFocus" style="width: 95%;">
</courseTag>
<el-tooltip content="点击查看标签新功能引导" placement="top">
<i class="el-icon-question" style="color: #409EFF; cursor: pointer;" @click="handleTagHelp"></i>
</el-tooltip>
</el-form-item>
<el-form-item label="资源归属" required>
<el-input placeholder="请选择" v-model="orgName" >
<el-button v-if="identity==3 || identity==5" @click="showChooseOrg()" slot="append" icon="el-icon-search">选择</el-button>
@@ -221,14 +269,16 @@
</div>
</el-col>
<el-col :span="12">
<el-form-item label="内容分类" required>
<el-form-item label="内容分类" required >
<el-cascader
placeholder="选择内容分类"
style="width: 100%;"
clearable
v-model="sysTypeList"
:props="{ value: 'id', label: 'name' }"
:options="sysTypeListMap">
:options="sysTypeListMap"
@focus="onContentTypeFocus"
@change="onContentTypeChange">
</el-cascader>
</el-form-item>
@@ -256,9 +306,16 @@
</el-select> -->
<choice :teacherValue="teacherValues" @getTeacherList="getTeacherList"></choice>
</el-form-item>
<el-form-item label="标签" required>
<courseTag ref="courseTag" :courseId="curCourseId" :sysTypeList="sysTypeList" :initialTags="courseTags" @change="handleTagsChange"></courseTag>
<el-form-item label="标签" required class="tag-from-item" data-step="1" >
<courseTag ref="courseTag" :courseId="curCourseId" :sysTypeList="sysTypeList"
:initialTags="courseTags" @change="handleTagsChange"
@focus="onTagFocus" >
</courseTag>
<el-tooltip content="点击查看标签新功能引导" placement="top">
<i class="el-icon-question" style="color: #409EFF; cursor: pointer;" @click="handleTagHelp"></i>
</el-tooltip>
</el-form-item>
<el-form-item label="关键字">
<el-input v-model.trim="keywords" maxlength="100" @keyup.enter.native="changeKeywords" placeholder="请输入关键字"></el-input>
<el-tag v-for="(tag,index) in tips" size="small" :key="index" closable type="info" @close="closeKeywordsTag(tag,index)">
@@ -536,7 +593,13 @@ export default {
dlgShow: false
},
rightTypeId: {},
catalogSortDialogShow: false
catalogSortDialogShow: false,
// 蒙层引导相关数据
showGuidance: false,
currentStep: 1,
highlightStyle: {},
guidanceElements: [],
isFirstCreate: false, // 标记是否为首次创建
};
},
created() {
@@ -590,6 +653,9 @@ export default {
this.loadUserGroup();
},
methods: {
handleTagHelp(){
this.checkAndShowGuidance();
},
// 关键字的更改
changeKeywords(option){
if(option.target.value){
@@ -831,7 +897,7 @@ export default {
}
} else {
//console.log(editData,'editData');
console.log(editData,'editData');
this.weikeReset = editData.id;
this.onlineReset = editData.id;
//console.log("编辑课程?");
@@ -849,7 +915,7 @@ export default {
},
resetCurCourseContent() {
this.curContent = { id: '', contentType: 0, contentName: '', content: '', csectionId: '', contentName: '', contentRefId: '', courseId: this.courseInfo.id };
this.curContent = { id: '', contentType: 0, contentName: '', content: '', csectionId: '', contentRefId: '', courseId: this.courseInfo.id };
},
// chooseCourseType(item, idx, open) {
// this.courseChooseId = item.id;
@@ -897,19 +963,106 @@ export default {
this.courseInfo = rs.result;
this.curCourseId = this.courseInfo.id
console.log('保存课程成功',this.curCourseId)
console.log('isTip ',this.courseInfo.isTip)
// if(this.courseInfo.isTip){
// // 检查是否为首次创建,显示引导
// this.checkAndShowGuidance();
// }
if (this.courseChooseId == 1) {
this.weike.dlgShow = true;
} else {
this.biaoke.dlgShow = true;
}
this.$nextTick(() => {
this.initTagComponent();
});
// this.$nextTick(() => {
// this.initTagComponent();
// // 如果显示引导,初始化高亮元素
// if (this.showGuidance) {
// this.$nextTick(() => {
// this.initGuidanceElements();
// this.highlightCurrentStep();
// });
// }
// });
} else {
this.$message.error(rs.message);
}
});
},
// 检查并显示引导
checkAndShowGuidance() {
// 检查本地存储,判断是否为首次创建
// const hasShownGuidance = localStorage.getItem('course_creation_guidance_shown');
// if (!hasShownGuidance) {
this.showGuidance = true;
this.currentStep = 1;
this.isFirstCreate = true;
// 标记引导已显示
localStorage.setItem('course_creation_guidance_shown', 'true');
// apiCourse.saveTip();
// }
},
// 初始化引导元素
initGuidanceElements() {
this.guidanceElements = Array.from(document.querySelectorAll('.guidance-highlight'));
},
// 高亮当前步骤对应的元素
highlightCurrentStep() {
if (this.guidanceElements.length === 0) return;
const currentElement = this.guidanceElements[this.currentStep - 1];
if (currentElement) {
currentElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
},
// 下一步
nextStep() {
if (this.currentStep < 2) {
this.currentStep++;
this.highlightCurrentStep();
} else {
this.closeGuidance();
}
},
// 上一步
previousStep1() {
if (this.currentStep > 1) {
this.currentStep--;
this.highlightCurrentStep();
}
},
// 关闭引导
closeGuidance() {
this.showGuidance = false;
this.currentStep = 1;
this.highlightStyle = {};
},
// 内容分类获得焦点时的处理
onContentTypeFocus() {
if (this.showGuidance && this.currentStep === 1) {
this.currentStep = 2;
this.highlightCurrentStep();
}
},
// 内容分类改变时的处理
onContentTypeChange() {
if (this.showGuidance && this.currentStep === 1) {
this.currentStep = 2;
this.highlightCurrentStep();
}
},
// 标签获得焦点时的处理
onTagFocus() {
if (this.showGuidance && this.currentStep === 2) {
this.closeGuidance();
}
},
// 新增初始化标签方法
initTagComponent() {
if (this.$refs.courseTag) {
@@ -1816,4 +1969,126 @@ export default {
}
}
}
/* 蒙层样式 */
.guidance-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.6);
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
}
.guidance-content {
position: fixed;
background: white;
border-radius: 8px;
padding: 24px;
max-width: 500px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
z-index: 10000;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.guidance-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 20px;
text-align: center;
}
.guidance-steps {
margin-bottom: 20px;
}
.guidance-step {
display: flex;
align-items: flex-start;
margin-bottom: 16px;
opacity: 0.5;
transition: opacity 0.3s;
}
.guidance-step.active {
opacity: 1;
}
.step-number {
//width: 24px;
height: 24px;
//background: #409EFF;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
font-size: 14px;
}
.step-content {
flex: 1;
}
.step-title {
font-weight: bold;
margin-bottom: 4px;
}
.step-desc {
color: #666;
font-size: 14px;
}
.guidance-actions {
display: flex;
justify-content: center;
gap: 12px;
}
/* 高亮元素样式 */
.highlight-element {
position: fixed;
border: 2px solid #409EFF;
border-radius: 4px;
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.4), 0 0 15px rgba(64, 158, 255, 0.5);
z-index: 9998;
pointer-events: none;
transition: all 0.3s ease;
}
/* 被高亮元素的样式 */
.guidance-highlight {
position: relative;
z-index: 10001;
}
//.el-form-item__content {
// display: flex;
// width: 80%;
// .tag-container {
// width: 95%;
// }
//}
.tag-from-item .el-form-item__content {
margin-left: 0 !important;
display: flex;
//align-items: center;
.tag-container {
width: 95%;
}
}
.guidance-highlight .el-form-item__content {
margin-left: 0 !important;
display: flex;
width: 75%;
}
</style>

View File

@@ -1,22 +1,22 @@
<template>
<div class="tag-container">
<div class="tag-container" @click="handleContainerClick">
<el-select style="width: 100%;"
v-model="selectedTags"
multiple
filterable
value-key="id"
remote
reserve-keyword
:remote-method="debouncedSearch"
:loading="loading"
:placeholder="'回车创建新标签'"
@remove-tag="handleTagRemove"
@change="handleSelectionChange"
@keyup.enter.native="handleEnterKey"
@keyup.delete.native="handleDeleteKey"
@focus="handleFocus"
ref="tagSelect"
v-model="selectedTags"
multiple
filterable
value-key="id"
remote
reserve-keyword
:remote-method="debouncedSearch"
:loading="loading"
:placeholder="'回车创建新标签'"
:no-data-text="'无此标签,按回车键创建'"
@remove-tag="handleTagRemove"
@change="handleSelectionChange"
@keyup.enter.native="handleEnterKey"
@keyup.delete.native="handleDeleteKey"
@focus="handleFocus"
ref="tagSelect"
>
<el-option
v-for="item in searchResults"
@@ -141,6 +141,11 @@ export default {
if (this.sysTypeList.length > 0) {
await this.doSearch('');
}
this.$emit('focus');
},
handleContainerClick() {
// 容器点击时也触发焦点事件
this.$emit('focus');
},
// 新增:重置标签状态的方法
resetTagState() {
@@ -212,6 +217,9 @@ export default {
this.$emit('change', this.displayTags);
this.clearInput();
this.$nextTick(() => {
this.$refs.tagSelect.visible = false;
});
},
clearInput() {
@@ -313,7 +321,7 @@ export default {
this.searchResults = tags
// 当搜索结果为空时,提示用户可以按回车键创建标签
if (tags.length === 0) {
this.$message.info('无此标签,按回车键创建')
// this.$message.info('无此标签,按回车键创建')
}
} finally {
this.loading = false
@@ -359,8 +367,9 @@ export default {
flex-wrap: wrap;
align-items: center;
}
/*
::v-deep(.el-tag) {
flex: 0 0 calc(50% - 8px);
max-width: calc(50% - 8px);
box-sizing: border-box;
margin-right: 8px;
@@ -368,6 +377,20 @@ export default {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}*/
::v-deep(.el-tag) {
flex: 1 1 auto; /* 自动调整宽度 */
min-width: 30%; /* 设置最小宽度 */
max-width: 48%; /* 设置最大宽度,留出边距 */
box-sizing: border-box;
margin-right: 8px;
margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
justify-content: center;
text-align: center;
}
::v-deep(.el-select__input) {

View File

@@ -414,6 +414,11 @@ export default {
if(meta.title == '添加受众' || meta.title == '查看受众'){
meta.activeMenu = '/manage/ugroups'
}
if(route.path == '/course/coursemanage'){
console.log('进入课程管理');
meta.activeMenu = '/need/course'
}
if (meta.activeMenu) {
return meta.activeMenu;
}

View File

@@ -29,6 +29,8 @@ export const pages=[
{title:'课程首页',path:'index',component:'course/Index',hidden:true},
{title:'课程建设',path:'mylist',component:'course/TeacherList',hidden:true},
{title:'课程管理',path:'manage',component:'course/ManageList',hidden:false},
{title:'课程管理',path:'coursemanage',component:'course/CourseManage',hidden:true},
{title:'课程管理新版',path:'manage-remote',component:'course/ManageListRemote',hidden:false},
{title:'课程统计',path:'stat',component:'course/StatIndex',hidden:false},
{title:'课件管理',path:'courseware',component:'course/Courseware',hidden:false},
{title:'报名管理',path:'msignup',component:'study/ManageSignup',hidden:true},
@@ -117,6 +119,7 @@ export const iframes=[
{title:'嵌入测试', path:'/iframe/index',hidden:false,component:'portal/iframe'},
{title:'课件管理', path:'/iframe/course/coursewares',hidden:false,component:'course/Courseware'},
{title:'课程管理', path:'/iframe/course/manages',hidden:false,component:'course/ManageList'},
{title:'课程管理新版', path:'/iframe/course/manage-remote',hidden:false,component:'course/ManageListRemote'},
{title:'考试试题管理', path:'/iframe/exam/questions',hidden:false,component:'exam/Question'},
{title:'查看答卷', path:'/iframe/exam/viewanswer',hidden:false,component:'exam/viewAnswer'},
{title:'考试试卷管理', path:'/iframe/exam/papers',hidden:false,component:'exam/TestPaper'},

14
src/icons/svg/del.svg Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="11px" height="12px" viewBox="0 0 11 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>shanchu</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="icon" transform="translate(-436, -918)" fill="#FF1818" fill-rule="nonzero">
<g id="shanchu" transform="translate(436, 918)">
<path d="M10.4999537,3.19289906 L0.500046316,3.19289906 C0.223878361,3.19289906 0,2.97107081 0,2.6974318 C0,2.42379279 0.223878361,2.20196454 0.500046316,2.20196454 L10.4999537,2.20196454 C10.7761216,2.20196454 11,2.42379279 11,2.6974318 C11,2.97107081 10.7761216,3.19289906 10.4999537,3.19289906 Z" id="路径"></path>
<path d="M8.27763883,12 L2.72210644,12 C1.83281125,11.9990263 1.11214015,11.2849546 1.11115743,10.4038029 L1.11115743,2.6974318 C1.11115743,2.42379279 1.33503579,2.20196454 1.61120374,2.20196454 C1.8873717,2.20196454 2.11125006,2.42379279 2.11125006,2.6974318 L2.11125006,10.4038029 C2.11125006,10.738359 2.38496787,11.0095703 2.7226159,11.0095703 L8.2781483,11.0095703 C8.61579634,11.0095703 8.88951415,10.738359 8.88951415,10.4038029 L8.88951415,2.6974318 C8.88951415,2.42379279 9.11339251,2.20196454 9.38956046,2.20196454 C9.66572842,2.20196454 9.88960678,2.42379279 9.88960678,2.6974318 L9.88960678,10.4038029 C9.88862317,11.2853484 9.16733192,11.9995828 8.27763883,12 Z M7.72206011,3.19289906 C7.4459504,3.1927598 7.22215435,2.9710131 7.22201381,2.6974318 L7.22201381,1.59619712 C7.22201381,1.43553761 7.15760223,1.2814583 7.04294889,1.16785488 C6.92829555,1.05425145 6.77279226,0.990429712 6.61064795,0.990429715 L4.3890973,0.990429715 C4.05144927,0.990429715 3.77773146,1.26164102 3.77773146,1.59619712 L3.77773146,2.6974318 C3.77773146,2.97107081 3.5538531,3.19289906 3.27768515,3.19289906 C3.00151719,3.19289906 2.77763883,2.97107081 2.77763883,2.6974318 L2.77763883,1.59619712 C2.77862156,0.715045435 3.49929265,0.000973720991 4.38858784,0 L6.6109027,0 C7.50019788,0.000973727448 8.22086897,0.71504544 8.2218517,1.59619712 L8.2218517,2.6974318 C8.22171131,2.97091464 7.99807034,3.19262061 7.72206011,3.19289906 L7.72206011,3.19289906 Z" id="形状"></path>
<path d="M4.3890973,9.2475444 C4.11292935,9.2475444 3.88905099,9.02571608 3.88905099,8.75207707 L3.88905099,5.44938266 C3.88902352,5.27235111 3.9843258,5.10875556 4.13905195,5.02023193 C4.2937781,4.93170829 4.48441652,4.93170829 4.63914267,5.02023193 C4.79386882,5.10875556 4.8891711,5.27235111 4.88914369,5.44938266 L4.88914369,8.75207707 C4.88921124,8.88350369 4.83654966,9.00956666 4.74275812,9.10249932 C4.64896658,9.19543199 4.52173855,9.24761133 4.3890973,9.2475444 L4.3890973,9.2475444 Z" id="路径"></path>
<path d="M6.61115743,9.24754433 C6.33498947,9.24754433 6.11111111,9.02571608 6.11111111,8.75207707 L6.11111111,5.44938266 C6.11111111,5.17574365 6.33498947,4.9539154 6.61115743,4.9539154 C6.88732538,4.9539154 7.11120374,5.17574365 7.11120374,5.44938266 L7.11120374,8.75207707 C7.11120374,9.02571608 6.88732538,9.24754433 6.61115743,9.24754433 L6.61115743,9.24754433 Z" id="路径"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

11
src/icons/svg/detail.svg Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>chakan</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="icon" transform="translate(-326, -920)" fill="#4284F7" fill-rule="nonzero">
<g id="chakan" transform="translate(326, 920)">
<path d="M8.94642857,0 C9.61213393,0 10.1517857,0.539651786 10.1517857,1.20535714 L10.1517857,3.16071429 L11.0625,3.16071429 C11.5750848,3.16071429 11.9915893,3.57208929 11.9998661,4.08270536 L12,4.09821429 L12,10.7946429 C12,11.453692 11.4710759,11.989192 10.8145714,11.9998393 L10.7946429,12 L1.20535714,12 C0.546308036,12 0.0108080357,11.4710759 0.000160714286,10.8145714 L0,10.7946429 L0,4.09821429 C0,3.58562946 0.411375,3.169125 0.921991071,3.16084821 L0.9375,3.16071429 L1.84821429,3.16071429 L1.84821429,1.20535714 C1.84821429,0.546308036 2.37713839,0.0108080357 3.03364286,0.000160714286 L3.05357143,0 L8.94642857,0 Z M11.1964286,8.83928571 L0.803571429,8.83928571 L0.803571429,10.7946429 C0.803571429,11.0143259 0.979875,11.1928259 1.19871429,11.196375 L1.20535714,11.1964286 L10.7946429,11.1964286 C11.0143259,11.1964286 11.1928259,11.020125 11.1964286,10.8012857 L11.1964286,10.7946429 L11.1964286,8.83928571 Z M1.84821429,3.96428571 L0.9375,3.96428571 C0.864776786,3.96428571 0.805607143,4.02223661 0.803625,4.09446429 L0.803571429,4.09821429 L0.803571429,8.03571429 L1.84821429,8.03571429 L1.84821429,3.96428571 Z M11.0625,3.96428571 L10.1517857,3.96428571 L10.1517857,8.03571429 L11.1964286,8.03571429 L11.1964286,4.09821429 C11.1964286,4.02549107 11.1384777,3.96632143 11.06625,3.96433929 L11.0625,3.96428571 Z M8.95307143,0.803571429 L3.05357143,0.803571429 C2.83166518,0.803571429 2.65178571,0.983450893 2.65178571,1.20535714 L2.65178571,8.03571429 L9.34821429,8.03571429 L9.34821429,1.20535714 C9.34821429,0.985674107 9.17191071,0.807174107 8.95307143,0.803571429 Z M5.69866071,1.47321429 C7.1595,1.47321429 8.34375,2.65746429 8.34375,4.11830357 C8.34375,4.70375893 8.15354464,5.24479018 7.83155357,5.68297768 L8.65222768,6.50366518 C8.80913839,6.6605625 8.80913839,6.91495982 8.65222768,7.07187054 C8.49531696,7.22878125 8.24093304,7.22878125 8.08402232,7.07187054 L7.26333482,6.25119643 C6.82513393,6.5731875 6.28411607,6.76339286 5.69866071,6.76339286 C4.23782143,6.76339286 3.05357143,5.57914286 3.05357143,4.11830357 C3.05357143,2.65746429 4.23782143,1.47321429 5.69866071,1.47321429 Z M5.69866071,2.27678571 C4.68160714,2.27678571 3.85714286,3.10125 3.85714286,4.11830357 C3.85714286,5.13535714 4.68160714,5.95982143 5.69866071,5.95982143 C6.71571429,5.95982143 7.54017857,5.13535714 7.54017857,4.11830357 C7.54017857,3.10125 6.71571429,2.27678571 5.69866071,2.27678571 Z" id="形状"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

9
src/icons/svg/edit.svg Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="10px" height="10px" viewBox="0 0 10 10" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>形状</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" opacity="0.4">
<g id="自定义考试_弹框2备份" transform="translate(-735, -435)" fill="#4284F7" fill-rule="nonzero">
<path d="M743.186667,436.766237 L741.615,435.165652 C741.398056,434.944783 741.046389,434.944783 740.829444,435.165652 L735.929444,440.155811 L735.102778,442.682575 C735.03591,442.885968 735.087729,443.110381 735.236598,443.262109 C735.385467,443.413836 735.605784,443.466786 735.805556,443.398849 L738.286667,442.556406 L743.186667,437.566813 C743.403546,437.345876 743.403546,436.987739 743.186667,436.766803 L743.186667,436.766237 Z M742.402222,437.166808 L737.884444,441.768278 L736.107778,442.374791 L736.705556,440.567698 L741.223889,435.966793 L742.402222,437.166808 L742.402222,437.166808 Z M735,444.151333 L745,444.151333 L745,445 L735,445 L735,444.151333 Z" id="形状"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

11
src/icons/svg/ercode.svg Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>erweima</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="icon" transform="translate(-258, -919)" fill="#4284F7" fill-rule="nonzero">
<g id="erweima" transform="translate(258, 919)">
<path d="M4,6.66666667 L1.33333333,6.66666667 C0.6,6.66666667 0,7.26666667 0,8 L0,10.6666667 C0,11.4 0.6,12 1.33333333,12 L4,12 C4.73333333,12 5.33333333,11.4 5.33333333,10.6666667 L5.33333333,8 C5.33333333,7.26666667 4.73333333,6.66666667 4,6.66666667 Z M4.44444444,10.6666667 C4.44444444,10.9111111 4.24444444,11.1111111 4,11.1111111 L1.33333333,11.1111111 C1.08888889,11.1111111 0.888888889,10.9111111 0.888888889,10.6666667 L0.888888889,8 C0.888888889,7.75555556 1.08888889,7.55555556 1.33333333,7.55555556 L4,7.55555556 C4.24444444,7.55555556 4.44444444,7.75555556 4.44444444,8 L4.44444444,10.6666667 Z M4,0 L1.33333333,0 C0.6,0 0,0.6 0,1.33333333 L0,4 C0,4.73333333 0.6,5.33333333 1.33333333,5.33333333 L4,5.33333333 C4.73333333,5.33333333 5.33333333,4.73333333 5.33333333,4 L5.33333333,1.33333333 C5.33333333,0.6 4.73333333,0 4,0 Z M4.44444444,4 C4.44444444,4.24444444 4.24444444,4.44444444 4,4.44444444 L1.33333333,4.44444444 C1.08888889,4.44444444 0.888888889,4.24444444 0.888888889,4 L0.888888889,1.33333333 C0.888888889,1.08888889 1.08888889,0.888888889 1.33333333,0.888888889 L4,0.888888889 C4.24444444,0.888888889 4.44444444,1.08888889 4.44444444,1.33333333 L4.44444444,4 Z M11.3333333,6.66666667 C11.0888889,6.66666667 10.8888889,6.86666667 10.8888889,7.11111111 L10.8888889,11.5555556 C10.8888889,11.8 11.0888889,12 11.3333333,12 C11.5777778,12 11.7777778,11.8 11.7777778,11.5555556 L11.7777778,7.11111111 C11.7777778,6.86666667 11.5777778,6.66666667 11.3333333,6.66666667 Z M10.6666667,0 L8,0 C7.26666667,0 6.66666667,0.6 6.66666667,1.33333333 L6.66666667,4 C6.66666667,4.73333333 7.26666667,5.33333333 8,5.33333333 L10.6666667,5.33333333 C11.4,5.33333333 12,4.73333333 12,4 L12,1.33333333 C12,0.6 11.4,0 10.6666667,0 Z M11.1111111,4 C11.1111111,4.24444444 10.9111111,4.44444444 10.6666667,4.44444444 L8,4.44444444 C7.75555556,4.44444444 7.55555556,4.24444444 7.55555556,4 L7.55555556,1.33333333 C7.55555556,1.08888889 7.75555556,0.888888889 8,0.888888889 L10.6666667,0.888888889 C10.9111111,0.888888889 11.1111111,1.08888889 11.1111111,1.33333333 L11.1111111,4 Z M7.33333333,7.33333333 C7.08888889,7.33333333 6.88888889,7.53333333 6.88888889,7.77777778 L6.88888889,11.5555556 C6.88888889,11.8 7.08888889,12 7.33333333,12 C7.57777778,12 7.77777778,11.8 7.77777778,11.5555556 L7.77777778,7.77777778 C7.77777778,7.53333333 7.57777778,7.33333333 7.33333333,7.33333333 Z M9.33333333,8.66666667 C9.08888889,8.66666667 8.88888889,8.86666667 8.88888889,9.11111111 L8.88888889,11.5555556 C8.88888889,11.8 9.08888889,12 9.33333333,12 C9.57777778,12 9.77777778,11.8 9.77777778,11.5555556 L9.77777778,9.11111111 C9.77777778,8.86666667 9.57777778,8.66666667 9.33333333,8.66666667 Z" id="形状"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

11
src/icons/svg/manage.svg Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>guanli</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="icon" transform="translate(-124, -920)" fill="#4284F7" fill-rule="nonzero">
<g id="guanli" transform="translate(124, 920)">
<path d="M4.49064449,5.50676379 L0.997920994,5.50676379 C0.449064449,5.50676379 0,5.05723205 0,4.50780437 L0,1.01144641 C0,0.462018737 0.449064449,0.0124869995 0.997920994,0.0124869995 L4.49064449,0.0124869995 C5.03950104,0.0124869995 5.48856549,0.462018737 5.48856549,1.01144641 L5.48856549,4.50780437 C5.48856549,5.05723205 5.03950104,5.50676379 4.49064449,5.50676379 Z M4.5347973,4.50780437 L4.5347973,5.00728409 L4.5347973,4.50780437 Z M0.997920994,1.01144641 L0.997920994,4.50780437 L4.49064449,4.50780437 L4.49064449,1.01144641 L0.997920994,1.01144641 Z M9.00623701,6.0062435 C8.75675676,6.0062435 8.53222453,5.90634756 8.35758836,5.73152966 L6.28690229,3.65868887 C6.11226612,3.48387098 6.01247401,3.25910511 6.01247401,3.00936525 C6.01247401,2.75962539 6.11226611,2.53485953 6.28690229,2.36004162 L8.35758836,0.26222685 C8.70686072,-0.0874089499 9.30561332,-0.0874089499 9.65488566,0.26222685 L11.7255717,2.36004162 C11.9002079,2.53485952 12,2.75962539 12,3.00936525 C12,3.25910511 11.9002079,3.48387097 11.7255717,3.65868887 L9.65488566,5.73152966 C9.48024949,5.90634755 9.25571726,6.0062435 9.00623701,6.0062435 L9.00623701,6.0062435 Z M7.06029106,3.00936525 L9.03118504,4.98231009 L10.977131,3.00936525 L9.00623701,1.03642041 L7.06029106,3.00936525 Z M4.49064449,12 L0.997920994,12 C0.449064449,12 0,11.5504683 0,11.0010406 L0,7.50468262 C0,6.95525495 0.449064449,6.50572321 0.997920994,6.50572321 L4.49064449,6.50572321 C5.03950104,6.50572321 5.48856549,6.95525495 5.48856549,7.50468262 L5.48856549,11.0010406 C5.48856549,11.5504683 5.03950104,12 4.49064449,12 Z M4.5347973,11.0010406 L4.5347973,11.5005203 L4.5347973,11.0010406 Z M0.997920994,7.50468262 L0.997920994,11.0010406 L4.49064449,11.0010406 L4.49064449,7.50468262 L0.997920994,7.50468262 Z M11.002079,12 L7.50935551,12 C6.96049896,12 6.51143451,11.5504683 6.51143451,11.0010406 L6.51143451,7.50468262 C6.51143451,6.95525495 6.96049896,6.50572321 7.50935551,6.50572321 L11.002079,6.50572321 C11.5509356,6.50572321 12,6.95525495 12,7.50468262 L12,11.0010406 C12,11.5504683 11.5509356,12 11.002079,12 Z M11.0462318,11.0010406 L11.0462318,11.5005203 L11.0462318,11.0010406 Z M7.50935551,7.50468262 L7.50935551,11.0010406 L11.002079,11.0010406 L11.002079,7.50468262 L7.50935551,7.50468262 Z" id="形状"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -123,16 +123,6 @@
</el-table-column>
<el-table-column label="创建人" prop="sysCreateBy"></el-table-column>
<el-table-column label="创建时间" prop="sysCreateTime" width="230px" show-overflow-tooltip></el-table-column>
<el-table-column label="审核人" prop="auditUser" width="130px" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.auditUser || '-' }}
</template>
</el-table-column>
<el-table-column label="审核时间" prop="auditTime" width="230px" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.auditTime || '-' }}
</template>
</el-table-column>
<el-table-column label="是否停用" width="130px">
<template slot-scope="scope">
{{ scope.row.enabled == true ? '启用' : '停用' }}

File diff suppressed because it is too large Load Diff

View File

@@ -8,30 +8,38 @@
</Remark> -->
<div style="display: flex;justify-content:space-between;position: relative;">
<div style="display: flex;justify-content: flex-start; padding: 12px 32px 10px 12px;">
<!-- <div style="padding-left: 10px;">
<el-select style="width: 120px;" v-model="params.type" clearable placeholder="课程类型">
<el-option label="全部" :value="null"></el-option>
<el-option label="微课" :value="10"></el-option>
<el-option label="在线课" :value="20"></el-option>
</el-select>
</div> -->
<div style="margin-left:10px"><el-input :maxlength="50" v-model="params.name" placeholder="课程名称" clearable></el-input></div>
<div style="padding-left: 10px;">
<el-select style="width: 120px;" v-model="params.status" clearable placeholder="状态">
<el-option label="全部" :value="null"></el-option>
<el-option label="草稿" :value="1"></el-option>
<el-option label="待审核" :value="2"></el-option>
<el-option label="已审核" :value="5"></el-option>
<el-select style="width: 120px;" v-model="params.publish" clearable placeholder="发布状态">
<!-- <el-option label="全部" :value="null"></el-option> -->
<el-option label="未发布" :value="false"></el-option>
<el-option label="已发布" :value="true"></el-option>
</el-select>
</div>
<div style="margin-left:10px"><el-input v-model="params.keyword" placeholder="名称关键字" clearable></el-input></div>
<div style="padding-left: 10px;"><el-button type="primary" icon="el-icon-search" @click="findList()">搜索</el-button></div>
<div style="padding: 0px 5px;"><el-button icon="el-icon-refresh-right" type="primary" @click="reset">重置</el-button></div>
<div style="padding-left: 10px;">
<el-select style="width: 120px;" v-model="params.enabled" clearable placeholder="启停用状态">
<!-- <el-option label="全部" :value="null"></el-option> -->
<el-option label="停用" :value="false"></el-option>
<el-option label="启用" :value="true"></el-option>
</el-select>
</div>
<div style="padding-left: 10px;">
<el-select style="width: 120px;" v-model="params.status" clearable placeholder="审核状态">
<!-- <el-option label="全部" :value="null"></el-option> -->
<!-- <el-option label="无审核状态" :value="1"></el-option> -->
<el-option label="审核中" :value="2"></el-option>
<el-option label="审核驳回" :value="3"></el-option>
<el-option label="审核通过" :value="5"></el-option>
</el-select>
</div>
<div style="padding-left: 10px;"><el-button type="primary" icon="el-icon-search" @click="findList">查询</el-button></div>
<div style="padding: 0px 5px;"><el-button icon="el-icon-refresh-right" @click="reset">重置</el-button></div>
<div class="Create-coures"><el-button type="primary" @click="addNewCourse()" icon="el-icon-plus">新建课程</el-button></div>
</div>
</div>
<!--课程列表内容-->
<div style="">
<div v-infinite-scroll="load" style="overflow:auto;height:1000px" infinite-scroll-distance="50" :infinite-scroll-immediate="false" :infinite-scroll-disabled="disabled">
<div class="uc-course" v-for="(item, idx) in couresList" :key="idx" @click="jumpRouter(item)">
<div class="uc-course-img" style="width: 212px;height:119px">
<course-image :course="item"></course-image>
@@ -39,34 +47,27 @@
</div>
<div class="uc-course-info">
<div class="uc-course-name">
<!-- <span class="uc-course-type1">{{courseType(item.type)}}</span> -->
<span v-if="item.type == 10" class="uc-course-type1">录播</span>
<span v-if="item.type == 20" class="uc-course-type1">录播</span>
<span class="caogao" v-if="item.status == 1">草稿</span>
<span class="daishenhe" v-if="item.status == 2">待审核</span>
<span class="weitongguo" v-if="item.status == 3">审核未通过</span>
<span class="yishenhe" v-if="item.status == 5">已审核</span>
<!-- <a :href="`${webBaseUrl}/course/detail?id=${item.id}`" target="_blank"> {{item.name}}</a> -->
<span style="font-size:18px;color:#333">{{ item.name }}</span>
<el-tooltip class="item" effect="dark" :content="item.name" placement="top-start">
<span style="font-size:18px;color:#333">{{ item.name }}</span>
</el-tooltip>
</div>
<div class="summary-item">
<div>{{ item.summary }}</div>
</div>
<div class="uc-course-text">
<div class="uc-course-item">
上次修改时间{{ item.sysUpdateTime }}
<span type="text" style="margin-left:10px;font-size:14px;cursor: pointer;color:#666" @click.stop="toExamine(item)">查看审核记录</span>
</div>
</div>
<div class="uc-course-btns" style="line-height: 30px;">
<el-link style="display:block" :underline="false" @click.stop="examine(item)" type="primary" v-if="item.status == 1" icon="el-icon-document-checked">提交审核</el-link>
<el-link style="display:block" :underline="false" @click.stop="editCourse(item)" v-if="item.status == 3 || item.status == 4" type="primary" icon="el-icon-edit">编辑</el-link>
<el-link style="display:block" :underline="false" @click.stop="editCourse(item)" v-if="item.status == 1" type="primary" icon="el-icon-edit">去开发</el-link>
<el-link style="display:block" :underline="false" @click.stop="delItem(item)" v-if="item.status == 1" type="danger" icon="el-icon-delete">删除</el-link>
<el-link style="display:block" :underline="false" @click.stop="withdraw(item)" v-if="item.status == 2" type="info" icon="el-icon-delete">撤回</el-link>
<div class="uc-course-item">
<div class="status-item">发布状态{{ item.published ? '发布' : '未发布' }}</div>
<div class="status-item">启停用状态{{ item.enabled ? '启用' : '停用' }}</div>
<div class="status-item">审核状态<el-link :type="getStatusLabel(item.status).type" @click.stop="" :underline="false">{{getStatusLabel(item.status).label}}</el-link></div>
</div>
<div class="btn-container">
<el-link class="btn-item" v-for="(it, idx) in availableActions(item)" :key="idx" :underline="false" type="primary" @click.stop="it.handler(item)"><svg-icon style="margin-right: 5px;font-size:19px;padding-top: 4px;" :icon-class="it.icon"></svg-icon>{{it.label}}</el-link>
</div>
</div>
</div>
<p v-if="loading" class="page-tip">加载中...</p>
<p v-if="noMore" class="page-tip">没有更多了</p>
</div>
<div v-if="couresList.length > 0" style="text-align: center; margin-top:57px;">
<!-- <div v-if="couresList.length > 0" style="text-align: center; margin-top:57px;">
<el-pagination
background
@size-change="handleSizeChange"
@@ -83,29 +84,62 @@
<div v-if="isSearh" class="zan-wu">没有查询到相关内容</div>
<div v-else class="zan-wu">暂无数据</div>
</div>
</div>
</div> -->
<div></div>
<el-dialog title="二维码" :visible.sync="qrCodedialogVisible" width="900px" @close="closeCode" custom-class="g-dialog">
<div>
<el-form size="medium" label-width="100px">
<el-form-item label="二维码" >
<div id="qrcode" ref="qrcode" class="qrcode-img" @mouseenter="showDownloadButton = true" @mouseleave="showDownloadButton = false">
<div v-show="showDownloadButton" @click="downloadQrcode" class="downloadn-container">
<i class="el-icon-download" style="color: #409EFF;font-size:18px;margin-bottom:5px"></i>
<span>下载</span>
</div>
</div>
</el-form-item>
<el-form-item label="链接">
<el-input v-model="copyUrl" readonly class="input-with-select" id="text">
<el-button slot="append" @click="handleCopyUrl">复制</el-button>
</el-input>
</el-form-item>
<el-form-item label="">上述内容兼容PC端与移动端您可按需分享</el-form-item>
</el-form>
</div>
<span slot="footer" class="dialog-footer"><el-button @click="closeCode"> </el-button></span>
</el-dialog>
<!-- TODO 修改展示字段 -->
<el-dialog title="审核记录" :visible.sync="dialogVisible" width="900px" custom-class="g-dialog">
<div>
<el-table max-height="500" border :data="inviteTeacher" style="width: 100%;">
<el-table-column prop="sysCreateBy" label="姓名" width="180"></el-table-column>
<el-table-column prop="type" label="审核状态">
<el-table-column prop="type" label="审核类型">
<template slot-scope="scope">
{{ scope.row.auditState ? '通过' : '不通过' }}
</template>
</el-table-column>
<el-table-column prop="type" label="审核状态">
<template slot-scope="scope">
{{ auditEnum[scope.row.status] }}
</template>
</el-table-column>
<el-table-column prop="auditUser" label="审核人"></el-table-column>
<el-table-column prop="auditTime" label="审核结果"></el-table-column>
<el-table-column prop="auditTime" label="审核时间"></el-table-column>
<el-table-column prop="auditRemark" label="备注"></el-table-column>
<el-table-column prop="auditRemark" label="审核意见"></el-table-column>
</el-table>
</div>
<span slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false"> </el-button></span>
<span slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false"> </el-button></span>
</el-dialog>
<course-form ref="courseForm" @submitSuccess="getList"></course-form>
<course-form ref="courseForm" @submitSuccess="getNewList"></course-form>
</div>
</template>
<script>
import QRCode from 'qrcodejs2';
import courseImage from '@/components/Course/courseImage.vue';
import { mapGetters } from 'vuex';
import studyItem from '@/components/Course/studyItem.vue';
@@ -116,11 +150,53 @@ import apiAudit from '@/api/system/audit.js';
import apiHRBP from '@/api/boe/HRBP.js';
import apiOrg from '@/api/system/organiza.js';
import apiUserBasic from '@/api/boe/userbasic.js';
// 状态权限映射
const STATUS_PERMISSIONS = {
// 未发布状态
unpublished: {
1: ['edit', 'delete'], // 无审核状态
2: ['auditRecord'], // 审核中'withdraw'
3: ['edit', 'delete', 'auditRecord'] // 审核驳回
},
// 已发布状态
published: {
enabled: {
1: ['edit', 'manage', 'auditRecord', 'qrcode'], //'offShelfApply', 'viewCurrent'
2: ['manage', 'auditRecord', 'qrcode'], // withdraw , 'viewCurrent'
3: ['edit', 'manage', 'auditRecord', 'qrcode'], //'offShelfApply', 'viewCurrent'
5: ['edit', 'manage', 'auditRecord', 'qrcode'] //'offShelfApply', 'viewCurrent'
},
disabled: {
// 所有状态在停用时操作一致
1: ['manage', 'auditRecord'], //, 'viewCurrent'
2: ['manage', 'auditRecord'], //, 'viewCurrent'
3: ['manage', 'auditRecord'], //, 'viewCurrent'
5: ['manage', 'auditRecord'] //, 'viewCurrent'
}
}
}
// 操作配置映射表
const ACTION_CONFIG = {
edit: { label: '编辑', handler: 'editCourse', icon: 'edit' },
manage: { label: '管理', handler: 'handleManage', icon: 'manage' },
// withdraw: { label: '撤回', handler: 'handleWithdraw' },
auditRecord: { label: '审核记录', handler: 'toExamine', icon: 'edit' },
qrcode: { label: '二维码', handler: 'handleQrcode', icon: 'ercode' },
// offShelfApply: { label: '下架申请', handler: 'handleOffShelfApply' },
// viewCurrent: { label: '查看当前版本', handler: 'handleViewCurrent', icon: 'detail' },
delete: { label: '删除', handler: 'delItem', icon: 'del' },
}
export default {
name: 'ucStudyIndex',
components: { studyItem, courseForm, courseImage },
computed: {
...mapGetters(['userInfo'])
...mapGetters(['userInfo']),
disabled() {
return this.loading || this.noMore;
},
},
data() {
@@ -130,17 +206,28 @@ export default {
courseType: courseType,
fileBaseUrl: process.env.VUE_APP_FILE_BASE_URL,
count: 0,
params: { keyword: '', type: '', status: '', createUser: '', pageIndex: 1, pageSize: 10, sysCreateAid: '' },
params: { name: '', publish: '', status: '', enabled: '', pageIndex: 0, pageSize: 10 },
couresList: [],
flag: true,
isSearh:false,
qrCodedialogVisible: false,
copyUrl: '',
qrcodeImgUrl: '',
showDownloadButton: false, // 是否显示下载按钮
loading: false,
noMore: false,
auditEnum: {
1: '未审核',
2: '审核不通过',
9: '审核通过'
},
};
},
mounted() {
if(this.$route.query && this.$route.query.open && this.$route.query.open == 'new') {
this.addNewCourse();
}
this.getList();
// this.getList();
},
watch:{
// '$route.query.open':function(val){
@@ -150,6 +237,126 @@ export default {
// }
},
methods: {
load() {
this.loading = true
this.params.pageIndex++
this.getList()
},
getNewList() {
this.couresList = [];
this.params.pageIndex = 1;
this.getList();
},
getStatusLabel(code) {
if (code == '1') {
return {type:'info',label:'-'}
} else if (code == '2') {
return {type:'warning',label:'审核中'}
} else if (code == '3') {
return {type:'danger',label:'审核驳回'}
} else if (code == '5') {
return {type:'success',label:'审核通过'}
}
},
// 获取可用的操作配置
availableActions(item) {
debugger
const { status, published, enabled } = item
let actionKeys = []
if (!published) {
// 未发布状态
actionKeys = STATUS_PERMISSIONS.unpublished[status] || []
} else {
// 已发布状态
const stateKey = enabled ? 'enabled' : 'disabled'
actionKeys = STATUS_PERMISSIONS.published[stateKey][status] || []
}
return actionKeys.map(key => ({
name: key,
label: ACTION_CONFIG[key].label,
handler: this[ACTION_CONFIG[key].handler],
icon: ACTION_CONFIG[key].icon
})).filter(Boolean)
},
handleManage(item) {
sessionStorage.setItem('courseDetail',JSON.stringify(item));
this.$router.push({ path: '/course/coursemanage' });
},
downloadQrcode() {
let img = document.getElementById("qrcode").getElementsByTagName("img")[0];
let canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
let tempUrl = canvas.toDataURL("image/png");
// 创建a标签下载
let link = document.createElement('a'); //创建a标签
link.style.display = 'none'; //使其隐藏
link.download = tempUrl;
link.setAttribute('target', '_blank');
link.href = tempUrl; //赋予文件下载地址
link.setAttribute('download', '二维码.jpg'); //设置下载属性 以及文件名
document.body.appendChild(link); //a标签插至页面中
link.click(); //强制触发a标签事件
document.body.removeChild(link);
},
handleCopyUrl() {
document.getElementById("text").select()
document.execCommand("Copy")
this.$message.success("复制成功")
},
handleQrcode(row) {
this.qrCodedialogVisible = true;
let urlPre=window.location.protocol+'//'+window.location.host;
//动态的地址
//urlPre=urlPre+'/m?returnUrl='+urlPre+'/mobile/pages/login/loading?returnUrl=';
//固定的地址
// let returnUrl=urlPre+'/mobile/pages/login/loading?returnUrl=/pages/study/courseStudy?id='+row.id;
// let mobilePre=urlPre+'/m?returnUrl=';
// this.qrcodeImgUrl = mobilePre+encodeURIComponent(returnUrl);
// this.copyUrl=urlPre+this.webBaseUrl+'/course/studyindex?id='+row.id;
// if(row.type==20){
// this.copyUrl=urlPre+this.webBaseUrl+'/course/detail?id='+row.id;
// }
this.copyUrl = this.qrcodeImgUrl = process.env.VUE_APP_BOE_WEB_URL + '/systemapi/xboe/m/course/manage/redirectDetail?courseId=' + row.id
console.log('qrcodeImgUrl', this.qrcodeImgUrl)
console.log('webBaseUrl', this.webBaseUrl)
// 使用$nextTick确保数据渲染
this.$nextTick(() => {
this.crateQrcode();
});
},
// 生成二维码
crateQrcode() {
let qrcode = new QRCode('qrcode', {
width: 150,
height: 150, // 高度
text: this.qrcodeImgUrl // 二维码内容
// render: 'canvas' // 设置渲染方式(有两种方式 table和canvas默认是canvas
// background: '#f0f'
// foreground: '#ff0'
});
console.log('qrcode', qrcode)
},
// 关闭弹框,清除已经生成的二维码
closeCode() {
this.qrCodedialogVisible = false
// 逐个节点移除防止事件一起移除
let images = document.querySelectorAll('.qrcode-img img');
images.forEach(img => img.remove());
let canvas = document.querySelectorAll('.qrcode-img canvas');
canvas.forEach(canvas => canvas.remove());
},
// 撤回接口
withdraw(item) {
this.$confirm('此操作将撤回审核中的课程, 是否继续?', '提示', {
@@ -165,7 +372,7 @@ export default {
type: 'success',
message: '撤回成功!'
});
this.getList();
this.getNewList();
}
}
});
@@ -178,17 +385,20 @@ export default {
});
},
reset() {
this.params.keyword = '';
this.params.status = '';
this.params.type = '';
this.params.name = ''
this.params.publish = ''
this.params.status = ''
this.params.enabled = ''
this.params.pageIndex = 1;
this.getList();
this.isSearh = false;
// this.getList();
this.isSearh = false;
},
toExamine(row) {
// this.detailType = row.type;
this.dialogVisible = true;
apiAudit.page(1, row.id).then(res => {
apiCourse.auditCourseRecords({
courseId: row.id
}).then(res => {
if (res.status === 200) {
this.inviteTeacher = res.result;
} else {
@@ -271,7 +481,8 @@ export default {
})
},
delItem(row) {
this.$confirm('您确定要删除所选课程吗?', '删除提示', {
console.log('delItem', row);
this.$confirm(`确认删除${row.name}吗?`, '删除提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
@@ -303,7 +514,9 @@ export default {
// this.$store.dispatch("userTrigger", event);
// }
this.getList();
this.getNewList();
} else {
this.$message.success('操作失败!');
}
} catch (error) {
console.log(error);
@@ -339,36 +552,30 @@ export default {
}
},
findList() {
this.params.pageIndex = 1;
this.isSearh = true;
this.getList();
this.getNewList();
},
getList() {
this.params.aid = this.userInfo.aid;
apiCourse.pageList(this.params).then(res => {
this.params.teacherId = this.userInfo.aid;
apiCourse.courseList(this.params).then(res => {
this.loading = false;
if (res.status == 200) {
this.couresList = res.result.list;
this.couresList = [...this.couresList, ...res.result.list];
this.count = res.result.count;
if (this.couresList.length >= this.count){
this.noMore = true;
}
} else {
this.$message.error(res.message);
}
});
},
handleSizeChange(val) {
this.params.pageSize = val;
// this.params.pageIndex = 1;
this.getList();
},
handleCurrentChange(val) {
this.params.pageIndex = val;
this.getList();
},
addNewCourse() {
this.$refs.courseForm.initShow();
},
editCourse(row) {
console.log(row, 'editCourse');
this.$refs.courseForm.initShow(row);
},
lastTabChange(tab, event) {
@@ -379,6 +586,11 @@ export default {
</script>
<style scoped lang="scss">
.page-tip {
margin: 20px auto;
text-align: center;
font-size: 13px;
}
.list-wu{
text-align: center;
margin: 40px;
@@ -422,6 +634,35 @@ export default {
margin-top: 10px;
margin-right: 40px;
}
.qrcode-img {
width: 150px;
height: 150px;
display: block;
position: relative;
.downloadn-container {
position: absolute;
width: 40px;
height: 45px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 99;
background: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
transition: all 0.3s ease;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
cursor: pointer;
span {
color: #409EFF;
display: block;
font-size: 12px;
line-height: 12px;
}
}
}
.uc-course {
cursor: pointer;
display: flex;
@@ -466,9 +707,25 @@ export default {
word-break:break-all;
// font-weight: 700;
}
.uc-course-text {
.uc-course-item {
color: #666;
margin-top: 28px;
// margin-top: 4px;
font-size: 14px;
display: flex;
// flex-wrap: nowrap;
.status-item {
margin-right: 20px;
flex-shrink: 0;
}
}
.btn-container {
margin-top: 10px;
display: flex;
.btn-item {
margin-right: 20px;
}
}
}
.uc-course-btns {

View File

@@ -2,9 +2,9 @@
<div>
<!-- 最大化状态的弹窗 -->
<el-dialog
v-show=" windowState === 'maximized'"
v-if="dialogVisible"
:visible="true"
width="800px"
:close-on-click-modal="false"
:show-close="true"
@close="onClose"
@@ -13,7 +13,6 @@
:append-to-body="true"
:fullscreen="false"
top="10vh"
v-show="windowState === 'maximized'"
v-resizeable
v-draggable
>
@@ -136,10 +135,18 @@ export default {
const headerEl = dialogEl.querySelector('.dialog-title');
if (!headerEl) return;
// 设置初始样式
dialogEl.style.position = 'fixed';
dialogEl.style.top = '100px';
dialogEl.style.left = (window.innerWidth - dialogEl.offsetWidth) / 2 + 'px';
// 检查是否有保存的位置状态
const savedPosition = sessionStorage.getItem('aiCallDialogPosition');
if (savedPosition) {
const { left, top } = JSON.parse(savedPosition);
dialogEl.style.left = left + 'px';
dialogEl.style.top = top + 'px';
} else {
// 设置初始样式
dialogEl.style.position = 'fixed';
dialogEl.style.top = '100px';
dialogEl.style.left = (window.innerWidth - dialogEl.offsetWidth) / 2 + 'px';
}
dialogEl.style.margin = '0';
let isDragging = false;
@@ -176,15 +183,18 @@ export default {
dialogEl.style.left = (startLeft + deltaX) + 'px';
dialogEl.style.top = (startTop + deltaY) + 'px';
};
const stopDrag = () => {
isDragging = false;
// 保存当前位置到 sessionStorage
const currentPosition = {
left: parseInt(dialogEl.style.left),
top: parseInt(dialogEl.style.top)
};
sessionStorage.setItem('aiCallDialogPosition', JSON.stringify(currentPosition));
// 移除全局事件监听
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', stopDrag);
@@ -203,10 +213,20 @@ export default {
const dialogEl = el.querySelector('.el-dialog');
if (!dialogEl) return;
// 设置初始样式
dialogEl.style.position = 'fixed';
dialogEl.style.top = '100px';
dialogEl.style.left = (window.innerWidth - dialogEl.offsetWidth) / 2 + 'px';
// 检查是否有保存的尺寸状态
const savedSize = sessionStorage.getItem('aiCallDialogSize');
if (savedSize) {
const { width, height, left, top } = JSON.parse(savedSize);
dialogEl.style.width = width + 'px';
dialogEl.style.height = height + 'px';
dialogEl.style.left = left + 'px';
dialogEl.style.top = top + 'px';
} else {
// 设置初始样式
dialogEl.style.position = 'fixed';
dialogEl.style.top = '100px';
dialogEl.style.left = (window.innerWidth - dialogEl.offsetWidth) / 2 + 'px';
}
// 创建拖拽手柄
const createHandle = (direction) => {
@@ -335,65 +355,93 @@ export default {
const deltaX = event.clientX - startX;
const deltaY = event.clientY - startY;
let newWidth, newHeight, newLeft, newTop;
switch (resizeDirection) {
case 'right':
dialogEl.style.width = Math.max(400, startWidth + deltaX) + 'px';
newWidth = Math.max(400, startWidth + deltaX);
dialogEl.style.width = newWidth + 'px';
break;
case 'left':
const newWidth = Math.max(400, startWidth - deltaX);
newWidth = Math.max(400, startWidth - deltaX);
newLeft = startLeft + startWidth - newWidth;
dialogEl.style.width = newWidth + 'px';
dialogEl.style.left = (startLeft + startWidth - newWidth) + 'px';
dialogEl.style.left = newLeft + 'px';
break;
case 'bottom':
dialogEl.style.height = Math.max(600, startHeight + deltaY) + 'px';
newHeight = Math.max(600, startHeight + deltaY);
dialogEl.style.height = newHeight + 'px';
break;
case 'top':
// 当窗口高度达到最小值时,不再调整高度和位置
if (startHeight - deltaY >= 600) {
dialogEl.style.height = (startHeight - deltaY) + 'px';
dialogEl.style.top = (startTop + deltaY) + 'px';
newHeight = startHeight - deltaY;
newTop = startTop + deltaY;
dialogEl.style.height = newHeight + 'px';
dialogEl.style.top = newTop + 'px';
}
break;
case 'bottom-right':
dialogEl.style.width = Math.max(400, startWidth + deltaX) + 'px';
dialogEl.style.height = Math.max(600, startHeight + deltaY) + 'px';
newWidth = Math.max(400, startWidth + deltaX);
newHeight = Math.max(600, startHeight + deltaY);
dialogEl.style.width = newWidth + 'px';
dialogEl.style.height = newHeight + 'px';
break;
case 'bottom-left':
const newWidthBL = Math.max(400, startWidth - deltaX);
dialogEl.style.width = newWidthBL + 'px';
dialogEl.style.left = (startLeft + startWidth - newWidthBL) + 'px';
dialogEl.style.height = Math.max(600, startHeight + deltaY) + 'px';
newWidth = Math.max(400, startWidth - deltaX);
newHeight = Math.max(600, startHeight + deltaY);
newLeft = startLeft + startWidth - newWidth;
dialogEl.style.width = newWidth + 'px';
dialogEl.style.left = newLeft + 'px';
dialogEl.style.height = newHeight + 'px';
break;
case 'top-right':
// 当窗口高度达到最小值时,不再调整高度和位置
if (startHeight - deltaY >= 600) {
dialogEl.style.height = (startHeight - deltaY) + 'px';
dialogEl.style.top = (startTop + deltaY) + 'px';
newHeight = startHeight - deltaY;
newTop = startTop + deltaY;
newWidth = Math.max(400, startWidth + deltaX);
dialogEl.style.height = newHeight + 'px';
dialogEl.style.top = newTop + 'px';
dialogEl.style.width = newWidth + 'px';
}
dialogEl.style.width = Math.max(400, startWidth + deltaX) + 'px';
break;
case 'top-left':
// 当窗口高度达到最小值时,不再调整高度和位置
if (startHeight - deltaY >= 600) {
dialogEl.style.height = (startHeight - deltaY) + 'px';
dialogEl.style.top = (startTop + deltaY) + 'px';
newHeight = startHeight - deltaY;
newTop = startTop + deltaY;
newWidth = Math.max(400, startWidth - deltaX);
newLeft = startLeft + startWidth - newWidth;
dialogEl.style.height = newHeight + 'px';
dialogEl.style.top = newTop + 'px';
dialogEl.style.width = newWidth + 'px';
dialogEl.style.left = newLeft + 'px';
}
const newWidthTL = Math.max(400, startWidth - deltaX);
dialogEl.style.width = newWidthTL + 'px';
dialogEl.style.left = (startLeft + startWidth - newWidthTL) + 'px';
break;
}
let doc = document.querySelector('.welcome-message')
let sendBox = document.querySelector('.input-area-wrapper');
// sendBox 的高度
doc.style.height = `calc(${dialogEl.style.height} - ${sendBox.offsetHeight}px - 120px)`;
if (doc && sendBox) {
doc.style.height = `calc(${dialogEl.style.height} - ${sendBox.offsetHeight}px - 120px)`;
}
};
const stopResize = () => {
isResizing = false;
resizeDirection = '';
// 保存当前尺寸和位置到 sessionStorage
const currentSize = {
width: parseInt(dialogEl.style.width),
height: parseInt(dialogEl.style.height),
left: parseInt(dialogEl.style.left),
top: parseInt(dialogEl.style.top)
};
sessionStorage.setItem('aiCallDialogSize', JSON.stringify(currentSize));
// 移除全局事件监听
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', stopResize);
@@ -436,26 +484,69 @@ export default {
}
],
suggestions:[],
isAutoScroll: true // 是否自动滚动
isAutoScroll: true, // 是否自动滚动
// 添加一个标志位,用于标识组件是否已经初始化完成
isComponentReady: false
}
},
mounted() {
// 组件挂载完成后,标记为已准备就绪
this.$nextTick(() => {
this.isComponentReady = true;
});
},
watch: {
dialogVisible(newVal) {
if(newVal){
this.$nextTick(() => {
let doc = document.querySelector('.welcome-message')
let sendBox = document.querySelector('.input-area-wrapper');
doc.style.height = `calc(600px - ${sendBox.offsetHeight}px - 120px)`;
});
}
dialogVisible: {
handler(newVal) {
if (newVal) {
this.$nextTick(() => {
// 获取对话框元素
const dialogEl = document.querySelector('.case-expert-dialog .el-dialog');
if (dialogEl) {
// 检查是否有保存的尺寸状态
const savedSize = sessionStorage.getItem('aiCallDialogSize');
if (savedSize) {
const { width, height, left, top } = JSON.parse(savedSize);
dialogEl.style.width = width + 'px';
dialogEl.style.height = height + 'px';
dialogEl.style.left = left + 'px';
dialogEl.style.top = top + 'px';
}
// 移除之前的逻辑,因为现在通过事件机制处理状态恢复
// 检查是否有保存的位置状态
const savedPosition = sessionStorage.getItem('aiCallDialogPosition');
if (savedPosition) {
const { left, top } = JSON.parse(savedPosition);
dialogEl.style.left = left + 'px';
dialogEl.style.top = top + 'px';
}
}
let doc = document.querySelector('.welcome-message')
let sendBox = document.querySelector('.input-area-wrapper');
// 只有在没有保存的尺寸状态时才使用默认值
if (doc && sendBox) {
const savedSize = sessionStorage.getItem('aiCallDialogSize');
if (!savedSize) {
doc.style.height = `calc(600px - ${sendBox.offsetHeight}px - 120px)`;
} else {
const { height } = JSON.parse(savedSize);
doc.style.height = `calc(${height}px - ${sendBox.offsetHeight}px - 120px)`;
}
}
});
}
},
immediate: true
},
messageList: {
handler() {
this.$nextTick(() => {
this.scrollToBottom();
});
// 只有在组件准备就绪后才执行滚动操作
if (this.isComponentReady) {
this.$nextTick(() => {
this.scrollToBottom();
});
}
},
deep: true
}
@@ -473,6 +564,9 @@ closeMinimizedWindow() {
},
onClose() {
console.log('关闭弹窗')
// 清除保存的状态
sessionStorage.removeItem('aiCallDialogSize');
sessionStorage.removeItem('aiCallDialogPosition');
this.$emit('close')
// 可以在这里执行其他逻辑
},
@@ -522,6 +616,9 @@ closeMinimizedWindow() {
},500)
},
startNewConversation() {
// 重置对话时,先标记组件为未准备就绪状态
this.isComponentReady = false;
this.messageList = [
{
isBot: true,
@@ -533,6 +630,11 @@ closeMinimizedWindow() {
];
this.AIContent = '';
this.isLoading = false;
// 在下一个 tick 中重新标记为准备就绪
this.$nextTick(() => {
this.isComponentReady = true;
});
},
// 处理滚动事件
@@ -641,7 +743,7 @@ closeMinimizedWindow() {
flex-direction: column;
align-items: flex-start;
margin-bottom: 10px;
//height: 200px;
height: 400px;
//flex:1;
overflow-y: auto;

View File

@@ -30,8 +30,13 @@
<!-- <div class="course-title-right"> -->
<!-- <interactBar :readonly="!stuStusts || stuStusts==0" :type="1" :data="courseInfo" :comments="false" :views="false"></interactBar> -->
<!-- </div> -->
<div class="label-div">
<!-- <div class="label-div">
<el-tag class="label-item" effect="plain" v-for="(item,tagIdx) in tagArray" :key="tagIdx">{{item}}</el-tag>
</div>-->
<div class="label-div">
<div v-for="(item, tagIdx) in tagArray" :key="tagIdx" class="keyword-tag">
{{ item }}
</div>
</div>
<div>
<div class="study-count">{{courseInfo.studys}}人学习</div>
@@ -420,7 +425,7 @@ export default {
.course-title{
position: relative;
height: 60px;
height: auto;
display: flex;
justify-content: space-between;
.title {
@@ -453,18 +458,43 @@ export default {
padding: 24px 24px 5px 24px;
// margin-right: 361px;
.study-count {
margin-top: 10px;
margin-top: 30px;
font-size: 16px;
color: #444444;
}
.label-div {
/*.label-div {
margin: 5px 0;
min-height: 20px;
.label-item {
padding: 0 7px;
padding: 0px 8px;
margin-top: 5px;
float: left;
line-height: 24px;
font-size: 12px;
border-radius: 2px;
margin-right: 8px;
margin-bottom: 0px;
color: #2C68FF;
height: 24px;
background: rgba(44, 104, 255, 0.06);
border: none; // 或者使用 border-color: transparent;
}
}*/
.label-div {
margin: 5px 0;
min-height: 20px;
.keyword-tag {
padding: 0px 10px;
margin-top: 7px;
float: left;
line-height: 24px;
font-size: 12px;
border-radius: 2px;
margin-right: 10px;
color: #2C68FF;
height: 24px;
background: rgba(44, 104, 255, 0.06);
}
}
::v-deep .el-rate__icon {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -123,13 +123,20 @@
<div>
<div class="course-interact">
<div class="score" style="display: flex;">
<div v-if="!scoreInfo.has" style="margin-left:10px;cursor: pointer;padding-top:18px">
<div v-if="!scoreInfo.has" style="margin-left:10px;cursor: pointer;padding-top:10px;display: flex;align-items: center;">
<!-- <el-popover placement="top" width="300" trigger="hover"> -->
<!-- <div style="text-align:center;line-height:50px;padding:20px 0px">
<div style="padding-top:30px"><el-button @click="addScore">提交评分</el-button></div>
</div> -->
<el-rate v-model="scoreInfo.score" @change="addScore"></el-rate>
<p style="margin-right:10px">告诉我们您的喜欢程度</p>
<el-rate v-model="scoreInfo.score" @change="showConfirmScore" :allow-half="true"></el-rate>
<div v-if="isShowScoreConfirm">
<span class="score-text">{{ toScore(scoreInfo.score) }}</span>
<span style="font-size: 18px;"></span>
<el-button style="margin-left:10px" type="primary" size="mini" @click="addScore" >确定</el-button>
<el-button size="mini" @click="handleCancelScore">取消</el-button>
</div>
<!-- <el-tag class="ref-score" slot="reference">去评分</el-tag> -->
<!-- </el-popover> -->
</div>
@@ -188,7 +195,7 @@
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose">
<el-submenu :index="item.section.id">
<el-submenu :index="item.section.id" v-if="catalogTree.length > 1">
<template slot="title">
<div style="display: flex;justify-content: space-between;">
<div style="width: 240px;font-weight: 700;font-size: 16px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;" :title="item.section.name">{{item.section.name}}</div>
@@ -215,6 +222,27 @@
</div>
</el-menu-item-group>
</el-submenu>
<div v-else>
<el-menu-item-group v-for="(ele, i) in item.children" :key="i">
<div class="units-info" :class="{'units-active':contentData.id == ele.id}" @click="showRes(ele,i,index,item)">
<el-menu-item :index="ele.id" style="padding: 0;padding-left: 10px;">
<div style="display: flex;justify-content: space-between;">
<div style="width: 200px;font-size: 16px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;" :title="ele.contentName">{{i+1}}. {{ ele.contentName }}</div>
<div>
<span v-if="contentData.id == ele.id" style="color: #387DF7;font-size: 14px;margin-right: 4px;">学习中</span>
<!-- <img v-if="contentData.id == ele.id" :src="`${webBaseUrl}/images/playicon.png`" alt=""> -->
<img v-if="contentData.id == ele.id && ele.status == 9" style="width: 16px;height: 16px;" src="@/assets/images/over.png" alt="">
<img v-if="contentData.id == ele.id && ele.status == 0" style="width: 16px;height: 16px;" src="@/assets/images/nowNot.png" alt="">
<img v-if="contentData.id == ele.id && (ele.status != 9&&ele.status != 0)" style="width: 16px;height: 16px;" src="@/assets/images/ban1.png" alt="">
<img v-if="contentData.id != ele.id && ele.status == 9" style="width: 16px;height: 16px;" src="@/assets/images/notNew.png" alt="">
<img v-if="contentData.id != ele.id && ele.status == 0" style="width: 16px;height: 16px;" src="@/assets/images/not.png" alt="">
<img v-if="contentData.id != ele.id && (ele.status != 9&&ele.status != 0)" style="width: 16px;height: 16px;" src="@/assets/images/newBan.png" alt="">
</div>
</div>
</el-menu-item>
</div>
</el-menu-item-group>
</div>
</el-menu>
</div>
<!-- <div v-for="(item, index) in catalogTree" :key="index" :name="index">
@@ -390,6 +418,7 @@
},
data() {
return {
isShowScoreConfirm: false,
protocolDialogVisible: false,
tentative: false,
isContentTypeTwo: null,
@@ -533,6 +562,13 @@
}
},
methods: {
handleCancelScore() {
this.isShowScoreConfirm = false;
this.scoreInfo.score = 5
},
showConfirmScore() {
this.isShowScoreConfirm = true;
},
handleOpen(key,path){
if(this.isFalse){
this.defaultOpeneds = [key]