index.vue 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355
  1. <template>
  2. <div id="courseManagement">
  3. <search-box :formList="formList" @search="search" @init="init" />
  4. <table-list
  5. :tableSets="tableSet"
  6. :tableData="tableData"
  7. :navText="navText"
  8. @addClick="addClick"
  9. :loading="loading"
  10. >
  11. <template slot="btn" slot-scope="props">
  12. <el-button type="text" @click="infoMessage(props.scope.row)"
  13. >详情</el-button
  14. >
  15. <el-button type="text" @click="modify(props.scope.row)">修改</el-button>
  16. <el-button type="text" @click="del(props.scope.row)">删除</el-button>
  17. </template>
  18. </table-list>
  19. <pagination
  20. :total="total"
  21. :pageSize="pageSize"
  22. :currentPage="currentPage"
  23. @handleSizeChange="handleSizeChange"
  24. @handleCurrentChange="handleCurrentChange"
  25. />
  26. <el-dialog
  27. :visible.sync="dialogBox"
  28. width="920px"
  29. :show-close="false"
  30. :destroy-on-close="true"
  31. :fullscreen="fullscreen"
  32. :close-on-click-modal="false"
  33. >
  34. <div slot="title" class="hearders">
  35. <div class="leftTitle">
  36. {{ statusPop === 1 ? "添加" : statusPop === 0 ? "修改" : "详情" }}
  37. </div>
  38. <div class="rightBoxs">
  39. <img
  40. src="@/assets/images/Max@2x.png"
  41. alt=""
  42. @click="fullscreen = !fullscreen"
  43. />
  44. <img src="@/assets/images/Close@2x.png" alt="" @click="closeBefore" />
  45. </div>
  46. </div>
  47. <el-row class="contentBox" :span="24">
  48. <el-col :span="24" class="juscon">
  49. <el-steps :active="activeTableStatus">
  50. <el-step title="课程信息"></el-step>
  51. <el-step title="章节信息"></el-step>
  52. </el-steps>
  53. </el-col>
  54. <el-col :span="24" v-show="activeTableStatus === 1">
  55. <el-col :span="12">
  56. <el-col :span="24">
  57. <header>课程分类</header>
  58. <el-cascader
  59. style="width: 100%"
  60. v-model="poppleData.categoryId"
  61. :options="optionsTion"
  62. :size="'medium'"
  63. clearable
  64. :disabled="statusPop === 2"
  65. :props="{
  66. label: 'categoryName',
  67. value: 'categoryId',
  68. checkStrictly: true,
  69. emitPath: false,
  70. }"
  71. ></el-cascader>
  72. </el-col>
  73. <el-col :span="24">
  74. <header>课程名称:</header>
  75. <el-input
  76. :disabled="statusPop === 2"
  77. placeholder="请输入课程名称:"
  78. v-model="poppleData.courseName"
  79. />
  80. </el-col>
  81. <el-col :span="24">
  82. <header>主讲名师</header>
  83. <el-select
  84. :disabled="statusPop === 2"
  85. style="width: 100%"
  86. multiple
  87. v-model="poppleData.teacherIds"
  88. placeholder="请选择主讲名师:"
  89. @change="changeTeacherlist"
  90. >
  91. <el-option
  92. v-for="item in optionsTeach"
  93. :key="item.teacherId"
  94. :label="item.teacherName"
  95. :value="item.teacherId"
  96. >
  97. </el-option>
  98. </el-select>
  99. </el-col>
  100. <el-col :span="24">
  101. <header>价格:</header>
  102. <el-input
  103. placeholder="请输入价格:"
  104. :disabled="statusPop === 2"
  105. v-model="poppleData.price"
  106. >
  107. <template slot="append">元</template>
  108. </el-input>
  109. </el-col>
  110. <el-col :span="24">
  111. <header>排序:</header>
  112. <el-input-number
  113. :disabled="statusPop === 2"
  114. v-model="poppleData.sort"
  115. controls-position="right"
  116. :min="0"
  117. placeholder="请输入排序"
  118. ></el-input-number>
  119. </el-col>
  120. </el-col>
  121. <el-col :span="12">
  122. <el-col :span="24">
  123. <header>课程封面:</header>
  124. <div class="imgBox">
  125. <label class="imgLabel" for="inputs">
  126. <div class="msPhoto" v-if="poppleData.coverUrl">
  127. <img
  128. :src="$methodsTools.splitImgHost(poppleData.coverUrl)"
  129. alt="图片加载失败"
  130. />
  131. </div>
  132. <div class="imgbbx" v-else>
  133. <p style="margin-top: 49px">
  134. 点击添加或将文件拖拽到这里上传
  135. </p>
  136. <i class="el-icon-plus"></i>
  137. <p style="margin-bottom: 37px">
  138. 图片格式:.jpg/.png/jpeg/bmp
  139. </p>
  140. </div>
  141. <input
  142. :disabled="statusPop === 2"
  143. id="inputs"
  144. type="file"
  145. ref="file"
  146. style="display: none"
  147. @change="getImgFile"
  148. />
  149. </label>
  150. <p style="color: #999999">请上传比例为16:9且小于2m的图片</p>
  151. </div>
  152. </el-col>
  153. <el-col :span="24">
  154. <header>是否启用:</header>
  155. <el-radio-group
  156. v-model="poppleData.status"
  157. :disabled="statusPop === 2"
  158. >
  159. <el-radio :label="1">是</el-radio>
  160. <el-radio :label="0">否</el-radio>
  161. </el-radio-group>
  162. </el-col>
  163. </el-col>
  164. <el-col :span="24">
  165. <el-col :span="24">
  166. <header>课程简介:</header>
  167. <div
  168. style="
  169. max-height: 300px;
  170. overflow-y: auto;
  171. border: 1px solid #e2e2e2;
  172. border-radius: 8px;
  173. padding: 13px;
  174. "
  175. v-if="statusPop === 2"
  176. v-html="poppleData.introduction"
  177. ></div>
  178. <editor
  179. v-else
  180. v-model="poppleData.introduction"
  181. :min-height="100"
  182. :max-height="300"
  183. :uploadStatus="uploadStatus"
  184. /> </el-col
  185. ></el-col>
  186. </el-col>
  187. <el-col :span="24" v-show="activeTableStatus === 2">
  188. <div>
  189. <el-button type="primary" @click="changeList({ level: 0 }, 0)"
  190. >添加章节</el-button
  191. >
  192. <el-button>导入章节</el-button>
  193. </div>
  194. <div>
  195. <el-table
  196. ref="pager"
  197. :data="tableDatas"
  198. row-key="id"
  199. :load="load"
  200. lazy
  201. :show-overflow-tooltip="true"
  202. @expand-change="expandChange"
  203. :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
  204. >
  205. <el-table-column type="index" width="50" label="序号">
  206. </el-table-column>
  207. <el-table-column
  208. v-for="(item, index) in poppleTableHeader"
  209. :key="index"
  210. :label="item.label"
  211. :width="item.width"
  212. align="center"
  213. >
  214. <template slot-scope="scope">
  215. <span v-if="item.scope === 'time'">{{
  216. scope.row["level"] === 0
  217. ? ""
  218. : numTime(scope.row[item.prop])
  219. }}</span>
  220. <span v-else-if="item.scope === 'level'">{{
  221. scope.row[item.prop] === 0 ? "章" : "节"
  222. }}</span>
  223. <span v-else>{{ scope.row[item.prop] }}</span>
  224. </template>
  225. </el-table-column>
  226. <el-table-column
  227. label="操作"
  228. align="center"
  229. fixed="right"
  230. width="180px"
  231. >
  232. <template slot-scope="scope">
  233. <div v-if="scope.row.level === 0">
  234. <el-button type="text" @click="changeList(scope.row, 0)"
  235. >添加下级</el-button
  236. >
  237. <el-button type="text" @click="changeList(scope.row, 1)"
  238. >修改</el-button
  239. >
  240. <el-button type="text" @click="delF(scope.row)"
  241. >删除</el-button
  242. >
  243. </div>
  244. <div v-else>
  245. <el-button
  246. type="text"
  247. @click="innerVisibleVideoFun(scope.row)"
  248. >视频预览</el-button
  249. >
  250. <el-button type="text" @click="changeList(scope.row, 1)"
  251. >修改</el-button
  252. >
  253. <el-button type="text" @click="delFChild(scope.row)"
  254. >删除</el-button
  255. >
  256. </div>
  257. </template></el-table-column
  258. >
  259. </el-table>
  260. <el-dialog
  261. width="520px"
  262. :visible.sync="innerVisibleVideo"
  263. append-to-body
  264. :show-close="false"
  265. :destroy-on-close="true"
  266. >
  267. <div slot="title" class="hearders">
  268. <div class="leftTitle">视频播放器</div>
  269. <div class="rightBoxs">
  270. <img
  271. src="@/assets/images/Close@2x.png"
  272. alt=""
  273. @click="closeVideoPop"
  274. />
  275. </div>
  276. </div>
  277. <player-video :videoDatas="videoDatas"></player-video>
  278. <span slot="footer" class="dialog-footer">
  279. <el-button @click="closeVideoPop">取 消</el-button>
  280. </span>
  281. </el-dialog>
  282. <el-dialog
  283. width="520px"
  284. :visible.sync="innerVisible"
  285. append-to-body
  286. :show-close="false"
  287. :fullscreen="fullscreenChild"
  288. :close-on-click-modal="false"
  289. >
  290. <div slot="title" class="hearders">
  291. <div class="leftTitle">
  292. {{ int === 0 ? "添加" : "修改" }}
  293. </div>
  294. <div class="rightBoxs">
  295. <img
  296. src="@/assets/images/Max@2x.png"
  297. alt=""
  298. @click="fullscreenChild = !fullscreenChild"
  299. />
  300. <img
  301. src="@/assets/images/Close@2x.png"
  302. alt=""
  303. @click="closeBeforeChild"
  304. />
  305. </div>
  306. </div>
  307. <el-col :span="24">
  308. <el-col :span="18" style="margin: 30px 0px 10px">
  309. <el-form label-width="150px">
  310. <el-form-item
  311. v-for="(items, indexs) in formTables"
  312. :key="indexs"
  313. :label="items.label"
  314. >
  315. <el-input
  316. v-if="items.scope === 'dis'"
  317. v-model="formTableChild[items.prop]"
  318. :placeholder="items.placeholder"
  319. :disabled="formTableChild.level === 0 ? true : false"
  320. ></el-input>
  321. <el-radio-group
  322. v-else-if="items.scope === 'radio'"
  323. v-model="formTableChild.status"
  324. >
  325. <el-radio :label="1">是</el-radio>
  326. <el-radio :label="0">否</el-radio>
  327. </el-radio-group>
  328. <el-input
  329. v-else
  330. v-model="formTableChild[items.prop]"
  331. :placeholder="items.placeholder"
  332. ></el-input>
  333. </el-form-item>
  334. </el-form>
  335. </el-col>
  336. </el-col>
  337. <span slot="footer" class="dialog-footer">
  338. <el-button @click="closeBeforeChild">取 消</el-button>
  339. <el-button type="primary" @click="submitChild">确 定</el-button>
  340. </span>
  341. </el-dialog>
  342. </div>
  343. <pagination
  344. :total="totalChild"
  345. :pageSize="pageSizeChild"
  346. :currentPage="currentPageChild"
  347. @handleSizeChange="handleSizeChangeChild"
  348. @handleCurrentChange="handleCurrentChangeChild"
  349. />
  350. </el-col>
  351. </el-row>
  352. <div slot="footer" class="dialog-footer">
  353. <el-button @click="backTable" v-if="activeTableStatus !== 1"
  354. >上一步</el-button
  355. >
  356. <el-button @click="closeBefore">取 消</el-button>
  357. <el-button v-if="statusPop === 1" @click="submitAddTable" type="primary"
  358. >提交</el-button
  359. >
  360. <el-button
  361. v-else
  362. type="primary"
  363. @click="activeTableStatus === 1 ? nextTable() : submitTabel()"
  364. >{{ activeTableStatus === 1 ? "下一步" : "确定" }}</el-button
  365. >
  366. </div>
  367. </el-dialog>
  368. </div>
  369. </template>
  370. <script>
  371. import playerVideo from "@/components/player";
  372. import searchBox from "@/components/searchBox";
  373. import tableList from "@/components/tableList";
  374. import pagination from "@/components/pagination";
  375. import Editor from "@/components/Editor";
  376. export default {
  377. components: { playerVideo, searchBox, tableList, pagination, Editor },
  378. data() {
  379. return {
  380. uploadStatus: 2, //富文本组件传值
  381. loading: false, //当前表单加载是否加载动画
  382. navText: {
  383. title: "课程合计",
  384. index: 0,
  385. ch: "个",
  386. num: true,
  387. choice: true,
  388. backFatherBtn: {
  389. status: false,
  390. title: "未定义",
  391. },
  392. },
  393. formTableChild: {},
  394. formTables: [
  395. {
  396. label: "名称",
  397. prop: "name",
  398. placeholder: "输入名称",
  399. },
  400. // {
  401. // label: "视频地址",
  402. // prop: "videoUrl",
  403. // placeholder: "输入视频地址",
  404. // scope: "dis",
  405. // },
  406. // {
  407. // label: "时长",
  408. // prop: "videoTime",
  409. // placeholder: "输入时长",
  410. // scope: "dis",
  411. // },
  412. {
  413. label: "视频ID",
  414. prop: "vid",
  415. placeholder: "输入视频ID",
  416. scope: "dis",
  417. },
  418. {
  419. label: "排序",
  420. prop: "sort",
  421. placeholder: "输入排序",
  422. },
  423. {
  424. label: "是否启用",
  425. prop: "status",
  426. scope: "radio",
  427. },
  428. ],
  429. tableSet: [
  430. {
  431. label: "课程名称",
  432. prop: "courseName",
  433. hidden: true,
  434. width: "180px",
  435. },
  436. {
  437. label: "课程类型",
  438. prop: "categoryName",
  439. hidden: true,
  440. width: "180px",
  441. },
  442. {
  443. label: "章节数",
  444. prop1: "chapterNum",
  445. prop2: "sectionNum",
  446. hidden: true,
  447. scope: "numberAll",
  448. },
  449. {
  450. label: "视频时长(小时)",
  451. prop: "duration",
  452. hidden: true,
  453. scope: "time",
  454. width: "200px",
  455. },
  456. {
  457. label: "排序",
  458. prop: "sort",
  459. hidden: true,
  460. },
  461. {
  462. label: "启用状态",
  463. prop: "status",
  464. hidden: true,
  465. scope: "status",
  466. },
  467. ], //表头信息
  468. tableData: [], //表单数据
  469. total: 0, //一共多少条
  470. pageSize: 10, //每页多少条数据
  471. currentPage: 1, //当前页码
  472. totalChild: 0, //一共多少条
  473. pageSizeChild: 5, //每页多少条数据
  474. currentPageChild: 1, //当前页码
  475. formList: [
  476. {
  477. label: "课程名称",
  478. prop: "courseName",
  479. placeholder: "输入课程名称",
  480. },
  481. {
  482. label: "启用状态",
  483. prop: "status",
  484. scope: "select",
  485. placeholder: "选择启用状态",
  486. options: [
  487. {
  488. label: "启用",
  489. value: 1,
  490. },
  491. {
  492. label: "关闭",
  493. value: 0,
  494. },
  495. ],
  496. },
  497. {
  498. label: "课程类型",
  499. prop: "categoryId",
  500. scope: "cascader",
  501. placeholder: "选择课程类型",
  502. options: [],
  503. props: {
  504. label: "categoryName",
  505. value: "categoryId",
  506. },
  507. },
  508. ], //搜索栏
  509. optionsTion: [],
  510. optionsTeach: [],
  511. poppleData: {},
  512. fullscreen: false,
  513. fullscreenChild: false,
  514. dialogBox: false,
  515. innerVisible: false,
  516. innerVisibleVideo: false, //视频播放器窗口
  517. videoDatas: {}, //视频数据
  518. statusPop: 0,
  519. beif: "", //备份数据
  520. activeTableStatus: 1, //步骤条当前状态
  521. tableDatas: [], //表格数据
  522. poppleTableHeader: [
  523. {
  524. label: "名称",
  525. prop: "name",
  526. width: "150px",
  527. },
  528. {
  529. label: "级别",
  530. prop: "level",
  531. width: "80px",
  532. scope: "level",
  533. },
  534. {
  535. label: "视频地址",
  536. prop: "mp4",
  537. width: "300px",
  538. },
  539. {
  540. label: "时长(小时)",
  541. prop: "duration",
  542. // scope: "time",
  543. width: "120px",
  544. },
  545. {
  546. label: "排序",
  547. prop: "sort",
  548. },
  549. ], //表头数据
  550. int: -1,
  551. treeObj: {},
  552. priceTest: /(^[1-9]\d*(\.\d{1,2})?$)|(^0(\.\d{1,2})?$)/,
  553. };
  554. },
  555. computed: {
  556. numTime: function () {
  557. return function (res) {
  558. return Number(res / 3600).toFixed(2);
  559. };
  560. },
  561. },
  562. mounted() {
  563. this.search();
  564. this.initCascader();
  565. this.initTeacherList();
  566. },
  567. methods: {
  568. //视频播放器窗口
  569. innerVisibleVideoFun(options) {
  570. this.videoDatas = options;
  571. this.innerVisibleVideo = true;
  572. },
  573. //视频窗口关闭
  574. closeVideoPop() {
  575. this.innerVisibleVideo = false;
  576. },
  577. changeTeacherlist(options) {
  578. console.log(this.poppleData);
  579. },
  580. // 初始化教师列表
  581. initTeacherList() {
  582. var data = {
  583. status: "0,1",
  584. };
  585. this.$api.inquireCourseTeacher(data).then((res) => {
  586. console.log(res);
  587. this.optionsTeach = res.rows;
  588. });
  589. },
  590. //级联选择器数据
  591. initCascader() {
  592. this.$api
  593. .inquireProfessionClassification()
  594. .then((res) => {
  595. this.arrayChangeType(res.rows);
  596. })
  597. .catch((err) => {
  598. console.log(err);
  599. });
  600. },
  601. // 接口数据转换级联类型
  602. arrayChangeType(options) {
  603. let result = [];
  604. if (!Array.isArray(options)) {
  605. return result;
  606. }
  607. options.forEach((item) => {
  608. delete item.children;
  609. });
  610. let map = {};
  611. options.forEach((item) => {
  612. map[item.categoryId] = item;
  613. });
  614. options.forEach((item) => {
  615. let parent = map[item.pid];
  616. if (parent) {
  617. (parent.children || (parent.children = [])).push(item);
  618. } else {
  619. result.push(item);
  620. }
  621. });
  622. result = result.sort(this.sortBy("sort", true));
  623. result.forEach((item, index) => {
  624. if (item.children) {
  625. item.children.sort(this.sortBy("sort", true));
  626. }
  627. });
  628. this.formList.forEach((item, index) => {
  629. if (item.prop === "categoryId") {
  630. item.options = result;
  631. this.optionsTion = result;
  632. }
  633. });
  634. return result;
  635. },
  636. sortBy(attr, rev) {
  637. //第二个参数没有传递 默认升序排列
  638. if (rev == undefined) {
  639. rev = 1;
  640. } else {
  641. rev = rev ? 1 : -1;
  642. }
  643. return function (a, b) {
  644. a = a[attr];
  645. b = b[attr];
  646. if (a < b) {
  647. return rev * -1;
  648. }
  649. if (a > b) {
  650. return rev * 1;
  651. }
  652. return 0;
  653. };
  654. },
  655. backTable() {
  656. if (this.statusPop === 1) {
  657. this.statusPop = 0;
  658. this.activeTableStatus--;
  659. } else {
  660. this.activeTableStatus--;
  661. }
  662. },
  663. nextTable() {
  664. this.activeTableStatus++;
  665. },
  666. getImgFile() {
  667. var self = this;
  668. var file = this.$refs.file.files[0];
  669. if (file === undefined) {
  670. self.$set(self.poppleData, "coverUrl", "");
  671. return;
  672. }
  673. if (file.size > 2 * 1024 * 1024) {
  674. this.$message.error("图片不得大于2MB");
  675. return;
  676. }
  677. var type = this.$refs.file.value.toLowerCase().split(".").splice(-1);
  678. if (
  679. type[0] != "jpg" &&
  680. type[0] != "png" &&
  681. type[0] != "jpeg" &&
  682. type[0] != "bmp"
  683. ) {
  684. this.$message.error("上传格式需为:.jpg/.png/.jpeg/bmp");
  685. this.$refs.file.value = "";
  686. return;
  687. }
  688. var reader = new FileReader();
  689. reader.readAsDataURL(file);
  690. reader.onload = function (ev) {
  691. self.$set(self.poppleData, "coverUrl", ev.target.result);
  692. };
  693. },
  694. // 详情
  695. infoMessage(options) {
  696. var data = options.courseId;
  697. this.$api.obtainCourse(data).then((res) => {
  698. this.poppleData = res.data;
  699. this.beif = res.data.coverUrl;
  700. this.poppleData.teacherIds = res.data.teacherIds.split(",").map(Number);
  701. this.getTables(data);
  702. });
  703. this.statusPop = 2;
  704. this.activeTableStatus = 1;
  705. this.dialogBox = true;
  706. },
  707. //确定提交
  708. async submitTabel() {
  709. var self = this;
  710. if (
  711. this.poppleData.categoryId === undefined ||
  712. this.poppleData.categoryId === null
  713. ) {
  714. this.$message.error("请选择课程分类");
  715. return;
  716. }
  717. if (!this.poppleData.courseName) {
  718. this.$message.error("请输入课程名称");
  719. return;
  720. }
  721. if (
  722. this.poppleData.teacherIds === undefined ||
  723. this.poppleData.teacherIds.length === 0
  724. ) {
  725. this.$message.error("请选择主讲名师");
  726. return;
  727. }
  728. if (this.poppleData.price === undefined) {
  729. this.$message.error("请输入价格");
  730. return;
  731. }
  732. if (this.poppleData.sort === undefined) {
  733. this.$message.error("请输入排序");
  734. return;
  735. }
  736. if (!this.priceTest.test(this.poppleData.price)) {
  737. this.$message.error("请输入正确价格");
  738. this.poppleData.price = "";
  739. return;
  740. }
  741. if (!this.poppleData.introduction) {
  742. this.$message.error("请输入课程简介");
  743. return;
  744. }
  745. if (this.poppleData.status === undefined) {
  746. this.$message.error("请选择是否启用该课程");
  747. return;
  748. }
  749. if (!this.poppleData.coverUrl || this.poppleData.coverUrl.length === 0) {
  750. this.$message.error("请上传课程封面");
  751. return;
  752. }
  753. var data = {
  754. categoryId: this.poppleData.categoryId,
  755. courseName: this.poppleData.courseName,
  756. duration: 0,
  757. introduction: this.poppleData.introduction,
  758. price: this.poppleData.price,
  759. sort: this.poppleData.sort,
  760. status: this.poppleData.status,
  761. teacherIds: this.poppleData.teacherIds.join(","),
  762. };
  763. if (this.statusPop === 1) {
  764. this.search();
  765. this.dialogBox = false;
  766. } else if (this.statusPop === 0) {
  767. data.courseId = this.poppleData.courseId;
  768. if (
  769. this.poppleData.coverUrl === this.beif &&
  770. this.poppleData.coverUrl.length !== 0
  771. ) {
  772. this.submitFun(data);
  773. } else {
  774. const awtimg = await this.imgUpload(2);
  775. data.coverUrl = this.poppleData.coverUrl;
  776. this.submitFun(data);
  777. }
  778. } else if (this.statusPop === 2) {
  779. this.dialogBox = false;
  780. }
  781. },
  782. submitFun(data) {
  783. this.$api
  784. .editCourse(data)
  785. .then((res) => {
  786. console.log(data);
  787. this.$message.success("修改成功!");
  788. this.dialogBox = false;
  789. this.search();
  790. })
  791. .catch((err) => {
  792. this.$message.error(err);
  793. });
  794. },
  795. imgUpload(int) {
  796. var self = this;
  797. return new Promise((resolve, reject) => {
  798. this.$upload
  799. .upload(this.$refs.file.files[0], int)
  800. .then((res) => {
  801. self.poppleData.coverUrl = res;
  802. resolve();
  803. })
  804. .catch((err) => {
  805. self.$message.error("图片上传错误");
  806. });
  807. });
  808. },
  809. //添加
  810. addClick() {
  811. this.poppleData = {};
  812. this.tableDatas = [];
  813. this.statusPop = 1;
  814. this.activeTableStatus = 1;
  815. this.dialogBox = true;
  816. },
  817. // 添加表单执行提交
  818. async submitAddTable() {
  819. var self = this;
  820. if (this.poppleData.categoryId === undefined) {
  821. this.$message.error("请选择课程分类");
  822. return;
  823. }
  824. if (this.poppleData.courseName === undefined) {
  825. this.$message.error("请输入课程名称");
  826. return;
  827. }
  828. if (this.poppleData.teacherIds === undefined) {
  829. this.$message.error("请选择主讲名师");
  830. return;
  831. }
  832. if (this.poppleData.price === undefined) {
  833. this.$message.error("请输入价格");
  834. return;
  835. }
  836. if (this.poppleData.sort === undefined) {
  837. this.$message.error("请输入排序");
  838. return;
  839. }
  840. if (!this.priceTest.test(this.poppleData.price)) {
  841. this.$message.error("请输入正确价格");
  842. this.poppleData.price = "";
  843. return;
  844. }
  845. if (this.poppleData.introduction === undefined) {
  846. this.$message.error("请输入课程简介");
  847. return;
  848. }
  849. if (this.poppleData.status === undefined) {
  850. this.$message.error("请选择是否启用该课程");
  851. return;
  852. }
  853. if (!this.poppleData.coverUrl || this.poppleData.coverUrl.length === 0) {
  854. this.$message.error("请上传课程封面");
  855. return;
  856. }
  857. var data = {
  858. categoryId: this.poppleData.categoryId,
  859. courseName: this.poppleData.courseName,
  860. duration: 0,
  861. sort: this.poppleData.sort,
  862. introduction: this.poppleData.introduction,
  863. price: this.poppleData.price,
  864. status: this.poppleData.status,
  865. teacherIds: this.poppleData.teacherIds.join(","),
  866. };
  867. const awtimg = await this.imgUpload(2);
  868. data.coverUrl = this.poppleData.coverUrl;
  869. this.$api
  870. .addCourse(data)
  871. .then((res) => {
  872. this.poppleData.courseId = res.log.primary_key_id;
  873. this.$message.success("新增成功!");
  874. this.search();
  875. this.getTables(res.log.primary_key_id);
  876. this.nextTable();
  877. })
  878. .catch((err) => {
  879. this.$message.error(err);
  880. });
  881. },
  882. // 修改
  883. modify(options) {
  884. var data = options.courseId;
  885. this.$api.obtainCourse(data).then((res) => {
  886. this.poppleData = res.data;
  887. this.beif = res.data.coverUrl;
  888. this.poppleData.teacherIds = res.data.teacherIds.split(",").map(Number);
  889. this.getTables(data);
  890. });
  891. this.statusPop = 0;
  892. this.activeTableStatus = 1;
  893. this.dialogBox = true;
  894. },
  895. //获取大章
  896. getTables(index) {
  897. var self = this;
  898. var data = {
  899. courseId: this.poppleData.courseId,
  900. status: "0,1",
  901. pageSize: this.pageSizeChild,
  902. pageNum: this.currentPageChild,
  903. };
  904. var nullBox = [];
  905. this.$api.inquireCourseListchapter(data).then((res) => {
  906. res.rows.forEach((item) => {
  907. item.id = item.chapterId + "";
  908. item.level = 0;
  909. item.hasChildren = true;
  910. nullBox.push(item);
  911. });
  912. nullBox.sort(this.sortBy("sort", true));
  913. self.tableDatas = nullBox;
  914. self.totalChild = res.total;
  915. });
  916. },
  917. //删除
  918. del(options) {
  919. var self = this;
  920. this.$confirm("此操作将删除该课程, 是否继续?", "提示", {
  921. confirmButtonText: "确定",
  922. cancelButtonText: "取消",
  923. type: "warning",
  924. })
  925. .then(() => {
  926. var data = {
  927. categoryId: options.categoryId,
  928. courseId: options.courseId,
  929. courseName: options.courseName,
  930. teacherIds: options.teacherIds.join(","),
  931. status: -1,
  932. };
  933. this.$api
  934. .editCourse(data)
  935. .then((res) => {
  936. this.$message.success("删除成功!");
  937. this.search();
  938. })
  939. .catch((err) => {
  940. this.$message.error("删除失败:" + err);
  941. });
  942. })
  943. .catch(() => {});
  944. },
  945. sortBy(attr, rev) {
  946. //第二个参数没有传递 默认升序排列
  947. if (rev == undefined) {
  948. rev = 1;
  949. } else {
  950. rev = rev ? 1 : -1;
  951. }
  952. return function (a, b) {
  953. a = a[attr];
  954. b = b[attr];
  955. if (a < b) {
  956. return rev * -1;
  957. }
  958. if (a > b) {
  959. return rev * 1;
  960. }
  961. return 0;
  962. };
  963. },
  964. search(v) {
  965. if (v === undefined) {
  966. v = {
  967. status: "0,1",
  968. pageSize: this.pageSize,
  969. pageNum: this.currentPage,
  970. };
  971. }
  972. var data = {
  973. courseName: v.courseName || "",
  974. categoryId: v.categoryId || "",
  975. status: v.status === undefined ? "0,1" : v.status,
  976. pageSize: this.pageSize,
  977. pageNum: this.currentPage,
  978. };
  979. this.loading = true;
  980. this.$api
  981. .inquireCourseList(data)
  982. .then((res) => {
  983. this.tableData = res.rows;
  984. this.total = res.total;
  985. this.navText.index = res.total;
  986. this.loading = false;
  987. })
  988. .catch((err) => {
  989. this.loading = false;
  990. });
  991. },
  992. init() {
  993. this.search();
  994. },
  995. //窗口关闭
  996. closeBefore() {
  997. this.dialogBox = false;
  998. this.fullscreen = false;
  999. this.poppleData = {};
  1000. },
  1001. closeBeforeChild() {
  1002. this.innerVisible = false;
  1003. this.fullscreenChild = false;
  1004. },
  1005. handleSizeChange(v) {
  1006. this.pageSize = v;
  1007. this.currentPage = 1;
  1008. this.search();
  1009. },
  1010. handleCurrentChange(v) {
  1011. this.currentPage = v;
  1012. this.search();
  1013. },
  1014. handleSizeChangeChild(v) {
  1015. this.pageSizeChild = v;
  1016. this.currentPageChild = 1;
  1017. this.getTables();
  1018. },
  1019. handleCurrentChangeChild(v) {
  1020. this.currentPageChild = v;
  1021. this.getTables();
  1022. },
  1023. //点击多级触发异步
  1024. async load(tree, treeNode, resolve) {
  1025. this.treeObj[tree.chapterId] = { tree, treeNode, resolve };
  1026. var data = {
  1027. status: "0,1",
  1028. chapterId: tree.chapterId,
  1029. };
  1030. const loadChildren = await this.$api.inquireCourseListsection(data);
  1031. if (Array.isArray(loadChildren.rows) && loadChildren.rows.length > 0) {
  1032. loadChildren.rows.forEach((item) => {
  1033. item.id = tree.chapterId + "." + item.sectionId;
  1034. item.level = 1;
  1035. });
  1036. loadChildren.rows.sort(this.sortBy("sort", true));
  1037. resolve(loadChildren.rows); // 返回最终数据对象
  1038. } else {
  1039. tree.children = [];
  1040. resolve([]);
  1041. }
  1042. },
  1043. changeTableList() {
  1044. var data = {
  1045. chapterId: this.formTableChild.chapterId,
  1046. sectionId: this.formTableChild.sectionId,
  1047. // videoTime: this.formTableChild.videoTime,
  1048. // videoUrl: this.formTableChild.videoUrl,
  1049. vid: this.formTableChild.vid,
  1050. name: this.formTableChild.name,
  1051. sort: this.formTableChild.sort,
  1052. status: this.formTableChild.status,
  1053. };
  1054. this.$api.editCoursesection(data).then((res) => {
  1055. this.$message.success("修改成功");
  1056. this.getTables(this.poppleData.courseId);
  1057. // 处理懒加载节点,先清空再渲染。
  1058. // 利用refs得到表格的数据结构,拿到 lazyTreeNodeMap 这个懒加载对象。
  1059. // 这里面存储的就是你之前加载的所有节点数据对象了。然后清空操作节点的父节点对象。
  1060. this.$refs.pager.store.states.lazyTreeNodeMap[
  1061. this.formTableChild.chapterId
  1062. ] = [];
  1063. // 根据我们声明的maps对象,拿到父节点。然后手动进行接口请求,重新进行数据渲染。
  1064. const { tree, treeNode, resolve } =
  1065. this.treeObj[this.formTableChild.chapterId];
  1066. this.load(tree, treeNode, resolve);
  1067. this.innerVisible = false;
  1068. });
  1069. },
  1070. //添加or修改章节or子节
  1071. changeList(row, int) {
  1072. this.int = int;
  1073. var kelone = JSON.stringify(row);
  1074. this.formTableChild = JSON.parse(kelone);
  1075. if (int === 0 && row.courseId !== undefined) {
  1076. this.formTableChild = {
  1077. level: 1,
  1078. courseId: row.courseId,
  1079. chapterId: row.chapterId,
  1080. };
  1081. }
  1082. this.innerVisible = true;
  1083. },
  1084. expandChange() {},
  1085. submitChild() {
  1086. if (this.formTableChild.name === undefined) {
  1087. this.$message.error("请输入名字");
  1088. return;
  1089. }
  1090. if (this.formTableChild.sort === undefined) {
  1091. this.$message.error("请输入排序");
  1092. return;
  1093. }
  1094. if (this.formTableChild.status === undefined) {
  1095. this.$message.error("请选择是否启用");
  1096. return;
  1097. }
  1098. // 添加
  1099. if (this.int === 0) {
  1100. console.log(this.formTableChild);
  1101. if (this.formTableChild.chapterId) {
  1102. //添加下级
  1103. // if (this.formTableChild.videoUrl === undefined) {
  1104. // this.$message.error("请输入视频地址");
  1105. // return;
  1106. // }
  1107. if (this.formTableChild.vid === undefined) {
  1108. this.$message.error("请输入视频ID");
  1109. return;
  1110. }
  1111. var data = {
  1112. chapterId: this.formTableChild.chapterId,
  1113. name: this.formTableChild.name,
  1114. sort: this.formTableChild.sort,
  1115. status: this.formTableChild.status,
  1116. vid: this.formTableChild.vid,
  1117. // videoTime: this.formTableChild.videoTime,
  1118. // videoUrl: this.formTableChild.videoUrl,
  1119. };
  1120. this.$api.addCoursesection(data).then((res) => {
  1121. this.$message.success("添加下级成功");
  1122. this.getTables(this.poppleData.courseId);
  1123. if (
  1124. this.$refs.pager.store.states.lazyTreeNodeMap[
  1125. this.formTableChild.bankChapterId
  1126. ] === undefined
  1127. ) {
  1128. } else {
  1129. this.$refs.pager.store.states.lazyTreeNodeMap[
  1130. this.formTableChild.chapterId
  1131. ] = [];
  1132. // 根据我们声明的maps对象,拿到父节点。然后手动进行接口请求,重新进行数据渲染。
  1133. const { tree, treeNode, resolve } =
  1134. this.treeObj[this.formTableChild.chapterId];
  1135. this.load(tree, treeNode, resolve);
  1136. }
  1137. this.innerVisible = false;
  1138. });
  1139. } else {
  1140. // 添加章节
  1141. var data = {
  1142. courseId: this.poppleData.courseId,
  1143. name: this.formTableChild.name,
  1144. sort: this.formTableChild.sort,
  1145. status: this.formTableChild.status,
  1146. };
  1147. this.$api.addCoursechapter(data).then((res) => {
  1148. this.$message.success("添加成功");
  1149. this.getTables(this.poppleData.courseId);
  1150. this.innerVisible = false;
  1151. });
  1152. }
  1153. }
  1154. //修改
  1155. if (this.int === 1) {
  1156. if (this.formTableChild.sectionId === undefined) {
  1157. var data = {
  1158. chapterId: this.formTableChild.chapterId,
  1159. name: this.formTableChild.name,
  1160. sort: this.formTableChild.sort,
  1161. status: this.formTableChild.status,
  1162. };
  1163. this.$api.editCoursechapter(data).then((res) => {
  1164. this.$message.success("修改成功");
  1165. this.getTables(this.poppleData.courseId);
  1166. this.innerVisible = false;
  1167. });
  1168. } else {
  1169. this.changeTableList();
  1170. }
  1171. }
  1172. },
  1173. delF(row) {
  1174. this.$confirm("是否确认删除该章?", "提示", {
  1175. confirmButtonText: "确定",
  1176. cancelButtonText: "取消",
  1177. type: "warning",
  1178. })
  1179. .then(() => {
  1180. var data = {
  1181. chapterId: row.chapterId,
  1182. name: row.name,
  1183. sort: row.sort,
  1184. status: -1,
  1185. };
  1186. this.$api.editCoursechapter(data).then((res) => {
  1187. this.$message.success("删除成功");
  1188. this.getTables(this.poppleData.courseId);
  1189. this.innerVisible = false;
  1190. });
  1191. })
  1192. .catch(() => {
  1193. this.$message({
  1194. type: "info",
  1195. message: "已取消删除",
  1196. });
  1197. });
  1198. },
  1199. delFChild(rowsz) {
  1200. this.$confirm("是否确认删除该节?", "提示", {
  1201. confirmButtonText: "确定",
  1202. cancelButtonText: "取消",
  1203. type: "warning",
  1204. })
  1205. .then(() => {
  1206. var data = {
  1207. chapterId: rowsz.chapterId,
  1208. sectionId: rowsz.sectionId,
  1209. // videoTime: rowsz.videoTime,
  1210. // videoUrl: rowsz.videoUrl,
  1211. vid: rowsz.vid,
  1212. name: rowsz.name,
  1213. sort: rowsz.sort,
  1214. status: -1,
  1215. };
  1216. this.$api.editCoursesection(data).then((res) => {
  1217. this.$message.success("删除成功");
  1218. this.getTables(this.poppleData.courseId);
  1219. // 处理懒加载节点,先清空再渲染。
  1220. // 利用refs得到表格的数据结构,拿到 lazyTreeNodeMap 这个懒加载对象。
  1221. // 这里面存储的就是你之前加载的所有节点数据对象了。然后清空操作节点的父节点对象。
  1222. this.$refs.pager.store.states.lazyTreeNodeMap[rowsz.chapterId] = [];
  1223. // 根据我们声明的maps对象,拿到父节点。然后手动进行接口请求,重新进行数据渲染。
  1224. const { tree, treeNode, resolve } = this.treeObj[rowsz.chapterId];
  1225. this.load(tree, treeNode, resolve);
  1226. this.innerVisible = false;
  1227. });
  1228. })
  1229. .catch(() => {
  1230. this.$message({
  1231. type: "info",
  1232. message: "已取消删除",
  1233. });
  1234. });
  1235. },
  1236. },
  1237. };
  1238. </script>
  1239. <style lang="less" scoped>
  1240. /deep/.el-button {
  1241. border-radius: 8px;
  1242. }
  1243. /deep/.el-dialog {
  1244. border-radius: 8px;
  1245. .el-dialog__header {
  1246. padding: 0;
  1247. .hearders {
  1248. height: 40px;
  1249. display: flex;
  1250. align-items: center;
  1251. justify-content: space-between;
  1252. padding: 0px 18px 0px 20px;
  1253. border-bottom: 1px solid #e2e2e2;
  1254. .leftTitle {
  1255. font-size: 14px;
  1256. font-weight: bold;
  1257. color: #2f4378;
  1258. }
  1259. .rightBoxs {
  1260. display: flex;
  1261. align-items: center;
  1262. img {
  1263. width: 14px;
  1264. height: 14px;
  1265. margin-left: 13px;
  1266. cursor: pointer;
  1267. }
  1268. }
  1269. }
  1270. }
  1271. .el-dialog__body {
  1272. padding: 0;
  1273. .contentBox {
  1274. padding: 20px 20px 5px;
  1275. .el-col {
  1276. padding: 0px 20px;
  1277. margin-bottom: 30px;
  1278. header {
  1279. margin-bottom: 6px;
  1280. color: #2f4378;
  1281. font-size: 14px;
  1282. }
  1283. }
  1284. .juscon {
  1285. float: none;
  1286. width: 50%;
  1287. margin: 0 auto;
  1288. }
  1289. }
  1290. }
  1291. .el-dialog__footer {
  1292. padding: 0;
  1293. .dialog-footer {
  1294. padding: 0px 40px;
  1295. height: 70px;
  1296. border-top: 1px solid #e2e2e2;
  1297. display: flex;
  1298. align-items: center;
  1299. justify-content: flex-end;
  1300. }
  1301. }
  1302. }
  1303. .imgBox {
  1304. width: 100%;
  1305. // height: 210px;
  1306. border: 1px solid #e2e2e2;
  1307. border-radius: 8px;
  1308. padding: 8px 8px 3px;
  1309. display: flex;
  1310. flex-direction: column;
  1311. align-items: center;
  1312. .imgLabel {
  1313. flex: 1;
  1314. width: 100%;
  1315. border: 1px dotted #e2e2e2;
  1316. color: #999;
  1317. font-size: 14px;
  1318. cursor: pointer;
  1319. border-radius: 8px;
  1320. .msPhoto {
  1321. display: flex;
  1322. justify-content: center;
  1323. align-items: center;
  1324. max-width: 100%;
  1325. max-height: 270px;
  1326. img {
  1327. max-width: 100%;
  1328. max-height: 270px;
  1329. }
  1330. }
  1331. .imgbbx {
  1332. display: flex;
  1333. flex-direction: column;
  1334. align-items: center;
  1335. justify-content: center;
  1336. width: 100%;
  1337. height: 100%;
  1338. i {
  1339. font-weight: bold;
  1340. margin: 14px 0;
  1341. font-size: 24px;
  1342. }
  1343. }
  1344. }
  1345. p {
  1346. margin: 5px 0px;
  1347. }
  1348. }
  1349. </style>