index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. <template>
  2. <div class="news">
  3. <div class="center-banner">
  4. <div class="content">
  5. <div class="user">
  6. <img v-if="userInfo.avatar" :src="userInfo.avatar" />
  7. <img v-else src="../../assets/img/default-avatar.png">
  8. <p>
  9. <span>Hi~{{ userInfo.nickName }}</span>
  10. <span>{{ userInfo.roleNames }}</span>
  11. <span>{{ userInfo.pgName || '未加入课题组' }}</span>
  12. </p>
  13. </div>
  14. <div class="btns">
  15. <p>安全积分:<span>100</span></p>
  16. <el-button size="medium"
  17. round>个人设置</el-button>
  18. </div>
  19. </div>
  20. </div>
  21. <div class="common-container">
  22. <el-container>
  23. <div class="left-tabs">
  24. <h4>个人中心</h4>
  25. <ul>
  26. <li
  27. v-for="(item, index) in options"
  28. :key="index"
  29. :class="{ active: $route.path === item.path }"
  30. @click="tabSelect(item, index)"
  31. >
  32. {{ item.label }}
  33. </li>
  34. </ul>
  35. </div>
  36. <router-view></router-view>
  37. </el-container>
  38. </div>
  39. <AppointCreate
  40. ref="appointCreateRef"
  41. @refresh="
  42. searchForm.pageNum = 1;
  43. getAppointList();
  44. "
  45. ></AppointCreate>
  46. <PersonalInfoDialog ref="personalInfoDialogRef" />
  47. </div>
  48. </template>
  49. <script>
  50. import Banner from "../../components/Banner";
  51. import LeftTabs from "@/components/LeftTabs";
  52. import RightContent from "@/components/RightContent";
  53. import FullCalendar from "@fullcalendar/vue";
  54. import dayGridPlugin from "@fullcalendar/daygrid";
  55. import interactionPlugin from "@fullcalendar/interaction";
  56. import { getNoticeList } from "@/api/login";
  57. import {
  58. getInstrList,
  59. getInstrListByUser,
  60. userCancelAppoint,
  61. } from "@/api/instr";
  62. import { mapGetters } from "vuex";
  63. import moment from "moment";
  64. import to from "await-to-js";
  65. import { getToken } from "@/utils/auth";
  66. import AppointCreate from "../equipment/components/appoint-create.vue";
  67. import PersonalInfoDialog from "@/components/PersonalInfo";
  68. export default {
  69. name: "PersonalCenter",
  70. components: {
  71. Banner,
  72. LeftTabs,
  73. RightContent,
  74. FullCalendar,
  75. AppointCreate,
  76. PersonalInfoDialog,
  77. },
  78. data() {
  79. return {
  80. active: -1,
  81. loading: true,
  82. selectTab: {},
  83. searchForm: {
  84. pageNum: 1,
  85. pageSize: 10,
  86. },
  87. total: 0,
  88. options: [
  89. {
  90. label: "工作台",
  91. path: '/personal-center/work'
  92. },
  93. {
  94. label: "预约信息",
  95. path: '/personal-center/appointment'
  96. },
  97. ],
  98. routeList: [
  99. {
  100. name: "首页",
  101. path: "/",
  102. },
  103. {
  104. name: "个人中心",
  105. },
  106. ],
  107. breadList: [],
  108. calendarOptions: {
  109. height: 300,
  110. plugins: [dayGridPlugin, interactionPlugin], // 需要用哪个插件引入后放到这个数组里
  111. initialDate: new Date(), // 日历第一次加载时显示的初始日期。可以解析为Date的任何职包括ISO8601日期字符串,例如"2014-02-01"。
  112. initialView: "dayGridMonth", // 日历加载时的初始视图,默认值为'dayGridMonth',可以为任何可用视图的值,如例如'dayGridWeek','timeGridDay','listWeek'
  113. locale: "zh-cn", // 设置日历的语言,中文为 “zh-cn”
  114. firstDay: "1", // 设置每周的第一天,默认值取决于当前语言环境,该值为代表星期几的数字,且值必须为整数,星期日=0
  115. weekNumberCalculation: "ISO", // 指定"ISO"结果为ISO8601周数。指定"ISO"将firstDay的默认值更改为1(Monday)
  116. buttonText: {
  117. // 文本将显示在headerToolbar / footerToolbar的按钮上。不支持HTML注入。所有特殊字符将被转义。
  118. today: "今天",
  119. month: "月",
  120. week: "周",
  121. day: "天",
  122. },
  123. headerToolbar: {
  124. // 在日历顶部定义按钮和标题。将headerToolbar选项设置为false不会显示任何标题工具栏。可以为对象提供属性start/center/end或left/center/right。这些属性包含带有逗号/空格分隔值的字符串。用逗号分隔的值将相邻显示。用空格分隔的值之间会显示一个很小的间隙。
  125. right: "today prev,next",
  126. center: "title",
  127. left: "dayGridMonth,dayGridWeek,dayGridDay",
  128. },
  129. eventTimeFormat: {
  130. // 在每个事件上显示的时间的格式
  131. hour: "numeric",
  132. minute: "2-digit",
  133. hour12: false,
  134. },
  135. events: [],
  136. dateClick: this.handleDateClick, // 当用户单击日期或时间时,触发该回调,触发此回调,您必须加载interaction插件
  137. },
  138. instrList: [],
  139. noticeList: [],
  140. curSelectedDate: "w",
  141. curTime: "",
  142. appointData: [],
  143. WEEKS: [
  144. "星期日",
  145. "星期一",
  146. "星期二",
  147. "星期三",
  148. "星期四",
  149. "星期五",
  150. "星期六",
  151. ],
  152. };
  153. },
  154. computed: {
  155. ...mapGetters(["userInfo"]),
  156. },
  157. mounted() {
  158. // this.tabSelect(this.options[0], 0);
  159. this.curTime = this.getToday();
  160. this.getNotice();
  161. this.getInstr();
  162. },
  163. methods: {
  164. moment,
  165. // 獲取當前日期
  166. getToday() {
  167. return moment(moment().startOf("day").valueOf()).format("YYYY-MM-DD");
  168. },
  169. getYesterday() {
  170. this.curTime = moment(
  171. moment(this.curTime).add(-1, "days").startOf("day").valueOf()
  172. ).format("YYYY-MM-DD");
  173. },
  174. getTomorrow() {
  175. this.curTime = moment(
  176. moment(this.curTime).add(+1, "days").startOf("day").valueOf()
  177. ).format("YYYY-MM-DD");
  178. },
  179. getPrevWeekDays() {
  180. this.curTime = moment(this.curTime)
  181. .subtract(1, "week")
  182. .format("YYYY-MM-DD");
  183. },
  184. getNextWeekDays() {
  185. this.curTime = moment(this.curTime).add(1, "week").format("YYYY-MM-DD");
  186. },
  187. getPrevMonthDays() {
  188. this.curTime = moment(this.curTime)
  189. .subtract(1, "months")
  190. .format("YYYY-MM-DD");
  191. },
  192. getNextMonthDays() {
  193. this.curTime = moment(this.curTime)
  194. .add(1, "months")
  195. .format("YYYY-MM-DD");
  196. },
  197. getWeekDate() {
  198. // 获取当前周的周一0点
  199. var startOfWeek = moment(this.curTime)
  200. .startOf("isoWeek")
  201. .format("YYYY-MM-DD HH:mm:ss");
  202. // 获取下一周的周一0点
  203. var startOfNextWeek = moment(this.curTime)
  204. .startOf("isoWeek")
  205. .add(1, "weeks")
  206. .format("YYYY-MM-DD HH:mm:ss");
  207. return [startOfWeek, startOfNextWeek];
  208. },
  209. getMonthDate() {
  210. const date = moment(this.curTime);
  211. // 获取当前月份的最后一天
  212. return [
  213. date.startOf("month").format("YYYY-MM-DD HH:mm:ss"),
  214. date.endOf("month").add(1, "days").format("YYYY-MM-DD") + " 00:00:00",
  215. ];
  216. },
  217. // 上一周、天、月
  218. handlePrev() {
  219. if (this.curSelectedDate == "d") {
  220. this.getYesterday();
  221. } else if (this.curSelectedDate == "w") {
  222. this.getPrevWeekDays();
  223. } else if (this.curSelectedDate == "m") {
  224. this.getPrevMonthDays();
  225. }
  226. this.getAppointList();
  227. },
  228. // 下一周、天、月
  229. handleNext() {
  230. if (this.curSelectedDate == "d") {
  231. this.getTomorrow();
  232. } else if (this.curSelectedDate == "w") {
  233. this.getNextWeekDays();
  234. } else if (this.curSelectedDate == "m") {
  235. this.getNextMonthDays();
  236. }
  237. this.getAppointList();
  238. },
  239. selectDate(date) {
  240. this.curSelectedDate = date;
  241. this.getAppointList();
  242. },
  243. async getAppointList() {
  244. let startTimeStart = "";
  245. let startTimeEnd = "";
  246. if (this.curSelectedDate == "d") {
  247. startTimeStart = moment(this.curTime).format("YYYY-MM-DD HH:mm:ss");
  248. startTimeEnd = moment(this.curTime)
  249. .add(1, "days")
  250. .format("YYYY-MM-DD HH:mm:ss");
  251. } else if (this.curSelectedDate == "w") {
  252. startTimeStart = this.getWeekDate()[0];
  253. startTimeEnd = this.getWeekDate()[1];
  254. } else if (this.curSelectedDate == "m") {
  255. startTimeStart = moment(this.curTime)
  256. .startOf("month")
  257. .format("YYYY-MM-DD HH:mm:ss");
  258. startTimeEnd = this.getMonthDate()[1];
  259. }
  260. console.log(startTimeStart);
  261. const [err, res] = await to(
  262. getInstrListByUser({
  263. startTimeStart,
  264. startTimeEnd,
  265. ...this.searchForm,
  266. })
  267. );
  268. if (err) return;
  269. this.appointData = res.data.list;
  270. this.total = res.data.total;
  271. },
  272. setStatus(key) {
  273. let str = "";
  274. switch (key) {
  275. case "10":
  276. str = "待审核";
  277. break;
  278. case "11":
  279. str = "已退回";
  280. break;
  281. case "20":
  282. str = "已通过";
  283. break;
  284. case "30":
  285. str = "已驳回";
  286. break;
  287. case "40":
  288. str = "已取消";
  289. break;
  290. case "50":
  291. str = "已上机";
  292. break;
  293. case "60":
  294. str = "已完成";
  295. break;
  296. case "70":
  297. str = "审核超时";
  298. break;
  299. case "80":
  300. str = "超时取消";
  301. break;
  302. case "90":
  303. str = "超时未上机";
  304. break;
  305. }
  306. return str;
  307. },
  308. getBreachTypes(row) {
  309. let breachTypes = [];
  310. if (row.isLate) breachTypes.push("迟到");
  311. if (row.isOvertime) breachTypes.push("超时");
  312. if (row.isLeaveEarly) breachTypes.push("早退");
  313. if (row.isAbsence) breachTypes.push("爽约");
  314. return breachTypes.join("、") || "-";
  315. },
  316. // 取消预约
  317. cancelAppoint(row) {
  318. // 删除附件
  319. this.$confirm("确认取消预约?", "提示", {
  320. confirmButtonText: "确认",
  321. cancelButtonText: "取消",
  322. type: "warning",
  323. })
  324. .then(async () => {
  325. const post = userCancelAppoint({ id: Number(row.id) });
  326. const [err] = await to(post);
  327. if (err) return;
  328. this.$message.success("取消成功");
  329. this.getAppointList();
  330. })
  331. .catch(() => {});
  332. },
  333. // 编辑预约
  334. handleEditAppoint(row) {
  335. const token = getToken();
  336. if (!token) {
  337. return this.$router.push("/login");
  338. }
  339. this.$refs.appointCreateRef.openDialog(row);
  340. },
  341. // 选择tab
  342. tabSelect(row, index) {
  343. this.active = index;
  344. this.selectTab = { ...row };
  345. this.breadList = [...this.routeList, { name: this.selectTab.label }];
  346. this.$router.push(row.path)
  347. },
  348. async getNotice() {
  349. const [err, res] = await to(getNoticeList());
  350. if (err) return;
  351. this.noticeList = res.data.list || [];
  352. },
  353. async getInstr() {
  354. const [err, res] = await to(getInstrList({ pageSize: 4, pageNum: 1 }));
  355. if (err) return;
  356. this.instrList = res.data.list || [];
  357. },
  358. handleDateClick(dateClickInfo) {
  359. console.log(dateClickInfo);
  360. },
  361. handleSizeChange(val) {
  362. this.searchForm.pageSize = val;
  363. this.getAppointList();
  364. },
  365. handleCurrentChange(val) {
  366. this.searchForm.pageNum = val;
  367. this.getAppointList();
  368. },
  369. },
  370. };
  371. </script>
  372. <style lang="scss" scoped>
  373. .center-banner {
  374. background: url(../../assets/img/center-banner.png) top no-repeat;
  375. background-size: 100% 240px;
  376. height: 240px;
  377. display: flex;
  378. justify-content: center;
  379. align-items: center;
  380. .content {
  381. height: 180px;
  382. width: 1200px;
  383. border-radius: 16px;
  384. background-color: rgba($color: #fff, $alpha: 0.5);
  385. display: flex;
  386. align-items: center;
  387. justify-content: space-between;
  388. padding: 0 40px;
  389. .user {
  390. display: flex;
  391. align-items: center;
  392. img {
  393. height: 100px;
  394. width: 100px;
  395. border-radius: 50%;
  396. }
  397. p {
  398. height: 100px;
  399. display: flex;
  400. flex-direction: column;
  401. justify-content: space-around;
  402. margin-left: 12px;
  403. span:nth-child(1) {
  404. font-weight: bold;
  405. font-size: 18px;
  406. }
  407. span:nth-child(3) {
  408. font-weight: bold;
  409. }
  410. }
  411. }
  412. .btns {
  413. display: flex;
  414. align-items: center;
  415. > p {
  416. display: flex;
  417. align-items: center;
  418. margin-right: 20px;
  419. line-height: 30px;
  420. font-weight: bold;
  421. span {
  422. font-size: 30px;
  423. font-weight: bold;
  424. color: #1d66dc;
  425. font-style: italic;
  426. }
  427. }
  428. }
  429. }
  430. }
  431. .flex {
  432. width: 100%;
  433. flex-wrap: wrap;
  434. .el-card {
  435. flex: 0 0 calc(50% - 14px);
  436. height: 400px;
  437. ::v-deep .el-card__body {
  438. padding: 10px;
  439. height: calc(100% - 79px);
  440. }
  441. &.calendar {
  442. height: 500px;
  443. ul {
  444. display: flex;
  445. flex-direction: column;
  446. justify-content: space-around;
  447. height: 130px;
  448. li {
  449. font-size: 14px;
  450. display: flex;
  451. align-items: center;
  452. &:before {
  453. content: "";
  454. display: inline-block;
  455. width: 6px;
  456. height: 6px;
  457. border-radius: 3px;
  458. background-color: #a30014;
  459. margin-right: 4px;
  460. }
  461. }
  462. }
  463. }
  464. &.cost {
  465. height: 300px;
  466. }
  467. }
  468. &.technical .el-card {
  469. height: auto;
  470. ::v-deep .el-card__body {
  471. height: auto;
  472. }
  473. }
  474. }
  475. .cage-list {
  476. display: flex;
  477. flex-wrap: wrap;
  478. list-style: none;
  479. li {
  480. height: 160px;
  481. width: 221px;
  482. font-size: 14px;
  483. border: 1px solid #ebeef5;
  484. border-radius: 6px;
  485. display: flex;
  486. flex-direction: column;
  487. justify-content: space-around;
  488. padding: 10px;
  489. &:nth-child(2n) {
  490. margin-left: 4px;
  491. }
  492. &:nth-child(n + 3) {
  493. margin-top: 4px;
  494. }
  495. header {
  496. display: flex;
  497. justify-content: space-between;
  498. p {
  499. color: #1d66dc;
  500. }
  501. }
  502. }
  503. }
  504. .platform-list {
  505. height: 100%;
  506. display: flex;
  507. flex-direction: column;
  508. li {
  509. flex: 1;
  510. display: flex;
  511. align-items: center;
  512. margin: 12px;
  513. border-radius: 8px;
  514. overflow: hidden;
  515. border: 1px solid #ebeef5;
  516. .text {
  517. flex: 1;
  518. height: 100%;
  519. padding: 12px;
  520. }
  521. .btn {
  522. width: 120px;
  523. height: 100%;
  524. color: #fff;
  525. font-size: 20px;
  526. background-color: #73b9b9;
  527. display: flex;
  528. flex-direction: column;
  529. align-items: center;
  530. justify-content: space-around;
  531. header {
  532. font-size: 14px;
  533. }
  534. .remain-time {
  535. color: #f56c6c;
  536. }
  537. }
  538. }
  539. }
  540. .chart-container {
  541. display: flex;
  542. .text {
  543. flex: 1;
  544. font-size: 14px;
  545. display: flex;
  546. flex-direction: column;
  547. header {
  548. color: #1d66dc;
  549. }
  550. ul {
  551. margin-top: 12px;
  552. flex: 1;
  553. display: flex;
  554. flex-direction: column;
  555. justify-content: space-around;
  556. }
  557. li {
  558. background-color: #e4b5bb;
  559. border-radius: 4px;
  560. padding: 4px 8px;
  561. }
  562. }
  563. #chart {
  564. width: 233px;
  565. height: 220px;
  566. }
  567. }
  568. .technical {
  569. p {
  570. font-size: 16px;
  571. height: 24px;
  572. line-height: 24px;
  573. }
  574. ul {
  575. display: flex;
  576. height: 50px;
  577. line-height: 50px;
  578. border-radius: 4px;
  579. overflow: hidden;
  580. margin: 20px 0;
  581. li {
  582. flex: 1;
  583. text-align: center;
  584. background-color: #d9fba5;
  585. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  586. &:nth-child(2) {
  587. background-color: #98ca49;
  588. }
  589. &:nth-child(3) {
  590. background-color: #a4dffa;
  591. }
  592. &:nth-child(4) {
  593. background-color: #48bff4;
  594. }
  595. &:nth-child(5) {
  596. background-color: #b6b6f1;
  597. }
  598. &:nth-child(6) {
  599. background-color: #ebcda7;
  600. }
  601. &:nth-child(7) {
  602. background-color: #f2a4ad;
  603. }
  604. }
  605. }
  606. }
  607. .toolbar-wrap {
  608. height: 30px;
  609. display: flex;
  610. justify-content: space-between;
  611. align-items: center;
  612. // margin-bottom: 20px;
  613. .switch-date {
  614. width: 108px;
  615. height: 28px;
  616. border-radius: 2px;
  617. border: 1px solid #b5c1d8;
  618. display: flex;
  619. padding: 4px 6px 0;
  620. .btn {
  621. cursor: pointer;
  622. flex: 1;
  623. font-size: 13px;
  624. color: #585858;
  625. display: flex;
  626. align-items: center;
  627. justify-content: center;
  628. &.actived {
  629. background: #eef3fe;
  630. font-weight: bold;
  631. color: #2c78ff;
  632. }
  633. }
  634. }
  635. .date-wrap {
  636. display: flex;
  637. align-items: center;
  638. :deep(.el-button--small.is-circle) {
  639. width: 26px;
  640. height: 26px;
  641. display: flex;
  642. align-items: center;
  643. justify-content: center;
  644. }
  645. .date {
  646. margin: 0 10px;
  647. font-size: 14px;
  648. color: #585858;
  649. display: flex;
  650. align-items: center;
  651. }
  652. .week {
  653. color: #2c78ff;
  654. padding-left: 6px;
  655. }
  656. }
  657. }
  658. .table-wrap {
  659. flex: 1;
  660. margin: 20px 0 0;
  661. // overflow: hidden;
  662. }
  663. .pagination {
  664. width: 100%;
  665. height: 50px;
  666. display: flex;
  667. align-items: center;
  668. justify-content: flex-end;
  669. }
  670. </style>