vipList.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. <template>
  2. <div class="m-container">
  3. <h2>
  4. <div class="squrt"></div>VIP课列表
  5. </h2>
  6. <div class="newBand" v-permission="'/buildVip'" @click="gotoBuildVip">新建VIP课</div>
  7. <div class="newBand" v-permission="'export/vipGroupList'" @click="onVIPCourseExport">导出VIP课</div>
  8. <div
  9. class="newBand"
  10. v-permission="'export/vipGroup'"
  11. @click="onStudentExport"
  12. style="width: 120px;"
  13. >VIP课程续费提醒</div>
  14. <div class="m-core">
  15. <!-- 搜索类型 -->
  16. <el-form :inline="true" class="searchForm" v-model.trim="searchForm">
  17. <!-- 状态 指导老师 活动方案-->
  18. <el-form-item>
  19. <el-input
  20. v-model.trim="searchForm.search"
  21. @keyup.enter.native="search"
  22. placeholder="课程名称"
  23. ></el-input>
  24. </el-form-item>
  25. <el-form-item prop="orgin">
  26. <el-select
  27. class="multiple"
  28. v-model.trim="searchForm.orgin"
  29. filterable
  30. clearable
  31. placeholder="请选择分部"
  32. >
  33. <el-option
  34. v-for="(item,index) in organList"
  35. :key="index"
  36. :label="item.name"
  37. :value="item.id"
  38. ></el-option>
  39. </el-select>
  40. </el-form-item>
  41. <!-- statusList -->
  42. <el-form-item prop="status">
  43. <el-select
  44. class="multiple"
  45. v-model.trim="searchForm.status"
  46. filterable
  47. clearable
  48. placeholder="请选课程状态"
  49. >
  50. <el-option
  51. v-for="(item,index) in statusList"
  52. :key="index"
  53. :label="item.lable"
  54. :value="item.value"
  55. ></el-option>
  56. </el-select>
  57. </el-form-item>
  58. <el-form-item>
  59. <el-select v-model.trim="searchForm.teacherId" clearable filterable placeholder="指导老师">
  60. <el-option
  61. v-for="(item,index) in teacherList"
  62. :key="index"
  63. :value="item.id"
  64. :label="item.realName"
  65. ></el-option>
  66. </el-select>
  67. </el-form-item>
  68. <el-form-item>
  69. <el-select v-model.trim="searchForm.activityId" clearable filterable placeholder="活动方案">
  70. <el-option
  71. v-for="(item,index) in activeList"
  72. :key="index"
  73. :value="item.id"
  74. :label="item.name"
  75. ></el-option>
  76. </el-select>
  77. </el-form-item>
  78. <el-form-item prop="status">
  79. <el-select
  80. class="multiple"
  81. v-model.trim="searchForm.hasEducationalTeacherId"
  82. filterable
  83. clearable
  84. placeholder="是否有教务老师"
  85. >
  86. <el-option label="是" value="true"></el-option>
  87. <el-option label="否" value="false"></el-option>
  88. </el-select>
  89. </el-form-item>
  90. <el-form-item>
  91. <el-button @click="search" type="danger">搜索</el-button>
  92. <el-button @click="onReSet" type="primary">重置</el-button>
  93. </el-form-item>
  94. </el-form>
  95. <!-- 查询列表 -->
  96. <!-- tab -->
  97. <div class="tableWrap">
  98. <el-table
  99. style="width: 100%"
  100. :header-cell-style="{background:'#EDEEF0',color:'#444'}"
  101. :data="tableData"
  102. >
  103. <el-table-column align="center" prop="id" label="课程组编号"></el-table-column>
  104. <el-table-column align="center" prop="name" label="课程名称"></el-table-column>
  105. <el-table-column align="center" prop="status" label="课程状态">
  106. <template slot-scope="scope">
  107. <div>{{ scope.row.status | formatterStatus}}</div>
  108. </template>
  109. </el-table-column>
  110. <el-table-column align="center" prop="userName" label="指导老师"></el-table-column>
  111. <el-table-column align="center" prop="educationalTeacherName" label="教务老师"></el-table-column>
  112. <el-table-column align="center" prop="studentNum" label="班级人数"></el-table-column>
  113. <el-table-column align="center" label="课程单价">
  114. <template slot-scope="scope">
  115. <div>
  116. <p>线上:{{scope.row.onlineClassesUnitPrice}}</p>
  117. <p>线下:{{scope.row.offlineClassesUnitPrice}}</p>
  118. </div>
  119. </template>
  120. </el-table-column>
  121. <el-table-column align="center" prop="vipGroupActivityName" label="活动方案"></el-table-column>
  122. <el-table-column align="center" label="当前课次">
  123. <template slot-scope="scope">
  124. <div>
  125. <p>{{scope.row.currentClassTimes + '/' + scope.row.totalClassTimes}}</p>
  126. </div>
  127. </template>
  128. </el-table-column>
  129. <el-table-column align="center" label="月均消耗">
  130. <template slot-scope="scope">{{ scope.row.monthConsumeRate }}%</template>
  131. </el-table-column>
  132. <el-table-column align="center" prop="createTime" label="上次课时间">
  133. <template slot-scope="scope">
  134. <div>
  135. <p>{{scope.row.lastOverTime | formatterTime}}</p>
  136. </div>
  137. </template>
  138. </el-table-column>
  139. <el-table-column align="center" prop="paymentExpireDate" label="开课时间">
  140. <template slot-scope="scope">
  141. <div>
  142. <p>{{scope.row.courseStartDate | formatterTime}}</p>
  143. </div>
  144. </template>
  145. </el-table-column>
  146. <el-table-column align="center" prop="coursesExpireDate" label="结束时间">
  147. <template slot-scope="scope">
  148. <div>
  149. <p>{{scope.row.coursesExpireDate | formatterTime}}</p>
  150. </div>
  151. </template>
  152. </el-table-column>
  153. <el-table-column align="center" prop="createTime" label="申请时间">
  154. <template slot-scope="scope">
  155. <div>
  156. <p>{{scope.row.createTime | formatterTime}}</p>
  157. </div>
  158. </template>
  159. </el-table-column>
  160. <el-table-column align="center" width="150px" fixed="right" label="操作">
  161. <template slot-scope="scope">
  162. <div>
  163. <el-button
  164. type="text"
  165. v-permission="'/vipDetail'"
  166. @click="gotoVipDetail(scope.row)"
  167. >查看</el-button>
  168. <!--
  169. <el-button type="text">启动</el-button>-->
  170. <!-- <el-button type="text"
  171. v-if="scope.row.status <3"
  172. @click='closeVip(scope.row.id)'>关闭</el-button>-->
  173. <el-popover
  174. placement="top"
  175. width="160"
  176. v-permission="'vipGroupManage/stopVipGroup'"
  177. v-if="scope.row.status <3"
  178. :ref="scope.$index"
  179. >
  180. <p style="margin-bottom:10px;">确定停止该vip课?</p>
  181. <el-input v-model.trim="scope.row.stopReason" placeholder="请输入关闭原因"></el-input>
  182. <div style="text-align: right; margin-top: 20px">
  183. <el-button
  184. size="mini"
  185. type="text"
  186. @click="scope._self.$refs[scope.$index].doClose()"
  187. >取消</el-button>
  188. <el-button type="primary" size="mini" @click="closeVip(scope)">确定</el-button>
  189. </div>
  190. <el-button type="text" slot="reference">停止</el-button>
  191. </el-popover>
  192. <el-button
  193. type="text"
  194. v-if="scope.row.status > 1&&scope.row.status != 3"
  195. v-permission="'/vipReset'"
  196. @click="resetVip(scope.row)"
  197. >修改</el-button>
  198. </div>
  199. </template>
  200. </el-table-column>
  201. </el-table>
  202. <!-- 分页器 -->
  203. <pagination
  204. :total="rules.total"
  205. :page.sync="rules.page"
  206. :limit.sync="rules.limit"
  207. :page-sizes="rules.page_size"
  208. @pagination="getList"
  209. />
  210. </div>
  211. </div>
  212. </div>
  213. </template>
  214. <script>
  215. import pagination from "@/components/Pagination/index";
  216. import {
  217. getVipList,
  218. vipGroupActivity,
  219. closeVip,
  220. getVipGroupDetail
  221. } from "@/api/vipSeting";
  222. import { getTeacher, getEmployeeOrgan } from "@/api/buildTeam";
  223. import store from "@/store";
  224. import axios from "axios";
  225. import { getToken } from "@/utils/auth";
  226. import load from "@/utils/loading";
  227. export default {
  228. components: { pagination },
  229. name: "vipList",
  230. data() {
  231. return {
  232. organId: null,
  233. searchForm: {
  234. teacherId: null,
  235. activityId: null,
  236. search: null,
  237. orgin: null,
  238. status: null,
  239. hasEducationalTeacherId: null
  240. },
  241. teacherList: [],
  242. activeList: [],
  243. tableData: [],
  244. organList: [],
  245. rules: {
  246. // 分页规则
  247. limit: 10, // 限制显示条数
  248. page: 1, // 当前页
  249. total: 0, // 总条数
  250. page_size: [10, 20, 40, 50] // 选择限制显示条数
  251. },
  252. statusList: [
  253. { lable: "未开始", value: "0" },
  254. { lable: "报名中", value: "1" },
  255. { lable: "报名结束", value: "5" },
  256. { lable: "进行中", value: "2" },
  257. { lable: "已结束", value: "4" },
  258. { lable: "取消", value: "3" },
  259. { lable: "暂停", value: "6" }
  260. ]
  261. };
  262. },
  263. created() {
  264. if (this.$route.query.searchForm) {
  265. this.$route.query.searchForm instanceof Object
  266. ? (this.searchForm = this.$route.query.searchForm)
  267. : (this.searchForm = JSON.parse(this.$route.query.searchForm));
  268. }
  269. if (this.$route.query.rules) {
  270. this.$route.query.rules instanceof Object
  271. ? (this.rules = this.$route.query.rules)
  272. : (this.rules = JSON.parse(this.$route.query.rules));
  273. }
  274. },
  275. mounted() {
  276. this.init();
  277. },
  278. activated() {
  279. this.init();
  280. },
  281. methods: {
  282. init() {
  283. getEmployeeOrgan().then(res => {
  284. if (res.code == 200) {
  285. this.organList = res.data;
  286. }
  287. });
  288. // <!-- 状态 指导老师 活动方案-->
  289. getTeacher({ organId: this.organId }).then(res => {
  290. if (res.code == 200) {
  291. this.teacherList = res.data;
  292. }
  293. });
  294. // 获取活动方案
  295. vipGroupActivity({ organId: this.organId }).then(res => {
  296. if (res.code == 200) {
  297. this.activeList = res.data.rows;
  298. }
  299. });
  300. this.getList();
  301. },
  302. search() {
  303. this.rules.page = 1;
  304. this.getList();
  305. },
  306. onReSet() {
  307. this.searchForm = {
  308. teacherId: null,
  309. activityId: null,
  310. search: null,
  311. orgin: null,
  312. status: null
  313. };
  314. this.getList();
  315. },
  316. getList() {
  317. let params = this.searchForm;
  318. params.page = this.rules.page;
  319. params.rows = this.rules.limit;
  320. params.hasEducationalTeacherId =
  321. this.searchForm.hasEducationalTeacherId || null;
  322. params.organId = this.searchForm.orgin || null;
  323. params.status = this.searchForm.status || null;
  324. getVipList(params).then(res => {
  325. if (res.code == 200) {
  326. this.tableData = [];
  327. setTimeout(() => {
  328. this.tableData = res.data.rows;
  329. }, 50);
  330. this.rules.total = res.data.total;
  331. }
  332. });
  333. },
  334. // 跳转到vip详情
  335. gotoVipDetail(row) {
  336. let rules = JSON.stringify(this.rules);
  337. let searchForm = JSON.stringify(this.searchForm);
  338. let id = row.id;
  339. let name = row.name;
  340. this.$router.push({
  341. path: "/business/vipDetail",
  342. query: { id, name, rules, searchForm }
  343. });
  344. },
  345. closeVip(scope) {
  346. let id = scope.row.id;
  347. closeVip({ vipGroupId: id, stopReason: scope.row.stopReason }).then(
  348. res => {
  349. if (res.code == 200) {
  350. this.$message.success("停止课程成功");
  351. scope._self.$refs[scope.$index].doClose();
  352. this.getList();
  353. }
  354. }
  355. );
  356. },
  357. gotoBuildVip() {
  358. let rules = JSON.stringify(this.rules);
  359. let searchForm = JSON.stringify(this.searchForm);
  360. this.$router.push({
  361. path: "/business/buildVip",
  362. query: { rules, searchForm }
  363. });
  364. },
  365. onVIPCourseExport() {
  366. // 导出VIP课
  367. let searchForm = this.searchForm;
  368. let data = {
  369. teacherId: searchForm.teacherId || null,
  370. activityId: searchForm.activityId || null,
  371. organId: searchForm.orgin || null,
  372. status: searchForm.status || null,
  373. search: searchForm.search || null
  374. };
  375. let url = "/api-web/export/vipGroupList";
  376. const options = {
  377. method: "get",
  378. headers: {
  379. Authorization: getToken()
  380. },
  381. params: data,
  382. url,
  383. responseType: "blob"
  384. };
  385. this.$confirm("您确定导出VIP课吗?", "提示", {
  386. confirmButtonText: "确定",
  387. cancelButtonText: "取消",
  388. type: "warning"
  389. })
  390. .then(() => {
  391. load.startLoading();
  392. axios(options)
  393. .then(res => {
  394. let blob = new Blob([res.data], {
  395. // type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'
  396. type: "application/vnd.ms-excel;charset=utf-8"
  397. //word文档为application/msword,pdf文档为application/pdf,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8
  398. });
  399. let text = new Response(blob).text();
  400. text.then(res => {
  401. // 判断是否报错
  402. if (res.indexOf("code") != -1) {
  403. let json = JSON.parse(res);
  404. this.$message.error(json.msg);
  405. } else {
  406. let objectUrl = URL.createObjectURL(blob);
  407. let link = document.createElement("a");
  408. let fname = "VIP列表" + new Date().getTime(); //下载文件的名字
  409. link.href = objectUrl;
  410. link.setAttribute("download", fname);
  411. document.body.appendChild(link);
  412. link.click();
  413. }
  414. });
  415. load.endLoading();
  416. })
  417. .catch(error => {
  418. this.$message.error("导出数据失败,请连接管理员");
  419. load.endLoading();
  420. });
  421. })
  422. .catch(() => {});
  423. },
  424. onStudentExport() {
  425. // 导出VIP课
  426. // let searchForm = this.searchForm;
  427. let data = {
  428. // teacherId: searchForm.teacherId || null,
  429. // activityId: searchForm.activityId || null,
  430. // organId: searchForm.orgin || null,
  431. // status: searchForm.status || null,
  432. // search: searchForm.search || null
  433. };
  434. let url = "/api-web/export/vipGroup";
  435. const options = {
  436. method: "get",
  437. headers: {
  438. Authorization: getToken()
  439. },
  440. params: data,
  441. url,
  442. responseType: "blob"
  443. };
  444. this.$confirm("VIP课程续费提醒导出?", "提示", {
  445. confirmButtonText: "确定",
  446. cancelButtonText: "取消",
  447. type: "warning"
  448. })
  449. .then(() => {
  450. load.startLoading();
  451. axios(options)
  452. .then(res => {
  453. let blob = new Blob([res.data], {
  454. // type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'
  455. type: "application/vnd.ms-excel;charset=utf-8"
  456. //word文档为application/msword,pdf文档为application/pdf,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8
  457. });
  458. let text = new Response(blob).text();
  459. text.then(res => {
  460. // 判断是否报错
  461. if (res.indexOf("code") != -1) {
  462. let json = JSON.parse(res);
  463. this.$message.error(json.msg);
  464. } else {
  465. let objectUrl = URL.createObjectURL(blob);
  466. let link = document.createElement("a");
  467. let fname = "VIP课程续费提醒" + new Date().getTime(); //下载文件的名字
  468. link.href = objectUrl;
  469. link.setAttribute("download", fname);
  470. document.body.appendChild(link);
  471. link.click();
  472. }
  473. });
  474. load.endLoading();
  475. })
  476. .catch(error => {
  477. this.$message.error("导出数据失败,请连接管理员");
  478. load.endLoading();
  479. });
  480. })
  481. .catch(() => {});
  482. },
  483. // 修改vip
  484. resetVip(row) {
  485. let id = row.id;
  486. let educationalTeacherId = row.educationalTeacherId;
  487. let rules = JSON.stringify(this.rules);
  488. let searchForm = JSON.stringify(this.searchForm);
  489. this.$router.push({
  490. path: "/business/vipReset",
  491. query: { id, educationalTeacherId, rules, searchForm }
  492. });
  493. }
  494. },
  495. filters: {
  496. formatterTime(val) {
  497. let result;
  498. if (val) {
  499. result = val.split(" ")[0];
  500. } else {
  501. result = "";
  502. }
  503. return result;
  504. },
  505. formatterStatus(val) {
  506. let arr = [
  507. "未开始",
  508. "报名中",
  509. "进行中",
  510. "取消",
  511. "已结束",
  512. "报名结束",
  513. "暂停"
  514. ];
  515. return arr[val];
  516. }
  517. }
  518. };
  519. </script>
  520. <style lang="scss" scoped>
  521. .m-container {
  522. box-sizing: border-box;
  523. background-color: #fff;
  524. // padding: 18px 95px 55px 60px;
  525. }
  526. .newBand {
  527. display: inline-block;
  528. }
  529. </style>