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.

460 lines
19 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
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
2 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 :category="category" @row-click="onCompSelect" ref="compsSet" :show-checked-only="isRptShow||showCheckedOnly" :is-rpt-cfg="isRptCfg" @sort="onSort" @change="onCompChange"></comps-set>
  6. </el-col>
  7. <el-col :span="18">
  8. <el-row class="padding">
  9. <span class="rpt-name" v-if="xmRptData && xmRptData.id">{{ xmRptData.rptName}}</span>
  10. <span class="rpt-name" v-else-if="xmRptConfig && xmRptConfig.id">{{ xmRptConfig.name}}</span>
  11. <span class="rpt-name" v-else>{{ rptConfigParamsCpd.name+'-报告'}}</span>
  12. <span style="float:right;">
  13. <el-button type="warning" v-if="isRptShow==true" @click="undoRptShow" icon="el-icon-error">退出报告</el-button>
  14. <el-button type="text" v-if="isRptCfg==false&&isRptShow==false" @click="toRptCfg" icon="el-icon-setting">制作报告</el-button>
  15. <el-button type="primary" v-if="isRptCfg==true" @click="undoRptCfg" icon="el-icon-error">取消制作</el-button>
  16. <el-button type="warning" v-if="isRptCfg==true" @click="toSaveRptCfg">保存&退出</el-button>
  17. <el-button type="primary" v-if="isRptShow==true && isRptCfg==false && (!xmRptData||!xmRptData.id)" @click="showCreateRptData()" icon="el-icon-time">存档</el-button>
  18. <el-button type="text" v-if="isRptShow==false && isRptCfg==false" @click="isRptShow=true" icon="el-icon-time">查看报告</el-button>
  19. <el-button type="text" v-if="isRptShow==true && isRptCfg==false" @click="toQueryRptData" icon="el-icon-time">查看历史报告</el-button>
  20. <el-button type="text" v-if="paramsVisible==true" @click="paramsVisible=false">隐藏过滤条件</el-button>
  21. <el-button type="text" v-if="paramsVisible==false" @click="paramsVisible=true">显示过滤条件</el-button>
  22. <el-button type="text" v-if="isRptShow==true && isRptCfg==false && xmRptData && xmRptData.id" @click="toShareRpt" icon="el-icon-share">分享</el-button>
  23. <el-button type="text" v-print="{id:'printBody',popTitle:xmRptData && xmRptData.id?xmRptData.rptName:(xmRptConfig&&xmRptConfig.id?xmRptConfig.name: rptConfigParamsCpd.name+'-报告')}" icon="el-icon-printer"></el-button>
  24. <el-button type="text" @click="exportToPdf">pdf</el-button>
  25. </span>
  26. </el-row>
  27. <el-row :style="{height:(maxTableHeight-24)+'px',overflowY:'auto',overflowX:'hidden',}" ref="table">
  28. <div class="empty" v-if="compCfgList.length == 0" >
  29. <el-empty description="暂未选择报表,请至少选择一个报表"></el-empty>
  30. </div>
  31. <div v-else id="printBody" ref="rptBox">
  32. <component style="margin-bottom:80px;" v-for="(item,index) in compCfgList" :key="item.compId" :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-datas="item.rawDatas" :is-rpt-cfg="isRptCfg" :show-params="paramsVisible"></component>
  33. </div>
  34. </el-row>
  35. </el-col>
  36. </el-row>
  37. <el-dialog append-to-body modal-append-to-body :visible.sync="rptDataSelectVisible" top="20px" width="60%">
  38. <rpt-data-select :xm-rpt-config="xmRptConfig" v-if="rptDataSelectVisible" @select="onRptDataSelect"/>
  39. </el-dialog>
  40. <el-dialog title="请确认" append-to-body modal-append-to-body :visible.sync="createRptConfigVisible">
  41. <el-form :model="xmRptConfig">
  42. <el-form-item label="报告名称">
  43. <el-input v-model="xmRptConfig.name"></el-input>
  44. </el-form-item>
  45. </el-form>
  46. <div slot="footer" class="dialog-footer">
  47. <el-button @click="createRptConfigVisible = false"> </el-button>
  48. <el-button type="primary" @click="finishRptCfg"> </el-button>
  49. </div>
  50. </el-dialog>
  51. <el-dialog title="请确认" append-to-body modal-append-to-body :visible.sync="createRptDataVisible">
  52. <el-form :model="xmRptData">
  53. <el-form-item label="报告名称">
  54. <el-input v-model="xmRptData.rptName"></el-input>
  55. </el-form-item>
  56. </el-form>
  57. <div slot="footer" class="dialog-footer">
  58. <el-button @click="createRptDataVisible = false"> </el-button>
  59. <el-button type="primary" @click="createRptData"> </el-button>
  60. </div>
  61. </el-dialog>
  62. </section>
  63. </template>
  64. <script>
  65. import util from '@/common/js/util';//全局公共库
  66. import seq from '@/common/js/sequence';//全局公共库
  67. import VueGridLayout from 'vue-grid-layout';
  68. import { mapGetters } from 'vuex'
  69. import CompsSet from '@/views/xm/rpt/index/CompsSet'
  70. import rptDataSelect from '@/views/xm/rpt/his/rptDataSelect'
  71. import { addXmRptData } from '@/api/xm/core/xmRptData';
  72. import { listXmRptConfig,editXmRptConfig,addXmRptConfig } from '@/api/xm/core/xmRptConfig';
  73. import rptComps from './comps.js';//组件库
  74. export default {
  75. components: {
  76. GridLayout: VueGridLayout.GridLayout,
  77. GridItem: VueGridLayout.GridItem,
  78. CompsSet, rptDataSelect,
  79. ...rptComps
  80. },
  81. props:['xmTestCasedb','xmTestPlan','xmProduct','xmProject','xmIteration','category','showParams','showCheckedOnly'],
  82. computed: {
  83. ...mapGetters(['userInfo']),
  84. rptConfigParamsCpd(){
  85. //业务类型1-产品报告,2-迭代报告,3-测试计划报告,4-项目报告,5-企业报告
  86. var params={bizType:'5',bizId:this.userInfo.branchId,name:this.userInfo.branchName}
  87. if(this.category=='企业级'){
  88. params.bizType='5';
  89. params.bizId=this.userInfo.branchId
  90. params.name=this.userInfo.branchName
  91. }else if(this.category=='产品级'){
  92. params.bizType='1';
  93. params.bizId=this.xmProduct.id
  94. params.name=this.xmProduct.productName
  95. }else if(this.category=='迭代级'){
  96. params.bizType='2';
  97. params.bizId=this.xmIteration.id
  98. params.name=this.xmIteration.iterationName
  99. }else if(this.category=='项目级'){
  100. params.bizType='4';
  101. params.bizId=this.xmProject.id
  102. params.name=this.xmProject.name
  103. }else if(this.category=='测试库级'){
  104. params.bizType='6';
  105. params.bizId=this.xmTestCasedb.id
  106. params.name=this.xmTestCasedb.name
  107. }else if(this.category=='测试计划级'){
  108. params.bizType='3';
  109. params.bizId=this.xmTestPlan.id
  110. params.name=this.xmTestPlan.name
  111. }
  112. return params;
  113. },
  114. toLoadXmRptConfigCpd(){
  115. return this.isRptCfg || this.isRptShow
  116. }
  117. },
  118. watch: {
  119. xmRptConfig:{
  120. handler(){
  121. this.initCompCfgList();
  122. },
  123. deep:true,
  124. },
  125. toLoadXmRptConfigCpd(){
  126. this.getXmRptConfig();
  127. this.$nextTick(()=>{
  128. this.compCfgList.forEach(k=>{
  129. this.sizeAutoChange(k);
  130. })
  131. })
  132. },
  133. rptConfigParamsCpd(){
  134. if(this.isRptCfg ||this.isRptShow){
  135. this.getXmRptConfig()
  136. }
  137. }
  138. },
  139. data() {
  140. return {
  141. isRptCfg:false,
  142. isRptShow:false,
  143. xmRptConfig:{id:'',name:'',bizType:'',bizId:'',cfg:[]},
  144. xmRptData:{id:'',rptName:'',bizId:'',bizType:'',bizDate:'',rptData:[]},
  145. xmRptDataInit:{id:'',rptName:'',bizId:'',bizType:'',bizDate:'',rptData:[]},
  146. compCfgList:[],
  147. maxTableHeight:300,
  148. // 布局列数
  149. layoutColNum: 12,
  150. paramsVisible:true,
  151. rptDataSelectVisible:false,
  152. createRptDataVisible:false,
  153. createRptConfigVisible:false,
  154. }
  155. },
  156. methods: {
  157. initData(){
  158. if(this.showParams!=undefined){
  159. this.paramsVisible=this.showParams
  160. }
  161. if(!this.toLoadXmRptConfigCpd){
  162. this.initCompCfgList();
  163. }else{
  164. this.getXmRptConfig();
  165. }
  166. },
  167. toShareRpt(){
  168. if(!this.xmRptData||!this.xmRptData.id){
  169. this.$message.error("只能分享历史报告")
  170. return;
  171. }
  172. var curlDomain=window.location.protocol+"//"+window.location.host; // 返回https://mp.csdn.net
  173. var link=curlDomain+"/"+process.env.CONTEXT+"/"+process.env.VERSION+"/#/xm/rpt/his/detail?id="+this.xmRptData.id
  174. this.$copyText(link).then(e => {
  175. this.$notify({position:'bottom-left',showClose:true,message:"拷贝链接成功,您可以黏贴到任何地方",type:'success'})
  176. });
  177. },
  178. showCreateRptData(){
  179. if(!this.xmRptConfig|| !this.xmRptConfig.id){
  180. this.$message.error("还没制作报告,请先制作报告")
  181. return;
  182. }
  183. this.xmRptData.rptName=this.xmRptConfig.name+"-"+util.getDate()
  184. this.createRptDataVisible=true
  185. },
  186. toSaveRptCfg(){
  187. this.createRptConfigVisible=true
  188. if(!this.xmRptConfig.name){
  189. this.xmRptConfig.name=this.rptConfigParamsCpd.name+"-报告"
  190. }
  191. },
  192. toQueryRptData(){
  193. this.rptDataSelectVisible=true;
  194. },
  195. createRptData(){
  196. if(!this.xmRptConfig|| !this.xmRptConfig.id){
  197. this.$message.error("还没制作报告,请先制作报告")
  198. return;
  199. }
  200. if(!this.xmRptData.rptName){
  201. this.$message.error("请输入报告名称")
  202. return;
  203. }
  204. var xmRptData={rptName:this.xmRptData.rptName,bizType:this.xmRptConfig.bizType,bizId:this.xmRptConfig.bizId,cfgId:this.xmRptConfig.id,rptData:[]}
  205. this.compCfgList.forEach(k=>{
  206. if(this.$refs[k.id] && this.$refs[k.id][0].$refs && this.$refs[k.id][0].$refs[k.id]){
  207. var com=this.$refs[k.id][0].$refs[k.id]
  208. var comData={compId:k.compId,params:com.params,title:com.title,remark:com.remark,rawDatas:com.rawDatas}
  209. xmRptData.rptData.push(comData)
  210. }else{
  211. var com=this.$refs[k.id][0]
  212. var comData={compId:k.compId,params:com.params,title:com.title,remark:com.remark,rawDatas:com.rawDatas}
  213. xmRptData.rptData.push(comData)
  214. }
  215. })
  216. xmRptData.rptData=JSON.stringify(xmRptData.rptData)
  217. addXmRptData(xmRptData).then(res=>{
  218. var tips = res.data.tips
  219. if(tips.isOk){
  220. this.$message.success("报告保存成功")
  221. this.createRptDataVisible=false
  222. }else{
  223. this.$message.error(tips.msg)
  224. }
  225. })
  226. },
  227. undoRptCfg(){
  228. this.xmRptConfig={};
  229. this.isRptCfg=false;
  230. },
  231. undoRptShow(){
  232. this.isRptShow=false;
  233. this.xmRptConfig={};
  234. this.xmRptData={...this.xmRptDataInit};
  235. },
  236. toRptCfg(){
  237. this.isRptCfg=true;
  238. this.$message.success("切换到报告制作模式成功。请选择报表加入报告中。")
  239. },
  240. finishRptCfg(){
  241. this.submitXmPrtConfig((res)=>{
  242. var tips = res.data.tips;
  243. if(tips.isOk){
  244. this.isRptCfg=false
  245. this.xmRptConfig={};
  246. this.createRptConfigVisible=false;
  247. this.$message.success("报告保存成功。将退出报告制作模式")
  248. }else{
  249. this.$message.error(tips.msg)
  250. }
  251. });
  252. },
  253. getXmRptConfig(){
  254. if(!this.toLoadXmRptConfigCpd){
  255. return;
  256. }
  257. if(this.rptDatas){
  258. this.rawDatas=this.rptDatas
  259. return;
  260. }
  261. var params={bizType:this.rptConfigParamsCpd.bizType,bizId:this.rptConfigParamsCpd.bizId}
  262. listXmRptConfig(params).then(res=>{
  263. if(!res.data.tips.isOk){
  264. this.$message.error(res.data.tips.msg)
  265. return ;
  266. }
  267. if(!res.data.data || res.data.data.length==0 ){
  268. this.xmRptConfig={}
  269. }else{
  270. this.xmRptConfig=res.data.data[0]
  271. }
  272. })
  273. },
  274. initCompCfgList(){
  275. if(this.xmRptConfig && this.xmRptConfig.id && this.xmRptConfig.cfg){
  276. var cfgJson=JSON.parse(this.xmRptConfig.cfg)
  277. cfgJson.forEach(k=>k.id=k.compId+seq.sn())
  278. this.compCfgList=cfgJson;
  279. this.$refs.compsSet.setCheckeds(this.compCfgList.map(k=>k.compId),true)
  280. }
  281. },
  282. onCompSelect(comp){
  283. if(this.compCfgList.some(k=>k.compId==comp.compId)){
  284. var compCfg=this.compCfgList.find(k=>k.compId==comp.compId)
  285. this.$nextTick(()=>{
  286. this.scrollToComp(compCfg)
  287. })
  288. return;
  289. }
  290. var allCheckedList=this.$refs.compsSet.datas.filter(k=>k.isChecked)
  291. var index=allCheckedList.findIndex(k=>k.compId==comp.compId)
  292. var compCfg={...comp,id:comp.compId+seq.sn()}
  293. this.compCfgList.splice(index,0,compCfg)
  294. this.$nextTick(()=>{
  295. setTimeout(()=>{
  296. this.scrollToComp(compCfg)
  297. },200)
  298. })
  299. },
  300. scrollToComp(compCfg){
  301. var doc=document.getElementById(compCfg.id)
  302. if(doc){
  303. doc.scrollIntoView(true)
  304. }
  305. },
  306. submitXmPrtConfig(callback){
  307. if(!this.xmRptConfig||!this.xmRptConfig.name){
  308. this.$message.error("请输入报告名称")
  309. return
  310. }
  311. if(!this.xmRptConfig.id){
  312. var xmRptConfig={...this.rptConfigParamsCpd,name:this.xmRptConfig.name,cfg:[]}
  313. this.compCfgList.forEach(k=>{
  314. if(this.$refs[k.id] && this.$refs[k.id][0].$refs && this.$refs[k.id][0].$refs[k.id]){
  315. var com=this.$refs[k.id][0].$refs[k.id]
  316. var comData={compId:k.compId,params:com.params,title:com.title,remark:com.remark}
  317. xmRptConfig.cfg.push(comData)
  318. }else{
  319. var com=this.$refs[k.id][0]
  320. var comData={compId:k.compId,params:com.params,title:com.title,remark:com.remark}
  321. xmRptConfig.cfg.push(comData)
  322. }
  323. })
  324. xmRptConfig.cfg=JSON.stringify(xmRptConfig.cfg)
  325. addXmRptConfig(xmRptConfig).then(res=>{
  326. this.xmRptConfig=xmRptConfig;
  327. callback(res)
  328. })
  329. }else{
  330. var xmRptConfig={...this.xmRptConfig,cfg:[]}
  331. this.compCfgList.forEach(k=>{
  332. if(this.$refs[k.id] && this.$refs[k.id][0].$refs && this.$refs[k.id][0].$refs[k.id]){
  333. var com=this.$refs[k.id][0].$refs[k.id]
  334. var comData={compId:k.compId,params:com.params,title:com.title,remark:com.remark}
  335. xmRptConfig.cfg.push(comData)
  336. }else{
  337. var com=this.$refs[k.id][0]
  338. var comData={compId:k.compId,params:com.params,title:com.title,remark:com.remark}
  339. xmRptConfig.cfg.push(comData)
  340. }
  341. })
  342. xmRptConfig.cfg=JSON.stringify(xmRptConfig.cfg)
  343. editXmRptConfig(xmRptConfig).then(res=>{
  344. this.xmRptConfig=xmRptConfig;
  345. callback(res)
  346. })
  347. }
  348. },
  349. doDelete(compCfg){
  350. var index=this.compCfgList.findIndex(k=>k.id==compCfg.id)
  351. if(index>=0){
  352. this.compCfgList.splice(index,1)
  353. }
  354. this.$refs.compsSet.setChecked(compCfg.compId,false)
  355. },
  356. sizeAutoChange(k){
  357. },
  358. onRptDataSelect(rptData){
  359. this.xmRptData=rptData
  360. this.rptDataSelectVisible=false;
  361. if(this.xmRptData && this.xmRptData.id ){
  362. if( this.xmRptData.cfgId==this.xmRptConfig.id){
  363. this.xmRptConfig.name=this.xmRptData.rptName
  364. var cfgList=JSON.parse(this.xmRptData.rptData)
  365. cfgList.forEach(k=>k.id=k.compId+seq.sn())
  366. this.compCfgList=cfgList
  367. this.$refs.compsSet.setCheckeds(this.compCfgList.map(k=>k.compId),true)
  368. }
  369. }
  370. },
  371. exportToPdf(){
  372. this.paramsVisible=false
  373. this.$nextTick(()=>{
  374. this.$PDFSave(this.$refs.rptBox, this.rptConfigParamsCpd.name+"-报告");
  375. })
  376. },
  377. onSort(evt,datas){
  378. datas.forEach((d,index)=>{
  379. var comp=this.compCfgList.find(k=>k.compId==d.compId)
  380. if(comp){
  381. comp.index=index
  382. }
  383. })
  384. this.compCfgList.sort((i1,i2)=>{
  385. return i1.index-i2.index
  386. })
  387. var compCfg=this.compCfgList.find(k=>k.compId==datas[evt.newIndex].compId)
  388. this.$nextTick(()=>{
  389. setTimeout(()=>{
  390. this.scrollToComp(compCfg)
  391. },200)
  392. })
  393. },
  394. onCompChange(compCfg,checked){
  395. if(!checked){
  396. var index=this.compCfgList.findIndex(k=>k.compId==compCfg.compId)
  397. if(index>=0){
  398. this.compCfgList.splice(index,1)
  399. }
  400. }else{
  401. this.onCompSelect(compCfg)
  402. }
  403. }
  404. },
  405. mounted() {
  406. this.$nextTick(() => {
  407. this.initData();
  408. this.maxTableHeight = util.calcMaxHeight(this.$refs.table.$el)
  409. //debugger
  410. })
  411. },
  412. }
  413. </script>
  414. <style lang="less" scoped>
  415. .toolbar{
  416. z-index: 999;
  417. position:absolute;
  418. top:0px;
  419. right:20px;
  420. }
  421. .rpt-name{
  422. text-align: center;
  423. font-size: 18px;
  424. font-weight: 600;
  425. }
  426. </style>