index.vue 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. <template>
  2. <div class="cqcy-content" style="margin: 0;height: calc(100% - 70px);">
  3. <breadcrumb-view :breadcrumbList="breadcrumbList" :show-index="false"></breadcrumb-view>
  4. <el-select class="cy-nav-sx" v-model="searchValue" placeholder="筛选报表" @change="changeReportTypeEvent">
  5. <el-option
  6. v-for="item in reportOptions"
  7. :key="item.value"
  8. :label="item.label"
  9. :value="item.value">
  10. </el-option>
  11. </el-select>
  12. <el-button type="primary" class="cy-nav-btn" icon="el-icon-menu" size="mini" @click="addReportEvent">新增</el-button>
  13. <el-divider></el-divider>
  14. <div class="cy-main">
  15. <div class="cy-main-left" v-if="!showMainView">
  16. <div class="cy-list">
  17. <ul>
  18. <li v-for="item in reportDataList">
  19. <i class="el-icon-error"
  20. style="float: right; margin-top: -10px; margin-right: -10px; cursor: pointer;"
  21. @click="removeReportItem(item)"></i>
  22. <div class="cy-item-name" @click="handleReportNodeClick(item)">{{ item.reportTableName }}</div>
  23. <div style="display: flex; float: left; cursor: pointer;" @click="setUserGroupEvent(item)">
  24. <i v-if="item.userGroupId" class="el-icon-user-solid"></i>
  25. <i v-else class="el-icon-user"></i>
  26. </div>
  27. <div style="display: flex; float: right;">
  28. <div class="cy-item-val" style="width: 60px;">自动报表</div>
  29. <el-switch :value="item.isAutoReport == '1'" active-color="#13ce66"
  30. @change="switchChangeEvent($event, item)"></el-switch>
  31. </div>
  32. </li>
  33. </ul>
  34. </div>
  35. <el-pagination
  36. background
  37. @size-change="handleSizeChange"
  38. @current-change="handleCurrentChange"
  39. :current-page="reportPage"
  40. :page-size="reportLimit"
  41. layout="total, prev, pager, next"
  42. :total="reportTotal">
  43. </el-pagination>
  44. </div>
  45. <div class="cy-main-right" :style="showMainView ? '' : 'display: none;'">
  46. <el-row v-if="btnType == 'add'" style="margin: 10px 20px;">
  47. <el-button type="primary" size="mini" icon="el-icon-document" @click="saveReportInfo">保存报表</el-button>
  48. <el-button type="danger" size="mini" icon="el-icon-circle-close" @click="cancelSaveReport">取消</el-button>
  49. </el-row>
  50. <el-row v-if="btnType == 'show'" style="margin: 10px 20px;">
  51. <el-button type="primary" size="mini" icon="el-icon-download" @click="downloadReport">下载</el-button>
  52. <el-button type="warning" size="mini" icon="el-icon-printer" @click="printExcel">打印</el-button>
  53. <el-button type="danger" size="mini" icon="el-icon-circle-close" @click="cancelSaveReport">取消</el-button>
  54. </el-row>
  55. <div id="luckysheet"
  56. style="margin:0px;padding:0px;position:relative;height:100%;width:100%;left: 20px;top: 0px;bottom:0px;">
  57. </div>
  58. </div>
  59. </div>
  60. <!-- 报表模板选择 -->
  61. <el-dialog
  62. title="选择报表模板"
  63. width="80%"
  64. top="10vh"
  65. center
  66. :before-close="dialogClose"
  67. :visible.sync="dialogReportTemplateVisible"
  68. :close-on-click-modal="false"
  69. :append-to-body="true">
  70. <el-form ref="reportForm" :model="reportForm" :rules="reportRules" label-width="80px" label-position="top">
  71. <el-form-item label="报表模板" prop="reportTemplate">
  72. <el-select v-model="reportForm.reportTemplate" filterable placeholder="请选择报表模板" style="width: 100%;">
  73. <el-option
  74. v-for="item in reportTemplateList"
  75. :key="item.id"
  76. :label="item.templateName"
  77. :value="item.id">
  78. </el-option>
  79. </el-select>
  80. </el-form-item>
  81. <el-form-item label="取值方式" prop="reportValueFormat">
  82. <el-radio v-model="reportForm.reportValueFormat" label="0">时段数据</el-radio>
  83. </el-form-item>
  84. <el-form-item style="text-align: center;">
  85. <el-button type="primary" @click="chooseReportEvent" style="margin-top: 20px;">确定</el-button>
  86. <el-button @click="dialogClose" style="margin-top: 20px;">取消</el-button>
  87. </el-form-item>
  88. </el-form>
  89. </el-dialog>
  90. <!-- 用户组选择 -->
  91. <el-dialog
  92. title="选择用户组"
  93. width="80%"
  94. top="10vh"
  95. center
  96. :before-close="dialogClose"
  97. :visible.sync="dialogUserGroupVisible"
  98. :close-on-click-modal="false"
  99. :append-to-body="true">
  100. <el-form label-width="80px">
  101. <el-form-item label="用户组">
  102. <el-select v-model="userGroupInfo" filterable placeholder="请选择用户组" style="width: 100%;">
  103. <el-option
  104. v-for="item in userGroupList"
  105. :key="item.id"
  106. :label="item.userGroupName"
  107. :value="item.id">
  108. </el-option>
  109. </el-select>
  110. </el-form-item>
  111. <el-form-item style="text-align: center;">
  112. <el-button type="primary" @click="chooseUserGroupEvent" style="margin-top: 20px;">确定</el-button>
  113. <el-button @click="dialogClose" style="margin-top: 20px;">取消</el-button>
  114. </el-form-item>
  115. </el-form>
  116. </el-dialog>
  117. <!-- 自动报表CRON定时表达式 -->
  118. <el-dialog
  119. title="请输入定时任务表达式"
  120. width="80%"
  121. top="10vh"
  122. center
  123. :before-close="dialogClose"
  124. :visible.sync="dialogAutoReportVisible"
  125. :close-on-click-modal="false"
  126. :append-to-body="true">
  127. <el-form label-width="80px">
  128. <el-form-item label="表达式">
  129. <el-input type="text" placeholder="请输入定时任务表达式" v-model="cronVal" maxlength="50"></el-input>
  130. </el-form-item>
  131. <div>
  132. <label>常用定时任务表达式例子</label>
  133. <ul>
  134. <li v-for="(item, i) in cronList" style="margin-bottom: 5px;">
  135. <span class="cron-txt" @click="cronNodeEvent(item)">{{ item.value }}</span>
  136. <span class="cron-txt-desc">{{ item.label }}</span>
  137. </li>
  138. </ul>
  139. </div>
  140. <el-form-item style="text-align: center;">
  141. <el-button type="primary" @click="chooseCronEvent" style="margin-top: 20px;">确定</el-button>
  142. <el-button @click="dialogClose" style="margin-top: 20px;">取消</el-button>
  143. </el-form-item>
  144. </el-form>
  145. </el-dialog>
  146. <!-- 报表下载类型选择 -->
  147. <el-dialog
  148. title="选择下载类型"
  149. width="500px"
  150. center
  151. :before-close="dialogClose"
  152. :visible.sync="dialogDownloadReportTypeVisible"
  153. :close-on-click-modal="false"
  154. :append-to-body="true">
  155. <div style="text-align: center;">
  156. <el-radio v-model="downloadType" label="1">Excel</el-radio>
  157. <el-radio v-model="downloadType" label="2" disabled>PDF</el-radio>
  158. </div>
  159. <div style="text-align: center; margin-top: 40px;">
  160. <el-button type="primary" @click="downloadReportEvent">确定</el-button>
  161. </div>
  162. </el-dialog>
  163. <div id="print-area" style="display: none;position: absolute;z-index: 0;top: 0;width: 100%;height: 100vh;overflow: hidden;">
  164. <div id="print-html" ref="printPayFeeNew"></div>
  165. </div>
  166. </div>
  167. </template>
  168. <script>
  169. import BreadcrumbView from '@/components/BreadcrumbView'
  170. import {getNowFormatDate, showLoading} from '@/utils/cqcy'
  171. import {
  172. delReportTableById,
  173. getAllDataModel,
  174. getAllReportTable,
  175. getAllTableTemplate, getChartData,
  176. getDataModelById, getReportTableById, getTableData,
  177. getTableTemplateById, saveReport, tableAssignUserById, tableExchangeTypeById
  178. } from '@/api/datasource'
  179. import {insertEChart} from 'luckytool'
  180. // import {insertEChart, exportExcel, packtable} from 'luckytool'
  181. // import ExcelJS from 'exceljs'
  182. import Print from 'print-js'
  183. import {getUsername} from '@/utils/auth'
  184. import {exportExcel} from '@/utils/export'
  185. import {getAllUserGroup} from "@/api/user";
  186. export default {
  187. name: "index",
  188. components: {
  189. BreadcrumbView
  190. },
  191. data() {
  192. return {
  193. breadcrumbList: ['我的报表'],
  194. groupProps: {
  195. isLeaf: 'leaf'
  196. },
  197. showMainView: false,
  198. chooseMyReport: null,
  199. dialogReportTemplateVisible: false,
  200. dialogDownloadReportTypeVisible: false,
  201. dialogAutoReportVisible: false,
  202. dialogUserGroupVisible: false,
  203. downloadType: '1',
  204. btnType: '',
  205. userGroupInfo: null,
  206. userGroupList: [],
  207. cronVal: '',
  208. cronList: [],
  209. reportId: null,
  210. reportTemplateList: [],
  211. dataModelList: [],
  212. chooseReportTemplate: null,
  213. delFlag: 0,
  214. reportForm: {
  215. reportTemplate: null,
  216. reportValueFormat: '0'
  217. },
  218. searchValue: null,
  219. reportOptions: [{
  220. label: '所有报表',
  221. value: -1
  222. }, {
  223. label: '自动报表',
  224. value: 1
  225. }, {
  226. label: '非自动报表',
  227. value: 0
  228. }],
  229. reportTotal: 0,
  230. reportPage: 1,
  231. reportLimit: 20,
  232. reportDataList: [],
  233. reportRules: {
  234. reportTemplate: [
  235. {required: true, message: '请选择报表模板', trigger: 'change'}
  236. ]
  237. }
  238. }
  239. },
  240. watch: {},
  241. beforeDestroy() {
  242. },
  243. created() {
  244. luckysheet.destroy()
  245. this.loadReport()
  246. this.initCronList()
  247. },
  248. methods: {
  249. handleSizeChange(val) {
  250. this.reportPage = 1
  251. this.reportLimit = val
  252. this.loadReport()
  253. },
  254. handleCurrentChange(val) {
  255. this.reportPage = val
  256. this.loadReport()
  257. },
  258. changeReportTypeEvent(val) {
  259. this.reportPage = 1
  260. this.loadReport()
  261. },
  262. validCron(value) {
  263. const cronParse = require('cron-parser')
  264. try {
  265. const interval = cronParse.parseExpression(value)
  266. console.log('cronDate:', interval.next().toDate())
  267. return true
  268. } catch (e) {
  269. }
  270. return false
  271. },
  272. chooseCronEvent() {
  273. if (!this.cronVal) {
  274. this.$message({
  275. message: '请输入定时任务表达式!',
  276. type: 'warning'
  277. })
  278. return
  279. }
  280. if (!this.validCron(this.cronVal)) {
  281. this.$message({
  282. message: '定时任务表达式格式不正确,请检查!',
  283. type: 'warning'
  284. })
  285. return
  286. }
  287. const loading = showLoading(this, '请稍候···')
  288. let params = {
  289. 'id': this.reportId,
  290. 'isAutoReport': '1',
  291. 'cron': this.cronVal
  292. }
  293. tableExchangeTypeById(params).then(res => {
  294. loading.close()
  295. this.dialogClose()
  296. this.loadReport()
  297. }).catch((e) => {
  298. loading.close()
  299. this.$message({
  300. message: '保存失败!',
  301. type: 'warning'
  302. })
  303. })
  304. },
  305. switchChangeEvent(val, data) {
  306. if (val) {
  307. this.dialogAutoReportVisible = true
  308. this.reportId = data.id
  309. getReportTableById(data.id).then(res => {
  310. this.cronVal = res.data.cron
  311. }).catch((e) => {
  312. console.log(e)
  313. })
  314. return
  315. }
  316. const loading = showLoading(this, '请稍候···')
  317. let params = {
  318. 'id': data.id,
  319. 'isAutoReport': '0'
  320. }
  321. tableExchangeTypeById(params).then(res => {
  322. loading.close()
  323. this.loadReport()
  324. }).catch((e) => {
  325. loading.close()
  326. this.$message({
  327. message: '保存失败!',
  328. type: 'warning'
  329. })
  330. })
  331. },
  332. /** 新增报表 */
  333. addReportEvent() {
  334. this.getAllReportTemplate()
  335. this.dialogReportTemplateVisible = true
  336. },
  337. /** 模版报表选择 */
  338. chooseReportEvent() {
  339. this.$refs['reportForm'].validate((valid) => {
  340. if (valid) {
  341. const loading = showLoading(this, '数据加载中,请稍候···')
  342. getTableTemplateById(this.reportForm.reportTemplate).then(res => {
  343. if (!res.data) {
  344. loading.close()
  345. this.$message({
  346. message: '选择的报表模板不存在!',
  347. type: 'warning'
  348. })
  349. return
  350. }
  351. this.showMainView = true
  352. let luckyData = JSON.parse(res.data.templateData)
  353. this.chooseReportTemplate = luckyData
  354. this.drawLuckyExcel(luckyData, () => {
  355. loading.close()
  356. this.btnType = 'add'
  357. this.$refs.reportForm.resetFields()
  358. this.dialogReportTemplateVisible = false
  359. })
  360. }).catch((e) => {
  361. loading.close()
  362. console.log(e)
  363. this.$message({
  364. message: '数据查询失败!',
  365. type: 'warning'
  366. })
  367. })
  368. }
  369. })
  370. },
  371. /** 根据 ID 查询数据模型 */
  372. getDataModelById(loading, reportData) {
  373. if (!loading) {
  374. loading = showLoading(this, '数据加载中,请稍候···')
  375. }
  376. getDataModelById(this.reportForm.operationRule).then(res => {
  377. loading.close()
  378. if (!res.data) {
  379. this.$message({
  380. message: '选择的四则运算表达式不存在!',
  381. type: 'warning'
  382. })
  383. return
  384. }
  385. let luckyData = JSON.parse(reportData.templateData)
  386. this.drawLuckyExcel(luckyData)
  387. this.btnType = 'add'
  388. this.dialogReportTemplateVisible = false
  389. }).catch((e) => {
  390. loading.close()
  391. this.$message({
  392. message: '数据查询失败!',
  393. type: 'warning'
  394. })
  395. })
  396. },
  397. /** 绘制 Excel 表 */
  398. drawLuckyExcel(luckyData, callback) {
  399. luckysheet.destroy()
  400. let option = luckyData.option
  401. option.data = luckyData.data
  402. let charts = luckyData.charts
  403. let tables = luckyData.tables
  404. luckysheet.create(option)
  405. for (let i in charts) {
  406. this.insertEChartInfo(charts[i])
  407. }
  408. for (let i in tables) {
  409. this.insertTableInfo(tables[i])
  410. }
  411. if (callback) {
  412. callback()
  413. }
  414. },
  415. /** 获取图表数据 */
  416. getChartData(data, callback) {
  417. getChartData(data).then(res => {
  418. if (!res.data) {
  419. if (callback) callback([])
  420. return
  421. }
  422. if (callback) callback(res.data)
  423. }).catch((e) => {
  424. if (callback) callback([])
  425. })
  426. },
  427. /** 获取表格数据 */
  428. getTableData(data, callback) {
  429. getTableData(data).then(res => {
  430. if (!res.data) {
  431. if (callback) callback([])
  432. return
  433. }
  434. if (callback) callback(res.data)
  435. }).catch((e) => {
  436. if (callback) callback([])
  437. })
  438. },
  439. /** 向 Excel 插入表格 */
  440. insertTableInfo(tableData) {
  441. let params = []
  442. let itemList = tableData.item
  443. for (let i in itemList) {
  444. let temp = itemList[i]
  445. params.push({
  446. 'itemGroupId': temp.itemGroupId,
  447. 'itemName': temp.itemName
  448. })
  449. }
  450. let p = {
  451. 'reportValueFormat': this.reportForm.reportValueFormat,
  452. 'tableDataDtoChList': params,
  453. 'showType': tableData.showType
  454. }
  455. this.getTableData(p, (res) => {
  456. // console.log('option==>', luckysheet.getAllSheets()[0])
  457. // console.log('table1==>', res)
  458. // const optionData = packtable(res, luckysheet.getAllSheets()[0])
  459. // console.log('table2==>', optionData)
  460. setTimeout(() => {
  461. this.drawTableData(res, p.showType, luckysheet.getAllSheets()[0])
  462. }, 500)
  463. })
  464. },
  465. drawTableData(sources, type, option) {
  466. option.celldata.map(item => {
  467. if (item.v.v) {
  468. item.v.v = String(item.v.v).trim();
  469. if (item.v.v.match(/\${([^}]+)}/g)) { // 替换${xxx}内部数据
  470. let currDate = getNowFormatDate('date')
  471. if (sources.length > 0 && sources[0].dataList.length > 0) {
  472. currDate = sources[0].dataList[0].dh
  473. currDate = currDate.substring(0, 10)
  474. }
  475. sources.forEach(source => {
  476. let itemName = source.itemName
  477. itemName = itemName.substring(itemName.lastIndexOf('.') + 1)
  478. let name = '${' + source.itemGroupId + '.' + itemName + '.' + type.dataId + '}'
  479. if (item.v.v === name) {
  480. let dataList = source.dataList
  481. // for (let i = 0; i < dataList.length; i++) {
  482. for (let i = 0; i < type.valLine; i++) {
  483. let p_r = parseInt(item.r)
  484. let p_c = parseInt(item.c)
  485. if (type.valType == '2') p_r += i
  486. else p_c += i
  487. luckysheet.setCellValue(p_r, p_c, dataList[i].dv)
  488. }
  489. }
  490. })
  491. if (item.v.v.indexOf('${currDate}') > -1) {
  492. let val = item.v.v
  493. val = val.replace('${currDate}', currDate)
  494. luckysheet.setCellValue(item.r, item.c, val)
  495. }
  496. if (item.v.v.indexOf('${currDateTime}') > -1) {
  497. let currDateTime = getNowFormatDate('')
  498. let val = item.v.v
  499. val = val.replace('${currDateTime}', currDateTime)
  500. luckysheet.setCellValue(item.r, item.c, val)
  501. }
  502. if (item.v.v.indexOf('${userName}') > -1) {
  503. let val = item.v.v
  504. val = val.replace('${userName}', getUsername())
  505. luckysheet.setCellValue(item.r, item.c, val)
  506. }
  507. if (item.v.v.indexOf('${winUserName}') > -1) {
  508. let val = item.v.v
  509. val = val.replace('${winUserName}', process.env.VUE_APP_WINNAME)
  510. luckysheet.setCellValue(item.r, item.c, val)
  511. }
  512. }
  513. }
  514. })
  515. },
  516. /** 向 Excel 插入图表 */
  517. insertEChartInfo(chart) {
  518. console.log(chart)
  519. let info = chart.info
  520. let itemList = chart.item
  521. let groupId = null
  522. let itemArr = []
  523. for (let i in itemList) {
  524. let temp = itemList[i]
  525. if (i == 0) groupId = temp.itemGroupId
  526. itemArr.push({
  527. 'id': temp.id,
  528. 'itemName': temp.itemName,
  529. // 'dataModelId': temp.rule.id
  530. })
  531. }
  532. let params = {
  533. 'type': info.option.series[0].type,
  534. 'itemGroup': {
  535. 'id': groupId,
  536. 'itemList': itemArr
  537. }
  538. }
  539. this.getChartData(params, (res) => {
  540. let result = (res instanceof Array) ? res : []
  541. // 系列
  542. let series = info.option.series
  543. for (let i in series) {
  544. let temp = series[i]
  545. temp.data = []
  546. for (let j in result) {
  547. if (params.type == 'pie') {
  548. temp.name = result[j].name
  549. temp.data.push({
  550. 'name': result[j].describe ? result[j].describe : result[j].name,
  551. 'value': result[j].value
  552. })
  553. } else {
  554. temp.data = result[i].dataList ? result[i].dataList : []
  555. }
  556. }
  557. }
  558. if (params.type != 'pie') {
  559. // x 轴
  560. let xAxis = info.option.xAxis
  561. let legend = info.option.legend
  562. xAxis.data = []
  563. legend.data = []
  564. for (let i in result) {
  565. let name = result[i].describe ? result[i].describe : result[i].itemName
  566. xAxis.data.push(name)
  567. legend.data.push(name)
  568. }
  569. }
  570. console.log(params.type, info)
  571. setTimeout(() => {
  572. const sheet = luckysheet.getLuckysheetfile()[0]
  573. let optionData = sheet.data
  574. try {
  575. insertEChart({
  576. selector: '#luckysheet',
  577. info,
  578. sheet,
  579. optionData,
  580. echarts,
  581. luckysheet,
  582. $
  583. })
  584. } catch (e) {
  585. console.log(999, e == 'echarts is not defined')
  586. console.error(e)
  587. }
  588. }, 200)
  589. })
  590. },
  591. /** 查询所有报表信息 */
  592. getAllReportTemplate() {
  593. let params = {
  594. 'page': 1,
  595. 'limit': 1000
  596. }
  597. getAllTableTemplate(params).then(res => {
  598. if (!res.data) {
  599. return
  600. }
  601. this.reportTemplateList = res.data.tableTemplateList
  602. }).catch((e) => {
  603. })
  604. },
  605. /** 查询所有数据模型 */
  606. getAllDataModel() {
  607. let params = {
  608. 'page': 1,
  609. 'limit': 1000
  610. }
  611. getAllDataModel(params).then(res => {
  612. if (!res.data) {
  613. return
  614. }
  615. this.dataModelList = res.data.dataModelList
  616. }).catch((e) => {
  617. })
  618. },
  619. /** 查询我的报表列表 */
  620. loadReport() {
  621. let params = {
  622. 'page': this.reportPage,
  623. 'limit': this.reportLimit
  624. }
  625. if (this.searchValue != '-1') {
  626. params.isAutoReport = this.searchValue
  627. }
  628. getAllReportTable(params).then(res => {
  629. if (!res.data) {
  630. return
  631. }
  632. this.reportTotal = res.data.count
  633. this.reportDataList = res.data.reportTableList
  634. }).catch((e) => {
  635. })
  636. },
  637. /** 设置用户组信息 */
  638. setUserGroupEvent(data) {
  639. this.reportId = data.id
  640. this.dialogUserGroupVisible = true
  641. getAllUserGroup().then(res => {
  642. this.$nextTick(() => {
  643. this.userGroupList = res.data
  644. this.userGroupInfo = data.userGroupId
  645. })
  646. }).catch((e) => {
  647. })
  648. },
  649. chooseUserGroupEvent() {
  650. if (!this.userGroupInfo) {
  651. this.$message({
  652. message: '请选择用户组!',
  653. type: 'warning'
  654. })
  655. return
  656. }
  657. tableAssignUserById({
  658. 'id': this.reportId,
  659. 'groupId': this.userGroupInfo
  660. }).then(res => {
  661. this.$message({
  662. message: res.data,
  663. type: 'success'
  664. })
  665. this.dialogClose()
  666. this.loadReport()
  667. }).catch((e) => {
  668. })
  669. },
  670. /** 报表点击事件 */
  671. handleReportNodeClick(data) {
  672. if (data.id == -1 || this.delFlag == 1) {
  673. return
  674. }
  675. const loading = showLoading(this, '加载中,请稍候···')
  676. getReportTableById(data.id).then(res => {
  677. loading.close()
  678. if (!res.data) {
  679. this.$message({
  680. message: '选择的报表不存在!',
  681. type: 'warning'
  682. })
  683. return
  684. }
  685. this.showMainView = true
  686. this.chooseMyReport = res.data
  687. let reportTableData = res.data.reportTableData
  688. let luckyData = JSON.parse(reportTableData)
  689. // this.drawLuckyExcel(luckyData, () => {
  690. // this.btnType = 'show'
  691. // loading.close()
  692. // })
  693. luckysheet.destroy()
  694. let option = luckyData.option
  695. option.data = luckyData.data
  696. // 设置工作表保护
  697. option.data[0].config.authority = {
  698. sheet: 1, // 如果为 1 或 true,则该工作表受到保护;如果为 0 或 false,则该工作表不受保护。
  699. hintText: '该工作表受到保护,无法操作', // 弹窗提示的文字
  700. }
  701. // 关闭右键菜单
  702. option.cellRightClickConfig.chart = false
  703. option.cellRightClickConfig.columnWidth = false
  704. option.cellRightClickConfig.rowHeight = false
  705. option.cellRightClickConfig.deleteColumn = false
  706. option.cellRightClickConfig.deleteRow = false
  707. option.cellRightClickConfig.insertColumn = false
  708. option.cellRightClickConfig.insertRow = false
  709. // 关闭工具栏
  710. option.showtoolbar = false
  711. option.enableAddRow = false
  712. option.showtoolbarConfig = {
  713. bold: false,
  714. border: false,
  715. fillColor: false,
  716. font: false,
  717. fontSize: false,
  718. function: false,
  719. horizontalAlignMode: false,
  720. italic: false,
  721. mergeCell: false,
  722. moreFormats: false,
  723. paintFormat: false,
  724. strikethrough: false,
  725. textColor: false,
  726. underline: false,
  727. verticalAlignMode: false
  728. }
  729. // 钩子函数
  730. option.hook = {
  731. workbookCreateAfter() {
  732. option.data.forEach((data, i) => {
  733. data.chart.forEach((chart, j) => {
  734. console.log(chart.chart_id)
  735. let dom = document.getElementById(chart.chart_id + '_c')
  736. if (dom) dom.style.display = 'none'
  737. })
  738. })
  739. }
  740. }
  741. luckysheet.create(option)
  742. this.btnType = 'show'
  743. }).catch((e) => {
  744. console.log(e)
  745. loading.close()
  746. this.$message({
  747. message: '加载失败!',
  748. type: 'warning'
  749. })
  750. })
  751. },
  752. /** 报表移除 */
  753. removeReportItem(data) {
  754. this.$confirm('您确定要删除该报表吗?', '温馨提示', {
  755. confirmButtonText: '确定',
  756. cancelButtonText: '取消'
  757. }).then(() => {
  758. const loading = showLoading(this, '删除中,请稍候···')
  759. delReportTableById(data.id).then(res => {
  760. loading.close()
  761. let msg = res.data ? '删除成功!' : '删除失败!'
  762. let msgType = res.data ? 'success' : 'warning'
  763. this.$message({
  764. message: msg,
  765. type: msgType
  766. })
  767. this.loadReport()
  768. this.cancelSaveReport()
  769. }).catch((e) => {
  770. loading.close()
  771. this.$message({
  772. message: '删除失败!',
  773. type: 'warning'
  774. })
  775. })
  776. }).catch(() => {
  777. })
  778. },
  779. /** 获取单元格大小 */
  780. getCellSize(celldata, r, c, luckysheet) {
  781. const { cs, rs } = celldata.data[r][c].mc
  782. const rowhidden = celldata.config.rowhidden && Object.keys(celldata.config.rowhidden) || ''
  783. const rowArr = []
  784. for (let i = 0; i < rs; i++) {
  785. let rowIndex = r + i
  786. if (rowhidden.indexOf(String(rowIndex)) < 0) {
  787. rowArr.push(rowIndex)
  788. }
  789. }
  790. const colArr = []
  791. for (let j = 0; j < cs; j++) {
  792. colArr.push(c + j)
  793. }
  794. const totalHeight = Object.values(
  795. luckysheet.getRowHeight(rowArr)
  796. ).reduce((a, b) => a + b)
  797. const totalWidth = Object.values(
  798. luckysheet.getColumnWidth(colArr)
  799. ).reduce((a, b) => a + b)
  800. return {
  801. w: totalWidth,
  802. h: totalHeight
  803. }
  804. },
  805. /** 获取图片位置 */
  806. getImagePosition(num, arr) {
  807. let index = 0
  808. let minIndex
  809. let maxIndex
  810. for (let i = 0; i < arr.length; i++) {
  811. if (num < arr[i]) {
  812. index = i
  813. break
  814. }
  815. }
  816. if (index == 0) {
  817. minIndex = 0
  818. maxIndex = 1
  819. } else if (index == arr.length - 1) {
  820. minIndex = arr.length - 2
  821. maxIndex = arr.length - 1
  822. } else {
  823. minIndex = index - 1
  824. maxIndex = index
  825. }
  826. let min = arr[minIndex]
  827. let max = arr[maxIndex]
  828. let radio = Math.abs((num - min) / (max - min)) + index
  829. return radio
  830. },
  831. /** 图表转换为图片 */
  832. convertChart() {
  833. let luckyData = this.chooseReportTemplate
  834. const optionData = luckysheet.getLuckysheetfile()[0]
  835. let {
  836. visibledatacolumn, // 所有行的位置
  837. visibledatarow // 所有列的位置
  838. } = optionData
  839. // 动态图表
  840. if (luckyData.charts && luckyData.charts.length > 0) {
  841. luckyData.charts.forEach((chart, i) => {
  842. let myChart = echarts.init(
  843. document.getElementsByClassName(chart.info.className)[0]
  844. )
  845. let baseData = myChart.getConnectedDataURL({
  846. type: 'png',
  847. pixelRatio: 2,
  848. backgroundColor: '#ffffff'
  849. })
  850. // luckysheet.cancelRangeMerge(chart.info.pos)
  851. luckysheet.insertImage(baseData, {
  852. rowIndex: chart.info.pos[0],
  853. colIndex: chart.info.pos[1],
  854. cellSize: this.getCellSize(optionData, chart.info.pos[0], chart.info.pos[1], luckysheet),
  855. success: function () {
  856. console.log("插入成功")
  857. }
  858. })
  859. })
  860. }
  861. // 静态图表
  862. if (luckyData.data && luckyData.data.length > 0) {
  863. luckyData.data.forEach((data, i) => {
  864. if (data.chart && data.chart.length > 0) {
  865. data.chart.forEach((chart, j) => {
  866. let myChart = echarts.init(
  867. document.getElementById(chart.chart_id)
  868. )
  869. myChart.setOption(chart.chartOptions);
  870. let baseData = myChart.getConnectedDataURL({
  871. type: 'png',
  872. pixelRatio: 2,
  873. backgroundColor: '#ffffff'
  874. })
  875. let col_st = this.getImagePosition(chart.left, visibledatacolumn)
  876. let row_st = this.getImagePosition(chart.top, visibledatarow)
  877. luckysheet.insertImage(baseData, {
  878. rowIndex: parseInt(row_st),
  879. colIndex: parseInt(col_st),
  880. cellSize: {
  881. w: chart.width,
  882. h: chart.height,
  883. },
  884. success: function () {
  885. console.log("插入成功")
  886. }
  887. })
  888. })
  889. }
  890. })
  891. }
  892. },
  893. /** 保存报表信息 */
  894. saveReportInfo() {
  895. this.$prompt('请输入报表名称', '保存', {
  896. confirmButtonText: '确定',
  897. cancelButtonText: '取消',
  898. inputValidator: (val) => {
  899. if (!val) {
  900. return '报表名称不能为空'
  901. }
  902. if (val.length > 20) {
  903. return '报表名称必须在20字以内'
  904. }
  905. }
  906. }).then(({value}) => {
  907. if (!this.chooseReportTemplate) {
  908. this.$message({
  909. message: '请重新选择报表模版',
  910. type: 'warning'
  911. })
  912. return
  913. }
  914. const loading = showLoading(this, '保存中,请稍候···')
  915. this.convertChart()
  916. let data = {
  917. 'reportTableName': value,
  918. 'reportValueFormat': this.reportForm.reportValueFormat,
  919. 'reportTableData': JSON.stringify(this.chooseReportTemplate)
  920. }
  921. saveReport(data).then(res => {
  922. loading.close()
  923. let msg = res.data ? '保存成功!' : '保存失败!'
  924. let msgType = res.data ? 'success' : 'error'
  925. this.$message({
  926. message: msg,
  927. type: msgType
  928. })
  929. this.showMainView = false
  930. this.loadReport()
  931. this.cancelSaveReport()
  932. }).catch((e) => {
  933. loading.close()
  934. })
  935. }).catch((e) => {
  936. console.log(e)
  937. })
  938. },
  939. /** 报表下载 */
  940. downloadReport() {
  941. this.dialogDownloadReportTypeVisible = true
  942. },
  943. /** 报表下载事件 */
  944. downloadReportEvent() {
  945. if (!this.chooseMyReport) {
  946. this.$message({
  947. message: '请选择报表',
  948. type: 'warning'
  949. })
  950. return
  951. }
  952. let reportName = this.chooseMyReport.reportTableName ? this.chooseMyReport.reportTableName : '统计报表'
  953. if (this.downloadType == '1') {
  954. // exportExcel(luckysheet, reportName, ExcelJS).then((res) => {
  955. // console.log("result==>", res)
  956. // // this.$message({
  957. // // message: res,
  958. // // type: 'success'
  959. // // })
  960. // this.dialogDownloadReportTypeVisible = false
  961. // }).catch((err) => {
  962. // })
  963. exportExcel(luckysheet.getAllSheets(), reportName)
  964. }
  965. },
  966. /** 打印操作 */
  967. printExcel() {
  968. const loading = showLoading(this, '请稍候···')
  969. let src = luckysheet.getScreenshot(); // 生成base64图片
  970. let $img = `<img src=${src} style="width: 100%; position: absolute; left: 0px" />`;
  971. document.querySelector("#print-area").style.display = "block";
  972. this.$nextTick(() => {
  973. document.querySelector("#print-html").innerHTML = $img;
  974. setTimeout(() => {
  975. Print({
  976. printable: 'print-html',
  977. type: 'html',
  978. documentTitle: '文档标题',
  979. header: '统计图',
  980. headerStyle: 'font-weight:400;text-align:center;',
  981. style: '@page {margin: 0 10mm};', // 不打印页眉和页脚
  982. honorColor: true, // 是否打印彩色文本
  983. targetStyles: ['*'] // 允许打印所有样式属性
  984. }) // Print.js插件
  985. document.querySelector("#print-area").style.display = "none";
  986. loading.close()
  987. }, 2000)
  988. });
  989. },
  990. /** 自定义选中区域 */
  991. getExcelRowColumn() {
  992. const sheetData = luckysheet.getSheetData();
  993. let objRowColumn = {
  994. row: [null, null], //行
  995. column: [null, null], //列
  996. };
  997. sheetData.forEach((item, index) => {
  998. //行数
  999. item.forEach((it, itemIndex) => {
  1000. console.log(it)
  1001. if (it !== null) {
  1002. if (objRowColumn.row[0] == null) objRowColumn.row[0] = index; // row第一位
  1003. objRowColumn.row[1] = index; //row第二位
  1004. if (objRowColumn.column[0] == null)
  1005. objRowColumn.column[0] = itemIndex; //column第一位
  1006. objRowColumn.column[1] = itemIndex; //column第二位
  1007. }
  1008. });
  1009. });
  1010. return objRowColumn;
  1011. },
  1012. cronNodeEvent(obj) {
  1013. this.cronVal = obj.value
  1014. },
  1015. initCronList() {
  1016. this.cronList = []
  1017. this.cronList.push({ value: '0/2 * * * * ?', label: '表示每2秒钟执行一次任务' })
  1018. this.cronList.push({ value: '0 0/2 * * * ?', label: '表示每2分钟执行一次任务' })
  1019. this.cronList.push({ value: '0 0 0/2 * * ?', label: '表示每2小时执行一次任务' })
  1020. this.cronList.push({ value: '0 15 10 * * ?', label: '表示在每天上午10点15分执行一次任务' })
  1021. this.cronList.push({ value: '0 0 10,14,16 * * ?', label: '表示在每天的上午10点、下午2点、下午4点分别执行一次任务' })
  1022. this.cronList.push({ value: '0 0/30 9-17 * * ?', label: '表示在每天的上午9点到下午5点的范围内每30分钟执行一次任务' })
  1023. this.cronList.push({ value: '0 0 12 ? * WED', label: '表示在每周星期三中午12点执行一次任务' })
  1024. this.cronList.push({ value: '0 0 2 1 * ?', label: '表示在每月的1日的凌晨2点执行一次任务' })
  1025. this.cronList.push({ value: '0 15 10 * * ? 2023', label: '表示在2023年每天上午10点15分执行一次任务' })
  1026. },
  1027. /** 取消保存报表 */
  1028. cancelSaveReport() {
  1029. luckysheet.destroy()
  1030. this.showMainView = false
  1031. this.btnType = ''
  1032. if (this.$refs['reportForm']) this.$refs['reportForm'].resetFields()
  1033. },
  1034. /** 弹出层关闭事件 */
  1035. dialogClose(done) {
  1036. this.cronVal = ''
  1037. this.reportId = null
  1038. this.userGroupInfo = null
  1039. if (typeof (done) === 'function') {
  1040. done()
  1041. } else {
  1042. this.dialogReportTemplateVisible = false
  1043. this.dialogDownloadReportTypeVisible = false
  1044. this.dialogAutoReportVisible = false
  1045. this.dialogUserGroupVisible = false
  1046. }
  1047. }
  1048. }
  1049. }
  1050. </script>
  1051. <style rel="stylesheet/scss" lang="scss">
  1052. .breadcrumb-content {
  1053. padding-bottom: 0;
  1054. }
  1055. .cy-nav-sx {
  1056. float: left !important;
  1057. margin-top: -23px !important;
  1058. margin-left: 120px !important;
  1059. }
  1060. .cy-nav-btn {
  1061. float: right !important;
  1062. margin-right: 20px !important;
  1063. margin-top: -20px !important;
  1064. }
  1065. .cy-main {
  1066. margin: 10px 20px;
  1067. width: calc(100% - 40px);
  1068. height: calc(100% - 100px);
  1069. .cy-btn {
  1070. margin-bottom: 10px;
  1071. }
  1072. .cy-btn .el-link {
  1073. margin-right: 15px;
  1074. }
  1075. .cy-main-left {
  1076. width: 100%;
  1077. height: 100%;
  1078. overflow: auto;
  1079. //float: left;
  1080. padding: 5px 10px;
  1081. //border-right: 1px solid #d4d4d4;
  1082. .cy-list {
  1083. overflow: auto;
  1084. height: 100%;
  1085. height: calc(100% - 40px);
  1086. ul {
  1087. display: flex;
  1088. flex-wrap: wrap;
  1089. padding: 0;
  1090. margin-top: 0;
  1091. li {
  1092. list-style-type: none;
  1093. background: #41aed7;
  1094. width: 23%;
  1095. margin: 10px 1%;
  1096. border-radius: 8px;
  1097. height: 75px;
  1098. padding: 15px;
  1099. color: #ffffff;
  1100. .cy-item-name {
  1101. font-size: 14px;
  1102. text-overflow: ellipsis;
  1103. white-space: nowrap;
  1104. overflow: hidden;
  1105. margin-bottom: 10px;
  1106. cursor: pointer;
  1107. }
  1108. .cy-item-val {
  1109. font-size: 12px;
  1110. }
  1111. }
  1112. }
  1113. }
  1114. }
  1115. .cy-main-right {
  1116. width: 100%;
  1117. height: 100%;
  1118. float: left;
  1119. overflow: hidden;
  1120. }
  1121. }
  1122. .cy-group-tree1 {
  1123. font-size: 14px;
  1124. .custom-tree-node1 {
  1125. height: 28px;
  1126. line-height: 28px;
  1127. }
  1128. }
  1129. .cron-txt {
  1130. color: blue;
  1131. margin-right: 20px;
  1132. cursor: pointer;
  1133. font-weight: bold;
  1134. font-size: 16px;
  1135. }
  1136. .cron-txt-desc {
  1137. }
  1138. </style>