This commit is contained in:
670788339
2025-11-10 18:49:54 +08:00
parent 702255d9d0
commit 8228b33cb0
3 changed files with 470 additions and 212 deletions

View File

@@ -440,6 +440,12 @@ const queryCrowd=function(query){
const ids=function (data){ const ids=function (data){
return ajax.postJson('/xboe/m/course/manage/ids',data); return ajax.postJson('/xboe/m/course/manage/ids',data);
} }
const saveTip = function() {
return ajax.postJson('/xboe/m/course/manage/saveTip');
}
export default { export default {
saveBase, saveBase,
submitCourse, submitCourse,
@@ -482,6 +488,7 @@ export default {
exportCourseAudit, exportCourseAudit,
exportCourse, exportCourse,
queryCrowd, queryCrowd,
ids ids,
saveTip
} }

View File

@@ -41,6 +41,41 @@
<el-button @click="toInputCourse()" type="primary">确定</el-button> <el-button @click="toInputCourse()" type="primary">确定</el-button>
</span> --> </span> -->
</el-dialog> </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">1</div>
<div class="step-content">
<div class="step-title">设置内容分类</div>
<div class="step-desc">当选择内容分类后其关联标签将在下拉列表中优先显示</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-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;"> <el-form label-width="100px" size="small" class="wei-from" style="min-height: 600px;">
@@ -64,18 +99,26 @@
</span> </span>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="内容分类" required> <el-form-item label="内容分类" required class="guidance-highlight" data-step="1">
<el-cascader <el-cascader
placeholder="选择内容分类" placeholder="选择内容分类"
style="width: 100%;" style="width: 100%;"
clearable clearable
v-model="sysTypeList" v-model="sysTypeList"
:props="{ value: 'id', label: 'name' }" :props="{ value: 'id', label: 'name' }"
:options="sysTypeListMap"></el-cascader> :options="sysTypeListMap"
@focus="onContentTypeFocus"
@change="onContentTypeChange">
</el-cascader>
</el-form-item> </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="2">
<courseTag ref="courseTag" :courseId="curCourseId" :sysTypeList="sysTypeList"
:initialTags="courseTags" @change="handleTagsChange"
@focus="onTagFocus">
</courseTag>
</el-form-item> </el-form-item>
<el-form-item label="资源归属" required> <el-form-item label="资源归属" required>
<el-input placeholder="请选择" v-model="orgName" > <el-input placeholder="请选择" v-model="orgName" >
<el-button v-if="identity==3 || identity==5" @click="showChooseOrg()" slot="append" icon="el-icon-search">选择</el-button> <el-button v-if="identity==3 || identity==5" @click="showChooseOrg()" slot="append" icon="el-icon-search">选择</el-button>
@@ -221,14 +264,16 @@
</div> </div>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="内容分类" required> <el-form-item label="内容分类" required class="guidance-highlight" data-step="1">
<el-cascader <el-cascader
placeholder="选择内容分类" placeholder="选择内容分类"
style="width: 100%;" style="width: 100%;"
clearable clearable
v-model="sysTypeList" v-model="sysTypeList"
:props="{ value: 'id', label: 'name' }" :props="{ value: 'id', label: 'name' }"
:options="sysTypeListMap"> :options="sysTypeListMap"
@focus="onContentTypeFocus"
@change="onContentTypeChange">
</el-cascader> </el-cascader>
</el-form-item> </el-form-item>
@@ -256,9 +301,13 @@
</el-select> --> </el-select> -->
<choice :teacherValue="teacherValues" @getTeacherList="getTeacherList"></choice> <choice :teacherValue="teacherValues" @getTeacherList="getTeacherList"></choice>
</el-form-item> </el-form-item>
<el-form-item label="标签" required> <el-form-item label="标签" required class="guidance-highlight" data-step="2">
<courseTag ref="courseTag" :courseId="curCourseId" :sysTypeList="sysTypeList" :initialTags="courseTags" @change="handleTagsChange"></courseTag> <courseTag ref="courseTag" :courseId="curCourseId" :sysTypeList="sysTypeList"
:initialTags="courseTags" @change="handleTagsChange"
@focus="onTagFocus">
</courseTag>
</el-form-item> </el-form-item>
<el-form-item label="关键字"> <el-form-item label="关键字">
<el-input v-model.trim="keywords" maxlength="100" @keyup.enter.native="changeKeywords" placeholder="请输入关键字"></el-input> <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)"> <el-tag v-for="(tag,index) in tips" size="small" :key="index" closable type="info" @close="closeKeywordsTag(tag,index)">
@@ -536,7 +585,13 @@ export default {
dlgShow: false dlgShow: false
}, },
rightTypeId: {}, rightTypeId: {},
catalogSortDialogShow: false catalogSortDialogShow: false,
// 蒙层引导相关数据
showGuidance: false,
currentStep: 1,
highlightStyle: {},
guidanceElements: [],
isFirstCreate: false, // 标记是否为首次创建
}; };
}, },
created() { created() {
@@ -849,7 +904,7 @@ export default {
}, },
resetCurCourseContent() { 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) { // chooseCourseType(item, idx, open) {
// this.courseChooseId = item.id; // this.courseChooseId = item.id;
@@ -897,6 +952,12 @@ export default {
this.courseInfo = rs.result; this.courseInfo = rs.result;
this.curCourseId = this.courseInfo.id this.curCourseId = this.courseInfo.id
console.log('保存课程成功',this.curCourseId) console.log('保存课程成功',this.curCourseId)
console.log('isTip ',this.courseInfo.isTip)
if(this.courseInfo.isTip){
// 检查是否为首次创建,显示引导
this.checkAndShowGuidance();
}
if (this.courseChooseId == 1) { if (this.courseChooseId == 1) {
this.weike.dlgShow = true; this.weike.dlgShow = true;
} else { } else {
@@ -904,12 +965,102 @@ export default {
} }
this.$nextTick(() => { this.$nextTick(() => {
this.initTagComponent(); this.initTagComponent();
// 如果显示引导,初始化高亮元素
if (this.showGuidance) {
this.$nextTick(() => {
this.initGuidanceElements();
this.highlightCurrentStep();
});
}
}); });
} else { } else {
this.$message.error(rs.message); 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) {
const rect = currentElement.getBoundingClientRect();
this.highlightStyle = {
top: `${rect.top}px`,
left: `${rect.left}px`,
width: `${rect.width}px`,
height: `${rect.height}px`,
};
// 滚动到可见区域
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() { initTagComponent() {
if (this.$refs.courseTag) { if (this.$refs.courseTag) {
@@ -1704,13 +1855,13 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.red-tip{ .red-tip{
margin-top: 8px; margin-top: 8px;
color: red; color: red;
font-size: 14px; font-size: 14px;
float: left; float: left;
font-weight: 600; font-weight: 600;
} }
::v-deep .wei-from{ ::v-deep .wei-from{
.el-form-item{ .el-form-item{
margin-bottom: 10px; margin-bottom: 10px;
@@ -1807,7 +1958,7 @@ export default {
</style> </style>
<style lang="scss"> <style lang="scss">
.cusprompt{ .cusprompt{
padding: 20px 30px; padding: 20px 30px;
.el-message-box__content{ .el-message-box__content{
margin-top: 30px; margin-top: 30px;
@@ -1815,5 +1966,101 @@ 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 {
background: white;
border-radius: 8px;
padding: 24px;
max-width: 500px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
z-index: 10000;
}
.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;
}
</style> </style>

View File

@@ -1,6 +1,5 @@
<template> <template>
<div class="tag-container"> <div class="tag-container" @click="handleContainerClick">
<el-select style="width: 100%;" <el-select style="width: 100%;"
v-model="selectedTags" v-model="selectedTags"
multiple multiple
@@ -142,6 +141,11 @@ export default {
if (this.sysTypeList.length > 0) { if (this.sysTypeList.length > 0) {
await this.doSearch(''); await this.doSearch('');
} }
this.$emit('focus');
},
handleContainerClick() {
// 容器点击时也触发焦点事件
this.$emit('focus');
}, },
// 新增:重置标签状态的方法 // 新增:重置标签状态的方法
resetTagState() { resetTagState() {