index.vue 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081
  1. <!--
  2. * @Author: wanglj 471442253@qq.com
  3. * @Date: 2022-12-13 10:28:33
  4. * @LastEditors: wanglj
  5. * @LastEditTime: 2023-03-02 11:47:39
  6. * @Description: file content
  7. * @FilePath: \opms_frontend\src\views\index\index.vue
  8. -->
  9. <template>
  10. <div class="index">
  11. <div class="left">
  12. <el-card class="board">
  13. <div slot="header" class="card-title">
  14. <span>个人看板</span>
  15. <div class="buttons">
  16. <el-button v-show="!editFlag" type="text" @click="editFlag = true">编辑布局</el-button>
  17. <el-button v-show="editFlag" type="text" @click="visible = true">编辑指标</el-button>
  18. <el-button v-show="editFlag" type="text" @click="saveLayout">保存布局</el-button>
  19. </div>
  20. </div>
  21. <el-row align="middle" :gutter="10" type="flex">
  22. <VueDragger
  23. v-model="privateBoard"
  24. :animation="500"
  25. :disabled="!editFlag"
  26. drag-class="drag"
  27. :force-fallback="true"
  28. ghost-class="ghost"
  29. handle=".mover">
  30. <el-col v-for="(item, index) in privateBoard" :key="index" class="mover" :span="6">
  31. <div class="board-container">
  32. <vab-icon class="icon" :icon="item.report_icon" />
  33. <div class="text">
  34. <p class="num">{{ item.report_data }}</p>
  35. <p>{{ item.report_name }}</p>
  36. </div>
  37. <vab-icon v-show="editFlag" class="close" icon="close-fill" @click.stop="handleDel(index)" />
  38. </div>
  39. </el-col>
  40. </VueDragger>
  41. </el-row>
  42. </el-card>
  43. <grid-layout
  44. :class="{ grid: editFlag }"
  45. :col-num="24"
  46. :is-draggable="editFlag"
  47. :is-mirrored="false"
  48. :is-resizable="editFlag"
  49. :layout.sync="layout"
  50. :margin="[10, 10]"
  51. :row-height="30"
  52. :use-css-transforms="true"
  53. :use-style-cursor="false"
  54. :vertical-compact="true"
  55. @layout-ready="layoutReadyEvent">
  56. <grid-item
  57. v-for="(item, index) in layout"
  58. :key="index"
  59. :h="item.h"
  60. :i="item.i"
  61. :w="item.w"
  62. :x="item.x"
  63. :y="item.y"
  64. @resized="handleResize">
  65. <h4>
  66. {{ item.name }}
  67. <!-- <div class="buttons">
  68. <el-select v-model="forecast" style="width: 120px">
  69. <el-option label="公司数据" value="1" />
  70. </el-select>
  71. </div> -->
  72. <div class="buttons">
  73. <el-radio-group
  74. v-show="!editFlag && (item.i == 20002 || item.i == 20003)"
  75. v-model="item.searchType"
  76. size="mini"
  77. @change="refresh(item, index)">
  78. <el-radio-button label="year">年</el-radio-button>
  79. <el-radio-button label="quarter">季度</el-radio-button>
  80. <el-radio-button label="month">月</el-radio-button>
  81. </el-radio-group>
  82. <el-radio-group
  83. v-show="!editFlag && (item.i == 20006 || item.i == 20007)"
  84. v-model="item.searchType"
  85. size="mini"
  86. @change="refresh(item, index)">
  87. <el-radio-button label="1">一季度</el-radio-button>
  88. <el-radio-button label="2">二季度</el-radio-button>
  89. <el-radio-button label="3">三季度</el-radio-button>
  90. <el-radio-button label="4">三季度</el-radio-button>
  91. <el-radio-button label="0">全年</el-radio-button>
  92. </el-radio-group>
  93. <el-button v-show="!editFlag" type="text" @click="refresh(item, index)">刷新</el-button>
  94. <vab-icon v-show="editFlag" icon="close-fill" @click="gridDel(index)" />
  95. </div>
  96. </h4>
  97. <div :id="item.i"></div>
  98. </grid-item>
  99. </grid-layout>
  100. </div>
  101. <div class="right">
  102. <el-card class="calendar">
  103. <div slot="header" class="card-title">
  104. <span>日程安排</span>
  105. <!-- <el-button style="padding: 0" type="text">添加</el-button> -->
  106. </div>
  107. <div class="week">
  108. <vab-icon
  109. icon="arrow-left-s-line"
  110. @click="
  111. date -= 7
  112. getDateList()
  113. " />
  114. <ul>
  115. <li
  116. v-for="(item, index) in list"
  117. :key="index"
  118. :class="{ active: item.day == day }"
  119. @click="dateClick(item)">
  120. <span>{{ dayList[item.day] }}</span>
  121. <span>{{ item.date }}</span>
  122. </li>
  123. </ul>
  124. <vab-icon
  125. icon="arrow-right-s-line"
  126. @click="
  127. date += 7
  128. getDateList()
  129. " />
  130. </div>
  131. <el-timeline>
  132. <el-timeline-item v-for="(activity, index) in activities" :key="index">
  133. <div class="detail" :class="{ active: activity.active }">
  134. <p class="time">
  135. {{ activity.schDate }}
  136. <!-- <el-button type="text">详情</el-button> -->
  137. </p>
  138. <p>{{ activity.schTitle }}</p>
  139. </div>
  140. </el-timeline-item>
  141. </el-timeline>
  142. </el-card>
  143. <el-card class="notice">
  144. <div slot="header" class="card-title">
  145. <span>公告</span>
  146. <div class="buttons">
  147. <el-button size="mini" @click="handleHistoryNotice">
  148. 更多
  149. <i class="el-icon-arrow-right el-icon--right"></i>
  150. </el-button>
  151. </div>
  152. </div>
  153. <ul>
  154. <li v-for="(message, index) in messageList" :key="index" class="notice-list" @click="getNoticeInfo(message)">
  155. <p>
  156. <span>【{{ msgTypeFormat(message) }}】</span>
  157. {{ message.msgTitle }}
  158. </p>
  159. <p class="time">
  160. <vab-icon icon="time-line" />
  161. {{ parseTime(message.createdTime, '{y}-{m}-{d}') }}
  162. </p>
  163. </li>
  164. </ul>
  165. </el-card>
  166. </div>
  167. <!-- 编辑指标 -->
  168. <el-dialog title="编辑指标" :visible.sync="visible" @open="getBoardOptions">
  169. <el-tabs v-model="activeName" type="card">
  170. <el-tab-pane label="看板数据" name="first">
  171. <ul class="add-board">
  172. <li v-for="(item, index) in addBoard" :key="index" @click="item.checked = !item.checked">
  173. <div class="board-container">
  174. <vab-icon class="icon" :icon="item.reportIcon" />
  175. <div class="text">
  176. <p class="num">*</p>
  177. <p>{{ item.reportName }}</p>
  178. </div>
  179. <vab-icon v-show="item.checked" class="check" icon="check-fill" />
  180. </div>
  181. </li>
  182. </ul>
  183. </el-tab-pane>
  184. <el-tab-pane label="销售指标" name="second">
  185. <ul class="add-board">
  186. <li v-for="(item, index) in addLayout" :key="index" @click="item.checked = !item.checked">
  187. <div class="board-container">
  188. <vab-icon class="icon" :icon="item.reportIcon" />
  189. <div class="text">
  190. <p class="num">*</p>
  191. <p>{{ item.reportName }}</p>
  192. </div>
  193. <vab-icon v-show="item.checked" class="check" icon="check-fill" />
  194. </div>
  195. </li>
  196. </ul>
  197. </el-tab-pane>
  198. </el-tabs>
  199. <template #footer>
  200. <el-button @click="visible = false">取 消</el-button>
  201. <el-button type="primary" @click="save">确 定</el-button>
  202. </template>
  203. </el-dialog>
  204. <!--通知公告详情-->
  205. <notice-details ref="notice-details" />
  206. </div>
  207. </template>
  208. <script>
  209. import NoticeDetails from '@/views/system/notice/details.vue'
  210. import * as echarts from 'echarts'
  211. import VueDragger from 'vuedraggable'
  212. import VueGridLayout from 'vue-grid-layout'
  213. import to from 'await-to-js'
  214. import indexApi from '@/api/index'
  215. import messageApi from '@/api/system/message'
  216. export default {
  217. name: 'Index',
  218. components: {
  219. NoticeDetails,
  220. VueDragger,
  221. GridLayout: VueGridLayout.GridLayout,
  222. GridItem: VueGridLayout.GridItem,
  223. },
  224. data() {
  225. return {
  226. editFlag: false,
  227. privateBoard: [
  228. // {
  229. // report_name: '当前客户数(个)',
  230. // id: 1,
  231. // report_icon: 'account-circle-line',
  232. // checked: false,
  233. // },
  234. // {
  235. // report_name: '新增客户数(个)',
  236. // id: 2,
  237. // report_icon: 'user-add-line',
  238. // checked: false,
  239. // },
  240. // {
  241. // report_name: '跟进客户数(个)',
  242. // id: 3,
  243. // report_icon: 'account-pin-box-line',
  244. // checked: false,
  245. // },
  246. // {
  247. // report_name: '合同订单数(个)',
  248. // id: 4,
  249. // report_icon: 'account-circle-line',
  250. // checked: false,
  251. // },
  252. // {
  253. // report_name: '合同订单金额(元)',
  254. // id: 5,
  255. // report_icon: 'exchange-cny-fill',
  256. // checked: false,
  257. // },
  258. // {
  259. // report_name: '新增机会数(个)',
  260. // id: 6,
  261. // report_icon: 'mail-add-line',
  262. // checked: false,
  263. // },
  264. // {
  265. // report_name: '新增机会金额(元)',
  266. // id: 7,
  267. // report_icon: 'exchange-cny-fill',
  268. // checked: false,
  269. // },
  270. // {
  271. // report_name: '采购合同数(个)',
  272. // id: 8,
  273. // report_icon: 'account-circle-line',
  274. // checked: false,
  275. // },
  276. // {
  277. // report_name: '采购金额(元)',
  278. // id: 9,
  279. // report_icon: 'money-cny-circle-line',
  280. // checked: false,
  281. // },
  282. // {
  283. // report_name: '应收未收(元)',
  284. // id: 10,
  285. // report_icon: 'money-cny-circle-line',
  286. // checked: false,
  287. // },
  288. // {
  289. // report_name: '计划付款(元)',
  290. // id: 11,
  291. // report_icon: 'money-cny-circle-line',
  292. // checked: false,
  293. // },
  294. // {
  295. // report_name: '当前库存成本(元)',
  296. // id: 12,
  297. // report_icon: 'money-cny-circle-line',
  298. // checked: false,
  299. // },
  300. ],
  301. addBoard: [],
  302. addLayout: [],
  303. visible: false,
  304. layout: [],
  305. day: -1,
  306. list: [],
  307. activities: [],
  308. charts: [],
  309. activeName: 'first',
  310. forecast: '1',
  311. date: 0,
  312. dayList: ['日', '一', '二', '三', '四', '五', '六'],
  313. messageList: [],
  314. msgTypeOptions: [],
  315. }
  316. },
  317. mounted() {
  318. this.init()
  319. this.getDateList()
  320. this.getOptions()
  321. this.handleNoticeList()
  322. this.$baseEventBus.$on('receivedMessage', () => {
  323. console.log('---------------通知更新公告----------------')
  324. this.handleNoticeList()
  325. })
  326. window.addEventListener('resize', this.handleResize)
  327. },
  328. beforeDestroy() {
  329. window.removeEventListener('resize', this.handleResize)
  330. },
  331. methods: {
  332. async getHomeNumReportData() {
  333. let ids = this.privateBoard.map((item) => {
  334. return item.id
  335. })
  336. const {
  337. data: { num_report_response },
  338. } = await indexApi.getHomeNumReportData({ ids: Array.from(new Set(ids)) })
  339. let privateBoardData = []
  340. this.privateBoard.forEach((item, index) => {
  341. num_report_response.forEach((val) => {
  342. if (item.id === val.id) {
  343. this.privateBoard[index].report_data = val.data
  344. privateBoardData.push(this.privateBoard[index])
  345. }
  346. })
  347. })
  348. this.privateBoard = privateBoardData
  349. },
  350. // 布局信息
  351. async init() {
  352. const [err, res] = await to(indexApi.getHomeReport({ module_code: 'HomePage' }))
  353. if (err) return
  354. const obj = JSON.parse(res.data.configInfo)
  355. this.privateBoard = obj.num_report_config || []
  356. if (obj.data_report_config && obj.data_report_config.length) {
  357. this.layout = obj.data_report_config.map((item) => {
  358. return {
  359. x: item.location_x,
  360. y: item.location_y,
  361. i: item.id,
  362. desc: item.report_desc,
  363. name: item.report_name,
  364. type: item.report_type,
  365. h: item.size_h,
  366. w: item.size_w,
  367. searchType: item.id != 20006 && item.id != 20007 ? 'year' : '0',
  368. }
  369. })
  370. }
  371. await this.$nextTick()
  372. this.initChart()
  373. this.getHomeNumReportData()
  374. },
  375. initChart() {
  376. // console.log(this.layout, 'layout')
  377. this.charts = []
  378. for (let i = 0; i < this.layout.length; i++) {
  379. this.getChartSize(this.layout[i].i)
  380. if (this.layout[i].i == 20006 || this.layout[i].i == 20007) {
  381. this.drawGoalChart(this.layout[i].i, i)
  382. } else {
  383. this.drawChart(this.layout[i].i, i)
  384. }
  385. }
  386. },
  387. //刷新
  388. refresh(item, index) {
  389. this.charts[index].dispose()
  390. if (item.i == 20006 || item.i == 20007) {
  391. this.drawGoalChart(item.i, index, item.searchType)
  392. } else {
  393. this.drawChart(item.i, index, item.searchType)
  394. }
  395. },
  396. getChartSize(id) {
  397. // console.log(id, 'id')
  398. const bar = document.getElementById(id)
  399. // console.log(bar, 'bar')
  400. bar.style.height = bar.parentNode.clientHeight - 65 + 'px'
  401. bar.style.width = bar.parentNode.clientWidth + 'px'
  402. },
  403. async drawChart(id, i, searchType) {
  404. const chartBar = echarts.init(document.getElementById(id))
  405. const [err, res] = await to(indexApi.getHomeDataReportData({ id, Params: { searchType } }))
  406. if (err) return
  407. console.log(res.data.data)
  408. const option = {
  409. grid: {
  410. bottom: 30,
  411. top: 40,
  412. right: 10,
  413. left: 10,
  414. containLabel: true,
  415. },
  416. tooltip: {
  417. trigger: 'axis',
  418. },
  419. xAxis: [
  420. {
  421. type: 'category',
  422. data: res.data.data.xData,
  423. axisTick: {
  424. alignWithLabel: true,
  425. },
  426. },
  427. ],
  428. yAxis: [
  429. {
  430. type: 'value',
  431. name: '金额(元)',
  432. alignTicks: true,
  433. },
  434. {
  435. type: 'value',
  436. name: res.data.id == 20000 || res.data.id == 20002 ? '签约完成率(%)' : '回款率(%)',
  437. min: 0,
  438. max: 100,
  439. alignTicks: true,
  440. interval: 20,
  441. axisLabel: {
  442. formatter: '{value} %',
  443. },
  444. },
  445. ],
  446. series: [
  447. {
  448. name: '销售指标',
  449. type: 'bar',
  450. data: res.data.data.yDataTarget,
  451. },
  452. {
  453. name: '销售额度',
  454. type: 'bar',
  455. data: res.data.data.yDataReal,
  456. },
  457. {
  458. name: '回款率',
  459. type: 'line',
  460. yAxisIndex: 1,
  461. tooltip: {
  462. valueFormatter: function (value) {
  463. return value + '%'
  464. },
  465. },
  466. data: res.data.data.percentData,
  467. },
  468. ],
  469. }
  470. chartBar.setOption(option)
  471. this.charts[i] = chartBar
  472. },
  473. async drawGoalChart(id, i, searchType) {
  474. const chartBar = echarts.init(document.getElementById(id))
  475. const [err, res] = await to(
  476. indexApi.getHomeDataReportData({
  477. id,
  478. Params: {
  479. year: new Date().getFullYear(),
  480. quarter: parseInt(searchType),
  481. },
  482. })
  483. )
  484. if (err) return
  485. console.log(res.data.data)
  486. const option = {
  487. grid: {
  488. bottom: 30,
  489. top: 40,
  490. right: 10,
  491. left: 10,
  492. containLabel: true,
  493. },
  494. tooltip: {
  495. trigger: 'axis',
  496. },
  497. xAxis: [
  498. {
  499. type: 'category',
  500. data: res.data.data.xData,
  501. axisTick: {
  502. alignWithLabel: true,
  503. },
  504. },
  505. ],
  506. yAxis: [
  507. {
  508. type: 'value',
  509. name: '金额(万元)',
  510. alignTicks: true,
  511. },
  512. ],
  513. series: [
  514. {
  515. name: id == 20006 ? '销售指标' : '回款指标',
  516. type: 'bar',
  517. data: res.data.data.yDataTarget,
  518. },
  519. {
  520. name: id == 20006 ? '销售额度' : '回款额度',
  521. type: 'bar',
  522. data: res.data.data.yDataReal,
  523. },
  524. ],
  525. }
  526. chartBar.setOption(option)
  527. this.charts[i] = chartBar
  528. },
  529. initFunnel() {
  530. this.chartFunnel = echarts.init(document.getElementById('funnel'))
  531. const option = {
  532. tooltip: {
  533. trigger: 'item',
  534. formatter: '{b} : {d}%',
  535. },
  536. label: {
  537. formatter: '{b} {c}',
  538. },
  539. labelLine: {
  540. show: true,
  541. },
  542. series: [
  543. {
  544. name: '销售漏斗',
  545. type: 'funnel',
  546. top: 10,
  547. bottom: 10,
  548. minSize: '20%',
  549. maxSize: '100%',
  550. label: {
  551. position: 'inside',
  552. formatter: '{c}%',
  553. color: '#fff',
  554. },
  555. data: [
  556. { value: 500, name: '初步洽谈' },
  557. { value: 300, name: '深入沟通' },
  558. { value: 250, name: '产品报价' },
  559. { value: 150, name: '成交项目' },
  560. { value: 120, name: '流失项目' },
  561. ],
  562. },
  563. {
  564. name: '销售漏斗',
  565. type: 'funnel',
  566. top: 10,
  567. bottom: 10,
  568. minSize: '20%',
  569. maxSize: '100%',
  570. z: 1,
  571. data: [
  572. { value: 500, name: '初步洽谈' },
  573. { value: 300, name: '深入沟通' },
  574. { value: 250, name: '产品报价' },
  575. { value: 150, name: '成交项目' },
  576. { value: 120, name: '流失项目' },
  577. ],
  578. },
  579. ],
  580. }
  581. this.chartFunnel.setOption(option)
  582. },
  583. async handleResize() {
  584. await this.$nextTick()
  585. for (let i = 0; i < this.layout.length; i++) {
  586. this.getChartSize(this.layout[i].i)
  587. this.charts[i].resize()
  588. }
  589. },
  590. layoutReadyEvent() {
  591. this.initChart()
  592. },
  593. handleDel(index) {
  594. this.privateBoard.splice(index, 1)
  595. },
  596. gridDel(index) {
  597. this.layout.splice(index, 1)
  598. },
  599. async save() {
  600. const board = this.addBoard.filter((item) => item.checked == true)
  601. this.privateBoard = []
  602. for (const item of board) {
  603. this.privateBoard.push({
  604. report_name: item.reportName,
  605. id: item.id,
  606. report_icon: item.reportIcon,
  607. })
  608. }
  609. const layout = this.addLayout.filter((item) => item.checked == true)
  610. for (const item of layout) {
  611. const index = this.layout.findIndex((each) => each.i == item.id)
  612. if (index > -1) continue
  613. this.layout.push({
  614. x: 0,
  615. y: 0,
  616. w: 12,
  617. h: 10,
  618. i: item.id,
  619. type: 'bar',
  620. name: item.reportName,
  621. searchType: 'year',
  622. })
  623. }
  624. this.visible = false
  625. await this.$nextTick()
  626. this.initChart()
  627. },
  628. async saveLayout() {
  629. this.editFlag = false
  630. const params = {
  631. module_code: 'HomePage',
  632. data_report_config: [],
  633. num_report_config: this.privateBoard,
  634. }
  635. params.data_report_config = this.layout.map((item) => {
  636. return {
  637. location_x: item.x,
  638. location_y: item.y,
  639. id: item.i,
  640. report_desc: item.desc,
  641. report_name: item.name,
  642. report_type: item.type,
  643. size_h: item.h,
  644. size_w: item.w,
  645. }
  646. })
  647. const [err, res] = await to(indexApi.setUpHomeConfig(params))
  648. if (err) return
  649. this.$message.success(res.msg)
  650. this.getHomeNumReportData()
  651. },
  652. getDateList() {
  653. const now = new Date()
  654. const date = new Date(now.getTime() + this.date * 1000 * 60 * 60 * 24)
  655. const day = date.getDay()
  656. this.day = day
  657. this.list = new Array(7)
  658. for (let i = 0; i < this.list.length; i++) {
  659. this.list[i] = {}
  660. const flag = -(day - i)
  661. const time = new Date(date.getTime() + flag * 1000 * 60 * 60 * 24)
  662. this.list[i].time = new Date(time)
  663. this.list[i].day = this.list[i].time.getDay()
  664. this.list[i].date = this.list[i].time.getDate()
  665. if (flag == 0) this.dateClick(this.list[i])
  666. }
  667. },
  668. async dateClick(item) {
  669. this.day = item.day
  670. const [err, res] = await to(indexApi.getSchedule({ schDate: this.parseTime(item.time) }))
  671. if (err) return
  672. this.activities = res.data.list || []
  673. const date = new Date().getTime()
  674. this.activities.forEach((item) => {
  675. const start = new Date(item.schDate).getTime()
  676. const end = new Date(item.schDateEnd).getTime()
  677. item.active = start <= date && end >= date
  678. })
  679. },
  680. async getBoardOptions() {
  681. this.activeName = 'first'
  682. const [err, res] = await to(indexApi.getRoleReportList())
  683. if (err) return
  684. console.log(res)
  685. const arr = res.data.list || []
  686. arr.forEach((item) => (item.checked = false))
  687. this.addLayout = arr.filter((item) => item.reportType == 20)
  688. this.addLayout.forEach((item) => {
  689. const index = this.layout.findIndex((each) => each.i == item.id)
  690. if (index > -1) item.checked = true
  691. })
  692. this.addBoard = arr.filter((item) => item.reportType == 10)
  693. this.addBoard.forEach((item) => {
  694. const index = this.privateBoard.findIndex((each) => each.id == item.id)
  695. if (index > -1) item.checked = true
  696. })
  697. },
  698. getOptions() {
  699. this.getDicts('sys_msg_type').then((response) => {
  700. this.msgTypeOptions = response.data.values || []
  701. })
  702. },
  703. msgTypeFormat(row) {
  704. return this.selectDictLabel(this.msgTypeOptions, row.msgType)
  705. },
  706. handleHistoryNotice() {
  707. this.$router.push({
  708. name: 'NoticeHistory',
  709. })
  710. },
  711. async handleNoticeList() {
  712. const { data } = await messageApi.getList({ msgType: '10' })
  713. this.messageList = data.list
  714. },
  715. getNoticeInfo(row) {
  716. this.$refs['notice-details'].open(row.id)
  717. },
  718. },
  719. }
  720. </script>
  721. <style lang="scss" scoped>
  722. $base: '.index';
  723. #{$base} {
  724. display: flex;
  725. background: #f6f8f9;
  726. height: calc(100vh - 60px - 12px * 2 - 40px);
  727. .el-card {
  728. margin-bottom: 12px;
  729. border-radius: 4px;
  730. }
  731. .card-title {
  732. font-size: 16px;
  733. font-weight: 700;
  734. line-height: 22px;
  735. height: 22px;
  736. display: flex;
  737. justify-content: space-between;
  738. align-items: center;
  739. i {
  740. font-weight: normal;
  741. cursor: pointer;
  742. &:hover {
  743. color: #1d66dc;
  744. }
  745. }
  746. }
  747. .left {
  748. flex: 1;
  749. margin-right: 10px;
  750. overflow-x: hidden;
  751. overflow-y: auto;
  752. .buttons {
  753. .el-radio-group {
  754. margin-right: 4px;
  755. }
  756. }
  757. .board {
  758. display: flex;
  759. flex-direction: column;
  760. margin-bottom: 2px;
  761. ::v-deep .el-card__body {
  762. flex: 1;
  763. display: flex;
  764. flex-direction: column;
  765. justify-content: space-around;
  766. }
  767. .el-row {
  768. height: 100%;
  769. flex-wrap: wrap;
  770. > div,
  771. > div span {
  772. display: flex;
  773. height: 100%;
  774. align-items: center;
  775. flex-wrap: wrap;
  776. width: 100%;
  777. }
  778. .ghost {
  779. background: #ecf5ff;
  780. }
  781. .drag {
  782. background: pink;
  783. }
  784. .el-col {
  785. display: flex;
  786. height: 60px;
  787. margin: 20px 0;
  788. user-select: none;
  789. cursor: pointer;
  790. }
  791. }
  792. }
  793. }
  794. .right {
  795. width: 450px;
  796. .detail {
  797. height: 64px;
  798. border: 1px solid rgb(235, 237, 240);
  799. border-radius: 4px;
  800. &.active {
  801. border: 1px solid #d7e8f4;
  802. background: #f7fbfe;
  803. p:last-child {
  804. font-weight: bold;
  805. }
  806. }
  807. p {
  808. height: 32px;
  809. line-height: 32px;
  810. padding: 0 10px;
  811. white-space: nowrap;
  812. overflow: hidden;
  813. text-overflow: ellipsis;
  814. }
  815. .time {
  816. display: flex;
  817. justify-content: space-between;
  818. align-items: center;
  819. }
  820. }
  821. .calendar {
  822. height: 60%;
  823. border-radius: 4px;
  824. display: flex;
  825. flex-direction: column;
  826. ::v-deep .el-card__body {
  827. flex: 1;
  828. overflow: hidden;
  829. }
  830. .week {
  831. height: 48px;
  832. display: flex;
  833. align-items: center;
  834. ul {
  835. flex: 1;
  836. display: flex;
  837. height: 48px;
  838. li {
  839. flex: 1;
  840. display: flex;
  841. flex-direction: column;
  842. align-items: center;
  843. justify-content: center;
  844. user-select: none;
  845. cursor: pointer;
  846. &.active span:last-child {
  847. background: rgb(34, 76, 218);
  848. color: #fff;
  849. }
  850. span {
  851. flex: 1;
  852. height: 24px;
  853. width: 24px;
  854. text-align: center;
  855. line-height: 24px;
  856. }
  857. span:first-child {
  858. color: rgb(150, 151, 153);
  859. }
  860. span:last-child {
  861. color: rgb(50, 50, 51);
  862. border-radius: 4px;
  863. }
  864. }
  865. }
  866. i {
  867. height: 24px;
  868. width: 24px;
  869. font-size: 24px;
  870. color: rgb(100, 101, 102);
  871. background: rgb(247, 248, 250);
  872. border-radius: 4px;
  873. cursor: pointer;
  874. transition: 0.3s all;
  875. &:hover {
  876. color: #fff;
  877. background: rgb(100, 101, 102);
  878. }
  879. }
  880. }
  881. .el-timeline {
  882. margin-top: 20px;
  883. padding-top: 4px;
  884. height: calc(100% - 72px);
  885. overflow: auto;
  886. .el-timeline-item {
  887. padding-bottom: 10px;
  888. }
  889. }
  890. }
  891. .notice {
  892. height: calc(40% - 12px);
  893. display: flex;
  894. flex-direction: column;
  895. ::v-deep .el-card__body {
  896. flex: 1;
  897. overflow: auto;
  898. }
  899. .notice-list {
  900. display: flex;
  901. justify-content: space-between;
  902. height: 40px;
  903. line-height: 40px;
  904. border-bottom: 1px solid #ebeef5;
  905. p:first-child {
  906. flex: 1;
  907. min-width: 0;
  908. overflow: hidden;
  909. text-overflow: ellipsis;
  910. white-space: nowrap;
  911. }
  912. span {
  913. font-weight: bold;
  914. }
  915. }
  916. }
  917. }
  918. }
  919. ::v-deep .el-timeline-item__tail {
  920. border-left: 2px dashed #e4e7ed;
  921. }
  922. // #funnel,
  923. // #bar {
  924. // height: 100%;
  925. // }
  926. .vue-grid-layout {
  927. margin-left: -10px;
  928. margin-right: -10px;
  929. &.grid {
  930. background: linear-gradient(90deg, #fff 10px, transparent 0), linear-gradient(#fff 10px, transparent 0);
  931. background-size: calc((100% - 10px) / 24) 40px;
  932. background-position: top 0px left 0px;
  933. }
  934. }
  935. .vue-grid-item {
  936. background: #fff;
  937. border: 1px solid #ebeef5;
  938. border-radius: 4px;
  939. box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
  940. overflow: hidden;
  941. h4 {
  942. line-height: 22px;
  943. height: 54px;
  944. font-size: 16px;
  945. padding: 16px 20px;
  946. display: flex;
  947. justify-content: space-between;
  948. align-items: center;
  949. border-bottom: 1px solid #ebeef5;
  950. i {
  951. font-size: 24px;
  952. font-weight: bold;
  953. cursor: pointer;
  954. &:hover {
  955. color: #1d66dc;
  956. }
  957. }
  958. }
  959. }
  960. .board-container {
  961. display: flex;
  962. height: 60px;
  963. border: 1px solid #eee;
  964. align-items: center;
  965. border-radius: 8px;
  966. padding: 0 10px;
  967. flex: 1;
  968. cursor: pointer;
  969. user-select: none;
  970. &:hover {
  971. box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
  972. }
  973. .icon {
  974. font-size: 32px;
  975. border-radius: 4px;
  976. width: 40px;
  977. height: 40px;
  978. color: #1d66dc;
  979. background: #ecf5ff;
  980. &.ri-money-cny-circle-line {
  981. color: #e6a23c;
  982. background: #fdf6ec;
  983. }
  984. &.ri-exchange-cny-fill {
  985. color: #f56c6c;
  986. background: #fef0f0;
  987. }
  988. }
  989. .full-icon {
  990. flex: 1;
  991. font-size: 32px;
  992. }
  993. .close {
  994. font-size: 32px;
  995. transition: 0.3s all;
  996. &:hover {
  997. color: #1d66dc;
  998. }
  999. }
  1000. .check {
  1001. font-size: 32px;
  1002. color: #67c23a;
  1003. }
  1004. .text {
  1005. padding-left: 10px;
  1006. color: #9499a0;
  1007. flex: 1;
  1008. .num {
  1009. font-size: 22px;
  1010. font-weight: bold;
  1011. color: #333;
  1012. }
  1013. }
  1014. }
  1015. .add-board {
  1016. height: 500px;
  1017. overflow: auto;
  1018. li + li {
  1019. margin-top: 10px;
  1020. }
  1021. }
  1022. </style>