index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. <template>
  2. <el-container class="event-script-container">
  3. <el-aside width="300px" class="event-script-aside">
  4. <el-container>
  5. <el-header height="40px">
  6. <el-button type="text" size="small" icon="el-icon-plus" @click="handleAdd">{{$t('fm.eventscript.config.add')}}</el-button>
  7. </el-header>
  8. <el-main>
  9. <el-scrollbar style="height: 100%;">
  10. <el-menu class="event-script-aside-menu" :default-active="selectKey" @select="handleSelect">
  11. <el-menu-item :index="item.key" v-for="(item, index) in list" :key="item.key" :disabled="!saved || !isFormChange()" @click.native="handleSelect(item.key)">
  12. <div slot="title">
  13. <span class="event-script-menu-i" :class="{'is-vis': item.type == 'rule'}">{{item.type == 'rule' ? 'VIS' : 'JS'}}</span>
  14. <div class="event-script-menu-label">{{item.name}}</div>
  15. <div class="event-script-menu-action" >
  16. <i v-if="!readonlyFunctions.includes(item.name)" class="fm-iconfont icon-icon_clone" @click.stop="handleClone(index)" :title="$t('fm.tooltip.clone')"></i>
  17. <i v-if="!readonlyFunctions.includes(item.name)" class="fm-iconfont icon-trash" @click.stop="handleRemove(index)" :title="$t('fm.tooltip.trash')"></i>
  18. </div>
  19. </div>
  20. </el-menu-item>
  21. </el-menu>
  22. </el-scrollbar>
  23. </el-main>
  24. </el-container>
  25. </el-aside>
  26. <el-main class="event-script-main">
  27. <el-container v-if="selectIndex >= 0">
  28. <el-header height="40px">
  29. <div class="event-script-action">
  30. <el-button type="primary" size="mini" @click="handleSave(true)" v-if="this.eventType">{{$t('fm.eventscript.config.confirm')}}</el-button>
  31. <el-button type="primary" size="mini" @click="handleSave(false)">{{$t('fm.eventscript.config.save')}}</el-button>
  32. <el-button size="mini" @click="handleCancal">{{$t('fm.eventscript.config.cancel')}}</el-button>
  33. </div>
  34. </el-header>
  35. <el-main>
  36. <el-form ref="dataForm" :model="formData" :rules="formRules" :label-width="'120px'" size="small" label-position="left" :key="selectKey" @submit.native.prevent>
  37. <el-form-item :label="$t('fm.eventscript.config.name')" :label-width="$i18n.locale == 'zh-CN' ? '80px' : '125px'" prop="name">
  38. <el-input v-model="formData.name" :disabled="readonlyFunctions.includes(formData.name)"></el-input>
  39. </el-form-item>
  40. <el-form-item label-width="0" v-if="formData.type">
  41. <el-radio-group v-model="formData.type">
  42. <el-radio-button label="rule">{{$t('fm.eventscript.config.rules')}}</el-radio-button>
  43. <el-radio-button label="js">{{$t('fm.eventscript.config.js')}}</el-radio-button>
  44. </el-radio-group>
  45. </el-form-item>
  46. <el-form-item label-width="0" prop="func" style="margin-bottom: 0px;" v-if="!formData.type || formData.type == 'js'">
  47. <div class="code-line">Function () {</div>
  48. <code-editor v-model="formData.func" mode="javascript" :height="formData.type ? '350px' : '400px'"></code-editor>
  49. <div class="code-line">}</div>
  50. </el-form-item>
  51. <template v-if="formData.type == 'rule'">
  52. <event-rule v-model="formData.rules"></event-rule>
  53. </template>
  54. </el-form>
  55. </el-main>
  56. </el-container>
  57. </el-main>
  58. </el-container>
  59. </template>
  60. <script>
  61. import ArrayDynamic from '../DataSource/arrayDynamic'
  62. import CodeEditor from '../CodeEditor'
  63. import axios from 'axios'
  64. import request from '../../util/request'
  65. import { Message, MessageBox } from 'element-ui'
  66. import EventRule from './rule.vue'
  67. import _ from 'lodash'
  68. import { ruleToFunction } from '../../util/rule-funcs.js'
  69. export default {
  70. components: {
  71. ArrayDynamic,
  72. CodeEditor,
  73. EventRule
  74. },
  75. props: {
  76. value: {
  77. type: Array,
  78. default: () => []
  79. }
  80. },
  81. data () {
  82. return {
  83. formData: {
  84. key: '',
  85. name: '',
  86. func: '',
  87. type: 'rule'
  88. },
  89. tmpData: {},
  90. formRules: {
  91. name: [
  92. {required: true, message: ' '},
  93. { validator: (rule, value, callback) => {
  94. let currentItem = this.historyList.find(item => item.name ==value)
  95. if (currentItem && currentItem.key != this.selectKey) {
  96. callback(new Error(this.$t('fm.eventscript.message.repeat')))
  97. } else {
  98. callback()
  99. }
  100. }}
  101. ]
  102. },
  103. list: [...this.value],
  104. selectIndex: -1,
  105. selectKey: '',
  106. historyList: [...this.value],
  107. saved: true,
  108. eventType: '',
  109. readonlyFunctions: ['mounted', 'refresh', 'onFormChange']
  110. }
  111. },
  112. provide () {
  113. return {
  114. 'getEventsArray': this.getEventsArray
  115. }
  116. },
  117. methods: {
  118. handleSave (confirm) {
  119. this.$refs.dataForm.validate((valid) => {
  120. if (valid) {
  121. try {
  122. if (this.formData.type === 'rule') {
  123. new Function(`return (async () => {${ruleToFunction(this.formData.rules)}})()`)
  124. } else {
  125. new Function(this.formData.func)
  126. }
  127. this.$set(this.list, this.list.findIndex(item => item.key === this.selectKey), this.formData)
  128. this.historyList = [...this.list]
  129. this.saved = true
  130. this.tmpData = _.cloneDeep(this.formData)
  131. this.$emit('input', this.historyList)
  132. if (confirm) {
  133. this.$emit('on-confirm-event', {...this.formData, type: this.eventType})
  134. } else {
  135. Message.success(this.$t('fm.eventscript.message.saveSuccess'))
  136. }
  137. } catch (error) {
  138. Message.error(this.$t('fm.message.eventSaveError') + error.message)
  139. }
  140. }
  141. })
  142. },
  143. handleAdd () {
  144. if (!this.saved || !this.isFormChange()) {
  145. Message.warning(this.$t('fm.eventscript.message.saveError'))
  146. return;
  147. }
  148. let key = Math.random().toString(36).slice(-8)
  149. this.list.push({
  150. key: key,
  151. name: 'func_'+key,
  152. func: '',
  153. type: 'rule'
  154. })
  155. this.selectKey = key
  156. this.saved = false
  157. },
  158. loadNewFunction (name) {
  159. let key = Math.random().toString(36).slice(-8)
  160. this.list.push({
  161. key: key,
  162. name: name + '_' + key,
  163. func: '',
  164. type: 'rule'
  165. })
  166. this.selectKey = key
  167. this.saved = false
  168. this.eventType = name
  169. },
  170. loadForm () {
  171. let currentData = this.list[this.selectIndex]
  172. this.formData = {
  173. ...currentData,
  174. type: currentData.type || 'js'
  175. }
  176. this.tmpData = _.cloneDeep(this.formData)
  177. },
  178. handleSelect (key) {
  179. if (key === this.selectKey) {
  180. return
  181. }
  182. if (!this.saved || !this.isFormChange()) {
  183. Message.warning(this.$t('fm.eventscript.message.saveError'))
  184. return;
  185. }
  186. this.selectKey = key
  187. },
  188. isFormChange () {
  189. if (this.selectKey && Object.keys(this.tmpData).length) {
  190. return _.isEqual(this.formData, this.tmpData)
  191. } else {
  192. return true
  193. }
  194. },
  195. loadFunction (key, name) {
  196. this.selectKey = key
  197. if (name) {
  198. this.eventType = name
  199. }
  200. },
  201. handleCancal () {
  202. this.selectKey = ''
  203. this.list = [...this.historyList]
  204. this.saved = true
  205. },
  206. handleClone (index) {
  207. if (!this.saved || !this.isFormChange()) {
  208. Message.warning(this.$t('fm.eventscript.message.saveError'))
  209. return;
  210. }
  211. let currentData = this.list[index]
  212. let key = Math.random().toString(36).slice(-8)
  213. let cloneData = {
  214. ...currentData,
  215. key,
  216. name: currentData.name + '_copy'
  217. }
  218. this.list.push(cloneData)
  219. this.selectKey = key
  220. this.saved = false
  221. },
  222. handleRemove (index) {
  223. let currentData = this.list[index]
  224. MessageBox.confirm(`${this.$t('fm.eventscript.message.confirmRemove')} [${currentData.name}] ?`, '', {
  225. type: 'warning'
  226. }).then(() => {
  227. if (currentData.key === this.selectKey) {
  228. this.selectKey = ''
  229. this.saved = true
  230. }
  231. this.list.splice(index, 1)
  232. if (index < this.historyList.length) {
  233. this.historyList.splice(index, 1)
  234. }
  235. this.$emit('input', this.historyList)
  236. }).catch(() => {})
  237. },
  238. getEventsArray () {
  239. return this.list.map(item => ({key: item.key, name: item.name, type: item.type})).filter(item => item.key != 'mounted' && item.key != 'refresh' && item.key != this.selectKey)
  240. }
  241. },
  242. watch: {
  243. selectKey (val) {
  244. if (val) {
  245. this.selectIndex = this.list.findIndex(item => item.key === val)
  246. if (this.selectIndex >= 0) {
  247. this.loadForm()
  248. }
  249. } else {
  250. this.selectIndex = -1
  251. }
  252. }
  253. }
  254. }
  255. </script>
  256. <style lang="scss">
  257. .event-script-container{
  258. .event-script-main{
  259. padding: 0;
  260. >.el-container{
  261. display: flex;
  262. height: 100%;
  263. >.el-header{
  264. padding: 5px;
  265. border-bottom: 1px solid #eee;
  266. background: #F2F6FC;
  267. }
  268. }
  269. .event-script-action{
  270. text-align: right;
  271. }
  272. .code-line{
  273. font-size: 14px;
  274. color: blue;
  275. font-weight: 500;
  276. }
  277. .code-desc{
  278. margin-left: 2px;
  279. font-size: 12px;
  280. color: #999;
  281. }
  282. .el-collapse-item{
  283. border: 1px solid #EBEEF5;
  284. }
  285. .el-collapse-item__header{
  286. background: #EBEEF5;
  287. // border: 0;
  288. height: 36px;
  289. line-height: 36px;
  290. padding: 5px;
  291. }
  292. .el-collapse-item__wrap{
  293. border: 0;
  294. .el-collapse-item__content{
  295. padding: 5px;
  296. }
  297. }
  298. }
  299. .event-script-aside{
  300. border-right: 1px solid #eee;
  301. >.el-container{
  302. display: flex;
  303. height: 100%;
  304. >.el-main{
  305. margin: 0;
  306. padding: 0;
  307. }
  308. >.el-header{
  309. padding: 5px;
  310. border-bottom: 1px solid #eee;
  311. background: #F2F6FC;
  312. }
  313. }
  314. .event-script-aside-menu{
  315. margin: 10px;
  316. border-right: 0;
  317. .el-menu-item{
  318. border: 1px solid #DCDFE6;
  319. background: #fff;
  320. border-radius: 3px;
  321. padding: 10px !important;
  322. height: auto;
  323. line-height: 1;
  324. cursor: default;
  325. position: relative;
  326. &.is-disabled{
  327. opacity: 1;
  328. cursor: default;
  329. background: #fff;
  330. }
  331. &.is-active{
  332. background: #E4E7ED;
  333. color: #303133;
  334. }
  335. &.is-active.is-disabled{
  336. background: #E4E7ED !important;
  337. color: #303133;
  338. }
  339. +.el-menu-item{
  340. margin-top: 6px;
  341. }
  342. }
  343. .event-script-menu-label{
  344. overflow:hidden;
  345. text-overflow:ellipsis;
  346. white-space:nowrap;
  347. display: inline-block;
  348. width: 190px;
  349. margin-left: 30px;
  350. }
  351. .event-script-menu-i{
  352. position: absolute;
  353. font-size: 12px;
  354. left: 5px;
  355. top: 13px;
  356. width: 30px;
  357. text-align: left;
  358. padding-left: 5px;
  359. color: #67C23A;
  360. font-style: italic;
  361. &.is-vis{
  362. color: #e6a23c;
  363. }
  364. }
  365. .event-script-menu-action{
  366. display: inline-block;
  367. padding-right: 10px;
  368. color: #666;
  369. font-weight: 600;
  370. >i{
  371. cursor: pointer;
  372. margin-left: 5px;
  373. }
  374. }
  375. }
  376. }
  377. }
  378. </style>