<template> <el-dialog :close-on-click-modal="false" title="表达式" width="60%" :visible.sync="formVisible" :append-to-body="false" class="bs-dialog-wrap bs-el-dialog" > <div class="main-box"> <div class="left-box"> <el-tree ref="tree" :data="treeData" :indent="0" :props="defaultProps" :default-expand-all="true" :highlight-current="true" :expand-on-click-node="false" class="bs-el-tree tree-box" @node-click="handleNodeClick" > <template #default="{ node, data }"> <!-- Check if the node is a top-level node --> <span v-if="node.level === 1" :class="{ 'disabled': node.disabled}" > <i class="el-icon-folder" /> {{ data.label }} </span> <span v-else :class="{ 'disabled': node.disabled}" style="padding-left: 20px" > {{ data.label }} </span> </template> </el-tree> </div> <div class="right-box"> <div class="codemirror-wrap"> <codemirror ref="codemirrorRef" v-model="currentConfig.expression" class="codemirror-box" :options="codemirrorOption" @dragover.prevent /> <el-button class="btn-box" type="primary" @click="executeScript" > 运行脚本 </el-button> </div> <div class="script-content-box"> {{ scriptContent }} </div> </div> </div> <div slot="footer" class="dialog-footer" > <el-button class="bs-el-button-default cancel" @click="cancel" > 取消 </el-button> <el-button type="primary" @click="sure" > 确定 </el-button> </div> </el-dialog> </template> <script> import { codemirror } from 'vue-codemirror' import 'codemirror/mode/javascript/javascript' import 'codemirror/lib/codemirror.css' import 'codemirror/theme/nord.css' import { mapMutations, mapState } from 'vuex' import cloneDeep from 'lodash/cloneDeep' export default { name: 'ExpressionDialog', components: { codemirror }, props: { config: { type: Object, default: () => { } } }, data () { return { scriptContent: '', // 脚本执行的内容 expression: '123', formVisible: false, codemirrorOption: { mode: 'text/javascript', lineNumbers: true, lineWrapping: true, theme: 'nord', extraKey: { Ctrl: 'autocomplete' }, hintOptions: { completeSingle: true } }, defaultProps: { label: 'label', children: 'children' } } }, computed: { ...mapState({ dataset: state => state.bigScreen.dataset, computedDatas: state => state.bigScreen.computedDatas }), // 获取树节点数据 treeData () { const list = [] for (const item in this.dataset) { let children = [] if (this.dataset[item][0]) { const fields = Object.keys(this.dataset[item][0]) children = fields.map((field) => { return { label: field, code: item, value: `dataset.${item}[0].${field}`, disabled: item.includes(this.config.code) } }) } list.push({ label: item.split('_')[0], code: item, value: `dataset.${item}`, disabled: item.includes(this.config.code), children }) } for (const item in this.computedDatas) { list.push({ label: item.split('_')[0], code: item, value: `computedDatas.${item}`, disabled: item.includes(this.config.code) }) } return list }, currentConfig () { return cloneDeep(this.config) } }, watch: {}, created () {}, mounted () {}, methods: { ...mapMutations({ changeChartConfig: 'bigScreen/changeChartConfig' }), init () { this.formVisible = true }, // 运行脚本 executeScript () { // eslint-disable-next-line no-new-func const result = new Function('dataset', 'computedDatas', this.currentConfig.expression) this.scriptContent = result(this.dataset, this.computedDatas) }, // 点击树节点将数据添加到脚本编辑器中 handleNodeClick (node, data, nodeObj) { const str = node.value const code = node.code if (node.disabled) return // 判断编辑器里面是return + 0或者多个空格 还是包含其他表达式,每次添加第一个表达式时不要加‘+’,防止计算错误 const isInit = /^[\w\s]+$/.test(this.currentConfig.expression) const newStr = isInit ? this.currentConfig.expression + str : this.currentConfig.expression + ' + ' + str this.$refs.codemirrorRef.codemirror.setValue(newStr) // 同时将点击的数据存在expressionCodes中 if (this.currentConfig.expressionCodes && Array.isArray(this.currentConfig.expressionCodes)) { this.currentConfig.expressionCodes.push(code) } }, cancel () { this.formVisible = false }, sure () { this.formVisible = false this.changeChartConfig(this.currentConfig) } } } </script> <style scoped lang="scss"> @import '../../assets/style/bsTheme.scss'; .bs-dialog-wrap{ ::v-deep.el-dialog__body{ min-height: 500px; } } .main-box{ width: 100%; height: 500px; display: flex; .left-box{ flex: 1; height: 100%; .tree-box{ height: 100%; overflow-y: auto; } } .right-box{; flex: 3 ; height: 100%; .codemirror-wrap{ height: 50%; position: relative; .btn-box{ position: absolute; right: 10px; bottom: 10px; } } .codemirror-box { height: 100% !important; ::v-deep .CodeMirror { height: 100% !important; font-family: Helvetica, Tahoma; } } .script-content-box{ padding:10px } } } .disabled{ cursor: not-allowed; color: #666666; } </style>