index.vue 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157
  1. <template>
  2. <div class="home-container">
  3. <el-row :gutter="6" style="height: 148px;">
  4. <el-col :span="24" class="box-col">
  5. <el-card class="box-card">
  6. <div slot="header" class="clearfix">
  7. <span>常用功能</span>
  8. <span style="float: right; padding: 3px 0">
  9. <i class="el-icon-edit" @click="handleEdit" style="cursor: pointer; margin-right: 16px; font-size: 18px;"></i>
  10. <i class="el-icon-setting" @click="handleAdd" style="cursor: pointer; font-size: 18px;"></i>
  11. </span>
  12. </div>
  13. <div class="card-content common-function-card">
  14. <vuedraggable v-model="commonFunctions" class="common-function">
  15. <div
  16. v-for="(item, index) in commonFunctions"
  17. :key="item.id"
  18. class="common-function-item"
  19. @click="handleDel(item, index)"
  20. :title="
  21. isDelFlag ? '点击删除' : ''
  22. "
  23. :class="
  24. isDelFlag ? 'div-del' : ''
  25. "
  26. >
  27. <!-- <div class="common-function-item-content"> -->
  28. <div class="common-function-item-icon">
  29. <!-- <i :class="item.icon || 'el-icon-s-opportunity'"></i> -->
  30. <img :src="getIconPath(item.pic)" alt="">
  31. </div>
  32. <div class="common-function-item-name">{{ item.name }}</div>
  33. <!-- </div> -->
  34. </div>
  35. </vuedraggable>
  36. </div>
  37. </el-card>
  38. </el-col>
  39. </el-row>
  40. <el-row :gutter="6" class="mt-20" style="height: 232px;">
  41. <el-col :span="18" class="box-col">
  42. <el-card class="box-card">
  43. <div slot="header" class="clearfix">
  44. <span>数据概览</span>
  45. </div>
  46. <div class="card-content">
  47. <el-row :gutter="20" class="overview-container">
  48. <el-col :span="8">
  49. <div class="overview">
  50. <div class="overview-top">
  51. <div class="overview-top-img"><img src="~@/assets/home/ceshi1.png" alt=""></div>
  52. <div class="overview-top-content">
  53. <div class="overview-top-content-name">我的项目</div>
  54. <div class="overview-top-content-count">{{ myProjectList['总数量'] || 0 }}</div>
  55. </div>
  56. </div>
  57. <el-divider></el-divider>
  58. <div class="overview-bottom">
  59. <div
  60. v-for="(value, key) in myProjectList"
  61. :key="key"
  62. v-if="key !== '总数量' && value != 0"
  63. class="overview-bottom-item"
  64. @click="handleToPro(key)"
  65. >
  66. <div class="overview-bottom-item-name">{{ key }}</div>
  67. <div class="overview-bottom-item-count">{{ value || 0 }}</div>
  68. </div>
  69. </div>
  70. </div>
  71. </el-col>
  72. <el-col :span="8">
  73. <div class="overview">
  74. <div class="overview-top">
  75. <div class="overview-top-img"><img src="~@/assets/home/ceshi.jpg" alt=""></div>
  76. <div class="overview-top-content">
  77. <div class="overview-top-content-name">我的工单</div>
  78. <div class="overview-top-content-count">{{ myWorkOrderList['总数量'] || 0 }}</div>
  79. </div>
  80. </div>
  81. <el-divider></el-divider>
  82. <div class="overview-bottom">
  83. <div
  84. v-for="(value, key) in myWorkOrderList"
  85. :key="key"
  86. v-if="key !== '总数量' && value != 0"
  87. class="overview-bottom-item"
  88. @click="handleTo(key)"
  89. >
  90. <div class="overview-bottom-item-name">{{ key }}</div>
  91. <div class="overview-bottom-item-count">{{ value || 0 }}</div>
  92. </div>
  93. </div>
  94. </div>
  95. </el-col>
  96. <el-col :span="8">
  97. <div class="overview">
  98. <div class="overview-top">
  99. <div class="overview-top-img"><img style="border-radius: 10px;" src="~@/assets/home/ceshi2.jpg" alt=""></div>
  100. <div class="overview-top-content">
  101. <div class="overview-top-content-name">我的任务</div>
  102. <div class="overview-top-content-count">{{ myTaskList['总数量'] || 0 }}</div>
  103. </div>
  104. </div>
  105. <el-divider></el-divider>
  106. <div class="overview-bottom">
  107. <div
  108. v-for="(value, key) in myTaskList"
  109. :key="key"
  110. v-if="key !== '总数量' && value != 0"
  111. class="overview-bottom-item"
  112. @click="handleTo(key)"
  113. >
  114. <div class="overview-bottom-item-name">{{ key }}</div>
  115. <div class="overview-bottom-item-count">{{ value || 0 }}</div>
  116. </div>
  117. </div>
  118. </div>
  119. </el-col>
  120. </el-row>
  121. </div>
  122. </el-card>
  123. </el-col>
  124. <el-col :span="6" class="box-col">
  125. <el-card class="box-card">
  126. <div slot="header" class="clearfix">
  127. <span>统一门户</span>
  128. </div>
  129. <div class="card-content portal-content">
  130. <div class="portal">
  131. <el-row :gutter="12">
  132. <el-col :span="8" v-for="item in portals" :key="item.name" class="portal-item">
  133. <div class="portal-item-content" @click="openUrl(item)">
  134. <div class="portal-item-img"><img :src="item.img" alt=""></div>
  135. <div class="portal-item-name">{{ item.name }}</div>
  136. </div>
  137. </el-col>
  138. </el-row>
  139. </div>
  140. </div>
  141. </el-card>
  142. </el-col>
  143. </el-row>
  144. <el-row :gutter="6" class="mt-20" style="height: 404px;">
  145. <el-col :span="18" class="box-col">
  146. <el-card class="box-card notification-card">
  147. <div class="card-content">
  148. <el-tabs v-model="activeTab" @tab-click="handleTabClick">
  149. <el-tab-pane label="通知公告" name="notice"></el-tab-pane>
  150. <!-- <el-tab-pane label="行业动态" name="industry"></el-tab-pane> -->
  151. <el-tab-pane label="系统通知" name="system"></el-tab-pane>
  152. </el-tabs>
  153. <div v-if="notifications.length > 0" class="notification-list">
  154. <div v-for="(item, index) in notifications" :key="index" class="notification-item" @click="handleNotificationsClick(item)">
  155. <div class="notification-dot"></div>
  156. <div class="notification-content">
  157. <div class="notification-header">
  158. <div class="notification-title">{{ item.title }}</div>
  159. <div v-if="activeTab == 'notice'" class="notification-text">
  160. {{ item.content }}
  161. <!-- <div v-if="item.images && item.images.length > 0" class="notification-images">
  162. <div v-for="(img, imgIndex) in item.images" :key="imgIndex" class="image-url">{{ img }}</div>
  163. </div> -->
  164. </div>
  165. <div v-else class="notification-text">{{ item.content }}</div>
  166. </div>
  167. <div class="notification-info">{{ item.createUser }} <span style="margin-left: 20px;">{{ item.department }}</span></div>
  168. </div>
  169. <div class="notification-date">{{ item.createTime }}</div>
  170. </div>
  171. <div v-if="hasMore" class="notification-more" @click="loadMoreNotifications">
  172. 更多......
  173. </div>
  174. </div>
  175. <div v-else class="no-notification">
  176. <el-empty description="暂无数据"></el-empty>
  177. </div>
  178. </div>
  179. </el-card>
  180. </el-col>
  181. <el-col :span="6" class="box-col">
  182. <el-card class="box-card">
  183. <div slot="header" class="clearfix">
  184. <span>工作日历</span>
  185. </div>
  186. <div class="card-content calendar-content">
  187. <el-calendar class="calendar" v-model="currentDate" :range="calendarRange">
  188. <template
  189. slot="dateCell"
  190. slot-scope="{date, data}">
  191. <div class="calendar-day-content" @click="handleDateClick(data.day)">
  192. <div :class="data.isSelected ? 'is-selected-day' : ''">{{ data.day.split('-')[2] }} </div>
  193. <div class="dot-container" v-if="hasSchedule(data.day)"><span class="dot"></span></div>
  194. </div>
  195. </template>
  196. </el-calendar>
  197. <div class="calendar-more">
  198. <!-- el-icon-caret-top -->
  199. <i :class="calendarShowMore ? 'el-icon-caret-top' : 'el-icon-caret-bottom'" style="font-size: 24px; color: #d7d7d7; cursor: pointer;" @click="handleCalendarMoreClick"></i>
  200. </div>
  201. <div class="schedule">
  202. <div class="schedule-item" v-for="(item, index) in getScheduleListByDate()" :key="index">
  203. <el-row :gutter="20">
  204. <el-col :span="10">
  205. <div class="schedule-item-time">
  206. <span>{{ item.startHour }}</span>
  207. <span>~</span>
  208. <span>{{ item.endHour }}</span>
  209. </div>
  210. </el-col>
  211. <el-col :span="14">
  212. <div class="schedule-item-name">
  213. {{ item.title || item.name || '日程安排' }}
  214. </div>
  215. </el-col>
  216. </el-row>
  217. </div>
  218. </div>
  219. </div>
  220. </el-card>
  221. </el-col>
  222. </el-row>
  223. <commonDialog
  224. ref="commonDialogRef"
  225. v-if="commonDialogFlag"
  226. :common-dialog-flag.sync="commonDialogFlag"
  227. @reload="getCommonFunctionsList"
  228. ></commonDialog>
  229. <noticeIssuanceDetail
  230. ref="noticeIssuanceDetailRef"
  231. ></noticeIssuanceDetail>
  232. </div>
  233. </template>
  234. <script>
  235. import { notifyMessagePageAPI } from '@/api/bpm/task';
  236. import {
  237. userResourceDeleteAPI,
  238. userResourceListAPI,
  239. getList,
  240. getMyProjectTotal,
  241. getMyWorkOrderTotal,
  242. getMyTaskTotal,
  243. getMyWorkCalendar
  244. } from '@/api/home';
  245. import { deepClone } from 'ele-admin/lib/utils/core';
  246. import { mapGetters } from 'vuex';
  247. import vuedraggable from 'vuedraggable';
  248. import commonDialog from '@/views/home/common-dialog.vue';
  249. import { noticeDocumentPageV1API } from '@/api/documents/noticeIssuance/index.js';
  250. import noticeIssuanceDetail from '@/views/bpm/documents/noticeIssuance/components/addOrEditDraw.vue';
  251. export default {
  252. name: 'HomePage',
  253. components: {
  254. vuedraggable,
  255. commonDialog,
  256. noticeIssuanceDetail
  257. },
  258. data() {
  259. return {
  260. currentDate: new Date(),
  261. calendarRange: [],
  262. commonFunctions: [],
  263. activeTab: 'notice',
  264. notifications: [],
  265. currentPage: 1,
  266. pageSize: 4,
  267. hasMore: true,
  268. portals: [],
  269. calendarShowMore: false,
  270. isDelFlag: false,
  271. commonDialogFlag: false,
  272. myProjectList: {},
  273. myWorkOrderList: {},
  274. myTaskList: {},
  275. myWorkCalendar: {},
  276. groupedWorkCalendar: {}, // 分组后的工作日历数据
  277. selectedScheduleDate: '', // 当前选中的日期
  278. };
  279. },
  280. computed: {
  281. ...mapGetters(['user']),
  282. currentNotifications() {
  283. const notifications = this.notifications[this.activeTab] || [];
  284. return notifications;
  285. }
  286. },
  287. watch: {
  288. currentDate: {
  289. handler(newVal, oldVal) {
  290. // 检查月份是否变化
  291. if (newVal.getMonth() !== oldVal.getMonth() || newVal.getFullYear() !== oldVal.getFullYear()) {
  292. // 重新计算日期范围
  293. this.calendarRange = this.getMonthRange();
  294. // 重新获取日程数据
  295. this.getMyWorkCalendar();
  296. }
  297. // 日期变化时,处理选中日期的日程
  298. if (newVal !== oldVal) {
  299. const year = newVal.getFullYear();
  300. const month = String(newVal.getMonth() + 1).padStart(2, '0');
  301. const day = String(newVal.getDate()).padStart(2, '0');
  302. const dateStr = `${year}-${month}-${day}`;
  303. this.handleDateClick(dateStr);
  304. }
  305. },
  306. deep: true
  307. }
  308. },
  309. created() {
  310. this.calendarRange = this.getWeekRange();
  311. this.getPortalsList();
  312. this.getCommonFunctionsList();
  313. this.getMyProjectTotal();
  314. this.getMyWorkOrderTotal();
  315. this.getMyTaskTotal();
  316. this.getMessageList();
  317. this.getMyWorkCalendar();
  318. },
  319. methods: {
  320. openUrl(item) {
  321. // 统一门户配置
  322. if (item.architType === '1') {
  323. // bs门户配置
  324. window.open(item.linkUrl);
  325. } else {
  326. let url = item.linkUrl.split('//');
  327. window.open(url[1], '_blank');
  328. }
  329. },
  330. handleNotificationsClick(item) {
  331. console.log('item~~~', item);
  332. if(this.activeTab === 'system') return;
  333. this.$refs.noticeIssuanceDetailRef.openDrawer({
  334. id: item.id,
  335. type: 'detail'
  336. });
  337. // this.$router.push({
  338. // // query
  339. // query: {
  340. // id: item.id,
  341. // type: 'detail'
  342. // }
  343. // });
  344. },
  345. async getMyWorkCalendar() {
  346. const res = await getMyWorkCalendar({
  347. startDate: this.calendarRange[0],
  348. endDate: this.calendarRange[1]
  349. });
  350. console.log('myWorkCalendar~~~', res);
  351. // 数据分组
  352. if (res) {
  353. const groupedData = {};
  354. res.forEach(item => {
  355. if (item.startTime) {
  356. // 分割 startTime 为日期和时间
  357. const dateTime = item.startTime.split(' ');
  358. const date = dateTime[0];
  359. const time = dateTime[1] || '';
  360. // 从 startTime 提取小时(去掉分秒)
  361. const startHourOnly = time.split(':')[0] || '';
  362. // 提取月日(去掉年份)
  363. const startDateParts = date.split('-');
  364. const startMonthDay = startDateParts[1] + '-' + startDateParts[2];
  365. // 从 endTime 提取小时
  366. let endHourOnly = '';
  367. let endMonthDay = '';
  368. if (item.endTime) {
  369. const endDateTime = item.endTime.split(' ');
  370. const endDate = endDateTime[0] || '';
  371. const endDateParts = endDate.split('-');
  372. endMonthDay = endDateParts[1] + '-' + endDateParts[2];
  373. const endTime = endDateTime[1] || '';
  374. endHourOnly = endTime.split(':')[0] || '';
  375. }
  376. // 添加日期和时间属性(保持原有的 date 和 time 不变)
  377. item.date = date;
  378. item.time = time;
  379. item.startHour = startHourOnly ? startMonthDay + ' ' + startHourOnly : '';
  380. item.endHour = endHourOnly ? endMonthDay + ' ' + endHourOnly : '';
  381. // 按日期分组
  382. if (!groupedData[date]) {
  383. groupedData[date] = [];
  384. }
  385. groupedData[date].push(item);
  386. }
  387. });
  388. console.log('分组后的数据:', groupedData);
  389. // 可以将分组后的数据存储到一个新的变量中,以便在模板中使用
  390. this.groupedWorkCalendar = groupedData;
  391. // 获取当天日期并设置为选中日期
  392. const today = new Date();
  393. const year = today.getFullYear();
  394. const month = String(today.getMonth() + 1).padStart(2, '0');
  395. const day = String(today.getDate()).padStart(2, '0');
  396. const todayStr = `${year}-${month}-${day}`;
  397. // 如果当天有日程,则自动选中当天
  398. if (groupedData[todayStr] && groupedData[todayStr].length > 0) {
  399. this.selectedScheduleDate = todayStr;
  400. }
  401. }
  402. },
  403. hasSchedule(day) {
  404. return this.groupedWorkCalendar && this.groupedWorkCalendar[day] && this.groupedWorkCalendar[day].length > 0;
  405. },
  406. handleDateClick(day) {
  407. console.log('day~~~', day);
  408. this.selectedScheduleDate = day;
  409. },
  410. getScheduleListByDate() {
  411. if (this.selectedScheduleDate && this.groupedWorkCalendar[this.selectedScheduleDate]) {
  412. return this.groupedWorkCalendar[this.selectedScheduleDate];
  413. }
  414. return [];
  415. },
  416. handleToPro() {
  417. this.handelRouterTo('/page-pro/project-initiation')
  418. },
  419. handleTo(key) {
  420. switch(key) {
  421. case '测试项目':
  422. case '研发项目':
  423. case '实施项目':
  424. case '开发项目':
  425. this.handelRouterTo('/page-pro/project-initiation')
  426. break;
  427. case '运维工单':
  428. this.handelRouterTo('/page-eam/maintenance/patrol?isWt=true&title=工单')
  429. break;
  430. case '质检工单':
  431. this.handelRouterTo('/page-qms/inspectionWork')
  432. break;
  433. case '生产工单':
  434. this.handelRouterTo('/page-mes/produceOrder?isWt=true')
  435. break;
  436. case '盘点工单':
  437. this.handelRouterTo('/page-wms/warehouseManagement/stocktaking/work')
  438. break;
  439. case '盘点任务':
  440. this.handelRouterTo('')
  441. break;
  442. case '生产任务':
  443. this.handelRouterTo('/page-mes/rw')
  444. break;
  445. case '质检任务':
  446. this.handelRouterTo('/page-qms/inspectionProjectTask')
  447. break;
  448. case '项目任务':
  449. this.handelRouterTo('/page-pro/task/myTaskList')
  450. break;
  451. }
  452. },
  453. async getMessageList() {
  454. let list = [];
  455. let total = 0;
  456. if(this.activeTab === 'notice') {
  457. const res = await noticeDocumentPageV1API({
  458. pageNum: this.currentPage,
  459. size: 5,
  460. approvalStatus: 2
  461. })
  462. console.log('noticeDocumentPageV1API~~~', res);
  463. total = res.count || 0;
  464. list = (res.list || []).map((item) => {
  465. // 提取 HTML 中的图片地址
  466. const images = [];
  467. const content = item.content || '';
  468. const imgRegex = /<img[^>]+src=["']([^"']+)["']/gi;
  469. let match;
  470. while ((match = imgRegex.exec(content)) !== null) {
  471. images.push(match[1]);
  472. }
  473. // 提取纯文本内容(去除 HTML 标签和空格)
  474. const textContent = content.replace(/<[^>]+>/g, '').replace(/\s+/g, '').trim();
  475. return {
  476. id: item.id,
  477. title: item.title,
  478. content: textContent,
  479. images: images,
  480. createUser: '拟稿人:' + item.draftAuthor,
  481. department: '发文机关:' + item.issuingAuthority,
  482. createTime: item.createTime
  483. }
  484. });
  485. } else if(this.activeTab === 'industry') {
  486. list = []
  487. } else if(this.activeTab === 'system') {
  488. const res = await notifyMessagePageAPI({
  489. pageNum: this.currentPage,
  490. size: 5,
  491. // userId: this.user.info.userId,
  492. });
  493. total = res.count || 0;
  494. list = (res.list || []).map((item) => {
  495. return {
  496. title: item.templateName,
  497. content: item.templateContent,
  498. createUser: '创建人:' + item.userName,
  499. department: item.department,
  500. createTime: item.createTime
  501. }
  502. });
  503. }
  504. // 如果是第一页,直接赋值;否则拼接到后面
  505. if (this.currentPage === 1) {
  506. this.notifications = list;
  507. } else {
  508. this.notifications = [...this.notifications, ...list];
  509. }
  510. // 判断是否还有更多数据
  511. this.hasMore = this.notifications.length < total;
  512. console.log('notifications~~~', this.notifications, 'hasMore:', this.hasMore);
  513. },
  514. async getMyProjectTotal() {
  515. this.myProjectList = await getMyProjectTotal({});
  516. },
  517. async getMyWorkOrderTotal() {
  518. this.myWorkOrderList = await getMyWorkOrderTotal({});
  519. },
  520. async getMyTaskTotal() {
  521. this.myTaskList = await getMyTaskTotal({});
  522. },
  523. handleAdd() {
  524. // if(this.commonFunctions.length > 12) return this.$message.warning('最多只能添加12个常用功能');
  525. this.commonDialogFlag = true;
  526. this.$nextTick(() => {
  527. this.$refs.commonDialogRef.init();
  528. });
  529. },
  530. handleEdit() {
  531. this.isDelFlag = !this.isDelFlag;
  532. },
  533. handleDel(item, index) {
  534. if (this.isDelFlag) return this.handleCommonListDel(item, index);
  535. let urlPath = item.topUrl + item.url;
  536. console.log('urlPath~~~', urlPath);
  537. this.handelRouterTo(urlPath);
  538. },
  539. async handleCommonListDel(item, index) {
  540. await userResourceDeleteAPI([item.id]);
  541. this.commonFunctions.splice(index, 1);
  542. },
  543. handelRouterTo(path) {
  544. window.history.pushState(null, '', path);
  545. },
  546. getIconPath(pic) {
  547. if (pic) {
  548. // 如果后端返回的是静态路径字符串,需要通过 require() 处理
  549. return require(`@/assets/home/commonIcon/${pic}`);
  550. }
  551. // 如果没有 pic 字段,使用默认图标
  552. return require('@/assets/home/commonIcon/icon_0.svg');
  553. },
  554. async getCommonFunctionsList() {
  555. this.commonFunctions = await userResourceListAPI();
  556. console.log('commonFunctions~~~', this.commonFunctions);
  557. },
  558. async getPortalsList() {
  559. let { list } = await getList({ pageNum: 1, size: 9999 });
  560. this.portals = list.map((item) => {
  561. return {
  562. ...item,
  563. img:
  564. window.location.origin +
  565. '/api/main/file/getFile?objectName=' +
  566. item.iconPath
  567. };
  568. });
  569. console.log(this.portals);
  570. },
  571. handleCalendarMoreClick() {
  572. this.calendarShowMore = !this.calendarShowMore;
  573. console.log(this.getMonthRange());
  574. if (this.calendarShowMore) {
  575. this.calendarRange = this.getMonthRange();
  576. } else {
  577. this.calendarRange = this.getWeekRange();
  578. }
  579. this.getMyWorkCalendar();
  580. },
  581. handleTabClick() {
  582. // 切换标签页时重置页码和hasMore
  583. this.currentPage = 1;
  584. this.hasMore = true;
  585. this.notifications = [];
  586. this.getMessageList();
  587. },
  588. loadMoreNotifications() {
  589. // 加载更多通知(增加页码)
  590. this.currentPage++;
  591. this.getMessageList();
  592. // switch(this.activeTab) {
  593. // case 'notice':
  594. // this.handelRouterTo('/page-wt/documents/noticeIssuance');
  595. // break;
  596. // case 'industry':
  597. // this.handelRouterTo('/industry');
  598. // break;
  599. // case 'system':
  600. // this.handelRouterTo('/page-wt/message');
  601. // break;
  602. // }
  603. },
  604. // 获取当天所在周的范围,周一开头
  605. getWeekRange() {
  606. const now = new Date(this.currentDate);
  607. const dayOfWeek = now.getDay(); // 0 是周日,1 是周一,...,6 是周六
  608. // 计算与周一的差值:如果是周日(0),则差值为 -6,否则差值为 1 - dayOfWeek
  609. const diff = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
  610. const monday = new Date(now);
  611. monday.setDate(now.getDate() + diff);
  612. const sunday = new Date(monday);
  613. sunday.setDate(monday.getDate() + 6);
  614. const formatDate = (date) => {
  615. const year = date.getFullYear();
  616. const month = String(date.getMonth() + 1).padStart(2, '0');
  617. const day = String(date.getDate()).padStart(2, '0');
  618. return `${year}-${month}-${day}`;
  619. };
  620. return [
  621. formatDate(monday),
  622. formatDate(sunday)
  623. ];
  624. },
  625. // 获取选中当月的范围
  626. getMonthRange() {
  627. const now = new Date(this.currentDate);
  628. const year = now.getFullYear();
  629. const month = now.getMonth();
  630. // 当月第一天
  631. const firstDay = new Date(year, month, 1);
  632. // 下月第一天,减一天就是当月最后一天
  633. const lastDay = new Date(year, month + 1, 0);
  634. const formatDate = (date) => {
  635. const year = date.getFullYear();
  636. const month = String(date.getMonth() + 1).padStart(2, '0');
  637. const day = String(date.getDate()).padStart(2, '0');
  638. return `${year}-${month}-${day}`;
  639. };
  640. return [
  641. formatDate(firstDay),
  642. formatDate(lastDay)
  643. ];
  644. }
  645. }
  646. };
  647. </script>
  648. <style lang="scss" scoped>
  649. .home-container {
  650. width: 100%;
  651. padding: 10px;
  652. box-sizing: border-box;
  653. display: flex;
  654. flex-direction: column;
  655. // height: calc(100vh - 96px);
  656. .card-content {
  657. &.common-function-card {
  658. overflow-x: auto;
  659. overflow-y: hidden;
  660. white-space: nowrap;
  661. padding-bottom: 8px;
  662. &::-webkit-scrollbar {
  663. height: 6px;
  664. }
  665. &::-webkit-scrollbar-track {
  666. background: #f1f1f1;
  667. border-radius: 3px;
  668. }
  669. &::-webkit-scrollbar-thumb {
  670. background: #c1c1c1;
  671. border-radius: 3px;
  672. }
  673. &::-webkit-scrollbar-thumb:hover {
  674. background: #a8a8a8;
  675. }
  676. .common-function {
  677. flex-wrap: nowrap;
  678. display: inline-flex;
  679. }
  680. }
  681. &.portal-content {
  682. max-height: 172px;
  683. overflow-y: auto;
  684. overflow-x: hidden;
  685. padding-right: 8px;
  686. flex-shrink: 0;
  687. min-height: 0;
  688. .portal {
  689. max-height: 100%;
  690. overflow-y: auto;
  691. overflow-x: hidden;
  692. }
  693. &::-webkit-scrollbar {
  694. width: 6px;
  695. }
  696. &::-webkit-scrollbar-track {
  697. background: #f1f1f1;
  698. border-radius: 3px;
  699. }
  700. &::-webkit-scrollbar-thumb {
  701. background: #c1c1c1;
  702. border-radius: 3px;
  703. }
  704. &::-webkit-scrollbar-thumb:hover {
  705. background: #a8a8a8;
  706. }
  707. }
  708. &.calendar-content {
  709. max-height: 332px;
  710. overflow-y: auto;
  711. overflow-x: hidden;
  712. padding-right: 8px;
  713. &::-webkit-scrollbar {
  714. width: 6px;
  715. }
  716. &::-webkit-scrollbar-track {
  717. background: #f1f1f1;
  718. border-radius: 3px;
  719. }
  720. &::-webkit-scrollbar-thumb {
  721. background: #c1c1c1;
  722. border-radius: 3px;
  723. }
  724. &::-webkit-scrollbar-thumb:hover {
  725. background: #a8a8a8;
  726. }
  727. }
  728. .common-function {
  729. display: flex;
  730. flex-wrap: wrap;
  731. justify-content: flex-start;
  732. align-items: center;
  733. .common-function-item {
  734. display: flex;
  735. flex-direction: column;
  736. justify-content: center;
  737. align-items: center;
  738. margin: 0 18px;
  739. border-radius: 5px;
  740. cursor: pointer;
  741. transition: all 0.3s ease-in-out;
  742. .common-function-item-icon {
  743. width: 50px;
  744. height: 50px;
  745. box-shadow: 0px 1px 2px 1px #eae7e7;
  746. background: #f8fafc;
  747. border-radius: 17px;
  748. display: flex;
  749. justify-content: center;
  750. align-items: center;
  751. img {
  752. width: 30px;
  753. height: 30px;
  754. }
  755. }
  756. .common-function-item-name {
  757. font-size: 14px;
  758. font-weight: 400;
  759. color: #3f3e3e;
  760. margin-top: 10px;
  761. }
  762. }
  763. .div-del {
  764. // background: #cccccc !important;
  765. position: relative;
  766. }
  767. .div-del::after {
  768. content: '—';
  769. position: absolute;
  770. padding: 5px; /* 按钮的内边距 */
  771. background-color: rgba(255, 0, 0, 0.91);
  772. color: white;
  773. border-radius: 50%;
  774. }
  775. }
  776. .overview-container {
  777. .overview {
  778. padding: 16px;
  779. box-sizing: border-box;
  780. border-radius: 17px;
  781. box-shadow: -1px 1px 0px 1px #ececec, 1px -1px 0px 1px #ececec;
  782. border: 1px solid #ececec;
  783. .overview-top {
  784. display: flex;
  785. justify-content: flex-start;
  786. align-items: center;
  787. .overview-top-img {
  788. width: 66px;
  789. height: 66px;
  790. border-radius: 50%;
  791. margin-right: 10px;
  792. img {
  793. width: 100%;
  794. height: 100%;
  795. }
  796. }
  797. .overview-top-content {
  798. display: flex;
  799. flex-direction: column;
  800. justify-content: center;
  801. align-items: flex-start;
  802. .overview-top-content-name {
  803. font-size: 18px;
  804. font-weight: 500;
  805. color: #525252;;
  806. }
  807. .overview-top-content-count {
  808. font-size: 28px;
  809. font-weight: 500;
  810. color: #2D80EE;
  811. line-height: 42px;
  812. // margin-top: 8px;
  813. }
  814. }
  815. }
  816. .el-divider {
  817. margin: 12px 0;
  818. }
  819. .overview-bottom {
  820. display: grid;
  821. grid-template-columns: repeat(2, 1fr);
  822. grid-gap: 10px;
  823. max-height: 45px;
  824. overflow-y: auto;
  825. .overview-bottom-item {
  826. display: flex;
  827. justify-content: flex-start;
  828. align-items: center;
  829. cursor: pointer;
  830. .overview-bottom-item-name {
  831. font-size: 14px;
  832. font-weight: 400;
  833. color: #686767;
  834. margin-right: 30px;
  835. }
  836. .overview-bottom-item-count {
  837. font-size: 15px;
  838. font-weight: 400;
  839. color: #303133;
  840. }
  841. }
  842. }
  843. }
  844. }
  845. .portal {
  846. .portal-item {
  847. margin-bottom: 12px;
  848. .portal-item-content {
  849. display: flex;
  850. justify-content: flex-start;
  851. align-items: center;
  852. border-radius: 11px;
  853. border: 1px solid #ebebeb;
  854. box-shadow: 0px 4px 4px #a7a7a740;
  855. padding: 6px 4px;
  856. cursor: pointer;
  857. .portal-item-img {
  858. width: 40px;
  859. height: 32px;
  860. border-radius: 5px;
  861. margin-right: 10px;
  862. img {
  863. width: 100%;
  864. height: 100%;
  865. }
  866. }
  867. .portal-item-name {
  868. font-size: 13px;
  869. font-weight: bold;
  870. color: #303133;
  871. }
  872. }
  873. }
  874. }
  875. .calendar {
  876. background-color: #f9fbfd;
  877. border-radius: 8px;
  878. ::v-deep .el-calendar__header {
  879. .el-calendar__title {
  880. font-size: 16px;
  881. font-weight: bold;
  882. color: #303133;
  883. }
  884. }
  885. ::v-deep .el-calendar__body {
  886. padding-bottom: 16px;
  887. .el-calendar-table {
  888. tr {
  889. td {
  890. border: none;
  891. .el-calendar-day {
  892. height: 48px;
  893. display: flex;
  894. flex-direction: column;
  895. justify-content: center;
  896. align-items: center;
  897. .calendar-day-content {
  898. display: flex;
  899. justify-content: space-between;
  900. align-items: center;
  901. flex-direction: column;
  902. height: 100%;
  903. width: 100%;
  904. .is-selected-day {
  905. background-color: #005dff;
  906. color: #fff;
  907. border-radius: 5px;
  908. box-shadow: 0 3px 7px rgba(0, 0, 0, 0.12);
  909. padding: 5px;
  910. }
  911. .dot-container {
  912. display: flex;
  913. justify-content: center;
  914. align-items: center;
  915. margin-top: 5px;
  916. .dot {
  917. width: 5px;
  918. height: 5px;
  919. border-radius: 50%;
  920. background-color: #00c951;
  921. }
  922. }
  923. }
  924. }
  925. }
  926. }
  927. }
  928. }
  929. }
  930. .calendar-more {
  931. display: flex;
  932. justify-content: center;
  933. align-items: center;
  934. }
  935. .schedule {
  936. .schedule-item {
  937. margin-bottom: 10px;
  938. padding: 10px;
  939. border-radius: 5px;
  940. background-color: #f9fbfd;
  941. font-size: 13px;
  942. }
  943. }
  944. }
  945. }
  946. .mt-20 {
  947. margin-top: 6px;
  948. }
  949. .box-col {
  950. height: 100%;
  951. }
  952. .box-card {
  953. height: 100%;
  954. margin-bottom: 0;
  955. ::v-deep .el-card__header {
  956. padding: 10px 8px;
  957. font-weight: bold;
  958. color: #303133;
  959. }
  960. ::v-deep .el-card__body {
  961. padding: 12px;
  962. }
  963. }
  964. .notification-card {
  965. ::v-deep .el-card__body {
  966. padding-top: 0;
  967. }
  968. ::v-deep .el-tabs__item {
  969. font-size: 15px;
  970. font-weight: bold;
  971. color: #333;
  972. }
  973. }
  974. /* 通知公告样式 */
  975. .notification-list {
  976. // margin-top: 16px;
  977. max-height: 344px;
  978. overflow-y: auto;
  979. overflow-x: hidden;
  980. padding-right: 8px;
  981. &::-webkit-scrollbar {
  982. width: 6px;
  983. }
  984. &::-webkit-scrollbar-track {
  985. background: #f1f1f1;
  986. border-radius: 3px;
  987. }
  988. &::-webkit-scrollbar-thumb {
  989. background: #c1c1c1;
  990. border-radius: 3px;
  991. }
  992. &::-webkit-scrollbar-thumb:hover {
  993. background: #a8a8a8;
  994. }
  995. }
  996. .notification-item {
  997. display: flex;
  998. align-items: center;
  999. padding: 12px 0;
  1000. border-bottom: 1px solid #f0f0f0;
  1001. &:last-child {
  1002. border-bottom: none;
  1003. }
  1004. }
  1005. .notification-dot {
  1006. width: 10px;
  1007. height: 10px;
  1008. background-color: #a6ccff;;
  1009. border-radius: 50%;
  1010. margin-right: 12px;
  1011. margin-top: 5px;
  1012. flex-shrink: 0;
  1013. }
  1014. .notification-content {
  1015. flex: 1;
  1016. min-width: 0;
  1017. cursor: pointer;
  1018. color: #333;
  1019. &:hover {
  1020. color: #1890ff;
  1021. }
  1022. }
  1023. .notification-header {
  1024. display: flex;
  1025. align-items: center;
  1026. margin-bottom: 4px;
  1027. }
  1028. .notification-title {
  1029. font-size: 15px;
  1030. font-weight: 500;
  1031. // color: #333;
  1032. margin-right: 12px;
  1033. white-space: nowrap;
  1034. overflow: hidden;
  1035. text-overflow: ellipsis;
  1036. flex-shrink: 1;
  1037. max-width: 200px;
  1038. }
  1039. .notification-info {
  1040. font-size: 12px;
  1041. // color: #333;
  1042. white-space: nowrap;
  1043. overflow: hidden;
  1044. text-overflow: ellipsis;
  1045. }
  1046. .notification-text {
  1047. font-size: 14px;
  1048. // color: #333;
  1049. line-height: 1.4;
  1050. flex: 1;
  1051. min-width: 0;
  1052. white-space: nowrap;
  1053. overflow: hidden;
  1054. text-overflow: ellipsis;
  1055. }
  1056. .notification-images {
  1057. margin-top: 8px;
  1058. }
  1059. .image-url {
  1060. font-size: 12px;
  1061. color: #1890ff;
  1062. cursor: pointer;
  1063. margin-bottom: 4px;
  1064. white-space: nowrap;
  1065. overflow: hidden;
  1066. text-overflow: ellipsis;
  1067. &:hover {
  1068. text-decoration: underline;
  1069. }
  1070. }
  1071. .notification-date {
  1072. font-size: 13px;
  1073. // color: #333;
  1074. margin-left: 12px;
  1075. flex-shrink: 0;
  1076. }
  1077. .notification-more {
  1078. text-align: right;
  1079. margin-top: 12px;
  1080. font-size: 15px;
  1081. color: #1890ff;
  1082. cursor: pointer;
  1083. &:hover {
  1084. text-decoration: underline;
  1085. }
  1086. }
  1087. </style>