index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. <template>
  2. <div class="h-100p">
  3. <div class="h-100p flex flex-d-c">
  4. <div class="bgc-w border-10 flex1 flex flex-d-c" style="height:0">
  5. <div class="active-menus">
  6. <div class="active-item" :class="{'active-select':activeIndex == 1}" @click="handleSelect(1)">
  7. <img :src="'/img/welIndex/Frame25'+(activeIndex == 1?'4':'7')+'.png'" alt="">
  8. <div style="margin-top:5px;">
  9. <el-badge :value="0" :hidden="true" class="item">
  10. <span class="font-bold">我的代办工单&nbsp;</span>
  11. </el-badge>
  12. </div>
  13. </div>
  14. <div class="triangle-bottomleft" :class="{'b-b-b-w':activeIndex == 1}" ></div>
  15. <div class="active-item" :class="{'active-select':activeIndex == 2}" @click="handleSelect(2)">
  16. <img :src="'/img/welIndex/Frame25'+(activeIndex == 2?'6':'5')+'.png'" alt="">
  17. <div style="margin-top:5px;">
  18. <el-badge :value="0" :hidden="true" class="item">
  19. <span class="font-bold">所有代办工单&nbsp;</span>
  20. </el-badge>
  21. </div>
  22. </div>
  23. <div class="triangle-bottomleft" :class="{'b-b-b-w':activeIndex == 2}"></div>
  24. </div>
  25. <el-scrollbar class="flex1">
  26. <div class="pd-10" v-show="activeIndex == 1">
  27. <div v-if="collapseData1.length == 0" class="flex flex-center">
  28. <div>
  29. <img src="/img/welIndex/empty.png" alt="">
  30. <div class="text-align-c" style="color:#CCD0DE;font-size:14px;">没有找到代办工单~</div>
  31. </div>
  32. </div>
  33. <div v-else class="info-item" v-for="(item,key) in collapseData1" :key="key">
  34. <div class="flex jc-al-c">
  35. <img style="width:14px;height:14px;" src="/img/welIndex/warning.png" alt="">
  36. <span class="mg-l-10" style="font-size:14px;">{{item.title}}</span>
  37. </div>
  38. <el-dropdown>
  39. <span class="el-dropdown-link">
  40. <el-link icon="el-icon-more" :underline="false" style="transform:rotate(90deg)"></el-link>
  41. </span>
  42. <el-dropdown-menu slot="dropdown">
  43. <el-dropdown-item @click.native="openPreview(item)">立即处理</el-dropdown-item>
  44. <el-dropdown-item @click.native="ignore(key)">忽略</el-dropdown-item>
  45. </el-dropdown-menu>
  46. </el-dropdown>
  47. </div>
  48. <div v-if="collapseData1.length == 1" style="color:#CCD0DE;">
  49. 没有更多数据了~
  50. </div>
  51. </div>
  52. <div class="pd-10" v-show="activeIndex == 2">
  53. <div v-if="collapseData2.length == 0" class="flex flex-center">
  54. <div>
  55. <img src="/img/welIndex/empty.png" alt="">
  56. <div class="text-align-c" style="color:#CCD0DE;font-size:14px;">没有找到代办工单~</div>
  57. </div>
  58. </div>
  59. <div v-else class="info-item" v-for="(item,key) in collapseData2" :key="key">
  60. <div class="flex jc-al-c">
  61. <img style="width:14px;height:14px;" src="/img/welIndex/warning.png" alt="">
  62. <span class="mg-l-10" style="font-size:14px;">{{item.title}}</span>
  63. </div>
  64. <el-dropdown v-if="item.operation">
  65. <span class="el-dropdown-link">
  66. <el-link icon="el-icon-more" :underline="false" style="transform:rotate(90deg)"></el-link>
  67. </span>
  68. <el-dropdown-menu slot="dropdown">
  69. <el-dropdown-item @click.native="openPreview(item)">立即处理</el-dropdown-item>
  70. <el-dropdown-item @click.native="ignore2(key)">忽略</el-dropdown-item>
  71. </el-dropdown-menu>
  72. </el-dropdown>
  73. </div>
  74. <div v-if="collapseData2.length == 1" style="color:#CCD0DE;">
  75. 没有更多数据了~
  76. </div>
  77. </div>
  78. </el-scrollbar>
  79. </div>
  80. <div style="height:500px">
  81. <el-row :gutter="20" class="mg-t-20">
  82. <el-col :span="8">
  83. <div class="bgc-w pd-10 border-10">
  84. <div class="chart-title">
  85. <img src="/img/welIndex/Frame001.png" alt="">
  86. <span class="mg-t-10">
  87. 证书统计
  88. <span class="title-tip">(总计{{pfxAmount}}个)</span>
  89. </span>
  90. </div>
  91. <div id="container"></div>
  92. </div>
  93. </el-col>
  94. <el-col :span="8">
  95. <div class="bgc-w pd-10 border-10">
  96. <div class="chart-title">
  97. <img src="/img/welIndex/Frame002.png" alt="">
  98. <span class="mg-t-10">
  99. 人员账户统计
  100. <span class="title-tip">(总计{{personAmount}}个)</span>
  101. </span>
  102. </div>
  103. <div id="container2"></div>
  104. </div>
  105. </el-col>
  106. <el-col :span="8">
  107. <div class="bgc-w pd-10 border-10">
  108. <div class="chart-title jc-sb">
  109. <div class="flex">
  110. <img src="/img/welIndex/Frame003.png" alt="">
  111. <span class="mg-t-10">
  112. 维护类型统计汇总
  113. </span>
  114. </div>
  115. <el-select v-model="projectId" placeholder="请选择项目" size="small" @change="selectProjectOpinion" style="width:340px;margin-top:6px;" class="index-select">
  116. <el-option
  117. v-for="item in projectList"
  118. :key="item.id"
  119. :label="item.projectName"
  120. :value="item.id"
  121. ></el-option>
  122. </el-select>
  123. </div>
  124. <div id="container3"></div>
  125. </div>
  126. </el-col>
  127. </el-row>
  128. </div>
  129. </div>
  130. <el-dialog
  131. title=""
  132. :visible.sync="imgVisible"
  133. append-to-body
  134. width="80%">
  135. <div class="mg-b-20">{{curNew.projectName}}——{{curNew.contractName}}</div>
  136. <div class="mg-b-20">问题描述:{{curNew.opinionContent}}</div>
  137. <div class="mg-b-20">
  138. <span class="mg-r-20">反馈人员:{{curNew.submitUserName}}</span>
  139. <span class="mg-r-20">电话:{{curNew.submitPhone}}</span>
  140. <span class="mg-r-20">岗位:{{curNew.submitUserRole}}</span>
  141. </div>
  142. <div class="mg-b-20">反馈时间:{{curNew.manageTime}}</div>
  143. <el-carousel trigger="click" height="500px" :autoplay="false" style="text-align:center;">
  144. <el-carousel-item v-for="item in curNew.fileUrl" :key="item">
  145. <el-image :src="item" fit="scale-down" style="height:500px;" :preview-src-list="curNew.fileUrl"></el-image>
  146. </el-carousel-item>
  147. </el-carousel>
  148. <span slot="footer" class="dialog-footer">
  149. <el-button @click="imgVisible = false">取 消</el-button>
  150. <el-button type="primary" @click="toDisposeView">处 理</el-button>
  151. </span>
  152. </el-dialog>
  153. <el-dialog
  154. :visible.sync="dialogVisible"
  155. width="600px"
  156. append-to-body
  157. :close-on-click-modal="false"
  158. >
  159. <div class="flex jc-al-c">
  160. <span class="mg-r-20 titl-font" style="font-size: 16px;margin-right:20px;width:180px;text-align:right;font-weight: 900;">提交进度</span>
  161. <el-select v-model="curRow.currentLink" placeholder="请选择" @change="operationMethods">
  162. <el-option label="进入人工预处理环境" :value="2"/>
  163. <el-option label="已解决" :value="3"/>
  164. </el-select>
  165. </div>
  166. <div style="margin-top:40px;" class="flex jc-al-c" v-show="operation.show">
  167. <span style="font-size: 16px;margin-right:20px;width:180px;text-align:right;font-weight: 900;">*预计处理截止时间</span>
  168. <el-date-picker v-model="operation.manageTime" type="date" format="yyyy-MM-dd" value-format="yyyy-MM-dd" placeholder="选择日期" :picker-options="setDateRange"/>
  169. </div>
  170. <span slot="footer" class="dialog-footer">
  171. <el-button @click="dialogVisible = false">取 消</el-button>
  172. <el-button type="primary" @click="submissionProgress">确 定</el-button>
  173. </span>
  174. </el-dialog>
  175. </div>
  176. </template>
  177. <script>
  178. // 引入统计图
  179. import { Area, Column, Pie } from '@antv/g2plot';
  180. import { queryBusinessUserOpinion, queryBusinessUserOpinionAll, queryProjectUserAmount, queryProjectPfx, queryOpinionTypeAmount, queryOpinionDetails, isIgnore, manageUserOperationStatus } from '@/api/news/news.js';
  181. export default {
  182. data () {
  183. return {
  184. setDateRange: {
  185. disabledDate: time => {
  186. return time.getTime() < Date.now() - 8.64e7
  187. }
  188. },
  189. LineData: [],//折线统计图数据
  190. ColumnData: [],//条形统计图数据
  191. personAmount: 0,
  192. PieData: [],//饼图统计图数据
  193. activeIndex: '1',
  194. collapseData1: [],//
  195. collapseData2: [],//
  196. userOpinionTypeList:[],
  197. projectList:[],
  198. projectId:'',
  199. imgVisible:false,
  200. dialogVisible: false,
  201. curNew:{},
  202. curRow:{},
  203. operation: {
  204. manageTime: '',
  205. show: false
  206. },
  207. pieObj:null,
  208. pfxAmount: 0,
  209. cancel: true,
  210. }
  211. },
  212. methods: {
  213. operationMethods () {
  214. this.operation.show = this.curRow.currentLink == 2;
  215. if (!this.operation.show) {
  216. this.operation.manageTime = '';
  217. }
  218. },
  219. toDisposeView(){
  220. this.dialogVisible = true;
  221. this.operationMethods();
  222. },
  223. async submissionProgress(){
  224. if (this.curRow.currentLink == 2) {
  225. if (this.operation.manageTime) {
  226. await this.manageUserOperationStatus({
  227. currentLinkId: this.curRow.currentLinkId,
  228. currentLink: this.curRow.currentLink,
  229. newNumber: this.curRow.newNumber,
  230. userOpinionId: this.curRow.userOpinionId,
  231. manageTime: this.operation.manageTime
  232. })
  233. } else {
  234. this.$message({
  235. type: 'error',
  236. message: '请设置预计处理截止时间'
  237. })
  238. }
  239. } else {
  240. await this.manageUserOperationStatus({
  241. currentLinkId: this.curRow.currentLinkId,
  242. currentLink: this.curRow.currentLink,
  243. newNumber: this.curRow.newNumber,
  244. userOpinionId: this.curRow.userOpinionId
  245. })
  246. }
  247. if(this.cancel){
  248. this.$confirm('是否前往消息区继续处理?', '提示', {
  249. confirmButtonText: '确定',
  250. cancelButtonText: '取消',
  251. type: 'warning'
  252. }).then(() => {
  253. this.$router.push({
  254. path: '/news/news'
  255. });
  256. }).catch(() => {
  257. this.cancel = false;
  258. });
  259. }
  260. },
  261. async manageUserOperationStatus (da) {//业务人员提交环节操作
  262. const { data: res } = await manageUserOperationStatus(da)
  263. if (res.code == 200) {
  264. this.dialogVisible = false
  265. this.imgVisible = false;
  266. this.$message({
  267. type: 'success',
  268. message: '提交成功!'
  269. })
  270. //重新刷新列表
  271. if(this.activeIndex == 2){
  272. await this.queryBusinessUserOpinionAll();
  273. } else {
  274. await this.queryBusinessUserOpinion();
  275. }
  276. }
  277. },
  278. async openPreview(row){
  279. const { data : res } = await queryOpinionDetails({userOpinionId : row.userOpinionId});
  280. if(res.code == 200){
  281. this.curNew = res.data;
  282. }
  283. this.curRow = row;
  284. this.imgVisible = true;
  285. },
  286. selectProjectOpinion(){
  287. let _this = this;
  288. _this.PieData = [];
  289. this.userOpinionTypeList.forEach(obj => {
  290. if(obj.projectId == _this.projectId){
  291. let problemType = obj.problemType;
  292. let problemTypeAmount = obj.problemTypeAmount;
  293. problemType.forEach((name, index) => {
  294. _this.PieData.push({type : name, value : Number(problemTypeAmount[index])});
  295. });
  296. }
  297. })
  298. this.pieObj.changeData(_this.PieData);
  299. },
  300. async handleSelect (key) {
  301. if (key == 2) {
  302. await this.queryBusinessUserOpinionAll();
  303. } else {
  304. await this.queryBusinessUserOpinion();
  305. }
  306. this.activeIndex = key
  307. },
  308. async queryBusinessUserOpinion () {//获取我的消息
  309. const { data: res } = await queryBusinessUserOpinion({
  310. current: 1,
  311. size: 999999
  312. })
  313. if (res.code == 200) {
  314. this.collapseData1 = res.data.userOpinionFlowList
  315. this.menuTag = res.data.operation
  316. if(this.collapseData1.length < 1){
  317. this.handleSelect('2')
  318. }
  319. }
  320. },
  321. async queryBusinessUserOpinionAll () {
  322. const { data: res } = await queryBusinessUserOpinionAll({
  323. current: 1,
  324. size: 999999
  325. })
  326. if (res.code == 200) {
  327. this.collapseData2 = res.data.userOpinionFlowList;
  328. }
  329. },
  330. ignore (key) {
  331. this.isIgnore(this.collapseData1[key].userOpinionId);
  332. this.collapseData1.splice(this.collapseData1.indexOf(this.collapseData1[key]), 1);
  333. if(this.collapseData2.length > 0){
  334. let removeIndex = -1;
  335. this.collapseData2.forEach((vo,index) => {
  336. if(this.collapseData1[key].userOpinionId == vo.userOpinionId){
  337. removeIndex = index;
  338. }
  339. });
  340. if(removeIndex > -1){
  341. this.collapseData2.splice(removeIndex, 1);
  342. }
  343. }
  344. },
  345. ignore2 (key) {
  346. this.isIgnore(this.collapseData2[key].userOpinionId);
  347. this.collapseData2.splice(this.collapseData2.indexOf(this.collapseData2[key]), 1);
  348. if(this.collapseData1.length > 0){
  349. let removeIndex = -1;
  350. this.collapseData1.forEach((vo,index) => {
  351. if(this.collapseData2[key].userOpinionId == vo.userOpinionId){
  352. removeIndex = index;
  353. }
  354. });
  355. if(removeIndex > -1){
  356. this.collapseData1.splice(removeIndex, 1);
  357. }
  358. }
  359. },
  360. async isIgnore(userOpinionId){
  361. const {data : res} = await isIgnore({userOpinionId : userOpinionId});
  362. },
  363. async LineV () {
  364. await this.queryProjectPfx();
  365. const line = new Area('container', {
  366. data: this.LineData,
  367. xField: 'project',
  368. yField: 'value',
  369. seriesField: 'category',
  370. isStack:false,
  371. legend: {
  372. position: 'top-left',
  373. offsetX:'12',
  374. marker:{
  375. symbol:'circle'
  376. }
  377. },
  378. autoFit:true,
  379. scrollbar: {
  380. type: 'vertical'
  381. },
  382. xAxis:{
  383. label:{
  384. autoRotate:true,
  385. autoHide:false,
  386. }
  387. }
  388. });
  389. line.render();
  390. },
  391. async ColumnV () {
  392. await this.queryProjectUserAmount();
  393. const columnPlot = new Column('container2', {
  394. data: this.ColumnData,
  395. isGroup: true,
  396. xField: 'project',
  397. yField: 'value',
  398. seriesField: 'category',
  399. /** 设置颜色 */
  400. //color: ['#1ca9e6', '#f88c24'],
  401. /** 设置间距 */
  402. // marginRatio: 0.1,
  403. label: {
  404. // 可手动配置 label 数据标签位置
  405. position: 'middle', // 'top', 'middle', 'bottom'
  406. // 可配置附加的布局方法
  407. layout: [
  408. // 柱形图数据标签位置自动调整
  409. { type: 'interval-adjust-position' },
  410. // 数据标签防遮挡
  411. { type: 'interval-hide-overlap' },
  412. // 数据标签文颜色自动调整
  413. { type: 'adjust-color' },
  414. ],
  415. },
  416. legend: {
  417. position: 'top-left',
  418. offsetX:'12',
  419. marker:{
  420. symbol:'circle'
  421. }
  422. },
  423. });
  424. columnPlot.render();
  425. },
  426. async PieV () {
  427. await this.queryOpinionTypeAmount();
  428. const piePlot = new Pie('container3', {
  429. appendPadding: 10,
  430. data: this.PieData,
  431. angleField: 'value',
  432. colorField: 'type',
  433. radius: 0.75,
  434. label: {
  435. type: 'spider',
  436. labelHeight: 28,
  437. content: '{name}\n{value}',
  438. },
  439. interactions: [{ type: 'element-selected' }, { type: 'element-active' }],
  440. legend: {
  441. position: 'top-left',
  442. offsetX:'12',
  443. marker:{
  444. symbol:'circle'
  445. }
  446. },
  447. });
  448. piePlot.render();
  449. this.pieObj = piePlot;
  450. },
  451. //获取人员账户
  452. async queryProjectUserAmount(){
  453. const {data : res} = await queryProjectUserAmount();
  454. let records = res.data.projectUserAmountVOList;
  455. records.forEach(vo => {
  456. this.ColumnData.push({"category": "施工方","project": vo.projectName,"value": vo.contractor});
  457. this.ColumnData.push({"category": "监理方","project": vo.projectName,"value": vo.supervision});
  458. this.ColumnData.push({"category": "指挥部","project": vo.projectName,"value": vo.owner});
  459. });
  460. this.personAmount = res.data.projectUserAmount;
  461. },
  462. //证书统计
  463. async queryProjectPfx(){
  464. let _this = this;
  465. const {data : res} = await queryProjectPfx();
  466. let pfxList = res.data.pfxList;
  467. pfxList.forEach(vo => {
  468. _this.LineData.push({"category": "个人证书","project": vo.projectName,"value": Number(vo.personalCount)});
  469. _this.LineData.push({"category": "企业证书","project": vo.projectName,"value": Number(vo.enterpriseCount)});
  470. });
  471. this.pfxAmount = res.data.pfxAmount;
  472. },
  473. //维护类型统计汇总
  474. async queryOpinionTypeAmount(){
  475. let _this = this;
  476. const {data : res} = await queryOpinionTypeAmount();
  477. this.userOpinionTypeList = res.data.userOpinionList;
  478. this.projectList = res.data.projectInfos;
  479. let userOpinion = this.userOpinionTypeList[0];
  480. let problemType = userOpinion.problemType;
  481. let problemTypeAmount = userOpinion.problemTypeAmount;
  482. this.projectId = userOpinion.projectId;
  483. problemType.forEach((name, index) => {
  484. _this.PieData.push({type : name, value : Number(problemTypeAmount[index])});
  485. });
  486. }
  487. },
  488. created () {
  489. this.queryBusinessUserOpinion();
  490. },
  491. mounted () {
  492. this.LineV()
  493. this.ColumnV()
  494. this.PieV()
  495. }
  496. }
  497. </script>
  498. <style lang="scss" scoped>
  499. .el-menu.el-menu--horizontal {
  500. display: flex;
  501. justify-content: center;
  502. }
  503. .el-menu-item,
  504. .el-submenu__title {
  505. line-height: 40px;
  506. height: 40px;
  507. }
  508. .el-menu-item {
  509. padding: 0px;
  510. margin: 0 20px;
  511. }
  512. .information {
  513. margin-top: 20px;
  514. background-color: #faf8fd;
  515. box-sizing: border-box;
  516. border-radius: 4px;
  517. overflow: auto;
  518. }
  519. .el-collapse {
  520. border: none;
  521. }
  522. /deep/.el-collapse-item__header {
  523. background-color: #faf8fd;
  524. border-bottom: none;
  525. box-sizing: border-box;
  526. padding-left: 20px;
  527. color: red;
  528. }
  529. /deep/.el-collapse-item__content {
  530. background-color: #f7f7f7;
  531. box-sizing: border-box;
  532. padding-left: 20px;
  533. padding-bottom: 0px;
  534. }
  535. .visualization {
  536. height: calc(60% - 60px);
  537. }
  538. .chart-title{
  539. display: flex;
  540. font-weight: 700;
  541. font-size: 16px;
  542. color: #1A1A1A;
  543. }
  544. .title-tip{
  545. font-size: 14px;
  546. color: #838791;
  547. }
  548. .boxswai{
  549. height: 100%;
  550. box-sizing: border-box;
  551. padding-bottom: 10px;
  552. }
  553. .active-menus{
  554. height: 62px;
  555. display: flex;
  556. background-color: #F8F8F8;
  557. border-radius: 10px 10px 0 0;
  558. }
  559. .active-item{
  560. padding: 10px;
  561. display: flex;
  562. cursor: pointer;
  563. }
  564. .active-select{
  565. background-color: #FFFFFF;
  566. border-radius: 10px 0 0 0 ;
  567. }
  568. .triangle-bottomleft {
  569. width: 0;
  570. height: 0;
  571. border-bottom: 62px solid #F8F8F8;
  572. border-right: 30px solid transparent;
  573. }
  574. .b-b-b-w{
  575. border-bottom-color: #FFFFFF;
  576. }
  577. .info-item{
  578. background-color: #F8FAFF;
  579. border: 1px solid #EEEEEE;
  580. margin-bottom: 10px;
  581. padding: 10px;
  582. border-radius: 10px;
  583. display: flex;
  584. justify-content: space-between;
  585. color: #50545E;
  586. }
  587. .index-select{
  588. /deep/.el-input__inner{
  589. border: none;
  590. background-color: #F7F9FD;
  591. border-radius: 64px;
  592. }
  593. }
  594. </style>