index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. <template>
  2. <el-row
  3. v-loading="loading"
  4. :gutter="10"
  5. class="qualityModule"
  6. >
  7. <el-col :span="9">
  8. <div class="itemBox onlineProblem" style="height: 470px">
  9. <div class="titleLevel3 mb10">线上问题</div>
  10. <el-row :gutter="10">
  11. <el-col
  12. v-for="(item, index) in mainData.onlineProblemList"
  13. :key="index"
  14. :span="12"
  15. class="mb10"
  16. >
  17. <div
  18. :style="{ cursor: item.label == '新增问题' && 'pointer'}"
  19. @click.stop="onDetial('线上问题', item)"
  20. >
  21. <dataItem :item="item" />
  22. </div>
  23. </el-col>
  24. </el-row>
  25. <div class="chartSearchbar inlineBetween mt15">
  26. <div style="width: 200px">
  27. <span style="margin-right: 10px">分布类型:</span>
  28. <el-select
  29. v-model="onlineProblemViweType"
  30. size="small"
  31. filterable
  32. style="width: 115px"
  33. @change="$emit('search', { onlineProblemViweType })"
  34. >
  35. <el-option v-for="(t, index) in onlineProblemSelect" :key="index" :label="t" :value="t" />
  36. </el-select>
  37. </div>
  38. </div>
  39. <div v-loading="onlineLoading" class="chartViewHeight">
  40. <normal-echart v-if="onlineProblemChartOption" :chart-id="'chart1'" :option="onlineProblemChartOption" @onClick="changeList" />
  41. </div>
  42. </div>
  43. </el-col>
  44. <el-col :span="9">
  45. <div class="itemBox quality" style="height: 470px">
  46. <div class="titleLevel3 mb10">线下质量</div>
  47. <el-row :gutter="10">
  48. <el-col v-for="(item, index) in mainData.offlineProblemList" :key="index" :span="12" class="mb10">
  49. <div
  50. :style="{ cursor: 'pointer'}"
  51. @click.stop="onDetial('线下质量', item)"
  52. >
  53. <dataItem :item="item" />
  54. </div>
  55. </el-col>
  56. </el-row>
  57. <div class="chartSearchbar inlineBetween mt15">
  58. <div style="width: 200px">
  59. <span style="margin-right: 10px">分布类型:</span>
  60. <el-select
  61. v-model="offlineProblemViweType"
  62. size="small"
  63. filterable
  64. style="width: 115px"
  65. @change="$emit('search', { offlineProblemViweType })"
  66. >
  67. <el-option v-for="(t, index) in offlineProblemSelect" :key="index" :label="t" :value="t" />
  68. </el-select>
  69. </div>
  70. </div>
  71. <div v-loading="offlineLoading" class="chartViewHeight">
  72. <normal-echart v-if="offlineProblemChartOption" :chart-id="'chart2'" :option="offlineProblemChartOption" @onClick="changeList" />
  73. </div>
  74. </div>
  75. </el-col>
  76. <el-col :span="6">
  77. <div class="itemBox progress" style="min-height: 328px">
  78. <div class="titleLevel3 mb10">上线过程</div>
  79. <el-row v-if="mainData.onlineProcessList" :gutter="10">
  80. <el-col
  81. v-for="(item, index) in mainData.onlineProcessList.list"
  82. :key="index"
  83. :span="12"
  84. class="mb10"
  85. >
  86. <div
  87. :style="{ cursor: item.label !== '免测上线率' && 'pointer'}"
  88. @click.stop="onDetial('上线过程', {...item, parent: mainData.onlineProcessList, index: index, moduleName: '上线过程' })"
  89. >
  90. <dataItem :item="item" />
  91. </div>
  92. </el-col>
  93. </el-row>
  94. <el-divider class="divider" />
  95. <el-row
  96. v-if="mainData.onlineProcessList"
  97. :gutter="10"
  98. class="rollBack"
  99. style="margin-top: 40px"
  100. >
  101. <el-col :span="12" class="mb10">
  102. <span class="rollBackItem" :style="{ cursor: 'pointer'}" @click.stop="onDetial('上线过程', {...mainData.onlineProcessList.rollBack, itemKey: 'countStr', index: 0, label:'回滚次数', moduleName: '回滚次数'})">
  103. <span class="bigCircle" style="background: #7ED321" />
  104. 回滚
  105. <span class="numText" style="font-size: 20px">{{ mainData.onlineProcessList.rollBack.countStr.countStr }}</span>
  106. </span>
  107. </el-col>
  108. <el-col :span="12" class="mb10">
  109. <div class="rollBackItem pointer" @click.stop="onDetial('上线过程', {...mainData.onlineProcessList.rollBack, itemKey: 'preCountStr', index: 1, label:'预发回滚', moduleName: '回滚次数'})">
  110. <span class="circle" style="background: #3F9DFE" />
  111. 预发回滚
  112. <span class="numText">{{ mainData.onlineProcessList.rollBack.preCountStr.countStr }}</span>
  113. </div>
  114. <div class="rollBackItem pointer" @click.stop="onDetial('上线过程', {...mainData.onlineProcessList.rollBack, itemKey: 'fullCountStr', index: 2, label:'全量回滚', moduleName: '回滚次数'})">
  115. <span class="circle" style="background: #3F9DFE" />
  116. 全量回滚
  117. <span class="numText">{{ mainData.onlineProcessList.rollBack.fullCountStr.countStr }}</span>
  118. </div>
  119. <div class="rollBackItem pointer" @click.stop="onDetial('上线过程',{...mainData.onlineProcessList.rollBack, itemKey: 'lowCountStr', index: 3, label:'小流量回滚', moduleName: '回滚次数'})">
  120. <span class="circle" style="background: #3F9DFE" />
  121. 小流量回滚
  122. <span class="numText">{{ mainData.onlineProcessList.rollBack.lowCountStr.countStr }}</span>
  123. </div>
  124. </el-col>
  125. </el-row>
  126. </div>
  127. <div class="itemBox progress" style="margin-top: 20px; height: 122px;">
  128. <div class="titleLevel3 mb10">移动端发布质量</div>
  129. <el-row :gutter="10">
  130. <el-col v-for="(item, index) in mainData.mobilePublishQualityList" :key="index" :span="12" class="mb10">
  131. <dataItem :item="item" />
  132. </el-col>
  133. </el-row>
  134. </div>
  135. </el-col>
  136. </el-row>
  137. </template>
  138. <script>
  139. import dataItem from '../dataItem'
  140. import normalEchart from '@/components/chart/normalEchart'
  141. import { getOption } from '@/utils/options'
  142. export default {
  143. components: {
  144. dataItem,
  145. normalEchart
  146. },
  147. props: {
  148. datas: {
  149. type: Object,
  150. required: true,
  151. default: () => {}
  152. },
  153. loading: {
  154. type: Boolean,
  155. required: false,
  156. default: false
  157. },
  158. onlineLoading: {
  159. type: Boolean,
  160. required: false,
  161. default: false
  162. },
  163. offlineLoading: {
  164. type: Boolean,
  165. required: false,
  166. default: false
  167. }
  168. },
  169. data() {
  170. return {
  171. itemList: [
  172. {
  173. 'innerColor': '#F5222D',
  174. 'outColor': '#F5DDE2',
  175. 'title': '123',
  176. 'subTitle': '123',
  177. 'bgColor': '#F5F7FC'
  178. },
  179. {
  180. 'innerColor': '#FAAD14',
  181. 'outColor': '#F7ECDA',
  182. 'title': '',
  183. 'subTitle': '',
  184. 'bgColor': '#F5F7FC'
  185. },
  186. {
  187. 'innerColor': '#C97DE9',
  188. 'outColor': '#EFE5FA',
  189. 'title': '',
  190. 'subTitle': '',
  191. 'bgColor': '#F5F7FC'
  192. },
  193. {
  194. 'innerColor': '#7ED321',
  195. 'outColor': '#E4F2DC',
  196. 'title': '',
  197. 'subTitle': '',
  198. 'bgColor': '#F5F7FC'
  199. }
  200. ],
  201. redObj: {
  202. 'innerColor': '#F5222D',
  203. 'outColor': '#F5DDE2',
  204. 'bgColor': '#F5F7FC'
  205. },
  206. yellowObj: {
  207. 'innerColor': '#FAAD14',
  208. 'outColor': '#F7ECDA',
  209. 'bgColor': '#F5F7FC'
  210. },
  211. purpleObj: {
  212. 'innerColor': '#C97DE9',
  213. 'outColor': '#EFE5FA',
  214. 'bgColor': '#F5F7FC'
  215. },
  216. greenObj: {
  217. 'innerColor': '#7ED321',
  218. 'outColor': '#E4F2DC',
  219. 'bgColor': '#F5F7FC'
  220. },
  221. onlineProblemSelect: ['日期', '等级', '影响业务方'],
  222. offlineProblemSelect: ['日期', '等级', '责任人', '发现阶段'],
  223. offlineProblemViweType: '日期',
  224. onlineProblemViweType: '日期',
  225. onlineProblemChartOption: null,
  226. offlineProblemChartOption: null,
  227. echartsOption1: null,
  228. viewType: null,
  229. mainData: {}
  230. }
  231. },
  232. watch: {
  233. datas(newVal, oldVal) {
  234. if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
  235. this.resetBaseData()
  236. }
  237. }
  238. },
  239. mounted() {
  240. this.resetBaseData()
  241. },
  242. methods: {
  243. onDetial(name, item) {
  244. if (
  245. (name === '线上问题' && item.label === '新增问题') ||
  246. name === '线下质量' ||
  247. (name === '上线过程' && item.label !== '免测上线率')
  248. ) {
  249. this.$emit('checkDetialModal', item)
  250. }
  251. },
  252. resetBaseData() {
  253. const {
  254. onlineProblemList,
  255. onlineProblemChart,
  256. offlineProblemList,
  257. offlineProblemChart,
  258. onlineProcessList,
  259. mobilePublishQualityList
  260. } = this.datas
  261. const {
  262. onlineProblemChart: mainDataOnlineProblemChart,
  263. offlineProblemChart: mainDataOfflineProblemChart
  264. } = this.mainData
  265. // 线上问题表
  266. if (onlineProblemList) {
  267. this.mainData = { ...this.mainData, onlineProblemList: this.getOnlineProblemList() }
  268. }
  269. // 线上问题图
  270. if (JSON.stringify(onlineProblemChart) !== JSON.stringify(mainDataOnlineProblemChart)) {
  271. const { xaxis, yaxis } = onlineProblemChart
  272. this.onlineProblemChartOption = getOption(xaxis, yaxis[0].data, 'bar', { endValue: 6 })
  273. this.mainData = { ...this.mainData, onlineProblemChart }
  274. }
  275. // 线下质量表
  276. if (offlineProblemList) {
  277. this.mainData = { ...this.mainData, offlineProblemList: this.getOfflineProblemList() }
  278. }
  279. // 线下质量图
  280. if (JSON.stringify(offlineProblemChart) !== JSON.stringify(mainDataOfflineProblemChart)) {
  281. const { xaxis, yaxis } = offlineProblemChart
  282. this.offlineProblemChartOption = getOption(xaxis, yaxis[0].data, 'bar', { endValue: 6 })
  283. this.mainData = { ...this.mainData, offlineProblemChart }
  284. }
  285. // 上线过程
  286. if (onlineProcessList) {
  287. this.mainData = { ...this.mainData, onlineProcessList: this.getOnlineProcessList() }
  288. }
  289. // 移动端发布质量
  290. if (mobilePublishQualityList) {
  291. this.mainData = { ...this.mainData, mobilePublishQualityList: this.getMobilePublishQualityList() }
  292. }
  293. },
  294. // 组织线上问题表数据
  295. getOnlineProblemList() {
  296. const { ImprovementsOverRate, depleteUnavailableTime, newOnlineProblems, onlineBreakRate } = this.datas.onlineProblemList
  297. return [
  298. {
  299. ...this.redObj,
  300. 'label': newOnlineProblems.label,
  301. 'title': newOnlineProblems.countStr,
  302. 'titleUnit': '个',
  303. 'subTitleUnit': 'rate',
  304. 'subTitle': newOnlineProblems.chainRatio,
  305. 'IdList': newOnlineProblems.idList
  306. },
  307. {
  308. ...this.yellowObj,
  309. 'label': depleteUnavailableTime.label,
  310. 'title': depleteUnavailableTime.countStr,
  311. 'titleUnit': '分',
  312. 'subTitleUnit': 'rate',
  313. 'subTitle': depleteUnavailableTime.chainRatio,
  314. 'IdList': depleteUnavailableTime.IdList
  315. },
  316. {
  317. ...this.purpleObj,
  318. 'label': onlineBreakRate.label,
  319. 'title': onlineBreakRate.countStr,
  320. 'titleUnit': '%',
  321. 'subTitleUnit': 'rate',
  322. 'subTitle': onlineBreakRate.chainRatio,
  323. 'IdList': onlineBreakRate.IdList
  324. },
  325. {
  326. ...this.greenObj,
  327. 'label': ImprovementsOverRate.label,
  328. 'title': ImprovementsOverRate.countStr,
  329. 'titleUnit': '%',
  330. 'subTitleUnit': 'rate',
  331. 'subTitle': ImprovementsOverRate.chainRatio,
  332. 'IdList': ImprovementsOverRate.IdList
  333. }
  334. ]
  335. },
  336. // 线下质量数据
  337. getOfflineProblemList() {
  338. const { newBug, reopen, testBackRate, releaseNopass } = this.datas.offlineProblemList
  339. return [
  340. {
  341. ...this.redObj,
  342. 'label': newBug.label,
  343. 'title': newBug.countStr,
  344. 'titleUnit': '个',
  345. 'subTitleUnit': 'rate',
  346. 'subTitle': newBug.chainRatio,
  347. 'IdList': newBug.idList
  348. },
  349. {
  350. ...this.yellowObj,
  351. 'label': reopen.label,
  352. 'title': reopen.countStr,
  353. 'titleUnit': '次',
  354. 'subTitleUnit': 'rate',
  355. 'subTitle': reopen.chainRatio,
  356. 'IdList': reopen.idList
  357. },
  358. {
  359. 'color': '#3F9DFE',
  360. 'bgColor': '#F5F7FC',
  361. 'label': testBackRate.label,
  362. 'title': testBackRate.countStr,
  363. 'titleUnit': '%',
  364. 'subTitleUnit': 'rate',
  365. 'subTitle': testBackRate.chainRatio,
  366. 'IdList': testBackRate.idList,
  367. 'remark': testBackRate.remark,
  368. 'type': 'circle'
  369. },
  370. {
  371. 'color': '#7ED321',
  372. 'bgColor': '#F5F7FC',
  373. 'label': releaseNopass.label,
  374. 'title': releaseNopass.countStr,
  375. 'titleUnit': '%',
  376. 'subTitleUnit': 'rate',
  377. 'subTitle': releaseNopass.chainRatio,
  378. 'IdList': releaseNopass.idList,
  379. 'remark': releaseNopass.remark,
  380. 'type': 'circle'
  381. }
  382. ]
  383. },
  384. // 上线过程数据
  385. getOnlineProcessList() {
  386. const { online, testFreeOnlineRate, onlinebyStreakingRate, rollBack } = this.datas.onlineProcessList
  387. return {
  388. 'list': [
  389. {
  390. 'innerColor': '#3F9DFE',
  391. 'outColor': '#E2F0FF',
  392. 'padding': '8px 0px',
  393. 'label': online.label,
  394. 'title': online.countStr,
  395. 'titleUnit': '次',
  396. 'IdList': online.idList
  397. },
  398. {
  399. 'innerColor': '#7ED321',
  400. 'outColor': '#E4F2DC',
  401. 'padding': '8px 0px',
  402. 'label': testFreeOnlineRate.label,
  403. 'title': testFreeOnlineRate.countStr,
  404. 'titleUnit': '%',
  405. 'IdList': testFreeOnlineRate.idList
  406. },
  407. {
  408. 'innerColor': '#F5222D',
  409. 'outColor': '#F5DDE2',
  410. 'padding': '8px 0px',
  411. 'label': onlinebyStreakingRate.label,
  412. 'title': onlinebyStreakingRate.countStr,
  413. 'titleUnit': '次',
  414. 'IdList': onlinebyStreakingRate.idList
  415. }
  416. ],
  417. rollBack
  418. }
  419. },
  420. // 移动端发布质量数据
  421. getMobilePublishQualityList() {
  422. const {
  423. hotpacth
  424. // addIssue // 增发
  425. } = this.datas.mobilePublishQualityList
  426. return [
  427. {
  428. 'innerColor': '#3F9DFE',
  429. 'outColor': '#E2F0FF',
  430. 'padding': '8px 0px',
  431. 'label': hotpacth.label,
  432. 'title': hotpacth.countStr,
  433. 'titleUnit': '次',
  434. 'IdList': hotpacth.IdList
  435. }
  436. // 增发次数暂时隐藏
  437. // {
  438. // 'innerColor': '#F5222D',
  439. // 'outColor': '#F5DDE2',
  440. // 'padding': '8px 0px',
  441. // 'label': addIssue.label,
  442. // 'title': addIssue.countStr,
  443. // 'titleUnit': '次',
  444. // 'IdList': addIssue.IdList
  445. // }
  446. ]
  447. },
  448. changeList() {}
  449. }
  450. }
  451. </script>
  452. <style scoped lang='less'>
  453. .qualityModule {
  454. .divider {
  455. margin: 0 0 15px 0;
  456. }
  457. .rollBack {
  458. height: 94px;
  459. display: flex;
  460. align-items: center;
  461. .rollBackItem {
  462. color: #666;
  463. margin-bottom: 10px;
  464. .numText {
  465. color: #409EFF;
  466. font-weight: 600;
  467. }
  468. }
  469. .circle {
  470. display: inline-block;
  471. width: 6px;
  472. height: 6px;
  473. border-radius: 100%;
  474. margin-right: 6px;
  475. }
  476. .bigCircle {
  477. display: inline-block;
  478. width: 10px;
  479. height: 10px;
  480. border-radius: 100%;
  481. margin-right: 6px;
  482. }
  483. }
  484. .itemBox {
  485. box-shadow: 0px 6px 50px rgba(0, 0, 0, 0.05);
  486. padding: 10px 10px;
  487. border-radius: 6px;
  488. }
  489. .chartSearchbar {
  490. margin-top: 10px;
  491. //margin-bottom: 10px;
  492. }
  493. .chartViewHeight {
  494. height: 250px;
  495. // padding-left: 15px;
  496. }
  497. }
  498. </style>