studentVisit.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. <template>
  2. <div>
  3. <!-- class="m-container" -->
  4. <div class="m-core">
  5. <save-form
  6. :inline="true"
  7. @submit="search"
  8. @reset="onReSet"
  9. :model="searchForm"
  10. save-key="studentManager-returnVisitList"
  11. ref="searchForm"
  12. >
  13. <el-form-item>
  14. <el-select
  15. v-model.trim="searchForm.visiterType"
  16. placeholder="请选择角色"
  17. clearable
  18. filterable
  19. >
  20. <el-option value="TEACHER" label="指导老师"></el-option>
  21. <el-option value="EDU_TEACHER" label="乐团主管"></el-option>
  22. </el-select>
  23. </el-form-item>
  24. <!-- @change="handleChange" -->
  25. <el-form-item>
  26. <el-cascader
  27. expand-trigger="hover"
  28. clearable
  29. placeholder="请选择回访类型"
  30. :options="visitChiose"
  31. v-model="searchForm.typeList"
  32. >
  33. </el-cascader>
  34. </el-form-item>
  35. <el-form-item>
  36. <el-select
  37. v-model.trim="searchForm.probStatus"
  38. placeholder="问题状态"
  39. clearable
  40. filterable
  41. >
  42. <el-option :value="0" label="待跟进"></el-option>
  43. <el-option :value="1" label="已解决"></el-option>
  44. </el-select>
  45. </el-form-item>
  46. <el-form-item>
  47. <el-date-picker
  48. v-model.trim="searchForm.timer"
  49. style="width: 420px"
  50. type="daterange"
  51. value-format="yyyy-MM-dd"
  52. range-separator="至"
  53. start-placeholder="回访开始日期"
  54. end-placeholder="回访结束日期"
  55. :picker-options="{
  56. firstDayOfWeek: 1,
  57. }"
  58. >
  59. </el-date-picker>
  60. </el-form-item>
  61. <el-form-item>
  62. <el-button type="danger" native-type="submit">搜索</el-button>
  63. <el-button native-type="reset" type="primary">重置</el-button>
  64. <el-button
  65. v-if="permission('export/studentVisitRecord')"
  66. type="primary"
  67. @click="onExport"
  68. >导出</el-button
  69. >
  70. </el-form-item>
  71. </save-form>
  72. <el-button type="primary" v-if="permission('visit/add/studentVisit')" style="margin-bottom:20px" @click="visitVisible= true">新增回访</el-button>
  73. <div class="tableWrap">
  74. <el-table
  75. :data="tableList"
  76. :header-cell-style="{ background: '#EDEEF0', color: '#444' }"
  77. >
  78. <el-table-column align="center" prop="teacherName" label="老师姓名">
  79. <template slot-scope="scope">
  80. <copy-text>{{ scope.row.teacherName }}</copy-text>
  81. </template>
  82. </el-table-column>
  83. <el-table-column align="center" prop="organName" label="所属分部">
  84. <template slot-scope="scope">
  85. <copy-text>{{ scope.row.organName }}</copy-text>
  86. </template>
  87. </el-table-column>
  88. <el-table-column align="center" prop="visiterType" label="角色">
  89. <template slot-scope="scope">
  90. <div>
  91. {{ scope.row.visiterType | visiterType }}
  92. </div>
  93. </template>
  94. </el-table-column>
  95. <!-- <el-table-column align="center" prop="studentId" label="学生ID">
  96. <template slot-scope="scope">
  97. <copy-text>{{ scope.row.studentId }}</copy-text>
  98. </template>
  99. </el-table-column>
  100. <el-table-column align="center" prop="studentName" label="学生姓名">
  101. <template slot-scope="scope">
  102. <copy-text>{{ scope.row.studentName }}</copy-text>
  103. </template>
  104. </el-table-column> -->
  105. <el-table-column align="center" prop="type" label="回访类型">
  106. </el-table-column>
  107. <el-table-column align="center" prop="purpose" label="回访目的">
  108. </el-table-column>
  109. <el-table-column align="center" prop="probStatus" label="问题状态">
  110. <template slot-scope="scope">
  111. <div>
  112. {{ scope.row.probStatus ? "已解决" : "待跟进" }}
  113. </div>
  114. </template>
  115. </el-table-column>
  116. <el-table-column align="center" prop="name" label="回访图片">
  117. <template slot-scope="scope">
  118. <div class="flexBox">
  119. <el-image
  120. v-if="scope.row.attachments"
  121. style="width: 60px; height: 60px"
  122. fit="cover"
  123. :src="scope.row.attachments.split(',')[0]"
  124. :previewSrcList="scope.row.attachments.split(',')"
  125. >
  126. </el-image>
  127. <p v-if="scope.row.attachments.split(',').length - 1 > 0">
  128. +{{ scope.row.attachments.split(",").length - 1 }}
  129. </p>
  130. </div>
  131. </template>
  132. </el-table-column>
  133. <el-table-column align="center" prop="visitTime" label="回访时间">
  134. <template slot-scope="scope">
  135. <div>
  136. {{
  137. scope.row.visitTime ? scope.row.visitTime.split(" ")[0] : "--"
  138. }}
  139. </div>
  140. </template>
  141. </el-table-column>
  142. <el-table-column align="center" prop="masterTeacherName" label="操作">
  143. <template slot-scope="scope">
  144. <div>
  145. <el-button type="text" @click="lookDetail(scope.row)"
  146. >查看</el-button
  147. >
  148. <el-button
  149. type="text"
  150. v-if="
  151. !scope.row.probStatus &&
  152. permission('visit/updateProbStatus')
  153. "
  154. @click="updateState(scope.row)"
  155. >状态修改</el-button
  156. >
  157. </div>
  158. </template>
  159. </el-table-column>
  160. </el-table>
  161. <pagination
  162. save-key="studentManager-returnVisitList"
  163. sync
  164. :total.sync="rules.total"
  165. :page.sync="rules.page"
  166. :limit.sync="rules.limit"
  167. :page-sizes="rules.page_size"
  168. @pagination="getList"
  169. />
  170. </div>
  171. </div>
  172. <el-dialog
  173. title="回访详情"
  174. width="740px"
  175. v-if="detailVisible"
  176. :visible.sync="detailVisible"
  177. >
  178. <div>
  179. <descriptions :column="2" v-if="activeRow" class="returnDialog">
  180. <descriptions-item label="老师姓名:">{{
  181. activeRow.teacherName
  182. }}</descriptions-item>
  183. <descriptions-item label="所属分部:">{{
  184. activeRow.organName
  185. }}</descriptions-item>
  186. <descriptions-item label="角色:">{{
  187. activeRow.visiterType | visiterType
  188. }}</descriptions-item>
  189. <descriptions-item label="学生姓名:">{{
  190. activeRow.studentName
  191. }}</descriptions-item>
  192. <descriptions-item label="回访类型:">{{
  193. activeRow.type
  194. }}</descriptions-item>
  195. <descriptions-item label="回访目的:">{{
  196. activeRow.purpose
  197. }}</descriptions-item>
  198. <descriptions-item
  199. label="家长反馈:"
  200. v-if="activeRow.type == '小课回访'"
  201. >{{
  202. activeRow.feedbackType | feedbackTypeFilter
  203. }}</descriptions-item
  204. >
  205. <descriptions-item
  206. label="反馈详情:"
  207. v-if="
  208. (activeRow.feedbackType == 'THINKING' ||
  209. activeRow.feedbackType == 'LOST') &&
  210. activeRow.type == '小课回访'
  211. "
  212. >{{
  213. activeRow.feedbackTypeDesc | feedbackTypeDescFilter
  214. }}</descriptions-item
  215. >
  216. <descriptions-item label="问题状态:">{{
  217. activeRow.probStatus ? "已解决" : "待跟进"
  218. }}</descriptions-item>
  219. <descriptions-item label="回访情况:" :span="6">{{
  220. activeRow.overview
  221. }}</descriptions-item>
  222. <descriptions-item
  223. :label="(activeRow.type == '小课回访' || activeRow.purpose == '考勤申诉') ? '原因' : '家长反馈'"
  224. :span="6"
  225. >{{ activeRow.feedback }}</descriptions-item
  226. >
  227. <descriptions-item label="回访图片" :span="6">
  228. <div class="list" v-if="imageList.length > 0">
  229. <div v-for="item in imageList" :key="item.url" class="item">
  230. <el-image
  231. v-if="item"
  232. :src="item"
  233. :preview-src-list="imageList.map((item) => item)"
  234. class="img"
  235. >
  236. </el-image>
  237. </div>
  238. </div>
  239. <empty v-else />
  240. </descriptions-item>
  241. </descriptions>
  242. </div>
  243. </el-dialog>
  244. <el-dialog
  245. title="新增回访"
  246. width="760px"
  247. :visible.sync="visitVisible"
  248. append-to-body
  249. >
  250. <visit
  251. v-if="visitVisible"
  252. :detail="{
  253. studentName:$route.query.username,
  254. studentId: $route.query.userId,
  255. }"
  256. :username="$route.query.username"
  257. @close="visitVisible = false"
  258. @submited="getList"
  259. :isMainGo="false"
  260. :useVisitType="false"
  261. />
  262. </el-dialog>
  263. </div>
  264. </template>
  265. <script>
  266. import { visitChiose1 } from "@/utils/searchArray";
  267. import { Export } from "@/utils/downLoadFile";
  268. import pagination from "@/components/Pagination/index";
  269. import { getEmployeeOrgan } from "@/api/buildTeam";
  270. import {
  271. getVisitList,
  272. updateProbStatus,
  273. } from "@/views/returnVisitManager/api.js";
  274. import cleanDeep from "clean-deep";
  275. import { getTimes } from "@/utils";
  276. import { resetQuery } from "@/utils/utils";
  277. import { permission } from "@/utils/directivePage";
  278. import visit from "@/views/withdrawal-application/modals/visit";
  279. import qs from "qs";
  280. export default {
  281. components: { pagination,visit },
  282. data() {
  283. return {
  284. searchForm: {
  285. search: "",
  286. organId: "",
  287. visiterType: "",
  288. typeList: [],
  289. timer: [],
  290. },
  291. visitChiose: visitChiose1,
  292. organList: [],
  293. rules: {
  294. // 分页规则
  295. limit: 10, // 限制显示条数
  296. page: 1, // 当前页
  297. total: 0, // 总条数
  298. page_size: [10, 20, 40, 50], // 选择限制显示条数
  299. },
  300. tableList: [],
  301. imageList: [],
  302. detailVisible: false,
  303. activeRow: null,
  304. visitVisible:false
  305. };
  306. },
  307. mounted() {
  308. // getEmployeeOrgan().then((res) => {
  309. // if (res.code == 200) {
  310. // this.organList = res.data;
  311. // }
  312. // });
  313. this.$store.dispatch("setBranchs");
  314. if (this.$route.query) {
  315. // 兼容老功能
  316. if (this.$route.query.search) {
  317. this.searchForm.search = this.$route.query.teacher;
  318. this.searchForm.search = this.$route.query.search;
  319. }
  320. this.searchForm.timer = this.$route.query.timer;
  321. }
  322. if (this.$route.query.typeList && this.$route.query.typeList.length > 0) {
  323. this.searchForm.typeList = this.$route.query.typeList;
  324. }
  325. this.getList();
  326. if (this.$route.query.search || this.$route.query.timer) {
  327. // console.log( )
  328. resetQuery(this, { timer: undefined, search: undefined });
  329. }
  330. },
  331. // activated() {
  332. // this.getList();
  333. // },
  334. methods: {
  335. handleChange(val) {
  336. this.searchForm.type = val[0];
  337. this.searchForm.purpose = val[1];
  338. },
  339. search() {
  340. // this.$router.replace({query:{...this.$route.query,timer:undefined,teacher:undefined}})
  341. this.rules.page = 1;
  342. this.getList();
  343. },
  344. onReSet() {
  345. this.type = [];
  346. this.timer = [];
  347. this.searchForm = {
  348. search: "",
  349. organId: "",
  350. visiterType: "",
  351. type: "",
  352. purpose: "",
  353. typeList: [],
  354. timer: [],
  355. };
  356. this.search();
  357. },
  358. getList() {
  359. // cleanDeep
  360. let { timer, typeList, ...rest } = this.searchForm;
  361. let type,
  362. purpose = null;
  363. if (typeList.length > 0) {
  364. type = typeList[0];
  365. purpose = typeList[1];
  366. }
  367. let params = {
  368. ...rest,
  369. page: this.rules.page,
  370. rows: this.rules.limit,
  371. ...getTimes(timer, ["startTime", "endTime"]),
  372. type,
  373. purpose,
  374. studentId: this.$route.query.userId,
  375. };
  376. getVisitList(cleanDeep(params)).then((res) => {
  377. if (res.code == 200) {
  378. this.tableList = res.data.rows;
  379. this.rules.total = res.data.total;
  380. }
  381. });
  382. },
  383. lookDetail(row) {
  384. this.activeRow = row;
  385. this.imageList = this.activeRow.attachments.split(",");
  386. this.detailVisible = true;
  387. },
  388. updateState(row) {
  389. this.$confirm("是否将问题状态更变为'已解决'", "提示", {
  390. confirmButtonText: "确定",
  391. cancelButtonText: "取消",
  392. type: "warning",
  393. }).then((res) => {
  394. updateProbStatus({ id: row.id, probStatus: 1 }).then((res) => {
  395. this.$message.success("修改成功");
  396. this.getList();
  397. });
  398. });
  399. },
  400. changeTimer(val) {
  401. if (val && val.length > 0) {
  402. this.searchForm.startTime = this.timer[0];
  403. this.searchForm.endTime = this.timer[1];
  404. } else {
  405. this.searchForm.startTime = null;
  406. this.searchForm.endTime = null;
  407. }
  408. },
  409. permission(str) {
  410. return permission(str);
  411. },
  412. onExport() {
  413. let { timer, typeList, ...rest } = this.searchForm;
  414. let type,
  415. purpose = null;
  416. if (typeList.length > 0) {
  417. type = typeList[0];
  418. purpose = typeList[1];
  419. }
  420. let params = {
  421. ...rest,
  422. page: this.rules.page,
  423. rows: this.rules.limit,
  424. ...getTimes(timer, ["startTime", "endTime"]),
  425. type,
  426. purpose,
  427. };
  428. Export(
  429. this,
  430. {
  431. url: "/api-web/export/studentVisitRecord",
  432. params: qs.stringify(
  433. cleanDeep({
  434. ...params,
  435. })
  436. ),
  437. fileName: `学员回访导出.xls`,
  438. method: "post",
  439. },
  440. "确定导出学员回访"
  441. );
  442. },
  443. },
  444. watch: {
  445. detailVisible(val) {
  446. if (!val) {
  447. this.activeRow = null;
  448. this.imageList = [];
  449. }
  450. },
  451. },
  452. };
  453. </script>
  454. <style lang="scss" scoped>
  455. .msg {
  456. min-width: 120px;
  457. }
  458. .label {
  459. width: 520px;
  460. }
  461. ::v-deep .description-title {
  462. margin-bottom: 0;
  463. }
  464. .returnDialog {
  465. ::v-deep .description-view {
  466. .description-content {
  467. white-space: normal !important;
  468. }
  469. }
  470. }
  471. .flexBox {
  472. display: flex;
  473. flex-direction: row;
  474. align-items: center;
  475. }
  476. .img-container {
  477. margin: 10px auto;
  478. }
  479. .item {
  480. width: 150px;
  481. margin-top: 10px;
  482. margin-right: 10px;
  483. display: inline-block;
  484. position: relative;
  485. }
  486. .img {
  487. width: 150px;
  488. height: 150px;
  489. }
  490. .ctrl-bar {
  491. background-color: rgba(0, 0, 0, 0.45);
  492. height: 30px;
  493. position: absolute;
  494. top: 0;
  495. width: 100%;
  496. z-index: 1;
  497. display: flex;
  498. justify-content: flex-end;
  499. align-items: center;
  500. padding: 0 15px;
  501. i {
  502. color: #fff;
  503. cursor: pointer;
  504. }
  505. }
  506. </style>