addVisit.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. <template>
  2. <div class="addVisit">
  3. <m-header v-if="statusList.headerStatus" :name="name" />
  4. <van-cell-group>
  5. <van-field
  6. label="回访老师"
  7. v-model="teacherName"
  8. readonly
  9. input-align="right"
  10. placeholder="请选择"
  11. />
  12. </van-cell-group>
  13. <van-cell-group>
  14. <van-field
  15. label="学员姓名"
  16. @click="onCheckStudent"
  17. v-model="studentName"
  18. readonly
  19. input-align="right"
  20. :is-link="id || userId || studentId ? false : true"
  21. placeholder="请选择"
  22. >
  23. <template #right-icon>
  24. <a v-if="studentPhone" @click.stop="() => {}" class="phone_section" :href="'tel:' + studentPhone"><img src="../../assets/images/icon_phone.png" class="iconPhone" alt=""></a>
  25. </template>
  26. </van-field>
  27. <van-field
  28. label="回访类型"
  29. @click="onChange('type')"
  30. v-model="form.type"
  31. readonly
  32. input-align="right"
  33. :is-link="id || userId ? false : true"
  34. placeholder="请选择"
  35. />
  36. <van-field
  37. label="回访目的"
  38. @click="onChange('purpose')"
  39. v-model="form.purpose"
  40. readonly
  41. input-align="right"
  42. :is-link="id || userId ? false : true"
  43. placeholder="请选择"
  44. />
  45. </van-cell-group>
  46. <van-cell-group>
  47. <div class="dot"></div>
  48. <van-field
  49. label="当前学生情况"
  50. class="textarea"
  51. :readonly="id ? true : false"
  52. v-model="form.overview"
  53. rows="2"
  54. autosize
  55. type="textarea"
  56. maxlength="50"
  57. placeholder="请输入留言"
  58. :show-word-limit="id ? false : true"
  59. />
  60. </van-cell-group>
  61. <van-cell-group>
  62. <div class="dot"></div>
  63. <van-field
  64. label="沟通后家长反馈"
  65. class="textarea"
  66. :readonly="id ? true : false"
  67. v-model="form.feedback"
  68. rows="2"
  69. autosize
  70. type="textarea"
  71. maxlength="50"
  72. placeholder="请输入留言"
  73. :show-word-limit="id ? false : true"
  74. />
  75. </van-cell-group>
  76. <van-cell-group>
  77. <van-field
  78. label="回访时间"
  79. class="visiTimer"
  80. v-model="form.visitTime"
  81. readonly
  82. @click="onEnListShow"
  83. input-align="right"
  84. :is-link="id ? false : true"
  85. placeholder="请选择"
  86. />
  87. </van-cell-group>
  88. <div class="button-group" v-if="!id">
  89. <van-button type="primary" @click="onSubmit" round size="large"
  90. >确认</van-button
  91. >
  92. </div>
  93. <van-action-sheet
  94. v-model="visit.status"
  95. :actions="visit.data"
  96. cancel-text="取消"
  97. @cancel="visit.status = false"
  98. @select="onModeSelect"
  99. />
  100. <van-popup v-model="dataForm.status" position="bottom">
  101. <van-datetime-picker
  102. v-model="dataForm.currentDate"
  103. type="date"
  104. :min-date="dataForm.minDate"
  105. :max-date="dataForm.maxDate"
  106. :formatter="formatter"
  107. @cancel="dataForm.status = false"
  108. @confirm="onCurrentConfirm"
  109. />
  110. </van-popup>
  111. <!-- 选择上课学生 -->
  112. <van-popup
  113. v-model="statusList.studentStatus"
  114. :lock-scroll="true"
  115. position="bottom"
  116. :style="{ height: '180%' }"
  117. >
  118. <van-sticky>
  119. <van-search
  120. show-action
  121. shape="round"
  122. :left-icon="searchIcon"
  123. @search="onSearch"
  124. v-model="params.search"
  125. placeholder="请输入学生名或手机号"
  126. >
  127. <template #action>
  128. <div @click="onSearch">搜索</div>
  129. </template>
  130. </van-search>
  131. </van-sticky>
  132. <div class="paddingB80">
  133. <van-list
  134. v-model="loading"
  135. class="studentContainer"
  136. v-if="dataShow"
  137. key="data"
  138. :finished="finished"
  139. finished-text=""
  140. @load="getStudent"
  141. >
  142. <van-radio-group v-model="radioSelect">
  143. <van-cell-group>
  144. <van-cell
  145. v-for="(item, index) in dataList"
  146. :key="index"
  147. @click="onCheckboxSelect(item)"
  148. class="input-cell"
  149. :center="true"
  150. >
  151. <template slot="icon">
  152. <img
  153. class="logo"
  154. v-if="item.avatar"
  155. :src="item.avatar"
  156. alt=""
  157. />
  158. <img
  159. class="logo"
  160. v-else
  161. src="@/assets/images/icon_student.png"
  162. alt=""
  163. />
  164. </template>
  165. <template slot="title">
  166. <div class="studentName">
  167. {{ item.userName }}
  168. </div>
  169. </template>
  170. <template slot="label">
  171. <span>{{ desensitPhone(item.phone) }}</span>
  172. </template>
  173. <template slot="default">
  174. <van-radio :name="item.userId"></van-radio>
  175. </template>
  176. </van-cell>
  177. </van-cell-group>
  178. </van-radio-group>
  179. </van-list>
  180. <m-empty class="empty" v-else key="data" />
  181. </div>
  182. <div class="button-group-popup">
  183. <span class="btn" @click="onPopupCancel">取消</span>
  184. <span class="btn primary" @click="onPopupSubmit">确认选择</span>
  185. </div>
  186. </van-popup>
  187. </div>
  188. </template>
  189. <script>
  190. import MHeader from "@/components/MHeader";
  191. import { browser } from "@/common/common";
  192. import MEmpty from "@/components/MEmpty";
  193. import dayjs from "dayjs";
  194. import {
  195. queryStudentsWithTeacher,
  196. visitAdd,
  197. visitGetInfo,
  198. queryUserById
  199. } from "@/api/teacher";
  200. import { queryUserInfo } from "@/api/app";
  201. import setLoading from "@/utils/loading";
  202. export default {
  203. name: "addVisit",
  204. components: {
  205. MHeader,
  206. MEmpty,
  207. },
  208. data() {
  209. const query = this.$route.query;
  210. return {
  211. id: query.id,
  212. name: query.name,
  213. userId: query.userId, // 如果有userId的时候说明是从评测详情进来的
  214. studentId: query.studentId, // 这个参数是从原生传过来的,单独做了处理
  215. beforeId: query.beforeId, // 如果有beforeId的时候说明是从待回访列表进来的
  216. inside: query.inside || 0,
  217. visitFlag: Number(query.visitFlag) || 0,
  218. dataForm: {
  219. // 时间下拉框
  220. status: false,
  221. minDate: new Date(2000, 0, 1),
  222. maxDate: new Date(),
  223. currentDate: new Date(),
  224. },
  225. statusList: {
  226. // 散状态集合
  227. headerStatus: true, // 头部是否展示
  228. studentStatus: false, // 上课学生状态
  229. },
  230. typeList: [{ name: "课程推荐" }, { name: "常规回访" }, { name: "团练宝" }, { name: "其它" }],
  231. visit: {
  232. status: false,
  233. type: null,
  234. data: [],
  235. },
  236. studentName: query.username || null,
  237. studentPhone: query.phone || null,
  238. teacherName: null,
  239. form: {
  240. teacherId: null,
  241. studentId: query.userId || query.studentId || query.beforeId || null,
  242. type: query.userId ? '团练宝' : query.beforeId ? '常规回访' : null,
  243. purpose: query.userId ? '体验回访' : query.beforeId ? '教学内容未达标' : null,
  244. overview: "",
  245. feedback: "",
  246. visitTime: query.userId || query.id || query.beforeId ? dayjs().format("YYYY年MM月DD日") : null,
  247. visiterType: "TEACHER",
  248. },
  249. loading: false,
  250. finished: false,
  251. params: {
  252. search: null,
  253. page: 1,
  254. rows: 20,
  255. },
  256. dataShow: true, // 是否有数据
  257. radioSelect: null,
  258. radioSelectName: null,
  259. radioSelectPhone: null,
  260. clickStatus: false,
  261. dataList: [],
  262. searchIcon: require("@/assets/images/search.png"),
  263. };
  264. },
  265. mounted() {
  266. let params = this.$route.query;
  267. if (params.Authorization) {
  268. localStorage.setItem("Authorization", decodeURI(params.Authorization));
  269. localStorage.setItem("userInfo", decodeURI(params.Authorization));
  270. }
  271. if (browser().android || browser().iPhone) {
  272. this.statusList.headerStatus = false;
  273. }
  274. document.title = this.name || '新增回访记录';
  275. this.__init();
  276. },
  277. methods: {
  278. async __init() {
  279. let res = await queryUserInfo();
  280. let result = res.data;
  281. if (res.status == 200) {
  282. this.teacherName = result.realName;
  283. this.form.teacherId = result.id;
  284. } else {
  285. this.$toast(res.msg);
  286. }
  287. if (this.id) {
  288. setLoading(true);
  289. let queryInfo = await visitGetInfo({ id: this.id });
  290. const queryResult = queryInfo.data;
  291. let form = this.form;
  292. setLoading(false);
  293. if (queryResult.code == 200) {
  294. let tempData = queryResult.data;
  295. this.studentName = tempData.studentName;
  296. form.studentId = tempData.studentId;
  297. form.type = tempData.type;
  298. form.purpose = tempData.purpose;
  299. form.overview = tempData.overview;
  300. form.feedback = tempData.feedback;
  301. form.visitTime = dayjs(tempData.visitTime).format("YYYY年MM月DD日");
  302. form.visiterType = tempData.visiterType;
  303. } else {
  304. this.$toast(res.msg);
  305. }
  306. }
  307. let userId = this.userId || this.studentId || this.beforeId;
  308. if(userId) {
  309. await queryUserById({ userId }).then(res => {
  310. let result = res.data
  311. this.studentPhone = result.phone
  312. this.studentName = result.username
  313. })
  314. }
  315. },
  316. async onSubmit() {
  317. let form = this.form;
  318. if (!form.studentId) {
  319. this.$toast("请选择学员");
  320. return;
  321. } else if (!form.type) {
  322. this.$toast("请选择回访类型");
  323. return;
  324. } else if (!form.purpose) {
  325. this.$toast("请选择回访目的");
  326. return;
  327. } else if (!form.overview) {
  328. this.$toast("请输入当前学生情况");
  329. return;
  330. } else if (!form.feedback) {
  331. this.$toast("请输入沟通后家长反馈");
  332. return;
  333. } else if (!form.visitTime) {
  334. this.$toast("请选择回访时间");
  335. return;
  336. }
  337. if (this.clickStatus) {
  338. return;
  339. }
  340. this.clickStatus = true;
  341. setLoading(true);
  342. // let visitTime = this.form.visitTime.replace(/[^\d]/g,'/');
  343. // let someDate = new Date(visitTime)
  344. let visitTime = dayjs(this.dataForm.currentDate).format("YYYY-MM-DD")
  345. let params = {
  346. ...form,
  347. visitTime
  348. }
  349. // 待回访时要传的参数
  350. if(this.beforeId) {
  351. params.objectId = this.$route.query.objectId
  352. }
  353. let res = await visitAdd({ ...params });
  354. let result = res.data;
  355. setLoading(false);
  356. if (result.code == 200) {
  357. this.$toast("添加成功");
  358. setTimeout(() => {
  359. if(this.inside) {
  360. this.onAppBack()
  361. } else if(this.userId) {
  362. let { visitFlag ,...query } = this.$route.query
  363. visitFlag = 0
  364. this.$router.replace({
  365. path: '/trainDetail',
  366. query: {
  367. ...query,
  368. visitFlag
  369. }
  370. });
  371. } else if(this.beforeId) {
  372. let { ...query } = this.$route.query
  373. this.$router.replace({
  374. path: '/visitList',
  375. query: {
  376. userId: this.beforeId,
  377. ...query
  378. }
  379. });
  380. } else {
  381. this.$router.replace("visitList");
  382. }
  383. }, 800);
  384. } else {
  385. this.$toast(result.msg);
  386. this.clickStatus = false;
  387. return;
  388. }
  389. },
  390. onAppBack() {
  391. if (browser().android) {
  392. DAYA.postMessage(JSON.stringify({ api: "back" }));
  393. } else if (browser().iPhone) {
  394. window.webkit.messageHandlers.DAYA.postMessage(
  395. JSON.stringify({ api: "back" })
  396. );
  397. }
  398. },
  399. onCheckStudent() {
  400. if (this.id || this.userId || this.studentId || this.beforeId) {
  401. return;
  402. }
  403. this.statusList.studentStatus = true;
  404. },
  405. onChange(type) {
  406. if (this.id || this.userId || this.beforeId) {
  407. return;
  408. }
  409. let visit = this.visit;
  410. let form = this.form;
  411. if (type == "type") {
  412. visit.data = this.typeList;
  413. } else if (type == "purpose") {
  414. if (form.type == "其它") {
  415. visit.data = [{ name: "其它" }];
  416. } else if (form.type == "课程推荐") {
  417. visit.data = [{ name: "新课推荐" }, { name: "续费提醒" }];
  418. } else if (form.type == "常规回访") {
  419. visit.data = [{ name: "课后及作业回访" }, { name: "练习及乐团表现" }, { name: "教学内容未达标" },];
  420. } else if(form.type == '团练宝') {
  421. visit.data = [{ name: "体验回访" }]
  422. } else {
  423. this.$toast("请选择回访类型");
  424. return;
  425. }
  426. }
  427. visit.status = true;
  428. visit.type = type;
  429. },
  430. onSearch() {
  431. this.params.page = 1;
  432. this.dataList = [];
  433. this.dataShow = true;
  434. this.loading = true;
  435. this.finished = false;
  436. this.getStudent();
  437. },
  438. onCheckboxSelect(item) {
  439. this.radioSelect = item.userId;
  440. this.radioSelectName = item.userName;
  441. this.radioSelectPhone = item.phone
  442. },
  443. onPopupCancel() {
  444. this.statusList.studentStatus = false;
  445. },
  446. onPopupSubmit() {
  447. this.form.studentId = this.radioSelect;
  448. this.studentName = this.radioSelectName;
  449. this.studentPhone = this.radioSelectPhone;
  450. this.statusList.studentStatus = false;
  451. },
  452. onCurrentConfirm(value) {
  453. if (value) {
  454. this.form.visitTime = dayjs(value).format("YYYY年MM月DD日");
  455. }
  456. this.dataForm.status = false;
  457. },
  458. onEnListShow() {
  459. // 从团练宝统计来的,不许改时间,默认当前时间
  460. if (this.id || this.visitFlag) {
  461. return;
  462. }
  463. this.dataForm.status = true;
  464. },
  465. getStudent() {
  466. let params = this.params;
  467. queryStudentsWithTeacher(params).then((res) => {
  468. let result = res.data;
  469. this.loading = false;
  470. if (result.code == 200 && result.data) {
  471. params.page = result.data.pageNo;
  472. this.dataList = this.dataList.concat(result.data.rows);
  473. if (params.page >= result.data.totalPage) {
  474. this.finished = true;
  475. }
  476. this.params.page++;
  477. } else {
  478. this.finished = true;
  479. }
  480. // 判断是否有数据
  481. if (this.dataList.length <= 0) {
  482. this.dataShow = false;
  483. }
  484. });
  485. },
  486. onModeSelect(value) {
  487. let visit = this.visit;
  488. let form = this.form;
  489. if (visit.type == "type") {
  490. form.type = value.name;
  491. form.purpose = null;
  492. } else if (visit.type == "purpose") {
  493. form.purpose = value.name;
  494. }
  495. visit.status = false;
  496. },
  497. formatter(type, value) {
  498. if (type === "year") {
  499. return `${value}年`;
  500. } else if (type === "month") {
  501. return `${value}月`;
  502. } else if (type === "day") {
  503. return `${value}日`;
  504. }
  505. return value;
  506. },
  507. desensitPhone(phone) {
  508. // 手机号脱敏
  509. let first = phone.substr(0, 3);
  510. let last = phone.substr(-4);
  511. return first + "****" + last;
  512. },
  513. },
  514. };
  515. </script>
  516. <style lang='less' scoped>
  517. @import url("../../assets/commonLess/variable.less");
  518. /deep/.van-popup--bottom {
  519. border-radius: 0px 0px 0px 0px!important;
  520. overflow: auto!important;
  521. }
  522. .addVisit {
  523. min-height: 100vh;
  524. }
  525. .vip-title {
  526. padding: .06rem .14rem .04rem;
  527. font-size: .14rem;
  528. color: #808080;
  529. }
  530. /deep/.van-cell-group {
  531. margin-bottom: 0.1rem;
  532. }
  533. /deep/.van-cell {
  534. display: flex;
  535. align-items: center;
  536. font-size: 0.16rem;
  537. line-height: 0.28rem;
  538. }
  539. /deep/.van-field__label,
  540. /deep/.van-cell__value {
  541. flex: 1 auto;
  542. }
  543. /deep/.van-field__word-limit {
  544. margin-top: 0px;
  545. position: absolute;
  546. top: -0.2rem;
  547. right: 5px;
  548. font-size: 0.14rem;
  549. }
  550. /deep/.van-field__control:disabled {
  551. color: #6a6969;
  552. }
  553. .textarea {
  554. display: flex;
  555. flex-direction: column;
  556. align-items: inherit;
  557. padding: 0.1rem 0.21rem;
  558. /deep/.van-field__label {
  559. width: 100%;
  560. }
  561. // /deep/.van-field__value {
  562. // border: 1px solid #ccc;
  563. // }
  564. }
  565. .button-group {
  566. margin: 0.3rem 0.26rem 0.2rem;
  567. .van-button--primary {
  568. background: @mColor;
  569. border: 1px solid @mColor;
  570. font-size: 0.18rem;
  571. height: 0.5rem;
  572. }
  573. }
  574. .studentContainer {
  575. /deep/.van-cell__title {
  576. font-size: 0.14rem;
  577. color: @mFontColor;
  578. // flex: 1 auto;
  579. }
  580. .logo {
  581. width: 0.42rem;
  582. height: 0.42rem;
  583. margin-right: 0.12rem;
  584. border-radius: 100%;
  585. }
  586. .input-cell {
  587. padding: 0.2rem 0.16rem;
  588. .van-radio {
  589. justify-content: flex-end;
  590. }
  591. }
  592. /deep/.van-cell__value {
  593. height: 0.2rem;
  594. }
  595. /deep/.van-radio__icon .van-icon {
  596. border-color: #d3d3d3;
  597. }
  598. /deep/.van-radio__icon--checked {
  599. .van-icon {
  600. border-color: @mColor;
  601. background: @mColor;
  602. }
  603. }
  604. .van-tag {
  605. margin-left: 0.08rem;
  606. }
  607. }
  608. /deep/.van-field__right-icon, .phone_section {
  609. display: flex;
  610. }
  611. .iconPhone {
  612. width: .17rem;
  613. height: .21rem;
  614. }
  615. .paddingB80 {
  616. padding-bottom: 0.8rem;
  617. }
  618. .button-group-popup {
  619. position: fixed;
  620. bottom: 0;
  621. padding: 0.2rem 0;
  622. width: 100%;
  623. text-align: center;
  624. background-color: #ffffff;
  625. .btn {
  626. line-height: 0.5rem;
  627. display: inline-block;
  628. border: 1px solid @mColor;
  629. width: 1.65rem;
  630. border-radius: 0.4rem;
  631. color: @mColor;
  632. background: #fff;
  633. font-size: 0.18rem;
  634. &.primary {
  635. color: #fff;
  636. background: @mColor;
  637. }
  638. }
  639. .btn + .btn {
  640. margin-left: 0.1rem;
  641. }
  642. }
  643. .studentColor {
  644. color: @mColor;
  645. }
  646. .visiTimer {
  647. /deep/.van-field__control {
  648. color: #333333 !important;
  649. font-size: 0.16rem;
  650. }
  651. }
  652. .studentName {
  653. font-size: 0.16rem;
  654. color: #1a1a1a;
  655. line-height: 0.22rem;
  656. }
  657. .dot {
  658. width: 4px;
  659. height: 0.17rem;
  660. background: #01c1b5;
  661. border-radius: 3px;
  662. position: absolute;
  663. z-index: 200;
  664. top: 0.14rem;
  665. left: 0.12rem;
  666. }
  667. .van-icon-search {
  668. color: @mColor;
  669. }
  670. </style>