index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. <template>
  2. <div class="m-container">
  3. <h2>
  4. <div class="squrt"></div>
  5. 退团申请
  6. <auth :auths="['/WithdrawalApplication/abnormal']">
  7. <filter-search
  8. @reload="reloadSearch"
  9. :keys="['visitFlag']"
  10. :moreKeys="['organId']"
  11. />
  12. </auth>
  13. </h2>
  14. <div class="m-core">
  15. <save-form
  16. :inline="true"
  17. @submit="submit"
  18. @reset="onReSet"
  19. ref="searchForm"
  20. :model.sync="searchForm"
  21. >
  22. <el-form-item>
  23. <el-input
  24. v-model.trim="searchForm.search"
  25. clearable
  26. placeholder="学员(乐团)编号、名称"
  27. ></el-input>
  28. </el-form-item>
  29. <el-form-item prop="organId">
  30. <el-select
  31. class="multiple"
  32. style="width: 180px !important"
  33. v-model.trim="searchForm.organId"
  34. filterable
  35. clearable
  36. placeholder="请选择分部"
  37. >
  38. <el-option
  39. v-for="(item, index) in selects.branchs"
  40. :key="index"
  41. :label="item.name"
  42. :value="item.id"
  43. ></el-option>
  44. </el-select>
  45. </el-form-item>
  46. <el-form-item>
  47. <el-select
  48. v-model="searchForm.status"
  49. clearable
  50. placeholder="请选择状态"
  51. >
  52. <el-option
  53. v-for="(item, key) in withdrawalStatus"
  54. :key="key"
  55. :label="item"
  56. :value="key"
  57. ></el-option>
  58. </el-select>
  59. </el-form-item>
  60. <el-form-item>
  61. <el-select
  62. v-model="searchForm.currentApproveRole"
  63. clearable
  64. placeholder="请选择角色"
  65. >
  66. <el-option label="分部经理" value="1"></el-option>
  67. <el-option label="乐团主管" value="2"></el-option>
  68. </el-select>
  69. </el-form-item>
  70. <el-form-item>
  71. <el-button native-type="submit" type="danger">搜索</el-button>
  72. <el-button native-type="reset" type="primary">重置</el-button>
  73. <el-button
  74. @click="onExport"
  75. type="primary"
  76. v-permission="'export/musicGroupQuit'"
  77. >导出</el-button
  78. >
  79. </el-form-item>
  80. </save-form>
  81. <div class="btnList">
  82. <auth :auths="['musicGroupQuit/batchQuitMusicGroup']">
  83. <el-button type="primary" @click="quiteTeamAll">批量处理</el-button>
  84. </auth>
  85. </div>
  86. <el-table
  87. :data="list"
  88. style="width: 100%"
  89. :header-cell-style="{ background: '#EDEEF0', color: '#444' }"
  90. @selection-change="handleSelectionChange"
  91. >
  92. <el-table-column
  93. type="selection"
  94. width="55"
  95. :selectable="checkSeleabled"
  96. >
  97. </el-table-column>
  98. <el-table-column prop="userId" label="学员编号">
  99. <copy-text slot-scope="scope">{{ scope.row.userId }}</copy-text>
  100. </el-table-column>
  101. <el-table-column prop="userId" label="学员姓名">
  102. <copy-text slot-scope="scope">{{
  103. scope.row.user.username
  104. }}</copy-text>
  105. </el-table-column>
  106. <el-table-column prop="organName" label="所属分部">
  107. <div slot-scope="scope">{{ scope.row.musicGroup.organName }}</div>
  108. </el-table-column>
  109. <el-table-column prop="musicGroupId" label="所属乐团">
  110. <copy-text slot-scope="scope">{{
  111. scope.row.musicGroup.name
  112. }}</copy-text>
  113. </el-table-column>
  114. <el-table-column prop="createTime" label="申请时间">
  115. <template slot-scope="scope">
  116. <div>
  117. {{ scope.row.createTime | dateForMinFormat }}
  118. </div>
  119. </template>
  120. </el-table-column>
  121. <el-table-column prop="applyUserName" label="申请人"></el-table-column>
  122. <el-table-column prop="createTime" label="退费金额">
  123. <template slot-scope="scope">
  124. <div>
  125. <p v-if="!scope.row.returnTotalFee">--</p>
  126. <p v-else>
  127. {{ scope.row.returnTotalFee | moneyFormat }}
  128. </p>
  129. </div>
  130. </template>
  131. </el-table-column>
  132. <el-table-column prop="reasonEnum" label="退团原因">
  133. <template slot-scope="scope">
  134. <p>{{ scope.row.reasonEnum | quiteReasonFilter }}</p>
  135. </template>
  136. </el-table-column>
  137. <el-table-column prop="userComment" label="退团理由">
  138. <overflow-text
  139. width="100%"
  140. slot-scope="scope"
  141. :text="scope.row.userComment"
  142. ></overflow-text>
  143. </el-table-column>
  144. <el-table-column label="状态">
  145. <template slot-scope="scope">
  146. <div>
  147. {{ scope.row.status | withdrawalStatus }}
  148. <span v-if="scope.row.status == 'PROCESSING'"
  149. >({{
  150. scope.row.currentApproveRole == 1 ? "分部经理" : "乐团主管"
  151. }})</span
  152. >
  153. </div>
  154. </template>
  155. </el-table-column>
  156. <el-table-column
  157. align="center"
  158. fixed="right"
  159. width="200px;"
  160. label="操作"
  161. v-if="
  162. permission('musicGroupQuit/quitMusicGroup/quit') ||
  163. permission('musicGroupQuit/quitMusicGroup/quit-only') ||
  164. permission('visit/add/tuituanliebiao')
  165. "
  166. >
  167. <template slot-scope="scope">
  168. <div>
  169. <el-button
  170. type="text"
  171. v-if="
  172. permission('musicGroupQuit/quitMusicGroup/quit') && isManage
  173. "
  174. :disabled="
  175. scope.row.status != 'PROCESSING' ||
  176. scope.row.status == 'DENIED' ||
  177. scope.row.status == 'CANCELED' ||
  178. (scope.row.status == 'PROCESSING' &&
  179. isManage == 'manage' &&
  180. scope.row.currentApproveRole == '2') ||
  181. (scope.row.status == 'PROCESSING' &&
  182. isManage == 'staff' &&
  183. scope.row.currentApproveRole == '1')
  184. "
  185. @click="quieTeamMask(scope.row)"
  186. >立即处理</el-button
  187. >
  188. <el-button
  189. type="text"
  190. v-if="
  191. permission('musicGroupQuit/quitMusicGroup/quit') && isManage
  192. "
  193. :disabled="
  194. !(
  195. scope.row.status != 'PROCESSING' ||
  196. scope.row.status == 'DENIED' ||
  197. scope.row.status == 'CANCELED' ||
  198. (scope.row.status == 'PROCESSING' &&
  199. isManage == 'manage' &&
  200. scope.row.currentApproveRole == '2') ||
  201. (scope.row.status == 'PROCESSING' &&
  202. isManage == 'staff' &&
  203. scope.row.currentApproveRole == '1')
  204. )
  205. "
  206. @click="quieTeamMask(scope.row, 'look')"
  207. >查看</el-button
  208. >
  209. <!-- <el-button
  210. type="text"
  211. v-if="permission('musicGroupQuit/quitMusicGroup/quit-only')"
  212. :disabled="scope.row.status != 'PROCESSING'"
  213. @click="quieTeam(scope.row)"
  214. >退团</el-button> -->
  215. <el-button
  216. type="text"
  217. v-if="permission('visit/add/tuituanliebiao')"
  218. @click="addVisit(scope.row)"
  219. >新增回访</el-button
  220. >
  221. </div>
  222. </template>
  223. </el-table-column>
  224. </el-table>
  225. <pagination
  226. sync
  227. :total.sync="rules.total"
  228. :page.sync="rules.page"
  229. :limit.sync="rules.limit"
  230. :page-sizes="rules.page_size"
  231. @pagination="FetchList"
  232. />
  233. </div>
  234. <el-dialog
  235. :title="isDisabled ? '查看' : '立即处理'"
  236. width="700px"
  237. :visible.sync="quitVisible"
  238. >
  239. <quitModal
  240. v-if="quitVisible"
  241. :quitForm="quitForm"
  242. @close="quitVisible = false"
  243. @submited="FetchList"
  244. :isDisabled="isDisabled"
  245. ref="musicForm"
  246. />
  247. <p style="color: red; paddingleft: 150px">退费金额暂不进入账户余额</p>
  248. <span slot="footer" class="dialog-footer question">
  249. <div>
  250. <el-popover placement="right" width="500" trigger="click">
  251. <div class="popoverWrap">
  252. <p>乐团退团退费规则:</p>
  253. <p>退还云教练费用:报名缴费时缴费的云教练费用</p>
  254. <p>退还课程费用:缴费总额-已结束课时单价之和</p>
  255. <p>退还乐器费用:报名缴费时缴纳的乐器费用(团购、租金)</p>
  256. <p>退还教辅费用:报名缴费时缴费的教辅费用</p>
  257. <p v-if="$helpers.tenantId == 1">
  258. 退还乐保费用:报名缴费时缴费的乐保费用
  259. </p>
  260. </div>
  261. <el-button
  262. type="text"
  263. icon="el-icon-question"
  264. slot="reference"
  265. style="color: red"
  266. >退团退费说明</el-button
  267. >
  268. </el-popover>
  269. </div>
  270. <div v-if="!isDisabled">
  271. <el-button
  272. type="primary"
  273. v-permission="'musicGroupQuit/quitMusicGroup'"
  274. v-if="isManage == 'staff'"
  275. @click="submitInfo('PROCESSING')"
  276. >提交审核</el-button
  277. >
  278. <el-button
  279. v-if="isManage == 'super' || isManage == 'manage'"
  280. type="primary"
  281. v-permission="'musicGroupQuit/quitMusicGroup'"
  282. @click="submitInfo('APPROVED')"
  283. >同意</el-button
  284. >
  285. <el-button
  286. type="danger"
  287. v-permission="'musicGroupQuit/quitMusicGroup'"
  288. @click="submitInfo('DENIED')"
  289. >拒绝</el-button
  290. >
  291. </div>
  292. </span>
  293. </el-dialog>
  294. <!-- <el-dialog title="退团" width="500px" :visible.sync="quitOnlyVisible">
  295. <quitOnlyModal
  296. v-if="quitOnlyVisible && detail"
  297. :detail="detail"
  298. @close="quitOnlyVisible = false"
  299. @submited="FetchList"
  300. />
  301. </el-dialog> -->
  302. <el-dialog title="新增回访" width="760px" :visible.sync="visitVisible">
  303. <visit
  304. v-if="visitVisible && detail"
  305. :detail="detail"
  306. @close="visitVisible = false"
  307. @submited="FetchList"
  308. />
  309. </el-dialog>
  310. </div>
  311. </template>
  312. <script>
  313. import pagination from "@/components/Pagination/index";
  314. import { permission } from "@/utils/directivePage";
  315. import quitModal from "@/views/teamDetail/components/modals/quite-team";
  316. import quitOnlyModal from "./modals/quitOnly";
  317. import visit from "./modals/visit";
  318. import { Export } from "@/utils/downLoadFile";
  319. import { quitMusicGroup } from "@/api/journal";
  320. import qs from "qs";
  321. import { musicGroupQuitQeryPage, batchQuitMusicGroup } from "./api";
  322. import cleanDeep from "clean-deep";
  323. import { musicGroupQuit } from "@/api/journal";
  324. import { withdrawalStatus } from "@/constant";
  325. import { quiteReasonFilter } from "@/utils/vueFilter";
  326. let that;
  327. const initSearch = {
  328. search: "",
  329. status: null,
  330. organId: "",
  331. };
  332. export default {
  333. components: { pagination, quitModal, quitOnlyModal, visit },
  334. data() {
  335. return {
  336. withdrawalStatus,
  337. quitVisible: false,
  338. quitOnlyVisible: false,
  339. visitVisible: false,
  340. detail: null,
  341. list: [],
  342. searchForm: {
  343. ...initSearch,
  344. },
  345. rules: {
  346. // 分页规则
  347. limit: 10, // 限制显示条数
  348. page: 1, // 当前页
  349. total: 0, // 总条数
  350. page_size: [10, 20, 40, 50], // 选择限制显示条数
  351. },
  352. quitForm: {
  353. // 退团信息确认
  354. isRefundCourseFee: null,
  355. isRefundInstrumentFee: null,
  356. isRefundTeachingAssistantsFee: null,
  357. reason: "",
  358. courseViewType: null,
  359. hasMaintenance: null,
  360. studentName: null,
  361. musicGroupName: null,
  362. isVisit: false,
  363. userComment: null,
  364. },
  365. isManage: false,
  366. isDisabled: false,
  367. multipleSelection: [],
  368. };
  369. },
  370. async mounted() {
  371. that = this;
  372. let isSuperAdmin = this.$store.getters.isSuperAdmin;
  373. let roles = this.$store.getters.roles;
  374. await this.$store.dispatch("setBranchs");
  375. if (isSuperAdmin) {
  376. this.isManage = "super";
  377. } else {
  378. if (roles.indexOf("1") != -1) {
  379. this.isManage = "manage";
  380. }
  381. if (roles.indexOf("2") != -1) {
  382. this.isManage = "staff";
  383. }
  384. }
  385. if (roles.indexOf("1") != -1 && roles.indexOf("2") != -1) {
  386. this.isManage = "super";
  387. }
  388. this.FetchList();
  389. },
  390. methods: {
  391. permission,
  392. reloadSearch() {
  393. this.rules.page = 1;
  394. this.FetchList();
  395. },
  396. submit() {
  397. this.rules.page = 1;
  398. this.FetchList();
  399. },
  400. onReSet() {
  401. this.searchForm = { ...initSearch };
  402. this.submit();
  403. },
  404. async quieTeamMask(row, look) {
  405. await musicGroupQuit({ id: row.id }).then((res) => {
  406. if (res.code == 200) {
  407. if (look == "look") {
  408. this.isDisabled = true;
  409. } else {
  410. this.isDisabled = false;
  411. }
  412. this.musicForm = res.data;
  413. this.activeRow = { ...row, ...res.data.returnFeeDto };
  414. this.quitForm.courseViewType = res.data.musicGroup.courseViewType;
  415. this.quitForm.userComment = res.data.userComment;
  416. this.quitForm.isVisit = res.data.isVisit;
  417. this.quitForm.visitTime = res.data.visitTime;
  418. this.quitForm.studentName = res.data.user?.username;
  419. this.quitForm.musicGroupName = res.data.musicGroup?.name;
  420. this.quitForm.hasMaintenance = res.data.hasMaintenance;
  421. this.quitForm.reason = res.data.reason;
  422. this.quitForm.id = res.data.id;
  423. this.quitForm.reasonEnum = res.data.reasonEnum;
  424. this.quitForm = { ...this.quitForm, ...res.data.returnFeeDto };
  425. this.quitVisible = true;
  426. } else {
  427. this.$message.error(res.msg);
  428. }
  429. });
  430. // this.detail = row;
  431. },
  432. quieTeam(row) {
  433. this.quitOnlyVisible = true;
  434. this.detail = row;
  435. },
  436. addVisit(row) {
  437. this.visitVisible = true;
  438. this.detail = row;
  439. },
  440. async FetchList() {
  441. try {
  442. const res = await musicGroupQuitQeryPage({
  443. page: this.rules.page,
  444. rows: this.rules.limit,
  445. visitFlag: this.$route.query.visitFlag,
  446. ...this.searchForm,
  447. });
  448. this.rules.total = res.data.total;
  449. this.list = res.data.rows;
  450. } catch (error) {}
  451. },
  452. onExport() {
  453. const { ...rest } = this.searchForm;
  454. Export(
  455. this,
  456. {
  457. url: "/api-web/export/musicGroupQuit",
  458. fileName: "退团申请.xls",
  459. method: "post",
  460. params: qs.stringify({
  461. visitFlag: this.$route.query.visitFlag,
  462. ...rest,
  463. }),
  464. },
  465. "您确定导出退团申请列表?"
  466. );
  467. },
  468. submitInfo(status) {
  469. this.$confirm("确定此操作吗?", "提示", {
  470. confirmButtonText: "确定",
  471. cancelButtonText: "取消",
  472. type: "warning",
  473. })
  474. .then(async () => {
  475. let query = this.quitForm;
  476. query.status = status;
  477. query.returnFeeDto = {
  478. accessoriesFee: query.accessoriesFee,
  479. courseFee: query.courseFee,
  480. isReturnAccessoriesFee: query.isReturnAccessoriesFee,
  481. isReturnCourseFee: query.isReturnCourseFee,
  482. isReturnMaintenanceFee: query.isReturnMaintenanceFee,
  483. isReturnMemberFee: query.isReturnMemberFee,
  484. isReturnMusicalFee: query.isReturnMusicalFee,
  485. maintenanceFee: query.maintenanceFee,
  486. memberFee: query.memberFee,
  487. musicalFee: query.musicalFee,
  488. };
  489. await quitMusicGroup(cleanDeep(query)).then((res) => {
  490. this.$message.success("处理成功");
  491. this.activeRow = null;
  492. if (this.$refs["musicForm"].$refs.quitForm) {
  493. this.$refs["musicForm"].$refs.quitForm.resetFields();
  494. }
  495. this.quitVisible = false;
  496. this.FetchList();
  497. });
  498. })
  499. .catch((e) => {
  500. console.log(e);
  501. });
  502. // 乐团主管这里
  503. },
  504. handleSelectionChange(val) {
  505. this.multipleSelection = val;
  506. },
  507. quiteTeamAll() {
  508. if (this.multipleSelection.length < 1) {
  509. this.$message.error("请至少选择一名学员");
  510. return;
  511. }
  512. let str = "";
  513. this.multipleSelection.forEach((stu) => {
  514. console.log(stu);
  515. str += stu.user.username + ",";
  516. });
  517. str += "的退团申请将批量处理";
  518. this.$prompt(str, "提示", {
  519. distinguishCancelAndClose: true,
  520. confirmButtonText: "同意",
  521. cancelButtonText: "拒绝",
  522. confirmButtonClass: "el-button--primary",
  523. cancelButtonClass: "el-button--danger",
  524. closeOnClickModal: false,
  525. inputPattern: /\S/,
  526. inputType: "textarea",
  527. inputErrorMessage: "请输入审批理由",
  528. inputPlaceholder: "请输入审批理由",
  529. beforeClose: async (val, instance, done) => {
  530. if (val == "confirm") {
  531. // reason
  532. let objList = this.multipleSelection.map((stu) => {
  533. return {
  534. id: stu.id,
  535. status: "APPROVED",
  536. reason: instance.inputValue,
  537. };
  538. });
  539. try {
  540. const res = await batchQuitMusicGroup(objList);
  541. this.$message.success("审批成功");
  542. done();
  543. this.FetchList();
  544. } catch (e) {
  545. console.log(e);
  546. }
  547. } else if (val == "cancel") {
  548. instance.validate();
  549. let objList = this.multipleSelection.map((stu) => {
  550. return {
  551. id: stu.id,
  552. status: "DENIED",
  553. reason: instance.inputValue,
  554. };
  555. });
  556. try {
  557. const res = await batchQuitMusicGroup(objList);
  558. this.$message.success("审批成功");
  559. done();
  560. this.FetchList();
  561. } catch (e) {
  562. console.log(e);
  563. }
  564. } else {
  565. done();
  566. }
  567. },
  568. })
  569. .then(() => {
  570. // 同意
  571. })
  572. .catch((msg) => {
  573. // close,cancel
  574. console.log(msg);
  575. });
  576. },
  577. checkSeleabled(row) {
  578. if (row.status == "PROCESSING" && row.currentApproveRole == "1") {
  579. return 1;
  580. } else {
  581. return 0;
  582. }
  583. },
  584. },
  585. };
  586. </script>
  587. <style lang="scss" scoped>
  588. .dialog-footer.question {
  589. display: flex;
  590. flex-direction: row;
  591. justify-content: space-between;
  592. }
  593. .btnList {
  594. margin-bottom: 20px;
  595. }
  596. .confirmButton {
  597. color: #fff;
  598. background-color: #f56c6c;
  599. border-color: #f56c6c;
  600. }
  601. .cancelButton {
  602. color: #fff;
  603. background-color: var(--color-primary);
  604. border-color: var(--color-primary);
  605. }
  606. </style>