Compare commits
51 Commits
251114-fea
...
251114-fea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b95b990d0 | ||
|
|
018d9af1d8 | ||
|
|
b26336604a | ||
|
|
d20f8c1724 | ||
|
|
c462eb00a2 | ||
|
|
0a74f1a064 | ||
|
|
afbcbcec97 | ||
|
|
4eef13943d | ||
|
|
c45548b629 | ||
|
|
40edd09a87 | ||
|
|
81962afb02 | ||
|
|
b9f69735cf | ||
|
|
54f496f962 | ||
|
|
0a11f6cff9 | ||
|
|
e4b0591509 | ||
|
|
d368b45739 | ||
|
|
ce00a46869 | ||
|
|
3dc0bf3949 | ||
|
|
59588c1372 | ||
|
|
bb453a0200 | ||
|
|
79111b9e6b | ||
|
|
6922882111 | ||
|
|
e217e107bc | ||
|
|
a26dc0aa2b | ||
|
|
c1d6021ef8 | ||
|
|
c74213d63a | ||
|
|
3be811cd8e | ||
|
|
fa48bdf266 | ||
|
|
1dbbb488d6 | ||
|
|
bf44d562dd | ||
|
|
d207d4eee6 | ||
|
|
2264a8a6a7 | ||
|
|
148ab93b07 | ||
|
|
7a9063ffae | ||
|
|
caa9ee848e | ||
|
|
47036beb64 | ||
|
|
632aaf8e75 | ||
|
|
436bb99814 | ||
|
|
5d2d8fcac6 | ||
|
|
4277f985b0 | ||
|
|
0bbbbeb0ee | ||
|
|
ea6ed74746 | ||
|
|
2389b06f28 | ||
|
|
523b5fd801 | ||
|
|
32874683e2 | ||
|
|
6c0d1de13d | ||
|
|
c4fdebe811 | ||
|
|
d6132738d5 | ||
|
|
b2654f5501 | ||
|
|
be730deef2 | ||
|
|
a6c389b80b |
@@ -1,8 +1,7 @@
|
||||
<template>
|
||||
<div id="app" style="width: 100vw">
|
||||
<div id="app">
|
||||
<keep-alive :include="['case']">
|
||||
<router-view />
|
||||
12312
|
||||
</keep-alive>
|
||||
<!-- 添加AI Call组件 -->
|
||||
<AICall
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*
|
||||
**/
|
||||
import ajax from '@/utils/xajax.js'
|
||||
import ajax2 from '../unionAjax.js';
|
||||
|
||||
/**
|
||||
* 保存课程基本信息,新增和更新都是此方式
|
||||
@@ -42,8 +43,8 @@ import ajax from '@/utils/xajax.js'
|
||||
]
|
||||
}
|
||||
*/
|
||||
const saveBase = function(data) {
|
||||
return ajax.postJson('/xboe/m/course/manage/save', data);
|
||||
const saveBase = function (data) {
|
||||
return ajax.postJson('/xboe/m/course/manage/save', data);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,39 +53,39 @@ const saveBase = function(data) {
|
||||
* 仅仅是保存课程信息,不包括教师信息
|
||||
* @param {Object} data
|
||||
*/
|
||||
const saveOnlyCourse = function(data) {
|
||||
return ajax.postJson('/xboe/m/course/manage/save-only-course', data);
|
||||
const saveOnlyCourse = function (data) {
|
||||
return ajax.postJson('/xboe/m/course/manage/save-only-course', data);
|
||||
}
|
||||
|
||||
/**提交课程*/
|
||||
const submitCourse = function(data) {
|
||||
return ajax.postJson('/xboe/m/course/manage/submit', data);
|
||||
const submitCourse = function (data) {
|
||||
return ajax.postJson('/xboe/m/course/manage/submit', data);
|
||||
}
|
||||
|
||||
/**撤销已提交审核的课程*/
|
||||
const revokeSubmit = function(id) {
|
||||
return ajax.post('/xboe/m/course/manage/revoke', {id});
|
||||
const revokeSubmit = function (id) {
|
||||
return ajax.post('/xboe/m/course/manage/revoke', { id });
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制课程
|
||||
* @param {Object}
|
||||
*/
|
||||
const copyCourse = function(data) {
|
||||
return ajax.post('/xboe/m/course/manage/copy',data);
|
||||
const copyCourse = function (data) {
|
||||
return ajax.post('/xboe/m/course/manage/copy', data);
|
||||
}
|
||||
/*
|
||||
查询课程是否有重复名称
|
||||
*/
|
||||
const isRedoName=function(){
|
||||
return ajax.get('/xboe/m/course/manage/isRedoName');
|
||||
const isRedoName = function () {
|
||||
return ajax.get('/xboe/m/course/manage/isRedoName');
|
||||
}
|
||||
/*
|
||||
查询当前添加课程是否已有
|
||||
courseName 要添加的课程姓名
|
||||
*/
|
||||
const isCourseName=function(courseName,courseId){
|
||||
return ajax.get(`/xboe/m/course/manage/isCourseName?courseName=${courseName}&courseId=${courseId}`);
|
||||
const isCourseName = function (courseName, courseId) {
|
||||
return ajax.get(`/xboe/m/course/manage/isCourseName?courseName=${courseName}&courseId=${courseId}`);
|
||||
}
|
||||
/**
|
||||
* 查询修改日志,列表,不分页
|
||||
@@ -95,16 +96,16 @@ const isCourseName=function(courseName,courseId){
|
||||
name: 修改人
|
||||
}
|
||||
*/
|
||||
const findUpdateLogs = function(params) {
|
||||
return ajax.post('/xboe/m/course/manage/upldate-logs',params);
|
||||
const findUpdateLogs = function (params) {
|
||||
return ajax.post('/xboe/m/course/manage/upldate-logs', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id获取修改的详细信息
|
||||
* @param {Object} id
|
||||
*/
|
||||
const getUpdateLog = function(id) {
|
||||
return ajax.get('/xboe/m/course/manage/upldate-log-detail?id='+id);
|
||||
const getUpdateLog = function (id) {
|
||||
return ajax.get('/xboe/m/course/manage/upldate-log-detail?id=' + id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,8 +140,8 @@ const getUpdateLog = function(id) {
|
||||
]
|
||||
}
|
||||
*/
|
||||
const saveContent = function(data) {
|
||||
return ajax.postJson('/xboe/m/course/content/save', data);
|
||||
const saveContent = function (data) {
|
||||
return ajax.postJson('/xboe/m/course/content/save', data);
|
||||
}
|
||||
|
||||
|
||||
@@ -159,18 +160,18 @@ const saveContent = function(data) {
|
||||
* ]
|
||||
* @returns
|
||||
*/
|
||||
const updateContentOrders = function(cid,items) {
|
||||
return ajax.postJson('/xboe/m/course/content/update-orders/'+cid, items);
|
||||
const updateContentOrders = function (cid, items) {
|
||||
return ajax.postJson('/xboe/m/course/content/update-orders/' + cid, items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 课程的详细信息
|
||||
* @param {String} id
|
||||
*/
|
||||
const detail = function(id) {
|
||||
return ajax.get('/xboe/m/course/manage/detail?id=' + id);
|
||||
const detail = function (id) {
|
||||
return ajax.get('/xboe/m/course/manage/detail?id=' + id);
|
||||
}
|
||||
const getDictIds = function(pid,type) {
|
||||
const getDictIds = function (pid, type) {
|
||||
return ajax.get(`/xboe/m/course/manage/getDictIds?pid=${pid}&type=${type}`);
|
||||
}
|
||||
/**
|
||||
@@ -181,8 +182,8 @@ const getDictIds = function(pid,type) {
|
||||
name:''
|
||||
}
|
||||
*/
|
||||
const updateContentName = function(data) {
|
||||
return ajax.post('/xboe/m/course/content/update-name', data);
|
||||
const updateContentName = function (data) {
|
||||
return ajax.post('/xboe/m/course/content/update-name', data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,8 +195,8 @@ const updateContentName = function(data) {
|
||||
erasable:是否物理删除,此值是课程信息中系统带过来的字段,直接使用它就可以了
|
||||
}
|
||||
*/
|
||||
const delContent = function(data) {
|
||||
return ajax.post('/xboe/m/course/content/delete', data);
|
||||
const delContent = function (data) {
|
||||
return ajax.post('/xboe/m/course/content/delete', data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,40 +208,40 @@ const delContent = function(data) {
|
||||
parentId: 上级id。如果没有可以填“-1”字符串
|
||||
orderIndex:显示顺序,顺序索引,整数
|
||||
*/
|
||||
const saveSection = function(data) {
|
||||
return ajax.post('/xboe/m/course/content/save-section', data);
|
||||
const saveSection = function (data) {
|
||||
return ajax.post('/xboe/m/course/content/save-section', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除章节目录,注意只有目录下没有学习内容时才允许删除
|
||||
* @param {Object} data
|
||||
*/
|
||||
const delSection = function(id) {
|
||||
return ajax.post('/xboe/m/course/content/delete-section?id=' + id);
|
||||
const delSection = function (id) {
|
||||
return ajax.post('/xboe/m/course/content/delete-section?id=' + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据课程学习内容的id。获取作业信息,只有学习内容是作业时才会有信息
|
||||
* @param {Object} ccid
|
||||
*/
|
||||
const getHomework = function(ccid) {
|
||||
return ajax.post('/xboe/m/course/content/homework?ccid=' + ccid);
|
||||
const getHomework = function (ccid) {
|
||||
return ajax.post('/xboe/m/course/content/homework?ccid=' + ccid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据课程学习内容的id。获取考试信息,只有学习内容是考试时才会有信息
|
||||
* @param {Object} ccid
|
||||
*/
|
||||
const getExam = function(ccid) {
|
||||
return ajax.post('/xboe/m/course/content/exam?ccid=' + ccid);
|
||||
const getExam = function (ccid) {
|
||||
return ajax.post('/xboe/m/course/content/exam?ccid=' + ccid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据课程学习内容的id。获取评估信息,评估内容可以获取
|
||||
* @param {Object} ccid
|
||||
*/
|
||||
const getAssess = function(ccid) {
|
||||
return ajax.post('/xboe/m/course/content/assess?ccid=' + ccid);
|
||||
const getAssess = function (ccid) {
|
||||
return ajax.post('/xboe/m/course/content/assess?ccid=' + ccid);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,21 +266,21 @@ const getAssess = function(ccid) {
|
||||
name 课程名称
|
||||
|
||||
*/
|
||||
const pageList = function(query) {
|
||||
return ajax.post('/xboe/m/course/manage/pagelist', query);
|
||||
const pageList = function (query) {
|
||||
return ajax.post('/xboe/m/course/manage/pagelist', query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 课程管理分页查询(新)
|
||||
* @param {Object} query
|
||||
*/
|
||||
const managePage = function(query) {
|
||||
return ajax.postJson('/xboe/m/course/manage/page', query);
|
||||
const managePage = function (query) {
|
||||
return ajax.postJson('/xboe/m/course/manage/page', query);
|
||||
}
|
||||
|
||||
|
||||
/**计算待审核课程*/
|
||||
const countWaitAudit = function() {
|
||||
const countWaitAudit = function () {
|
||||
return ajax.get('/xboe/m/course/manage/wait-audit-num');
|
||||
}
|
||||
|
||||
@@ -288,8 +289,8 @@ const countWaitAudit = function() {
|
||||
* 当前用户需要审核的课程列表
|
||||
* @param {Object} query 同pageList
|
||||
*/
|
||||
const auditList = function(query) {
|
||||
return ajax.post('/xboe/m/course/manage/audit-pagelist', query);
|
||||
const auditList = function (query) {
|
||||
return ajax.post('/xboe/m/course/manage/audit-pagelist', query);
|
||||
}
|
||||
|
||||
|
||||
@@ -297,8 +298,8 @@ const auditList = function(query) {
|
||||
* 【已移到courseAudit中】
|
||||
* 教师需要审核的课程列表
|
||||
*/
|
||||
const teacherAuditList = function(query) {
|
||||
return ajax.post('/xboe/m/course/audit/teacher-course', query);
|
||||
const teacherAuditList = function (query) {
|
||||
return ajax.post('/xboe/m/course/audit/teacher-course', query);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,16 +308,16 @@ const teacherAuditList = function(query) {
|
||||
* @param {Object} data
|
||||
* {courseId:课程id,teacherId:指定的审核人教师的id,teacherName:教师名称,remark:备注}
|
||||
*/
|
||||
const auditAppoint = function(data) {
|
||||
return ajax.post('/xboe/m/course/audit/appoint', data);
|
||||
const auditAppoint = function (data) {
|
||||
return ajax.post('/xboe/m/course/audit/appoint', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审核信息,上面教师点击审核课程时,用于查询,上面“转审”时,用户填写的备注信息
|
||||
* @param {courseId:'课程id',teacherId:'可以不填写,系统会查询当前人'} data
|
||||
*/
|
||||
const getAuditInfo = function(data) {
|
||||
return ajax.post('/xboe/m/course/audit/infos', data);
|
||||
const getAuditInfo = function (data) {
|
||||
return ajax.post('/xboe/m/course/audit/infos', data);
|
||||
}
|
||||
|
||||
|
||||
@@ -324,56 +325,56 @@ const getAuditInfo = function(data) {
|
||||
* 管理员的课程审核处理
|
||||
* @param {Object} query {id:课程id,title:课程的名称, Boolean pass 是否通过,remark 备注}
|
||||
*/
|
||||
const audit = function(data) {
|
||||
return ajax.post('/xboe/m/course/manage/audit', data);
|
||||
const audit = function (data) {
|
||||
return ajax.post('/xboe/m/course/manage/audit', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核记录列表,分页查询
|
||||
*/
|
||||
const auditPageRecords = function(data) {
|
||||
return ajax.post('/xboe/m/course/audit/page-records', data);
|
||||
const auditPageRecords = function (data) {
|
||||
return ajax.post('/xboe/m/course/audit/page-records', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核记录列表,要卖课程id,查询出审核列记录信息
|
||||
* { courseId:必须}
|
||||
*/
|
||||
const auditCourseRecords = function(data) {
|
||||
return ajax.post('/xboe/m/course/audit/course-records',data);
|
||||
const auditCourseRecords = function (data) {
|
||||
return ajax.post('/xboe/m/course/audit/course-records', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员的课程发布,当前已经不再使用了
|
||||
* @param {Object} query {ids:课程id,多个使用逗号分隔,title:课程的名称, Boolean pass 是否发布}
|
||||
*/
|
||||
const publish = function(data) {
|
||||
return ajax.post('/xboe/m/course/manage/publish', data);
|
||||
const publish = function (data) {
|
||||
return ajax.post('/xboe/m/course/manage/publish', data);
|
||||
}
|
||||
|
||||
const auditAndPublish=function(data) {
|
||||
return ajax.post('/xboe/m/course/manage/audit-publish', data);
|
||||
const auditAndPublish = function (data) {
|
||||
return ajax.post('/xboe/m/course/manage/audit-publish', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置top
|
||||
* @param {Object} query {ids:课程id,多个使用逗号分隔,title:课程的名称,Boolean top 是否置顶}
|
||||
*/
|
||||
const setTop = function(data) {
|
||||
return ajax.post('/xboe/m/course/manage/top', data);
|
||||
const setTop = function (data) {
|
||||
return ajax.post('/xboe/m/course/manage/top', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否展示置顶相关功能
|
||||
*/
|
||||
const showSetTop = function() {
|
||||
const showSetTop = function () {
|
||||
return ajax.get('/xboe/m/course/manage/show-settop');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取置顶课程列表
|
||||
*/
|
||||
const fetchTopCourseList = function() {
|
||||
const fetchTopCourseList = function () {
|
||||
return ajax.get('/xboe/m/course/manage/topList');
|
||||
}
|
||||
|
||||
@@ -381,7 +382,7 @@ const fetchTopCourseList = function() {
|
||||
* 更新置顶课程排序
|
||||
* @param {Array<{id:string,sortWeight:number}>} data
|
||||
*/
|
||||
const updateTopCourseSort = function(data) {
|
||||
const updateTopCourseSort = function (data) {
|
||||
return ajax.postJson('/xboe/m/course/manage/top-sortchange', data);
|
||||
}
|
||||
|
||||
@@ -389,8 +390,8 @@ const updateTopCourseSort = function(data) {
|
||||
* 管理员的设置启用停用
|
||||
* @param {Object} query {ids:课程id,多个使用逗号分隔,title:课程的名称, Boolean enabled 是否启用}
|
||||
*/
|
||||
const setEnabled = function(data) {
|
||||
return ajax.post('/xboe/m/course/manage/enabled', data);
|
||||
const setEnabled = function (data) {
|
||||
return ajax.post('/xboe/m/course/manage/enabled', data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -398,34 +399,34 @@ const setEnabled = function(data) {
|
||||
* erasable 此值是课程信息带过来的,直接传就可以
|
||||
* @param {Object} query {id:课程id,多个使用逗号分隔,Boolean erasable 是否物理删除,title:课程的名称, remark 备注}
|
||||
*/
|
||||
const del = function(data) {
|
||||
return ajax.post('/xboe/m/course/manage/delete', data);
|
||||
const del = function (data) {
|
||||
return ajax.post('/xboe/m/course/manage/delete', data);
|
||||
}
|
||||
/*
|
||||
详情
|
||||
*/
|
||||
const detailFew=function(id){
|
||||
return ajax.get('/xboe/m/course/portal/detail-few?id=' + id);
|
||||
const detailFew = function (id) {
|
||||
return ajax.get('/xboe/m/course/portal/detail-few?id=' + id);
|
||||
}
|
||||
|
||||
/*
|
||||
直接审核,教师提交审核
|
||||
*/
|
||||
const sumbits=function(data){
|
||||
return ajax.post('/xboe/m/course/manage/sumbits',data);
|
||||
const sumbits = function (data) {
|
||||
return ajax.post('/xboe/m/course/manage/sumbits', data);
|
||||
}
|
||||
/*
|
||||
教师授课记录
|
||||
*/
|
||||
const teacherCourse=function(teacherId){
|
||||
return ajax.get('/xboe/m/course/manage/teacher-course?teacherId='+teacherId);
|
||||
const teacherCourse = function (teacherId) {
|
||||
return ajax.get('/xboe/m/course/manage/teacher-course?teacherId=' + teacherId);
|
||||
}
|
||||
/*
|
||||
教师授课记录导出
|
||||
@param teacherId 教师id
|
||||
*/
|
||||
const exportTeacherCourse=function(teacherId){
|
||||
return ajax.post('/xboe/m/course/manage/export-teacher-course?teacherId='+teacherId)
|
||||
const exportTeacherCourse = function (teacherId) {
|
||||
return ajax.post('/xboe/m/course/manage/export-teacher-course?teacherId=' + teacherId)
|
||||
}
|
||||
/*
|
||||
*待审核课程记录导出
|
||||
@@ -445,25 +446,25 @@ const exportTeacherCourse=function(teacherId){
|
||||
type:课程类型,10微课,21在线课(直播);20:在线课( 录播);30:面授课;40:混合式,
|
||||
name 课程名称
|
||||
*/
|
||||
const exportCourseAudit=function(query){
|
||||
return ajax.post('/xboe/m/course/manage/exportCourseAudit',query);
|
||||
const exportCourseAudit = function (query) {
|
||||
return ajax.post('/xboe/m/course/manage/exportCourseAudit', query);
|
||||
}
|
||||
/*
|
||||
参数同上待审核课程记录导出
|
||||
课程的导出和已审核的课程导出
|
||||
*/
|
||||
const exportCourse = function(query) {
|
||||
return ajax.get({
|
||||
url: '/xboe/m/course/manage/export',
|
||||
method: 'get',
|
||||
params: query,
|
||||
responseType: 'blob'
|
||||
});
|
||||
const exportCourse = function (query) {
|
||||
return ajax.get({
|
||||
url: '/xboe/m/course/manage/export',
|
||||
method: 'get',
|
||||
params: query,
|
||||
responseType: 'blob'
|
||||
});
|
||||
}
|
||||
|
||||
//判断受众id是否有关联
|
||||
const queryCrowd=function(query){
|
||||
return ajax.postJson('/xboe/m/course/manage/queryCrowd',query);
|
||||
const queryCrowd = function (query) {
|
||||
return ajax.postJson('/xboe/m/course/manage/queryCrowd', query);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -472,69 +473,78 @@ const queryCrowd=function(query){
|
||||
* ids
|
||||
* }
|
||||
* */
|
||||
const ids=function (data){
|
||||
return ajax.postJson('/xboe/m/course/manage/ids',data);
|
||||
const ids = function (data) {
|
||||
return ajax.postJson('/xboe/m/course/manage/ids', data);
|
||||
}
|
||||
|
||||
const saveTip = function() {
|
||||
const saveTip = function () {
|
||||
return ajax.postJson('/xboe/m/course/manage/saveTip');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我开发的课程列表
|
||||
*/
|
||||
const courseList = function(data) {
|
||||
const courseList = function (data) {
|
||||
return ajax.postJson('/xboe/m/course/manage/develop_page', data);
|
||||
}
|
||||
|
||||
// ai播放器相关 - 批量AI设置
|
||||
const benchAiSet = function (data) {
|
||||
return ajax.postJson('/xboe/m/course/manage/benchAiSet', data);
|
||||
}
|
||||
const listByUser = function (data) {
|
||||
return ajax2.get('/manageApi/admin/thirdApi/permission/listByUser?permissionType=PAGE');
|
||||
}
|
||||
|
||||
export default {
|
||||
saveBase,
|
||||
submitCourse,
|
||||
revokeSubmit,
|
||||
copyCourse,
|
||||
findUpdateLogs,
|
||||
getUpdateLog,
|
||||
detail,
|
||||
getDictIds,
|
||||
saveContent,
|
||||
pageList,
|
||||
managePage,
|
||||
setEnabled,
|
||||
del,
|
||||
publish,
|
||||
saveSection,
|
||||
getHomework,
|
||||
countWaitAudit,
|
||||
auditList,
|
||||
teacherAuditList,
|
||||
auditAppoint,
|
||||
getAuditInfo,
|
||||
audit,
|
||||
auditPageRecords,
|
||||
auditCourseRecords,
|
||||
auditAndPublish,
|
||||
getAssess,
|
||||
setTop,
|
||||
showSetTop,
|
||||
fetchTopCourseList,
|
||||
updateTopCourseSort,
|
||||
delSection,
|
||||
getExam,
|
||||
delContent,
|
||||
updateContentName,
|
||||
updateContentOrders,
|
||||
saveOnlyCourse,
|
||||
isRedoName,
|
||||
isCourseName,
|
||||
detailFew,
|
||||
sumbits,
|
||||
teacherCourse,
|
||||
exportTeacherCourse,
|
||||
exportCourseAudit,
|
||||
exportCourse,
|
||||
queryCrowd,
|
||||
saveBase,
|
||||
submitCourse,
|
||||
revokeSubmit,
|
||||
copyCourse,
|
||||
findUpdateLogs,
|
||||
getUpdateLog,
|
||||
detail,
|
||||
getDictIds,
|
||||
saveContent,
|
||||
pageList,
|
||||
managePage,
|
||||
setEnabled,
|
||||
del,
|
||||
publish,
|
||||
saveSection,
|
||||
getHomework,
|
||||
countWaitAudit,
|
||||
auditList,
|
||||
teacherAuditList,
|
||||
auditAppoint,
|
||||
getAuditInfo,
|
||||
audit,
|
||||
auditPageRecords,
|
||||
auditCourseRecords,
|
||||
auditAndPublish,
|
||||
getAssess,
|
||||
setTop,
|
||||
showSetTop,
|
||||
fetchTopCourseList,
|
||||
updateTopCourseSort,
|
||||
delSection,
|
||||
getExam,
|
||||
delContent,
|
||||
updateContentName,
|
||||
updateContentOrders,
|
||||
saveOnlyCourse,
|
||||
isRedoName,
|
||||
isCourseName,
|
||||
detailFew,
|
||||
sumbits,
|
||||
teacherCourse,
|
||||
exportTeacherCourse,
|
||||
exportCourseAudit,
|
||||
exportCourse,
|
||||
queryCrowd,
|
||||
ids,
|
||||
saveTip,
|
||||
courseList
|
||||
courseList,
|
||||
benchAiSet,
|
||||
listByUser,
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ export const USER_LIST_PAGE = "/manageApi/admin/thirdApi/user/list";
|
||||
export const ORG_LIST = "/manageApi/admin/thirdApi/org/list";
|
||||
export const ORG_CHILD_LIST = "/manageApi/admin/thirdApi/org/info";
|
||||
// 查询受众中的用户列表
|
||||
export const AUDIENCE_LIST = "/userbasic/audience/memberList";
|
||||
export const AUDIENCE_LIST = "/systemapi/xboe/school/study/signup/audience/memberList";
|
||||
|
||||
export const USER_AUDIENCES = "/manageApi/admin/thirdApi/audience/userAudiences";
|
||||
export const fetchUserAudiences = (params) =>
|
||||
|
||||
BIN
src/assets/images/course/courseAbstract.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
src/assets/images/course/courseNew.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
src/assets/images/course/generationFailed.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
src/assets/images/course/languageIcon.png
Normal file
|
After Width: | Height: | Size: 478 B |
BIN
src/assets/images/course/noData.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
src/assets/images/course/selectLanguage.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
src/assets/images/course/wengaoTip.png
Normal file
|
After Width: | Height: | Size: 504 B |
@@ -97,3 +97,29 @@
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
// 已下架
|
||||
.custom-takeout{
|
||||
display: inline-block;
|
||||
padding: 3px 13px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
background: rgba(254, 249, 195, 1);
|
||||
color: rgba(133, 77, 14, 1);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 17px;
|
||||
letter-spacing: 0px;
|
||||
}
|
||||
// 已上架
|
||||
.custom-putaway{
|
||||
display: inline-block;
|
||||
padding: 3px 13px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
background: rgba(220, 252, 231, 1);
|
||||
color: rgba(22, 101, 52, 1);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 17px;
|
||||
letter-spacing: 0px;
|
||||
}
|
||||
@@ -19,7 +19,7 @@ body {
|
||||
font-family: "Menlo", "苹方-简" !important;
|
||||
}
|
||||
.el-message {
|
||||
top: 15%!important
|
||||
top: 15%;
|
||||
}
|
||||
.el-card.is-always-shadow, .el-card.is-hover-shadow:focus, .el-card.is-hover-shadow:hover{
|
||||
box-shadow: none;
|
||||
@@ -462,6 +462,7 @@ li{
|
||||
justify-content: flex-end;
|
||||
padding-right: 20px;
|
||||
margin-top: 8px;
|
||||
padding-bottom: 14px;
|
||||
|
||||
.el-button {
|
||||
width: 78px;
|
||||
@@ -651,7 +652,8 @@ li{
|
||||
color: #000000;
|
||||
margin-left: 4px;
|
||||
.el-input__inner {
|
||||
width: 28px;
|
||||
width: 100%;
|
||||
min-width: 28px;
|
||||
height: 28px;
|
||||
background: #F5F9FF;
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -433,59 +433,73 @@
|
||||
}
|
||||
}
|
||||
.el-message.new-message {
|
||||
box-shadow: none !important;
|
||||
background-color: #edf2fc !important;
|
||||
min-width: 240px !important;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 8px 20px 0px rgba(0,35,114,0.1);
|
||||
min-width: 180px !important;
|
||||
height: 52px !important;
|
||||
border-radius: 12px !important;
|
||||
top: 236px !important;
|
||||
font-size: 20px !important;
|
||||
// top: 270px !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: bold !important;
|
||||
border: none !important;
|
||||
.el-message__content {
|
||||
font-size: 14px !important;
|
||||
font-weight: bold !important;
|
||||
// margin-top: 2px;
|
||||
}
|
||||
}
|
||||
.el-message--success.new-message {
|
||||
box-shadow: none !important;
|
||||
background-color: rgba($color: #4CB967, $alpha: 0.1) !important;
|
||||
min-width: 240px !important;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 8px 20px 0px rgba(0,35,114,0.1);
|
||||
min-width: 180px !important;
|
||||
height: 52px !important;
|
||||
border-radius: 12px !important;
|
||||
top: 236px !important;
|
||||
font-size: 22px !important;
|
||||
// top: 270px !important;
|
||||
font-size: 16px !important;
|
||||
|
||||
border: none !important;
|
||||
.el-message__content {
|
||||
color: #189B39 !important;
|
||||
font-size: 20px !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: bold !important;
|
||||
// margin-top: 2px;
|
||||
|
||||
}
|
||||
}
|
||||
.el-message--error.new-message {
|
||||
box-shadow: none !important;
|
||||
background-color: rgba($color: #FF3636 , $alpha: 0.1) !important;
|
||||
min-width: 240px !important;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 8px 20px 0px rgba(0,35,114,0.1);
|
||||
min-width: 180px !important;
|
||||
height: 52px !important;
|
||||
border-radius: 12px !important;
|
||||
top: 236px !important;
|
||||
font-size: 22px !important;
|
||||
// top: 270px !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: bold !important;
|
||||
|
||||
border: none !important;
|
||||
.el-message__content {
|
||||
color: #CF1717 !important;
|
||||
font-size: 20px !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: bold !important;
|
||||
// margin-top: 2px;
|
||||
|
||||
}
|
||||
}
|
||||
.el-message--warning.new-message {
|
||||
box-shadow: none !important;
|
||||
background-color: #fdf6ec !important;
|
||||
min-width: 240px !important;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 8px 20px 0px rgba(0,35,114,0.1);
|
||||
min-width: 180px !important;
|
||||
height: 52px !important;
|
||||
border-radius: 12px !important;
|
||||
top: 236px !important;
|
||||
font-size: 20px !important;
|
||||
// top: 270px !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: bold !important;
|
||||
border: none !important;
|
||||
.el-message__content {
|
||||
font-size: 14px !important;
|
||||
font-weight: bold !important;
|
||||
// margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1596,3 +1610,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.tooltip-multiline {
|
||||
max-width: 400px; /* 设置最大宽度,超出则换行 */
|
||||
white-space: normal; /* 允许换行 */
|
||||
word-break: break-all; /* 断行规则 */
|
||||
}
|
||||
439
src/components/Course/aiScript.vue
Normal file
@@ -0,0 +1,439 @@
|
||||
<template>
|
||||
<div class="ai-script">
|
||||
<!-- 搜索和语言选择区域 -->
|
||||
<div v-if="selectableLang && selectableLang.length > 0" class="search-container">
|
||||
<el-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="请输入关键词查找文稿内容"
|
||||
class="search-input"
|
||||
prefix-icon="el-icon-search"
|
||||
@keyup.enter.native="searchContent"
|
||||
@input="handleInputChange"
|
||||
clearable
|
||||
native-type="text"
|
||||
/>
|
||||
<div class="language-selector">
|
||||
<span class="language-label">语言</span>
|
||||
<el-select v-model="selectedLanguage" class="language-select" @change="changeLanguage" placeholder="请选择语言">
|
||||
<el-option v-for="lang in selectableLang" :key="lang.srclang" :label="getSelectLabel(lang)" :value="lang.srclang"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容展示区域 -->
|
||||
<div v-if="selectableLang && selectableLang.length > 0" class="content-container">
|
||||
<!-- 动态渲染内容块 -->
|
||||
<div v-for="(item, index) in contentList" :key="index" class="content-item" :class="{'active': currentTime >= item.start && currentTime <= item.end}">
|
||||
<div class="timestamp">
|
||||
<div class="timestamp-text">
|
||||
<i class="el-icon-time"></i>
|
||||
{{ formatTime(item.start) }}
|
||||
</div>
|
||||
</div>
|
||||
<el-card class="content-text" @click.native="scrollToTime(item.start)">
|
||||
<div v-html="item.highlightedContent || item.text"></div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="no-data">
|
||||
<img src="../../assets/images/course/noData.png" alt="">
|
||||
<span >生成中,过程可能耗时较长,<br>无需在此等待哦~</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
export default {
|
||||
props: {
|
||||
// 视频链接对应的content Id
|
||||
blobId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isDrag:{
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
name: 'ai-script',
|
||||
data() {
|
||||
return {
|
||||
searchKeyword: '',
|
||||
selectedLanguage: 'zh-CN',
|
||||
originalContentList: [],
|
||||
contentList: [], // 用于显示的内容列表
|
||||
isUserScrolling: false, // 用户是否正在滚动
|
||||
userScrollTimeout: null // 滚动超时计时器
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'currentTime',
|
||||
'selectableLang',
|
||||
'duration'
|
||||
]),
|
||||
},
|
||||
mounted: function() {
|
||||
// 添加滚动事件监听,检测用户手动滚动
|
||||
const container = document.querySelector('.content-container');
|
||||
if (container) {
|
||||
container.addEventListener('scroll', this.handleUserScroll);
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy: function() {
|
||||
// 清理事件监听和计时器
|
||||
const container = document.querySelector('.content-container');
|
||||
if (container) {
|
||||
container.removeEventListener('scroll', this.handleUserScroll);
|
||||
}
|
||||
if (this.userScrollTimeout) {
|
||||
clearTimeout(this.userScrollTimeout);
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
// 监听currentTime变化,自动滚动到当前激活项
|
||||
currentTime: function(newTime) {
|
||||
// 只有当用户没有手动滚动时才执行自动滚动
|
||||
if (!this.isUserScrolling) {
|
||||
this.$nextTick(function() {
|
||||
const activeElement = document.querySelector('.content-item.active');
|
||||
if (activeElement) {
|
||||
// 获取内容容器
|
||||
const container = document.querySelector('.content-container');
|
||||
|
||||
// 计算元素是否在可视区域内
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const elementRect = activeElement.getBoundingClientRect();
|
||||
|
||||
// 如果元素不在可视区域内,则滚动到可视区域
|
||||
if (elementRect.top < containerRect.top || elementRect.bottom > containerRect.bottom) {
|
||||
// 计算元素相对于容器的偏移量,而不是使用scrollIntoView
|
||||
// 这样只会滚动content-container内部,不会影响页面滚动
|
||||
|
||||
// 计算元素相对于容器的位置
|
||||
const elementOffsetTop = activeElement.offsetTop;
|
||||
const containerScrollTop = container.scrollTop;
|
||||
const containerHeight = container.clientHeight;
|
||||
const elementHeight = activeElement.clientHeight;
|
||||
|
||||
// 计算目标滚动位置,使元素居中显示
|
||||
// 考虑容器的内边距和元素本身的高度
|
||||
let targetScrollTop = elementOffsetTop - (containerHeight / 2) + (elementHeight / 2);
|
||||
|
||||
// 确保目标滚动位置不会小于0
|
||||
targetScrollTop = Math.max(0, targetScrollTop);
|
||||
|
||||
// 确保目标滚动位置不会导致元素超出容器底部
|
||||
const maxScrollTop = container.scrollHeight - containerHeight;
|
||||
targetScrollTop = Math.min(targetScrollTop, maxScrollTop);
|
||||
|
||||
// 使用requestAnimationFrame实现平滑滚动
|
||||
const startScrollTop = containerScrollTop;
|
||||
const distance = targetScrollTop - startScrollTop;
|
||||
const duration = 300; // 滚动持续时间,毫秒
|
||||
let startTime = null;
|
||||
|
||||
function animateScroll(currentTime) {
|
||||
if (!startTime) startTime = currentTime;
|
||||
const timeElapsed = currentTime - startTime;
|
||||
container.scrollTo({
|
||||
top: startScrollTop + distance - elementHeight - 120,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
if (timeElapsed < duration) {
|
||||
requestAnimationFrame(animateScroll);
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(animateScroll);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 初始化时根据语言选择显示内容
|
||||
this.changeLanguage(this.selectedLanguage)
|
||||
|
||||
},
|
||||
methods: {
|
||||
// 动态获取选择框的标签
|
||||
getSelectLabel(lang) {
|
||||
if (lang.srclang == 'zh-CN') {
|
||||
return lang.label;
|
||||
}
|
||||
return `${lang.name} (${lang.label})`;
|
||||
},
|
||||
formatTime (time) {
|
||||
// 格式化时间为HH:MM:SS,如01:00:00
|
||||
const hours = Math.floor(time / 3600);
|
||||
const minutes = Math.floor((time % 3600) / 60);
|
||||
const seconds = Math.floor(time % 60);
|
||||
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||
},
|
||||
// 跳转到指定时间点
|
||||
scrollToTime(time) {
|
||||
// console.log('scrollToTime', time , this.blobId, localStorage.getItem('videoProgressData'), this.duration)
|
||||
if(!this.isDrag && this.duration){
|
||||
var t = localStorage.getItem('videoProgressData')
|
||||
var arr = t&&JSON.parse(t) || {}
|
||||
if(arr[this.blobId] < time/this.duration || !arr[this.blobId]){
|
||||
return
|
||||
}
|
||||
}
|
||||
console.log('跳转到时间点:', time);
|
||||
this.$emit('changeCurrentTime', time);
|
||||
// 设置用户滚动状态,避免自动滚动干扰
|
||||
this.isUserScrolling = true;
|
||||
if (this.userScrollTimeout) {
|
||||
clearTimeout(this.userScrollTimeout);
|
||||
}
|
||||
this.userScrollTimeout = setTimeout(() => {
|
||||
this.isUserScrolling = false;
|
||||
}, 3000);
|
||||
},
|
||||
// 处理用户滚动事件
|
||||
handleUserScroll: function() {
|
||||
this.isUserScrolling = true;
|
||||
|
||||
// 清除之前的计时器
|
||||
if (this.userScrollTimeout) {
|
||||
clearTimeout(this.userScrollTimeout);
|
||||
}
|
||||
|
||||
// 设置新的计时器,3秒后恢复自动滚动
|
||||
this.userScrollTimeout = setTimeout(() => {
|
||||
this.isUserScrolling = false;
|
||||
}, 3000);
|
||||
},
|
||||
|
||||
searchContent () {
|
||||
// 搜索功能实现
|
||||
if (!this.searchKeyword.trim()) {
|
||||
// 如果搜索关键词为空,显示所有内容
|
||||
this.contentList = this.originalContentList.map(item => ({ ...item }));
|
||||
return;
|
||||
}
|
||||
|
||||
const keyword = this.searchKeyword.trim();
|
||||
// 过滤包含关键词的内容
|
||||
const filteredList = this.originalContentList.filter(item =>
|
||||
item.text.includes(keyword)
|
||||
);
|
||||
|
||||
if (filteredList.length === 0) {
|
||||
// 如果没有搜索到内容,显示提示
|
||||
this.$message({
|
||||
message: '未找到相关内容',
|
||||
type: 'info'
|
||||
});
|
||||
this.contentList = this.originalContentList.map(item => ({ ...item }));
|
||||
} else {
|
||||
// 对搜索到的内容进行关键词高亮处理
|
||||
this.contentList = filteredList.map(item => ({
|
||||
...item,
|
||||
highlightedContent: this.highlightKeyword(item.text, keyword)
|
||||
}));
|
||||
console.log(this.contentList)
|
||||
}
|
||||
},
|
||||
highlightKeyword(content, keyword) {
|
||||
// 对关键词进行转义,防止正则表达式特殊字符的影响
|
||||
const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
// 使用正则表达式全局匹配关键词并添加高亮标记
|
||||
const regex = new RegExp(`(${escapedKeyword})`, 'gi');
|
||||
return content.replace(regex, '<span style="color: rgba(6, 125, 255, 1); background: rgba(6, 125, 255, 0.1);">$1</span>');
|
||||
},
|
||||
changeLanguage (event) {
|
||||
// this.selectedLanguage = event
|
||||
this.selectableLang.forEach(item => {
|
||||
if (item.srclang === event) {
|
||||
console.log('当前语言:', item)
|
||||
if (!item.originalContentList) {
|
||||
try {
|
||||
item.originalContentList = JSON.parse(item.subtitleData)
|
||||
} catch (error) {
|
||||
console.error('ai文稿格式有问题!')
|
||||
}
|
||||
}
|
||||
this.originalContentList = item.originalContentList || []
|
||||
// 初始化时显示所有内容
|
||||
this.contentList = this.originalContentList.map(item => ({ ...item }));
|
||||
console.log('ai文稿数据:', this.originalContentList)
|
||||
}
|
||||
})
|
||||
console.log('切换语言:', event)
|
||||
},
|
||||
handleInputChange() {
|
||||
// 当输入框内容变化时,如果为空则重置显示所有内容
|
||||
if (!this.searchKeyword.trim()) {
|
||||
this.contentList = this.originalContentList.map(item => ({ ...item }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-script {
|
||||
padding: 15px 0;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin: 0 20px 15px 20px;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
::v-deep .el-input__inner {
|
||||
border-color: #2688FF;
|
||||
}
|
||||
|
||||
::v-deep .el-input__inner:focus {
|
||||
border-color: #1a6fe0;
|
||||
box-shadow: 0 0 0 2px rgba(38, 136, 255, 0.2);
|
||||
}
|
||||
|
||||
::v-deep .el-input__prefix {
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
::v-deep .el-input__icon {
|
||||
color: #2688FF;
|
||||
}
|
||||
|
||||
.language-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.language-label {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.language-select {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
|
||||
.content-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
max-height: 410px;
|
||||
overflow-y: auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.content-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
padding: 5px 0;
|
||||
.timestamp-text{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
border-radius: 12px;
|
||||
padding: 2px 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
}
|
||||
|
||||
.content-text {
|
||||
cursor: pointer;
|
||||
line-height: 1.6;
|
||||
font-size: 14px;
|
||||
color: rgba(102, 102, 102, 1);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
background: rgba(250, 250, 250, 1);
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.28px;
|
||||
}
|
||||
|
||||
.active {
|
||||
.timestamp-text{
|
||||
color: rgba(6, 125, 255, 1);
|
||||
background: rgba(6, 125, 255, 0.1);
|
||||
}
|
||||
.content-text{
|
||||
border: 1px solid rgba(116, 182, 255, 1);
|
||||
box-shadow: 0px 0px 7px 0px rgba(6, 125, 255, 0.24);
|
||||
background: rgba(250, 250, 250, 1);
|
||||
}
|
||||
}
|
||||
:deep(.el-card__body) {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
:deep(.el-card.is-hover-shadow:focus, .el-card.is-hover-shadow:hover) {
|
||||
box-shadow: 0 2px 12px 0 rgba(38, 136, 255, 0.2);
|
||||
}
|
||||
:deep(.el-input__inner) {
|
||||
border-radius: 4px!important;
|
||||
}
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.search-container {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.language-selector {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
.no-data{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding-top: 60px;
|
||||
color: rgba(0, 0, 0, 0.34);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 21px;
|
||||
letter-spacing: 0.26px;
|
||||
text-align: center;
|
||||
img{
|
||||
width: 96px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -208,6 +208,44 @@
|
||||
placeholder="请尽量填写课程简介,用于列表中显示,可以让用户更容易了解课程信息">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- ai播放器相关 -->
|
||||
<el-form-item v-show="aiPermission" label="AI设置">
|
||||
<div style="margin-top: 7px;">
|
||||
<div style="display: flex; align-items: center;gap: 5px;">
|
||||
<el-switch v-model="courseInfo.aiSet" :active-value="1" :inactive-value="0"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" :content="aiSetTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div v-show="courseInfo.aiSet==1" style="margin-left: -20px;">
|
||||
<div style="display: flex; justify-content: space-between;;align-items: center;gap: 5px;margin: 10px 0;">
|
||||
<div style="display: flex; align-items: center;gap: 5px;">
|
||||
<span>AI摘要</span>
|
||||
<el-switch v-model="courseInfo.aiAbstract" :active-value="1" :inactive-value="0"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" :content="aiAbstractTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center;gap: 5px;">
|
||||
<span>AI文稿</span>
|
||||
<el-switch v-model="courseInfo.aiDraft" :active-value="1" :inactive-value="0"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" :content="aiDraftTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center;gap: 5px;margin: 10px 0;margin-left: -30px;">
|
||||
<span>AI翻译语种</span>
|
||||
<el-select v-model="courseInfo.languageCode" placeholder="请选择" multiple filterable style="width: 240px;">
|
||||
<el-option v-for="item in selectAllLang" :key="item.key" :label="item.label" :value="item.srclang"> </el-option>
|
||||
</el-select>
|
||||
<el-tooltip class="item" effect="dark" :content="aiTranslateTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div @click="checkCourse"><weikeContent ref="weikeContent" :reset="weikeReset" :contents="contentInfo.list" :course="courseInfo" min-height="644px"></weikeContent></div>
|
||||
@@ -390,6 +428,44 @@
|
||||
placeholder="请尽量填写课程简介,用于列表中显示,可以让用户更容易了解课程信息">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- ai播放器相关 -->
|
||||
<el-form-item v-show="aiPermission" label="AI设置">
|
||||
<div style="margin-top: 7px;">
|
||||
<div style="display: flex; align-items: center;gap: 5px;">
|
||||
<el-switch v-model="courseInfo.aiSet" :active-value="1" :inactive-value="0"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" :content="aiSetTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div v-show="courseInfo.aiSet==1" style="margin-left: -20px;">
|
||||
<div style="display: flex;align-items: center;gap: 80px;margin: 20px 0;">
|
||||
<div style="display: flex; align-items: center;gap: 5px;">
|
||||
<span>AI摘要</span>
|
||||
<el-switch v-model="courseInfo.aiAbstract" :active-value="1" :inactive-value="0"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" :content="aiAbstractTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center;gap: 5px;">
|
||||
<span>AI文稿</span>
|
||||
<el-switch v-model="courseInfo.aiDraft" :active-value="1" :inactive-value="0"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" :content="aiDraftTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center;gap: 5px;margin: 20px 0;margin-left: -30px;">
|
||||
<span>AI翻译语种</span>
|
||||
<el-select v-model="courseInfo.languageCode" placeholder="请选择" multiple filterable style="flex:1">
|
||||
<el-option v-for="item in selectAllLang" :key="item.key" :label="item.label" :value="item.srclang"> </el-option>
|
||||
</el-select>
|
||||
<el-tooltip class="item" effect="dark" :content="aiTranslateTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<!-- v-if="!weike.onlyRequired" -->
|
||||
<!-- <el-form-item label="课程描述">
|
||||
<WxEditor v-model="courseInfo.overview" :minHeight="50"></WxEditor>
|
||||
@@ -600,13 +676,23 @@ export default {
|
||||
highlightStyle: {},
|
||||
guidanceElements: [],
|
||||
isFirstCreate: false, // 标记是否为首次创建
|
||||
selectedOrg: {
|
||||
orgId: null,
|
||||
name: ''
|
||||
},
|
||||
aiPermission: false, // ai播放器权限
|
||||
aiSetTip: '是否将课程进行AI处理', //提示信息
|
||||
aiAbstractTip: '一键提炼课程视频核心要点,助力学员课前高效掌握重点,快速筛选学习资源', // 提示信息
|
||||
aiDraftTip: '分段展示视频内容并精准同步时间轴,实现视频进度与文稿双向定位,学习内容触手可及', //提示信息
|
||||
aiTranslateTip: '智能转换视频字幕与语音为多语种,支持全球学员按需切换语言,打破学习边界', // 提示信息
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getSceneData();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['resOwnerMap', 'sysTypeMap','userInfo','identity']),
|
||||
// ai播放器相关
|
||||
...mapGetters(['resOwnerMap', 'sysTypeMap','userInfo','identity', 'selectAllLang']),
|
||||
catalogTree() {
|
||||
let treeList = [];
|
||||
let $this = this;
|
||||
@@ -632,6 +718,9 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getDictIds();
|
||||
// ai播放器相关
|
||||
this.getAiPermission();
|
||||
let extendFlag=this.$route.query.f; //是否是管理端过来的
|
||||
this.extendRefId=this.$route.query.refId;
|
||||
this.extendRefType=this.$route.query.refType;
|
||||
@@ -656,6 +745,16 @@ export default {
|
||||
handleTagHelp(){
|
||||
this.checkAndShowGuidance();
|
||||
},
|
||||
getAiPermission() {
|
||||
apiCourse.listByUser({}).then(res => {
|
||||
console.log('res', res);
|
||||
if(res.code === 200){
|
||||
let index = res.data.findIndex(item => item.permissionCode === 'KjbAiSetCode');
|
||||
this.aiPermission = index !== -1;
|
||||
console.log('index', index, this.aiPermission);
|
||||
}
|
||||
})
|
||||
},
|
||||
// 关键字的更改
|
||||
changeKeywords(option){
|
||||
if(option.target.value){
|
||||
@@ -895,7 +994,7 @@ export default {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.initAiData();
|
||||
} else {
|
||||
console.log(editData,'editData');
|
||||
this.weikeReset = editData.id;
|
||||
@@ -1082,6 +1181,45 @@ export default {
|
||||
this.courseCoverurl = '';
|
||||
this.courseInfo.coverImg = '';
|
||||
},
|
||||
//获取字典信息
|
||||
async getDictIds() {
|
||||
console.log("--- 获取字典信息 1 = ", this.dicts);
|
||||
try {
|
||||
const response = await apiCourse.getDictIds(637, 1); // 确保返回 Promise
|
||||
console.log("--- 获取字典信息 2 result= ", response);
|
||||
|
||||
if (response.status === 200) {
|
||||
this.dicts = response.result.dicts; // 正确提取 dicts
|
||||
console.log("--- 获取字典信息 3 = ", this.dicts);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取字典信息失败:", error);
|
||||
}
|
||||
},
|
||||
// ai播放器相关
|
||||
// 初始化ai数据
|
||||
initAiData() {
|
||||
if (!this.aiPermission) {
|
||||
// 如果ai设置为空则给默认值 - 会看成新增状态
|
||||
if(this.courseInfo.aiSet === null || this.courseInfo.aiSet === '' || this.courseInfo.aiSet === undefined){
|
||||
this.courseInfo.languageCode = ['zh-CN', 'en-US'];
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 如果ai设置为空则给默认值 - 会看成新增状态
|
||||
if(this.courseInfo.aiSet === null || this.courseInfo.aiSet === '' || this.courseInfo.aiSet === undefined){
|
||||
this.courseInfo.isAddAI = 1; //暂时是否是新增
|
||||
this.courseInfo.aiSet = 1;
|
||||
this.courseInfo.aiAbstract = 1;
|
||||
this.courseInfo.aiDraft = 1;
|
||||
this.courseInfo.aiTranslate = 1;
|
||||
this.courseInfo.languageStatus = 1;
|
||||
this.courseInfo.languageCode = ['zh-CN', 'en-US'];
|
||||
} else {
|
||||
// 获取ai设置信息
|
||||
this.courseInfo.isAddAI = 0;
|
||||
}
|
||||
},
|
||||
//获取课程信息
|
||||
async getDetail(id) {
|
||||
this.curCourseId = id;
|
||||
@@ -1106,6 +1244,12 @@ export default {
|
||||
this.sectionInfo.list = result.sections;
|
||||
this.courseTeachers = result.teachers; //课程的老师信息
|
||||
|
||||
this.isPermission = result.isPermission; //课程的老师信息
|
||||
this.dicts = result.dicts; //课程的老师信息
|
||||
console.log("--- 编辑查看 this.isPermission = ",this.isPermission)
|
||||
console.log("--- 编辑查看 this.dicts = ",this.dicts)
|
||||
// ai播放器相关
|
||||
this.initAiData()
|
||||
if(!this.courseInfo.orgId){
|
||||
//根据课程创建者获取机构id
|
||||
apiUser.getOrgSimpleByUserId(result.course.sysCreateAid).then(ors=>{
|
||||
|
||||
@@ -1,142 +1,463 @@
|
||||
<template>
|
||||
<div id="NameFilterSelect">
|
||||
<el-select @clear="handleClose" style="width:100%" @change="handleChange" clearable multiple v-model="aids"
|
||||
filterable placeholder="姓名" v-limit-input="50" remote reserve-keyword :remote-method="initNameList"
|
||||
:multiple-limit="5" :loading="nameListLoading">
|
||||
<el-option v-for="item in nameList" :key="item.userId" :label="item.name" :value="item.userId">
|
||||
<span>{{ item.name }}</span>
|
||||
<div class="name-remote-select">
|
||||
<el-select ref="selectRef" v-model="innerValue" multiple filterable remote clearable :remote-method="remoteSearch"
|
||||
:loading="loading" :placeholder="placeholder" :multiple-limit="multipleLimit" :collapse-tags="innerCollapseTags"
|
||||
:max-collapse-tag="maxCollapseTags" :no-data-text="noDataText" class="no-wrap-select" style="width: 100%;"
|
||||
@visible-change="handleVisibleChange" @clear="handleClear" @change="emitChange">
|
||||
<el-option v-for="item in options" :key="item.value" :label="formatLabel(item)" :value="item.value">
|
||||
<span>{{ item.label }}</span>
|
||||
<span v-if="item.code" class="option-code">({{ item.code }})</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<div v-if="panelVisible && selectedPanelItems.length"
|
||||
class="selected-panel el-select-dropdown el-popper is-multiple" x-placement="bottom-start" :style="panelStyle"
|
||||
ref="selectedPanel">
|
||||
<div class="el-scrollbar">
|
||||
<div class="el-select-dropdown__wrap el-scrollbar__wrap">
|
||||
<ul class="el-scrollbar__view el-select-dropdown__list">
|
||||
<li v-for="item in selectedPanelItems" :key="item.value"
|
||||
:class="['el-select-dropdown__item', { selected: isSelected(item.value) }]"
|
||||
@click.stop="togglePanelItem(item)">
|
||||
<span>{{ item.label }}</span>
|
||||
<span v-if="item.code" class="option-code">({{ item.code }})</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="popper__arrow"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import apiUserbasic from "@/api/boe/userbasic.js";
|
||||
export default {
|
||||
name: "NameRemoteSelect",
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
// 远程拉取方法,返回数组;可返回 Promise
|
||||
fetcher: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
// 将 fetcher 返回的原始项映射为 { value, label, code }
|
||||
optionFormatter: {
|
||||
type: Function,
|
||||
default: (item) => item,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
multipleLimit: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
minQueryLen: {
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
collapseTags: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 打开下拉时是否自动展开全部已选标签(取消+N收起)
|
||||
expandOnVisible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
maxCollapseTags: {
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
maxInputLen: {
|
||||
type: Number,
|
||||
default: 50,
|
||||
},
|
||||
// 是否在下拉时额外展示“已选列表”面板
|
||||
showSelectedPanel: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 额外增加的下拉最小宽度(在选择框宽度基础上追加,单位 px)
|
||||
panelWidthExtra: {
|
||||
type: Number,
|
||||
default: 60,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
aids: [],
|
||||
nameListLoading: false,
|
||||
nameList: [],
|
||||
innerValue: [],
|
||||
options: [],
|
||||
loading: false,
|
||||
keyword: "",
|
||||
// 传递给 el-select 的 collapse 状态(limitedMode 下需关闭折叠以便手动控制可见标签)
|
||||
innerCollapseTags: true,
|
||||
optionCache: {},
|
||||
panelVisible: false,
|
||||
countEl: null,
|
||||
tagsContainerEl: null,
|
||||
panelMinWidth: "",
|
||||
outsideHandler: null,
|
||||
selectedPanelItems: [],
|
||||
// 标记:由点击 +N 主动关闭 el-select 下拉引起的 visible-change(false),需要忽略一次
|
||||
ignoreNextVisibleChange: false,
|
||||
};
|
||||
},
|
||||
props: {},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.innerValue = Array.isArray(val) ? val : [];
|
||||
this.bindCountClick();
|
||||
},
|
||||
},
|
||||
collapseTags: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.innerCollapseTags = val;
|
||||
},
|
||||
},
|
||||
innerValue() {
|
||||
this.bindCountClick();
|
||||
},
|
||||
panelVisible(visible) {
|
||||
if (visible) {
|
||||
this.bindOutsideClick();
|
||||
} else {
|
||||
this.unbindOutsideClick();
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.bindCountClick();
|
||||
this.updatePanelWidth();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.unbindCountClick();
|
||||
this.unbindOutsideClick();
|
||||
},
|
||||
methods: {
|
||||
handleChange() {
|
||||
console.log("handleChange", this.aids);
|
||||
this.$emit("handleNameChange", this.aids);
|
||||
},
|
||||
handleClose() {
|
||||
this.$set(this, 'nameList', [])
|
||||
console.log("handleClose", this.aids);
|
||||
this.$emit("handleClose");
|
||||
},
|
||||
handleReset() {
|
||||
this.aids = [];
|
||||
this.nameList = [];
|
||||
this.handleClear()
|
||||
},
|
||||
async initNameList(keyword) {
|
||||
console.log("initNameList", keyword);
|
||||
if (!keyword) {
|
||||
|
||||
formatLabel(item) {
|
||||
if (!item || !item.label) return "";
|
||||
return item.code ? `${item.label}` : item.label;
|
||||
},
|
||||
syncInputQuery(limited) {
|
||||
const select = this.$refs.selectRef;
|
||||
if (select && select.query !== limited) {
|
||||
select.query = limited;
|
||||
this.$nextTick(() => {
|
||||
if (select.$refs && select.$refs.input) {
|
||||
select.$refs.input.value = limited;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
async remoteSearch(keyword) {
|
||||
const limited = (keyword || "").slice(0, this.maxInputLen);
|
||||
this.syncInputQuery(limited);
|
||||
this.keyword = limited;
|
||||
const query = limited.trim();
|
||||
if (!query || query.length < this.minQueryLen) {
|
||||
this.options = [];
|
||||
return;
|
||||
}
|
||||
this.nameListLoading = true;
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await apiUserbasic.selectUser(keyword);
|
||||
this.nameListLoading = false;
|
||||
|
||||
if (res && res.status === 200) {
|
||||
const resultList = res.result || [];
|
||||
this.nameList = resultList
|
||||
.map((item) => this.formatCreatorItem(item))
|
||||
.filter((item) => item.userId);
|
||||
} else {
|
||||
this.creatorOptions = [];
|
||||
}
|
||||
const res = await this.fetcher(query);
|
||||
const list = Array.isArray(res) ? res : [];
|
||||
this.options = list
|
||||
.map((item) => this.optionFormatter(item) || {})
|
||||
.filter((item) => item.value);
|
||||
// 缓存本次查询的选项,便于展示已选列表
|
||||
this.options.forEach((item) => {
|
||||
this.optionCache[item.value] = item;
|
||||
});
|
||||
} catch (error) {
|
||||
this.nameList = [];
|
||||
this.nameListLoading = false;
|
||||
this.options = [];
|
||||
} finally {
|
||||
this.nameListLoading = false;
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
emitChange() {
|
||||
const selectedOptions = this.selectedDisplay;
|
||||
this.$emit("input", this.innerValue);
|
||||
this.$emit("change", {
|
||||
value: this.innerValue,
|
||||
options: selectedOptions,
|
||||
});
|
||||
},
|
||||
handleClear() {
|
||||
this.innerValue = [];
|
||||
this.options = [];
|
||||
this.panelVisible = false;
|
||||
this.selectedPanelItems = [];
|
||||
this.emitChange();
|
||||
},
|
||||
handleVisibleChange(visible) {
|
||||
// 如果是由点击 +N 主动触发的关闭,下拉的 visible-change(false) 需要被忽略一次,避免把自定义面板也关掉
|
||||
if (!visible && this.ignoreNextVisibleChange) {
|
||||
this.ignoreNextVisibleChange = false;
|
||||
return;
|
||||
}
|
||||
// 聚焦/下拉打开时不自动展示面板
|
||||
if (visible) {
|
||||
// 打开时展开标签,避免点击 +N 看不到具体选项
|
||||
if (this.expandOnVisible) {
|
||||
this.innerCollapseTags = false;
|
||||
}
|
||||
// 无论关键字是否存在,都清空现有搜索结果,下次输入再重新远程拉取
|
||||
this.options = [];
|
||||
this.updatePanelWidth();
|
||||
this.panelVisible = false;
|
||||
} else {
|
||||
// 关闭时恢复原始折叠配置
|
||||
this.innerCollapseTags = this.collapseTags;
|
||||
this.panelVisible = false;
|
||||
this.selectedPanelItems = [];
|
||||
}
|
||||
},
|
||||
// 用缓存兜底,保证已选项能在面板中展示
|
||||
getOptionByValue(val) {
|
||||
return (
|
||||
this.options.find((item) => item.value === val) ||
|
||||
this.optionCache[val] || { value: val, label: val }
|
||||
);
|
||||
},
|
||||
handleCountClick(event) {
|
||||
// 点击 +N 展开/收起已选列表
|
||||
if (event && event.stopPropagation) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
if (!this.showSelectedPanel) return;
|
||||
const hasSelected = this.selectedDisplay.length > 0;
|
||||
if (!hasSelected) {
|
||||
this.panelVisible = false;
|
||||
return;
|
||||
}
|
||||
const nextVisible = !this.panelVisible;
|
||||
if (nextVisible) {
|
||||
// 准备已选面板,并忽略接下来由关闭 el-select 下拉触发的一次 visible-change(false)
|
||||
this.ignoreNextVisibleChange = true;
|
||||
this.prepareSelectedPanel();
|
||||
this.closeSelectDropdown();
|
||||
} else {
|
||||
this.selectedPanelItems = [];
|
||||
}
|
||||
this.panelVisible = nextVisible;
|
||||
},
|
||||
bindCountClick() {
|
||||
this.$nextTick(() => {
|
||||
const selectEl = this.$refs.selectRef && this.$refs.selectRef.$el;
|
||||
if (!selectEl) return;
|
||||
let count = this.getCountElement(selectEl);
|
||||
if (count && count !== this.countEl) {
|
||||
this.unbindCountClick();
|
||||
this.countEl = count;
|
||||
this.countEl.style.cursor = "pointer";
|
||||
this.countEl.addEventListener("click", this.handleCountClick);
|
||||
}
|
||||
|
||||
formatCreatorItem(item = {}) {
|
||||
// 同时绑定 tags 容器做事件委托,避免找不到 +N 时无法响应
|
||||
const tagsContainer = selectEl.querySelector(".el-select__tags");
|
||||
if (tagsContainer && tagsContainer !== this.tagsContainerEl) {
|
||||
if (this.tagsContainerEl) {
|
||||
this.tagsContainerEl.removeEventListener(
|
||||
"click",
|
||||
this.handleTagsContainerClick,
|
||||
true
|
||||
);
|
||||
}
|
||||
this.tagsContainerEl = tagsContainer;
|
||||
this.tagsContainerEl.addEventListener(
|
||||
"click",
|
||||
this.handleTagsContainerClick,
|
||||
true
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
unbindCountClick() {
|
||||
if (this.countEl) {
|
||||
this.countEl.removeEventListener("click", this.handleCountClick);
|
||||
this.countEl = null;
|
||||
}
|
||||
if (this.tagsContainerEl) {
|
||||
this.tagsContainerEl.removeEventListener(
|
||||
"click",
|
||||
this.handleTagsContainerClick,
|
||||
true
|
||||
);
|
||||
this.tagsContainerEl = null;
|
||||
}
|
||||
},
|
||||
handleTagsContainerClick(event) {
|
||||
const target = event.target;
|
||||
if (!target) return;
|
||||
const isCountText =
|
||||
target.classList && target.classList.contains("el-select__tags-text");
|
||||
const isInfoTag =
|
||||
target.classList && target.classList.contains("el-tag--info");
|
||||
if (isCountText || isInfoTag) {
|
||||
this.handleCountClick(event);
|
||||
}
|
||||
},
|
||||
getCountElement(selectEl) {
|
||||
let count = selectEl.querySelector(".el-select__tags-text");
|
||||
if (!count) {
|
||||
const tags = selectEl.querySelectorAll(
|
||||
".el-select__tags .el-tag--info"
|
||||
);
|
||||
count = tags && tags.length ? tags[tags.length - 1] : null;
|
||||
}
|
||||
if (!count) {
|
||||
count = selectEl.querySelector(".el-select__tags");
|
||||
}
|
||||
return count;
|
||||
},
|
||||
bindOutsideClick() {
|
||||
if (this.outsideHandler) return;
|
||||
this.outsideHandler = (e) => {
|
||||
const panel = this.$refs.selectedPanel;
|
||||
const selectEl = this.$refs.selectRef && this.$refs.selectRef.$el;
|
||||
const target = e.target;
|
||||
if (!target) return;
|
||||
const insidePanel = panel && panel.contains(target);
|
||||
const insideSelect = selectEl && selectEl.contains(target);
|
||||
if (!insidePanel && !insideSelect) {
|
||||
this.panelVisible = false;
|
||||
this.selectedPanelItems = [];
|
||||
}
|
||||
};
|
||||
document.addEventListener("mousedown", this.outsideHandler, true);
|
||||
},
|
||||
unbindOutsideClick() {
|
||||
if (this.outsideHandler) {
|
||||
document.removeEventListener("mousedown", this.outsideHandler, true);
|
||||
this.outsideHandler = null;
|
||||
}
|
||||
},
|
||||
updatePanelWidth() {
|
||||
this.$nextTick(() => {
|
||||
const selectEl = this.$refs.selectRef && this.$refs.selectRef.$el;
|
||||
if (!selectEl) return;
|
||||
const rect = selectEl.getBoundingClientRect();
|
||||
const base = rect ? rect.width : 0;
|
||||
const extra = Number(this.panelWidthExtra) || 0;
|
||||
const minWidth = base + extra;
|
||||
this.panelMinWidth = `${minWidth}px`;
|
||||
});
|
||||
},
|
||||
prepareSelectedPanel() {
|
||||
// 在展开已选列表时,冻结当前已选项,支持在面板内反复勾选/取消
|
||||
this.selectedPanelItems = this.selectedDisplay.map((item) => ({
|
||||
...item,
|
||||
}));
|
||||
},
|
||||
closeSelectDropdown() {
|
||||
// 关闭原生下拉,避免与已选列表面板同时展示;保留输入内容
|
||||
const select = this.$refs.selectRef;
|
||||
if (select && select.visible) {
|
||||
if (typeof select.handleClose === "function") {
|
||||
select.handleClose();
|
||||
} else {
|
||||
select.visible = false;
|
||||
}
|
||||
this.syncInputQuery(this.keyword || "");
|
||||
}
|
||||
},
|
||||
isSelected(val) {
|
||||
return (this.innerValue || []).includes(val);
|
||||
},
|
||||
togglePanelItem(item) {
|
||||
if (!item || !item.value) return;
|
||||
const current = this.innerValue || [];
|
||||
const exists = this.isSelected(item.value);
|
||||
this.innerValue = exists
|
||||
? current.filter((v) => v !== item.value)
|
||||
: [...current, item.value];
|
||||
this.emitChange();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
noDataText() {
|
||||
return this.keyword && this.keyword.length >= this.minQueryLen
|
||||
? "无数据"
|
||||
: `请至少输入${this.minQueryLen}个字`;
|
||||
},
|
||||
selectedDisplay() {
|
||||
return (this.innerValue || [])
|
||||
.map((val) => this.getOptionByValue(val))
|
||||
.filter((item) => item && item.value);
|
||||
},
|
||||
panelStyle() {
|
||||
return {
|
||||
userId: item.id,
|
||||
name: item.realName,
|
||||
code: item.userNo,
|
||||
minWidth: this.panelMinWidth || "100%",
|
||||
};
|
||||
},
|
||||
},
|
||||
watch: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
::v-deep .el-upload-dragger {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
.image-upload {
|
||||
width: 410px;
|
||||
height: 168px;
|
||||
}
|
||||
.image-card .el-upload--picture-card,
|
||||
.image-card .el-upload-list--picture-card .el-upload-list__item {
|
||||
background-color: #fbfdff;
|
||||
border: 1px dashed #c0ccda;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
vertical-align: top;
|
||||
}
|
||||
.el-upload--picture-card {
|
||||
background-color: #fbfdff;
|
||||
border: 1px dashed #c0ccda;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
vertical-align: top;
|
||||
line-height: 100%;
|
||||
}
|
||||
.image-uploader .el-upload {
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
image-uploader .el-upload--picture-card {
|
||||
.name-remote-select {
|
||||
width: 100%;
|
||||
}
|
||||
image-uploader .el-upload:hover {
|
||||
border-color: #409eff;
|
||||
}
|
||||
.image-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
display: block;
|
||||
}
|
||||
.icon-text {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
height: 30px;
|
||||
line-height: 35px;
|
||||
}
|
||||
.image {
|
||||
position: relative;
|
||||
.mask {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
&:hover .mask {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.option-code {
|
||||
margin-left: 4px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.selected-panel {
|
||||
position: absolute;
|
||||
z-index: 2000;
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
/* 参照 ElementUI popper 箭头默认样式(packages/theme-chalk/src/popper.scss) */
|
||||
.selected-panel .popper__arrow,
|
||||
.selected-panel .popper__arrow::after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
.selected-panel .popper__arrow {
|
||||
border-width: 6px;
|
||||
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
|
||||
}
|
||||
.selected-panel .popper__arrow::after {
|
||||
content: " ";
|
||||
border-width: 6px;
|
||||
}
|
||||
.selected-panel[x-placement^="bottom"] {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.selected-panel[x-placement^="bottom"] .popper__arrow {
|
||||
top: -6px;
|
||||
/* ElementUI 下拉箭头靠左偏移(非居中),与官方样式对齐 */
|
||||
left: 35px;
|
||||
border-top-width: 0;
|
||||
border-bottom-color: #e4e7ed;
|
||||
}
|
||||
.selected-panel[x-placement^="bottom"] .popper__arrow::after {
|
||||
top: 1px;
|
||||
margin-left: -6px;
|
||||
border-top-width: 0;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -119,7 +119,35 @@
|
||||
</div>
|
||||
<div class="player-time">{{ currentTimeFormat }} / {{ fullTimeFormat }}</div>
|
||||
</div>
|
||||
<!-- ai播放器相关 -->
|
||||
<div class="player-controls-bottom-right">
|
||||
<div v-if="isAiTranslate" class="player-controls-btn box-aiTranslate">
|
||||
<div v-show="isSubtitle" class="player-controls-btn cursor-pointer btn-speed">
|
||||
<span>{{!currentLang ? 'AI翻译' : currentLangLabel}}</span>
|
||||
<div class="speed-control">
|
||||
<ul class="speed-control-list">
|
||||
<li
|
||||
v-for="item in selectableLang"
|
||||
:key="item.srclang"
|
||||
@click="changeLang(item)"
|
||||
:data-value="item.srclang"
|
||||
class="one-line-ellipsis"
|
||||
:title="item.label"
|
||||
:class="{'current': currentLang === item.srclang}"
|
||||
>{{ item.label }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="isSubtitle" style="margin-top: -3px;">|</div>
|
||||
<div class="player-controls-btn" style="display: flex;gap: 0.3rem;align-items: center;">
|
||||
<span>字幕</span>
|
||||
<el-switch
|
||||
@change="toggleSubtitle"
|
||||
v-model="isSubtitle">
|
||||
</el-switch>
|
||||
</div>
|
||||
<div style="margin-top: -3px;">|</div>
|
||||
</div>
|
||||
<div class="player-controls-btn cursor-pointer btn-speed">
|
||||
<span>{{currentSpeed === 1 ? '倍速' : `${currentSpeed}x`}}</span>
|
||||
<div class="speed-control">
|
||||
@@ -224,6 +252,7 @@
|
||||
import volumeBar from "@/components/VideoPlayer/volume-bar.vue";
|
||||
import progressBar from "@/components/VideoPlayer/progress-bar.vue";
|
||||
import playerBarrageScreen from "@/components/VideoPlayer/player-barrage-screen.vue";
|
||||
import { mapGetters, mapMutations } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: "barrage-videoplayer",
|
||||
@@ -301,12 +330,24 @@ export default {
|
||||
fullTimeFormat: "00:00:00", // 视频总长度的文字
|
||||
barrageTimelineStart: 0, // 弹幕时间轴的起始时间点(手动调整进度条触发更新)
|
||||
isInit:false, // 是否初始化过
|
||||
// ai播放器相关
|
||||
isSubtitle: true, // 是否开启字幕
|
||||
currentLangLabel:'', // 当前字幕语言
|
||||
};
|
||||
},
|
||||
// ai播放器相关
|
||||
computed: {
|
||||
...mapGetters(['selectableLang','currentLang','courseInfo']),
|
||||
isAiTranslate () {
|
||||
return this.courseInfo?.aiSet == 1 && this.courseInfo?.aiTranslate == 1 && this.selectableLang && this.selectableLang.length > 0;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
// ai播放器相关
|
||||
this.SET_currentLang('');
|
||||
},
|
||||
mounted() {
|
||||
console.log('---',this.isAiTranslate,this.courseInfo,'courseInfo');
|
||||
this.videoDom = this.$refs.video;
|
||||
this.videoDom.focus({preventScroll: true});
|
||||
let speedValue=localStorage.getItem('boe_video_speed');
|
||||
@@ -317,7 +358,8 @@ export default {
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
console.log('当前状态:',this.currentProgress,this.isDrag,this.videoDom.currentTime , this.videoDom.duration)
|
||||
this.SET_duration(this.videoDom.duration);
|
||||
console.log('当前状态:',localStorage.getItem('videoProgressData'),this.currentProgress,this.isDrag,this.videoDom.currentTime , this.videoDom.duration)
|
||||
// 视频播放时本地记录视频实时播放时长,视频设置了禁止拖动时执行
|
||||
if(!this.isDrag){
|
||||
var time = localStorage.getItem('videoProgressData')
|
||||
@@ -372,6 +414,7 @@ export default {
|
||||
//if()
|
||||
//console.log(this.videoDom.readyState,'this.videoDom.readyState');
|
||||
}, 1000);
|
||||
|
||||
// 视频dom监听器,用于控制鼠标的显示
|
||||
this.videoDom.addEventListener("mousemove", () => {
|
||||
this.isCursorStatic = false;
|
||||
@@ -411,6 +454,13 @@ export default {
|
||||
// });
|
||||
},
|
||||
methods: {
|
||||
// ai播放器相关
|
||||
...mapMutations({
|
||||
SET_currentLang: 'video/SET_currentLang',
|
||||
SET_currentTime: 'video/SET_currentTime',
|
||||
SET_selectableLang: 'video/SET_selectableLang',
|
||||
SET_duration: 'video/SET_duration',
|
||||
}),
|
||||
//当视频由于需要缓冲下一帧而停止,解决一直计时的问题
|
||||
onWaiting(){
|
||||
console.log('触发了onWairing');
|
||||
@@ -624,6 +674,8 @@ export default {
|
||||
},
|
||||
onAudioTimeUpdate() {
|
||||
const currentTime = this.$refs.video.currentTime;
|
||||
// ai播放器相关
|
||||
this.SET_currentTime(currentTime)
|
||||
this.$emit('onTimeUpdate', currentTime);
|
||||
},
|
||||
/**
|
||||
@@ -641,9 +693,77 @@ export default {
|
||||
this.$emit('onFullscreen',false);//全屏
|
||||
}
|
||||
}
|
||||
},
|
||||
/** ai播放器相关
|
||||
* 切换字幕
|
||||
*/
|
||||
toggleSubtitle(value) {
|
||||
if (this.videoDom && this.videoDom.textTracks && this.videoDom.textTracks.length >0) {
|
||||
if (!value) {
|
||||
// 关闭字幕
|
||||
this.videoDom.textTracks[this.videoDom.textTracks.length - 1].mode = 'hidden';
|
||||
} else {
|
||||
// 打开字幕
|
||||
this.videoDom.textTracks[this.videoDom.textTracks.length - 1].mode = 'showing';
|
||||
}
|
||||
}
|
||||
},
|
||||
/** ai播放器相关
|
||||
* 切换字幕语言
|
||||
*/
|
||||
changeLang(item = {}) {
|
||||
this.SET_currentLang(item.srclang);
|
||||
this.currentLangLabel = item.label;
|
||||
console.log("changeLang",item);
|
||||
// 先移除所有字幕轨道
|
||||
Array.from(this.videoDom.querySelectorAll('track')).forEach(t => t.remove());
|
||||
if(!item.vttContent){
|
||||
console.log("字幕内容为空!")
|
||||
return;
|
||||
}
|
||||
if(!item.srcUrl){
|
||||
try{
|
||||
const blob = new Blob([item.vttContent], { type: 'text/vtt' });
|
||||
item.srcUrl = URL.createObjectURL(blob);
|
||||
}catch(e){
|
||||
console.log("字幕格式错误",e)
|
||||
}
|
||||
}
|
||||
const trackEl = document.createElement('track');
|
||||
trackEl.kind = 'subtitles';
|
||||
trackEl.srclang = item.srclang;
|
||||
trackEl.label = item.label;
|
||||
trackEl.src = item.srcUrl;
|
||||
trackEl.default = true; // 确保字幕默认启用
|
||||
|
||||
// 使用箭头函数保持this上下文
|
||||
trackEl.addEventListener('load', () => {
|
||||
console.log('字幕加载成功!');
|
||||
// console.log('#########Track cues:', trackEl.track.cues);
|
||||
});
|
||||
|
||||
trackEl.addEventListener('error', () => {
|
||||
console.error('字幕加载失败!');
|
||||
});
|
||||
|
||||
// 确保视频已加载到可添加轨道的状态
|
||||
if (this.videoDom.readyState >= 1) {
|
||||
this.videoDom.appendChild(trackEl);
|
||||
this.videoDom.textTracks[this.videoDom.textTracks.length - 1].mode = 'showing';
|
||||
} else {
|
||||
this.videoDom.addEventListener('loadedmetadata', () => {
|
||||
this.videoDom.appendChild(trackEl);
|
||||
this.videoDom.textTracks[this.videoDom.textTracks.length - 1].mode = 'showing';
|
||||
}, { once: true });
|
||||
}
|
||||
},
|
||||
seekToTime(time) {
|
||||
if (!this.videoDom) return;
|
||||
this.videoDom.currentTime = time + 0.01;
|
||||
this.isPlaying = true;
|
||||
this.videoDom.play();
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
currentVolume: function () {
|
||||
@@ -668,9 +788,37 @@ export default {
|
||||
// }
|
||||
// },
|
||||
src: function () {
|
||||
// 当视频地址变更时,重载视频
|
||||
// 当视频地址变更时,先重置字幕再重载视频
|
||||
this.isPlaying = false;
|
||||
// 重置字幕相关状态
|
||||
this.SET_currentLang('');
|
||||
this.currentLangLabel = '';
|
||||
|
||||
// 移除所有现有字幕轨道元素
|
||||
Array.from(this.videoDom.querySelectorAll('track')).forEach(t => t.remove());
|
||||
|
||||
// 更彻底地清除字幕:重置所有textTracks
|
||||
Array.from(this.videoDom.textTracks).forEach(track => {
|
||||
track.mode = 'hidden';
|
||||
// 尝试移除所有cues(浏览器支持的话)
|
||||
if (track.cues) {
|
||||
while (track.cues.length > 0) {
|
||||
track.cues.remove(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 重载视频
|
||||
this.videoDom.load();
|
||||
this.isPlaying = false
|
||||
|
||||
// 如果有默认语言且支持AI翻译,重新设置字幕
|
||||
// if (this.isAiTranslate && this.selectableLang && this.selectableLang.length > 0) {
|
||||
// // 找到默认语言或第一个可用语言
|
||||
// const defaultLang = this.selectableLang.find(lang => lang.srclang === 'zh-CN') || this.selectableLang[0];
|
||||
// if (defaultLang) {
|
||||
// this.changeLang(defaultLang);
|
||||
// }
|
||||
// }
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -907,6 +1055,12 @@ export default {
|
||||
color: #fff;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.box-aiTranslate{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
@media (device-width: 100vw) {
|
||||
.player-controls-btn .player-controls-icon {
|
||||
/* height: 26px; */
|
||||
@@ -918,4 +1072,22 @@ export default {
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
video::cue {
|
||||
/* color: #fff; */
|
||||
/* background-color: transparent; */
|
||||
/* font-size: 0.85em; */
|
||||
/* font-family: 'Arial', sans-serif;
|
||||
-webkit-text-stroke: 4px #000;
|
||||
text-stroke: 4px #000; */
|
||||
|
||||
/* text-shadow:
|
||||
2px 2px 0 #000,
|
||||
-2px 2px 0 #000,
|
||||
2px -2px 0 #000,
|
||||
-2px -2px 0 #000,
|
||||
0 2px 0 #000,
|
||||
2px 0 0 #000,
|
||||
0 -2px 0 #000,
|
||||
-2px 0 0 #000; */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,35 +5,20 @@
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="tab-search">
|
||||
<el-select
|
||||
<div class="member-select-wrapper">
|
||||
<name-remote-select
|
||||
class="member-select"
|
||||
ref="memberSelect"
|
||||
v-model="memberSelected"
|
||||
multiple
|
||||
filterable
|
||||
remote
|
||||
clearable
|
||||
reserve-keyword
|
||||
:fetcher="fetchMembers"
|
||||
:option-formatter="formatMemberOption"
|
||||
placeholder="姓名"
|
||||
:multiple-limit="5"
|
||||
:remote-method="remoteSearchMember"
|
||||
:loading="memberLoading"
|
||||
@input.native="limitMemberInput"
|
||||
@visible-change="handleMemberVisibleChange"
|
||||
@change="handleMemberChange"
|
||||
@clear="handleMemberClear"
|
||||
size="small"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in memberOptions"
|
||||
:key="item.userId"
|
||||
:label="item.name"
|
||||
:value="item.userId"
|
||||
>
|
||||
<span>{{ item.name }}</span>
|
||||
<span v-if="item.code" class="option-code">({{ item.code }})</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
@clear="handleMemberClear"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
<el-button type="primary" size="small" @click="onSearch" style="margin-left: 8px;">查询</el-button>
|
||||
<el-button size="small" @click="onReset" style="margin-left: 4px;">重置</el-button>
|
||||
</div>
|
||||
@@ -44,12 +29,11 @@
|
||||
:data="tableData"
|
||||
@selection-change="onSelectionChange"
|
||||
:row-key="row => row.id"
|
||||
max-height="420"
|
||||
>
|
||||
<el-table-column type="selection" width="50" />
|
||||
<el-table-column prop="userName" label="姓名" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="workNum" label="工号" width="120" />
|
||||
<el-table-column prop="departName" label="部门" width="120" />
|
||||
<el-table-column prop="userName" label="姓名" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="workNum" label="工号" width="130" />
|
||||
<el-table-column prop="departName" label="部门" width="140" />
|
||||
<el-table-column prop="orgPath" label="组织路径" min-width="200" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<div class="pagination">
|
||||
@@ -73,9 +57,11 @@
|
||||
<script>
|
||||
import { fetchAudienceList, saveStu } from "@/api/signup/commonStudent";
|
||||
import apiUserbasic from "@/api/boe/userbasic.js";
|
||||
import NameRemoteSelect from '@/views/course/components/NameRemoteSelect.vue';
|
||||
|
||||
export default {
|
||||
name: "AudienceModal",
|
||||
components: { NameRemoteSelect },
|
||||
props: {
|
||||
visible: { type: Boolean, default: false },
|
||||
pageSize: { type: Number, default: 10 },
|
||||
@@ -98,8 +84,6 @@ export default {
|
||||
selectedRows: [],
|
||||
// 成员远程搜索多选
|
||||
memberSelected: [],
|
||||
memberOptions: [],
|
||||
memberLoading: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -119,22 +103,22 @@ export default {
|
||||
this.selectedRows = [];
|
||||
this.keyword = "";
|
||||
this.memberSelected = [];
|
||||
this.memberOptions = [];
|
||||
this.fetchList();
|
||||
},
|
||||
fetchList() {
|
||||
this.loading = true;
|
||||
fetchAudienceList({
|
||||
// memberList 为多选成员 ID 列表
|
||||
memberList: this.memberSelected || [],
|
||||
memberIdList: this.memberSelected || [],
|
||||
courseId: this.courseDetail?.id || '',
|
||||
pageNo: this.pageNo,
|
||||
pageSize: this.pageSize,
|
||||
audienceIdList: this.audienceIds || [],
|
||||
})
|
||||
.then((res) => {
|
||||
const data = res.data || res.result || res || {};
|
||||
const data = res.result || res || {};
|
||||
this.tableData = data.list || [];
|
||||
this.total = data.total || 0;
|
||||
this.total = data.count || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
@@ -147,7 +131,6 @@ export default {
|
||||
onReset() {
|
||||
this.keyword = "";
|
||||
this.memberSelected = [];
|
||||
this.memberOptions = [];
|
||||
this.onSearch();
|
||||
},
|
||||
onPageChange(page) {
|
||||
@@ -157,78 +140,28 @@ export default {
|
||||
onSelectionChange(list) {
|
||||
this.selectedRows = list;
|
||||
},
|
||||
// ===== 成员远程搜索,多选逻辑(参考 ManageListRemote.vue 的创建人筛选) =====
|
||||
async remoteSearchMember(keyword) {
|
||||
const limited = (keyword || "").slice(0, 50);
|
||||
if (this.$refs.memberSelect && this.$refs.memberSelect.query !== limited) {
|
||||
this.$refs.memberSelect.query = limited;
|
||||
this.$nextTick(() => {
|
||||
if (
|
||||
this.$refs.memberSelect &&
|
||||
this.$refs.memberSelect.$refs &&
|
||||
this.$refs.memberSelect.$refs.input
|
||||
) {
|
||||
this.$refs.memberSelect.$refs.input.value = limited;
|
||||
}
|
||||
});
|
||||
}
|
||||
const query = limited.trim();
|
||||
if (!query || query.length <= 1) {
|
||||
this.memberOptions = [];
|
||||
return;
|
||||
}
|
||||
this.memberLoading = true;
|
||||
try {
|
||||
const res = await apiUserbasic.selectUser(query);
|
||||
if (res && res.status === 200) {
|
||||
const resultList = res.result || [];
|
||||
this.memberOptions = resultList
|
||||
.map((item) => this.formatMemberItem(item))
|
||||
.filter((item) => item.userId);
|
||||
} else {
|
||||
this.memberOptions = [];
|
||||
}
|
||||
} catch (error) {
|
||||
this.memberOptions = [];
|
||||
} finally {
|
||||
this.memberLoading = false;
|
||||
async fetchMembers(keyword) {
|
||||
const query = (keyword || "").trim();
|
||||
if (!query || query.length <= 1) return [];
|
||||
const res = await apiUserbasic.selectUser(query);
|
||||
if (res && res.status === 200) {
|
||||
return res.result || [];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
formatMemberItem(item = {}) {
|
||||
formatMemberOption(item = {}) {
|
||||
return {
|
||||
userId: item.id,
|
||||
name: item.realName,
|
||||
value: item.id,
|
||||
label: item.realName,
|
||||
code: item.userNo,
|
||||
};
|
||||
},
|
||||
handleMemberChange(value = []) {
|
||||
handleMemberChange({ value = [] } = {}) {
|
||||
// 限制最多 5 个,保持与 ManageListRemote 中创建人筛选一致
|
||||
this.memberSelected = (value || []).slice(0, 5);
|
||||
},
|
||||
handleMemberClear() {
|
||||
this.memberSelected = [];
|
||||
this.memberOptions = [];
|
||||
},
|
||||
handleMemberVisibleChange(visible) {
|
||||
if (!visible) return;
|
||||
const select = this.$refs.memberSelect;
|
||||
const query = (select && select.query) || "";
|
||||
if (!query) {
|
||||
this.memberOptions = [];
|
||||
}
|
||||
},
|
||||
limitMemberInput(event) {
|
||||
const limited =
|
||||
(event && event.target && event.target.value
|
||||
? event.target.value
|
||||
: ""
|
||||
).slice(0, 50);
|
||||
if (event && event.target && event.target.value !== limited) {
|
||||
event.target.value = limited;
|
||||
}
|
||||
if (this.$refs.memberSelect) {
|
||||
this.$refs.memberSelect.query = limited;
|
||||
}
|
||||
},
|
||||
handleClose() {
|
||||
this.visibleSync = false;
|
||||
@@ -236,7 +169,7 @@ export default {
|
||||
handleConfirm() {
|
||||
const targetId = this.courseDetail?.id;
|
||||
if(!this.selectedRows.length) {
|
||||
this.$showMessage('请添加学员', 'error');
|
||||
this.$showManageMessage('请添加学员', 'error');
|
||||
return
|
||||
}
|
||||
saveStu({
|
||||
@@ -244,9 +177,9 @@ export default {
|
||||
type: 13,
|
||||
deptIds: [],
|
||||
groupIds: [],
|
||||
studentList: this.selectedRows.map((e) => ({ id: e.userId })),
|
||||
studentList: this.selectedRows.map((e) => ({ id: e.userId, realName: e.userName })),
|
||||
}).then(() => {
|
||||
this.$showMessage("添加成功", 'success');
|
||||
this.$showManageMessage("添加成功", 'success');
|
||||
this.$emit("confirm", this.selectedRows);
|
||||
this.handleClose();
|
||||
});
|
||||
@@ -271,6 +204,9 @@ export default {
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
.member-select-wrapper {
|
||||
width: 240px;
|
||||
}
|
||||
::v-deep .el-dialog__body {
|
||||
padding: 0 20px 30px 20px;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
<div class="tab-search">
|
||||
<span class="label">姓名:</span>
|
||||
<el-input v-model="nameSearch.keyword" placeholder="请输入姓名" size="small" clearable class="input" />
|
||||
<el-button type="primary" size="small" @click="onSearchStu">
|
||||
<!-- 点击搜索时,认为是一次“新的搜索”,页码应重置为第 1 页 -->
|
||||
<el-button type="primary" size="small" @click="onSearchStu(true)">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button size="small" @click="resetStu">重置</el-button>
|
||||
@@ -92,7 +93,8 @@
|
||||
<div class="tab-search">
|
||||
<span class="label">受众名称:</span>
|
||||
<el-input v-model="audienceName.keyword" placeholder="请输入受众名称" size="small" clearable class="input" />
|
||||
<el-button type="primary" size="small" @click="searchAudience">
|
||||
<!-- 搜索受众时,视为新查询,页码需归 1 -->
|
||||
<el-button type="primary" size="small" @click="searchAudience(true)">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button size="small" @click="resetAudienceInfo">重置</el-button>
|
||||
@@ -412,7 +414,15 @@ export default {
|
||||
this.projectParams.studentName = "";
|
||||
this.getProjectStu();
|
||||
},
|
||||
onSearchStu() {
|
||||
/**
|
||||
* 获取“快速选人”列表
|
||||
* @param {Boolean} resetPage 是否重置到第 1 页(新的搜索条件或重置时需要)
|
||||
*/
|
||||
onSearchStu(resetPage = false) {
|
||||
// 当关键字、组织等搜索条件变化或点击“搜索”按钮时,应将页码重置为 1
|
||||
if (resetPage) {
|
||||
this.stuTable.pageNo = 1;
|
||||
}
|
||||
fetchQuickStudents({
|
||||
...this.nameSearch,
|
||||
pageNo: this.stuTable.pageNo,
|
||||
@@ -451,6 +461,8 @@ export default {
|
||||
},
|
||||
resetStu() {
|
||||
this.nameSearch = { keyword: "", departId: "" };
|
||||
// 重置时也应从第 1 页重新加载
|
||||
this.stuTable.pageNo = 1;
|
||||
this.onSearchStu();
|
||||
},
|
||||
fetchOrgTree() {
|
||||
@@ -501,7 +513,14 @@ export default {
|
||||
}
|
||||
this.selectedOrgKeys = this.deptList.map((d) => d.id);
|
||||
},
|
||||
searchAudience() {
|
||||
/**
|
||||
* 搜索受众列表
|
||||
* @param {Boolean} resetPage 是否重置到第 1 页(新搜索或重置时需要)
|
||||
*/
|
||||
searchAudience(resetPage = false) {
|
||||
if (resetPage) {
|
||||
this.audienceTable.pageNo = 1;
|
||||
}
|
||||
fetchUserAudiences({
|
||||
...this.audienceName,
|
||||
pageNo: this.audienceTable.pageNo,
|
||||
@@ -568,6 +587,8 @@ export default {
|
||||
},
|
||||
resetAudienceInfo() {
|
||||
this.audienceName.keyword = "";
|
||||
// 重置后从第 1 页重新拉取
|
||||
this.audienceTable.pageNo = 1;
|
||||
this.searchAudience();
|
||||
},
|
||||
onOrgSelectChange() { },
|
||||
@@ -615,17 +636,17 @@ export default {
|
||||
this.groupMemberNumber &&
|
||||
this.groupMemberCount < this.groupMemberNumber + this.projectSelectRows.length + this.stuSelectRows.length
|
||||
) {
|
||||
return this.$showMessage("添加小组学员超过最大值", 'error');
|
||||
return this.$showManageMessage("添加小组学员超过最大值", 'error');
|
||||
}
|
||||
saveStu({
|
||||
targetId: this.courseDetail?.id || this.id,
|
||||
type: 13,
|
||||
deptIds: this.deptList.map((e) => e.id),
|
||||
groupIds: this.auditSelectRows.map((e) => e.id),
|
||||
studentList: this.stuSelectRows.map((e) => ({id: e.id})),
|
||||
studentList: this.stuSelectRows.map((e) => ({id: e.id, realName: e.realName})),
|
||||
}).then((res) => {
|
||||
console.log('res', res);
|
||||
this.$showMessage("添加成功", 'success');
|
||||
this.$showManageMessage("添加成功", 'success');
|
||||
this.$emit("confirm");
|
||||
this.handleClose();
|
||||
});
|
||||
@@ -688,9 +709,6 @@ export default {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.table-area {
|
||||
}
|
||||
|
||||
.pager {
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
|
||||
@@ -122,6 +122,8 @@ export const iframes=[
|
||||
{title:'课程管理', path:'/iframe/course/manages',hidden:false,component:'course/ManageList'},
|
||||
{title:'课程管理新版', path:'/iframe/course/manage-remote',hidden:false,component:'course/ManageListRemote'},
|
||||
{title:'课程管理新版', path:'/iframe/course/coursemanage-remote',hidden:false,component:'course/CourseManageRemote'},
|
||||
{title:'ai摘要', path:'/iframe/course/aiAbstract',hidden:false,component:'course/aiSet/aiAbstract'},
|
||||
{title:'ai翻译', path:'/iframe/course/aiTranslate',hidden:false,component:'course/aiSet/aiTranslate'},
|
||||
{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'},
|
||||
|
||||
@@ -59,7 +59,7 @@ import 'swiper/dist/css/swiper.css';
|
||||
Vue.use(VueAwesomeSwiper)
|
||||
import watermark from './utils/warterMark.js'
|
||||
import Bus from './utils/bus.js'
|
||||
import {showMessage} from './utils/index.js'
|
||||
import {showMessage, showManageMessage} from './utils/index.js'
|
||||
|
||||
import MessageBoxService from './utils/simpleMessageBox.js'
|
||||
|
||||
@@ -68,6 +68,8 @@ Vue.use(MessageBoxService)
|
||||
|
||||
Vue.prototype.$showMessage = showMessage
|
||||
|
||||
Vue.prototype.$showManageMessage = showManageMessage
|
||||
|
||||
Vue.prototype.$bus = Bus
|
||||
|
||||
Vue.prototype.$watermark = watermark
|
||||
|
||||
@@ -29,6 +29,19 @@ router.beforeEach((to, from, next) => {
|
||||
// 在免登录白名单,直接进入
|
||||
next()
|
||||
}else{
|
||||
// if (!store.getters.init) {
|
||||
// store.commit('app/SET_INITDATA',true);
|
||||
// let myRouters=routers();
|
||||
// store.dispatch('GenerateRoutes',{routers:myRouters}).then(accessRoutes=>{
|
||||
// console.log('accessRoutes::',accessRoutes)
|
||||
// router.addRoutes(accessRoutes) // 动态添加可访问路由表
|
||||
// next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
|
||||
// });
|
||||
// } else {
|
||||
// to.meta.keepAlive = true
|
||||
// next();
|
||||
// }
|
||||
// return;
|
||||
if(getToken()){
|
||||
if(to.path === '/login'){
|
||||
// 如果是外部用户,把配置的路由跳转到个人中心
|
||||
|
||||
@@ -28,5 +28,12 @@ const getters = {
|
||||
studyTaskCount:state => state.user.studyTaskCount,
|
||||
praisesUnicom:state =>state.pdf.praisesUnicom,
|
||||
favoritesUnicom:state =>state.pdf.favoritesUnicom,
|
||||
// ai播放器相关
|
||||
selectAllLang:state => state.video.selectAllLang,
|
||||
selectableLang:state => state.video.selectableLang,
|
||||
currentLang:state => state.video.currentLang,
|
||||
currentTime:state => state.video.currentTime,
|
||||
courseInfo:state => state.video.courseInfo,
|
||||
duration:state => state.video.duration,
|
||||
}
|
||||
export default getters
|
||||
|
||||
@@ -12,6 +12,7 @@ import resOwner from './modules/resOwner'
|
||||
import majorType from './modules/majorType'
|
||||
import orgDomain from './modules/orgDomain'
|
||||
import pdf from './modules/pdf'
|
||||
import video from './modules/video' // ai播放器相关
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
@@ -27,7 +28,8 @@ const store = new Vuex.Store({
|
||||
resOwner,
|
||||
majorType,
|
||||
orgDomain,
|
||||
pdf
|
||||
pdf,
|
||||
video
|
||||
},
|
||||
getters
|
||||
})
|
||||
|
||||
171
src/store/modules/video.js
Normal file
@@ -0,0 +1,171 @@
|
||||
// ai播放器相关
|
||||
|
||||
/**
|
||||
*
|
||||
selectAllLang: [
|
||||
{
|
||||
key: 'ZH_CN',
|
||||
srclang: 'zh-CN',
|
||||
label: '中文',
|
||||
name: '中文',
|
||||
},
|
||||
{
|
||||
key: 'EN_US',
|
||||
srclang: 'en-US',
|
||||
label: '英语',
|
||||
name: 'English',
|
||||
},
|
||||
{
|
||||
key: 'JA_JP',
|
||||
srclang: 'ja-JP',
|
||||
label: '日语',
|
||||
name: '日本語',
|
||||
},
|
||||
{
|
||||
key: 'KO_KR',
|
||||
srclang: 'ko-KR',
|
||||
label: '韩语',
|
||||
name: '한국어',
|
||||
},
|
||||
{
|
||||
key: 'FR_FR',
|
||||
srclang: 'fr-FR',
|
||||
label: '法语',
|
||||
name: 'français',
|
||||
},
|
||||
{
|
||||
key: 'DE_DE',
|
||||
srclang: 'de-DE',
|
||||
label: '德语',
|
||||
name: 'Deutsch',
|
||||
},
|
||||
{
|
||||
key: 'ES_ES',
|
||||
srclang: 'es-ES',
|
||||
label: '西班牙语',
|
||||
name: 'español',
|
||||
},
|
||||
{
|
||||
key: 'RU_RU',
|
||||
srclang: 'ru-RU',
|
||||
label: '俄语',
|
||||
name: 'русский',
|
||||
},
|
||||
{
|
||||
key: 'PT_BR',
|
||||
srclang: 'pt-BR',
|
||||
label: '葡萄牙语',
|
||||
name: 'português',
|
||||
},
|
||||
{
|
||||
key: 'IT_IT',
|
||||
srclang: 'it-IT',
|
||||
label: '意大利语',
|
||||
name: 'italiano',
|
||||
},
|
||||
{
|
||||
key: 'AR_SA',
|
||||
srclang: 'ar-SA',
|
||||
label: '阿拉伯语',
|
||||
name: 'العربية',
|
||||
},
|
||||
{
|
||||
key: 'TH_TH',
|
||||
srclang: 'th-TH',
|
||||
label: '泰语',
|
||||
name: 'ไทย',
|
||||
},
|
||||
{
|
||||
key: 'VI_VN',
|
||||
srclang: 'vi-VN',
|
||||
label: '越南语',
|
||||
name: 'tiếng Việt',
|
||||
},
|
||||
{
|
||||
key: 'ID_ID',
|
||||
srclang: 'id-ID',
|
||||
label: '印度尼西亚语',
|
||||
name: 'Bahasa Indonesia',
|
||||
},
|
||||
{
|
||||
key: 'HI_IN',
|
||||
srclang: 'hi-IN',
|
||||
label: '印地语',
|
||||
name: 'हिन्दी',
|
||||
}
|
||||
], // 全部语言列表
|
||||
*/
|
||||
|
||||
const state = {
|
||||
selectAllLang: [
|
||||
{
|
||||
key: 'ZH_CN',
|
||||
srclang: 'zh-CN',
|
||||
label: '中文',
|
||||
name: '中文',
|
||||
},
|
||||
{
|
||||
key: 'EN_US',
|
||||
srclang: 'en-US',
|
||||
label: '英语',
|
||||
name: 'English',
|
||||
},
|
||||
{
|
||||
key: 'VI_VN',
|
||||
srclang: 'vi-VN',
|
||||
label: '越南语',
|
||||
name: 'tiếng Việt',
|
||||
},
|
||||
{
|
||||
key: 'ES_ES',
|
||||
srclang: 'es-ES',
|
||||
label: '西班牙语',
|
||||
name: 'español',
|
||||
},
|
||||
], // 一期语言列表
|
||||
selectableLang: [], // 可选语言列表+字幕信息
|
||||
currentLang: '', // 当前选中语言
|
||||
currentTime: -1, // 当前视频时间
|
||||
courseInfo: {},
|
||||
duration: 0, // 视频时长
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_currentLang: (state, lang) => {
|
||||
state.currentLang = lang
|
||||
},
|
||||
SET_selectableLang: (state, list = []) => {
|
||||
let selectableLang = []
|
||||
list.forEach(item => {
|
||||
let selectItem = state.selectAllLang.find(selectItem => selectItem.srclang === item.language)
|
||||
if (selectItem) {
|
||||
selectableLang.push({
|
||||
...item,
|
||||
...selectItem,
|
||||
})
|
||||
}
|
||||
})
|
||||
state.selectableLang = selectableLang
|
||||
},
|
||||
SET_currentTime: (state, time) => {
|
||||
state.currentTime = time
|
||||
},
|
||||
SET_courseInfo: (state, info) => {
|
||||
state.courseInfo = info
|
||||
},
|
||||
SET_duration: (state, duration) => {
|
||||
state.duration = duration
|
||||
},
|
||||
}
|
||||
|
||||
const actions = {
|
||||
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions
|
||||
}
|
||||
|
||||
@@ -462,6 +462,17 @@ export function showMessage(message, status) {
|
||||
message: message,
|
||||
type: status,
|
||||
duration: 5000,
|
||||
customClass: 'new-message'
|
||||
customClass: 'new-message',
|
||||
offset: 270
|
||||
});
|
||||
}
|
||||
|
||||
export function showManageMessage(message, status) {
|
||||
this.$message({
|
||||
message: message,
|
||||
type: status,
|
||||
duration: 5000,
|
||||
customClass: 'new-message',
|
||||
offset: 80
|
||||
});
|
||||
}
|
||||
@@ -121,7 +121,7 @@ export function toContentType(fileType) {
|
||||
// }
|
||||
export function courseType(type) {
|
||||
const maps = {
|
||||
10: '录播课',
|
||||
10: '微课',
|
||||
21: '在线课(直播)',
|
||||
20: '录播课',
|
||||
30: '线下课',
|
||||
|
||||
@@ -2,24 +2,36 @@
|
||||
<div id="courseManage">
|
||||
<!--课程管理-->
|
||||
<div class="course-info">
|
||||
<div class="course-desc-item"><span class="title">课程名称:</span><span class="desc">{{courseDetail.name}}</span>
|
||||
<div class="course-info-left">
|
||||
<div class="course-top">
|
||||
<p class="course-name">{{courseDetail.name}}</p>
|
||||
<p class="course-type">{{courseType(courseDetail.type)}}</p>
|
||||
</div>
|
||||
<p class="course-sys-type">
|
||||
{{sysTypeName(courseDetail.sysType1)}}{{courseDetail.sysType2 == ''? '': '/'}}{{sysTypeName(courseDetail.sysType2)}}{{courseDetail.sysType3 == ''? '': '/'}}{{sysTypeName(courseDetail.sysType3)}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="course-info-right" >
|
||||
<el-button type="text" @click="handleBack">返回</el-button>
|
||||
</div>
|
||||
<!-- <div class="course-desc-item"><span class="title">课程名称:</span><span class="desc">{{courseDetail.name}}</span>
|
||||
</div>
|
||||
<div class="course-desc-item"><span class="title">类型:</span><span
|
||||
class="desc">{{courseType(courseDetail.type)}}</span></div>
|
||||
<div class="course-desc-item"><span class="title">课程分类:</span><span
|
||||
class="desc">{{sysTypeName(courseDetail.sysType1)}}{{courseDetail.sysType2 == ''? '': '/'}}{{sysTypeName(courseDetail.sysType2)}}{{courseDetail.sysType3 == ''? '': '/'}}{{sysTypeName(courseDetail.sysType3)}}</span>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<!-- <div style="color: red;">下面的表格标题及内容需要调整完善</div> -->
|
||||
<div>
|
||||
<el-tabs style="width: 100%;" v-model="tabName" @tab-click="handleTabClick">
|
||||
<el-tab-pane label="报名记录" name="second">
|
||||
<el-tabs style="width: 100%;" type="border-card" v-model="tabName" >
|
||||
<el-tab-pane label="报名记录" name="second">
|
||||
<el-row style="margin: 20px 0;" :gutter="20">
|
||||
<el-col :span="4">
|
||||
<el-col :span="6">
|
||||
<div class="grid-content bg-purple">
|
||||
<!-- <el-input v-model="signup.name" clearable placeholder="姓名" maxlength="50" /> -->
|
||||
<NameFilterSelect @handleNameChange="aids => signup.aid = aids" ref="signupNameFilter"
|
||||
@handleClose="signup.aid = []" />
|
||||
<NameFilterSelect @change="aids => signup.aid = aids.value" placeholder="姓名" ref="signupNameFilter" :fetcher="fetchNameList"
|
||||
:option-formatter="formatOption" />
|
||||
<!-- <el-select :key="2" style="width:100%" clearable multiple v-model="signup.aid" filterable
|
||||
placeholder="姓名" v-limit-input="50" remote reserve-keyword :remote-method="initNameList"
|
||||
:multiple-limit="5" :loading="nameListLoading">
|
||||
@@ -40,20 +52,15 @@
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="grid-content bg-purple">
|
||||
<el-button type="primary" @click="getSignupList()">查 询</el-button>
|
||||
<el-button type="primary" @click="getSignupList(true)">查 询</el-button>
|
||||
<el-button @click="resetSignupList()">重 置</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="6" :offset="4">
|
||||
<el-col :span="6" :offset="2" style="padding-right:0 !important">
|
||||
<div class="grid-content bg-purple" style="text-align: right;">
|
||||
<el-button
|
||||
v-if="showSignupActions"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
style="margin-right: 10px;"
|
||||
@click="handleAddSignupClick"
|
||||
>
|
||||
<el-button v-if="showSignupActions" type="primary" icon="el-icon-plus" style="margin-right: 10px;"
|
||||
@click="handleAddSignupClick">
|
||||
添加报名
|
||||
</el-button>
|
||||
<el-button type="primary" icon="el-icon-upload2" @click="handleExportSignup">
|
||||
@@ -62,12 +69,13 @@
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="tab-content">
|
||||
<div class="tab-content" v-if="tabName == 'second'">
|
||||
|
||||
<el-table border max-height="350" :data="study.list" :header-cell-style="{textAlign: 'center'}"
|
||||
:cell-style="{ textAlign: 'center' }" style="width: 100%">
|
||||
<!-- <el-table-column type="selection" width="55"></el-table-column> -->
|
||||
<el-table-column prop="name" label="姓名"></el-table-column>
|
||||
<el-table-column prop="code" label="工号">
|
||||
<el-table-column prop="code" width="200" label="工号">
|
||||
</el-table-column>
|
||||
<el-table-column prop="orgInfo" label="部门">
|
||||
<template slot-scope="scope">
|
||||
@@ -83,40 +91,32 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="signTime" label="报名时间"></el-table-column>
|
||||
<el-table-column
|
||||
v-if="showSignupActions"
|
||||
label="操作"
|
||||
width="140"
|
||||
>
|
||||
<el-table-column v-if="showSignupActions" label="操作" width="140">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
type="text"
|
||||
size="mini"
|
||||
class="delete-action-link--danger"
|
||||
@click="handleDeleteSignup(scope.row)"
|
||||
>
|
||||
<el-button type="text" size="mini" class="delete-action-link--danger"
|
||||
@click="handleDeleteSignup(scope.row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div style="padding: 10px;">
|
||||
<div style="text-align:center; padding: 10px;">
|
||||
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
|
||||
:current-page="study.pageIndex" :page-sizes="[10, 20, 30, 40]" :page-size="study.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="study.count"></el-pagination>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
<!-- <div style="text-align:center; padding: 10px;"> -->
|
||||
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
|
||||
:current-page="study.pageIndex" :page-sizes="[10, 20, 30, 40]" :page-size="study.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="study.count"></el-pagination>
|
||||
</div>
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane lazy label="学习记录" name="third">
|
||||
<el-tab-pane label="学习记录" name="third">
|
||||
<el-row style="margin: 20px 0;" :gutter="20">
|
||||
<el-col :span="4">
|
||||
<el-col :span="5">
|
||||
<div class="grid-content bg-purple">
|
||||
<!-- <el-input clearable v-model="learningRecords.name" maxlength="50"
|
||||
placeholder="姓名"></el-input> -->
|
||||
<NameFilterSelect @handleNameChange="aids => learningRecords.aid = aids" ref="learningRecordsNameFilter"
|
||||
@handleClose="learningRecords.aid = []" />
|
||||
<NameFilterSelect placeholder="姓名" @change="aids => learningRecords.aid = aids.value" ref="learningRecordsNameFilter"
|
||||
:fetcher="fetchNameList" :option-formatter="formatOption" />
|
||||
<!-- <el-select :key="1" style="width:100%" clearable multiple v-model="learningRecords.aid" filterable
|
||||
placeholder="姓名" v-limit-input="50" reserve-keyword remote :remote-method="initNameList"
|
||||
:multiple-limit="5" :loading="nameListLoading">
|
||||
@@ -128,7 +128,7 @@
|
||||
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-col :span="3">
|
||||
<div class="grid-content bg-purple">
|
||||
<el-select style="width: 100%;" v-model="learningRecords.status" clearable placeholder="学习状态">
|
||||
<el-option label="未开始" :value="1"></el-option>
|
||||
@@ -149,22 +149,22 @@
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<div class="grid-content bg-purple">
|
||||
<el-button type="primary" @click="getStudyRecords()">查 询</el-button>
|
||||
<el-button type="primary" @click="getStudyRecords(true)">查 询</el-button>
|
||||
<el-button @click="resetStudyRecords()">重 置</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col style="float: right; width:185px">
|
||||
<div class="grid-content bg-purple" style="text-align: right;">
|
||||
<el-button type="primary" icon="el-icon-upload2" @click="handleExportStudyDetail">导出学习课程记录</el-button>
|
||||
<el-button type="primary" icon="el-icon-upload2" @click="handleExportStudyDetail">导出课程学习记录</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="tab-content">
|
||||
<div class="tab-content" v-if="tabName == 'third'">
|
||||
<el-table max-height="350" border :data="learningRecords.list" :header-cell-style="{textAlign: 'center'}"
|
||||
:cell-style="{ textAlign: 'center' }" style="width: 100%">
|
||||
<el-table-column prop="aname" label="姓名"></el-table-column>
|
||||
<el-table-column prop="code" label="工号"></el-table-column>
|
||||
<el-table-column prop="orgInfo" label="部门">
|
||||
<el-table-column prop="aname" width="100" label="姓名"></el-table-column>
|
||||
<el-table-column prop="code" width="100" label="工号"></el-table-column>
|
||||
<el-table-column prop="orgInfo" label="部门" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip class="item" effect="dark" :content="scope.row.orgInfo" placement="top-start">
|
||||
<p class="no-wrap">
|
||||
@@ -172,7 +172,7 @@
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="addTime" width="300" label="学习时间"></el-table-column>
|
||||
<el-table-column prop="addTime" width="200" label="学习时间"></el-table-column>
|
||||
<el-table-column prop="totalDuration" label="学习时长">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.totalDuration == 0? '0': (scope.row.totalDuration/60).toFixed(2)}}
|
||||
@@ -188,7 +188,7 @@
|
||||
{{ scope.row.progress }}%
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="orgInfo" label="操作" width="120">
|
||||
<el-table-column label="操作" width="120">
|
||||
<template slot-scope="scope">
|
||||
<!--弹出每一项资源的学习情况列表-->
|
||||
<el-link :underline="false" type="primary" @click.stop="showStudyDetails(scope.row)">查看</el-link>
|
||||
@@ -196,7 +196,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div style="text-align: center; padding: 10px;">
|
||||
<div class="pagination">
|
||||
<el-pagination @size-change="handleSizeChangeRecords" @current-change="handleCurrentChangeRecords"
|
||||
:current-page="learningRecords.pageIndex" :page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="learningRecords.pageSize" layout="total, sizes, prev, pager, next, jumper"
|
||||
@@ -205,28 +205,28 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane lazy label="资源学习情况" name="first">
|
||||
<el-tab-pane label="资源学习情况" name="first">
|
||||
<el-row style="margin: 20px 0;" :gutter="20">
|
||||
<el-col :span="4">
|
||||
<el-col :span="6">
|
||||
<div class="grid-content bg-purple"><el-input clearable v-model="recourseListQuery.contentName"
|
||||
placeholder="资源名称"></el-input></div>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<div class="grid-content bg-purple">
|
||||
<el-button type="primary" @click="getResourseList">查 询</el-button>
|
||||
<el-button type="primary" @click="getResourseList(true)">查 询</el-button>
|
||||
<el-button @click="resetResourseList">重 置</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6" :offset="9">
|
||||
<el-col :span="6" :offset="7" style="padding-right:0 !important">
|
||||
<div class="grid-content bg-purple" style="text-align: right;">
|
||||
<el-button type="primary" icon="el-icon-upload2" @click="handleExportStudyResource">导出资源学习记录</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="tab-content">
|
||||
<div class="tab-content" v-if="tabName == 'first'">
|
||||
<el-table max-height="350" border :header-cell-style="{textAlign: 'center'}"
|
||||
:cell-style="{ textAlign: 'center' }" :data="recourseList" style="width: 100%">
|
||||
<el-table-column label="资源名称">
|
||||
<el-table-column label="资源名称" prop="contentName">
|
||||
<template slot-scope="scope">
|
||||
{{scope.row.contentName}}
|
||||
</template>
|
||||
@@ -241,7 +241,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div style="text-align: center; padding: 10px;">
|
||||
<div class="pagination">
|
||||
<el-pagination @size-change="handleSizeRecourseList" @current-change="handleCurrentChangeRecourseList"
|
||||
:current-page="recourseListQuery.pageIndex" :page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="recourseListQuery.pageSize" layout="total, sizes, prev, pager, next, jumper"
|
||||
@@ -253,19 +253,13 @@
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<AudienceModal
|
||||
v-if="showSignupActions && hasCourseCrowds"
|
||||
:visible.sync="audienceDialogVisible"
|
||||
@confirm="handleAudienceConfirm"
|
||||
:audience-ids="courseCrowds.map(item => item.id)"
|
||||
/>
|
||||
<SignupModal
|
||||
v-if="showSignupActions && !hasCourseCrowds"
|
||||
:visible.sync="addSignupVisible"
|
||||
@confirm="handleSignupCreate"
|
||||
/>
|
||||
<AudienceModal v-if="showSignupActions && hasCourseCrowds" :visible.sync="audienceDialogVisible"
|
||||
@confirm="handleAudienceConfirm" :audience-ids="courseCrowds.map(item => item.groupId)" />
|
||||
<SignupModal v-if="showSignupActions && !hasCourseCrowds" :visible.sync="addSignupVisible"
|
||||
@confirm="handleSignupCreate" />
|
||||
<!-- 学习详情 -->
|
||||
<el-dialog title="学习详情" :visible.sync="study.detailShow" width="900px" :append-to-body="true">
|
||||
<el-dialog title="学习详情" :visible.sync="study.detailShow" class="common-course-dialog" width="900px"
|
||||
:append-to-body="true">
|
||||
<div>
|
||||
<!-- <div v-if="study.detailType == 10"><auditCourse1 :isDetails="true" :isShow="false" :id="study.examineId"></auditCourse1></div>
|
||||
<div v-if="study.detailType == 20"><auditCourse2 :isDetails="true" :isShow="false" :id="study.examineId"></auditCourse2></div> -->
|
||||
@@ -295,7 +289,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div style="text-align: center;padding: 10px;" v-if="showStudyDetailPage">
|
||||
<div class="pagination" v-if="showStudyDetailPage">
|
||||
<el-pagination @size-change="handleSizeChangeStudyDetail" @current-change="handleCurrentStudyDetail"
|
||||
:current-page="studyDetailQuery.pageIndex" :page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="studyDetailQuery.pageSize" layout="total, sizes, prev, pager, next, jumper"
|
||||
@@ -308,14 +302,18 @@
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="学习详情" v-if="commonResourceStudyPeopleShow" :visible.sync="commonResourceStudyPeopleShow"
|
||||
width="50%" :append-to-body="true">
|
||||
<el-dialog class="common-course-dialog" title="学习详情" v-if="commonResourceStudyPeopleShow"
|
||||
:visible.sync="commonResourceStudyPeopleShow" width="65%" :append-to-body="true">
|
||||
<el-row style="margin: 20px 0 20px -10px;" :gutter="20">
|
||||
<el-col :span="6">
|
||||
<div class="grid-content bg-purple">
|
||||
<NameFilterSelect ref="commonResourceStudyPeopleNameFilter"
|
||||
@handleNameChange="aids => commonResourceStudyPeopleQuery.aid = aids"
|
||||
@handleClose="commonResourceStudyPeopleQuery.aid = []" />
|
||||
<NameFilterSelect ref="commonResourceStudyPeopleNameFilter" placeholder="姓名"
|
||||
@change="aids => commonResourceStudyPeopleQuery.aid = aids.value" :option-formatter="formatOption"
|
||||
:fetcher="fetchNameList"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- <el-select :key="3" style="width:100%" clearable multiple v-model="commonResourceStudyPeopleQuery.aid"
|
||||
filterable placeholder="姓名" v-limit-input="50" remote reserve-keyword :remote-method="initNameList"
|
||||
@@ -348,7 +346,7 @@
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="grid-content bg-purple">
|
||||
<el-button type="primary" @click="queryResourceStudyPeopleList">查 询</el-button>
|
||||
<el-button type="primary" @click="queryCommonResource(true)">查 询</el-button>
|
||||
<el-button @click="resetCommonResourceQuery">重 置</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
@@ -356,8 +354,8 @@
|
||||
<el-table max-height="500" border :data="commonResourceStudyPeopleList" style="width: 100%"
|
||||
:header-cell-style="{textAlign: 'center'}" :cell-style="{ textAlign: 'center' }">
|
||||
<el-table-column prop="aname" label="姓名"></el-table-column>
|
||||
<el-table-column prop="code" label="工号"></el-table-column>
|
||||
<el-table-column prop="orgInfo" label="部门">
|
||||
<el-table-column prop="code" label="工号" width="150"></el-table-column>
|
||||
<el-table-column prop="orgInfo" label="部门" width="220">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip class="item" effect="dark" :content="scope.row.orgInfo" placement="top-start">
|
||||
<p class="no-wrap">
|
||||
@@ -381,7 +379,7 @@
|
||||
label="满意度分数"></el-table-column>
|
||||
<el-table-column prop="finishTime" width="200" label="完成时间"></el-table-column>
|
||||
</el-table>
|
||||
<div style="text-align: center;padding: 10px;">
|
||||
<div class="pagination">
|
||||
<el-pagination @size-change="handleSizeChangeStudyPeople" @current-change="handleCurrentChangeStudyPeople"
|
||||
:current-page="commonResourceStudyPeopleQuery.pageIndex" :page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="commonResourceStudyPeopleQuery.pageSize" layout="total, sizes, prev, pager, next, jumper"
|
||||
@@ -393,14 +391,14 @@
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="学习详情" v-if="examResourceStudyPeopleShow" :visible.sync="examResourceStudyPeopleShow" width="50%"
|
||||
:append-to-body="true">
|
||||
<el-dialog title="学习详情" class="common-course-dialog" v-if="examResourceStudyPeopleShow"
|
||||
:visible.sync="examResourceStudyPeopleShow" width="65%" :append-to-body="true">
|
||||
<el-row style="margin: 20px 0 20px -10px;" :gutter="20">
|
||||
<el-col :span="6">
|
||||
<div class="grid-content bg-purple">
|
||||
<NameFilterSelect ref="examResourceStudyPeopleNameFilter"
|
||||
@handleNameChange="aids => examResourceStudyPeopleQuery.aid = aids"
|
||||
@handleClose="examResourceStudyPeopleQuery.aid = []" />
|
||||
<NameFilterSelect ref="examResourceStudyPeopleNameFilter" placeholder="姓名"
|
||||
@change="aids => examResourceStudyPeopleQuery.aid = aids.value"
|
||||
:option-formatter="formatOption" :fetcher="fetchNameList" />
|
||||
<!--
|
||||
<el-select :key="4" style="width:100%" clearable multiple v-model="examResourceStudyPeopleQuery.aid"
|
||||
filterable placeholder="姓名" v-limit-input="50" remote reserve-keyword :remote-method="initNameList"
|
||||
@@ -424,7 +422,7 @@
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="grid-content bg-purple">
|
||||
<el-button type="primary" @click="queryExamStudyPeopleList">查 询</el-button>
|
||||
<el-button type="primary" @click="queryExamStudyPeopleList(true)">查 询</el-button>
|
||||
<el-button @click="resetExamCommonResourceQuery">重 置</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
@@ -446,8 +444,8 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="aname" label="姓名"></el-table-column>
|
||||
<el-table-column prop="code" label="工号"></el-table-column>
|
||||
<el-table-column prop="orgInfo" label="部门">
|
||||
<el-table-column prop="code" label="工号" width="150"></el-table-column>
|
||||
<el-table-column prop="orgInfo" label="部门" width="220">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip class="item" effect="dark" :content="scope.row.orgInfo" placement="top-start">
|
||||
<p class="no-wrap">
|
||||
@@ -463,7 +461,7 @@
|
||||
<el-table-column prop="score" label="成绩"></el-table-column>
|
||||
<el-table-column prop="finishTime" width="200" label="完成时间"></el-table-column>
|
||||
</el-table>
|
||||
<div style="text-align: center;padding: 10px;">
|
||||
<div class="pagination">
|
||||
<el-pagination @size-change="handleSizeChangeExamStudyPeople"
|
||||
@current-change="handleCurrentChangeExamStudyPeople" :current-page="examResourceStudyPeopleQuery.pageIndex"
|
||||
:page-sizes="[10, 20, 30, 40]" :page-size="examResourceStudyPeopleQuery.pageSize"
|
||||
@@ -496,8 +494,6 @@ import axios from "axios";
|
||||
import NameFilterSelect from "@/components/NameFilterSelect/index.vue";
|
||||
import SignupModal from "@/components/signup/SignupModal.vue";
|
||||
import AudienceModal from "@/components/signup/AudienceModal.vue";
|
||||
|
||||
NameFilterSelect;
|
||||
export default {
|
||||
components: { NameFilterSelect, SignupModal, AudienceModal },
|
||||
props: {
|
||||
@@ -680,6 +676,11 @@ export default {
|
||||
mounted() {
|
||||
this.getCourseDetailCrowds();
|
||||
this.getSignupList();
|
||||
|
||||
this.getStudyRecords();
|
||||
this.recourseListQuery.courseId = this.courseDetail.id;
|
||||
this.getResourseList();
|
||||
|
||||
this.getResOwnerTree().then((rs) => {
|
||||
this.resOwnerListMap = rs;
|
||||
});
|
||||
@@ -695,13 +696,36 @@ export default {
|
||||
loadSysTypes: "sysType/loadSysTypes",
|
||||
}),
|
||||
|
||||
async fetchNameList(query) {
|
||||
console.log('in fetchNameList')
|
||||
const res = await apiUserbasic.selectUser(query);
|
||||
if (res && res.status === 200) {
|
||||
return res.result || [];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
formatOption(item = {}) {
|
||||
return {
|
||||
value: item.id,
|
||||
label: item.realName,
|
||||
code: item.userNo,
|
||||
};
|
||||
},
|
||||
queryCommonResource(flag) {
|
||||
if (this.rousourceRow.contentType == "62") {
|
||||
this.queryAssessStudyPeopleList(flag);
|
||||
} else {
|
||||
this.queryResourceStudyPeopleList(flag);
|
||||
}
|
||||
},
|
||||
|
||||
// 查询课程详情,获取 crowds 信息
|
||||
getCourseDetailCrowds() {
|
||||
if (!this.courseDetail || !this.courseDetail.id) return;
|
||||
apiCourse
|
||||
.detail(this.courseDetail.id)
|
||||
.then((res) => {
|
||||
console.log('res1', res);
|
||||
console.log("res1", res);
|
||||
const result = res.result || {};
|
||||
this.courseCrowds = Array.isArray(result.crowds) ? result.crowds : [];
|
||||
})
|
||||
@@ -722,30 +746,38 @@ export default {
|
||||
this.getSignupList();
|
||||
},
|
||||
handleDeleteSignup(row) {
|
||||
this.$confirm(`<i class="el-icon-warning-outline"></i>确定删除${row.name || ''}的报名记录吗?`, '删除确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
dangerouslyUseHTMLString: true,
|
||||
type: 'warning',
|
||||
customClass: 'custom-confirm-dialog'
|
||||
}).then(() => {
|
||||
apicourseStudy.deleteNewSignUp({
|
||||
id: row.id,
|
||||
courseId: this.courseDetail.id,
|
||||
studentId: row.aid
|
||||
this.$confirm(
|
||||
`<i class="el-icon-warning-outline"></i>确定删除${
|
||||
row.name || ""
|
||||
}的报名记录吗?`,
|
||||
"删除确认",
|
||||
{
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
dangerouslyUseHTMLString: true,
|
||||
type: "warning",
|
||||
customClass: "custom-confirm-dialog",
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
apicourseStudy
|
||||
.deleteNewSignUp({
|
||||
id: row.id,
|
||||
courseId: this.courseDetail.id,
|
||||
studentId: row.aid,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res && res.status === 200) {
|
||||
this.$showManageMessage("删除成功", "success");
|
||||
this.getSignupList();
|
||||
} else if (res) {
|
||||
this.$showManageMessage(res.message || "删除失败", "error");
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$showManageMessage("删除失败", "error");
|
||||
});
|
||||
})
|
||||
.then((res) => {
|
||||
if (res && res.status === 200) {
|
||||
this.$showMessage("删除成功", 'success');
|
||||
this.getSignupList();
|
||||
} else if (res) {
|
||||
this.$showMessage(res.message || "删除失败", 'error');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$showMessage("删除失败", 'error');
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
// this.$showMessage('已取消删除', 'info');
|
||||
});
|
||||
@@ -761,7 +793,11 @@ export default {
|
||||
status: "",
|
||||
aid: [],
|
||||
};
|
||||
this.queryResourceStudyPeopleList();
|
||||
if (this.rousourceRow.contentType == "62") {
|
||||
this.queryAssessStudyPeopleList();
|
||||
} else {
|
||||
this.queryResourceStudyPeopleList();
|
||||
}
|
||||
},
|
||||
|
||||
resetExamCommonResourceQuery() {
|
||||
@@ -778,8 +814,6 @@ export default {
|
||||
},
|
||||
|
||||
handleShowResourdeDetailList(row) {
|
||||
console.log(row);
|
||||
console.log(7777);
|
||||
this.commonResourceStudyPeopleQuery = {
|
||||
pageIndex: 1, //第几页
|
||||
pageSize: 10, // 每页多少条
|
||||
@@ -804,7 +838,10 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
queryExamStudyPeopleList() {
|
||||
queryExamStudyPeopleList(resetPage) {
|
||||
if (resetPage) {
|
||||
this.examResourceStudyPeopleQuery.pageIndex = 1;
|
||||
}
|
||||
apicourseStudy
|
||||
.contentsExam({
|
||||
courseId: this.courseDetail.id,
|
||||
@@ -831,7 +868,11 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
queryAssessStudyPeopleList() {
|
||||
queryAssessStudyPeopleList(resetPage) {
|
||||
if (resetPage) {
|
||||
this.commonResourceStudyPeopleQuery.pageIndex = 1;
|
||||
|
||||
}
|
||||
apicourseStudy
|
||||
.contentsAssess({
|
||||
courseId: this.courseDetail.id,
|
||||
@@ -858,9 +899,10 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
queryResourceStudyPeopleList() {
|
||||
console.log(this.rousourceRow);
|
||||
console.log(888);
|
||||
queryResourceStudyPeopleList(resetPage) {
|
||||
if (resetPage) {
|
||||
this.commonResourceStudyPeopleQuery.pageIndex = 1;
|
||||
}
|
||||
apicourseStudy
|
||||
.studyContentRecords({
|
||||
courseId: this.courseDetail.id,
|
||||
@@ -938,6 +980,7 @@ export default {
|
||||
apicourseStudy
|
||||
.studyExport({
|
||||
courseId: this.courseDetail.id,
|
||||
contentName: this.recourseListQuery.contentName,
|
||||
})
|
||||
.then((res) => {
|
||||
this.handleExport(
|
||||
@@ -950,10 +993,12 @@ export default {
|
||||
apicourseStudy
|
||||
.studyExport({
|
||||
courseId: this.courseDetail.id,
|
||||
aname: this.learningRecords.name,
|
||||
aid: this.learningRecords.aid.join(","),
|
||||
status: this.learningRecords.status,
|
||||
queryStartTime: this.learningRecords.queryStartTime,
|
||||
queryFinishTime: this.learningRecords.queryFinishTime,
|
||||
queryStartTime:
|
||||
this.studyDateTime.length > 0 ? this.studyDateTime[0] : "",
|
||||
queryFinishTime:
|
||||
this.studyDateTime.length > 1 ? this.studyDateTime[1] : "",
|
||||
})
|
||||
.then((res) => {
|
||||
this.handleExport(res, this.courseDetail.name + "的学习记录.xlsx");
|
||||
@@ -963,7 +1008,7 @@ export default {
|
||||
apicourseStudy
|
||||
.exportSignup({
|
||||
courseId: this.courseDetail.id,
|
||||
name: this.signup.name,
|
||||
aid: this.signup.aid.join(","),
|
||||
signType: this.signup.signType,
|
||||
})
|
||||
.then((res) => {
|
||||
@@ -972,6 +1017,8 @@ export default {
|
||||
},
|
||||
resetResourseList() {
|
||||
this.recourseListQuery.contentName = "";
|
||||
this.recourseListQuery.pageIndex = 1;
|
||||
this.recourseListQuery.pageSize = 10;
|
||||
this.getResourseList();
|
||||
},
|
||||
resetStudyRecords() {
|
||||
@@ -982,6 +1029,8 @@ export default {
|
||||
this.studyDateTime = [];
|
||||
this.learningRecords.queryStartTime = "";
|
||||
this.learningRecords.queryFinishTime = "";
|
||||
this.learningRecords.pageIndex = 1;
|
||||
this.learningRecords.pageSize = 10;
|
||||
this.getStudyRecords();
|
||||
},
|
||||
|
||||
@@ -992,6 +1041,8 @@ export default {
|
||||
signType: "",
|
||||
aid: [],
|
||||
};
|
||||
this.study.pageIndex = 1;
|
||||
this.study.pageSize = 10;
|
||||
this.getSignupList();
|
||||
},
|
||||
resOwnerName(code) {
|
||||
@@ -1006,7 +1057,10 @@ export default {
|
||||
}
|
||||
return this.sysTypeMap.get(code);
|
||||
},
|
||||
getResourseList() {
|
||||
getResourseList(resetPage) {
|
||||
if (resetPage) {
|
||||
this.recourseListQuery.pageIndex = 1;
|
||||
}
|
||||
apiCoursePortal.pageListResource(this.recourseListQuery).then((rs) => {
|
||||
if (rs.status == 200) {
|
||||
this.recourseList = rs.result.list;
|
||||
@@ -1101,12 +1155,21 @@ export default {
|
||||
handleSizeChangeStudyPeople(val) {
|
||||
this.commonResourceStudyPeopleQuery.pageSize = val;
|
||||
this.commonResourceStudyPeopleQuery.pageIndex = 1;
|
||||
this.queryResourceStudyPeopleList();
|
||||
if (this.rousourceRow.contentType == "62") {
|
||||
this.queryAssessStudyPeopleList();
|
||||
} else {
|
||||
this.queryResourceStudyPeopleList();
|
||||
}
|
||||
},
|
||||
|
||||
handleCurrentChangeStudyPeople(val) {
|
||||
this.commonResourceStudyPeopleQuery.pageIndex = val;
|
||||
//console.log('learningSituation.pageIndex',this.learningSituation.pageIndex);
|
||||
this.queryResourceStudyPeopleList();
|
||||
if (this.rousourceRow.contentType == "62") {
|
||||
this.queryAssessStudyPeopleList();
|
||||
} else {
|
||||
this.queryResourceStudyPeopleList();
|
||||
}
|
||||
},
|
||||
handleSizeChangeSituation(val) {
|
||||
this.learningSituation.pageSize = val;
|
||||
@@ -1125,12 +1188,15 @@ export default {
|
||||
this.getStudyDetail();
|
||||
},
|
||||
handleCurrentStudyDetail(val) {
|
||||
this.learningSituation.pageIndex = val;
|
||||
this.studyDetailQuery.pageIndex = val;
|
||||
//console.log('learningSituation.pageIndex',this.learningSituation.pageIndex);
|
||||
this.getStudyDetail();
|
||||
},
|
||||
// 学习记录
|
||||
getStudyRecords() {
|
||||
getStudyRecords(resetPage) {
|
||||
if (resetPage) {
|
||||
this.learningRecords.pageIndex = 1;
|
||||
}
|
||||
let params = {
|
||||
courseId: this.courseDetail.id, //课程的id
|
||||
status: this.learningRecords.status, //状态
|
||||
@@ -1162,10 +1228,10 @@ export default {
|
||||
getStudyDetail() {
|
||||
apiCoursePortal.detailStudyPage(this.studyDetailQuery).then((res) => {
|
||||
if (res.status == 200) {
|
||||
if (res.result && res.result.length > 0) {
|
||||
if (res.result && res.result.list && res.result.list.length > 0) {
|
||||
this.showStudyDetailPage = true;
|
||||
this.courseStudyList = res.result.list;
|
||||
this.getStudyDetail.count = res.result.count;
|
||||
this.studyDetailQuery.count = res.result.count;
|
||||
} else {
|
||||
this.showStudyDetailPage = false;
|
||||
this.courseStudyList = [
|
||||
@@ -1196,7 +1262,10 @@ export default {
|
||||
this.getSignupList();
|
||||
},
|
||||
// 报名列表
|
||||
getSignupList() {
|
||||
getSignupList(resetPage) {
|
||||
if (resetPage) {
|
||||
this.study.pageIndex = 1;
|
||||
}
|
||||
let params = {
|
||||
courseId: this.courseDetail.id, //课程的id
|
||||
signType: this.signup.signType, //报名方式
|
||||
@@ -1237,7 +1306,6 @@ export default {
|
||||
});
|
||||
console.log(11111);
|
||||
// this.study.list = list;
|
||||
|
||||
});
|
||||
resolve();
|
||||
} else {
|
||||
@@ -1256,19 +1324,23 @@ export default {
|
||||
this.study.pageIndex = val;
|
||||
this.getSignupList();
|
||||
},
|
||||
handleTabClick(tab) {
|
||||
if (tab.name === "second") {
|
||||
this.getSignupList();
|
||||
} else if (tab.name === "third") {
|
||||
this.getStudyRecords();
|
||||
} else {
|
||||
// 资源
|
||||
this.recourseListQuery.courseId = this.courseDetail.id;
|
||||
this.getResourseList();
|
||||
}
|
||||
|
||||
this.tabName = tab.name;
|
||||
handleBack() {
|
||||
window.history.back();
|
||||
},
|
||||
// handleTabClick(tab) {
|
||||
// if (tab.name === "second") {
|
||||
// this.getSignupList();
|
||||
// } else if (tab.name === "third") {
|
||||
// this.getStudyRecords();
|
||||
// } else {
|
||||
// // 资源
|
||||
// this.getStudyRecords();
|
||||
// this.recourseListQuery.courseId = this.courseDetail.id;
|
||||
// this.getResourseList();
|
||||
// }
|
||||
|
||||
// // this.tabName = tab.name;
|
||||
// },
|
||||
showStudyDetails(row) {
|
||||
this.studyDetailQuery.courseId = row.courseId;
|
||||
this.studyDetailQuery.aid = row.aid;
|
||||
@@ -1282,7 +1354,7 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.no-wrap {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -1292,30 +1364,109 @@ export default {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.delete-action-link--danger {
|
||||
color: #E32E2E;
|
||||
color: #e32e2e;
|
||||
&:hover {
|
||||
color: #E32E2E;
|
||||
color: #e32e2e;
|
||||
}
|
||||
&:active {
|
||||
color: #E32E2E;
|
||||
color: #e32e2e;
|
||||
}
|
||||
&:focus {
|
||||
color: #E32E2E;
|
||||
color: #e32e2e;
|
||||
}
|
||||
}
|
||||
#courseManage {
|
||||
.pagination {
|
||||
text-align: right;
|
||||
padding-top: 20px;
|
||||
::v-deep .el-pagination {
|
||||
.el-pagination__total {
|
||||
font-size: 14px;
|
||||
color: #000000;
|
||||
}
|
||||
.el-pagination__sizes {
|
||||
margin-right: 4px;
|
||||
.el-input {
|
||||
margin: 0;
|
||||
width: 89px;
|
||||
}
|
||||
.el-input__inner {
|
||||
width: 89px;
|
||||
background: #f5f9ff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dfdfdf;
|
||||
height: 28px;
|
||||
font-size: 14px;
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
.btn-prev,
|
||||
.btn-next {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: #f5f9ff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dfdfdf;
|
||||
// &:hover {
|
||||
// background: #4284F7;
|
||||
// color: #FFFFFF;
|
||||
// }
|
||||
}
|
||||
.btn-quicknext {
|
||||
background: transparent;
|
||||
border: none;
|
||||
line-height: 44px;
|
||||
&:before {
|
||||
content: "......";
|
||||
}
|
||||
}
|
||||
.el-pager {
|
||||
.number {
|
||||
min-width: 28px;
|
||||
height: 28px;
|
||||
background: #f5f9ff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dfdfdf;
|
||||
font-weight: normal;
|
||||
color: #000000;
|
||||
margin: 0 4px;
|
||||
&.active {
|
||||
background: #4284f7;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-pagination__jump {
|
||||
font-size: 14px;
|
||||
color: #000000;
|
||||
margin-left: 4px;
|
||||
.el-input__inner {
|
||||
width: 100%;
|
||||
min-width: 28px;
|
||||
height: 28px;
|
||||
background: #f5f9ff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dfdfdf;
|
||||
font-size: 14px;
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.option-code {
|
||||
margin-left: 4px;
|
||||
color: #999;
|
||||
}
|
||||
.noSplitDatePicker {
|
||||
::v-deep .noSplitDatePicker {
|
||||
/* 隐藏范围选择器的分隔符和占位符 */
|
||||
.el-range-separator,
|
||||
.el-range__close-icon {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.resetDatePicker {
|
||||
::v-deep .resetDatePicker {
|
||||
.el-range-input {
|
||||
text-align: left;
|
||||
}
|
||||
@@ -1342,14 +1493,43 @@ export default {
|
||||
}
|
||||
.course-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 40px;
|
||||
.course-desc-item {
|
||||
margin-right: 40px;
|
||||
.title {
|
||||
font-weight: bold;
|
||||
justify-content: space-between;
|
||||
.course-info-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.course-info-right {
|
||||
flex-shrink: 0;
|
||||
text-align: right;
|
||||
::v-deep .el-button {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.course-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.course-name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.course-type {
|
||||
// width: 48px;
|
||||
padding: 0 5px;
|
||||
height: 16px;
|
||||
background: #FF8F20;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.course-sys-type {
|
||||
margin: 8px 0;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ment-div {
|
||||
@@ -1387,4 +1567,9 @@ export default {
|
||||
.el-table .el-table__body-wrapper::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera*/
|
||||
}
|
||||
|
||||
::v-deep.el-table .el-table__header-wrapper .el-table__header th {
|
||||
background: rgba(66, 132, 247, 0.1);
|
||||
color: #60769d;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -60,101 +60,108 @@
|
||||
</div>
|
||||
<div style="width:390px">
|
||||
<el-button type="primary" @click="searchData(true)" icon="el-icon-search" >搜索</el-button>
|
||||
<el-button icon="el-icon-refresh-right" type="primary" style="margin-left:5px" @click="reset">重置</el-button>
|
||||
<el-button icon="el-icon-refresh-right" type="primary" style="margin-left:5px" @click="reset">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-row :gutter="20" style="margin-top:10px">
|
||||
<el-col :span="4">
|
||||
<!-- <el-button icon="el-icon-folder" type="primary" size="small">导出</el-button> -->
|
||||
<el-button class="Create-coures" type="primary" @click="addNewCourse()" icon="el-icon-plus">新建课程</el-button>
|
||||
</el-col >
|
||||
<!-- ai播放器相关 -->
|
||||
<el-col :span="24">
|
||||
<!-- <el-button icon="el-icon-folder" type="primary" size="small">导出</el-button> -->
|
||||
<el-button class="Create-coures" type="primary" @click="addNewCourse()" icon="el-icon-plus">新建课程</el-button>
|
||||
<el-button v-if="aiPermission" type="primary" @click="setLanguage()" icon="el-icon-connection" :disabled="selectedCourses.length === 0">设置语种</el-button>
|
||||
<el-button v-if="aiPermission" type="primary" @click="enableAI()" icon="el-icon-switch-button" :disabled="selectedCourses.length === 0">开启AI处理</el-button>
|
||||
</el-col >
|
||||
</el-row>
|
||||
</div>
|
||||
<div style="margin-right:30px;">
|
||||
<el-table style="margin:10px 32px 10px 22px;" :data="pageData" border stripe>
|
||||
<el-table-column label="序号" type="index" width="50"></el-table-column>
|
||||
<el-table-column v-if="forChoose" label="选择" width="80">
|
||||
<template slot-scope="scope" v-if="scope.row.published">
|
||||
<el-button type="default" size="mini" @click="handleChoose(scope.row)">选择</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称" prop="name" width="200" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<span class="previewStyle" @click="viewTopic(scope.row)">{{ scope.row.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="内容分类" prop="sysType" sortable width="240px">
|
||||
<template slot-scope="scope">
|
||||
<span>{{sysTypeName(scope.row.sysType1)}}</span>
|
||||
<span v-if="scope.row.sysType2 !=''">/{{sysTypeName(scope.row.sysType2)}}</span>
|
||||
<span v-if="scope.row.sysType3 !=''">/{{sysTypeName(scope.row.sysType3)}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="关键字" :show-overflow-tooltip="true" prop="name" width="200px">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.keywords }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="资源归属" sortable prop="author" width="240px">
|
||||
<template slot-scope="scope">
|
||||
<span>{{resOwnerName(scope.row.resOwner1)}}</span>
|
||||
<span v-if="scope.row.resOwner2 != ''">/{{resOwnerName(scope.row.resOwner2)}}</span>
|
||||
<span v-if="scope.row.resOwner3 != ''">/{{resOwnerName(scope.row.resOwner3)}}</span>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<!-- <el-table-column label="授课方式" prop="type" width="120px">
|
||||
<template slot-scope="scope">
|
||||
{{ courseType(scope.row.type)}}
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="状态" prop="status" width="120px">
|
||||
<template slot-scope="scope">
|
||||
<!-- 1,未提交 2.已提交 = 未审核 5 已审核 -->
|
||||
<span v-if="scope.row.status == 1">未提交</span>
|
||||
<span v-if="scope.row.status == 2">待审核</span>
|
||||
<span v-if="scope.row.status == 5">已审核</span>
|
||||
<span v-if="scope.row.status == 3">审核未通过</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否发布" width="130px">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.published == true ? '已发布' : '未发布' }}
|
||||
</template>
|
||||
</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="是否停用" width="130px">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.enabled == true ? '启用' : '停用' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否置顶" width="130px">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.isTop == true ? '置顶' : '未置顶' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180px" fixed="right">
|
||||
<template slot-scope="scope" class="btn-gl">
|
||||
<!-- 20240621 修改scope.row.isPermission = fasle 时不展示操作按钮-->
|
||||
<el-button type="text" size="mini" v-if="scope.row.isPermission && scope.row.status == 5 && !scope.row.published" @click="releaseData(scope.row)">发布</el-button>
|
||||
<el-button v-if="scope.row.isPermission && pageManage && scope.row.published" @click="showStudent(scope.row)" type="text" size="mini">学员</el-button>
|
||||
<el-button v-if="scope.row.isPermission && !forChoose && scope.row.published" @click="showManageStudy(scope.row)" type="text" size="mini">管理</el-button>
|
||||
<el-button v-if="scope.row.isPermission && !forChoose && scope.row.status == 2" @click="withdraw(scope.row)" type="text" size="mini">撤回</el-button>
|
||||
<el-button v-if="scope.row.isPermission && scope.row.status != 2" type="text" size="mini" @click="editCurriculum(scope.row)">编辑</el-button>
|
||||
<el-button v-if="scope.row.isPermission && (scope.row.status != 2 && !scope.row.published) || scope.row.isPermission &&!scope.row.enabled" type="text" size="mini" @click="delItem(scope.row)">删除</el-button>
|
||||
<el-dropdown v-if="scope.row.isPermission && scope.row.published" type="text" size="mini" style="margin-left:10px">
|
||||
<el-button type="text" size="mini">更多<i class="el-icon-arrow-down el-icon--right"></i></el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="copyCourse(scope.row)">复制</el-dropdown-item>
|
||||
<el-dropdown-item v-if="scope.row.published" @click.native="isDisable(scope.row)">{{scope.row.enabled? '停用':'启用'}}</el-dropdown-item>
|
||||
<el-dropdown-item v-if="scope.row.published" @click.native="showQrimage(scope.row)">二维码</el-dropdown-item><!--发布之后才可以查看二维码-->
|
||||
<el-dropdown-item v-if="scope.row.published" @click.native="setTop(scope.row)">{{scope.row.isTop? '取消置顶':'置顶'}}</el-dropdown-item>
|
||||
<!-- ai播放器相关 -->
|
||||
<el-table style="margin:10px 32px 10px 22px;" :data="pageData" border stripe @selection-change="handleSelectionChange">
|
||||
<el-table-column v-if="aiPermission" type="selection" width="55"></el-table-column>
|
||||
<el-table-column label="序号" type="index" width="50"></el-table-column>
|
||||
<el-table-column v-if="forChoose" label="选择" width="80">
|
||||
<template slot-scope="scope" v-if="scope.row.published">
|
||||
<el-button type="default" size="mini" @click="handleChoose(scope.row)">选择</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称" prop="name" width="200" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<span class="previewStyle" @click="viewTopic(scope.row)">{{ scope.row.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="内容分类" prop="sysType" sortable width="240px">
|
||||
<template slot-scope="scope">
|
||||
<span>{{sysTypeName(scope.row.sysType1)}}</span>
|
||||
<span v-if="scope.row.sysType2 !=''">/{{sysTypeName(scope.row.sysType2)}}</span>
|
||||
<span v-if="scope.row.sysType3 !=''">/{{sysTypeName(scope.row.sysType3)}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="关键字" :show-overflow-tooltip="true" prop="name" width="200px">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.keywords }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="资源归属" sortable prop="author" width="240px">
|
||||
<template slot-scope="scope">
|
||||
<span>{{resOwnerName(scope.row.resOwner1)}}</span>
|
||||
<span v-if="scope.row.resOwner2 != ''">/{{resOwnerName(scope.row.resOwner2)}}</span>
|
||||
<span v-if="scope.row.resOwner3 != ''">/{{resOwnerName(scope.row.resOwner3)}}</span>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<!-- <el-table-column label="授课方式" prop="type" width="120px">
|
||||
<template slot-scope="scope">
|
||||
{{ courseType(scope.row.type)}}
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="状态" prop="status" width="120px">
|
||||
<template slot-scope="scope">
|
||||
<!-- 1,未提交 2.已提交 = 未审核 5 已审核 -->
|
||||
<span v-if="scope.row.status == 1">未提交</span>
|
||||
<span v-if="scope.row.status == 2">待审核</span>
|
||||
<span v-if="scope.row.status == 5">已审核</span>
|
||||
<span v-if="scope.row.status == 3">审核未通过</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否发布" width="130px">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.published == true ? '已发布' : '未发布' }}
|
||||
</template>
|
||||
</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="是否停用" width="130px">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.enabled == true ? '启用' : '停用' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否置顶" width="130px">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.isTop == true ? '置顶' : '未置顶' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180px" fixed="right">
|
||||
<template slot-scope="scope" class="btn-gl">
|
||||
<!-- 20240621 修改scope.row.isPermission = fasle 时不展示操作按钮-->
|
||||
<!-- ai播放器相关 -->
|
||||
<el-button v-if="aiPermission && scope.row.isPermission && scope.row.status != 2" type="text" size="mini" @click="setAI(scope.row)">AI设置</el-button>
|
||||
<el-button type="text" size="mini" v-if="scope.row.isPermission && scope.row.status == 5 && !scope.row.published" @click="releaseData(scope.row)">发布</el-button>
|
||||
<el-button v-if="scope.row.isPermission && pageManage && scope.row.published" @click="showStudent(scope.row)" type="text" size="mini">学员</el-button>
|
||||
<el-button v-if="scope.row.isPermission && !forChoose && scope.row.published" @click="showManageStudy(scope.row)" type="text" size="mini">管理</el-button>
|
||||
<el-button v-if="scope.row.isPermission && !forChoose && scope.row.status == 2" @click="withdraw(scope.row)" type="text" size="mini">撤回</el-button>
|
||||
<el-button v-if="scope.row.isPermission && scope.row.status != 2" type="text" size="mini" @click="editCurriculum(scope.row)">编辑</el-button>
|
||||
<el-button v-if="scope.row.isPermission && (scope.row.status != 2 && !scope.row.published) || scope.row.isPermission &&!scope.row.enabled" type="text" size="mini" @click="delItem(scope.row)">删除</el-button>
|
||||
<el-dropdown v-if="scope.row.isPermission && scope.row.published" type="text" size="mini" style="margin-left:10px">
|
||||
<el-button type="text" size="mini">更多<i class="el-icon-arrow-down el-icon--right"></i></el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="copyCourse(scope.row)">复制</el-dropdown-item>
|
||||
<el-dropdown-item v-if="scope.row.published" @click.native="isDisable(scope.row)">{{scope.row.enabled? '停用':'启用'}}</el-dropdown-item>
|
||||
<el-dropdown-item v-if="scope.row.published" @click.native="showQrimage(scope.row)">二维码</el-dropdown-item><!--发布之后才可以查看二维码-->
|
||||
<el-dropdown-item v-if="scope.row.published" @click.native="setTop(scope.row)">{{scope.row.isTop? '取消置顶':'置顶'}}</el-dropdown-item>
|
||||
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -287,6 +294,236 @@
|
||||
<div>
|
||||
<course-form ref="courseForm" @submitSuccess="searchData" @close="searchData"></course-form>
|
||||
</div>
|
||||
|
||||
<!-- ai播放器相关 -->
|
||||
<!-- 设置语种弹框 -->
|
||||
<el-dialog
|
||||
title="AI翻译"
|
||||
:visible.sync="languageSetting.dlgShow"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div style="margin-bottom: 20px;">
|
||||
<div style="margin-bottom: 15px;">请选择课程所支持语种</div>
|
||||
<el-select
|
||||
v-model="languageSetting.languageCode"
|
||||
multiple
|
||||
placeholder="请选择语种"
|
||||
style="width: 100%;"
|
||||
>
|
||||
<el-option
|
||||
v-for="lang in selectAllLang"
|
||||
:key="lang.srclang"
|
||||
:label="lang.label"
|
||||
:value="lang.srclang"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div style="color: #ff4d4f; font-size: 12px;">
|
||||
注:仅支持对已开启AI处理的课程进行批量语种设置;所选的课程中有{{languageSetting.aiSetNoNum}}个未开启AI处理的课程,以上配置仅对{{languageSetting.aiSetNum}}个已开启AI处理的课程生效。
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="languageSetting.dlgShow = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmLanguageSetting">确认</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 开启AI处理弹框 -->
|
||||
<el-dialog
|
||||
title="开启AI处理"
|
||||
:visible.sync="aiProcessSetting.dlgShow"
|
||||
width="400px"
|
||||
>
|
||||
<div class="ai-process-dialog">
|
||||
<!-- AI处理状态 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">
|
||||
<el-tooltip class="item" effect="dark" :content="aiSetTip" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
AI处理:
|
||||
</span>
|
||||
<span class="status-text">
|
||||
{{ aiProcessSetting.aiSet === 1 ? '开启' : '关闭' }}
|
||||
</span>
|
||||
<el-switch
|
||||
v-model="aiProcessSetting.aiSet"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
></el-switch>
|
||||
</div>
|
||||
|
||||
<div v-show="aiProcessSetting.aiSet === 1">
|
||||
<!-- 是否生成AI摘要 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">
|
||||
<el-tooltip class="item" effect="dark" :content="aiAbstractTip" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
AI摘要:
|
||||
</span>
|
||||
<span class="status-text">
|
||||
{{ aiProcessSetting.aiAbstract === 1 ? '开启' : '关闭' }}
|
||||
</span>
|
||||
<el-switch
|
||||
v-model="aiProcessSetting.aiAbstract"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
>
|
||||
</el-switch>
|
||||
</div>
|
||||
|
||||
<!-- 是否生成AI文稿 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">
|
||||
<el-tooltip class="item" effect="dark" :content="aiDraftTip" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
AI文稿:
|
||||
</span>
|
||||
<span class="status-text">
|
||||
{{ aiProcessSetting.aiDraft === 1 ? '开启' : '关闭' }}
|
||||
</span>
|
||||
<el-switch
|
||||
v-model="aiProcessSetting.aiDraft"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
>
|
||||
</el-switch>
|
||||
</div>
|
||||
|
||||
<!-- 课程支持语种选择 -->
|
||||
<div class="form-item" style="flex-flow: column;align-items: baseline;">
|
||||
<span class="form-label">
|
||||
<el-tooltip class="item" effect="dark" :content="aiTranslateTip" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
AI翻译语种:
|
||||
</span>
|
||||
<el-select
|
||||
v-model="aiProcessSetting.languageCode"
|
||||
multiple
|
||||
style="width: 100%;"
|
||||
placeholder="请选择语种"
|
||||
>
|
||||
<el-option
|
||||
v-for="lang in selectAllLang"
|
||||
:key="lang.srclang"
|
||||
:label="lang.label"
|
||||
:value="lang.srclang"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示信息 -->
|
||||
<div class="tips">
|
||||
<span>注:已跳过{{aiProcessSetting.aiSetNum}}个已开启AI处理的课程,仅更新剩余{{aiProcessSetting.aiSetNoNum}}个</span>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="aiProcessSetting.dlgShow = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmAiProcess">确认</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- AI设置弹框 -->
|
||||
<el-dialog
|
||||
title="AI设置"
|
||||
:visible.sync="aiSetting.dlgShow"
|
||||
width="500px"
|
||||
>
|
||||
<div class="ai-setting-dialog">
|
||||
|
||||
<!-- AI功能状态 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">
|
||||
<el-tooltip class="item" effect="dark" content="是否将课程进行AI处理" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
AI功能状态:
|
||||
</span>
|
||||
<span class="status-text">
|
||||
{{ aiSetting.aiSet === 1 ? '已开放' : '未开放' }}
|
||||
</span>
|
||||
<el-switch
|
||||
v-model="aiSetting.aiSet"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
></el-switch>
|
||||
</div>
|
||||
<div v-show="aiSetting.aiSet === 1">
|
||||
<!-- AI摘要状态 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">AI摘要状态:</span>
|
||||
<span :class="aiSetting.aiAbstract === 1 ? 'custom-putaway' : 'custom-takeout'">
|
||||
{{ aiSetting.aiAbstract === 1 ? '已上架' : '已下架' }}
|
||||
</span>
|
||||
<div class="action-buttons">
|
||||
<el-button type="text" @click="changeAIKey('aiAbstract')">
|
||||
{{ aiSetting.aiAbstract === 1 ? '下架' : '上架' }}
|
||||
</el-button>
|
||||
<el-button v-show="false" type="text" >编辑</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI文稿状态 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">AI文稿状态:</span>
|
||||
<span :class="aiSetting.aiDraft === 1 ? 'custom-putaway' : 'custom-takeout'">
|
||||
{{ aiSetting.aiDraft === 1 ? '已上架' : '已下架' }}
|
||||
</span>
|
||||
<div class="action-buttons">
|
||||
<el-button type="text" @click="changeAIKey('aiDraft')">
|
||||
{{ aiSetting.aiDraft === 1 ? '下架' : '上架' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI翻译状态 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">AI翻译状态:</span>
|
||||
<span :class="aiSetting.aiTranslate === 1 ? 'custom-putaway' : 'custom-takeout'">
|
||||
{{ aiSetting.aiTranslate === 1 ? '已上架' : '已下架' }}
|
||||
</span>
|
||||
<div class="action-buttons">
|
||||
<el-button type="text" @click="changeAIKey('aiTranslate')">
|
||||
{{ aiSetting.aiTranslate === 1 ? '下架' : '上架' }}
|
||||
</el-button>
|
||||
<el-button v-show="false" type="text" >编辑</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 支持语种 -->
|
||||
<div class="form-item" style="align-items: flex-start;">
|
||||
<span class="form-label" style="white-space: nowrap;">支持语种:</span>
|
||||
<div class="languages-list" v-show="false">
|
||||
<div v-for="lang in aiSetting.languageCode" :key="lang" class="language-tag">
|
||||
{{ getLanguageName(lang) }}
|
||||
<span class="custom-takeout">已下架</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-select
|
||||
v-model="aiSetting.languageCode"
|
||||
multiple
|
||||
style="width: 100%;"
|
||||
placeholder="请选择语种"
|
||||
>
|
||||
<el-option
|
||||
v-for="lang in selectAllLang"
|
||||
:key="lang.srclang"
|
||||
:label="lang.label"
|
||||
:value="lang.srclang"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="aiSetting.dlgShow = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmAISetting">确认</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -299,7 +536,7 @@ import auditCourse2 from '@/components/Course/auditCourse2.vue';
|
||||
import adminPage from '@/components/Administration/adminPage.vue';
|
||||
import apiResowner from '../../api/modules/resowner.js';
|
||||
import apiType from '../../api/modules/type.js'
|
||||
import {courseType} from '../../utils/tools.js';
|
||||
import {courseType, deepCopy} from '../../utils/tools.js';
|
||||
import apiCourse from '../../api/modules/course.js';
|
||||
// import {resOwnerIndexName,sysTypeIndexName} from '@/utils/type.js';
|
||||
import { mapGetters,mapActions } from 'vuex';
|
||||
@@ -307,8 +544,9 @@ import apiUserbasic from "@/api/boe/userbasic.js"
|
||||
export default {
|
||||
name: 'manageCourse',
|
||||
components: {courseForm, manager, auditCourse1, auditCourse2,adminPage},
|
||||
// ai播放器相关
|
||||
computed: {
|
||||
...mapGetters(['resOwnerMap','sysTypeMap','userInfo']),
|
||||
...mapGetters(['resOwnerMap','sysTypeMap','userInfo', 'selectAllLang']),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -392,9 +630,38 @@ export default {
|
||||
},
|
||||
extendRefId:'',
|
||||
extendRefType:'',
|
||||
// ai播放器相关
|
||||
aiPermission: false,
|
||||
selectedCourses: [], //已选课程
|
||||
languageSetting: { // 设置语种弹框
|
||||
dlgShow: false,
|
||||
languageCode: ['zh-CN', 'en-US'] // 默认选中的语种
|
||||
},
|
||||
aiProcessSetting: { // 开启AI处理弹框
|
||||
dlgShow: false,
|
||||
aiSet: 1,
|
||||
aiAbstract: 1,
|
||||
aiDraft: 1,
|
||||
languageCode: ['zh-CN', 'en-US'] // 默认选中的语种
|
||||
},
|
||||
aiSetting: { // AI设置弹框
|
||||
dlgShow: false,
|
||||
courseId: '',
|
||||
aiSet: 1,
|
||||
aiAbstract: 1, // 1:上架 0:下架
|
||||
aiDraft: 1, // 1:上架 0:下架
|
||||
aiTranslate: 1, // 1:上架 0:下架
|
||||
languageCode: ['zh-CN', 'en-US', 'vi-VN'] // 支持的语种
|
||||
},
|
||||
aiSetTip: '是否将课程进行AI处理', //提示信息
|
||||
aiAbstractTip: '一键提炼课程视频核心要点,助力学员课前高效掌握重点,快速筛选学习资源', // 提示信息
|
||||
aiDraftTip: '分段展示视频内容并精准同步时间轴,实现视频进度与文稿双向定位,学习内容触手可及', //提示信息
|
||||
aiTranslateTip: '智能转换视频字幕与语音为多语种,支持全球学员按需切换语言,打破学习边界', // 提示信息
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// ai播放器相关
|
||||
this.getAiPermission();
|
||||
this.getAudiences()
|
||||
let chooseFlag=this.$route.query.f;
|
||||
this.extendRefId=this.$route.query.refId;
|
||||
@@ -426,6 +693,7 @@ export default {
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
getAudiences(){
|
||||
apiUserbasic.getInAudienceIds().then(res=>{
|
||||
if (res.status == 200) {
|
||||
@@ -449,6 +717,7 @@ export default {
|
||||
inputOn() {
|
||||
this.$forceUpdate();
|
||||
},
|
||||
|
||||
// 置顶
|
||||
setTop(row) {
|
||||
let params = {
|
||||
@@ -885,6 +1154,150 @@ export default {
|
||||
saveNewCatalogZhang() {
|
||||
this.catalogs.addNewZhang = false;
|
||||
},
|
||||
|
||||
|
||||
// ai播放器相关
|
||||
getLanguageName(lang){
|
||||
return this.selectAllLang.find(item => item.srclang === lang)?.label || '';
|
||||
},
|
||||
handleSelectionChange(val){
|
||||
this.selectedCourses = val;
|
||||
console.log(val);
|
||||
},
|
||||
// 获取选中课程的AI信息
|
||||
getAIInfoByList(list = []) {
|
||||
let selectNum = 0; // 选中的课程数量
|
||||
let aiSetNum = 0; // 已设置AI的课程数量
|
||||
let aiSetNoNum = 0; // 未设置AI的课程数量
|
||||
list.forEach(item => {
|
||||
if(item.aiSet === 1){
|
||||
aiSetNum++;
|
||||
}else{
|
||||
aiSetNoNum++;
|
||||
}
|
||||
});
|
||||
return {
|
||||
selectNum,
|
||||
aiSetNum,
|
||||
aiSetNoNum
|
||||
}
|
||||
},
|
||||
// AI设置
|
||||
setAI(row) {
|
||||
console.log('row', row);
|
||||
this.aiSetting = {
|
||||
dlgShow: true,
|
||||
...row
|
||||
};
|
||||
},
|
||||
changeAIKey(key) {
|
||||
this.aiSetting[key] = this.aiSetting[key] === 1 ? 0 : 1;
|
||||
},
|
||||
// 确认AI设置
|
||||
confirmAISetting() {
|
||||
const item = deepCopy(this.aiSetting);
|
||||
item.languageStatus = item.aiSet;
|
||||
item.languageCode = item.languageCode || [];
|
||||
if (!item.languageCode.includes('zh-CN')) {
|
||||
item.languageCode.unshift('zh-CN'); // 默认添加中文 去重
|
||||
}
|
||||
this._benchAiSet([item], (res) => {
|
||||
this.$message.success('AI设置保存成功');
|
||||
this.aiSetting.dlgShow = false;
|
||||
// 可以选择是否刷新列表数据
|
||||
this.searchData();
|
||||
}, (res) => {
|
||||
this.$message.error('AI设置保存失败!');
|
||||
})
|
||||
},
|
||||
setLanguage() {
|
||||
if (this.selectedCourses.length > 0) {
|
||||
this.languageSetting = {...{
|
||||
dlgShow: true,
|
||||
languageCode: ['zh-CN', 'en-US'] // 默认选中的语种
|
||||
}, ...this.getAIInfoByList(this.selectedCourses)}
|
||||
}
|
||||
},
|
||||
enableAI() {
|
||||
// 开启AI处理按钮点击事件
|
||||
if (this.selectedCourses.length > 0) {
|
||||
this.aiProcessSetting = {...{
|
||||
dlgShow: true,
|
||||
aiSet: 1,
|
||||
aiAbstract: 1,
|
||||
aiDraft: 1,
|
||||
languageCode: ['zh-CN', 'en-US'] // 默认选中的语种
|
||||
}, ...this.getAIInfoByList(this.selectedCourses)}
|
||||
}
|
||||
},
|
||||
// 批量设置语种 - 确认
|
||||
confirmLanguageSetting() {
|
||||
const courseList = deepCopy(this.selectedCourses);
|
||||
let languageCode = deepCopy(this.languageSetting.languageCode || []);
|
||||
if (!languageCode.includes('zh-CN')) {
|
||||
languageCode.unshift('zh-CN'); // 默认添加中文 去重
|
||||
}
|
||||
courseList.forEach(item => {
|
||||
item.languageCode = languageCode;
|
||||
item.aiTranslate = item.aiSet;
|
||||
item.languageStatus = item.aiSet;
|
||||
})
|
||||
this._benchAiSet(courseList, (res) => {
|
||||
this.$message.success('设置语种成功!');
|
||||
this.languageSetting.dlgShow = false;
|
||||
// 可以选择是否刷新列表数据
|
||||
this.searchData();
|
||||
}, (res) => {
|
||||
this.$message.error('设置语种失败!');
|
||||
})
|
||||
},
|
||||
|
||||
// 批量开启AI处理 - 确认
|
||||
confirmAiProcess() {
|
||||
// 获取AI处理配置
|
||||
let { aiSet, aiAbstract, aiDraft, languageCode } = this.aiProcessSetting;
|
||||
const courseList = deepCopy(this.selectedCourses);
|
||||
languageCode = languageCode || [];
|
||||
if (!languageCode.includes('zh-CN')) {
|
||||
languageCode.unshift('zh-CN'); // 默认添加中文 去重
|
||||
}
|
||||
courseList.forEach(item => {
|
||||
item.aiSet = aiSet;
|
||||
item.aiAbstract = aiAbstract;
|
||||
item.aiDraft = aiDraft;
|
||||
item.aiTranslate = aiSet;
|
||||
item.languageStatus = aiSet;
|
||||
item.languageCode = languageCode;
|
||||
})
|
||||
this._benchAiSet(courseList, (res) => {
|
||||
this.$message.success('开启AI处理成功!');
|
||||
this.aiProcessSetting.dlgShow = false;
|
||||
// 可以选择是否刷新列表数据
|
||||
this.searchData();
|
||||
}, (res) => {
|
||||
this.$message.error('开启AI处理失败!');
|
||||
})
|
||||
},
|
||||
|
||||
_benchAiSet(courseList, successCB, failCB) {
|
||||
apiCourse.benchAiSet({courseList}).then(res => {
|
||||
if(res.status === 200){
|
||||
successCB && successCB(res);
|
||||
}else{
|
||||
failCB && failCB(res);
|
||||
}
|
||||
})
|
||||
},
|
||||
getAiPermission() {
|
||||
apiCourse.listByUser({}).then(res => {
|
||||
console.log('res', res);
|
||||
if(res.code === 200){
|
||||
let index = res.data.findIndex(item => item.permissionCode === 'KjbAiSetCode');
|
||||
this.aiPermission = index !== -1;
|
||||
console.log('index', index, this.aiPermission);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -953,4 +1366,22 @@ export default {
|
||||
.el-dialog__body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.form-item{
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.tips {
|
||||
color: #f56c6c;
|
||||
font-size: 12px;
|
||||
margin: 10px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.languages-list{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -111,9 +111,9 @@
|
||||
</div> -->
|
||||
|
||||
<div></div>
|
||||
<el-dialog title="二维码" :visible.sync="qrCodedialogVisible" width="900px" @close="closeCode" custom-class="g-dialog">
|
||||
<el-dialog title="二维码" :visible.sync="qrCodedialogVisible" width="700px" @close="closeCode" class="common-course-dialog">
|
||||
<div>
|
||||
<el-form size="medium" label-width="100px">
|
||||
<el-form size="medium" label-width="70px">
|
||||
<el-form-item label="二维码">
|
||||
<div id="qrcode" ref="qrcode" class="qrcode-img" @mouseenter="showDownloadButton = true"
|
||||
@mouseleave="showDownloadButton = false">
|
||||
@@ -125,10 +125,12 @@
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="链接">
|
||||
<el-form-item label="链接" >
|
||||
<div style="width:500px">
|
||||
<el-input v-model="copyUrl" readonly class="input-with-select" id="text">
|
||||
<el-button slot="append" @click="handleCopyUrl">复制</el-button>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="">上述内容兼容PC端与移动端,您可按需分享。</el-form-item>
|
||||
</el-form>
|
||||
@@ -137,23 +139,36 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- TODO 修改展示字段 -->
|
||||
<el-dialog title="审核记录" :visible.sync="dialogVisible" width="900px" custom-class="g-dialog">
|
||||
<el-dialog title="审核记录" :visible.sync="dialogVisible" width="900px" class="common-course-dialog">
|
||||
<div>
|
||||
<el-table max-height="500" border :data="inviteTeacher" style="width: 100%;">
|
||||
<el-table-column prop="type" label="审核类型">
|
||||
<el-table :header-cell-style="{textAlign: 'center', background: 'rgba(66, 132, 247, 0.1)',
|
||||
color: '#60769D'}"
|
||||
:cell-style="{ textAlign: 'center' }" max-height="500" border :data="inviteTeacher" style="width: 100%;">
|
||||
<el-table-column prop="auditType" width="80" label="审核类型">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.auditState ? '通过' : '不通过' }}
|
||||
{{ auditTypeEnum[scope.row.auditType] }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="审核状态">
|
||||
<el-table-column prop="status" width="100" 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="auditUser" width="80" label="审核人"></el-table-column>
|
||||
<el-table-column prop="auditResult" width="80" label="审核结果">
|
||||
<template slot-scope="scope">
|
||||
{{ auditResultEnum[scope.row.auditResult] }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="auditTime" width="220" label="审核时间"></el-table-column>
|
||||
<el-table-column prop="auditRemark" label="审核意见">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip class="item" popper-class="tooltip-multiline" effect="dark" :content="scope.row.auditRemark" placement="top-start">
|
||||
<p class="no-wrap">
|
||||
{{scope.row.auditRemark}}</p>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false">关 闭</el-button></span>
|
||||
@@ -274,9 +289,19 @@ export default {
|
||||
loading: false,
|
||||
noMore: false,
|
||||
auditEnum: {
|
||||
1: "未审核",
|
||||
1: "审核中",
|
||||
2: "审核不通过",
|
||||
9: "审核通过",
|
||||
},
|
||||
auditTypeEnum: {
|
||||
1: "新建",
|
||||
2: "编辑",
|
||||
3: "停用",
|
||||
4: "启用",
|
||||
},
|
||||
auditResultEnum: {
|
||||
0: "驳回",
|
||||
1: "通过",
|
||||
},
|
||||
hoverStates: {},
|
||||
};
|
||||
@@ -617,6 +642,14 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.no-wrap {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
word-break: break-all;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.page-tip {
|
||||
margin: 20px auto;
|
||||
text-align: center;
|
||||
|
||||
245
src/views/course/aiSet/aiAbstract.vue
Normal file
@@ -0,0 +1,245 @@
|
||||
<template>
|
||||
<div class="aiAbstract">
|
||||
<div class="ai-left">
|
||||
<div class="left-title">{{courseName}}</div>
|
||||
<ul class="ai-list">
|
||||
<li class="ai-item" v-for="(item, index) in videoList" @click="currentVideo = item" :class="{'active': currentVideo.id === item.id}" :key="index">
|
||||
<div class="ai-item-title">{{item.name}}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ai-right">
|
||||
<div class="right-title">
|
||||
<h3>{{currentVideo.name}}</h3>
|
||||
<div>
|
||||
<el-button type="primary" @click="status = 1">下架本课程AI摘要</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-content">
|
||||
<div class="videoBox">
|
||||
<videoPlayer :src="testUrl" style="height: auto;"> </videoPlayer>
|
||||
<div class="video-content">
|
||||
<h4>视频摘要</h4>
|
||||
<p>
|
||||
{{currentVideo.aiAbstract}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="videoOperation">
|
||||
<div class="opera-title">
|
||||
<h4>课程摘要</h4>
|
||||
<div class="opera-btn">
|
||||
<el-button type="primary" plain round size="mini" @click="status = 2">重新生成</el-button>
|
||||
<el-button type="primary" plain round size="mini" @click="status = 4">编辑</el-button>
|
||||
<el-button plain round size="mini" @click="status = 3">取消</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="opera-content" v-show="status != 4">
|
||||
<span v-show="status == 1">{{ aiAbstract }}</span>
|
||||
<p v-show="status == 2" style="color: rgba(207, 207, 207, 1);text-align: center;margin-top: 48%;">AI 摘要重新生成中,过程可能耗时较长,<br>无需在此等待哦~</p>
|
||||
<img v-show="status == 3" src="@/assets/images/course/generationFailed.png" alt="" width="150" height="159" style="display: flex;margin: 35% auto 0 auto;">
|
||||
</div>
|
||||
<el-input v-show="status == 4"
|
||||
type="textarea"
|
||||
placeholder="请输入内容"
|
||||
autosize
|
||||
v-model="aiAbstract">
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import videoPlayer from "@/components/VideoPlayer/index.vue";
|
||||
export default {
|
||||
name: 'aiAbstract',
|
||||
// ai播放器相关
|
||||
components: {
|
||||
videoPlayer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
courseName: '企业经营法则--课程单元',
|
||||
videoList: [
|
||||
{
|
||||
id: 1,
|
||||
name: '1. 开源节流1',
|
||||
aiAbstract: '人工智能(AI)是让计算机模拟人类智能的技术,核心包括机器学习、深度学习等。主要分为弱 AI(专注特定任务)和强 AI(通用智能)两类。应用涵盖医疗诊断、自动驾驶、语音助手等多个领域。它通过数据学习模式,实现预测和适应能力,正在改变生活方式和工作方式。未来发展需平衡创新与伦理考量,确保对人类社会有益。'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '2. 企业经营法则总述',
|
||||
aiAbstract: '本课程将介绍企业经营法则的总述,包括企业经营的基本原理、经营策略、经营模式等。'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '3. 企业经营法则总述',
|
||||
aiAbstract: '本课程将介绍企业经营法则的总述,包括企业经营的基本原理、经营策略、经营模式等。'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '4. 企业经营法则总述',
|
||||
aiAbstract: '本课程将介绍企业经营法则的总述,包括企业经营的基本原理、经营策略、经营模式等。'
|
||||
},
|
||||
],
|
||||
testUrl: 'https://vjs.zencdn.net/v/oceans.mp4',
|
||||
currentVideo: {},
|
||||
aiAbstract: '人工智能(AI)是让计算机模拟人类智能的技术,核心包括机器学习、深度学习等。主要分为弱 AI(专注特定任务)和强 AI(通用智能)两类。应用涵盖医疗诊断、自动驾驶、语音助手等多个领域。它通过数据学习模式,实现预测和适应能力,正在改变生活方式和工作方式。未来发展需平衡创新与伦理考量,确保对人类社会有益。',
|
||||
status: '1', // 1: 正常 2: 生成中 3: 错误 4: 编辑中
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.currentVideo = this.videoList[0];
|
||||
},
|
||||
methods: {}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.aiAbstract{
|
||||
height: 100%;
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
justify-content: space-between;
|
||||
gap: 15px;
|
||||
background: #f4f7fa;
|
||||
.ai-left{
|
||||
padding: 9px 10px;
|
||||
width: 24%;
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
.left-title{
|
||||
background: rgba(239, 244, 252, 1);
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
color: rgba(75, 92, 118, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 23px;
|
||||
letter-spacing: 0px;
|
||||
}
|
||||
.ai-list{
|
||||
margin: 0;
|
||||
.ai-item{
|
||||
cursor: pointer;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
color: rgba(75, 92, 118, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 23px;
|
||||
letter-spacing: 0px;
|
||||
padding: 17px 31px;
|
||||
border-bottom: 1px solid rgba(240, 240, 240, 1);
|
||||
text-align: left;
|
||||
&:hover{
|
||||
background: rgba(240, 240, 240, 1);
|
||||
}
|
||||
&.active{
|
||||
color: rgba(64, 158, 255, 1);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ai-right{
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
.right-title{
|
||||
display: flex;
|
||||
padding: 0 28px;
|
||||
height: 76px;
|
||||
border-bottom: 1px solid rgba(229, 231, 235, 1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
h3{
|
||||
color: rgba(17, 24, 39, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 26px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.ai-content{
|
||||
flex: 1;
|
||||
padding: 24px 30px;
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
|
||||
.videoBox{
|
||||
width: 55%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
.video-content{
|
||||
flex: 1;
|
||||
h4{
|
||||
margin: 0 0 10px 0;
|
||||
color: rgba(17, 24, 39, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 23px;
|
||||
}
|
||||
p{
|
||||
color: rgba(17, 24, 39, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.videoOperation{
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
.opera-title{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
h4{
|
||||
margin: 0;
|
||||
color: rgba(17, 24, 39, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 23px;
|
||||
}
|
||||
.opera-btn{
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
.opera-content{
|
||||
flex: 1;
|
||||
border-radius: 7px;
|
||||
background: rgba(249, 250, 251, 1);
|
||||
padding: 15px;
|
||||
color: rgba(17, 24, 39, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
::v-deep .el-textarea{
|
||||
flex: 1;
|
||||
}
|
||||
::v-deep .el-textarea__inner{
|
||||
height: 100%!important;
|
||||
}
|
||||
</style>
|
||||
388
src/views/course/aiSet/aiTranslate.vue
Normal file
@@ -0,0 +1,388 @@
|
||||
<template>
|
||||
<div class="aiTranslate">
|
||||
<div class="ai-left">
|
||||
<div class="left-title">{{courseName}}</div>
|
||||
<ul class="ai-list">
|
||||
<li class="ai-item" v-for="(item, index) in videoList" @click="currentVideo = item" :class="{'active': currentVideo.id === item.id}" :key="index">
|
||||
<div class="ai-item-title">{{item.name}}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ai-right">
|
||||
<div class="right-title">
|
||||
<h3>{{currentVideo.name}}</h3>
|
||||
<div>
|
||||
<el-button type="primary" @click="status = 1">下架本课程AI翻译</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-content">
|
||||
<div class="videoBox">
|
||||
<videoPlayer :src="testUrl" style="height: auto;"> </videoPlayer>
|
||||
<div class="video-content">
|
||||
<div class="select-lang">
|
||||
<img src="@/assets/images/course/languageIcon.png" alt="" width="10" height="10">
|
||||
<span>本课程支持语种:</span>
|
||||
<el-button type="primary" @click="setLanguage()" icon="el-icon-connection">设置语种</el-button>
|
||||
</div>
|
||||
<div class="lang-content">
|
||||
<div class="lang-item" v-for="(item, index) in selectedLang" :key="index">
|
||||
<span>{{ item.label }}</span>
|
||||
<span :class="item.aiTranslate == 1 ? 'custom-putaway' : 'custom-takeout'">{{ item.aiTranslate == 1 ? '已上架' : '已下架' }}</span>
|
||||
<el-button type="text" @click="item.aiTranslate = item.aiTranslate == 1 ? 0 : 1">{{ item.aiTranslate == 1 ? '下架' : '上架' }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="videoOperation">
|
||||
<div class="opera-title">
|
||||
<span>目标语种</span>
|
||||
<el-select v-model="value" placeholder="请选择目标语种" style="flex: 1;">
|
||||
<el-option
|
||||
v-for="item in selectAllLang"
|
||||
:key="item.srclang"
|
||||
:label="item.label"
|
||||
:value="item.srclang"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" @click="status = 2">加载字幕</el-button>
|
||||
</div>
|
||||
<div class="opera-content">
|
||||
<div class="bg-gray" v-show="status != 4">
|
||||
<p v-show="status == 1" v-html="aiTranslate"></p>
|
||||
<p v-show="status == 2" style="color: rgba(207, 207, 207, 1);text-align: center;margin-top: 48%;">AI 翻译重新生成中,过程可能耗时较长,<br>无需在此等待哦~</p>
|
||||
<img v-show="status == 3" src="@/assets/images/course/generationFailed.png" alt="" width="150" height="159" style="display: flex;margin: 35% auto 0 auto;">
|
||||
<img v-show="status == 5" src="@/assets/images/course/selectLanguage.png" alt="" width="112" height="130" style="display: flex;margin: 35% auto 0 auto;">
|
||||
</div>
|
||||
<el-input v-show="status == 4"
|
||||
type="textarea"
|
||||
placeholder="请输入内容"
|
||||
autosize
|
||||
v-model="aiTranslate">
|
||||
</el-input>
|
||||
<div class="opera-btn">
|
||||
<el-button v-show="status == 1" type="primary" plain round size="mini" @click="updateDialogVisible = true">重新生成</el-button>
|
||||
<el-button v-show="status == 1" type="primary" plain round size="mini" @click="status = 4">编辑</el-button>
|
||||
<el-button v-show="status == 4" plain round size="mini" @click="status = 1">取消</el-button>
|
||||
<el-button v-show="status == 4" type="primary" plain round size="mini" @click="status = 1">确认</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
title="确认同步更新吗"
|
||||
:visible.sync="updateDialogVisible"
|
||||
width="420px"
|
||||
style="border-radius: 10px;"
|
||||
center>
|
||||
<p style="text-align: center;">系统将根据当前最新中文内容重新生成其他语种的翻译,您此前对翻译的修改将会丢失!</p>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="updateDialogVisible = false">取 消</el-button>
|
||||
<el-button style="margin-left: 60px;" type="primary" @click="updateDialogVisible = false">确认同步更新</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
title="AIf翻译"
|
||||
:visible.sync="selectDialogVisible"
|
||||
width="500px"
|
||||
class="select-dialog">
|
||||
<div class="select-dialog-content">
|
||||
<p>请选择该课程所支持语种</p>
|
||||
<el-select v-model="selectLang" placeholder="请选择目标语种" style="width: 100%;" multiple>
|
||||
<el-option
|
||||
v-for="item in selectAllLang"
|
||||
:key="item.srclang"
|
||||
:label="item.label"
|
||||
:value="item.srclang"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="selectDialogVisible = false">取 消</el-button>
|
||||
<el-button style="" type="primary" @click="selectDialogVisible = false">确 认</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import videoPlayer from "@/components/VideoPlayer/index.vue";
|
||||
import { mapGetters } from 'vuex';
|
||||
export default {
|
||||
name: 'aiTranslate',
|
||||
// ai播放器相关
|
||||
components: {
|
||||
videoPlayer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
courseName: '企业经营法则--课程单元',
|
||||
videoList: [
|
||||
{
|
||||
id: 1,
|
||||
name: '1. 开源节流1',
|
||||
aiTranslate: '人工智能(AI)是让计算机模拟人类智能的技术,核心包括机器学习、深度学习等。主要分为弱 AI(专注特定任务)和强 AI(通用智能)两类。应用涵盖医疗诊断、自动驾驶、语音助手等多个领域。它通过数据学习模式,实现预测和适应能力,正在改变生活方式和工作方式。未来发展需平衡创新与伦理考量,确保对人类社会有益。'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '2. 企业经营法则总述',
|
||||
aiTranslate: '本课程将介绍企业经营法则的总述,包括企业经营的基本原理、经营策略、经营模式等。'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '3. 企业经营法则总述',
|
||||
aiTranslate: '本课程将介绍企业经营法则的总述,包括企业经营的基本原理、经营策略、经营模式等。'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '4. 企业经营法则总述',
|
||||
aiTranslate: '本课程将介绍企业经营法则的总述,包括企业经营的基本原理、经营策略、经营模式等。'
|
||||
},
|
||||
],
|
||||
testUrl: 'https://vjs.zencdn.net/v/oceans.mp4',
|
||||
currentVideo: {},
|
||||
aiTranslate: `
|
||||
00:00:01/00:00:03
|
||||
Hello everyone in the audience
|
||||
|
||||
00:00:03/00:00:05
|
||||
today I want to share with you the topic of
|
||||
|
||||
00:00:05/00:00:09
|
||||
"The Development History and Future Prospects of Computer Technology -
|
||||
`,
|
||||
status: '1', // 1: 正常 2: 生成中 3: 错误 4: 编辑中 5: 未选择语种
|
||||
selectedLang: [
|
||||
{
|
||||
label: '英文',
|
||||
srclang: 'en',
|
||||
aiTranslate: 1,
|
||||
},
|
||||
{
|
||||
label: '中文',
|
||||
srclang: 'zh',
|
||||
aiTranslate: 1,
|
||||
},
|
||||
{
|
||||
label: '日文',
|
||||
srclang: 'ja',
|
||||
aiTranslate: 1,
|
||||
},
|
||||
{
|
||||
label: '韩文',
|
||||
srclang: 'ko',
|
||||
aiTranslate: 1,
|
||||
},
|
||||
{
|
||||
label: '法文',
|
||||
srclang: 'fr',
|
||||
aiTranslate: 0,
|
||||
},
|
||||
],
|
||||
updateDialogVisible: false,
|
||||
selectDialogVisible: false,
|
||||
selectLang: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['selectAllLang']),
|
||||
},
|
||||
mounted() {
|
||||
this.currentVideo = this.videoList[0];
|
||||
},
|
||||
methods: {
|
||||
setLanguage(){
|
||||
this.selectDialogVisible = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.aiTranslate{
|
||||
height: 100%;
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
justify-content: space-between;
|
||||
gap: 15px;
|
||||
background: #f4f7fa;
|
||||
.ai-left{
|
||||
padding: 9px 10px;
|
||||
width: 24%;
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
.left-title{
|
||||
background: rgba(239, 244, 252, 1);
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
color: rgba(75, 92, 118, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 23px;
|
||||
letter-spacing: 0px;
|
||||
}
|
||||
.ai-list{
|
||||
margin: 0;
|
||||
.ai-item{
|
||||
cursor: pointer;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
color: rgba(75, 92, 118, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 23px;
|
||||
letter-spacing: 0px;
|
||||
padding: 17px 31px;
|
||||
border-bottom: 1px solid rgba(240, 240, 240, 1);
|
||||
text-align: left;
|
||||
&:hover{
|
||||
background: rgba(240, 240, 240, 1);
|
||||
}
|
||||
&.active{
|
||||
color: rgba(64, 158, 255, 1);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ai-right{
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
.right-title{
|
||||
display: flex;
|
||||
padding: 0 28px;
|
||||
height: 76px;
|
||||
border-bottom: 1px solid rgba(229, 231, 235, 1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
h3{
|
||||
color: rgba(17, 24, 39, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 26px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.ai-content{
|
||||
flex: 1;
|
||||
padding: 24px 30px;
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
|
||||
.videoBox{
|
||||
width: 55%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
.video-content{
|
||||
flex: 1;
|
||||
.select-lang{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
color: rgba(107, 114, 128, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 17px;
|
||||
}
|
||||
.lang-content{
|
||||
display: flex;
|
||||
margin-top: 25px;
|
||||
align-items: center;
|
||||
gap: 25px 60px;
|
||||
flex-wrap: wrap;
|
||||
.lang-item{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: rgba(96, 98, 102, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.videoOperation{
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
.opera-title{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
>span{
|
||||
color: rgba(75, 92, 118, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
.opera-content{
|
||||
position: relative;
|
||||
flex: 1;
|
||||
color: rgba(17, 24, 39, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0px;
|
||||
white-space: pre-wrap;
|
||||
.bg-gray{
|
||||
border-radius: 7px;
|
||||
background: rgba(249, 250, 251, 1);
|
||||
height: 100%;
|
||||
padding: 15px;
|
||||
}
|
||||
.opera-btn{
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
::v-deep .el-textarea{
|
||||
height: 100%!important;
|
||||
}
|
||||
::v-deep .el-textarea__inner{
|
||||
height: 100%!important;
|
||||
}
|
||||
::v-deep .el-dialog {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.select-dialog{
|
||||
::v-deep .el-dialog__header{
|
||||
border-bottom: 1px solid rgba(229, 231, 235, 1);
|
||||
}
|
||||
.select-dialog-content{
|
||||
p{
|
||||
color: rgba(17, 24, 39, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
457
src/views/course/components/NameRemoteSelect.vue
Normal file
@@ -0,0 +1,457 @@
|
||||
<template>
|
||||
<div class="name-remote-select">
|
||||
<el-select
|
||||
ref="selectRef"
|
||||
v-model="innerValue"
|
||||
multiple
|
||||
filterable
|
||||
remote
|
||||
clearable
|
||||
:remote-method="remoteSearch"
|
||||
:loading="loading"
|
||||
:placeholder="placeholder"
|
||||
:multiple-limit="multipleLimit"
|
||||
:collapse-tags="innerCollapseTags"
|
||||
:max-collapse-tag="maxCollapseTags"
|
||||
:no-data-text="noDataText"
|
||||
class="no-wrap-select"
|
||||
style="width: 100%;"
|
||||
@visible-change="handleVisibleChange"
|
||||
@clear="handleClear"
|
||||
@change="emitChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="formatLabel(item)"
|
||||
:value="item.value"
|
||||
>
|
||||
<span>{{ item.label }}</span>
|
||||
<span v-if="item.code" class="option-code">({{ item.code }})</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<div
|
||||
v-if="panelVisible && selectedPanelItems.length"
|
||||
class="selected-panel el-select-dropdown el-popper is-multiple"
|
||||
x-placement="bottom-start"
|
||||
:style="panelStyle"
|
||||
ref="selectedPanel"
|
||||
>
|
||||
<div class="el-scrollbar">
|
||||
<div class="el-select-dropdown__wrap el-scrollbar__wrap">
|
||||
<ul class="el-scrollbar__view el-select-dropdown__list">
|
||||
<li
|
||||
v-for="item in selectedPanelItems"
|
||||
:key="item.value"
|
||||
:class="['el-select-dropdown__item', { selected: isSelected(item.value) }]"
|
||||
@click.stop="togglePanelItem(item)"
|
||||
>
|
||||
<span>{{ item.label }}</span>
|
||||
<span v-if="item.code" class="option-code">({{ item.code }})</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="popper__arrow"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'NameRemoteSelect',
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
// 远程拉取方法,返回数组;可返回 Promise
|
||||
fetcher: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
// 将 fetcher 返回的原始项映射为 { value, label, code }
|
||||
optionFormatter: {
|
||||
type: Function,
|
||||
default: item => item,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
multipleLimit: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
minQueryLen: {
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
collapseTags: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 打开下拉时是否自动展开全部已选标签(取消+N收起)
|
||||
expandOnVisible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
maxCollapseTags: {
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
maxInputLen: {
|
||||
type: Number,
|
||||
default: 50,
|
||||
},
|
||||
// 是否在下拉时额外展示“已选列表”面板
|
||||
showSelectedPanel: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 额外增加的下拉最小宽度(在选择框宽度基础上追加,单位 px)
|
||||
panelWidthExtra: {
|
||||
type: Number,
|
||||
default: 60,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
innerValue: [],
|
||||
options: [],
|
||||
loading: false,
|
||||
keyword: '',
|
||||
// 传递给 el-select 的 collapse 状态(limitedMode 下需关闭折叠以便手动控制可见标签)
|
||||
innerCollapseTags: true,
|
||||
optionCache: {},
|
||||
panelVisible: false,
|
||||
countEl: null,
|
||||
tagsContainerEl: null,
|
||||
panelMinWidth: '',
|
||||
outsideHandler: null,
|
||||
selectedPanelItems: [],
|
||||
// 标记:由点击 +N 主动关闭 el-select 下拉引起的 visible-change(false),需要忽略一次
|
||||
ignoreNextVisibleChange: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.innerValue = Array.isArray(val) ? val : [];
|
||||
this.bindCountClick();
|
||||
},
|
||||
},
|
||||
collapseTags: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.innerCollapseTags = val;
|
||||
},
|
||||
},
|
||||
innerValue() {
|
||||
this.bindCountClick();
|
||||
},
|
||||
panelVisible(visible) {
|
||||
if (visible) {
|
||||
this.bindOutsideClick();
|
||||
} else {
|
||||
this.unbindOutsideClick();
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.bindCountClick();
|
||||
this.updatePanelWidth();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.unbindCountClick();
|
||||
this.unbindOutsideClick();
|
||||
},
|
||||
methods: {
|
||||
formatLabel(item) {
|
||||
if (!item || !item.label) return '';
|
||||
return item.code ? `${item.label}` : item.label;
|
||||
},
|
||||
syncInputQuery(limited) {
|
||||
const select = this.$refs.selectRef;
|
||||
if (select && select.query !== limited) {
|
||||
select.query = limited;
|
||||
this.$nextTick(() => {
|
||||
if (select.$refs && select.$refs.input) {
|
||||
select.$refs.input.value = limited;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
async remoteSearch(keyword) {
|
||||
const limited = (keyword || '').slice(0, this.maxInputLen);
|
||||
this.syncInputQuery(limited);
|
||||
this.keyword = limited;
|
||||
const query = limited.trim();
|
||||
if (!query || query.length < this.minQueryLen) {
|
||||
this.options = [];
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await this.fetcher(query);
|
||||
const list = Array.isArray(res) ? res : [];
|
||||
this.options = list
|
||||
.map(item => this.optionFormatter(item) || {})
|
||||
.filter(item => item.value);
|
||||
// 缓存本次查询的选项,便于展示已选列表
|
||||
this.options.forEach(item => {
|
||||
this.optionCache[item.value] = item;
|
||||
});
|
||||
} catch (error) {
|
||||
this.options = [];
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
emitChange() {
|
||||
const selectedOptions = this.selectedDisplay;
|
||||
this.$emit('input', this.innerValue);
|
||||
this.$emit('change', { value: this.innerValue, options: selectedOptions });
|
||||
},
|
||||
handleClear() {
|
||||
this.innerValue = [];
|
||||
this.options = [];
|
||||
this.panelVisible = false;
|
||||
this.selectedPanelItems = [];
|
||||
this.emitChange();
|
||||
},
|
||||
handleVisibleChange(visible) {
|
||||
// 如果是由点击 +N 主动触发的关闭,下拉的 visible-change(false) 需要被忽略一次,避免把自定义面板也关掉
|
||||
if (!visible && this.ignoreNextVisibleChange) {
|
||||
this.ignoreNextVisibleChange = false;
|
||||
return;
|
||||
}
|
||||
// 聚焦/下拉打开时不自动展示面板
|
||||
if (visible) {
|
||||
// 打开时展开标签,避免点击 +N 看不到具体选项
|
||||
if (this.expandOnVisible) {
|
||||
this.innerCollapseTags = false;
|
||||
}
|
||||
// 无论关键字是否存在,都清空现有搜索结果,下次输入再重新远程拉取
|
||||
this.options = [];
|
||||
this.updatePanelWidth();
|
||||
this.panelVisible = false;
|
||||
} else {
|
||||
// 关闭时恢复原始折叠配置
|
||||
this.innerCollapseTags = this.collapseTags;
|
||||
this.panelVisible = false;
|
||||
this.selectedPanelItems = [];
|
||||
}
|
||||
},
|
||||
// 用缓存兜底,保证已选项能在面板中展示
|
||||
getOptionByValue(val) {
|
||||
return this.options.find(item => item.value === val) || this.optionCache[val] || { value: val, label: val };
|
||||
},
|
||||
handleCountClick(event) {
|
||||
// 点击 +N 展开/收起已选列表
|
||||
if (event && event.stopPropagation) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
if (!this.showSelectedPanel) return;
|
||||
const hasSelected = this.selectedDisplay.length > 0;
|
||||
if (!hasSelected) {
|
||||
this.panelVisible = false;
|
||||
return;
|
||||
}
|
||||
const nextVisible = !this.panelVisible;
|
||||
if (nextVisible) {
|
||||
// 准备已选面板,并忽略接下来由关闭 el-select 下拉触发的一次 visible-change(false)
|
||||
this.ignoreNextVisibleChange = true;
|
||||
this.prepareSelectedPanel();
|
||||
this.closeSelectDropdown();
|
||||
} else {
|
||||
this.selectedPanelItems = [];
|
||||
}
|
||||
this.panelVisible = nextVisible;
|
||||
},
|
||||
bindCountClick() {
|
||||
this.$nextTick(() => {
|
||||
const selectEl = this.$refs.selectRef && this.$refs.selectRef.$el;
|
||||
if (!selectEl) return;
|
||||
let count = this.getCountElement(selectEl);
|
||||
if (count && count !== this.countEl) {
|
||||
this.unbindCountClick();
|
||||
this.countEl = count;
|
||||
this.countEl.style.cursor = 'pointer';
|
||||
this.countEl.addEventListener('click', this.handleCountClick);
|
||||
}
|
||||
|
||||
// 同时绑定 tags 容器做事件委托,避免找不到 +N 时无法响应
|
||||
const tagsContainer = selectEl.querySelector('.el-select__tags');
|
||||
if (tagsContainer && tagsContainer !== this.tagsContainerEl) {
|
||||
if (this.tagsContainerEl) {
|
||||
this.tagsContainerEl.removeEventListener('click', this.handleTagsContainerClick, true);
|
||||
}
|
||||
this.tagsContainerEl = tagsContainer;
|
||||
this.tagsContainerEl.addEventListener('click', this.handleTagsContainerClick, true);
|
||||
}
|
||||
});
|
||||
},
|
||||
unbindCountClick() {
|
||||
if (this.countEl) {
|
||||
this.countEl.removeEventListener('click', this.handleCountClick);
|
||||
this.countEl = null;
|
||||
}
|
||||
if (this.tagsContainerEl) {
|
||||
this.tagsContainerEl.removeEventListener('click', this.handleTagsContainerClick, true);
|
||||
this.tagsContainerEl = null;
|
||||
}
|
||||
},
|
||||
handleTagsContainerClick(event) {
|
||||
const target = event.target;
|
||||
if (!target) return;
|
||||
const isCountText = target.classList && target.classList.contains('el-select__tags-text');
|
||||
const isInfoTag = target.classList && target.classList.contains('el-tag--info');
|
||||
if (isCountText || isInfoTag) {
|
||||
this.handleCountClick(event);
|
||||
}
|
||||
},
|
||||
getCountElement(selectEl) {
|
||||
let count = selectEl.querySelector('.el-select__tags-text');
|
||||
if (!count) {
|
||||
const tags = selectEl.querySelectorAll('.el-select__tags .el-tag--info');
|
||||
count = tags && tags.length ? tags[tags.length - 1] : null;
|
||||
}
|
||||
if (!count) {
|
||||
count = selectEl.querySelector('.el-select__tags');
|
||||
}
|
||||
return count;
|
||||
},
|
||||
bindOutsideClick() {
|
||||
if (this.outsideHandler) return;
|
||||
this.outsideHandler = (e) => {
|
||||
const panel = this.$refs.selectedPanel;
|
||||
const selectEl = this.$refs.selectRef && this.$refs.selectRef.$el;
|
||||
const target = e.target;
|
||||
if (!target) return;
|
||||
const insidePanel = panel && panel.contains(target);
|
||||
const insideSelect = selectEl && selectEl.contains(target);
|
||||
if (!insidePanel && !insideSelect) {
|
||||
this.panelVisible = false;
|
||||
this.selectedPanelItems = [];
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', this.outsideHandler, true);
|
||||
},
|
||||
unbindOutsideClick() {
|
||||
if (this.outsideHandler) {
|
||||
document.removeEventListener('mousedown', this.outsideHandler, true);
|
||||
this.outsideHandler = null;
|
||||
}
|
||||
},
|
||||
updatePanelWidth() {
|
||||
this.$nextTick(() => {
|
||||
const selectEl = this.$refs.selectRef && this.$refs.selectRef.$el;
|
||||
if (!selectEl) return;
|
||||
const rect = selectEl.getBoundingClientRect();
|
||||
const base = rect ? rect.width : 0;
|
||||
const extra = Number(this.panelWidthExtra) || 0;
|
||||
const minWidth = base + extra;
|
||||
this.panelMinWidth = `${minWidth}px`;
|
||||
});
|
||||
},
|
||||
prepareSelectedPanel() {
|
||||
// 在展开已选列表时,冻结当前已选项,支持在面板内反复勾选/取消
|
||||
this.selectedPanelItems = this.selectedDisplay.map(item => ({ ...item }));
|
||||
},
|
||||
closeSelectDropdown() {
|
||||
// 关闭原生下拉,避免与已选列表面板同时展示;保留输入内容
|
||||
const select = this.$refs.selectRef;
|
||||
if (select && select.visible) {
|
||||
if (typeof select.handleClose === 'function') {
|
||||
select.handleClose();
|
||||
} else {
|
||||
select.visible = false;
|
||||
}
|
||||
this.syncInputQuery(this.keyword || '');
|
||||
}
|
||||
},
|
||||
isSelected(val) {
|
||||
return (this.innerValue || []).includes(val);
|
||||
},
|
||||
togglePanelItem(item) {
|
||||
if (!item || !item.value) return;
|
||||
const current = this.innerValue || [];
|
||||
const exists = this.isSelected(item.value);
|
||||
this.innerValue = exists ? current.filter(v => v !== item.value) : [...current, item.value];
|
||||
this.emitChange();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
noDataText() {
|
||||
return this.keyword && this.keyword.length >= this.minQueryLen ? '无数据' : `请至少输入${this.minQueryLen}个字`;
|
||||
},
|
||||
selectedDisplay() {
|
||||
return (this.innerValue || []).map(val => this.getOptionByValue(val)).filter(item => item && item.value);
|
||||
},
|
||||
panelStyle() {
|
||||
return {
|
||||
minWidth: this.panelMinWidth || '100%',
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.name-remote-select {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.option-code {
|
||||
margin-left: 4px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.selected-panel {
|
||||
position: absolute;
|
||||
z-index: 2000;
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
/* 参照 ElementUI popper 箭头默认样式(packages/theme-chalk/src/popper.scss) */
|
||||
.selected-panel .popper__arrow,
|
||||
.selected-panel .popper__arrow::after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
.selected-panel .popper__arrow {
|
||||
border-width: 6px;
|
||||
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
|
||||
}
|
||||
.selected-panel .popper__arrow::after {
|
||||
content: " ";
|
||||
border-width: 6px;
|
||||
}
|
||||
.selected-panel[x-placement^='bottom'] {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.selected-panel[x-placement^='bottom'] .popper__arrow {
|
||||
top: -6px;
|
||||
/* ElementUI 下拉箭头靠左偏移(非居中),与官方样式对齐 */
|
||||
left: 35px;
|
||||
border-top-width: 0;
|
||||
border-bottom-color: #e4e7ed;
|
||||
}
|
||||
.selected-panel[x-placement^='bottom'] .popper__arrow::after {
|
||||
top: 1px;
|
||||
margin-left: -6px;
|
||||
border-top-width: 0;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -206,8 +206,25 @@
|
||||
<div class="couresstartTime">
|
||||
<span v-if="cinfo.type == 30 && cinfo.startTime">开课时间:{{ cinfo.startTime }}</span>
|
||||
</div>
|
||||
|
||||
<div class="course-info">
|
||||
<!-- ai播放器相关 -->
|
||||
<div class="course-info" style="align-items: center;">
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
:width="cinfo.summaryContent && cinfo.summaryContent.length > 200 ? '402' : '253'"
|
||||
trigger="hover"
|
||||
popper-class="course-popover"
|
||||
>
|
||||
<div class="course-popover-content">
|
||||
<h4>课程摘要</h4>
|
||||
<div v-if="cinfo.summaryContent" class="course-popover-content-text" >{{ cinfo.summaryContent }}</div>
|
||||
<div v-else class="course-popover-noContent" >
|
||||
<img src="../../../assets/images/course/noData.png" alt="">
|
||||
<span>暂无数据</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <img slot="reference" src="../../../assets/images/course/courseAbstract.png" alt="摘要" style="width: 94px;height: 44px;margin-left: -10px;"> -->
|
||||
<img v-show="cinfo.aiSet == 1 && cinfo.aiAbstract == 1" slot="reference" src="../../../assets/images/course/courseAbstract.png" alt="摘要" style="width: 94px;height: 44px;margin-left: -10px;">
|
||||
</el-popover>
|
||||
<div class="course-info-user" style="max-width: 100px;" v-if="cinfo.teacher">
|
||||
<el-tooltip :content="cinfo.teacher" placement="bottom" effect="light">
|
||||
<span class="course-info-author">{{ cinfo.teacher }}</span>
|
||||
@@ -217,13 +234,13 @@
|
||||
<span class="course-info-studys">{{ formatNum(cinfo.studies) }}人学习</span>
|
||||
</div>
|
||||
<div class="course-info-score">
|
||||
<div style="display: flex;">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<interactBar :type="1" nodeWidth="20px" :data="cinfo" :courseExclusive="true" :comments="false"
|
||||
:praises="false" :shares="false" :views="false"></interactBar>
|
||||
<div v-if="cinfo.score">
|
||||
<span class="course-score-value">{{ toScore(cinfo.score) }}分</span>
|
||||
<span class="course-score-value" style="white-space: nowrap;">{{ toScore(cinfo.score) }}分</span>
|
||||
</div>
|
||||
<div v-else class="course-score-no">未评分</div>
|
||||
<div v-else class="course-score-no" style="white-space: nowrap;">未评分</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2710,3 +2727,43 @@ a.custom2 {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- ai播放器相关 -->
|
||||
<style lang="scss">
|
||||
.course-popover{
|
||||
border-radius: 12px;
|
||||
box-shadow: 0px 0px 16px 0px rgba(0, 0, 0, 0.12);
|
||||
.course-popover-content{
|
||||
h4{
|
||||
margin: 0 0 7px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 17px;
|
||||
letter-spacing: 0.3px;
|
||||
color: rgba(0, 0, 0, 1);
|
||||
}
|
||||
.course-popover-content-text{
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.26px;
|
||||
color: rgba(102, 102, 102, 1);
|
||||
}
|
||||
.course-popover-noContent{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
color: rgba(0, 0, 0, 0.34);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 14px;
|
||||
letter-spacing: 0.26px;
|
||||
padding-bottom: 20px;
|
||||
img{
|
||||
width: 96px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||