chartContextMenu.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. import { mapMutations, mapState } from 'vuex'
  2. import cloneDeep from 'lodash/cloneDeep'
  3. import { toJpeg, toPng } from 'html-to-image'
  4. import isEmpty from 'lodash/isEmpty'
  5. import { randomString } from 'data-room-ui/js/utils'
  6. import Contextmenu from 'vue-contextmenujs'
  7. import Vue from 'vue'
  8. Vue.use(Contextmenu)
  9. export default {
  10. computed: {
  11. ...mapState({
  12. activeCode: state => state.bigScreen.activeCode,
  13. activeCodes: state => state.bigScreen.activeCodes,
  14. hoverCode: state => state.bigScreen.hoverCode,
  15. activeItemConfig: state => state.bigScreen.activeItemConfig,
  16. chartList: state => state.bigScreen.pageInfo.chartList,
  17. presetLine: state => state.bigScreen.presetLine
  18. })
  19. },
  20. data () {
  21. return {}
  22. },
  23. mounted () {
  24. },
  25. methods: {
  26. ...mapMutations('bigScreen', ['changeHoverCode', 'changeActiveCode', 'changeChartConfig', 'addItem', 'delItem', 'resetPresetLine', 'changeLayout', 'changeZIndex', 'changeLocked', 'saveTimeLine', 'copyCharts', 'pasteCharts', 'clearActiveCodes']), // 改变hover的组件
  27. changeHover (code) {
  28. this.changeHoverCode(code)
  29. }, // 改变激活的组件
  30. changeActive (code) {
  31. this.changeActiveCode(code)
  32. }, // 打开右侧面板
  33. openRightPanel (config) {
  34. this.changeActiveCode(config.code)
  35. this.$emit('openRightPanel', config)
  36. }, // 查看数据
  37. dataView (config) {
  38. this.changeActiveCode(config.code)
  39. this.$emit('openDataViewDialog', config)
  40. }, // 复制组件
  41. copyItem (config) {
  42. const newConfig = cloneDeep(config)
  43. newConfig.code = randomString(8)
  44. newConfig.title = newConfig.title + '_副本'
  45. // 区分是从左侧添加还是复制的组件
  46. newConfig.isCopy = true
  47. newConfig.x = config.x + 20
  48. newConfig.y = config.y + 20
  49. if (config.group) {
  50. newConfig.group = 'copy_' + config.group
  51. }
  52. this.addItem(newConfig)
  53. }, // 删除单个组件
  54. deleteItem (config) {
  55. this.$confirm('确定删除该组件吗?', '提示', {
  56. confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', customClass: 'bs-el-message-box'
  57. }).then(() => {
  58. this.delItem(config.code)
  59. })
  60. }, // 批量删除组合元素
  61. deleteGroupItem (config) {
  62. this.$confirm('确定批量删除选中的组件吗?', '提示', {
  63. confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', customClass: 'bs-el-message-box'
  64. }).then(() => {
  65. // 找到和本组件group相同的组件 删除
  66. const codes = this.chartList.filter(_chart => _chart.group === config.group && config.group).map(_chart => _chart.code)
  67. if (!isEmpty(codes)) {
  68. this.delItem(codes)
  69. } else {
  70. this.delItem(config.code)
  71. }
  72. })
  73. }, // 获取组件的坐标字符串,取整 (100, 100)
  74. getPoint ({ x, y }) {
  75. return `(${Math.round(x)}, ${Math.round(y)})`
  76. }, // 组合/取消组合图表
  77. groupChart (chart) {
  78. if (!chart.group || chart.group === 'tempGroup') {
  79. // 添加组合
  80. // eslint-disable-next-line no-unused-expressions
  81. this.activeCodes?.forEach(code => {
  82. const config = this.chartList.find(item => item.code === code)
  83. this.changeChartConfig({
  84. ...config, group: `group_${chart.code}`
  85. })
  86. })
  87. this.saveTimeLine('组合图表')
  88. } else {
  89. // 取消组合
  90. this.clearActiveCodes()
  91. // 找到和本组件group相同的组件 取消group
  92. this.chartList.forEach(_chart => {
  93. if (_chart.group === chart.group) {
  94. this.changeChartConfig({
  95. ..._chart, group: ''
  96. })
  97. }
  98. })
  99. this.saveTimeLine('取消组合图表')
  100. }
  101. }, // 生成图片
  102. generateImage (chart) {
  103. let componentDom = document.querySelector(`#${chart.code} .render-item-wrap`)
  104. if (this.isPreview) {
  105. componentDom = document.querySelector(`#${chart.code}`)
  106. }
  107. toPng(componentDom)
  108. .then((dataUrl) => {
  109. const link = document.createElement('a')
  110. link.download = `${chart.title}.png`
  111. link.href = dataUrl
  112. link.click()
  113. link.addEventListener('click', () => {
  114. link.remove()
  115. })
  116. }).catch((error) => {
  117. if (error.type === 'error') {
  118. // 判断的error.currentTarget是img标签,如果是的,就弹出消息说是图片跨域
  119. if (error.currentTarget.tagName.toLowerCase() === 'img') {
  120. // 确认框
  121. this.$confirm('图片资源跨域导致使用toDataURL API生成图片失败,请将图片上传到资源库,然后在组件中使用资源库中的图片资源,确保没有跨域问题。', '提示', {
  122. confirmButtonText: '确定',
  123. showCancelButton: false,
  124. type: 'warning',
  125. customClass: 'bs-el-message-box'
  126. }).then(() => { }).catch(() => { })
  127. }
  128. } else {
  129. this.$message.warning('出现未知错误,请重试')
  130. }
  131. })
  132. }, // 右键菜单
  133. onContextmenu (event, chart) {
  134. const isHidden = !chart?.option?.displayOption?.dataAllocation?.enable
  135. event.preventDefault()
  136. if (this.isPreview) {
  137. this.$contextmenu({
  138. items: [{
  139. label: '查看数据',
  140. icon: 'el-icon-view',
  141. hidden: isHidden,
  142. onClick: () => {
  143. this.dataView(chart)
  144. }
  145. },
  146. {
  147. label: '生成图片',
  148. icon: 'el-icon-download',
  149. hidden: isHidden,
  150. onClick: () => {
  151. this.generateImage(chart)
  152. }
  153. }],
  154. event, // 鼠标事件信息
  155. customClass: 'bs-context-menu-class', // 自定义菜单 class
  156. zIndex: 999, // 菜单样式 z-index
  157. minWidth: 150 // 主菜单最小宽度
  158. })
  159. } else {
  160. this.$contextmenu({
  161. items: [{
  162. label: '配置',
  163. icon: 'el-icon-setting',
  164. onClick: () => {
  165. this.openRightPanel(chart)
  166. }
  167. }, {
  168. label: '删除',
  169. icon: 'el-icon-delete',
  170. onClick: () => {
  171. this.deleteItem(chart)
  172. }
  173. }, {
  174. label: '批量删除',
  175. icon: 'el-icon-delete',
  176. onClick: () => {
  177. this.deleteGroupItem(chart)
  178. }
  179. }, {
  180. label: '复制',
  181. icon: 'el-icon-copy-document',
  182. onClick: () => {
  183. this.copyItem(chart)
  184. }
  185. }, {
  186. label: '组合复制',
  187. icon: 'el-icon-copy-document',
  188. onClick: () => {
  189. this.copyCharts()
  190. this.pasteCharts()
  191. }
  192. }, {
  193. label: '置于顶层',
  194. icon: 'el-icon-arrow-up',
  195. onClick: () => {
  196. let chartList = cloneDeep(this.chartList)
  197. // 将当前图表置底
  198. chartList = chartList.filter(item => item.code !== chart.code)
  199. chartList.unshift(chart)
  200. this.changeLayout(chartList)
  201. this.changeZIndex(chartList)
  202. }
  203. }, {
  204. label: '置于底层',
  205. icon: 'el-icon-arrow-down',
  206. onClick: () => {
  207. let chartList = cloneDeep(this.chartList)
  208. // 将当前图表置顶
  209. chartList = chartList.filter(item => item.code !== chart.code)
  210. chartList.push(chart)
  211. this.changeLayout(chartList)
  212. this.changeZIndex(chartList)
  213. }
  214. }, {
  215. label: chart.locked ? '解锁' : '锁定',
  216. icon: chart.locked ? 'el-icon-unlock' : 'el-icon-lock',
  217. onClick: () => {
  218. this.changeLocked(chart)
  219. }
  220. }, {
  221. label: (chart.group && chart.group !== 'tempGroup') ? '取消组合' : '组合',
  222. icon: (chart.group && chart.group !== 'tempGroup') ? 'iconfont-bigscreen icon-quxiaoguanlian' : 'iconfont-bigscreen icon-zuhe',
  223. onClick: () => {
  224. this.groupChart(chart)
  225. }
  226. }, {
  227. label: '查看数据',
  228. icon: 'el-icon-view',
  229. hidden: isHidden,
  230. onClick: () => {
  231. this.dataView(chart)
  232. }
  233. }, {
  234. label: '生成图片',
  235. icon: 'el-icon-download',
  236. onClick: () => {
  237. this.generateImage(chart)
  238. }
  239. }],
  240. event, // 鼠标事件信息
  241. customClass: 'bs-context-menu-class', // 自定义菜单 class
  242. zIndex: 999, // 菜单样式 z-index
  243. minWidth: 150 // 主菜单最小宽度
  244. })
  245. }
  246. return false
  247. }
  248. }
  249. }