index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. <template>
  2. <div>
  3. <div class="sy-content">
  4. <split-pane
  5. :min-percent='leftpercent'
  6. :default-percent='leftpercent'
  7. split="vertical">
  8. <template slot="paneL">
  9. <el-scrollbar class="tree-scroll">
  10. <el-tree :data="treeData"
  11. :props="defaultProps"
  12. node-key="id"
  13. default-expand-all
  14. highlight-current
  15. v-if="hideLeft==1"
  16. :expand-on-click-node="false"
  17. @node-contextmenu="rightReportNodeEvent"
  18. @node-click="templateNameClick" class="lefttree">
  19. <!-- <span class="custom-tree-node" slot-scope="{ node, data }">
  20. <i class="el-icon-folder" style="color: #DFBA49; margin-right: 5px;"></i>
  21. <span style="font-size: 15px;">{{ node.label }}</span>
  22. </span> -->
  23. <span class="custom-tree-node" slot-scope="{ node, data }">
  24. <!-- <span style="font-size: 15px;">{{ node.label }}</span> -->
  25. <svg-icon v-if="data.id == -1" icon-class="reports" />
  26. <template v-else>
  27. <svg-icon v-if="data.templateType == 0" icon-class="report_m" />
  28. <svg-icon v-else-if="data.templateType == 1 || data.templateType == 3" icon-class="report_a" />
  29. <svg-icon v-else-if="data.templateType == 2 || data.templateType == 4" icon-class="report_e" />
  30. <svg-icon v-else-if="data.templateType == 5 || data.templateType == 6" icon-class="report_d" />
  31. <svg-icon v-else icon-class="report_m" />
  32. </template>
  33. <span :title="data.templateName" style="margin-left: 2px;font-size: 14px;">{{
  34. data.templateName
  35. }}</span>
  36. <span v-if="data.id == -1" style="position: absolute; right: 10px;">
  37. <el-button type="text" size="mini" title="新建报表" @click="addReportEvent"
  38. icon="el-icon-document-add"></el-button>
  39. <el-button type="text" size="mini" style="margin-left: 5px" title="导入报表"
  40. @click="importReportEvent" icon="el-icon-upload2"></el-button>
  41. <input ref="importFileNode" class="import-file-node" type="file" @change="loadExcel"
  42. accept=".xlsx" style="display: none;"></input>
  43. </span>
  44. </span>
  45. </el-tree>
  46. </el-scrollbar>
  47. <span style="position: absolute; right: 10px;top:300px">
  48. <el-button type="text" size="mini" title="" @click="splitClickEvent" :icon="iconstr"></el-button>
  49. </span>
  50. </template>
  51. <template slot="paneR">
  52. <iframe @load="load(-1)" id="myIframe" :src="iframeSrc"
  53. :style="{ 'height': '90vh', 'width': '100%', 'border': 'none', 'display': 'block' }"></iframe>
  54. </template>
  55. </split-pane>
  56. <el-row>
  57. <!-- 报表模板列表右键操作 -->
  58. <ul v-show="visibleReportChildMenu" :style="{ left: menuLeft + 'px', top: menuTop + 'px' }"
  59. class="contextmenu contextmenu-black">
  60. <li @click="updateReportName">修改名称</li>
  61. <li @click="exportReport">导出</li>
  62. <li @click="delReport">删除</li>
  63. </ul>
  64. </el-row>
  65. </div>
  66. </div>
  67. </template>
  68. <script>
  69. import CommonTable from '@/components/CommonTable/index.vue'
  70. import { getAllTableTemplate,updateTableTemplateNameById,delTableTemplateById } from '@/api/report/tableTemplate'
  71. import {
  72. showConfirmWin,
  73. showAlertMsgWin,
  74. showPromptWin,
  75. showAlertWin,
  76. showLoading,
  77. goPage,
  78. checkReportChangeStatus
  79. } from "@/utils/cqcy"
  80. import LuckyExcel from 'luckyexcel'
  81. import { getToken } from '@/utils/auth'
  82. import ExcelJS from 'exceljs';
  83. export default {
  84. created() {
  85. this.getAllTableTemplate()
  86. const loading = showLoading(this, '加载中,请稍候···')
  87. const task = setInterval(() => {
  88. loading.close()
  89. }, 200); },
  90. components: {
  91. CommonTable
  92. },
  93. watch: {
  94. '$route.query.t': {
  95. handler(now, old) {
  96. this.sendMsg({
  97. cmd: 'show',
  98. data: {
  99. reportId: this.$route.query.id
  100. }
  101. })
  102. }
  103. },
  104. visibleReportChildMenu(value) {
  105. if (value) {
  106. document.body.addEventListener("click", this.closeMenu);
  107. } else {
  108. document.body.removeEventListener("click", this.closeMenu);
  109. }
  110. },
  111. },
  112. data() {
  113. return {
  114. isLoadingStatus: false,
  115. //reportId: null,
  116. reportId: 0,
  117. hideLeft:1,
  118. leftpercent:15,
  119. iconstr:'el-icon-caret-left',
  120. type: '',
  121. iframeSrc: process.env.VUE_APP_BASE_API + '/reportSheet/index.html',
  122. // iframeSrc: 'http://localhost:83/#/tableTemplate/templateDetail?id=18',
  123. visibleReportChildMenu: false,
  124. visibleReportMenu:false,
  125. chooseReportData: null,
  126. // 右键布局显示位置
  127. menuLeft: 0,
  128. // 右键布局显示位置
  129. menuTop: 0,
  130. treeData:[
  131. {
  132. id:-1,
  133. templateName: '报表列表',
  134. children: []
  135. }
  136. ],
  137. defaultProps: {
  138. children: 'children',
  139. label: 'templateName'
  140. },
  141. queryParams: {
  142. page: 1,
  143. limit: 10,
  144. templateName: null,
  145. templateType: null
  146. },
  147. tableTotal: 0,
  148. //报表类型
  149. reportTableTypeList: [
  150. {
  151. value: 0,
  152. label: '手动报表'
  153. }, {
  154. value: 1,
  155. label: '周期报表'
  156. }, {
  157. value: 2,
  158. label: '事件驱动报表'
  159. }, {
  160. value: 3,
  161. label: '设备报表'
  162. }
  163. ]
  164. }
  165. },
  166. methods: {
  167. load(e) {
  168. //console.log(this.reportId)
  169. if(e==-1){
  170. return;
  171. }
  172. const _this = this
  173. const files = localStorage.getItem('IMPORT_FILES_JSON')
  174. _this.sendMsg({
  175. cmd: 'init',
  176. data: {
  177. token: getToken(),
  178. url: process.env.VUE_APP_BASE_API,
  179. reportId: this.reportId,
  180. type: this.type,
  181. files
  182. }
  183. })
  184. localStorage.setItem('IMPORT_FILES_JSON', '')
  185. window.addEventListener('message', function (event) {
  186. const json = event.data
  187. if (json.cmd === 'close') {
  188. //_this.$router.go(-1)
  189. }else if(json.cmd==='newsave'){
  190. console.log('newsave')
  191. _this.getAllTableTemplate()
  192. }
  193. })
  194. },
  195. sendMsg(msg) {
  196. document.getElementById('myIframe').contentWindow.postMessage(msg, '*')
  197. },
  198. /** 如果userId为空,则获取当前登录人的所有报表模板;不为空,则查询对应的参数的报表模板 */
  199. getAllTableTemplate() {
  200. getAllTableTemplate(this.queryParams).then(res => {
  201. if (!res || !res.data) {
  202. this.$message({
  203. message: '数据查询失败!',
  204. type: 'warning'
  205. })
  206. return
  207. }
  208. this.tableTotal = res.data.count
  209. this.tableData = res.data.tableTemplateList
  210. //this.treeData = res.data.tableTemplateList
  211. this.treeData[0].children = res.data.tableTemplateList
  212. if(res.data.count>0){
  213. this.reportId = res.data.tableTemplateList[0].id
  214. this.type = ''
  215. }
  216. this.load(this.reportId)
  217. })
  218. },
  219. /** 报表名称点击事件 */
  220. templateNameClick(data) {
  221. this.closeMenu()
  222. if(data.id==-1){
  223. return;
  224. };
  225. //checkReportChangeStatus()
  226. this.reportId=data.id
  227. this.type = ''
  228. this.load(this.reportId)
  229. },
  230. addReportEvent(){
  231. this.reportId=0
  232. this.type = ''
  233. this.load(this.reportId)
  234. },
  235. splitClickEvent(){
  236. this.hideLeft = !this.hideLeft;
  237. if(this.leftpercent==0){
  238. this.leftpercent=15
  239. }else{
  240. this.leftpercent=0
  241. };
  242. if(this.iconstr=="el-icon-caret-left"){
  243. this.iconstr = 'el-icon-caret-right'
  244. }else{
  245. this.iconstr = 'el-icon-caret-left'
  246. }
  247. },
  248. importReportEvent(){
  249. this.$refs.importFileNode.dispatchEvent(new MouseEvent('click'))
  250. },
  251. /** 关闭右键弹出层 */
  252. closeMenu() {
  253. this.visibleReportMenu = false;
  254. this.visibleReportChildMenu = false;
  255. },
  256. /** 报表配置右键菜单事件 */
  257. rightReportNodeEvent(event, data, node, target) {
  258. this.closeMenu();
  259. this.menuLeft = 80;
  260. if (data.id == -1) {
  261. this.menuTop = 30;
  262. this.visibleReportMenu = true;
  263. this.chooseReportParentNode = node;
  264. } else if (!data.itemName) {
  265. // this.menuTop = 30 + event.layerY
  266. this.menuTop = 30 + event.y - 100;
  267. this.visibleReportChildMenu = true;
  268. this.chooseReportData = data;
  269. this.reportId = data.id;
  270. this.chooseReportParentNode = node.parent;
  271. }
  272. },
  273. updateReportName(){
  274. this.closeMenu();
  275. if (!this.chooseReportData || !this.chooseReportData.id) {
  276. showAlertMsgWin(this, null, cqcyCode[201]);
  277. return;
  278. }
  279. showPromptWin(
  280. this,
  281. "修改报表名称",
  282. "请输入报表名称",
  283. this.chooseReportData.templateName,
  284. (val) => {
  285. if (!val || !val.trim()) {
  286. return "报表名称不能为空";
  287. }
  288. if (val.length > 20) {
  289. return "报表名称必须在20字以内";
  290. }
  291. },
  292. (value) => {
  293. const loading = showLoading(this, "修改中,请稍候···");
  294. let params = {
  295. id: this.chooseReportData.id,
  296. templateName: encodeURIComponent(value),
  297. };
  298. updateTableTemplateNameById(params)
  299. .then((res) => {
  300. loading.close();
  301. let msg = res.data ? "修改成功!" : "修改失败!";
  302. showAlertMsgWin(this, null, msg, () => {
  303. this.getAllTableTemplate();
  304. // goPage(this, "/reportTemplate", {
  305. // id: this.chooseReportData.id,
  306. // });
  307. });
  308. })
  309. .catch((e) => {
  310. loading.close();
  311. showAlertWin(this, null, e);
  312. });
  313. }
  314. );
  315. },
  316. delReport(){
  317. this.closeMenu();
  318. if (!this.chooseReportData || !this.chooseReportData.id) {
  319. showAlertMsgWin(this, null, cqcyCode[201]);
  320. return;
  321. }
  322. showConfirmWin(this, null, '您确定要删除该报表设计吗?', () => {
  323. // delReportDataPolicyById(this.chooseReportData.id).then(res => {
  324. // this.$message({
  325. // message: '删除报表设计成功!',
  326. // type: 'success'
  327. // })
  328. // this.getAllTableTemplate()
  329. // })
  330. delTableTemplateById(this.chooseReportData.id, 0).then((res) => {
  331. if (res.msg == "fail") {
  332. this.delById(res.data, this.chooseReportData.id)
  333. return
  334. }
  335. if (res.data) {
  336. this.$message({
  337. message: '删除成功!',
  338. type: 'success'
  339. })
  340. } else {
  341. this.$message({
  342. message: '删除失败!',
  343. type: 'warning'
  344. })
  345. return
  346. }
  347. this.getAllTableTemplate()
  348. })
  349. })
  350. },
  351. exportReport(){
  352. this.closeMenu();
  353. this.reportId = this.chooseReportData.id;
  354. this.type = 'export';
  355. this.load(this.reportId);
  356. },
  357. /** 新增报表设计 */
  358. addTableTemplateEvent() {
  359. this.$router.push({
  360. path: '/tableTemplate/templateDetail'
  361. })
  362. },
  363. /** 导入报表设计 */
  364. importTableTemplateEvent() {
  365. this.$refs.importFileNode.dispatchEvent(new MouseEvent('click'))
  366. },
  367. /** 删除报表模板 */
  368. delById(tips, id) {
  369. showConfirmWin(this, null, tips, () => {
  370. delTableTemplateById(id, 1).then((res) => {
  371. if (res.data) {
  372. this.$message({
  373. message: '删除成功!',
  374. type: 'success'
  375. })
  376. } else {
  377. this.$message({
  378. message: '删除失败!',
  379. type: 'warning'
  380. })
  381. return
  382. }
  383. this.getAllTableTemplate()
  384. })
  385. });
  386. },
  387. loadExcel(evt) {
  388. let _this = this
  389. const files = evt.target.files
  390. if (files == null || files.length == 0) {
  391. return
  392. }
  393. if (!files[0].name.endsWith('.xlsx')) {
  394. showAlertMsgWin(_this, null, '只能导入xlsx格式文件!')
  395. return
  396. }
  397. const loading = showLoading(this, '文件导入中,请稍候···')
  398. let status = true
  399. const reader = new FileReader();
  400. reader.onload = function (event) {
  401. const data = new Uint8Array(event.target.result);
  402. const workbook = new ExcelJS.Workbook();
  403. workbook.xlsx.load(data).then(function (workbook) {
  404. workbook.eachSheet(function (worksheet, sheetId) {
  405. if (status) {
  406. status = false
  407. let hyperlink = {}
  408. worksheet.eachRow(function (row, rowNumber) {
  409. row.eachCell(function (cell, colNumber) {
  410. if (cell.type === ExcelJS.ValueType.Hyperlink) {
  411. hyperlink[(parseInt(rowNumber) - 1) + '_' + (parseInt(colNumber) - 1)] = {
  412. linkAddress: cell.value.hyperlink,
  413. linkTooltip: cell.value.text,
  414. linkType: 'external'
  415. }
  416. } else if (cell.type === ExcelJS.ValueType.String) {
  417. if (/![A-Z]+\d+/g.test(cell.value) && cell.value.startsWith(worksheet.name + '!')) {
  418. hyperlink[(parseInt(rowNumber) - 1) + '_' + (parseInt(colNumber) - 1)] = {
  419. linkAddress: cell.value,
  420. linkTooltip: cell.value,
  421. linkType: 'internal'
  422. }
  423. }
  424. }
  425. });
  426. });
  427. LuckyExcel.transformExcelToLucky(files[0], function (exportJson, luckysheetfile) {
  428. loading.close()
  429. localStorage.setItem('IMPORT_FILES_JSON', JSON.stringify({
  430. luckysheetfile,
  431. hyperlink
  432. }))
  433. // _this.$router.push({
  434. // path: '/tableTemplate/templateDetail'
  435. // , query: {
  436. // id: -999,
  437. // type: 'import',
  438. // }
  439. // })
  440. _this.reportId = 0
  441. _this.type = 'import'
  442. _this.load(_this.reportId)
  443. document.getElementsByClassName('import-file-node')[0].value = ''
  444. })
  445. }
  446. });
  447. });
  448. };
  449. reader.onerror = function (event) {
  450. document.getElementsByClassName('import-file-node')[0].value = ''
  451. showAlertMsgWin(_this, null, '读取文件失败,请稍后再试!')
  452. return
  453. };
  454. reader.readAsArrayBuffer(files[0]);
  455. },
  456. },
  457. }
  458. </script>
  459. <style rel="stylesheet/scss" lang="scss">
  460. .el-tree{
  461. //background: #646464 !important;
  462. //color: #fff;
  463. //font-size:14px;
  464. }
  465. .sy-content{
  466. //background: #646464 !important;
  467. }
  468. .tree-scroll {
  469. height: 90vh;
  470. }
  471. .contextmenu {
  472. margin: 0;
  473. background: #fff;
  474. z-index: 3000;
  475. position: absolute;
  476. list-style-type: none;
  477. padding: 5px 0;
  478. border-radius: 4px;
  479. font-size: 12px;
  480. font-weight: 400;
  481. color: #333;
  482. box-shadow: 2px 2px 3px 0 rgb(0 0 0 / 30%);
  483. }
  484. .contextmenu-black {
  485. background: #000;
  486. color: #fff;
  487. }
  488. .contextmenu-black li {
  489. border-bottom: 1px solid;
  490. margin: 0 5px;
  491. }
  492. .contextmenu li {
  493. margin: 0;
  494. padding: 7px 16px;
  495. cursor: pointer;
  496. }
  497. .contextmenu-black li:last-child {
  498. border-bottom: 0;
  499. }
  500. .splitter-paneL {
  501. background-color: #2c3e50;
  502. padding-right: 0 !important;
  503. }
  504. .splitter-paneR {
  505. padding-left: 0 !important;
  506. }
  507. .splitter-pane-resizer {
  508. opacity: unset !important;
  509. background: #2c3e50 !important;
  510. }
  511. </style>