appoint-record.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. <template>
  2. <div class="container">
  3. <FullCalendar
  4. ref="fullCalendar"
  5. class="calendar-container-calendar"
  6. :options="calendarOptions"
  7. >
  8. <template v-slot:eventContent="arg">
  9. <!-- 弹窗信息 -->
  10. <el-popover
  11. trigger="click"
  12. popper-class="fullcalendar-popover"
  13. placement="right"
  14. :width="268"
  15. popper-style="padding: 0;border:none;background:none"
  16. >
  17. <!-- 预约信息 -->
  18. <template #reference>
  19. <div
  20. class="st1 cursor-pointer"
  21. :class="'status' + arg.event.extendedProps.status"
  22. >
  23. <span class="mr8">{{ arg.event.extendedProps.projectName }}</span>
  24. <span>
  25. {{ parseTime(arg.event.start, "HH:mm") }} -
  26. {{ parseTime(arg.event.end, "HH:mm") }}
  27. </span>
  28. </div>
  29. </template>
  30. <!-- 预约详细信息 -->
  31. <template #default>
  32. <div class="popover-def-header">
  33. <div class="name mt7">
  34. <span class="mr2">{{ arg.event.extendedProps.instName }}</span>
  35. 预约信息
  36. </div>
  37. <div class="date mt8 pb25">
  38. <span class="mr2">{{ changeTime(arg.event.start) }}</span>
  39. <span class="mr2">({{ getWeek(arg.event.start) }})</span>
  40. <span>
  41. {{ parseTime(arg.event.start, "HH:mm") }} ~
  42. {{ parseTime(arg.event.end, "HH:mm") }}
  43. </span>
  44. </div>
  45. </div>
  46. <div class="popover-def-content">
  47. <div class="content-felx mb12">
  48. <p class="mx-1" tag="b" size="small">
  49. 地点: {{ arg.event.extendedProps.placeAddress }}
  50. </p>
  51. </div>
  52. <div class="content-felx mb12">
  53. <p class="mx-1" size="small">
  54. 课题组:{{ arg.event.extendedProps.projectName }}
  55. </p>
  56. </div>
  57. <div class="content-felx mb12">
  58. <p class="mx-1" size="small">
  59. 预约人: {{ arg.event.extendedProps.userName }}
  60. </p>
  61. </div>
  62. </div>
  63. </template>
  64. </el-popover>
  65. </template>
  66. </FullCalendar>
  67. </div>
  68. </template>
  69. <script>
  70. import to from "await-to-js";
  71. import FullCalendar from "@fullcalendar/vue";
  72. import dayGridPlugin from "@fullcalendar/daygrid";
  73. import timeGridPlugin from "@fullcalendar/timegrid";
  74. import interactionPlugin from "@fullcalendar/interaction";
  75. import { getAppointListByInst } from "@/api/instr/index";
  76. import moment from "moment";
  77. export default {
  78. name: "equipment",
  79. components: {
  80. FullCalendar,
  81. },
  82. data() {
  83. return {
  84. recordData: [],
  85. searchForm: {
  86. instId: 0,
  87. startTimeEnd: "",
  88. startTimeStart: "",
  89. },
  90. calendarOptions: {
  91. plugins: [
  92. dayGridPlugin,
  93. timeGridPlugin,
  94. interactionPlugin, // needed for dateClick
  95. ],
  96. height: 600,
  97. headerToolbar: {
  98. left: null,
  99. center: "prev,title,next,today",
  100. right: null,
  101. },
  102. customButtons: {
  103. prev: {
  104. click: () => {
  105. this.prevWeekClick();
  106. },
  107. },
  108. next: {
  109. click: () => {
  110. this.nextWeekClick();
  111. },
  112. },
  113. today: {
  114. text: "本周",
  115. click: () => {
  116. this.todayWeekClick();
  117. },
  118. },
  119. },
  120. locale: "zh-cn", // 切换语言,当前为中文
  121. // headerToolbar: false,
  122. headerToolbar: {
  123. left: null,
  124. center: "prev,title,next,today",
  125. right: null,
  126. },
  127. events: [], //事件事件+文本
  128. initialView: "timeGridWeek", // 默认为那个视图(月:dayGridMonth,周:timeGridWeek,日:timeGridDay)
  129. selectMirror: true,
  130. dayMaxEvents: true,
  131. firstDay: 1, //new Date().getDay(), // 设置一周中显示的第一天是哪天,周日是0,周一是1,类推 new Date().getDay()当前天
  132. slotLabelFormat: {
  133. hour: "2-digit",
  134. minute: "2-digit",
  135. meridiem: false,
  136. hour12: false, // 设置时间为24小时
  137. },
  138. slotMinTime: "00:00:00",
  139. slotMaxTime: "24:00:00",
  140. allDaySlot: false, //是否显示日历上方的allDay
  141. moreLinkClassNames: "more-btns",
  142. },
  143. currentEvents: [],
  144. timeSplit: 10,
  145. recordData: [],
  146. curTime: "",
  147. };
  148. },
  149. created() {},
  150. mounted() {
  151. this.curTime = this.getToday();
  152. this.$nextTick(async () => {
  153. this.searchForm.instId = this.$route.query.id * 1;
  154. this.searchForm.startTimeStart = this.getWeekDate()[0];
  155. this.searchForm.startTimeEnd = this.getWeekDate()[1];
  156. await this.getAppointData();
  157. let calendarFunc = this.$refs["fullCalendar"].getApi().view.calendar;
  158. calendarFunc.setOption(
  159. "slotDuration",
  160. this.setSplitTime(this.timeSplit)
  161. );
  162. this.initData();
  163. });
  164. },
  165. methods: {
  166. getToday() {
  167. return moment(moment().startOf("day").valueOf()).format("YYYY-MM-DD");
  168. },
  169. getWeekDate() {
  170. // 获取当前周的周一0点
  171. var startOfWeek = moment(this.curTime)
  172. .startOf("isoWeek")
  173. .format("YYYY-MM-DD HH:mm:ss");
  174. // 获取下一周的周一0点
  175. var startOfNextWeek = moment(this.curTime)
  176. .startOf("isoWeek")
  177. .add(1, "weeks")
  178. .format("YYYY-MM-DD HH:mm:ss");
  179. return [startOfWeek, startOfNextWeek];
  180. },
  181. async getAppointData() {
  182. const [err, res] = await to(getAppointListByInst(this.searchForm));
  183. if (err) return;
  184. if (res.code == 200) {
  185. this.recordData = res.data.list;
  186. }
  187. },
  188. // 上周点击
  189. async prevWeekClick() {
  190. let calendarApi = this.$refs["fullCalendar"].getApi();
  191. calendarApi.prev();
  192. this.curTime = moment(calendarApi.getCurrentData().currentDate).format(
  193. "YYYY-MM-DD"
  194. );
  195. this.searchForm.startTimeStart = this.curTime + " 00:00:00";
  196. this.searchForm.startTimeEnd = this.getNextWeekDays() + " 00:00:00";
  197. await this.getAppointData();
  198. this.initData();
  199. },
  200. // 下周点击
  201. async nextWeekClick() {
  202. let calendarApi = this.$refs["fullCalendar"].getApi();
  203. calendarApi.next();
  204. this.curTime = moment(calendarApi.getCurrentData().currentDate).format(
  205. "YYYY-MM-DD"
  206. );
  207. this.searchForm.startTimeStart = this.curTime + " 00:00:00";
  208. this.searchForm.startTimeEnd = this.getNextWeekDays() + " 00:00:00";
  209. await this.getAppointData();
  210. this.initData();
  211. },
  212. // 今天点击
  213. async todayWeekClick() {
  214. let calendarApi = this.$refs["fullCalendar"].getApi();
  215. calendarApi.today();
  216. const date = moment(calendarApi.getCurrentData().currentDate).format(
  217. "YYYY-MM-DD"
  218. );
  219. const currentMonday = moment(date).startOf("isoWeek");
  220. const nextMonday = currentMonday.clone().add(1, "week");
  221. this.searchForm.startTimeStart =
  222. currentMonday.format("YYYY-MM-DD") + " 00:00:00";
  223. this.searchForm.startTimeEnd =
  224. nextMonday.format("YYYY-MM-DD") + " 00:00:00";
  225. await this.getAppointData();
  226. this.initData();
  227. },
  228. initData() {
  229. const filterArr = ["10", "20", "50", "60"];
  230. let appointData = [];
  231. let status = 1;
  232. this.recordData
  233. .filter((item) => filterArr.includes(item.appointStatus))
  234. .map((item) => {
  235. appointData.push({
  236. ...item,
  237. status,
  238. start: item.startTime,
  239. end: item.endTime,
  240. });
  241. status++;
  242. if (status > 3) {
  243. status = 1;
  244. }
  245. });
  246. let calendarApi = this.$refs["fullCalendar"].getApi();
  247. let calendarFunc = calendarApi.view.calendar;
  248. calendarFunc.unselect();
  249. let getEvents = calendarFunc.getEvents();
  250. if (getEvents && getEvents.length > 0) {
  251. //如果日历看板之前有数据,那么删除之前的数据
  252. getEvents.map((item) => {
  253. calendarFunc.getEventById(item.id).remove();
  254. });
  255. }
  256. appointData.map((item) => {
  257. calendarFunc.addEvent(item); //数据填充到日历看板中
  258. });
  259. // calendarApi.gotoDate(new Date(this.curTime));
  260. },
  261. // 设置一格时间槽代表多长时间
  262. setSplitTime(time) {
  263. let split_time = "";
  264. if (time < 60) {
  265. split_time = "00:" + (time < 10 ? "0" + time : time) + ":00";
  266. } else if (time == 60) {
  267. split_time = "01:00:00";
  268. }
  269. return split_time;
  270. },
  271. parseTime(date, format) {
  272. return moment(date).format(format);
  273. },
  274. setDiffTime(row) {
  275. // 计算时间差
  276. const startTime = moment(row.start);
  277. const endTime = moment(row.end);
  278. return (
  279. moment.duration(endTime.diff(startTime)).asHours().toFixed(1) + "h"
  280. );
  281. },
  282. changeTime(date) {
  283. const time = moment(date).format("MM:DD").split(":");
  284. return `${time[0]}月${time[1]}日`;
  285. },
  286. getWeek(date) {
  287. // 参数时间戳
  288. let week = moment(date).day();
  289. switch (week) {
  290. case 1:
  291. return "周一";
  292. case 2:
  293. return "周二";
  294. case 3:
  295. return "周三";
  296. case 4:
  297. return "周四";
  298. case 5:
  299. return "周五";
  300. case 6:
  301. return "周六";
  302. case 0:
  303. return "周日";
  304. }
  305. },
  306. // // 获取上一周
  307. getPrevWeekDays() {
  308. return moment(this.curTime).subtract(1, "week").format("YYYY-MM-DD");
  309. },
  310. // // 获取下一周
  311. getNextWeekDays() {
  312. return moment(this.curTime).add(1, "week").format("YYYY-MM-DD");
  313. },
  314. },
  315. };
  316. </script>
  317. <style lang="scss" scoped>
  318. :deep(.fc-day-disabled) {
  319. opacity: 0.5;
  320. pointer-events: none;
  321. }
  322. :deep(.fc-cell-not-allowed) {
  323. background-color: #eee;
  324. }
  325. :deep(.fc-toolbar-chunk) {
  326. div {
  327. display: flex;
  328. }
  329. }
  330. :deep(.fc-toolbar-title) {
  331. font-size: 1.75em;
  332. margin: 0;
  333. width: 320px;
  334. text-align: center;
  335. }
  336. :deep(.borderBlue) {
  337. border-left: 5px solid blue !important;
  338. border-radius: 0;
  339. }
  340. :deep(.borderOrange) {
  341. border-left: 5px solid yellow !important;
  342. border-radius: 0;
  343. }
  344. :deep(.fc) {
  345. .fc-day-today {
  346. background: unset;
  347. }
  348. }
  349. :deep(.fc-past-event) {
  350. background-color: #e3e3e3;
  351. }
  352. :deep(.fc-today-button) {
  353. margin-left: 10px;
  354. }
  355. :deep(.fc .fc-toolbar.fc-header-toolbar) {
  356. margin-bottom: 20px;
  357. }
  358. :deep(.fc .fc-timegrid-slot) {
  359. height: 2em;
  360. }
  361. :deep(.fc-col-header-cell-cushion) {
  362. font-size: 15px !important;
  363. color: #333333 !important;
  364. font-weight: bold !important;
  365. }
  366. :deep(.fc-timeGridDay-view) {
  367. .fc-day-today {
  368. background: none;
  369. }
  370. .st1 {
  371. border-top: none;
  372. display: block;
  373. }
  374. .fc-timegrid-col-events {
  375. margin: 0;
  376. }
  377. .fc-v-event {
  378. background: none;
  379. width: 50%;
  380. border: none;
  381. }
  382. }
  383. :deep(.fc-timeGridWeek-view) {
  384. .fc-day-today {
  385. background: none;
  386. }
  387. .st1 {
  388. border-left: none;
  389. display: block;
  390. }
  391. .fc-timegrid-col-events {
  392. margin: 0;
  393. }
  394. .fc-v-event {
  395. background: none;
  396. width: 100%;
  397. border: none;
  398. }
  399. }
  400. :deep(.fc-dayGridMonth-view) {
  401. .fc-day {
  402. height: 85px;
  403. }
  404. .st1 {
  405. border-top: none;
  406. }
  407. }
  408. :deep(.fc-popover-body) {
  409. .st1 {
  410. border-top: none;
  411. }
  412. }
  413. .st1 {
  414. display: flex;
  415. justify-content: space-between;
  416. width: 100%;
  417. height: 100%;
  418. border-left: 2px solid;
  419. border-top: 2px solid;
  420. padding-left: 7px;
  421. margin-bottom: 2px;
  422. }
  423. // :deep(.fc .fc-scroller-liquid-absolute) {
  424. // overflow: hidden !important;
  425. // }
  426. // :deep(.fc .fc-scroller) {
  427. // overflow: hidden !important;
  428. // }
  429. :deep(.fc .fc-daygrid-more-link) {
  430. color: #b5c1d8 !important;
  431. }
  432. :deep(.fc-h-event) {
  433. border: none;
  434. }
  435. :deep(.fc-daygrid-day-number) {
  436. color: #585858;
  437. }
  438. :deep(.fc .fc-timegrid-slot-label) {
  439. color: #585858;
  440. }
  441. :deep(.fc .fc-daygrid-day-top) {
  442. flex-direction: row;
  443. }
  444. :deep(.fc .fc-daygrid-day.fc-day-today) {
  445. background: none;
  446. .fc-daygrid-day-number {
  447. background: #2c78ff;
  448. border-radius: 12px;
  449. color: #ffffff;
  450. }
  451. }
  452. :deep(.fc-daygrid-day-number) {
  453. margin: 3px 0 0 6px;
  454. }
  455. :deep(.fc-col-header-cell) {
  456. .fc-scrollgrid-sync-inner {
  457. height: 50px;
  458. display: flex;
  459. align-items: center;
  460. justify-content: center;
  461. .fc-col-header-cell-cushion {
  462. font-size: 13px;
  463. color: #585858;
  464. font-weight: 100;
  465. }
  466. }
  467. }
  468. .status1 {
  469. background: #fff6e2;
  470. border-color: #fdc33e;
  471. color: #bf743c;
  472. }
  473. .status2 {
  474. background: #f0faf2;
  475. border-color: #0dda83;
  476. color: #006e3f;
  477. }
  478. .status3 {
  479. background: #eef3fe;
  480. border-color: #2c78ff;
  481. color: #2c78ff;
  482. }
  483. .fullcalendar-popover {
  484. .popover-def-header {
  485. // width: 268px;
  486. padding: 8px 12px 0 14px;
  487. background: linear-gradient(241deg, #2c78ff 0%, #1c9bfd 100%);
  488. font-size: 12px;
  489. color: #ffffff;
  490. .header {
  491. display: flex;
  492. justify-content: end;
  493. }
  494. }
  495. .popover-def-content {
  496. padding: 12px 22px;
  497. background: #fff;
  498. .content-felx {
  499. display: flex;
  500. .icon-l {
  501. width: 16px;
  502. height: 16px;
  503. display: flex;
  504. align-items: center;
  505. justify-content: center;
  506. margin-right: 6px;
  507. }
  508. }
  509. }
  510. :deep(.el-popper__arrow) {
  511. opacity: 0;
  512. }
  513. }
  514. </style>