You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

458 lines
20 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. <template>
  2. <section>
  3. <el-row class="padding-left padding-right">
  4. <el-col :span="6">
  5. <comps-set :comp-ids="compIds" :category="category" @row-click="onCompSelect" ref="compsSet" :show-checked-only="isRptShow"></comps-set>
  6. </el-col>
  7. <el-col :span="18">
  8. <el-row class="padding">
  9. <span class="rpt-name">{{ xmRptData && xmRptData.id?xmRptData.rptName:(xmRptConfig&&xmRptConfig.id?xmRptConfig.name: rptConfigParamsCpd.name+'-报告')}}</span>
  10. <span style="float:right;">
  11. <el-button type="text" v-if="isRptShow==true && isRptCfg==false" @click="toQueryRptData" icon="el-icon-time">查看历史报告</el-button>
  12. <el-button type="primary" v-if="isRptShow==true && isRptCfg==false && (!xmRptData||!xmRptData.id)" @click="showCreateRptData()" icon="el-icon-time">保存报告(可供历史查询)</el-button>
  13. <el-button type="text" v-if="isRptShow==false && isRptCfg==false" @click="isRptShow=true" icon="el-icon-time">查看报告</el-button>
  14. <el-button type="warning" v-if="isRptShow==true" @click="undoRptShow" icon="el-icon-error">退出报告</el-button>
  15. <el-button type="text" v-if="isRptCfg==false&&isRptShow==false" @click="toRptCfg" icon="el-icon-setting">制作报告</el-button>
  16. <el-button type="primary" v-if="isRptCfg==true" @click="undoRptCfg" icon="el-icon-error">取消制作</el-button>
  17. <el-button type="warning" v-if="isRptCfg==true" @click="finishRptCfg">保存报告</el-button>
  18. <el-button type="text" v-if="paramsVisible==true" @click="paramsVisible=false">隐藏过滤条件</el-button>
  19. <el-button type="text" v-if="paramsVisible==false" @click="paramsVisible=true">显示过滤条件</el-button>
  20. <el-button type="text" v-print="{id:'printBody',popTitle:rptConfigParamsCpd.name+'-报告'}" icon="el-icon-printer"></el-button>
  21. <el-button type="text" @click="exportToPdf">pdf</el-button>
  22. </span>
  23. </el-row>
  24. <el-row :style="{height:maxTableHeight+'px',overflowY:'auto',overflowX:'hidden',}" ref="table">
  25. <div class="empty" v-if="compCfgList.length == 0" >
  26. <el-empty description="暂未选择报表,请至少选择一个报表"></el-empty>
  27. </div>
  28. <div v-else id="printBody" ref="rptBox">
  29. <component style="margin-bottom:80px;" v-for="(item,index) in compCfgList" :key="index" :is="item.compId" :xm-test-plan="xmTestPlan" :xm-product="xmProduct" :xm-project="xmProject" :xm-iteration="xmIteration" :xm-test-casedb="xmTestCasedb" :category="category" :cfg="item.cfg" :ref="item.id" @delete="doDelete(item)" :init-group-by="item.initGroupBy" :show-tool-bar="false" :id="item.id" :rpt-data="item.rawDatas" :show-params="paramsVisible"></component>
  30. </div>
  31. </el-row>
  32. </el-col>
  33. </el-row>
  34. <el-dialog append-to-body modal-append-to-body :visible.sync="rptDataListVisible" top="20px" width="60%">
  35. <rpt-data-list :xm-rpt-config="xmRptConfig" v-if="rptDataListVisible" @select="onRptDataSelect"/>
  36. </el-dialog>
  37. <el-dialog title="请确认" append-to-body modal-append-to-body :visible.sync="createRptDataVisible">
  38. <el-form :model="xmRptData">
  39. <el-form-item label="报告名称">
  40. <el-input v-model="xmRptData.rptName"></el-input>
  41. </el-form-item>
  42. </el-form>
  43. <div slot="footer" class="dialog-footer">
  44. <el-button @click="createRptDataVisible = false"> </el-button>
  45. <el-button type="primary" @click="createRptData"> </el-button>
  46. </div>
  47. </el-dialog>
  48. </section>
  49. </template>
  50. <script>
  51. import util from '@/common/js/util';//全局公共库
  52. import seq from '@/common/js/sequence';//全局公共库
  53. import VueGridLayout from 'vue-grid-layout';
  54. import { mapGetters } from 'vuex'
  55. import CompsSet from '@/views/xm/rpt/CompsSet'
  56. import rptDataList from '@/views/xm/rpt/rptDataList'
  57. import { addXmRptData } from '@/api/xm/core/xmRptData';
  58. import { listXmRptConfig,editXmRptConfig,addXmRptConfig } from '@/api/xm/core/xmRptConfig';
  59. export default {
  60. components: {
  61. GridLayout: VueGridLayout.GridLayout,
  62. GridItem: VueGridLayout.GridItem,
  63. CompsSet, rptDataList,
  64. xmTestRptOverview:()=>import("../core/xmTestPlan/xmTestRptOverview.vue"),
  65. xmMenuDayTrend:()=>import("./product/menuDayTrend.vue"),
  66. xmMenuDayAccumulate:()=>import("./product/menuDayTrend.vue"),
  67. xmMenuAttDist:()=>import('./product/menuAttDist'),
  68. xmMenuAgeDist:()=>import('./product/menuAgeDist.vue'),
  69. xmMenuSort:()=>import('./product/menuSort.vue'),
  70. xmMenuFuncSort:()=>import('./product/menuSort.vue'),
  71. xmMenuProductSort:()=>import('./product/menuSort.vue'),
  72. xmMenuIterationSort:()=>import('./product/menuSort.vue'),
  73. xmProductWorkItemDayList:()=>import('./product/productWorkItemDayList.vue'),
  74. xmTaskDayTrend:()=>import('./project/taskDayTrend.vue'),
  75. xmTaskDayAccumulate:()=>import('./project/taskDayAccumulate.vue'),
  76. xmTaskAttDist:()=>import('./project/taskAttDist.vue'),
  77. xmTaskAgeDist:()=>import('./project/taskAgeDist.vue'),
  78. xmTaskSort:()=>import('./project/taskSort.vue'),
  79. xmProjectWorkItemDayList:()=>import('./project/projectWorkItemDayList.vue'),
  80. xmProjectWorkloadSetDayList:()=>import('./project/projectWorkloadSetDayList.vue'),
  81. xmProjectWorkloadSetMonthList:()=>import('./project/projectWorkloadSetMonthList.vue'),
  82. xmQuestionDayTrend:()=>import('./product/questionDayTrend.vue'),
  83. xmQuestionDayAccumulate:()=>import('./product/questionDayAccumulate.vue'),
  84. xmQuestionAttDist:()=>import('./product/questionAttDist.vue'),
  85. xmQuestionStateDist:()=>import('./product/questionAttDist.vue'),
  86. xmQuestionAgeDist:()=>import('./product/questionAgeDist.vue'),
  87. xmQuestionBugReasonDist:()=>import('./product/questionAttDist.vue'),
  88. xmQuestionBugTypeDist:()=>import('./product/questionAttDist.vue'),
  89. xmQuestionPriorityDist:()=>import('./product/questionAttDist.vue'),
  90. xmQuestionSort:()=>import('./product/questionSort.vue'),
  91. xmQuestionAskUserSort:()=>import('./product/questionSort.vue'),
  92. xmQuestionHandlerUserSort:()=>import('./product/questionSort.vue'),
  93. xmQuestionFuncSort:()=>import('./product/questionSort.vue'),
  94. xmQuestionMenuSort:()=>import('./product/questionSort.vue'),
  95. xmQuestionRetestDist:()=>import('./product/questionRetestDist.vue'),
  96. xmTestPlanCaseExecStatusDist:()=>import('./testPlan/testPlanCaseExecStatusDist.vue'),
  97. xmTestPlanCaseUserDist:()=>import('./testPlan/testPlanCaseUserDist.vue'),
  98. xmTestDayTimesCalc:()=>import('./testPlan/testDayTimesCalc.vue'),
  99. xmTestCaseToPlanCalc:()=>import('./testPlan/testCaseToPlanCalc.vue'),
  100. xmTestCaseSort:()=>import('./testCase/testCaseSort.vue'),
  101. xmTestCaseCuserSort:()=>import('./testCase/testCaseSort.vue'),
  102. xmTestCaseFuncSort:()=>import('./testCase/testCaseSort.vue'),
  103. xmTestCaseMenuSort:()=>import('./testCase/testCaseSort.vue'),
  104. xmIterationMenuDayTrend:()=>import('./iteration/menuDayTrend.vue'),
  105. xmIterationMenuDayAccumulate:()=>import('./iteration/menuDayAccumulate.vue'),
  106. xmIterationBurnout:()=>import('./iteration/burnout.vue'),
  107. xmIterationWorkItemDayList:()=>import('./iteration/iterationWorkItemDayList.vue'),
  108. xmIterationQuestionDayTrend:()=>import('./iteration/questionDayTrend.vue'),
  109. xmIterationQuestionDayAccumulate:()=>import('./iteration/questionDayAccumulate.vue'),
  110. xmBranchWorkItemDayList:()=>import('./branch/branchWorkItemDayList.vue'),
  111. xmBranchQuestionDayTrend:()=>import('./branch/questionDayTrend.vue'),
  112. xmBranchQuestionDayAccumulate:()=>import('./branch/questionDayAccumulate.vue'),
  113. xmBranchMenuDayTrend:()=>import('./branch/menuDayTrend.vue'),
  114. xmBranchMenuDayAccumulate:()=>import('./branch/menuDayAccumulate.vue'),
  115. },
  116. props:['xmTestCasedb','xmTestPlan','xmProduct','xmProject','xmIteration','category','showParams'],
  117. computed: {
  118. ...mapGetters(['userInfo']),
  119. compIds(){
  120. return this.compCfgList.map(k=>k.compId)
  121. },
  122. rptConfigParamsCpd(){
  123. //业务类型1-产品报告,2-迭代报告,3-测试计划报告,4-项目报告,5-企业报告
  124. var params={bizType:'5',bizId:this.userInfo.branchId,name:this.userInfo.branchName}
  125. if(this.category=='企业级'){
  126. params.bizType='5';
  127. params.bizId=this.userInfo.branchId
  128. params.name=this.userInfo.branchName
  129. }else if(this.category=='产品级'){
  130. params.bizType='1';
  131. params.bizId=this.xmProduct.id
  132. params.name=this.xmProduct.productName
  133. }else if(this.category=='迭代级'){
  134. params.bizType='2';
  135. params.bizId=this.xmIteration.id
  136. params.name=this.xmIteration.iterationName
  137. }else if(this.category=='项目级'){
  138. params.bizType='4';
  139. params.bizId=this.xmProject.id
  140. params.name=this.xmProject.name
  141. }else if(this.category=='测试级'){
  142. if(this.xmTestPlan && this.xmTestPlan.id){
  143. params.bizType='3';
  144. params.bizId=this.xmTestPlan.id
  145. params.name=this.xmTestPlan.name
  146. }else{
  147. params.bizType='6';
  148. params.bizId=this.xmTestCasedb.id
  149. params.name=this.xmTestCasedb.name
  150. }
  151. }else {
  152. return params;
  153. }
  154. return params;
  155. },
  156. toLoadXmRptConfigCpd(){
  157. return this.isRptCfg || this.isRptShow
  158. }
  159. },
  160. watch: {
  161. xmRptConfig:{
  162. handler(){
  163. this.initCompCfgList();
  164. },
  165. deep:true,
  166. },
  167. toLoadXmRptConfigCpd(){
  168. this.getXmRptConfig();
  169. this.$nextTick(()=>{
  170. this.compCfgList.forEach(k=>{
  171. this.sizeAutoChange(k);
  172. })
  173. })
  174. },
  175. rptConfigParamsCpd(){
  176. if(this.isRptCfg ||this.isRptShow){
  177. this.getXmRptConfig()
  178. }
  179. }
  180. },
  181. data() {
  182. return {
  183. isRptCfg:false,
  184. isRptShow:false,
  185. xmRptConfig:null,
  186. xmRptData:{id:'',rptName:'',bizId:'',bizType:'',bizDate:''},
  187. xmRptDataInit:{id:'',rptName:'',bizId:'',bizType:'',bizDate:''},
  188. compCfgList:[],
  189. maxTableHeight:300,
  190. // 布局列数
  191. layoutColNum: 12,
  192. paramsVisible:true,
  193. rptDataListVisible:false,
  194. createRptDataVisible:false,
  195. }
  196. },
  197. methods: {
  198. initData(){
  199. if(this.showParams!=undefined){
  200. this.paramsVisible=this.showParams
  201. }
  202. if(!this.toLoadXmRptConfigCpd){
  203. this.initCompCfgList();
  204. }else{
  205. this.getXmRptConfig();
  206. }
  207. },
  208. showCreateRptData(){
  209. if(this.xmRptConfig==null){
  210. this.$message.error("还没制作报告,请先制作报告")
  211. return;
  212. }
  213. this.xmRptData.rptName=this.xmRptConfig.name
  214. this.createRptDataVisible=true
  215. },
  216. toQueryRptData(){
  217. this.rptDataListVisible=true;
  218. },
  219. createRptData(){
  220. if(this.xmRptConfig==null){
  221. this.$message.error("还没制作报告,请先制作报告")
  222. return;
  223. }
  224. if(!this.xmRptData.rptName){
  225. this.$message.error("请输入报告名称")
  226. return;
  227. }
  228. var xmRptData={...this.xmRptData,cfgId:this.xmRptConfig.id,rptData:[]}
  229. this.compCfgList.forEach(k=>{
  230. if(this.$refs[k.id] && this.$refs[k.id][0].$refs && this.$refs[k.id][0].$refs[k.id]){
  231. var com=this.$refs[k.id][0].$refs[k.id]
  232. var comData={compId:k.compId,params:com.params,title:com.title,remark:com.remark}
  233. xmRptData.rptData.push(comData)
  234. }else{
  235. var com=this.$refs[k.id][0]
  236. var comData={compId:k.compId,params:com.params,title:com.title,remark:com.remark}
  237. xmRptData.rptData.push(comData)
  238. }
  239. })
  240. xmRptData.rptData=JSON.stringify(xmRptData.rptData)
  241. addXmRptData(xmRptData).then(res=>{
  242. var tips = res.data.tips
  243. if(tips.isOk){
  244. this.$message.success("报告保存成功")
  245. this.createRptDataVisible=false
  246. }else{
  247. this.$message.error(tips.msg)
  248. }
  249. })
  250. },
  251. undoRptCfg(){
  252. this.xmRptConfig=null;
  253. this.isRptCfg=false;
  254. },
  255. undoRptShow(){
  256. this.isRptShow=false;
  257. this.xmRptConfig=null;
  258. this.xmRptData={...this.xmRptDataInit};
  259. },
  260. toRptCfg(){
  261. this.isRptCfg=true;
  262. this.$message.success("切换到报告制作模式成功。请选择报表加入报告中。")
  263. },
  264. finishRptCfg(){
  265. this.submitXmPrtConfig((res)=>{
  266. var tips = res.data.tips;
  267. if(tips.isOk){
  268. this.isRptCfg=false
  269. this.xmRptConfig=null;
  270. this.$message.success("报告保存成功。将退出报告制作模式")
  271. }else{
  272. this.$message.error(tips.msg)
  273. }
  274. });
  275. },
  276. getXmRptConfig(){
  277. if(!this.toLoadXmRptConfigCpd){
  278. return;
  279. }
  280. if(this.rptDatas){
  281. this.rawDatas=this.rptDatas
  282. return;
  283. }
  284. var params={bizType:this.rptConfigParamsCpd.bizType,bizId:this.rptConfigParamsCpd.bizId}
  285. listXmRptConfig(params).then(res=>{
  286. this.xmRptConfig=res.data.data[0]
  287. })
  288. },
  289. initCompCfgList(){
  290. if(this.xmRptConfig && this.xmRptConfig.cfg){
  291. var cfgJson=JSON.parse(this.xmRptConfig.cfg)
  292. cfgJson.forEach(k=>k.id=k.compId+seq.sn())
  293. this.compCfgList=cfgJson;
  294. }else{
  295. var defList=this.$refs['compsSet'].rptListCpd
  296. if(defList && defList.length>3){
  297. defList=defList.slice(0,3);
  298. }
  299. defList.forEach(k=>k.id=k.compId+seq.sn())
  300. this.compCfgList=JSON.parse(JSON.stringify(defList))
  301. }
  302. },
  303. onCompSelect(comp){
  304. if(this.compCfgList.some(k=>k.compId==comp.compId)){
  305. var compCfg=this.compCfgList.find(k=>k.compId==comp.compId)
  306. this.$nextTick(()=>{
  307. this.scrollToComp(compCfg)
  308. })
  309. return;
  310. }
  311. var compCfgListTemp=JSON.parse(JSON.stringify(this.compCfgList))
  312. compCfgListTemp.sort((i1,i2)=>{
  313. return i2.i-i1.i
  314. })
  315. var maxI=(compCfgListTemp.length>0?(compCfgListTemp[0].i+1):1);
  316. compCfgListTemp.sort((i1,i2)=>{
  317. return i2.y-i1.y
  318. })
  319. var maxY=(compCfgListTemp.length>0?(compCfgListTemp[0].y+6):0);
  320. var compCfg={...comp,i:maxI, x: 0, y: maxY, w: 12, h: 6,id:comp.compId+seq.sn()}
  321. this.compCfgList.push(compCfg)
  322. this.$nextTick(()=>{
  323. setTimeout(()=>{
  324. this.scrollToComp(compCfg)
  325. },200)
  326. })
  327. },
  328. scrollToComp(compCfg){
  329. var doc=document.getElementById(compCfg.id)
  330. if(doc){
  331. doc.scrollIntoView(true)
  332. }
  333. },
  334. submitXmPrtConfig(callback){
  335. if(this.xmRptConfig==null){
  336. var xmRptConfig={...this.rptConfigParamsCpd,cfg:[]}
  337. this.compCfgList.forEach(k=>{
  338. if(this.$refs[k.id] && this.$refs[k.id][0].$refs && this.$refs[k.id][0].$refs[k.id]){
  339. var com=this.$refs[k.id][0].$refs[k.id]
  340. var comData={compId:k.compId,params:com.params,title:com.title,remark:com.remark}
  341. xmRptConfig.cfg.push(comData)
  342. }else{
  343. var com=this.$refs[k.id][0]
  344. var comData={compId:k.compId,params:com.params,title:com.title,remark:com.remark}
  345. xmRptConfig.cfg.push(comData)
  346. }
  347. })
  348. xmRptConfig.cfg=JSON.stringify(xmRptConfig.cfg)
  349. addXmRptConfig(xmRptConfig).then(res=>{
  350. this.xmRptConfig=xmRptConfig;
  351. callback(res)
  352. })
  353. }else{
  354. var xmRptConfig={...this.xmRptConfig,cfg:[]}
  355. this.compCfgList.forEach(k=>{
  356. if(this.$refs[k.id] && this.$refs[k.id][0].$refs && this.$refs[k.id][0].$refs[k.id]){
  357. var com=this.$refs[k.id][0].$refs[k.id]
  358. var comData={compId:k.compId,params:com.params,title:com.title,remark:com.remark}
  359. xmRptConfig.cfg.push(comData)
  360. }else{
  361. var com=this.$refs[k.id][0]
  362. var comData={compId:k.compId,params:com.params,title:com.title,remark:com.remark}
  363. xmRptConfig.cfg.push(comData)
  364. }
  365. })
  366. xmRptConfig.cfg=JSON.stringify(xmRptConfig.cfg)
  367. editXmRptConfig(xmRptConfig).then(res=>{
  368. this.xmRptConfig=xmRptConfig;
  369. callback(res)
  370. })
  371. }
  372. },
  373. doDelete(compCfg){
  374. var index=this.compCfgList.findIndex(k=>k.id==compCfg.id)
  375. if(index>=0){
  376. this.compCfgList.splice(index,1)
  377. }
  378. },
  379. sizeAutoChange(k){
  380. },
  381. onRptDataSelect(rptData){
  382. this.xmRptData=rptData
  383. this.rptDataListVisible=false;
  384. if(this.xmRptData && this.xmRptData.id ){
  385. if( this.xmRptData.cfgId==this.xmRptConfig.id){
  386. this.xmRptConfig.name=this.xmRptData.rptName
  387. var cfgList=JSON.parse(this.xmRptData.rptData)
  388. cfgList.forEach(k=>k.id=k.compId+seq.sn())
  389. this.compCfgList=cfgList
  390. }
  391. }
  392. },
  393. exportToPdf(){
  394. this.paramsVisible=false
  395. this.$nextTick(()=>{
  396. this.$PDFSave(this.$refs.rptBox, this.rptConfigParamsCpd.name+"-报告");
  397. })
  398. },
  399. },
  400. mounted() {
  401. this.$nextTick(() => {
  402. this.initData();
  403. this.maxTableHeight = util.calcTableMaxHeight(this.$refs.table.$el)
  404. })
  405. },
  406. }
  407. </script>
  408. <style lang="less" scoped>
  409. .toolbar{
  410. z-index: 999;
  411. position:absolute;
  412. top:0px;
  413. right:20px;
  414. }
  415. .rpt-name{
  416. text-align: center;
  417. font-size: 18px;
  418. font-weight: 600;
  419. }
  420. </style>