index.vue 17 KB

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