index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. <template>
  2. <div class="home-container layout-pd">
  3. <el-row :gutter="20">
  4. <el-col :xs="24" :sm="24" :md="24" :lg="16" :xl="16" class="left-content">
  5. <el-row :gutter="20">
  6. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="6">
  7. <div class="statistics-item box" v-loading="state.loading">
  8. <div class="statistics-title">
  9. <img v-lazy="getImageUrl('home/Call.png')" alt="" src=""/>
  10. 今日来电
  11. </div>
  12. <div class="statistics-number">
  13. <div class="statistics-number-item">
  14. <p class="statistics-number-item-number"><b>520</b></p>
  15. <p class="statistics-number-item-tips">全部</p>
  16. </div>
  17. <div class="statistics-number-item">
  18. <p class="statistics-number-item-number"><b>200</b></p>
  19. <p class="statistics-number-item-tips">有效</p>
  20. </div>
  21. </div>
  22. </div>
  23. </el-col>
  24. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="6">
  25. <div class="statistics-item box" v-loading="state.loading">
  26. <div class="statistics-title">
  27. <img v-lazy="getImageUrl('home/connectionRate.png')" alt="" src=""/>
  28. 今日接通率
  29. </div>
  30. <div class="statistics-number">
  31. <div class="statistics-number-item">
  32. <p class="statistics-number-item-number"><b>198</b></p>
  33. <p class="statistics-number-item-tips">接通</p>
  34. </div>
  35. <div class="statistics-number-item">
  36. <p class="statistics-number-item-number"><b>90%</b></p>
  37. <p class="statistics-number-item-tips">接通率</p>
  38. </div>
  39. </div>
  40. </div>
  41. </el-col>
  42. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="6">
  43. <div class="statistics-item box" v-loading="state.loading">
  44. <div class="statistics-title">
  45. <img v-lazy="getImageUrl('home/workOrder.png')" alt="" src=""/>
  46. 受理工单
  47. </div>
  48. <div class="statistics-number">
  49. <p class="statistics-number-item-total"><b>10</b></p>
  50. </div>
  51. </div>
  52. </el-col>
  53. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="6">
  54. <div class="statistics-item box" v-loading="state.loading">
  55. <div class="statistics-title">
  56. <img v-lazy="getImageUrl('home/wait.png')" alt="" src=""/>
  57. 当前等待
  58. </div>
  59. <div class="statistics-number">
  60. <p class="statistics-number-item-total"><b>20</b></p>
  61. <p></p>
  62. </div>
  63. </div>
  64. </el-col>
  65. </el-row>
  66. <div class="list-content box w100 mb20" v-loading="state.listLoading">
  67. <div class="list-content-tabs">
  68. <div class="list-content-tabs-item" :class="state.active === 'gd' ? 'active' : ''" @click="changeList('gd')">
  69. 工单池 <span v-show="state.activities.length">({{ state.activities.length }})</span>
  70. </div>
  71. <div class="list-content-tabs-item" :class="state.active === 'db' ? 'active' : ''" @click="changeList('db')">
  72. 待办列表 <span v-show="state.activities.length">({{ state.activities.length }})</span>
  73. </div>
  74. </div>
  75. <div class="list-content-timeline">
  76. <p class="list-content-timeline-title">
  77. <span> <SvgIcon :name="getImageUrl('home/clock.png')" class="icons" size="28px" /><b>期满时间</b> </span>
  78. <el-button type="primary" text bg round v-show="state.activities.length" @click="more('workOrder')"
  79. >更多
  80. <SvgIcon name="ele-Right" class="ml5" color="var(--el-color-primary-light-4)" />
  81. </el-button>
  82. </p>
  83. <template v-if="state.activities.length">
  84. <el-timeline>
  85. <el-timeline-item
  86. v-for="(activity, index) in state.activities"
  87. :key="index"
  88. :type="activity.type"
  89. placement="top"
  90. :timestamp="activity.timestamp"
  91. >
  92. <el-card shadow="never">
  93. <h4>{{ activity.content }}</h4>
  94. </el-card>
  95. </el-timeline-item>
  96. </el-timeline>
  97. </template>
  98. <Empty v-else />
  99. </div>
  100. </div>
  101. </el-col>
  102. <!-- 常用入口 -->
  103. <el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8" class="right-content">
  104. <div class="right-entrance box w100" v-loading="state.entranceLoading">
  105. <p class="right-entrance-title">
  106. 常用入口
  107. <span @click="customEntry"> <SvgIcon name="ele-Setting" class="mr5" />自定义 </span>
  108. </p>
  109. <template v-if="state.entranceList.length">
  110. <ul class="right-entrance-list">
  111. <li
  112. class="right-entrance-list-item"
  113. v-for="item in state.entranceList"
  114. :key="item.id"
  115. :data-id="item.id"
  116. @click="goLink(item.path)"
  117. >
  118. <img v-lazy="getImageUrl(item.fastIcon)" alt="" class="my-handle" src=""/>
  119. <p>{{ item.pageName }}</p>
  120. </li>
  121. </ul>
  122. </template>
  123. <Empty descriptionData="暂无常用入口" v-else />
  124. </div>
  125. <div class="right-notice mt20 box w100" v-loading="state.noticeLoading">
  126. <p class="right-notice-title">
  127. 通知公告
  128. <el-button type="primary" text bg round v-show="state.noticeList.length" @click="more('notice')">
  129. 更多
  130. <SvgIcon name="ele-Right" class="ml5" color="var(--el-color-primary-light-4)" />
  131. </el-button>
  132. </p>
  133. <template v-if="state.noticeList.length">
  134. <vue3-seamless-scroll :list="state.noticeList" class="right-notice-scroll" :hover="true" :limitScrollNum="6">
  135. <div class="right-notice-scroll-item" v-for="(item, index) in state.noticeList" :key="index">
  136. <span class="right-notice-scroll-item-name text-no-wrap" :title="item.title">
  137. <SvgIcon name="iconfont icon-jiadayinliang" class="mr5 vd" />{{ item.title }}</span
  138. >
  139. <span class="right-notice-scroll-item-date"
  140. >{{ formatDate(item.date, 'YYYY-mm-dd') }}
  141. <SvgIcon name="ele-DArrowRight" class="ml5" />
  142. </span>
  143. </div>
  144. </vue3-seamless-scroll>
  145. </template>
  146. <Empty v-else descriptionData="暂无通知公告" />
  147. </div>
  148. </el-col>
  149. </el-row>
  150. <entrance ref="entranceRef" @updateEntrance="getEntrance" />
  151. </div>
  152. </template>
  153. <script lang="ts" setup name="home">
  154. import { defineAsyncComponent, reactive, ref, onMounted } from 'vue';
  155. import { useRouter } from 'vue-router';
  156. import { Vue3SeamlessScroll } from 'vue3-seamless-scroll';
  157. import { getImageUrl } from '/@/utils/tools';
  158. import { formatDate } from '/@/utils/formatTime';
  159. import { geFastMenu } from '/@/api/home';
  160. const Entrance = defineAsyncComponent(() => import('/@/views/home/component/Entrance.vue'));
  161. const router = useRouter();
  162. const state = reactive({
  163. entranceList: <EmptyArrayType>[],
  164. noticeList: [
  165. {
  166. title: '市城区迁改23处盲道优化446个临时停车泊位',
  167. date: Date.now(),
  168. },
  169. {
  170. title: '彭清华在成都调研指导疫情防控工作时强调',
  171. date: Date.now(),
  172. },
  173. {
  174. title: '广元市第二次全国污染源普查公报',
  175. date: Date.now(),
  176. },
  177. {
  178. title: '昭化:发展高效农林产业 打造乡村振兴“新引擎”',
  179. date: Date.now(),
  180. },
  181. {
  182. title: '市城区迁改23处盲道优化446个临时停车泊位',
  183. date: Date.now(),
  184. },
  185. {
  186. title: '广元市第二次全国污染源普查公报',
  187. date: Date.now(),
  188. },
  189. ],
  190. active: 'gd',
  191. activities: [
  192. {
  193. content: '这是内容',
  194. timestamp: '2018-04-12 20:46',
  195. type: 'primary',
  196. },
  197. {
  198. content: '这是内容',
  199. timestamp: '2018-04-03 20:46',
  200. type: 'primary',
  201. },
  202. ],
  203. loading: false, //头部
  204. listLoading: false, // 列表
  205. entranceLoading: false, //入口
  206. noticeLoading: false, // 公告
  207. sortEntranceList: [],
  208. sortable: <any>'',
  209. });
  210. const entranceRef = ref<RefType>();
  211. // 自定义入口
  212. const customEntry = () => {
  213. entranceRef.value.openDialog();
  214. };
  215. //获取入口设置
  216. const getEntrance = () => {
  217. state.entranceLoading = true;
  218. geFastMenu()
  219. .then((res: any) => {
  220. state.entranceList = res?.result ?? [];
  221. state.entranceLoading = false;
  222. })
  223. .catch(() => {
  224. state.entranceLoading = false;
  225. });
  226. };
  227. // 跳转
  228. const goLink = (val: string) => {
  229. router.push(val);
  230. };
  231. //切换列表
  232. const changeList = (val: string) => {
  233. if (state.active === val) return;
  234. state.active = val;
  235. };
  236. // 更多
  237. const more = (val: string) => {
  238. switch (val) {
  239. case 'workOrder':
  240. break;
  241. case 'notice':
  242. break;
  243. default:
  244. break;
  245. }
  246. };
  247. onMounted(() => {
  248. getEntrance();
  249. });
  250. </script>
  251. <style lang="scss" scoped>
  252. .box {
  253. background-color: var(--el-color-white);
  254. border-radius: 8px;
  255. }
  256. .vd {
  257. vertical-align: middle;
  258. }
  259. .home-container {
  260. .left-content {
  261. .statistics {
  262. display: flex;
  263. justify-content: space-between;
  264. &-item {
  265. padding: 20px;
  266. height: 200px;
  267. margin-bottom: 20px;
  268. }
  269. &-title {
  270. background: var(--hotline-bg-main-color);
  271. border-radius: 16px;
  272. height: 60px;
  273. position: relative;
  274. display: flex;
  275. align-items: center;
  276. justify-content: center;
  277. font-size: var(--el-font-size-medium);
  278. img {
  279. width: 83px;
  280. height: 83px;
  281. position: absolute;
  282. left: -22px;
  283. top: -2px;
  284. }
  285. }
  286. &-number {
  287. display: flex;
  288. align-items: center;
  289. justify-content: space-between;
  290. padding: 40px 20px 0 20px;
  291. text-align: center;
  292. &-item-number {
  293. font-size: 30px;
  294. }
  295. &-item-tips {
  296. margin-top: 12px;
  297. color: var(--hotline-color-text-main-light);
  298. }
  299. &-item-total {
  300. flex: 1;
  301. font-size: 30px;
  302. }
  303. }
  304. }
  305. .list-content {
  306. padding: 20px;
  307. &-tabs {
  308. width: 300px;
  309. height: 50px;
  310. line-height: 50px;
  311. background: var(--hotline-bg-main-color);
  312. border-radius: 25px;
  313. display: flex;
  314. align-items: center;
  315. margin-bottom: 30px;
  316. &-item {
  317. color: var(--el-color-primary);
  318. font-size: var(--el-font-size-medium);
  319. text-align: center;
  320. height: 100%;
  321. width: 100%;
  322. border-radius: 25px;
  323. cursor: pointer;
  324. &.active {
  325. background-color: var(--el-color-primary);
  326. color: var(--el-color-white);
  327. }
  328. }
  329. }
  330. &-timeline {
  331. background: var(--hotline-bg-main-color);
  332. border-radius: 4px;
  333. padding: 20px;
  334. &-title {
  335. display: flex;
  336. align-items: center;
  337. justify-content: space-between;
  338. padding-bottom: 20px;
  339. font-size: var(--el-font-size-medium);
  340. font-weight: 600;
  341. span {
  342. color: var(--el-color-primary);
  343. position: relative;
  344. .icons {
  345. position: absolute;
  346. top: 0;
  347. left: -8px;
  348. }
  349. b {
  350. padding-left: 30px;
  351. font-weight: normal;
  352. }
  353. }
  354. }
  355. }
  356. }
  357. }
  358. .right-content {
  359. .right-entrance {
  360. padding: 30px 20px;
  361. &-title {
  362. font-size: var(--el-font-size-medium);
  363. display: flex;
  364. align-items: center;
  365. justify-content: space-between;
  366. padding-bottom: 20px;
  367. font-weight: 600;
  368. span {
  369. font-size: 14px;
  370. display: flex;
  371. align-items: center;
  372. color: var(--el-text-color-regular);
  373. cursor: pointer;
  374. font-weight: normal;
  375. }
  376. }
  377. &-list {
  378. display: grid;
  379. justify-content: space-between;
  380. grid-template-columns: repeat(auto-fill, 110px);
  381. grid-gap: 15px;
  382. &-item {
  383. background: var(--hotline-bg-main-color);
  384. border-radius: 8px;
  385. text-align: center;
  386. width: 110px;
  387. height: 110px;
  388. cursor: pointer;
  389. color: var(--el-text-color-regular);
  390. user-select: none;
  391. .my-handle {
  392. width: 40px;
  393. height: 50px;
  394. margin-top: 15px;
  395. margin-bottom: 13px;
  396. }
  397. &:hover {
  398. color: var(--el-color-primary);
  399. background-color: var(--el-color-primary-light-8);
  400. }
  401. }
  402. }
  403. }
  404. .right-notice {
  405. padding: 30px 20px;
  406. &-title {
  407. font-size: var(--el-font-size-medium);
  408. display: flex;
  409. align-items: center;
  410. justify-content: space-between;
  411. padding-bottom: 20px;
  412. font-weight: 600;
  413. }
  414. &-scroll {
  415. height: 300px;
  416. overflow: hidden;
  417. &-item {
  418. display: flex;
  419. align-items: center;
  420. justify-content: space-between;
  421. border-top: 1px solid var(--el-border-color);
  422. cursor: pointer;
  423. height: 50px;
  424. &-name {
  425. flex: 1;
  426. max-width: 70%;
  427. }
  428. &-date {
  429. display: flex;
  430. align-items: center;
  431. color: var(--el-color-info);
  432. text-align: right;
  433. }
  434. &:hover {
  435. color: var(--el-color-primary);
  436. }
  437. }
  438. }
  439. }
  440. }
  441. }
  442. </style>