first commit

This commit is contained in:
2026-08-26 14:25:17 +08:00
commit 06debfd1fc
10595 changed files with 1836924 additions and 0 deletions
@@ -0,0 +1,412 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<guava ref="guava">
<el-card shadow="never">
<search :is-search-button="false">
<search-item label="课程名称">
<el-input v-model="pageForm.courseName" clearable placeholder="请输入课程名称" style="width: 100%" @keyup.enter.native="doSearch"></el-input>
</search-item>
<search-item label="课程类型">
<el-select v-model="pageForm.courseTypeId" clearable filterable placeholder="请选择课程类型" style="width: 100%">
<el-option v-for="item in courseTypeOptions" :key="item.id" :label="item.typeName" :value="item.id"></el-option>
</el-select>
</search-item>
<search-item label="开课时间">
<el-date-picker
v-model="pageForm.courseTime"
type="daterange"
start-placeholder="开始时间"
end-placeholder="结束时间"
range-separator="至"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100%">
</el-date-picker>
</search-item>
<search-item label="授课讲师">
<el-input v-model="pageForm.lecturerName" clearable placeholder="请输入授课讲师" style="width: 100%" @keyup.enter.native="doSearch"></el-input>
</search-item>
<search-item label="课程状态">
<el-select v-model="pageForm.status" clearable placeholder="请选择课程状态" style="width: 100%">
<el-option v-for="item in statusOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
</el-select>
</search-item>
<search-item label="推荐标识">
<el-select v-model="pageForm.recommendFlag" clearable placeholder="请选择推荐标识" style="width: 100%">
<el-option v-for="item in recommendOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
</el-select>
</search-item>
<div class="search-query">
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
<el-button size="medium" @click="resetSearch">重置</el-button>
</div>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool :app="this" label="课程列表">
<el-button @click="openAdd" size="medium" type="primary">
<i class="el-icon-plus"></i>
新增课程
</el-button>
</table-tool>
<el-table
v-loading="tableLoading"
:data="tableData"
:size="tableSize"
border
@sort-change="pageOrder">
<el-table-column :index="indexMethod" align="center" header-align="center" label="序号" type="index" width="80"></el-table-column>
<el-table-column align="center" header-align="center" label="课程名称" prop="courseName" sortable="custom" show-overflow-tooltip min-width="180"></el-table-column>
<el-table-column align="center" header-align="center" label="课程类型" prop="courseTypeName" sortable="custom" show-overflow-tooltip width="140"></el-table-column>
<el-table-column align="center" header-align="center" label="授课讲师" prop="lecturerName" sortable="custom" show-overflow-tooltip width="140"></el-table-column>
<el-table-column align="center" header-align="center" label="开课时间" prop="startTime" sortable="custom" width="230">
<template slot-scope="{row}">
<span v-if="row.openType === 'long_term'">长期开放</span>
<span v-else>{{formatTime(row.startTime)}} 至 {{formatTime(row.endTime)}}</span>
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="课程状态" prop="status" sortable="custom" width="120">
<template slot-scope="{row}">
<el-tag :type="getStatusType(row.status)" size="mini">{{getStatusName(row.status)}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="推荐标识" prop="recommendFlags" sortable="custom" show-overflow-tooltip min-width="140">
<template slot-scope="{row}">
<el-tag
v-for="code in splitValue(row.recommendFlags)"
:key="code"
:type="getRecommendType(code)"
size="mini"
style="margin: 2px 3px">
{{getDictName(code, recommendOptions)}}
</el-tag>
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="观看次数" prop="viewCount" sortable="custom" width="120"></el-table-column>
<el-table-column align="center" header-align="center" label="排序编码" prop="sortNum" sortable="custom" width="120"></el-table-column>
<el-table-column align="center" header-align="center" label="操作" width="260">
<template slot-scope="{row}">
<el-button @click="openEdit(row)" size="mini" type="primary">编辑</el-button>
<el-button @click="goChapterContent(row)" size="mini" type="success">章节内容</el-button>
<el-button @click="doDelete(row)" size="mini" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</guava>
<el-dialog :title="title" :visible.sync="dialogVisible" :close-on-click-modal="false" width="72%">
<el-form :model="formData" ref="courseForm" :rules="formRules" label-width="110px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="课程名称" prop="courseName">
<el-input v-model="formData.courseName" maxlength="100" placeholder="请输入课程名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="课程类型" prop="courseTypeId">
<el-select v-model="formData.courseTypeId" filterable placeholder="请选择课程类型" style="width: 100%">
<el-option v-for="item in courseTypeOptions" :key="item.id" :label="item.typeName" :value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="授课讲师" prop="lecturerName">
<el-input v-model="formData.lecturerName" maxlength="100" placeholder="请输入教师姓名"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="课程状态" prop="status">
<el-select v-model="formData.status" placeholder="请选择课程状态" style="width: 100%">
<el-option v-for="item in statusOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="开课方式" prop="openType">
<el-radio-group v-model="formData.openType">
<el-radio label="fixed">固定开课时间</el-radio>
<el-radio label="long_term">长期开放</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="开课时间" prop="openTime" v-if="formData.openType !== 'long_term'">
<el-date-picker
v-model="formData.openTime"
type="datetimerange"
start-placeholder="开始时间"
end-placeholder="结束时间"
range-separator="至"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100%">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="推荐标识" prop="recommendFlagList">
<el-select v-model="formData.recommendFlagList" multiple clearable placeholder="请选择推荐标识" style="width: 100%">
<el-option v-for="item in recommendOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="排序编码" prop="sortNum">
<el-input-number v-model="formData.sortNum" :controls="false" :min="0" :precision="0" style="width: 100%" placeholder="请输入排序编码"></el-input-number>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="观看次数" prop="viewCount">
<el-input-number v-model="formData.viewCount" :controls="false" :min="0" :precision="0" style="width: 100%" placeholder="请输入观看次数"></el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="课程介绍" prop="courseIntro">
<el-input v-model="formData.courseIntro" type="textarea" maxlength="2000" :rows="4" show-word-limit placeholder="请输入课程简介"></el-input>
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="适合人群" prop="suitablePeople">
<el-input v-model="formData.suitablePeople" type="textarea" maxlength="500" :rows="3" show-word-limit placeholder="请输入适合人群"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学习目标" prop="learningGoal">
<el-input v-model="formData.learningGoal" type="textarea" maxlength="500" :rows="3" show-word-limit placeholder="请输入学习目标"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="课程封面" prop="cover">
<file-upload
:upload_number="1"
:upload_size="5242880"
:value.sync="formData.cover"
accept=".jpg,.jpeg,.png"
complete_result
upload_mode="image"
upload_result_category="interval">
</file-upload>
<div class="el-upload__tip">只能上传1个文件;只能上传.jpg,.jpeg,.png文件;单个文件大小不能超过5M。</div>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :disabled="subDis" @click="operation">确定</el-button>
</span>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
title: "",
dialogVisible: false,
subDis: false,
courseTypeOptions: [],
recommendOptions: [],
statusOptions: [
{ name: "草稿", code: "draft" },
{ name: "待发布", code: "pending" },
{ name: "已发布", code: "published" },
{ name: "已下架", code: "offline" }
],
formData: {},
formRules: {
courseName: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
courseTypeId: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
lecturerName: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
status: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
sortNum: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
viewCount: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
cover: [{ required: true, message: "请上传课程封面", trigger: ["blur", "change"] }]
}
}
},
methods: {
buildPageParams() {
const params = Object.assign({}, this.pageForm)
params.startTime = params.courseTime && params.courseTime.length ? params.courseTime[0] : ""
params.endTime = params.courseTime && params.courseTime.length ? params.courseTime[1] : ""
return params
},
pageData(data = null) {
const address = this.pageDataUrl ? this.pageDataUrl : loc() + "/pageData"
this.tableLoading = true
this.$axios.post(address, data ? data : this.buildPageParams()).then((res) => {
if (res.code === 0) {
this.tableData = res.data.list
this.pageForm.totalCount = res.data.totalCount
}
}).finally(() => {
this.tableLoading = false
})
},
resetSearch() {
this.pageForm.courseName = ""
this.pageForm.courseTypeId = ""
this.pageForm.courseTime = []
this.pageForm.lecturerName = ""
this.pageForm.status = ""
this.pageForm.recommendFlag = ""
this.doSearch()
},
openAdd() {
this.title = "新增课程"
this.formData = {
courseName: "",
courseTypeId: "",
lecturerName: "",
courseIntro: "",
suitablePeople: "",
learningGoal: "",
openType: "fixed",
openTime: [],
status: "draft",
recommendFlagList: [],
sortNum: 0,
viewCount: 0,
cover: ""
}
this.dialogVisible = true
this.$nextTick(() => {
this.$refs.courseForm && this.$refs.courseForm.clearValidate()
})
},
openEdit(row) {
this.title = "编辑课程"
this.formData = Object.assign({}, row, {
openTime: row.startTime && row.endTime ? [this.formatTime(row.startTime), this.formatTime(row.endTime)] : [],
recommendFlagList: this.splitValue(row.recommendFlags),
viewCount: row.viewCount == null ? 0 : row.viewCount
})
this.dialogVisible = true
this.$nextTick(() => {
this.$refs.courseForm && this.$refs.courseForm.clearValidate()
})
},
goChapterContent(row) {
const url = "/platform/learning/chapter/content?courseId=" + encodeURIComponent(row.id || "")
if (window.commonUtil && commonUtil.pjaxPush) {
commonUtil.pjaxPush(url)
} else {
window.location.href = url
}
},
operation() {
this.$refs.courseForm.validate((valid) => {
if (!valid) return
if (this.formData.openType !== "long_term" && (!this.formData.openTime || this.formData.openTime.length !== 2)) {
this.$message.warning("请选择开课时间")
return
}
const data = Object.assign({}, this.formData)
data.startTime = data.openType === "long_term" ? "" : data.openTime[0]
data.endTime = data.openType === "long_term" ? "" : data.openTime[1]
data.recommendFlags = (data.recommendFlagList || []).join(",")
delete data.openTime
delete data.recommendFlagList
const method = data.id ? "/doEdit" : "/doAdd"
this.subDis = true
this.$axios.post(loc() + method, data).then((resp) => {
if (resp.code === 0) {
this.dialogVisible = false
this.$message.success(resp.msg)
this.pageData()
} else {
this.$message.warning(resp.msg)
}
}).finally(() => {
this.subDis = false
})
})
},
doDelete(row) {
this.$confirm("确定要删除该课程吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post(loc() + "/doDelete", { id: row.id }).then((resp) => {
if (resp.code === 0) {
this.$message.success(resp.msg)
this.pageData()
} else {
this.$message.error(resp.msg)
}
})
})
},
getCourseTypes() {
return this.$axios.post(loc() + "/courseTypes").then((resp) => {
if (resp.code === 0) {
this.courseTypeOptions = resp.data
}
})
},
getLearningDict(name) {
return this.$axios.post(loc() + "/learningDictOptions", { name }).then((resp) => {
return resp.code === 0 ? resp.data : []
})
},
splitValue(value) {
return value ? value.split(",").filter(Boolean) : []
},
getStatusName(code) {
const item = this.statusOptions.find(v => v.code === code)
return item ? item.name : ""
},
getStatusType(code) {
const map = {
draft: "info",
pending: "warning",
published: "success",
offline: "danger"
}
return map[code] || ""
},
getDictName(code, options) {
const item = options.find(v => v.code === code)
return item ? item.name : code
},
getDictNames(value, options) {
const values = this.splitValue(value)
return values.map(code => {
const item = options.find(v => v.code === code)
return item ? item.name : code
}).join("、")
},
getRecommendType(code) {
const name = this.getDictName(code, this.recommendOptions)
if (name.includes("热门") || name.includes("推荐")) return "warning"
if (name.includes("新")) return "success"
return ""
},
formatTime(time) {
return time ? this.$moment(time).format("YYYY-MM-DD HH:mm:ss") : ""
}
},
created() {
this.pageForm.courseTime = []
Promise.all([this.getCourseTypes(), this.getLearningDict("推荐标识")]).then((values) => {
this.recommendOptions = values[1]
this.pageData()
})
}
})
</script>
<!--#
}
#-->