mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/learning-system-portal.git
synced 2025-12-17 14:56:44 +08:00
2529 lines
78 KiB
Vue
2529 lines
78 KiB
Vue
<template>
|
||
<div class="manage-list-remote">
|
||
<div class="filter-wrapper">
|
||
<div class="filter-row filter-row--primary">
|
||
<div class="filter-fields">
|
||
<div class="filter-field filter-field--name">
|
||
<el-input type="text" placeholder="课程名称" v-model="params.name" clearable maxlength="50"></el-input>
|
||
</div>
|
||
<div class="filter-field filter-field--category">
|
||
<el-cascader placeholder="课程分类" clearable v-model="sysTypeList" :props="defaultTypeProps"
|
||
:options="sysTypeListMap"></el-cascader>
|
||
</div>
|
||
<div class="filter-field filter-field--teacher teacher-filter">
|
||
<div class="teacher-select-wrapper">
|
||
<el-select
|
||
class="creator-select"
|
||
ref="teacherSelect"
|
||
v-model="teacherSelected"
|
||
multiple
|
||
filterable
|
||
remote
|
||
clearable
|
||
reserve-keyword
|
||
placeholder="授课教师"
|
||
:multiple-limit="5"
|
||
:remote-method="remoteSearchTeacher"
|
||
:loading="teacherLoading"
|
||
@input.native="limitTeacherInput"
|
||
@visible-change="handleTeacherVisibleChange"
|
||
@change="handleTeacherChange"
|
||
@clear="handleTeacherClear"
|
||
>
|
||
<el-option v-for="item in teacherOptions" :key="item.teacherId" :label="item.teacherName"
|
||
:value="item.teacherId">
|
||
<span>{{ item.teacherName }}</span>
|
||
<span v-if="item.teacherCode" class="option-code">({{ item.teacherCode }})</span>
|
||
</el-option>
|
||
</el-select>
|
||
</div>
|
||
</div>
|
||
<div class="filter-field filter-field--time learning-time-range">
|
||
<div :class="[
|
||
'grid-content',
|
||
'bg-purple',
|
||
'resetDatePicker',
|
||
!learningTimeRange || learningTimeRange.length === 0 ? 'noSplitDatePicker' : ''
|
||
]">
|
||
<el-date-picker v-model="learningTimeRange" type="daterange" align="right" unlink-panels clearable
|
||
value-format="yyyy-MM-dd 00:00:00" range-separator="至" start-placeholder="培训时间"
|
||
:picker-options="pickerOptions" @change="handleLearningTimeRangeChange"></el-date-picker>
|
||
</div>
|
||
</div>
|
||
<div class="filter-field filter-field--status">
|
||
<el-select v-model="params.status" placeholder="审核状态" clearable>
|
||
<el-option label="-" value="1"></el-option>
|
||
<el-option label="审核中" value="2"></el-option>
|
||
<el-option label="审核通过" value="5"></el-option>
|
||
<el-option label="审核驳回" value="3"></el-option>
|
||
</el-select>
|
||
</div>
|
||
<div class="filter-field filter-field--publish">
|
||
<el-select v-model="params.publish" placeholder="发布状态" clearable>
|
||
<el-option label="已发布" :value="true"></el-option>
|
||
<el-option label="未发布" :value="false"></el-option>
|
||
</el-select>
|
||
</div>
|
||
|
||
</div>
|
||
<div class="filter-actions" v-show="!showAdvancedFilter">
|
||
<el-button type="text" class="toggle-link" @click="toggleAdvancedFilter">
|
||
{{ showAdvancedFilter ? '收起' : '展开' }}
|
||
<i :class="showAdvancedFilter ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
|
||
</el-button>
|
||
<el-tooltip content="查询" placement="top" effect="dark" popper-class="icon-btn-tooltip">
|
||
<div class="icon-btn icon-btn--search" @click="searchData(true)" aria-label="查询"></div>
|
||
</el-tooltip>
|
||
<el-tooltip content="重置" placement="top" effect="dark" popper-class="icon-btn-tooltip">
|
||
<div class="icon-btn icon-btn--reset" @click="reset" aria-label="重置"></div>
|
||
</el-tooltip>
|
||
</div>
|
||
</div>
|
||
<div v-if="showAdvancedFilter" class="filter-row filter-row--advanced advanced-filter">
|
||
<div class="filter-fields">
|
||
<div class="filter-field filter-field--enabled">
|
||
<el-select v-model="params.enabled" placeholder="启停用状态" clearable>
|
||
<el-option label="启用" :value="true"></el-option>
|
||
<el-option label="停用" :value="false"></el-option>
|
||
</el-select>
|
||
</div>
|
||
<div class="filter-field filter-field--open">
|
||
<el-select v-model="params.openCourse" placeholder="是否公开课" clearable>
|
||
<el-option label="是" :value="1"></el-option>
|
||
<el-option label="否" :value="0"></el-option>
|
||
</el-select>
|
||
</div>
|
||
<div class="filter-field filter-field--resowner">
|
||
<el-cascader
|
||
ref="resOwnerCascader"
|
||
v-model="resOwnerSelected"
|
||
placeholder="资源归属"
|
||
clearable
|
||
:props="resOwnerCascaderProps"
|
||
:show-all-levels="false"
|
||
@change="handleResOwnerChange"
|
||
@input.native="limitResOwnerInput"
|
||
filterable
|
||
:filter-method="resOwnerFilterMethod"
|
||
></el-cascader>
|
||
</div>
|
||
<div class="filter-field filter-field--creator creator-filter">
|
||
<div class="teacher-select-wrapper">
|
||
<el-select
|
||
class="creator-select"
|
||
ref="creatorSelect"
|
||
v-model="creatorSelected"
|
||
multiple
|
||
filterable
|
||
remote
|
||
clearable
|
||
reserve-keyword
|
||
placeholder="创建人"
|
||
:multiple-limit="5"
|
||
:remote-method="remoteSearchCreator"
|
||
:loading="creatorLoading"
|
||
@input.native="limitCreatorInput"
|
||
@visible-change="handleCreatorVisibleChange"
|
||
@change="handleCreatorChange"
|
||
@clear="handleCreatorClear"
|
||
>
|
||
<el-option v-for="item in creatorOptions" :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>
|
||
</div>
|
||
</div>
|
||
<div class="filter-field filter-field--create-from">
|
||
<el-select v-model="params.createFrom" placeholder="创建来源" clearable>
|
||
<el-option label="教师端" value="teacher"></el-option>
|
||
<el-option label="管理端" value="admin"></el-option>
|
||
</el-select>
|
||
</div>
|
||
</div>
|
||
<div class="filter-actions filter-actions--inline">
|
||
<el-button type="text" class="toggle-link" @click="toggleAdvancedFilter">
|
||
{{ showAdvancedFilter ? '收起' : '展开' }}
|
||
<i :class="showAdvancedFilter ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
|
||
</el-button>
|
||
<el-tooltip content="查询" placement="top" effect="dark" popper-class="icon-btn-tooltip">
|
||
<div class="icon-btn icon-btn--search" @click="searchData(true)" aria-label="查询"></div>
|
||
</el-tooltip>
|
||
<el-tooltip content="重置" placement="top" effect="dark" popper-class="icon-btn-tooltip">
|
||
<div class="icon-btn icon-btn--reset" @click="reset" aria-label="重置"></div>
|
||
</el-tooltip>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
<div class="table-wrapper">
|
||
<div class="filter-extra-actions">
|
||
<div class="create-course-btn" @click="addNewCourse()" aria-label="新建课程">新建课程</div>
|
||
<el-tooltip v-if="showSetTopFeature" content="置顶排序" placement="top" effect="dark" popper-class="icon-btn-tooltip">
|
||
<div class="icon-btn icon-btn--top" @click="handleTopSort" aria-label="置顶排序"></div>
|
||
</el-tooltip>
|
||
<el-tooltip content="导出" placement="top" effect="dark" popper-class="icon-btn-tooltip">
|
||
<div class="icon-btn icon-btn--export" :class="{ 'is-loading': exportLoading, 'is-disabled': exportLoading }"
|
||
@click="!exportLoading && handleExport()" aria-label="导出"></div>
|
||
</el-tooltip>
|
||
</div>
|
||
<el-table :data="pageData" @sort-change="handleSortChange">
|
||
<el-table-column v-if="forChoose" label="选择" width="80" align="center">
|
||
<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" header-align="center" align="left" min-width="360"
|
||
show-overflow-tooltip fixed="left" sortable="custom">
|
||
<template slot-scope="scope">
|
||
<span class="course-name" @click="viewTopic(scope.row)">{{ scope.row.name }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="课程分类" prop="sysType" min-width="180" align="center" sortable="custom" show-overflow-tooltip>
|
||
<template slot-scope="scope">
|
||
<span class="common-cell common-cell-right single-line-ellipsis">{{ formatSysTypeChain(scope.row) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="授课教师" prop="teacherName" :min-width="teacherColumnWidth" align="center" >
|
||
<template slot-scope="scope">
|
||
<span class="common-cell">{{ scope.row.teacherName }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="课程时长" prop="courseDuration" min-width="110" align="center" sortable="custom">
|
||
<template slot-scope="scope">
|
||
<span class="common-cell common-cell-right">{{ formatCourseDuration(scope.row) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="学习人数" prop="studys" min-width="110" align="center" sortable="custom">
|
||
<template slot-scope="scope">
|
||
<span class="common-cell common-cell-right">{{ scope.row.studys || '-' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="课程评分" prop="score" min-width="110" align="center" sortable="custom">
|
||
<template slot-scope="scope">
|
||
<span class="common-cell common-cell-right">{{ formatScore(scope.row) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="审核状态" prop="status" min-width="110" align="center" sortable="custom">
|
||
<template slot-scope="scope">
|
||
<span class="common-cell common-cell-right" v-if="scope.row.status == 1">-</span>
|
||
<span class="common-cell common-cell-right" v-if="scope.row.status == 2">审核中</span>
|
||
<span :class="['common-cell common-cell-right', 'status--pass']" v-if="scope.row.status == 5">审核通过</span>
|
||
<span :class="['common-cell common-cell-right', 'status--reject']" v-if="scope.row.status == 3">审核驳回</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="发布状态" prop="published" min-width="110" align="center" sortable="custom">
|
||
<template slot-scope="scope">
|
||
<span class="common-cell common-cell-right">{{ scope.row.published == true ? '已发布' : '未发布' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="启停用状态" prop="enabled" min-width="130" align="center" sortable="custom">
|
||
<template slot-scope="scope">
|
||
<span class="common-cell common-cell-right">{{ scope.row.enabled == true ? '启用' : '停用' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="公开课" prop="openCourse" min-width="110" align="center" sortable="custom">
|
||
<template slot-scope="scope">
|
||
<span class="common-cell common-cell-right">{{ scope.row.openCourse == 1 ? '是' : '否' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="资源归属" prop="orgName" min-width="220" align="center" sortable="custom">
|
||
<template slot-scope="scope">
|
||
<el-tooltip :content="scope.row.orgFullName || scope.row.orgName" placement="top" effect="dark">
|
||
<span class="common-cell common-cell-right">{{ scope.row.orgName }}</span>
|
||
</el-tooltip>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="创建人" prop="sysCreateBy" min-width="130" align="center" sortable="custom" show-overflow-tooltip>
|
||
<template slot-scope="scope">
|
||
<span class="common-cell common-cell-right">{{ scope.row.sysCreateBy }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="创建来源" min-width="140" align="center">
|
||
<template slot-scope="scope">
|
||
<span class="common-cell" v-if="scope.row.createFrom === 'teacher'">教师端</span>
|
||
<span class="common-cell" v-else-if="scope.row.createFrom === 'admin'">管理端</span>
|
||
<span class="common-cell" v-else>-</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="创建时间" prop="sysCreateTime" min-width="200" show-overflow-tooltip align="center"
|
||
sortable="custom">
|
||
<template slot-scope="scope">
|
||
<span class="common-cell">{{ scope.row.sysCreateTime }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" min-width="280px" fixed="right" header-align="center" align="left">
|
||
<template slot-scope="scope" class="btn-gl">
|
||
<template v-if="getInlineActions(scope.row).length">
|
||
<span v-for="(action, index) in getInlineActions(scope.row)" :key="action.key">
|
||
<el-button
|
||
type="text"
|
||
size="mini"
|
||
:class="action.className"
|
||
@click="handleAction(action.key, scope.row)"
|
||
>
|
||
{{ action.label }}
|
||
</el-button>
|
||
<el-divider
|
||
v-if="index < getInlineActions(scope.row).length - 1 || (index === getInlineActions(scope.row).length - 1 && getDropdownActions(scope.row).length)"
|
||
direction="vertical"
|
||
></el-divider>
|
||
</span>
|
||
</template>
|
||
<el-dropdown
|
||
v-if="getDropdownActions(scope.row).length"
|
||
type="text"
|
||
size="mini"
|
||
style="margin-left:0px"
|
||
trigger="click"
|
||
>
|
||
<el-button type="text" size="mini" class="action-link--more">
|
||
更多<i class="el-icon-arrow-down el-icon--right"></i>
|
||
</el-button>
|
||
<el-dropdown-menu slot="dropdown">
|
||
<el-dropdown-item
|
||
v-for="action in getDropdownActions(scope.row)"
|
||
:key="action.key"
|
||
:class="action.className"
|
||
@click.native="handleAction(action.key, scope.row)"
|
||
>
|
||
{{ action.label }}
|
||
</el-dropdown-item>
|
||
</el-dropdown-menu>
|
||
</el-dropdown>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<div class="pagination">
|
||
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange"
|
||
:current-page="page.pageIndex" :page-sizes="[10, 20, 30, 40]" :page-size="page.pageSize"
|
||
layout="total, sizes, prev, pager, next, jumper" :total="page.count"></el-pagination>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<!-- <div style="height: 100px;"></div> -->
|
||
<!--邀请审核-->
|
||
<el-dialog custom-class="g-dialog" title="邀请教师审核课程" :visible.sync="inviteTeacher.dlgShow">
|
||
<div style="display: flex;justify-content:flex-start;padding-bottom: 10px;">
|
||
<div style="padding: 0px 5px;"><el-input placeholder="姓名" v-model="inviteTeacher.params.name"></el-input></div>
|
||
<div style="padding: 0px 5px;"><el-button @click="findTeachers()" icon="el-icon-search"
|
||
type="primary">搜索</el-button></div>
|
||
</div>
|
||
<div>
|
||
<el-table max-height="500" border :data="inviteTeacher.list" style="width: 100%">
|
||
<el-table-column prop="name" label="姓名" width="180"></el-table-column>
|
||
<el-table-column prop="sex" label="性别"></el-table-column>
|
||
<el-table-column prop="code" label="工号"></el-table-column>
|
||
<el-table-column prop="orgInfo" label="组织"></el-table-column>
|
||
<el-table-column prop="orgInfo" label="选择">
|
||
<template slot-scope="scope">
|
||
<el-radio v-model="scope.row.checked">选择</el-radio>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
<template #footer>
|
||
<el-button @click="inviteTeacher.dlgShow = false">取 消</el-button>
|
||
<el-button type="primary" @click="enSure">确认</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
<!--课程管理-->
|
||
<el-dialog custom-class="g-dialog" title="课程学习管理" width="900px" height="900px" :visible.sync="manageStudy.dlgShow"
|
||
:close-on-click-modal="false">
|
||
<manager :manageStudyData="manageStudyData" :isShowDialog="manageStudy.dlgShow"></manager>
|
||
<template #footer>
|
||
<el-button @click="manageStudy.dlgShow = false">关闭</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
<el-dialog title="二维码" :visible.sync="qrCodedialogVisible" width="900px" @close="closeCode" custom-class="g-dialog">
|
||
<div>
|
||
<el-form size="medium" label-width="100px">
|
||
<el-form-item label="二维码">
|
||
<div id="qrcode" ref="qrcode" class="qrcode-img" @mouseenter="showDownloadButton = true"
|
||
@mouseleave="showDownloadButton = false">
|
||
<div v-show="showDownloadButton" @click="downloadQrcode" class="downloadn-container">
|
||
<i class="el-icon-download" style="color: #409EFF;font-size:18px;margin-bottom:5px"></i>
|
||
<span>下载</span>
|
||
</div>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="链接">
|
||
<el-input v-model="copyUrl" readonly class="input-with-select" id="text">
|
||
<el-button slot="append" @click="handleCopyUrl">复制</el-button>
|
||
</el-input>
|
||
</el-form-item>
|
||
<el-form-item label="">上述内容兼容PC端与移动端,您可按需分享。</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
<span slot="footer" class="dialog-footer"><el-button @click="closeCode">关 闭</el-button></span>
|
||
</el-dialog>
|
||
<!-- 审核 -->
|
||
<el-dialog title="审核" :visible.sync="dialogVisible" width="900px" custom-class="g-dialog">
|
||
<div v-show="expandDetails">
|
||
<div v-if="examin.detailType == 10">
|
||
<auditCourse1 :id="examin.examineId"></auditCourse1>
|
||
</div>
|
||
<div v-if="examin.detailType == 20">
|
||
<auditCourse2 :id="examin.examineId"></auditCourse2>
|
||
</div>
|
||
</div>
|
||
<div style="border-top: 1px solid #eee; background-color: #eee; padding: 5px;">
|
||
<div style=" text-align: center;margin-bottom: 10px;">
|
||
<el-button @click="expandDetails = !expandDetails">{{ expandDetails ? '详情折叠' : '详情展开' }}</el-button>
|
||
<el-button @click="isExamine = 1">直接审核</el-button>
|
||
<el-button @click="isExamine = 2">邀请审核</el-button>
|
||
</div>
|
||
|
||
<el-form label-width="80px" v-if="isExamine === 1">
|
||
<el-form-item label="审核">
|
||
<el-radio-group v-model="auditInfo.pass">
|
||
<el-radio :label="true">通过</el-radio>
|
||
<el-radio :label="false">不通过</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="审核意见"><el-input v-model="auditInfo.remark" type="textarea"
|
||
rows="3"></el-input></el-form-item>
|
||
</el-form>
|
||
<div v-if="isExamine === 2">
|
||
<div style="display: flex;justify-content:flex-start;padding-bottom: 10px;">
|
||
<div style="padding: 0px 5px;"><el-input placeholder="姓名" v-model="inviteTeacher.params.name"></el-input>
|
||
</div>
|
||
<div style="padding: 0px 5px;"><el-button @click="findTeachers()" icon="el-icon-search"
|
||
type="primary">搜索</el-button></div>
|
||
</div>
|
||
<div>
|
||
<el-table v-if="inviteTeacher.list.length !== 0" max-height="500" border :data="inviteTeacher.list"
|
||
style="width: 100%;margin-bottom: 10px;">
|
||
<el-table-column prop="name" label="姓名" width="180"></el-table-column>
|
||
<el-table-column prop="code" label="工号"></el-table-column>
|
||
<el-table-column prop="orgInfo" label="组织"></el-table-column>
|
||
<el-table-column prop="orgInfo" label="选择">
|
||
<template slot-scope="scope">
|
||
<el-radio v-model="scope.row.checked">选择</el-radio>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<div>审核记录:</div>
|
||
<el-table max-height="500" border :data="inviteTeacher.list" style="width: 100%;">
|
||
<el-table-column prop="name" label="姓名" width="180"></el-table-column>
|
||
<el-table-column prop="code" label="工号"></el-table-column>
|
||
<el-table-column prop="orgInfo" label="组织"></el-table-column>
|
||
<el-table-column prop="type" label="审核状态"></el-table-column>
|
||
<el-table-column prop="text" label="备注"></el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<span slot="footer" class="dialog-footer">
|
||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||
<el-button type="primary" @click="examineData()">提交</el-button>
|
||
</span>
|
||
</el-dialog>
|
||
<el-dialog v-if="showDetails" title="课程详情" :visible.sync="showDetails" @close="examin = {};" width="900px"
|
||
custom-class="g-dialog">
|
||
<div v-show="expandDetails">
|
||
<div v-if="examin.detailType == 10">
|
||
<auditCourse1 :showTest="true" :isDetails="false" :isShow="false" :id="examin.examineId"></auditCourse1>
|
||
</div>
|
||
<div v-if="examin.detailType == 20">
|
||
<auditCourse2 :showTest="true" :isDetails="false" :isShow="false" :id="examin.examineId"></auditCourse2>
|
||
</div>
|
||
</div>
|
||
<span slot="footer" class="dialog-footer">
|
||
<el-button @click="showDetails = false; examin = {};">取 消</el-button>
|
||
</span>
|
||
</el-dialog>
|
||
<div>
|
||
<course-form ref="courseForm" @submitSuccess="searchData" @close="searchData"></course-form>
|
||
</div>
|
||
<top-course-sorter v-if="showSetTopFeature" ref="topSorter" @sorted="onTopSorted"></top-course-sorter>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import courseForm from '@/components/Course/courseForm.vue';
|
||
import manager from '@/components/Study/manager.vue';
|
||
import QRCode from 'qrcodejs2';
|
||
import auditCourse1 from '@/components/Course/auditCourse1.vue';
|
||
import auditCourse2 from '@/components/Course/auditCourse2.vue';
|
||
import adminPage from '@/components/Administration/adminPage.vue';
|
||
import TopCourseSorter from '@/components/Course/TopCourseSorter.vue';
|
||
import apiResowner from '../../api/modules/resowner.js';
|
||
import apiType from '../../api/modules/type.js'
|
||
import { courseType } from '../../utils/tools.js';
|
||
import apiCourse from '@/api/modules/course.js';
|
||
// import {resOwnerIndexName,sysTypeIndexName} from '@/utils/type.js';
|
||
import { mapGetters, mapActions } from 'vuex';
|
||
import apiUserbasic from "@/api/boe/userbasic.js"
|
||
import apiTeacher from '../../api/modules/teacher.js';
|
||
export default {
|
||
name: 'manageCourse',
|
||
components: { courseForm, manager, auditCourse1, auditCourse2, adminPage, TopCourseSorter },
|
||
computed: {
|
||
...mapGetters(['resOwnerMap', 'sysTypeMap', 'userInfo', 'identity']),
|
||
resOwnerCascaderProps() {
|
||
return {
|
||
value: 'id',
|
||
label: 'name',
|
||
children: 'children',
|
||
lazy: true,
|
||
lazyLoad: this.loadResOwnerNode,
|
||
leaf: 'leaf',
|
||
checkStrictly: true // 允许选择任意一级选项
|
||
};
|
||
},
|
||
// 动态计算“授课教师”列的最小宽度,避免固定 260px
|
||
teacherColumnWidth() {
|
||
return this.calcColumnWidth('授课教师', 'teacherName');
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
audiences: [],
|
||
forChoose: false,
|
||
pageManage: false,
|
||
showDetails: false,
|
||
examin: {
|
||
detailType: '',
|
||
examineId: '',
|
||
examineName: '',
|
||
},
|
||
paperJson: { items: [] },
|
||
courseType: courseType,
|
||
sysTypeListMap: [],
|
||
sysTypeList: [],
|
||
resOwnerListMap: [],
|
||
resOwnerSelected: [],
|
||
resOwnerCascaderOptions: [],
|
||
showSetTopFeature: false,
|
||
page: {
|
||
pageIndex: 1,//第几页
|
||
pageSize: 10, // 每页多少条
|
||
count: 0
|
||
},
|
||
exportLoading: false,
|
||
resOwner: [],
|
||
resOwnerFilterWarned: false,
|
||
orgId: '',
|
||
orgName: '',
|
||
defaultProps: {
|
||
value: 'code',
|
||
label: 'name',
|
||
},
|
||
defaultTypeProps: {
|
||
value: 'id',
|
||
label: 'name',
|
||
},
|
||
showAdvancedFilter: false,
|
||
learningTimeRange: [],
|
||
pickerOptions: { shortcuts: [] },
|
||
manageStudyData: {},
|
||
expandDetails: true,
|
||
qrCodedialogVisible: false,
|
||
copyUrl: '',
|
||
qrcodeImgUrl: '',
|
||
showDownloadButton: false,
|
||
isExamine: 1,
|
||
auditInfo: {
|
||
pass: true,
|
||
remark: ''
|
||
},
|
||
dialogVisible: false,
|
||
currentPage4: 4,
|
||
inviteTeacher: {
|
||
//邀请
|
||
dlgShow: false,
|
||
params: { name: '' },
|
||
list: []
|
||
},
|
||
manageStudy: {
|
||
dlgShow: false
|
||
},
|
||
params: {
|
||
name: '',
|
||
teacherId: '',
|
||
learningTimeStart: '',
|
||
learningTimeEnd: '',
|
||
status: '',
|
||
publish: '',
|
||
enabled: '',
|
||
openCourse: '',
|
||
createUserId: '',
|
||
createFrom: '',
|
||
},
|
||
orderField: '', // 排序字段
|
||
orderAsc: true, // 排序顺序:true-升序,false-降序
|
||
pageData: [],
|
||
courseChooseShow: false,
|
||
courseChooseId: '',
|
||
teacherFilterList: [],
|
||
teacherSelected: [],
|
||
teacherOptions: [],
|
||
teacherLoading: false,
|
||
creatorSelected: [],
|
||
creatorOptions: [],
|
||
creatorLoading: false,
|
||
courseTypes: [
|
||
{ id: '1', img: this.webBaseUrl + '/images/ctype1.png', name: '微课', info: '一种单一课件的课程' },
|
||
{ id: '2', img: this.webBaseUrl + '/images/ctype2.png', name: '在线课', info: '有章节多课件的课程' },
|
||
{ id: '3', img: this.webBaseUrl + '/images/ctype3.png', name: '课程包', info: '微课和在线课组成' },
|
||
{ id: '4', img: this.webBaseUrl + '/images/ctype4.png', name: '线下课', info: 'XXXX' },
|
||
{ id: '5', img: this.webBaseUrl + '/images/ctype5.png', name: '直播课', info: 'XXXX' }
|
||
],
|
||
weike: {
|
||
onlyRequired: false,
|
||
dlgShow: false,
|
||
fileType: '',
|
||
info: {
|
||
shebei: ''
|
||
}
|
||
},
|
||
biaoke: {
|
||
dlgShow: false
|
||
},
|
||
recommend: {
|
||
dlgShow: false,
|
||
},
|
||
catalogs: {
|
||
addNewZhang: false,
|
||
addNewCell: false,
|
||
},
|
||
extendRefId: '',
|
||
extendRefType: '',
|
||
scrollbarStyleApplied: false,
|
||
};
|
||
},
|
||
created() {
|
||
this.pickerOptions = this.buildPickerOptions();
|
||
},
|
||
mounted() {
|
||
this.getAudiences()
|
||
let chooseFlag = this.$route.query.f;
|
||
this.extendRefId = this.$route.query.refId;
|
||
this.extendRefType = this.$route.query.refType;
|
||
if (chooseFlag && chooseFlag == 'choose') {
|
||
this.forChoose = true;
|
||
}
|
||
|
||
if (this.$route.query && this.$route.query.page && this.$route.query.page == 'manage') {
|
||
this.pageManage = true;
|
||
}
|
||
|
||
if (this.$route.query && this.$route.query.open && this.$route.query.open == 'new') {
|
||
this.addNewCourse();
|
||
}
|
||
this.loadShowSetTopFlag();
|
||
// this.getTree();
|
||
// this.getTypeData();
|
||
// this.searchData();
|
||
this.getResOwnerTree().then(rs => {
|
||
this.resOwnerListMap = rs;
|
||
});
|
||
// 取消全局课程分类
|
||
this.getSysTypeTree().then(rs => {
|
||
this.sysTypeListMap = rs;
|
||
})
|
||
//已经加载tree的情况下,不需要再单独的加载一次
|
||
this.loadResOwners();
|
||
this.loadSysTypes();
|
||
document.querySelector('#app').style.overflowX = 'hidden';
|
||
this.applyAppScrollbarStyle();
|
||
|
||
},
|
||
methods: {
|
||
toggleAdvancedFilter() {
|
||
this.showAdvancedFilter = !this.showAdvancedFilter;
|
||
},
|
||
// 计算文本宽度(通过隐藏 span 获取实际宽度)
|
||
getTextWidth(text = '') {
|
||
if (typeof document === 'undefined') return 0;
|
||
const span = document.createElement('span');
|
||
span.innerText = text;
|
||
span.style.cssText = 'position:absolute;visibility:hidden;font-size:14px;font-family:inherit;white-space:nowrap;';
|
||
document.body.appendChild(span);
|
||
const { width } = span.getBoundingClientRect();
|
||
document.body.removeChild(span);
|
||
return Math.ceil(width);
|
||
},
|
||
// 计算列宽(头+内容取最大值,加 padding,并控制最小值)
|
||
calcColumnWidth(label, prop, padding = 24, min = 260, max = Infinity) {
|
||
const contents = (this.pageData || []).map(row => this.getTextWidth((row && row[prop]) || ''));
|
||
const maxContentWidth = contents.length ? Math.max(...contents) : 0;
|
||
const labelWidth = this.getTextWidth(label || '');
|
||
const baseWidth = Math.max(maxContentWidth, labelWidth) + padding;
|
||
const clamped = Math.max(min, Math.min(baseWidth, max));
|
||
return `${clamped}px`;
|
||
},
|
||
applyAppScrollbarStyle() {
|
||
if (this.scrollbarStyleApplied || typeof document === 'undefined') return;
|
||
if (document.getElementById('app-scrollbar-style')) {
|
||
this.scrollbarStyleApplied = true;
|
||
return;
|
||
}
|
||
const style = document.createElement('style');
|
||
style.id = 'app-scrollbar-style';
|
||
style.innerHTML = `
|
||
#app::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 8px;
|
||
}
|
||
#app::-webkit-scrollbar-thumb {
|
||
border-radius: 4px;
|
||
background-color: #4284F7;
|
||
}
|
||
#app::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
background-color:rgba(0, 0, 0, .1);
|
||
border-radius: 4px;
|
||
}
|
||
`;
|
||
document.head.appendChild(style);
|
||
this.scrollbarStyleApplied = true;
|
||
},
|
||
async loadShowSetTopFlag() {
|
||
try {
|
||
const res = await apiCourse.showSetTop();
|
||
this.showSetTopFeature = res && res.status === 200 && res.result === true;
|
||
} catch (error) {
|
||
this.showSetTopFeature = false;
|
||
}
|
||
},
|
||
async remoteSearchTeacher(keyword) {
|
||
const limited = (keyword || '').slice(0, 50);
|
||
if (this.$refs.teacherSelect && this.$refs.teacherSelect.query !== limited) {
|
||
this.$refs.teacherSelect.query = limited;
|
||
this.$nextTick(() => {
|
||
if (this.$refs.teacherSelect && this.$refs.teacherSelect.$refs && this.$refs.teacherSelect.$refs.input) {
|
||
this.$refs.teacherSelect.$refs.input.value = limited;
|
||
}
|
||
});
|
||
}
|
||
const query = limited.trim();
|
||
if (!query || query.length <= 1) {
|
||
this.teacherOptions = [];
|
||
return;
|
||
}
|
||
this.teacherLoading = true;
|
||
try {
|
||
const res = await apiTeacher.findByNameNew(query);
|
||
if (res && res.code === 200) {
|
||
const teacherList = res.data || [];
|
||
this.teacherOptions = teacherList
|
||
.map(item => this.formatTeacherItem(item))
|
||
.filter(item => item.teacherId);
|
||
} else {
|
||
this.teacherOptions = [];
|
||
}
|
||
} catch (error) {
|
||
this.teacherOptions = [];
|
||
} finally {
|
||
this.teacherLoading = false;
|
||
}
|
||
},
|
||
formatTeacherItem(item = {}) {
|
||
return {
|
||
teacherId: item.id,
|
||
teacherName: item.name,
|
||
teacherCode: item.mobile || '',
|
||
};
|
||
},
|
||
formatTeacherLabel(item = {}) {
|
||
if (!item.teacherName) {
|
||
return '';
|
||
}
|
||
return item.teacherCode ? `${item.teacherName}(${item.teacherCode})` : item.teacherName;
|
||
},
|
||
handleTeacherChange(value = []) {
|
||
this.params.teacherId = (value || []).join(',');
|
||
},
|
||
handleTeacherClear() {
|
||
this.teacherSelected = [];
|
||
this.teacherOptions = [];
|
||
this.params.teacherId = '';
|
||
},
|
||
// 授课教师下拉展开时,如果当前没有关键字,则清空上一次的查询结果
|
||
handleTeacherVisibleChange(visible) {
|
||
if (!visible) return;
|
||
// 打开时才处理
|
||
const select = this.$refs.teacherSelect;
|
||
const query = (select && select.query) || '';
|
||
if (!query) {
|
||
this.teacherOptions = [];
|
||
}
|
||
},
|
||
async remoteSearchCreator(keyword) {
|
||
const limited = (keyword || '').slice(0, 50);
|
||
if (this.$refs.creatorSelect && this.$refs.creatorSelect.query !== limited) {
|
||
this.$refs.creatorSelect.query = limited;
|
||
this.$nextTick(() => {
|
||
if (this.$refs.creatorSelect && this.$refs.creatorSelect.$refs && this.$refs.creatorSelect.$refs.input) {
|
||
this.$refs.creatorSelect.$refs.input.value = limited;
|
||
}
|
||
});
|
||
}
|
||
const query = limited.trim();
|
||
if (!query || query.length <= 1) {
|
||
this.creatorOptions = [];
|
||
return;
|
||
}
|
||
this.creatorLoading = true;
|
||
try {
|
||
const res = await apiUserbasic.selectUser(query);
|
||
if (res && res.status === 200) {
|
||
const resultList = res.result || [];
|
||
this.creatorOptions = resultList
|
||
.map(item => this.formatCreatorItem(item))
|
||
.filter(item => item.userId);
|
||
} else {
|
||
this.creatorOptions = [];
|
||
}
|
||
} catch (error) {
|
||
this.creatorOptions = [];
|
||
} finally {
|
||
this.creatorLoading = false;
|
||
}
|
||
},
|
||
formatCreatorItem(item = {}) {
|
||
return {
|
||
userId: item.id,
|
||
name: item.realName,
|
||
code: item.userNo,
|
||
};
|
||
},
|
||
formatCreatorLabel(item = {}) {
|
||
if (!item.name) {
|
||
return '';
|
||
}
|
||
return item.code ? `${item.name}(${item.code})` : item.name;
|
||
},
|
||
handleCreatorChange(value = []) {
|
||
this.params.createUserId = (value || []).slice(0, 5).join(',');
|
||
},
|
||
handleCreatorClear() {
|
||
this.creatorSelected = [];
|
||
this.params.createUserId = '';
|
||
},
|
||
// 创建人下拉展开时,如果当前没有关键字,则清空上一次的查询结果
|
||
handleCreatorVisibleChange(visible) {
|
||
if (!visible) return;
|
||
const select = this.$refs.creatorSelect;
|
||
const query = (select && select.query) || '';
|
||
if (!query) {
|
||
this.creatorOptions = [];
|
||
}
|
||
},
|
||
limitTeacherInput(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.teacherSelect) {
|
||
this.$refs.teacherSelect.query = limited;
|
||
}
|
||
},
|
||
limitCreatorInput(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.creatorSelect) {
|
||
this.$refs.creatorSelect.query = limited;
|
||
}
|
||
},
|
||
limitResOwnerInput(event) {
|
||
const limited = (event && event.target && event.target.value ? event.target.value : '').slice(0, 200);
|
||
if (event && event.target && event.target.value !== limited) {
|
||
event.target.value = limited;
|
||
}
|
||
if (this.$refs.resOwnerCascader) {
|
||
this.$refs.resOwnerCascader.inputValue = limited;
|
||
}
|
||
},
|
||
resOwnerFilterMethod(node, keyword) {
|
||
if (!keyword) return true;
|
||
const text = (node.label || (node.data && node.data.name) || '').toString().toLowerCase();
|
||
const kw = keyword.toString().toLowerCase();
|
||
return text.includes(kw);
|
||
},
|
||
handleTopSort() {
|
||
if (this.$refs.topSorter) {
|
||
this.$refs.topSorter.open();
|
||
}
|
||
},
|
||
onTopSorted() {
|
||
this.searchData(true);
|
||
},
|
||
handleLearningTimeRangeChange(val) {
|
||
if (val && val.length === 2) {
|
||
this.params.learningTimeStart = val[0];
|
||
this.params.learningTimeEnd = val[1];
|
||
} else {
|
||
this.params.learningTimeStart = '';
|
||
this.params.learningTimeEnd = '';
|
||
}
|
||
},
|
||
buildPickerOptions() {
|
||
const shortcutConfigs = [
|
||
{
|
||
text: '今年以来',
|
||
range: () => {
|
||
const end = new Date();
|
||
const start = new Date(end.getFullYear(), 0, 1);
|
||
return [start, end];
|
||
}
|
||
},
|
||
{
|
||
text: '最近一年',
|
||
range: () => {
|
||
const end = new Date();
|
||
const start = new Date();
|
||
start.setTime(end.getTime() - 3600 * 1000 * 24 * 365);
|
||
return [start, end];
|
||
}
|
||
},
|
||
{
|
||
text: '最近三个月',
|
||
range: () => {
|
||
const end = new Date();
|
||
const start = new Date();
|
||
start.setTime(end.getTime() - 3600 * 1000 * 24 * 90);
|
||
return [start, end];
|
||
}
|
||
},
|
||
{
|
||
text: '最近一个月',
|
||
range: () => {
|
||
const end = new Date();
|
||
const start = new Date();
|
||
start.setTime(end.getTime() - 3600 * 1000 * 24 * 30);
|
||
return [start, end];
|
||
}
|
||
},
|
||
{
|
||
text: '最近一周',
|
||
range: () => {
|
||
const end = new Date();
|
||
const start = new Date();
|
||
start.setTime(end.getTime() - 3600 * 1000 * 24 * 7);
|
||
return [start, end];
|
||
}
|
||
}
|
||
];
|
||
return {
|
||
shortcuts: shortcutConfigs.map(item => ({
|
||
text: item.text,
|
||
onClick: picker => {
|
||
picker.$emit('pick', item.range());
|
||
}
|
||
}))
|
||
};
|
||
},
|
||
validateLearningTimeRange() {
|
||
const hasStart = !!this.params.learningTimeStart;
|
||
const hasEnd = !!this.params.learningTimeEnd;
|
||
if ((hasStart && !hasEnd) || (!hasStart && hasEnd)) {
|
||
this.$showMessage('请选择完整的培训时间范围', 'warning');
|
||
return false;
|
||
}
|
||
return true;
|
||
},
|
||
formatCourseDuration(row) {
|
||
// if (row.durationDesc) {
|
||
// return row.durationDesc;
|
||
// }
|
||
const duration = row.courseDuration
|
||
if (duration === undefined || duration === null || duration === 0) {
|
||
return '0';
|
||
}
|
||
const minutes = Math.round(duration / 60);
|
||
//if (minutes >= 60) {
|
||
//const hours = (minutes / 60).toFixed(1);
|
||
//return `${hours}小时`;
|
||
//}
|
||
return `${minutes || 0}`;
|
||
},
|
||
formatStudyCount(row) {
|
||
const count = row.studyCount !== undefined
|
||
? row.studyCount
|
||
: (row.studyNum !== undefined ? row.studyNum : (row.learners !== undefined ? row.learners : row.studyUsers));
|
||
if (count === undefined || count === null) {
|
||
return '-';
|
||
}
|
||
return count;
|
||
},
|
||
formatScore(row) {
|
||
let score;
|
||
// 优先取score,其次取avgScore
|
||
if (row.score !== undefined && (row.score || row.score === 0)) {
|
||
score = row.score;
|
||
} else if (row.avgScore !== undefined && (row.avgScore || row.avgScore === 0)) {
|
||
score = row.avgScore;
|
||
} else {
|
||
return '-';
|
||
}
|
||
|
||
// 处理数值,四舍五入保留1位小数
|
||
// 先转换为数字类型,避免字符串格式的数值问题
|
||
const numScore = Number(score);
|
||
// 检查是否为有效数字
|
||
if (isNaN(numScore)) {
|
||
return '-';
|
||
}
|
||
|
||
// 四舍五入保留1位小数
|
||
return numScore.toFixed(1);
|
||
},
|
||
getDurationNumber(row) {
|
||
const duration = row.duration !== undefined
|
||
? row.duration
|
||
: (row.studyDuration !== undefined ? row.studyDuration : row.totalDuration);
|
||
if (duration === undefined || duration === null) {
|
||
return 0;
|
||
}
|
||
return Number(duration) || 0;
|
||
},
|
||
sortByDuration(a, b) {
|
||
return this.getDurationNumber(a) - this.getDurationNumber(b);
|
||
},
|
||
formatSysTypeChain(row = {}) {
|
||
const codes = [row.sysType1, row.sysType2, row.sysType3];
|
||
const names = codes
|
||
.filter(code => code !== undefined && code !== null && code !== '')
|
||
.map(code => this.sysTypeName(code))
|
||
.filter(name => name && name !== '');
|
||
return names.length ? names.join('/') : '';
|
||
},
|
||
sysTypeName(code) {
|
||
// console.log('code', code);
|
||
// console.log('this.sysTypeMap', this.sysTypeMap);
|
||
if (code == '') { return ''; }
|
||
return this.sysTypeMap.get(code);
|
||
},
|
||
getStudyCountNumber(row) {
|
||
const count = row.studyCount !== undefined
|
||
? row.studyCount
|
||
: (row.studyNum !== undefined ? row.studyNum : (row.learners !== undefined ? row.learners : row.studyUsers));
|
||
if (count === undefined || count === null) {
|
||
return 0;
|
||
}
|
||
return Number(count) || 0;
|
||
},
|
||
sortByStudyCount(a, b) {
|
||
return this.getStudyCountNumber(a) - this.getStudyCountNumber(b);
|
||
},
|
||
getScoreNumber(row) {
|
||
if (row.score || row.score === 0) {
|
||
return Number(row.score) || 0;
|
||
}
|
||
if (row.avgScore || row.avgScore === 0) {
|
||
return Number(row.avgScore) || 0;
|
||
}
|
||
return 0;
|
||
},
|
||
sortByScore(a, b) {
|
||
return this.getScoreNumber(a) - this.getScoreNumber(b);
|
||
},
|
||
getOrderNumber(row) {
|
||
if (row.orderIndex || row.orderIndex === 0) {
|
||
return Number(row.orderIndex) || 0;
|
||
}
|
||
if (row.orderValue || row.orderValue === 0) {
|
||
return Number(row.orderValue) || 0;
|
||
}
|
||
if (row.sortIndex || row.sortIndex === 0) {
|
||
return Number(row.sortIndex) || 0;
|
||
}
|
||
return 0;
|
||
},
|
||
sortByOrderValue(a, b) {
|
||
return this.getOrderNumber(a) - this.getOrderNumber(b);
|
||
},
|
||
formatOrderValue(row) {
|
||
if (row.orderIndex || row.orderIndex === 0) {
|
||
return row.orderIndex;
|
||
}
|
||
if (row.orderValue || row.orderValue === 0) {
|
||
return row.orderValue;
|
||
}
|
||
if (row.sortIndex || row.sortIndex === 0) {
|
||
return row.sortIndex;
|
||
}
|
||
return '-';
|
||
},
|
||
buildQueryParams() {
|
||
const query = {
|
||
...this.params,
|
||
pageIndex: this.page.pageIndex,
|
||
pageSize: this.page.pageSize,
|
||
};
|
||
Object.keys(query).forEach(key => {
|
||
if (query[key] === '' || query[key] === null) {
|
||
delete query[key];
|
||
}
|
||
});
|
||
const [sysOne = '', sysTwo = '', sysThree = ''] = this.sysTypeList || [];
|
||
query.sysType1 = sysOne;
|
||
query.sysType2 = sysTwo;
|
||
query.sysType3 = sysThree;
|
||
if (this.orgId) {
|
||
query.orgId = this.orgId;
|
||
}
|
||
if (this.params.name) {
|
||
query.keyword = this.params.name;
|
||
}
|
||
if (this.$route.query.courseIds) query.courseIds = this.$route.query.courseIds.split(',');
|
||
if (this.$route.query.projectId) query.projectId = this.$route.query.projectId;
|
||
if (this.audiences && this.audiences.length > 0) {
|
||
query.audiences = this.audiences.join(',');
|
||
}
|
||
query.isCreateCourse = !this.pageManage;
|
||
|
||
// 添加排序参数
|
||
if (this.orderField) {
|
||
query.orderField = this.orderField;
|
||
query.orderAsc = this.orderAsc;
|
||
}
|
||
|
||
return query;
|
||
},
|
||
async handleExport() {
|
||
if (!this.validateLearningTimeRange()) {
|
||
return;
|
||
}
|
||
const query = this.buildQueryParams();
|
||
delete query.pageIndex;
|
||
delete query.pageSize;
|
||
this.exportLoading = true;
|
||
try {
|
||
const blob = await apiCourse.exportCourse(query);
|
||
if (!(blob instanceof Blob)) {
|
||
throw new Error('导出失败');
|
||
}
|
||
const link = document.createElement('a');
|
||
const url = window.URL.createObjectURL(blob);
|
||
const timestamp = new Date().toISOString().replace(/[-:T]/g, '').split('.')[0];
|
||
link.href = url;
|
||
link.download = `在线课列表.xlsx`;
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
window.URL.revokeObjectURL(url);
|
||
} catch (error) {
|
||
this.$showMessage(error.message || '导出失败', 'error');
|
||
} finally {
|
||
this.exportLoading = false;
|
||
}
|
||
},
|
||
getAudiences() {
|
||
apiUserbasic.getInAudienceIds().then(res => {
|
||
if (res.status == 200) {
|
||
this.audiences = res.result;
|
||
}
|
||
this.searchData();
|
||
})
|
||
},
|
||
handleChoose(row) { //选择课程
|
||
window.parent.selectCourse(row);
|
||
},
|
||
// 置顶
|
||
setTop(row) {
|
||
let params = {
|
||
ids: row.id,//课程id,多个使用逗号分隔,
|
||
title: row.name,//课程的名称,
|
||
top: !row.isTop,// top 是否置顶}
|
||
}
|
||
console.log(row.isTop);
|
||
if (row.isTop == false) {
|
||
// console.log('fa')
|
||
apiCourse.setTop(params).then(res => {
|
||
if (res.status === 200 && res.result === true) {
|
||
this.$showMessage('置顶成功!', 'success')
|
||
this.searchData();
|
||
} else if (res.status === 500) {
|
||
const confirmText = `<i class="el-icon-warning-outline"></i>已置顶10条课程,若需继续置顶,请对部分课程执行取消置顶操作`;
|
||
this.$confirm(confirmText, '置顶确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
dangerouslyUseHTMLString: true,
|
||
type: 'warning',
|
||
customClass: 'custom-confirm-dialog'
|
||
}).then(() => {}).catch(() => { });
|
||
} else {
|
||
this.$showMessage(res.message, 'error');
|
||
}
|
||
})
|
||
} else if (row.isTop == true) {
|
||
apiCourse.setTop(params).then(res => {
|
||
if (res.status === 200 && res.result === true) {
|
||
this.$showMessage('取消成功!', 'success')
|
||
this.searchData();
|
||
} else {
|
||
this.$showMessage(res.message, 'error');
|
||
}
|
||
})
|
||
}
|
||
},
|
||
// 复制
|
||
copyCourse(item) {
|
||
const confirmText = `<i class="el-icon-warning-outline"></i>确认复制${item.name}吗?`;
|
||
this.$confirm(confirmText, '复制确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
dangerouslyUseHTMLString: true,
|
||
type: 'warning',
|
||
customClass: 'custom-confirm-dialog'
|
||
}).then(() => {
|
||
const reqdata = {
|
||
id: item.id,
|
||
refId: this.extendRefId,
|
||
refType: this.extendRefType,
|
||
};
|
||
apiCourse.copyCourse(reqdata).then(rs => {
|
||
if (rs.status === 200) {
|
||
this.$showMessage('复制成功', 'success');
|
||
this.searchData();
|
||
} else {
|
||
this.$showMessage(rs.message || '复制失败', 'error');
|
||
}
|
||
}).catch(() => {
|
||
this.$showMessage('复制失败', 'error');
|
||
});
|
||
}).catch(() => { });
|
||
},
|
||
// 撤回接口
|
||
withdraw(row) {
|
||
this.$confirm(`确定撤回${row.name}的审核申请吗?`, '撤回确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
customClass: 'custom-confirm-dialog'
|
||
}).then(() => {
|
||
apiCourse.revokeSubmit(row.id).then((res) => {
|
||
if (res.status === 200 && res.result) {
|
||
this.$showMessage('撤回成功', 'success');
|
||
this.searchData();
|
||
} else {
|
||
this.$showMessage(res.message || '撤回失败', 'error');
|
||
}
|
||
}).catch(() => {
|
||
this.$showMessage('撤回失败', 'error');
|
||
});
|
||
}).catch(() => { });
|
||
},
|
||
reset() {
|
||
this.resOwner = [];
|
||
this.sysTypeList = [];
|
||
this.learningTimeRange = [];
|
||
this.orgId = '';
|
||
this.orgName = '';
|
||
this.resOwnerSelected = [];
|
||
this.teacherFilterList = [];
|
||
this.teacherSelected = [];
|
||
this.teacherOptions = [];
|
||
this.teacherLoading = false;
|
||
this.creatorSelected = [];
|
||
this.creatorOptions = [];
|
||
Object.assign(this.params, {
|
||
name: '',
|
||
teacherId: '',
|
||
learningTimeStart: '',
|
||
learningTimeEnd: '',
|
||
status: '',
|
||
publish: '',
|
||
enabled: '',
|
||
openCourse: '',
|
||
createUserId: '',
|
||
createFrom: '',
|
||
});
|
||
// 重置排序
|
||
this.orderField = '';
|
||
this.orderAsc = true;
|
||
this.searchData(true);
|
||
},
|
||
...mapActions({
|
||
getResOwnerTree: 'resOwner/getResOwnerTree',
|
||
loadResOwners: 'resOwner/loadResOwners',
|
||
getSysTypeTree: 'sysType/getSysTypeTree',
|
||
loadSysTypes: 'sysType/loadSysTypes'
|
||
}),
|
||
resOwnerName(code) {
|
||
if (code == '') { return ''; }
|
||
return this.resOwnerMap.get(code);
|
||
},
|
||
// 直接审核
|
||
examineData() {
|
||
if (this.isExamine == 1) {
|
||
let params = {
|
||
id: this.examin.examineId,//课程id,
|
||
title: this.examin.examineName,//课程的名称,
|
||
pass: this.auditInfo.pass,//Boolean 是否通过,
|
||
remark: this.auditInfo.remark,// 备注
|
||
}
|
||
apiCourse.audit(params).then(res => {
|
||
if (res.status === 200) {
|
||
this.$showMessage('操作成功!', 'success');
|
||
this.dialogVisible = false;
|
||
this.searchData();
|
||
} else {
|
||
this.$showMessage(res.message, 'error');
|
||
}
|
||
})
|
||
} else {
|
||
this.$showMessage('暂未开放!', 'warning');
|
||
}
|
||
|
||
},
|
||
addNewCourse() {
|
||
this.$refs.courseForm.initShow();
|
||
},
|
||
editCurriculum(row) {
|
||
this.$refs.courseForm.initShow(row);
|
||
},
|
||
// 课程启停
|
||
async isDisable(row) {
|
||
const nextEnabled = !row.enabled;
|
||
const actionText = nextEnabled ? '启用' : '停用';
|
||
try {
|
||
await this.$confirm(`<i class="el-icon-warning-outline"></i>确定${actionText}${row.name}吗?`, `${actionText}确认`, {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning',
|
||
dangerouslyUseHTMLString: true,
|
||
customClass: 'custom-confirm-dialog'
|
||
});
|
||
} catch (error) {
|
||
return;
|
||
}
|
||
const params = {
|
||
ids: row.id,
|
||
title: row.name,
|
||
enabled: nextEnabled,
|
||
};
|
||
try {
|
||
const { status } = await apiCourse.setEnabled(params);
|
||
if (status === 200) {
|
||
this.$showMessage(`${actionText}成功`, 'success');
|
||
row.enabled = nextEnabled;
|
||
} else {
|
||
this.$showMessage(`${actionText}失败`, 'error');
|
||
}
|
||
} catch (error) {
|
||
this.$showMessage(`${actionText}失败`, 'error');
|
||
}
|
||
},
|
||
// 课程查询
|
||
searchData(pageReset) {
|
||
if (pageReset) {
|
||
this.page.pageIndex = 1;
|
||
}
|
||
if (!this.validateLearningTimeRange()) {
|
||
return;
|
||
}
|
||
console.log('apiCourse', apiCourse);
|
||
console.log('buildQueryParams', this.buildQueryParams());
|
||
const query = this.buildQueryParams();
|
||
apiCourse.managePage(query).then(rs => {
|
||
if (rs.status == 200) {
|
||
this.pageData = rs.result.list ? rs.result.list : [];
|
||
this.page.count = rs.result.count;
|
||
this.page.pageSize = rs.result.pageSize;
|
||
} else {
|
||
return this.$showMessage(rs.message, 'error');
|
||
}
|
||
}).catch(err => {
|
||
console.error('查询课程列表失败:', err);
|
||
this.$showMessage('查询失败,请稍后重试', 'error');
|
||
});
|
||
|
||
},
|
||
// 表格排序变化事件
|
||
handleSortChange({ column, prop, order }) {
|
||
// order: ascending(升序) | descending(降序) | null(取消排序)
|
||
console.log('排序变化:', { column, prop, order });
|
||
|
||
if (order) {
|
||
// 有排序:保存排序字段和顺序
|
||
this.orderField = prop;
|
||
this.orderAsc = order === 'ascending';
|
||
} else {
|
||
// 取消排序:清空排序字段
|
||
this.orderField = '';
|
||
this.orderAsc = true;
|
||
}
|
||
|
||
// 重新查询数据
|
||
this.searchData(true);
|
||
},
|
||
viewTopic(row) {
|
||
if (row.status == 1) {
|
||
return this.$showMessage('请提交课程再预览!', 'warning')
|
||
}
|
||
this.examin = {};
|
||
this.examin.detailType = row.type;
|
||
this.examin.examineId = row.id;
|
||
this.examin.examineName = row.name;
|
||
this.showDetails = true;
|
||
},
|
||
jumpDetails() {
|
||
// this.$router.push('/course/micro');
|
||
// 跳转打开新页面
|
||
let routeData = this.$router.resolve({ path: '/course/detail' }); // , query: { id: 1 }
|
||
window.open(this.webBaseUrl + routeData.href, '_blank');
|
||
},
|
||
toExamine(row) {
|
||
this.auditInfo = { pass: true };
|
||
this.examin.detailType = row.type;
|
||
this.examin.examineId = row.id;
|
||
this.examin.examineName = row.name;
|
||
this.dialogVisible = true;
|
||
},
|
||
enSure() {
|
||
// 确认事件
|
||
},
|
||
handleSizeChange(val) {
|
||
this.page.pageSize = val;
|
||
this.page.pageIndex = 1;
|
||
this.searchData();
|
||
},
|
||
handleCurrentChange(val) {
|
||
this.page.pageIndex = val;
|
||
this.searchData();
|
||
},
|
||
chooseInvite(row) {
|
||
//邀请老师审核
|
||
this.inviteTeacher.dlgShow = true;
|
||
},
|
||
findTeachers() {
|
||
this.inviteTeacher.list = [
|
||
{ id: '1', name: '李玉冰', type: '通过', text: '实用', sex: '男', code: '1023123', orgInfo: '教育技术中心', checked: false },
|
||
{ id: '2', name: '李玉冰', type: '未通过', text: '内容在调整', sex: '男', code: '1023123', orgInfo: '教育技术中心', checked: false },
|
||
{ id: '3', name: '李玉冰', type: '驳回', text: '内容重复', sex: '男', code: '1023123', orgInfo: '教育技术中心', checked: false }
|
||
];
|
||
},
|
||
showQrimage(row) {
|
||
// 使用本页二维码弹窗逻辑
|
||
this.qrCodedialogVisible = true;
|
||
// 与 TeacherList 保持一致的二维码生成逻辑
|
||
this.copyUrl = this.qrcodeImgUrl = `${process.env.VUE_APP_BOE_WEB_URL}/systemapi/xboe/m/course/manage/redirectDetail?courseId=${row.id}`;
|
||
this.$nextTick(() => {
|
||
this.crateQrcode();
|
||
});
|
||
},
|
||
handleCopyUrl() {
|
||
const ele = document.getElementById('text');
|
||
if (!ele) return;
|
||
ele.select();
|
||
document.execCommand('Copy');
|
||
this.$showMessage('复制成功', 'success');
|
||
},
|
||
downloadQrcode() {
|
||
const container = document.getElementById('qrcode');
|
||
if (!container) return;
|
||
const img = container.getElementsByTagName('img')[0];
|
||
if (!img) return;
|
||
const canvas = document.createElement('canvas');
|
||
canvas.width = img.width;
|
||
canvas.height = img.height;
|
||
const ctx = canvas.getContext('2d');
|
||
ctx.drawImage(img, 0, 0);
|
||
const tempUrl = canvas.toDataURL('image/png');
|
||
const link = document.createElement('a');
|
||
link.style.display = 'none';
|
||
link.href = tempUrl;
|
||
link.setAttribute('download', '二维码.jpg');
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
},
|
||
crateQrcode() {
|
||
// eslint-disable-next-line no-new
|
||
new QRCode('qrcode', {
|
||
width: 150,
|
||
height: 150,
|
||
text: this.qrcodeImgUrl
|
||
});
|
||
},
|
||
// 关闭弹框,清除已经生成的二维码
|
||
closeCode() {
|
||
this.qrCodedialogVisible = false;
|
||
// 逐个节点移除,避免事件丢失
|
||
const images = document.querySelectorAll('.qrcode-img img');
|
||
images.forEach(img => img.remove());
|
||
const canvas = document.querySelectorAll('.qrcode-img canvas');
|
||
canvas.forEach(c => c.remove());
|
||
},
|
||
showManageStudy(row) {
|
||
// 带课程详情跳转到课程管理页
|
||
sessionStorage.setItem('courseDetail', JSON.stringify(row));
|
||
this.$router.push({ path: '/iframe/course/coursemanage-remote' });
|
||
},
|
||
async loadResOwnerNode(node, resolve) {
|
||
const { level } = node;
|
||
let parentId = '';
|
||
|
||
if (level === 0) {
|
||
// 第一层:获取根节点
|
||
try {
|
||
const res = await apiUserbasic.findOrgsByKeyword('');
|
||
if (res && res.result) {
|
||
const nodes = res.result.map(item => ({
|
||
id: item.id,
|
||
name: item.name,
|
||
hrbpId: item.hrbpId,
|
||
leaf: false, // 懒加载模式下,先假设所有节点都可能有子节点
|
||
children: [] // 空数组表示需要懒加载
|
||
}));
|
||
resolve(nodes);
|
||
} else {
|
||
resolve([]);
|
||
}
|
||
} catch (error) {
|
||
console.error('加载资源归属失败:', error);
|
||
resolve([]);
|
||
}
|
||
} else {
|
||
// 子节点:根据父节点ID加载
|
||
parentId = node.value || (node.data && node.data.id);
|
||
if (!parentId) {
|
||
resolve([]);
|
||
return;
|
||
}
|
||
try {
|
||
const res = await apiUserbasic.getOrgInfo(parentId);
|
||
if (res && res.status === 200 && res.result) {
|
||
let treeList = [];
|
||
if (res.result.directChildList && res.result.directChildList.length > 0) {
|
||
treeList = res.result.directChildList.map(item => ({
|
||
id: item.id,
|
||
name: item.name,
|
||
hrbpId: item.hrbpId,
|
||
leaf: false, // 懒加载模式下,先假设所有节点都可能有子节点
|
||
children: [] // 空数组表示需要懒加载
|
||
}));
|
||
}
|
||
resolve(treeList);
|
||
} else {
|
||
// 如果没有子节点,返回空数组,级联选择器会自动将其视为叶子节点
|
||
resolve([]);
|
||
}
|
||
} catch (error) {
|
||
console.error('加载资源归属子节点失败:', error);
|
||
resolve([]);
|
||
}
|
||
}
|
||
},
|
||
handleResOwnerChange(value) {
|
||
if (!value || value.length === 0) {
|
||
this.orgId = '';
|
||
this.orgName = '';
|
||
return;
|
||
}
|
||
// value 是选中的路径数组,最后一个元素是选中的节点ID
|
||
const selectedId = value[value.length - 1];
|
||
this.orgId = selectedId;
|
||
|
||
// 通过级联选择器的 getCheckedNodes 方法获取选中的节点信息
|
||
this.$nextTick(() => {
|
||
if (this.$refs.resOwnerCascader) {
|
||
const checkedNodes = this.$refs.resOwnerCascader.getCheckedNodes();
|
||
if (checkedNodes && checkedNodes.length > 0) {
|
||
const lastNode = checkedNodes[checkedNodes.length - 1];
|
||
this.orgName = lastNode.label || lastNode.name || '';
|
||
}
|
||
}
|
||
});
|
||
},
|
||
showChooseCourse() {
|
||
this.courseChooseShow = true;
|
||
},
|
||
chooseCourseType(item, idx) {
|
||
this.courseChooseId = item.id;
|
||
},
|
||
toInputCourse() {
|
||
if (this.courseChooseId == '1') {
|
||
this.showWeike();
|
||
} else if (this.courseChooseId == '2') {
|
||
this.showBiaoke();
|
||
}
|
||
this.courseChooseShow = false;
|
||
},
|
||
newHandleClick() { },
|
||
showRecords(item) {
|
||
this.recommend.dlgShow = true;
|
||
},
|
||
showWeike() {
|
||
this.weike.dlgShow = true;
|
||
},
|
||
showBiaoke() {
|
||
this.biaoke.dlgShow = true;
|
||
},
|
||
// setTop(item, idx) {
|
||
// let msg = '已设置置顶';
|
||
// if (item.isTop) {
|
||
// item.isTop = false;
|
||
// msg = '已取消置顶';
|
||
// } else {
|
||
// item.isTop = true;
|
||
// }
|
||
// this.$showMessage('xxx', 'success');
|
||
// },
|
||
delItem(row) {
|
||
// this.$showMessage('删除成功', 'success');
|
||
// return false
|
||
this.$confirm(`<i class="el-icon-warning-outline"></i>确认删除${row.name}吗?`, '删除确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
dangerouslyUseHTMLString: true,
|
||
type: 'warning',
|
||
customClass: 'custom-confirm-dialog'
|
||
}).then(async () => {
|
||
let params = {
|
||
id: row.id,
|
||
title: row.name,
|
||
|
||
};
|
||
try {
|
||
// {id:课程id,多个使用逗号分隔,Boolean erasable 是否物理删除,title:课程的名称, remark 备注}
|
||
const { status } = await apiCourse.del(params);
|
||
if (status === 200) {
|
||
this.$showMessage('删除成功', 'success'); //只是之前发布过的课程删除才可
|
||
// if(!row.erasable){
|
||
// let event = {
|
||
// key: "CourseDelete",//被管理员删除
|
||
// title: '被管理员删除课程',//事件的标题
|
||
// parameters:"author:"+row.sysCreateAid,//作者,一般这种情况不是管理员自己添加的
|
||
// content: '被管理员删除课程',//事件的内容
|
||
// objId: row.id,//关联的id
|
||
// objType: "1",//关联的类型
|
||
// objInfo:row.name,
|
||
// aid: this.userInfo.aid, //当前登录人的id
|
||
// aname: this.userInfo.name,//当前人的姓名
|
||
// status: 1 //状态,直接写1
|
||
// }
|
||
// this.$store.dispatch("userTrigger", event);
|
||
// }
|
||
|
||
this.searchData();
|
||
}
|
||
} catch (error) {
|
||
this.$showMessage('删除失败', 'error');
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
// this.$showMessage('已取消删除', 'info');
|
||
});
|
||
},
|
||
showAddCatalogZhang(bal) {
|
||
this.catalogs.addNewZhang = bal;
|
||
},
|
||
saveNewCatalogZhang() {
|
||
this.catalogs.addNewZhang = false;
|
||
},
|
||
handleAction(key, row) {
|
||
switch (key) {
|
||
case 'edit':
|
||
return this.editCurriculum(row);
|
||
case 'qrcode':
|
||
return this.showQrimage(row);
|
||
case 'manage':
|
||
return this.showManageStudy(row);
|
||
case 'withdraw':
|
||
return this.withdraw(row);
|
||
case 'delete':
|
||
return this.delItem(row);
|
||
case 'copy':
|
||
return this.copyCourse(row);
|
||
case 'toggleEnable':
|
||
return this.isDisable(row);
|
||
case 'toggleTop':
|
||
return this.setTop(row);
|
||
default:
|
||
return;
|
||
}
|
||
},
|
||
buildActions(row) {
|
||
const actions = [];
|
||
// 优先级按原有展示顺序
|
||
if (row.isPermission && row.status != 2) {
|
||
actions.push({ key: 'edit', label: '编辑', className: 'action-link--primary' });
|
||
}
|
||
if (row.published) {
|
||
actions.push({ key: 'qrcode', label: '二维码', className: 'action-link--primary' });
|
||
}
|
||
if (row.isPermission && !this.forChoose && row.published) {
|
||
actions.push({ key: 'manage', label: '管理', className: 'action-link--primary' });
|
||
}
|
||
if (row.isPermission && !this.forChoose && row.status == 2) {
|
||
actions.push({ key: 'withdraw', label: '撤回', className: 'action-link--primary' });
|
||
}
|
||
if ((row.isPermission && row.status != 2 && !row.published) || (row.isPermission && !row.enabled)) {
|
||
actions.push({ key: 'delete', label: '删除', className: 'action-link--danger' });
|
||
}
|
||
// 更多里的动作
|
||
if (row.isPermission && row.published) {
|
||
actions.push({ key: 'copy', label: '复制', className: 'action-link--primary' });
|
||
actions.push({ key: 'toggleEnable', label: row.enabled ? '停用' : '启用', className: 'action-link--primary' });
|
||
if (this.showSetTopFeature) {
|
||
actions.push({ key: 'toggleTop', label: row.isTop ? '取消置顶' : '置顶', className: 'action-link--primary' });
|
||
}
|
||
}
|
||
return actions;
|
||
},
|
||
getInlineActions(row) {
|
||
const actions = this.buildActions(row);
|
||
return actions.slice(0, 4);
|
||
},
|
||
getDropdownActions(row) {
|
||
const actions = this.buildActions(row);
|
||
return actions.length > 4 ? actions.slice(4) : [];
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
#app::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 8px;
|
||
}
|
||
|
||
#app::-webkit-scrollbar-thumb {
|
||
border-radius: 6px;
|
||
background-color: rgb(78, 166, 255);
|
||
}
|
||
|
||
.noSplitDatePicker {
|
||
|
||
/* 初始隐藏范围选择器的分隔符与关闭图标 */
|
||
::v-deep .el-range-separator,
|
||
::v-deep .el-range__close-icon {
|
||
display: none !important;
|
||
}
|
||
}
|
||
|
||
// .resetDatePicker {
|
||
// ::v-deep .el-date-editor {
|
||
// width: 250px;
|
||
// }
|
||
// }
|
||
|
||
.sou {
|
||
padding: 0 0 0 0px !important;
|
||
}
|
||
|
||
.el-col {
|
||
padding: 0 0 0 10px !important;
|
||
}
|
||
|
||
.table-wrapper {
|
||
padding: 16px;
|
||
background-color: #ffffff;
|
||
margin-top: 10px;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.grid-content {
|
||
padding-right: 0px;
|
||
}
|
||
|
||
.org-name-cell {
|
||
display: block;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.el-button--text {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.filter-wrapper {
|
||
padding: 16px;
|
||
background-color: #ffffff;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.filter-row {
|
||
display: flex;
|
||
flex-wrap: nowrap;
|
||
align-items: center;
|
||
// margin-bottom: 10px;
|
||
}
|
||
|
||
.filter-fields {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.filter-field {
|
||
margin-right: 10px;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.filter-row--advanced {
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.toggle-link {
|
||
padding: 0;
|
||
font-size: 14px;
|
||
color: #4284F7;
|
||
}
|
||
|
||
.option-code {
|
||
margin-left: 4px;
|
||
color: #999;
|
||
}
|
||
|
||
.teacher-filter {
|
||
::v-deep .choice {
|
||
width: 100%;
|
||
}
|
||
}
|
||
.creator-filter {
|
||
::v-deep .choice {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
.filter-actions--inline {
|
||
padding-top: 0;
|
||
}
|
||
|
||
.filter-actions .el-button+.el-button {
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.filter-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.filter-actions .icon-btn+.icon-btn {
|
||
margin-left: 8px;
|
||
}
|
||
|
||
.filter-actions .toggle-link {
|
||
margin-right: 10px;
|
||
}
|
||
|
||
.icon-btn {
|
||
width: 32px;
|
||
height: 32px;
|
||
cursor: pointer;
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
background-size: 32px 32px;
|
||
background-color: transparent;
|
||
// transition: all 0.2s ease;
|
||
}
|
||
|
||
.icon-btn:disabled {
|
||
cursor: not-allowed;
|
||
opacity: 0.6;
|
||
}
|
||
|
||
.icon-btn--search {
|
||
background-image: url('~@/assets/images/svg/search_active.svg');
|
||
}
|
||
|
||
.icon-btn--reset {
|
||
background-image: url('~@/assets/images/svg/reset.svg');
|
||
background-size: 15px 15px;
|
||
}
|
||
|
||
.icon-btn--reset:hover,
|
||
.icon-btn--reset:active {
|
||
background-image: url('~@/assets/images/svg/reset_active.svg');
|
||
background-size: 32px 32px;
|
||
}
|
||
|
||
.icon-btn--top {
|
||
background-image: url('~@/assets/images/svg/pintotop.svg');
|
||
background-size: 14px 14px;
|
||
}
|
||
|
||
.icon-btn--top:hover,
|
||
.icon-btn--top:active {
|
||
background-image: url('~@/assets/images/svg/pintotop_active.svg');
|
||
background-size: 32px 32px;
|
||
}
|
||
|
||
.icon-btn--export {
|
||
background-image: url('~@/assets/images/svg/export.svg');
|
||
background-size: 14px 14px;
|
||
}
|
||
|
||
.icon-btn--export:hover,
|
||
.icon-btn--export:active {
|
||
background-image: url('~@/assets/images/svg/export_active.svg');
|
||
background-size: 32px 32px;
|
||
|
||
}
|
||
|
||
.icon-btn.is-disabled {
|
||
cursor: not-allowed;
|
||
opacity: 0.6;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.filter-extra-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
padding-bottom: 20px;
|
||
}
|
||
|
||
.filter-extra-actions .icon-btn,
|
||
.filter-extra-actions .create-course-btn {
|
||
margin-right: 10px;
|
||
}
|
||
|
||
.create-course-btn {
|
||
width: 120px;
|
||
height: 32px;
|
||
padding-left: 36px;
|
||
padding-right: 16px;
|
||
border-radius: 6px;
|
||
border: 1px solid #4284F7;
|
||
background-color: #ffffff;
|
||
background-image: url('~@/assets/images/svg/createCourse.svg');
|
||
background-repeat: no-repeat;
|
||
background-position: 12px center;
|
||
background-size: 14px 14px;
|
||
color: #4284F7;
|
||
font-weight: 400;
|
||
font-size: 14px;
|
||
line-height: 32px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.create-course-btn:hover,
|
||
.create-course-btn:active {
|
||
background-color: #4284F7;
|
||
border-color: transparent;
|
||
color: #ffffff;
|
||
background-image: url('~@/assets/images/svg/createCourse_active.svg');
|
||
}
|
||
|
||
.toggle-link {
|
||
padding: 0;
|
||
}
|
||
|
||
/* 移除右对齐,保持左侧布局 */
|
||
::v-deep .el-table .el-table__body-wrapper::-webkit-scrollbar {
|
||
display: block;
|
||
}
|
||
|
||
.advanced-filter {
|
||
margin-top: 10px;
|
||
}
|
||
|
||
// .learning-time-range .el-date-editor {
|
||
// width: 250px;
|
||
// }
|
||
|
||
.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: 28px;
|
||
height: 28px;
|
||
background: #F5F9FF;
|
||
border-radius: 4px;
|
||
border: 1px solid #DFDFDF;
|
||
font-size: 14px;
|
||
color: #000000;
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
.course-types {
|
||
display: flex;
|
||
justify-content: center;
|
||
padding: 15px;
|
||
|
||
.course-type {
|
||
margin: 10px;
|
||
text-align: center;
|
||
padding: 5px;
|
||
cursor: pointer;
|
||
|
||
img {
|
||
width: 110px;
|
||
height: 110px;
|
||
}
|
||
|
||
.info {
|
||
padding-top: 10px;
|
||
}
|
||
}
|
||
|
||
.choose {
|
||
border: 2px solid #008000;
|
||
}
|
||
}
|
||
|
||
.el-aside {
|
||
padding: 5px 10px;
|
||
}
|
||
|
||
.cctree {
|
||
.cctree-chapter {
|
||
.cctree-chapter-name {
|
||
border-bottom: 1px solid #dddddd;
|
||
}
|
||
|
||
.cctree-chapter-cells {
|
||
margin: 0px;
|
||
list-style-type: circle;
|
||
padding: 0px;
|
||
|
||
.cctree-chapter-cell {
|
||
line-height: 30px;
|
||
list-style-type: circle;
|
||
padding-left: 25px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.el-dialog__body {
|
||
overflow: hidden;
|
||
}
|
||
|
||
::v-deep .el-table .el-table__body-wrapper::-webkit-scrollbar-thumb {
|
||
border-radius: 4px;
|
||
background-color: #4284F7;
|
||
}
|
||
|
||
::v-deep .el-table .el-table__body-wrapper::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
background-color:rgba(0, 0, 0, .1);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
::v-deep .el-table .el-table__body-wrapper::-webkit-scrollbar-thumb {
|
||
border-radius: 4px;
|
||
background-color: #4284F7
|
||
}
|
||
|
||
|
||
|
||
::v-deep.el-table {
|
||
border-radius: 6px 6px 0 0;
|
||
td.el-table__cell {
|
||
border-bottom: 1px solid rgba(0, 0, 0, .1);
|
||
}
|
||
th.el-table__cell {
|
||
background: rgba(66, 132, 247, 0.1);
|
||
padding: 3px 0;
|
||
|
||
.cell {
|
||
font-weight: bold;
|
||
font-size: 14px;
|
||
color: #60769D;
|
||
}
|
||
.caret-wrapper {
|
||
.sort-caret {
|
||
border: 4px solid transparent;
|
||
&.ascending {
|
||
border-bottom-color: #C0C4CC;
|
||
top: 8px;
|
||
}
|
||
&.descending {
|
||
border-top-color: #C0C4CC;
|
||
bottom: 8px;
|
||
}
|
||
}
|
||
}
|
||
&.ascending .sort-caret.ascending {
|
||
border-bottom-color: #409EFF;
|
||
}
|
||
&.descending .sort-caret.descending{
|
||
border-top-color: #409EFF;
|
||
}
|
||
|
||
&.el-table--medium .el-table__cell {
|
||
padding: 5px 0;
|
||
}
|
||
}
|
||
|
||
.course-name {
|
||
font-weight: 400;
|
||
font-size: 14px;
|
||
color: #000000;
|
||
line-height: 20px;
|
||
}
|
||
|
||
.common-cell {
|
||
font-weight: 400;
|
||
font-size: 14px;
|
||
color: #000000;
|
||
}
|
||
.common-cell-right {
|
||
padding-right: 20px;
|
||
}
|
||
|
||
.single-line-ellipsis {
|
||
display: inline-block;
|
||
max-width: 100%;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.status--pass {
|
||
color: #2EAD4D;
|
||
}
|
||
|
||
.status--reject {
|
||
color: #F41228;
|
||
}
|
||
|
||
.action-link--primary {
|
||
color: #4284F7;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.action-link--danger {
|
||
color: #E32E2E;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.action-link--more {
|
||
color: #999999;
|
||
font-weight: bold;
|
||
.el-icon-arrow-down {
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
|
||
.action-link--bold {
|
||
font-weight: bold;
|
||
}
|
||
|
||
::v-deep .el-table--medium .el-table__cell {
|
||
padding: 5px 0;
|
||
}
|
||
}
|
||
.qrcode-img {
|
||
width: 150px;
|
||
height: 150px;
|
||
display: block;
|
||
position: relative;
|
||
.downloadn-container {
|
||
position: absolute;
|
||
width: 40px;
|
||
height: 45px;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
z-index: 99;
|
||
background: white;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||
cursor: pointer;
|
||
span {
|
||
color: #409eff;
|
||
display: block;
|
||
font-size: 12px;
|
||
line-height: 12px;
|
||
}
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<style lang="scss">
|
||
.filter-field--name {
|
||
.el-input__inner {
|
||
width: 444px;
|
||
height: 32px;
|
||
background: #FFFFFF;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 0, 0, .2);
|
||
line-height: 32px;
|
||
padding: 0 10px;
|
||
&:focus {
|
||
border-color: #4284F7;
|
||
}
|
||
}
|
||
}
|
||
|
||
.filter-field--category {
|
||
.el-cascader {
|
||
line-height: 32px;
|
||
.el-input__inner {
|
||
width: 180px;
|
||
height: 32px;
|
||
background: #FFFFFF;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 0, 0, .2);
|
||
line-height: 32px;
|
||
padding: 0 10px;
|
||
color: #000000;
|
||
&:focus {
|
||
border: 1px solid #4284F7;
|
||
}
|
||
}
|
||
|
||
.el-input {
|
||
line-height: 32px;
|
||
&.is-focus .el-input__inner{
|
||
border-color: #4284F7;
|
||
}
|
||
}
|
||
|
||
.el-input__suffix {
|
||
line-height: 32px;
|
||
}
|
||
|
||
.el-input__icon {
|
||
line-height: 32px;
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
.filter-field--teacher {
|
||
.el-select {
|
||
.el-select__input {
|
||
margin-left: 10px;
|
||
}
|
||
.el-input__inner {
|
||
width: 180px;
|
||
height: 32px;
|
||
background: #FFFFFF;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 0, 0, .2);
|
||
line-height: 32px;
|
||
padding: 0 10px;
|
||
}
|
||
.el-input {
|
||
&.is-focus .el-input__inner{
|
||
border-color: #4284F7;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.filter-field--status {
|
||
.el-select {
|
||
.el-select__input {
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.el-input__inner {
|
||
width: 136px;
|
||
height: 32px;
|
||
background: #FFFFFF;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 0, 0, .2);
|
||
line-height: 32px;
|
||
padding: 0 10px;
|
||
&.is-focus .el-input__inner{
|
||
border-color: #4284F7;
|
||
}
|
||
}
|
||
|
||
.el-input__icon {
|
||
line-height: 32px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.filter-field--publish {
|
||
.el-select {
|
||
.el-select__input {
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.el-input__inner {
|
||
width: 136px;
|
||
height: 32px;
|
||
background: #FFFFFF;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 0, 0, .2);
|
||
line-height: 32px;
|
||
padding: 0 10px;
|
||
&.is-focus .el-input__inner{
|
||
border-color: #4284F7;
|
||
}
|
||
}
|
||
|
||
.el-input__icon {
|
||
line-height: 32px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.filter-field--enabled {
|
||
.el-select {
|
||
.el-select__input {
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.el-input__inner {
|
||
width: 136px;
|
||
height: 32px;
|
||
background: #FFFFFF;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 0, 0, .2);
|
||
line-height: 32px;
|
||
padding: 0 10px;
|
||
&.is-focus .el-input__inner{
|
||
border-color: #4284F7;
|
||
}
|
||
}
|
||
|
||
.el-input__icon {
|
||
line-height: 32px;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
.filter-field--open {
|
||
.el-select {
|
||
.el-select__input {
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.el-input__inner {
|
||
width: 124px;
|
||
height: 32px;
|
||
background: #FFFFFF;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 0, 0, .2);
|
||
line-height: 32px;
|
||
padding: 0 10px;
|
||
&.is-focus .el-input__inner{
|
||
border-color: #4284F7;
|
||
}
|
||
}
|
||
|
||
.el-input__icon {
|
||
line-height: 32px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.filter-field--resowner {
|
||
.el-cascader {
|
||
width: 180px;
|
||
line-height: 32px;
|
||
.el-input__inner {
|
||
width: 180px;
|
||
height: 32px;
|
||
background: #FFFFFF;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 0, 0, .2);
|
||
line-height: 32px;
|
||
padding: 0 10px;
|
||
&:focus {
|
||
border-color: #4284F7;
|
||
}
|
||
}
|
||
&.is-focus .el-input__inner {
|
||
border-color: #4284F7;
|
||
}
|
||
}
|
||
.el-input {
|
||
.el-input__inner {
|
||
width: 180px;
|
||
height: 32px;
|
||
background: #FFFFFF;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 0, 0, .2);
|
||
line-height: 32px;
|
||
padding: 0 10px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.filter-field--creator {
|
||
.el-select {
|
||
.el-select__input {
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.el-input__inner {
|
||
width: 136px;
|
||
height: 32px;
|
||
background: #FFFFFF;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 0, 0, .2);
|
||
line-height: 32px;
|
||
padding: 0 10px;
|
||
&.is-focus .el-input__inner{
|
||
border-color: #4284F7;
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
.filter-field--create-from {
|
||
.el-select {
|
||
.el-select__input {
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.el-input__inner {
|
||
width: 180px;
|
||
height: 32px;
|
||
background: #FFFFFF;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 0, 0, .2);
|
||
line-height: 32px;
|
||
padding: 0 10px;
|
||
&.is-focus .el-input__inner{
|
||
border-color: #4284F7;
|
||
}
|
||
}
|
||
|
||
.el-input__icon {
|
||
line-height: 32px;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
.filter-field--time {
|
||
.el-input__inner {
|
||
width: 316px;
|
||
height: 32px;
|
||
background: #FFFFFF;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(0, 0, 0, .2);
|
||
line-height: 32px;
|
||
padding: 0 10px;
|
||
|
||
.el-input__icon {
|
||
line-height: 32px;
|
||
|
||
&.el-icon-date {
|
||
position: absolute;
|
||
right: 0;
|
||
}
|
||
|
||
&.el-icon-close {
|
||
margin-right: 8px;
|
||
}
|
||
}
|
||
.el-range-editor {
|
||
.is-active:hover{
|
||
border-color: #4284F7;
|
||
}
|
||
}
|
||
.el-range-separator {
|
||
line-height: 30px;
|
||
}
|
||
|
||
.el-range-input {
|
||
line-height: 30px;
|
||
}
|
||
}
|
||
.noSplitDatePicker {
|
||
.el-range-input {
|
||
text-align: left;
|
||
}
|
||
}
|
||
}
|
||
|
||
.icon-btn-tooltip {
|
||
height: 30px;
|
||
background: rgba(0, 0, 0, 1);
|
||
border-radius: 15px;
|
||
padding: 0 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 14px;
|
||
line-height: 30px;
|
||
.popper__arrow {
|
||
display: none !important;
|
||
}
|
||
.el-tooltip__popper__arrow {
|
||
display: none !important;
|
||
}
|
||
}
|
||
</style>
|
||
|