rule.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. <template>
  2. <div class="fm-rules-config-container">
  3. <draggable
  4. :list="ruleList"
  5. v-bind="{group:'peo2', ghostClass: 'ghost11',animation: 200, handle: '.drag-widget'}"
  6. :no-transition-on-drag="true"
  7. item-key="key"
  8. class="widget-form-list"
  9. >
  10. <template #item="{element, index}">
  11. <el-card class="rule-card" shadow="never" size="small">
  12. <template #header>
  13. <div class="card-header">
  14. <el-tag>{{$t('fm.rules.actions.'+element.action)}}</el-tag>
  15. <div>
  16. <el-popconfirm width="200" :title="$t('fm.rules.message.confirmDelete')" @confirm="handleActonRemove(index)">
  17. <template #reference>
  18. <div class="widget-view-delete widget-view-action">
  19. <i class="fm-iconfont icon-delete"></i>
  20. </div>
  21. </template>
  22. </el-popconfirm>
  23. <div class="widget-view-drag widget-view-action">
  24. <i class="fm-iconfont icon-icon_bars drag-widget"></i>
  25. </div>
  26. </div>
  27. </div>
  28. </template>
  29. <el-form label-width="100px" style="margin-right: 20px;" v-model="element.options" class="rule-card-form" >
  30. <!-- {{element}} -->
  31. <el-form-item :label="$t('fm.rules.label.field')" v-if="Object.keys(element.options).includes('fields')">
  32. <FieldsSelect :defaultExpand="true" :multiple="true" v-model="element.options.fields" :action="element.action"></FieldsSelect>
  33. </el-form-item>
  34. <el-form-item :label="$t('fm.rules.label.field')" v-if="Object.keys(element.options).includes('field')">
  35. <FieldsSelect :defaultExpand="element.action != 'openDialog' && element.action != 'closeDialog'" :multiple="false" v-model="element.options.field" :action="element.action" @on-data-change="handleFieldDataChange($event, element)"></FieldsSelect>
  36. </el-form-item>
  37. <el-form-item :label="$t('fm.rules.label.functionName')" v-if="Object.keys(element.options).includes('functionName')">
  38. <events-select v-model="element.options.functionName"></events-select>
  39. </el-form-item>
  40. <el-form-item :label="$t('fm.rules.label.functionParams')" v-if="element.options.functionName && !['mounted', 'refresh'].includes(element.options.functionName) && Object.keys(element.options).includes('functionParams')">
  41. <el-input type="textarea" v-model="element.options.functionParams" :autosize="true"></el-input>
  42. </el-form-item>
  43. <el-form-item :label="$t('fm.rules.label.datasource')" v-if="Object.keys(element.options).includes('dataSource') && element.action == 'sendRequest'">
  44. <data-source-select v-model="element.options.dataSource"></data-source-select>
  45. </el-form-item>
  46. <el-form-item :label="$t('fm.rules.label.datasourceArgs')"
  47. v-if="element.options.dataSource && element.options.dataSource.args && Object.keys(element.options.dataSource.args).length"
  48. >
  49. <el-switch v-model="element.options.isArgs" v-if="element.action !== 'sendRequest'"></el-switch>
  50. <template v-if="Object.keys(element.options.dataSource.args).length && (element.options.isArgs || element.action == 'sendRequest')">
  51. <div v-for="(argKey) in Object.keys(element.options.dataSource.args)" :key="argKey" class="values-item">
  52. <el-input :value="argKey" readonly style="width: 200px;" :title="argKey" />
  53. <div>&nbsp;=&nbsp;</div>
  54. <el-select @change="handleTypeChange($event, element.options.dataSource.args, argKey)" v-model="element.options.valueTypes[argKey]" :placeholder="$t('fm.rules.label.string')" style="width: 150px;">
  55. <el-option :label="$t('fm.rules.label.string')" value="string" />
  56. <el-option :label="$t('fm.rules.label.number')" value="number" />
  57. <el-option :label="$t('fm.rules.label.boolean')" value="boolean" />
  58. <el-option :label="$t('fm.rules.label.fx')" value="fx" />
  59. </el-select>
  60. <div class="values-value">
  61. <el-input v-if="!element.options.valueTypes[argKey] || element.options.valueTypes[argKey] == 'string'" v-model="element.options.dataSource.args[argKey]" ></el-input>
  62. <el-input v-if="element.options.valueTypes[argKey] == 'number'" type="number" v-model.number="element.options.dataSource.args[argKey]" ></el-input>
  63. <el-switch v-if="element.options.valueTypes[argKey] == 'boolean'" :active-value="'true'" :inactive-value="'false'" v-model="element.options.dataSource.args[argKey]" style="margin-left: 5px;"></el-switch>
  64. <el-input :class="{'is-fx': element.options.valueTypes[argKey] == 'fx'}" v-if="element.options.valueTypes[argKey] == 'fx'" readonly v-model="element.options.dataSource.args[argKey]" :placeholder="$t('fm.rules.message.editFx')" @click="handleOpenFx(element.options.dataSource.args[argKey], index, argKey, 'dataSource')"></el-input>
  65. </div>
  66. </div>
  67. </template>
  68. </el-form-item>
  69. <el-form-item :label="$t('fm.rules.label.responseVariable')" v-if="element.options.dataSource && Object.keys(element.options).includes('responseVariable')">
  70. <el-input v-model="element.options.responseVariable" clearable></el-input>
  71. </el-form-item>
  72. <el-form-item :label="$t('fm.rules.label.localVariable')" v-if="element.options.field && Object.keys(element.options).includes('localVariable')">
  73. <el-input v-model="element.options.localVariable" clearable></el-input>
  74. </el-form-item>
  75. <el-form-item :label-width="$i18n.locale == 'zh-cn' ? '100px' : '120px'" :label="$t('fm.rules.label.enabled') + '/' + $t('fm.rules.label.disabled')" v-if="Object.keys(element.options).includes('disabled')">
  76. <el-radio-group v-model="element.options.disabled">
  77. <el-radio :label="false" :value="false">{{$t('fm.rules.label.enabled')}}</el-radio>
  78. <el-radio :label="true" :value="true">{{$t('fm.rules.label.disabled')}}</el-radio>
  79. </el-radio-group>
  80. </el-form-item>
  81. <el-form-item :label="$t('fm.rules.label.values')" v-if="Object.keys(element.options).includes('values') && element.options.fields.length">
  82. <div v-for="(field) in element.options.fields" :key="field" class="values-item">
  83. <el-input :value="field" readonly style="width: 200px; direction: rtl;" :title="field" />
  84. <div>&nbsp;=&nbsp;</div>
  85. <el-select @change="handleTypeChange($event, element.options.values, field)" v-model="element.options.valueTypes[field]" :placeholder="$t('fm.rules.label.string')" style="width: 150px;">
  86. <el-option :label="$t('fm.rules.label.string')" value="string" />
  87. <el-option :label="$t('fm.rules.label.number')" value="number" />
  88. <el-option :label="$t('fm.rules.label.boolean')" value="boolean" />
  89. <el-option :label="$t('fm.rules.label.fx')" value="fx" />
  90. </el-select>
  91. <div class="values-value">
  92. <el-input v-if="!element.options.valueTypes[field] || element.options.valueTypes[field] == 'string'" v-model="element.options.values[field]" ></el-input>
  93. <el-input v-if="element.options.valueTypes[field] == 'number'" type="number" v-model.number="element.options.values[field]" ></el-input>
  94. <el-switch v-if="element.options.valueTypes[field] == 'boolean'" v-model="element.options.values[field]" style="margin-left: 5px;"></el-switch>
  95. <el-input :class="{'is-fx': element.options.valueTypes[field] == 'fx'}" v-if="element.options.valueTypes[field] == 'fx'" readonly v-model="element.options.values[field]" :placeholder="$t('fm.rules.message.editFx')" @click="handleOpenFx(element.options.values[field], index, field)"></el-input>
  96. </div>
  97. </div>
  98. </el-form-item>
  99. <el-form-item :label="$t('fm.rules.label.validateFail')" v-if="Object.keys(element.options).includes('failSuspend')">
  100. <el-radio-group v-model="element.options.failSuspend">
  101. <el-radio :label="false" :value="false">{{$t('fm.rules.label.validateFailContinue')}}</el-radio>
  102. <el-radio :label="true" :value="true">{{$t('fm.rules.label.validateFailSuspend')}}</el-radio>
  103. </el-radio-group>
  104. </el-form-item>
  105. <el-form-item :label="$t('fm.rules.label.condition')" v-if="Object.keys(element.options).includes('condition')">
  106. <el-switch v-model="element.options.isCondition"></el-switch>
  107. <el-input
  108. v-model="element.options.condition"
  109. :placeholder="$t('fm.rules.message.condition')"
  110. class="input-with-select"
  111. v-if="element.options.isCondition"
  112. readonly
  113. @click="handleOpenCondition(element.options.condition, index)"
  114. >
  115. <template #append>
  116. <el-button size="small" @click="handleOpenCondition(element.options.condition, index)"><i class="fm-iconfont icon-editor-formula" style="font-size: 13px;"></i></el-button>
  117. </template>
  118. </el-input>
  119. </el-form-item>
  120. <el-form-item label-width="0" style="margin-bottom: 0px;" v-if="Object.keys(element.options).includes('func')">
  121. <div class="code-line">Function () {</div>
  122. <code-editor v-model="element.options.func" mode="javascript" :height="'200px'"></code-editor>
  123. <div class="code-line">}</div>
  124. </el-form-item>
  125. </el-form>
  126. </el-card>
  127. </template>
  128. </draggable>
  129. <el-dropdown @command="handleActionCommand" trigger="click" class="fm-add-rules-wrapper" max-height="400px">
  130. <div class="fm-add-rules-button">{{$t('fm.rules.actions.add')}}<i class="fm-iconfont icon-plus" style="font-size: 12px; margin: 5px;"></i></div>
  131. <template #dropdown>
  132. <el-dropdown-menu>
  133. <el-dropdown-item v-for="item in dialogActions" :key="item.action" :command="item">
  134. <div>{{$t('fm.rules.actions.'+item.action)}}</div>
  135. </el-dropdown-item>
  136. <el-dropdown-item v-for="(item, index) in formActions" :key="item.action" :command="item" :divided="index == 0">
  137. <div>{{$t('fm.rules.actions.'+item.action)}}</div>
  138. </el-dropdown-item>
  139. <el-dropdown-item v-for="(item, index) in requestActions" :key="item.action" :command="item" :divided="index == 0">
  140. <div>{{$t('fm.rules.actions.'+item.action)}}</div>
  141. </el-dropdown-item>
  142. <el-dropdown-item v-for="(item, index) in otherActions" :key="item.action" :command="item" :divided="index == 0">
  143. <div>{{$t('fm.rules.actions.'+item.action)}}</div>
  144. </el-dropdown-item>
  145. </el-dropdown-menu>
  146. </template>
  147. </el-dropdown>
  148. </div>
  149. <FormulaDialog ref="formulaDialog" @dialog-confirm="handleFormulaConfirm"></FormulaDialog>
  150. </template>
  151. <script>
  152. import Draggable from 'vuedraggable/src/vuedraggable'
  153. import FieldsSelect from './fieldsSelect.vue'
  154. import DataSourceSelect from './dataSourceSelect.vue'
  155. import EventsSelect from './eventSelect.vue'
  156. import { formActions, requestActions, dialogActions, otherActions } from './actions.js'
  157. import _ from 'lodash'
  158. import FormulaDialog from '../FormulaPanel/dialog.vue'
  159. import CodeEditor from '../CodeEditor/index.vue'
  160. export default {
  161. components: {
  162. Draggable,
  163. FieldsSelect,
  164. DataSourceSelect,
  165. EventsSelect,
  166. FormulaDialog,
  167. CodeEditor
  168. },
  169. props: ['modelValue'],
  170. emits: ['update:modelValue'],
  171. data () {
  172. return {
  173. ruleList: this.modelValue || [],
  174. select: '',
  175. formActions: formActions,
  176. requestActions: requestActions,
  177. dialogActions: dialogActions,
  178. otherActions: otherActions,
  179. curIndex: -1,
  180. curOption: '',
  181. curField: ''
  182. }
  183. },
  184. provide () {
  185. return {
  186. 'getResponseVariables': this.getResponseVariables,
  187. 'getLocalVariables': this.getLocalVariables
  188. }
  189. },
  190. methods: {
  191. handleOpenCondition (condition, index) {
  192. this.$refs.formulaDialog.open(condition)
  193. this.curIndex = index
  194. this.curOption = 'condition'
  195. },
  196. handleOpenFx (fx, index, field, option = 'values') {
  197. this.$refs.formulaDialog.open(fx)
  198. this.curIndex = index
  199. this.curOption = option
  200. this.curField = field
  201. },
  202. handleFormulaConfirm (val) {
  203. if (this.curIndex >= 0 && this.curOption == 'condition') {
  204. this.ruleList[this.curIndex].options[this.curOption] = val
  205. }
  206. if (this.curIndex >= 0 && this.curOption == 'values') {
  207. this.ruleList[this.curIndex].options[this.curOption][this.curField] = val
  208. }
  209. if (this.curIndex >= 0 && this.curOption == 'dataSource') {
  210. this.ruleList[this.curIndex].options.dataSource['args'][this.curField] = val
  211. }
  212. },
  213. handleActionCommand (command) {
  214. let key = Math.random().toString(36).slice(-8)
  215. this.ruleList.push({
  216. key,
  217. ..._.cloneDeep(command)
  218. })
  219. },
  220. handleActonRemove (index) {
  221. this.ruleList.splice(index, 1)
  222. },
  223. handleFieldDataChange (fieldData, element) {
  224. if (fieldData) {
  225. if (element.action == 'refreshDynamicValue') {
  226. element.options.dataSource = fieldData.options.dynamicValueType == 'datasource' ? {
  227. args: typeof fieldData.options.dynamicValueArgs == 'string' ? JSON.parse(fieldData.options.dynamicValueArgs) : fieldData.options.dynamicValueArgs
  228. } : {args: {}}
  229. } else {
  230. element.options.dataSource = fieldData.options.remoteType == 'datasource' ? {
  231. args: typeof fieldData.options.remoteArgs == 'string' ? JSON.parse(fieldData.options.remoteArgs) : fieldData.options.remoteArgs
  232. } : {args: {}}
  233. }
  234. element.options.dataSource.args && Object.keys(element.options.dataSource.args).forEach(key => {
  235. element.options.dataSource.args[key] = ''
  236. })
  237. } else {
  238. element.options.dataSource = {}
  239. }
  240. },
  241. getResponseVariables () {
  242. let vars = []
  243. this.ruleList.forEach(item => {
  244. if (item.options.responseVariable && !vars.includes(item.options.responseVariable)) {
  245. vars.push(item.options.responseVariable)
  246. }
  247. })
  248. return vars
  249. },
  250. getLocalVariables () {
  251. let vars = []
  252. this.ruleList.forEach(item => {
  253. if (item.options.localVariable && !vars.includes(item.options.localVariable)) {
  254. vars.push(item.options.localVariable)
  255. }
  256. })
  257. return vars
  258. },
  259. handleTypeChange (val, target, field) {
  260. switch (val) {
  261. case 'string':
  262. case 'fx':
  263. target[field] = String(target[field] ?? '')
  264. break
  265. case 'boolean':
  266. target[field] = Boolean(target[field] ?? false)
  267. break
  268. case 'number':
  269. target[field] = Number(target[field] ?? 0)
  270. break
  271. }
  272. }
  273. },
  274. watch: {
  275. modelValue (val) {
  276. this.ruleList = val
  277. },
  278. ruleList: {
  279. deep: true,
  280. handler (val) {
  281. this.$emit('update:modelValue', val)
  282. }
  283. }
  284. }
  285. }
  286. </script>
  287. <style lang="scss">
  288. .fm-rules-config-container{
  289. .rule-card{
  290. +.rule-card{
  291. margin-top: 10px;
  292. }
  293. &.ghost11{
  294. border: 2px solid var(--fm-drag-color);
  295. outline-width: 0;
  296. }
  297. .el-card__header{
  298. padding: 5px;
  299. .card-header{
  300. display: flex;
  301. justify-content: space-between;
  302. align-items: center;
  303. .el-tag__content{
  304. font-size: 13px;
  305. }
  306. }
  307. }
  308. .el-card__body{
  309. padding: 10px 10px 0 10px;
  310. }
  311. .widget-view-action{
  312. display: inline-block;
  313. margin-left: 10px;
  314. }
  315. .widget-view-drag{
  316. cursor: move;
  317. }
  318. .widget-view-delete{
  319. cursor: pointer;
  320. }
  321. .el-form-item__label,.el-radio__label{
  322. font-size: 13px;
  323. }
  324. input{
  325. font-size: 13px;
  326. }
  327. .rule-card-form{
  328. .values-item{
  329. display: flex;
  330. justify-content: flex-start;
  331. width: 100%;
  332. align-items: flex-end;
  333. &+.values-item{
  334. margin-top: 5px;
  335. }
  336. .values-value{
  337. display: inline-block;
  338. width: 400px;
  339. }
  340. .is-fx{
  341. .el-input__wrapper{
  342. line-height: 24px;
  343. min-height: 32px;
  344. &::before{
  345. content: '{{';
  346. padding-right: 3px;
  347. opacity: 0.8;
  348. color: var(--el-color-primary);
  349. }
  350. &::after{
  351. content: '}}';
  352. padding-left: 3px;
  353. opacity: 0.8;
  354. color: var(--el-color-primary);
  355. }
  356. }
  357. }
  358. }
  359. }
  360. }
  361. .fm-add-rules-wrapper{
  362. width: 100%;
  363. margin-top: 10px;
  364. }
  365. .fm-add-rules-button{
  366. background-color: var(--el-fill-color-blank);
  367. border: 1px dashed var(--el-border-color);
  368. border-radius: 6px;
  369. box-sizing: border-box;
  370. text-align: center;
  371. cursor: pointer;
  372. position: relative;
  373. overflow: hidden;
  374. height: 30px;
  375. width: 100%;
  376. line-height: 30px;
  377. &:hover{
  378. border-color: var(--el-color-primary);
  379. }
  380. }
  381. }
  382. </style>