index.vue 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  1. <template>
  2. <div class="home-container">
  3. <el-row :gutter="12" 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">
  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="12" class="mt-20" style="height: 242px;">
  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/my-project.svg" 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 !== '总数量'"
  63. class="overview-bottom-item"
  64. @click="handleTo(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/my-project.svg" 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 !== '总数量'"
  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 src="~@/assets/home/my-project.svg" 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 !== '总数量'"
  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">
  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="12" class="mt-20" style="height: 404px;">
  145. <el-col :span="18" class="box-col">
  146. <el-card class="box-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 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. <div>{{ item.startTime }}</div>
  207. <div>~</div>
  208. <div>{{ item.endTime }}</div>
  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. </div>
  230. </template>
  231. <script>
  232. import { notifyMessagePageAPI } from '@/api/bpm/task';
  233. import {
  234. userResourceDeleteAPI,
  235. userResourceListAPI,
  236. getList,
  237. getMyProjectTotal,
  238. getMyWorkOrderTotal,
  239. getMyTaskTotal,
  240. getMyWorkCalendar
  241. } from '@/api/home';
  242. import { deepClone } from 'ele-admin/lib/utils/core';
  243. import { mapGetters } from 'vuex';
  244. import vuedraggable from 'vuedraggable';
  245. import commonDialog from '@/views/home/common-dialog.vue';
  246. import { noticeDocumentPageV1API } from '@/api/documents/noticeIssuance/index.js';
  247. export default {
  248. name: 'HomePage',
  249. components: {
  250. vuedraggable,
  251. commonDialog
  252. },
  253. data() {
  254. return {
  255. currentDate: new Date(),
  256. calendarRange: [],
  257. commonFunctions: [],
  258. activeTab: 'notice',
  259. notifications: [],
  260. currentPage: 1,
  261. pageSize: 4,
  262. portals: [],
  263. calendarShowMore: false,
  264. isDelFlag: false,
  265. commonDialogFlag: false,
  266. myProjectList: {},
  267. myWorkOrderList: {},
  268. myTaskList: {},
  269. myWorkCalendar: {},
  270. groupedWorkCalendar: {}, // 分组后的工作日历数据
  271. selectedScheduleDate: '', // 当前选中的日期
  272. };
  273. },
  274. computed: {
  275. ...mapGetters(['user']),
  276. currentNotifications() {
  277. const notifications = this.notifications[this.activeTab] || [];
  278. return notifications;
  279. }
  280. },
  281. watch: {
  282. currentDate: {
  283. handler(newVal, oldVal) {
  284. // 检查月份是否变化
  285. if (newVal.getMonth() !== oldVal.getMonth() || newVal.getFullYear() !== oldVal.getFullYear()) {
  286. // 重新计算日期范围
  287. this.calendarRange = this.getMonthRange();
  288. // 重新获取日程数据
  289. this.getMyWorkCalendar();
  290. }
  291. // 日期变化时,处理选中日期的日程
  292. if (newVal !== oldVal) {
  293. const year = newVal.getFullYear();
  294. const month = String(newVal.getMonth() + 1).padStart(2, '0');
  295. const day = String(newVal.getDate()).padStart(2, '0');
  296. const dateStr = `${year}-${month}-${day}`;
  297. this.handleDateClick(dateStr);
  298. }
  299. },
  300. deep: true
  301. }
  302. },
  303. created() {
  304. this.calendarRange = this.getWeekRange();
  305. this.getPortalsList();
  306. this.getCommonFunctionsList();
  307. this.getMyProjectTotal();
  308. this.getMyWorkOrderTotal();
  309. this.getMyTaskTotal();
  310. this.getMessageList();
  311. this.getMyWorkCalendar();
  312. },
  313. methods: {
  314. handleNotificationsClick(item) {
  315. console.log('item~~~', item);
  316. this.$router.push({
  317. path: '/page-wt/documents/noticeIssuance/add-or-edit',
  318. // query
  319. query: {
  320. id: item.id,
  321. type: 'detail'
  322. }
  323. });
  324. },
  325. async getMyWorkCalendar() {
  326. const res = await getMyWorkCalendar({
  327. startDate: this.calendarRange[0],
  328. endDate: this.calendarRange[1]
  329. });
  330. console.log('myWorkCalendar~~~', res);
  331. // 数据分组
  332. if (res) {
  333. const groupedData = {};
  334. res.forEach(item => {
  335. if (item.startTime) {
  336. // 分割 startTime 为日期和时间
  337. const dateTime = item.startTime.split(' ');
  338. const date = dateTime[0];
  339. const time = dateTime[1] || '';
  340. // 添加日期和时间属性
  341. item.date = date;
  342. item.time = time;
  343. // 按日期分组
  344. if (!groupedData[date]) {
  345. groupedData[date] = [];
  346. }
  347. groupedData[date].push(item);
  348. }
  349. });
  350. console.log('分组后的数据:', groupedData);
  351. // 可以将分组后的数据存储到一个新的变量中,以便在模板中使用
  352. this.groupedWorkCalendar = groupedData;
  353. // 获取当天日期并设置为选中日期
  354. const today = new Date();
  355. const year = today.getFullYear();
  356. const month = String(today.getMonth() + 1).padStart(2, '0');
  357. const day = String(today.getDate()).padStart(2, '0');
  358. const todayStr = `${year}-${month}-${day}`;
  359. // 如果当天有日程,则自动选中当天
  360. if (groupedData[todayStr] && groupedData[todayStr].length > 0) {
  361. this.selectedScheduleDate = todayStr;
  362. }
  363. }
  364. },
  365. hasSchedule(day) {
  366. return this.groupedWorkCalendar && this.groupedWorkCalendar[day] && this.groupedWorkCalendar[day].length > 0;
  367. },
  368. handleDateClick(day) {
  369. console.log('day~~~', day);
  370. this.selectedScheduleDate = day;
  371. },
  372. getScheduleListByDate() {
  373. if (this.selectedScheduleDate && this.groupedWorkCalendar[this.selectedScheduleDate]) {
  374. return this.groupedWorkCalendar[this.selectedScheduleDate];
  375. }
  376. return [];
  377. },
  378. handleTo(key) {
  379. switch(key) {
  380. case '测试项目':
  381. case '研发项目':
  382. case '实施项目':
  383. case '开发项目':
  384. this.handelRouterTo('/page-pro/project-initiation')
  385. break;
  386. case '运维工单':
  387. this.handelRouterTo('/page-eam/maintenance/patrol')
  388. break;
  389. case '质检工单':
  390. this.handelRouterTo('/page-qms/inspectionWork')
  391. break;
  392. case '生产工单':
  393. this.handelRouterTo('/page-mes/produceOrder?isWt=true')
  394. break;
  395. case '盘点工单':
  396. this.handelRouterTo('/page-wms/warehouseManagement/stocktaking/work')
  397. break;
  398. case '盘点任务':
  399. this.handelRouterTo('')
  400. break;
  401. case '生产任务':
  402. this.handelRouterTo('/page-mes/rw')
  403. break;
  404. case '质检任务':
  405. this.handelRouterTo('/page-qms/inspectionProjectTask')
  406. break;
  407. case '项目任务':
  408. this.handelRouterTo('/page-pro/task/myTaskList')
  409. break;
  410. }
  411. },
  412. async getMessageList() {
  413. let list = [];
  414. if(this.activeTab === 'notice') {
  415. const res = await noticeDocumentPageV1API({
  416. pageNum: 1,
  417. size: 5,
  418. approvalStatus: 2
  419. })
  420. console.log('noticeDocumentPageV1API~~~', res);
  421. list = (res.list || []).map((item) => {
  422. // 提取 HTML 中的图片地址
  423. const images = [];
  424. const content = item.content || '';
  425. const imgRegex = /<img[^>]+src=["']([^"']+)["']/gi;
  426. let match;
  427. while ((match = imgRegex.exec(content)) !== null) {
  428. images.push(match[1]);
  429. }
  430. // 提取纯文本内容(去除 HTML 标签)
  431. const textContent = content.replace(/<[^>]+>/g, '').trim();
  432. return {
  433. title: item.title,
  434. content: textContent,
  435. images: images,
  436. createUser: '拟稿人:' + item.draftAuthor,
  437. department: '发文机关:' + item.issuingAuthority,
  438. createTime: item.createTime
  439. }
  440. });
  441. } else if(this.activeTab === 'industry') {
  442. list = []
  443. } else if(this.activeTab === 'system') {
  444. const res = await notifyMessagePageAPI({
  445. pageNum: 1,
  446. size: 5,
  447. // userId: this.user.info.userId,
  448. });
  449. list = (res.list || []).map((item) => {
  450. return {
  451. title: item.templateName,
  452. content: item.templateContent,
  453. createUser: '创建人:' + item.userName,
  454. department: item.department,
  455. createTime: item.createTime
  456. }
  457. });
  458. }
  459. this.notifications = list;
  460. console.log('notifications~~~', this.notifications);
  461. },
  462. async getMyProjectTotal() {
  463. this.myProjectList = await getMyProjectTotal({});
  464. },
  465. async getMyWorkOrderTotal() {
  466. this.myWorkOrderList = await getMyWorkOrderTotal({});
  467. },
  468. async getMyTaskTotal() {
  469. this.myTaskList = await getMyTaskTotal({});
  470. },
  471. handleAdd() {
  472. if(this.commonFunctions.length > 12) return this.$message.warning('最多只能添加12个常用功能');
  473. this.commonDialogFlag = true;
  474. this.$nextTick(() => {
  475. this.$refs.commonDialogRef.init();
  476. });
  477. },
  478. handleEdit() {
  479. this.isDelFlag = !this.isDelFlag;
  480. },
  481. handleDel(item, index) {
  482. if (this.isDelFlag) return this.handleCommonListDel(item, index);
  483. let urlPath = item.topUrl + item.url;
  484. console.log('urlPath~~~', urlPath);
  485. this.handelRouterTo(urlPath);
  486. },
  487. async handleCommonListDel(item, index) {
  488. await userResourceDeleteAPI([item.id]);
  489. this.commonFunctions.splice(index, 1);
  490. },
  491. handelRouterTo(path) {
  492. window.history.pushState(null, '', path);
  493. },
  494. getIconPath(pic) {
  495. if (pic) {
  496. // 如果后端返回的是静态路径字符串,需要通过 require() 处理
  497. return require(`@/assets/home/commonIcon/${pic}`);
  498. }
  499. // 如果没有 pic 字段,使用默认图标
  500. return require('@/assets/home/commonIcon/icon_0.svg');
  501. },
  502. async getCommonFunctionsList() {
  503. this.commonFunctions = await userResourceListAPI();
  504. console.log('commonFunctions~~~', this.commonFunctions);
  505. },
  506. async getPortalsList() {
  507. let { list } = await getList({ pageNum: 1, size: 9999 });
  508. this.portals = list.map((item) => {
  509. return {
  510. ...item,
  511. img:
  512. window.location.origin +
  513. '/api/main/file/getFile?objectName=' +
  514. item.iconPath
  515. };
  516. });
  517. console.log(this.portals);
  518. },
  519. handleCalendarMoreClick() {
  520. this.calendarShowMore = !this.calendarShowMore;
  521. console.log(this.getMonthRange());
  522. if (this.calendarShowMore) {
  523. this.calendarRange = this.getMonthRange();
  524. } else {
  525. this.calendarRange = this.getWeekRange();
  526. }
  527. this.getMyWorkCalendar();
  528. },
  529. handleTabClick() {
  530. // 切换标签页时重置页码
  531. // this.currentPage = 1;
  532. this.getMessageList();
  533. },
  534. loadMoreNotifications() {
  535. // 加载更多通知(增加页码)
  536. // this.currentPage++;
  537. switch(this.activeTab) {
  538. case 'notice':
  539. this.handelRouterTo('/page-wt/documents/noticeIssuance');
  540. break;
  541. case 'industry':
  542. this.handelRouterTo('/industry');
  543. break;
  544. case 'system':
  545. this.handelRouterTo('/page-wt/message');
  546. break;
  547. }
  548. },
  549. // 获取当天所在周的范围,周一开头
  550. getWeekRange() {
  551. const now = new Date(this.currentDate);
  552. const dayOfWeek = now.getDay(); // 0 是周日,1 是周一,...,6 是周六
  553. // 计算与周一的差值:如果是周日(0),则差值为 -6,否则差值为 1 - dayOfWeek
  554. const diff = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
  555. const monday = new Date(now);
  556. monday.setDate(now.getDate() + diff);
  557. const sunday = new Date(monday);
  558. sunday.setDate(monday.getDate() + 6);
  559. const formatDate = (date) => {
  560. const year = date.getFullYear();
  561. const month = String(date.getMonth() + 1).padStart(2, '0');
  562. const day = String(date.getDate()).padStart(2, '0');
  563. return `${year}-${month}-${day}`;
  564. };
  565. return [
  566. formatDate(monday),
  567. formatDate(sunday)
  568. ];
  569. },
  570. // 获取选中当月的范围
  571. getMonthRange() {
  572. const now = new Date(this.currentDate);
  573. const year = now.getFullYear();
  574. const month = now.getMonth();
  575. // 当月第一天
  576. const firstDay = new Date(year, month, 1);
  577. // 下月第一天,减一天就是当月最后一天
  578. const lastDay = new Date(year, month + 1, 0);
  579. const formatDate = (date) => {
  580. const year = date.getFullYear();
  581. const month = String(date.getMonth() + 1).padStart(2, '0');
  582. const day = String(date.getDate()).padStart(2, '0');
  583. return `${year}-${month}-${day}`;
  584. };
  585. return [
  586. formatDate(firstDay),
  587. formatDate(lastDay)
  588. ];
  589. }
  590. }
  591. };
  592. </script>
  593. <style lang="scss" scoped>
  594. .home-container {
  595. width: 100%;
  596. padding: 10px;
  597. box-sizing: border-box;
  598. display: flex;
  599. flex-direction: column;
  600. // height: calc(100vh - 96px);
  601. .card-content {
  602. &.portal-content {
  603. max-height: 180px;
  604. overflow-y: auto;
  605. overflow-x: hidden;
  606. padding-right: 8px;
  607. flex-shrink: 0;
  608. min-height: 0;
  609. .portal {
  610. max-height: 100%;
  611. overflow-y: auto;
  612. overflow-x: hidden;
  613. }
  614. &::-webkit-scrollbar {
  615. width: 6px;
  616. }
  617. &::-webkit-scrollbar-track {
  618. background: #f1f1f1;
  619. border-radius: 3px;
  620. }
  621. &::-webkit-scrollbar-thumb {
  622. background: #c1c1c1;
  623. border-radius: 3px;
  624. }
  625. &::-webkit-scrollbar-thumb:hover {
  626. background: #a8a8a8;
  627. }
  628. }
  629. &.calendar-content {
  630. max-height: 332px;
  631. overflow-y: auto;
  632. overflow-x: hidden;
  633. padding-right: 8px;
  634. &::-webkit-scrollbar {
  635. width: 6px;
  636. }
  637. &::-webkit-scrollbar-track {
  638. background: #f1f1f1;
  639. border-radius: 3px;
  640. }
  641. &::-webkit-scrollbar-thumb {
  642. background: #c1c1c1;
  643. border-radius: 3px;
  644. }
  645. &::-webkit-scrollbar-thumb:hover {
  646. background: #a8a8a8;
  647. }
  648. }
  649. .common-function {
  650. display: flex;
  651. flex-wrap: wrap;
  652. justify-content: flex-start;
  653. align-items: center;
  654. .common-function-item {
  655. display: flex;
  656. flex-direction: column;
  657. justify-content: center;
  658. align-items: center;
  659. margin: 0 18px;
  660. border-radius: 5px;
  661. cursor: pointer;
  662. transition: all 0.3s ease-in-out;
  663. .common-function-item-icon {
  664. width: 50px;
  665. height: 50px;
  666. box-shadow: 0px 1px 2px 1px #eae7e7;
  667. background: #f8fafc;
  668. border-radius: 17px;
  669. display: flex;
  670. justify-content: center;
  671. align-items: center;
  672. img {
  673. width: 30px;
  674. height: 30px;
  675. }
  676. }
  677. .common-function-item-name {
  678. font-size: 14px;
  679. font-weight: 400;
  680. color: #3f3e3e;
  681. margin-top: 10px;
  682. }
  683. }
  684. .div-del {
  685. // background: #cccccc !important;
  686. position: relative;
  687. }
  688. .div-del::after {
  689. content: '—';
  690. position: absolute;
  691. padding: 5px; /* 按钮的内边距 */
  692. background-color: rgba(255, 0, 0, 0.91);
  693. color: white;
  694. border-radius: 50%;
  695. }
  696. }
  697. .overview-container {
  698. .overview {
  699. padding: 16px;
  700. box-sizing: border-box;
  701. border-radius: 17px;
  702. box-shadow: -1px 1px 0px 1px #ececec, 1px -1px 0px 1px #ececec;
  703. border: 1px solid #ececec;
  704. .overview-top {
  705. display: flex;
  706. justify-content: flex-start;
  707. align-items: center;
  708. .overview-top-img {
  709. width: 66px;
  710. height: 66px;
  711. border-radius: 50%;
  712. margin-right: 10px;
  713. img {
  714. width: 100%;
  715. height: 100%;
  716. }
  717. }
  718. .overview-top-content {
  719. display: flex;
  720. flex-direction: column;
  721. justify-content: center;
  722. align-items: flex-start;
  723. .overview-top-content-name {
  724. font-size: 18px;
  725. font-weight: 500;
  726. color: #525252;;
  727. }
  728. .overview-top-content-count {
  729. font-size: 28px;
  730. font-weight: 500;
  731. color: #2D80EE;
  732. line-height: 42px;
  733. // margin-top: 8px;
  734. }
  735. }
  736. }
  737. .el-divider {
  738. margin: 12px 0;
  739. }
  740. .overview-bottom {
  741. display: grid;
  742. grid-template-columns: repeat(2, 1fr);
  743. grid-gap: 10px;
  744. .overview-bottom-item {
  745. display: flex;
  746. justify-content: flex-start;
  747. align-items: center;
  748. cursor: pointer;
  749. .overview-bottom-item-name {
  750. font-size: 14px;
  751. font-weight: 400;
  752. color: #686767;
  753. margin-right: 30px;
  754. }
  755. .overview-bottom-item-count {
  756. font-size: 15px;
  757. font-weight: 400;
  758. color: #303133;
  759. }
  760. }
  761. }
  762. }
  763. }
  764. .portal {
  765. .portal-item {
  766. margin-bottom: 12px;
  767. .portal-item-content {
  768. display: flex;
  769. justify-content: flex-start;
  770. align-items: center;
  771. border-radius: 11px;
  772. border: 1px solid #ebebeb;
  773. box-shadow: 0px 4px 4px #a7a7a740;
  774. padding: 6px 4px;
  775. .portal-item-img {
  776. width: 32px;
  777. height: 32px;
  778. border-radius: 5px;
  779. margin-right: 10px;
  780. img {
  781. width: 100%;
  782. height: 100%;
  783. }
  784. }
  785. .portal-item-name {
  786. font-size: 13px;
  787. font-weight: bold;
  788. color: #303133;
  789. }
  790. }
  791. }
  792. }
  793. .calendar {
  794. background-color: #f9fbfd;
  795. border-radius: 8px;
  796. ::v-deep .el-calendar__header {
  797. .el-calendar__title {
  798. font-size: 16px;
  799. font-weight: bold;
  800. color: #303133;
  801. }
  802. }
  803. ::v-deep .el-calendar__body {
  804. padding-bottom: 16px;
  805. .el-calendar-table {
  806. tr {
  807. td {
  808. border: none;
  809. .el-calendar-day {
  810. height: 48px;
  811. display: flex;
  812. flex-direction: column;
  813. justify-content: center;
  814. align-items: center;
  815. .calendar-day-content {
  816. display: flex;
  817. justify-content: space-between;
  818. align-items: center;
  819. flex-direction: column;
  820. height: 100%;
  821. width: 100%;
  822. .is-selected-day {
  823. background-color: #005dff;
  824. color: #fff;
  825. border-radius: 5px;
  826. box-shadow: 0 3px 7px rgba(0, 0, 0, 0.12);
  827. padding: 5px;
  828. }
  829. .dot-container {
  830. display: flex;
  831. justify-content: center;
  832. align-items: center;
  833. margin-top: 5px;
  834. .dot {
  835. width: 5px;
  836. height: 5px;
  837. border-radius: 50%;
  838. background-color: #00c951;
  839. }
  840. }
  841. }
  842. }
  843. }
  844. }
  845. }
  846. }
  847. }
  848. .calendar-more {
  849. display: flex;
  850. justify-content: center;
  851. align-items: center;
  852. }
  853. .schedule {
  854. .schedule-item {
  855. margin-bottom: 10px;
  856. padding: 10px;
  857. border-radius: 5px;
  858. background-color: #f9fbfd;
  859. font-size: 13px;
  860. }
  861. }
  862. }
  863. }
  864. .mt-20 {
  865. margin-top: 16px;
  866. }
  867. .box-col {
  868. height: 100%;
  869. }
  870. .box-card {
  871. height: 100%;
  872. ::v-deep .el-card__body {
  873. padding: 12px;
  874. }
  875. }
  876. /* 通知公告样式 */
  877. .notification-list {
  878. margin-top: 16px;
  879. max-height: 328px;
  880. overflow-y: auto;
  881. overflow-x: hidden;
  882. padding-right: 8px;
  883. &::-webkit-scrollbar {
  884. width: 6px;
  885. }
  886. &::-webkit-scrollbar-track {
  887. background: #f1f1f1;
  888. border-radius: 3px;
  889. }
  890. &::-webkit-scrollbar-thumb {
  891. background: #c1c1c1;
  892. border-radius: 3px;
  893. }
  894. &::-webkit-scrollbar-thumb:hover {
  895. background: #a8a8a8;
  896. }
  897. }
  898. .no-notification {
  899. }
  900. .notification-item {
  901. display: flex;
  902. align-items: center;
  903. padding: 12px 0;
  904. border-bottom: 1px solid #f0f0f0;
  905. &:last-child {
  906. border-bottom: none;
  907. }
  908. }
  909. .notification-dot {
  910. width: 15px;
  911. height: 15px;
  912. background-color: #a6ccff;;
  913. border-radius: 50%;
  914. margin-right: 12px;
  915. margin-top: 5px;
  916. flex-shrink: 0;
  917. }
  918. .notification-content {
  919. flex: 1;
  920. min-width: 0;
  921. }
  922. .notification-header {
  923. display: flex;
  924. align-items: center;
  925. margin-bottom: 4px;
  926. }
  927. .notification-title {
  928. font-size: 15px;
  929. font-weight: 500;
  930. color: #333;
  931. margin-right: 12px;
  932. white-space: nowrap;
  933. overflow: hidden;
  934. text-overflow: ellipsis;
  935. flex-shrink: 1;
  936. max-width: 200px;
  937. }
  938. .notification-info {
  939. font-size: 12px;
  940. color: #333;
  941. white-space: nowrap;
  942. overflow: hidden;
  943. text-overflow: ellipsis;
  944. }
  945. .notification-text {
  946. font-size: 12px;
  947. color: #333;
  948. line-height: 1.4;
  949. flex: 1;
  950. min-width: 0;
  951. white-space: nowrap;
  952. overflow: hidden;
  953. text-overflow: ellipsis;
  954. }
  955. .notification-images {
  956. margin-top: 8px;
  957. }
  958. .image-url {
  959. font-size: 12px;
  960. color: #1890ff;
  961. cursor: pointer;
  962. margin-bottom: 4px;
  963. white-space: nowrap;
  964. overflow: hidden;
  965. text-overflow: ellipsis;
  966. &:hover {
  967. text-decoration: underline;
  968. }
  969. }
  970. .notification-date {
  971. font-size: 12px;
  972. color: #999;
  973. margin-left: 12px;
  974. flex-shrink: 0;
  975. }
  976. .notification-more {
  977. text-align: right;
  978. margin-top: 12px;
  979. font-size: 12px;
  980. color: #1890ff;
  981. cursor: pointer;
  982. &:hover {
  983. text-decoration: underline;
  984. }
  985. }
  986. </style>