FcDesigner.vue 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076
  1. <style>
  2. ._fc-designer {
  3. height: 100%;
  4. min-height: 500px;
  5. overflow: hidden;
  6. cursor: default;
  7. position: relative;
  8. }
  9. ._fc-designer > .el-main {
  10. position: absolute;
  11. top: 0;
  12. bottom: 0;
  13. left: 0;
  14. right: 0;
  15. padding: 0px;
  16. }
  17. ._fc-m .form-create ._fc-l-item {
  18. background: #2e73ff;
  19. width: 100%;
  20. height: 10px;
  21. overflow: hidden;
  22. transition: all 0.3s ease;
  23. }
  24. ._fc-l,
  25. ._fc-m,
  26. ._fc-r {
  27. border-top: 1px solid #ececec;
  28. box-sizing: border-box;
  29. }
  30. ._fc-l-group {
  31. padding: 0 12px;
  32. }
  33. ._fc-l-title {
  34. font-weight: 600;
  35. font-size: 14px;
  36. margin: 18px 0px 5px;
  37. }
  38. ._fc-l-item {
  39. display: inline-block;
  40. background: #fff;
  41. color: #000;
  42. min-width: 70px;
  43. width: 33.33%;
  44. height: 70px;
  45. line-height: 1;
  46. text-align: center;
  47. transition: all 0.2s ease;
  48. cursor: pointer;
  49. }
  50. ._fc-l-item i {
  51. font-size: 21px;
  52. display: inline-block;
  53. }
  54. ._fc-l-item ._fc-l-name {
  55. font-size: 12px;
  56. }
  57. ._fc-l-item ._fc-l-icon {
  58. padding: 10px 5px 12px;
  59. }
  60. ._fc-l-item:hover {
  61. background: #2e73ff;
  62. color: #fff;
  63. }
  64. ._fc-m-tools {
  65. height: 40px;
  66. align-items: center;
  67. display: flex;
  68. justify-content: flex-end;
  69. border: 1px solid #ececec;
  70. border-top: 0 none;
  71. }
  72. ._fc-m-tools button.el-button {
  73. padding: 5px 14px;
  74. display: flex;
  75. align-items: center;
  76. }
  77. ._fc-m-tools .fc-icon {
  78. font-size: 14px;
  79. margin-right: 2px;
  80. }
  81. ._fc-r .el-tabs__nav-wrap::after {
  82. height: 1px;
  83. background-color: #ececec;
  84. }
  85. ._fc-r ._fc-r-tabs {
  86. display: flex;
  87. padding: 0;
  88. border-bottom: 1px solid #ececec;
  89. }
  90. ._fc-r ._fc-r-tab {
  91. height: 40px;
  92. box-sizing: border-box;
  93. line-height: 40px;
  94. display: inline-block;
  95. list-style: none;
  96. font-size: 14px;
  97. font-weight: 600;
  98. color: #303133;
  99. position: relative;
  100. flex: 1;
  101. text-align: center;
  102. }
  103. ._fc-r ._fc-r-tab.active {
  104. color: #409eff;
  105. border-bottom: 2px solid #409eff;
  106. }
  107. .drag-box {
  108. min-height: 60px;
  109. width: 100%;
  110. }
  111. ._fc-m-drag {
  112. overflow: auto;
  113. padding: 2px;
  114. box-sizing: border-box;
  115. }
  116. ._fc-m-drag,
  117. .draggable-drag {
  118. background: #fff;
  119. height: 100%;
  120. position: relative;
  121. }
  122. ._fc-m-drag > form,
  123. ._fc-m-drag > form > .el-row {
  124. height: 100%;
  125. }
  126. </style>
  127. <template>
  128. <ElContainer class="_fc-designer" :style="'height:' + dragHeight">
  129. <ElMain>
  130. <ElContainer style="height: 100%">
  131. <el-aside class="_fc-l" width="266px">
  132. <template v-for="(item, index) in menuList" :key="index">
  133. <div class="_fc-l-group">
  134. <h4 class="_fc-l-title">{{ item.title }}</h4>
  135. <draggable :group="{ name: 'default', pull: 'clone', put: false }" :sort="false" itemKey="name" :list="item.list">
  136. <template #item="{ element }">
  137. <div class="_fc-l-item">
  138. <div class="_fc-l-icon">
  139. <i class="fc-icon" :class="element.icon || 'icon-input'"></i>
  140. </div>
  141. <span class="_fc-l-name">{{ t('components.' + element.name + '.name') || element.label }}</span>
  142. </div>
  143. </template>
  144. </draggable>
  145. </div>
  146. </template>
  147. </el-aside>
  148. <ElContainer class="_fc-m">
  149. <el-header class="_fc-m-tools" height="45">
  150. <slot name="handle"></slot>
  151. <el-button size="small" type="primary">保存当前JSON</el-button>
  152. <el-button type="primary" plain round size="small" @click="previewFc"
  153. ><i class="fc-icon icon-preview"></i> {{ t('designer.preview') }}
  154. </el-button>
  155. <el-popconfirm
  156. :title="t('designer.clearConfirmTitle')"
  157. width="200px"
  158. :confirm-button-text="t('designer.clearConfirm')"
  159. :cancel-button-text="t('designer.clearCancel')"
  160. @confirm="clearDragRule"
  161. >
  162. <template #reference>
  163. <el-button type="danger" plain round size="small"><i class="fc-icon icon-delete"></i>{{ t('designer.clear') }} </el-button>
  164. </template>
  165. </el-popconfirm>
  166. </el-header>
  167. <ElMain style="background: #f5f5f5; padding: 20px">
  168. <div class="_fc-m-drag">
  169. <DragForm :rule="dragForm.rule" :option="form.value" v-model:api="dragForm.api"></DragForm>
  170. </div>
  171. </ElMain>
  172. </ElContainer>
  173. <ElAside class="_fc-r" width="320px" v-if="!config || config.showConfig !== false">
  174. <ElContainer style="height: 100%">
  175. <el-header height="40px" class="_fc-r-tabs">
  176. <div
  177. class="_fc-r-tab"
  178. :class="{ active: activeTab === 'props' }"
  179. v-if="!!activeRule || (config && config.showFormConfig === false)"
  180. @click="activeTab = 'props'"
  181. >
  182. {{ t('designer.config.component') }}
  183. </div>
  184. <div
  185. class="_fc-r-tab"
  186. v-if="!config || config.showFormConfig !== false"
  187. :class="{ active: activeTab === 'form' && !!activeRule }"
  188. @click="activeTab = 'form'"
  189. >
  190. {{ t('designer.config.form') }}
  191. </div>
  192. </el-header>
  193. <ElMain v-show="activeTab === 'form'" v-if="!config || config.showFormConfig !== false">
  194. <DragForm :rule="form.rule" :option="form.option" v-model="form.value.form" v-model:api="form.api"></DragForm>
  195. </ElMain>
  196. <ElMain v-show="activeTab === 'props'" style="padding: 0 20px" :key="activeRule ? activeRule._id : ''">
  197. <div>
  198. <ElDivider v-if="showBaseRule">{{ t('designer.config.rule') }}</ElDivider>
  199. <DragForm
  200. v-show="showBaseRule"
  201. v-model:api="baseForm.api"
  202. :rule="baseForm.rule"
  203. :option="baseForm.options"
  204. :modelValue="baseForm.value"
  205. @change="baseChange"
  206. ></DragForm>
  207. <ElDivider>{{ t('designer.config.props') }}</ElDivider>
  208. <DragForm
  209. v-model:api="propsForm.api"
  210. :rule="propsForm.rule"
  211. :option="propsForm.options"
  212. :modelValue="propsForm.value"
  213. @change="propChange"
  214. @removeField="propRemoveField"
  215. ></DragForm>
  216. <ElDivider v-if="showBaseRule">{{ t('designer.config.validate') }}</ElDivider>
  217. <DragForm
  218. v-show="showBaseRule"
  219. v-model:api="validateForm.api"
  220. :rule="validateForm.rule"
  221. :option="validateForm.options"
  222. :modelValue="validateForm.value"
  223. @update:modelValue="validateChange"
  224. ></DragForm>
  225. </div>
  226. </ElMain>
  227. </ElContainer>
  228. </ElAside>
  229. <ElDialog v-model="preview.state" width="800px" append-to-body>
  230. <ViewForm :rule="preview.rule" :option="preview.option" v-if="preview.state"></ViewForm>
  231. </ElDialog>
  232. </ElContainer>
  233. </ElMain>
  234. </ElContainer>
  235. </template>
  236. <script>
  237. import form from '../../config/base/form'
  238. import field from '../../config/base/field'
  239. import validate from '../../config/base/validate'
  240. import { deepCopy } from '@form-create/utils/lib/deepextend'
  241. import is, { hasProperty } from '@form-create/utils/lib/type'
  242. import { lower } from '@form-create/utils/lib/tocase'
  243. import ruleList from '../../config/rule'
  244. import draggable from 'vuedraggable/src/vuedraggable'
  245. import createMenu from '../../config/menu'
  246. import { upper, useLocale } from '../../utils/formCreateIndex'
  247. import { designerForm } from '../../utils/form'
  248. import viewForm from '../../utils/form'
  249. import { t as globalT } from '../../utils/locale'
  250. import { computed, reactive, toRefs, toRef, ref, getCurrentInstance, provide, nextTick, watch, defineComponent, markRaw } from 'vue'
  251. export default defineComponent({
  252. name: 'FcDesigner',
  253. components: {
  254. draggable,
  255. DragForm: designerForm.$form(),
  256. ViewForm: viewForm.$form()
  257. },
  258. props: ['menu', 'height', 'config', 'mask', 'locale'],
  259. setup(props) {
  260. const { menu, height, mask, locale } = toRefs(props)
  261. const vm = getCurrentInstance()
  262. provide('fcx', ref({ active: null }))
  263. provide('designer', vm)
  264. const config = toRef(props, 'config', {})
  265. const baseRule = toRef(config.value, 'baseRule', null)
  266. const componentRule = toRef(config.value, 'componentRule', {})
  267. const validateRule = toRef(config.value, 'validateRule', null)
  268. const formRule = toRef(config.value, 'formRule', null)
  269. const dragHeight = computed(() => {
  270. const h = height.value
  271. if (!h) return '100%'
  272. return is.Number(h) ? `${h}px` : h
  273. })
  274. let _t = globalT
  275. if (locale.value) {
  276. _t = useLocale(locale).t
  277. }
  278. const t = (...args) => _t(...args)
  279. const tidyRuleConfig = (orgRule, configRule, ...args) => {
  280. if (configRule) {
  281. if (is.Function(configRule)) {
  282. return configRule(...args)
  283. }
  284. if (configRule.rule) {
  285. let rule = configRule.rule(...args)
  286. if (configRule.append) {
  287. rule = [...rule, ...orgRule(...args)]
  288. }
  289. return rule
  290. }
  291. }
  292. return orgRule(...args)
  293. }
  294. const data = reactive({
  295. cacheProps: {},
  296. moveRule: null,
  297. addRule: null,
  298. added: null,
  299. activeTab: 'form',
  300. activeRule: null,
  301. children: ref([]),
  302. menuList: menu.value || createMenu({ t }),
  303. showBaseRule: false,
  304. visible: {
  305. preview: false
  306. },
  307. t,
  308. preview: {
  309. state: false,
  310. rule: [],
  311. option: {}
  312. },
  313. dragForm: ref({
  314. rule: [],
  315. api: {}
  316. }),
  317. form: {
  318. rule: tidyRuleConfig(form, formRule.value, { t }),
  319. api: {},
  320. option: {
  321. form: {
  322. labelPosition: 'top',
  323. size: 'small'
  324. },
  325. submitBtn: false
  326. },
  327. value: {
  328. form: {
  329. inline: false,
  330. hideRequiredAsterisk: false,
  331. labelPosition: 'right',
  332. size: 'small',
  333. labelWidth: '125px',
  334. formCreateSubmitBtn: true,
  335. formCreateResetBtn: false
  336. },
  337. submitBtn: false
  338. }
  339. },
  340. baseForm: {
  341. rule: tidyRuleConfig(field, baseRule.value, { t }),
  342. api: {},
  343. value: {},
  344. options: {
  345. form: {
  346. labelPosition: 'top',
  347. size: 'small'
  348. },
  349. submitBtn: false,
  350. mounted: fapi => {
  351. fapi.activeRule = data.activeRule
  352. fapi.setValue(fapi.options.formData || {})
  353. }
  354. }
  355. },
  356. validateForm: {
  357. rule: tidyRuleConfig(validate, validateRule.value, { t }),
  358. api: {},
  359. value: [],
  360. options: {
  361. form: {
  362. labelPosition: 'top',
  363. size: 'small'
  364. },
  365. submitBtn: false,
  366. mounted: fapi => {
  367. fapi.activeRule = data.activeRule
  368. fapi.setValue(fapi.options.formData || {})
  369. }
  370. }
  371. },
  372. propsForm: {
  373. rule: [],
  374. api: {},
  375. value: {},
  376. options: {
  377. form: {
  378. labelPosition: 'top',
  379. size: 'small'
  380. },
  381. submitBtn: false,
  382. mounted: fapi => {
  383. fapi.activeRule = data.activeRule
  384. fapi.setValue(fapi.options.formData || {})
  385. }
  386. }
  387. }
  388. })
  389. watch(
  390. () => data.preview.state,
  391. function (n) {
  392. if (!n) {
  393. nextTick(() => {
  394. data.preview.rule = data.preview.option = null
  395. })
  396. }
  397. }
  398. )
  399. let unWatchActiveRule = null
  400. watch(
  401. () => locale.value,
  402. n => {
  403. _t = n ? useLocale(locale).t : globalT
  404. const formVal = data.form.api.formData && data.form.api.formData()
  405. const baseFormVal = data.baseForm.api.formData && data.baseForm.api.formData()
  406. const validateFormVal = data.validateForm.api.formData && data.validateForm.api.formData()
  407. data.validateForm.rule = tidyRuleConfig(validate, validateRule.value, { t })
  408. data.baseForm.rule = tidyRuleConfig(field, baseRule.value, { t })
  409. data.form.rule = tidyRuleConfig(form, formRule.value, { t })
  410. data.cacheProps = {}
  411. const rule = data.activeRule
  412. let propsVal = null
  413. if (rule) {
  414. propsVal = data.propsForm.api.formData && data.propsForm.api.formData()
  415. data.propsForm.rule = data.cacheProps[rule._id] = tidyRuleConfig(
  416. rule.config.config.props,
  417. componentRule.value && componentRule.value[rule.config.config.name],
  418. rule,
  419. { t, api: data.dragForm.api }
  420. )
  421. }
  422. nextTick(() => {
  423. formVal && data.form.api.setValue(formVal)
  424. baseFormVal && data.baseForm.api.setValue(baseFormVal)
  425. validateFormVal && data.validateForm.api.setValue(validateFormVal)
  426. propsVal && data.propsForm.api.setValue(propsVal)
  427. })
  428. }
  429. )
  430. const methods = {
  431. unWatchActiveRule() {
  432. unWatchActiveRule && unWatchActiveRule()
  433. unWatchActiveRule = null
  434. },
  435. watchActiveRule() {
  436. methods.unWatchActiveRule()
  437. unWatchActiveRule = watch(
  438. () => data.activeRule,
  439. function (n) {
  440. n && methods.updateRuleFormData()
  441. },
  442. { deep: true, flush: 'post' }
  443. )
  444. },
  445. makeChildren(children) {
  446. return reactive({ children }).children
  447. },
  448. addMenu(config) {
  449. if (!config.name || !config.list) return
  450. let flag = true
  451. data.menuList.forEach((v, i) => {
  452. if (v.name === config.name) {
  453. data.menuList[i] = config
  454. flag = false
  455. }
  456. })
  457. if (flag) {
  458. data.menuList.push(config)
  459. }
  460. },
  461. removeMenu(name) {
  462. ;[...data.menuList].forEach((v, i) => {
  463. if (v.name === name) {
  464. data.menuList.splice(i, 1)
  465. }
  466. })
  467. },
  468. setMenuItem(name, list) {
  469. data.menuList.forEach(v => {
  470. if (v.name === name) {
  471. v.list = list
  472. }
  473. })
  474. },
  475. appendMenuItem(name, item) {
  476. data.menuList.forEach(v => {
  477. if (v.name === name) {
  478. v.list.push(...(Array.isArray(item) ? item : [item]))
  479. }
  480. })
  481. },
  482. removeMenuItem(item) {
  483. data.menuList.forEach(v => {
  484. let idx
  485. if (is.String(item)) {
  486. ;[...v.list].forEach((menu, idx) => {
  487. if (menu.name === item) {
  488. v.list.splice(idx, 1)
  489. }
  490. })
  491. } else {
  492. if ((idx = v.list.indexOf(item)) > -1) {
  493. v.list.splice(idx, 1)
  494. }
  495. }
  496. })
  497. },
  498. addComponent(component) {
  499. if (Array.isArray(component)) {
  500. component.forEach(v => {
  501. ruleList[v.name] = v
  502. })
  503. } else {
  504. ruleList[component.name] = component
  505. }
  506. },
  507. getParent(rule) {
  508. let parent = rule.__fc__.parent.rule
  509. const config = parent.config
  510. if (config && config.config.inside) {
  511. rule = parent
  512. parent = parent.__fc__.parent.rule
  513. }
  514. return { root: parent, parent: rule }
  515. },
  516. makeDrag(group, tag, children, on) {
  517. return {
  518. type: 'DragBox',
  519. wrap: {
  520. show: false
  521. },
  522. col: {
  523. show: false
  524. },
  525. inject: true,
  526. props: {
  527. rule: {
  528. props: {
  529. tag: 'el-col',
  530. group: group === true ? 'default' : group,
  531. ghostClass: 'ghost',
  532. animation: 150,
  533. handle: '._fc-drag-btn',
  534. emptyInsertThreshold: 0,
  535. direction: 'vertical',
  536. itemKey: 'type'
  537. }
  538. },
  539. tag
  540. },
  541. children,
  542. on
  543. }
  544. },
  545. clearDragRule() {
  546. methods.setRule([])
  547. },
  548. makeDragRule(children) {
  549. return methods.makeChildren([
  550. methods.makeDrag(true, 'draggable', children, {
  551. add: (inject, evt) => methods.dragAdd(children, evt),
  552. end: (inject, evt) => methods.dragEnd(children, evt),
  553. start: (inject, evt) => methods.dragStart(children, evt),
  554. unchoose: (inject, evt) => methods.dragUnchoose(children, evt)
  555. })
  556. ])
  557. },
  558. previewFc() {
  559. data.preview.state = true
  560. data.preview.rule = methods.getRule()
  561. data.preview.option = methods.getOption()
  562. },
  563. getRule() {
  564. return methods.parseRule(deepCopy(data.dragForm.api.rule[0].children))
  565. },
  566. getJson() {
  567. return designerForm.toJson(methods.getRule())
  568. },
  569. getOption() {
  570. const option = deepCopy(data.form.value)
  571. option.submitBtn = option._submitBtn
  572. option.resetBtn = option._resetBtn
  573. if (typeof option.submitBtn === 'object') {
  574. option.submitBtn.show = option.form.formCreateSubmitBtn
  575. } else {
  576. option.submitBtn = {
  577. show: option.form.formCreateSubmitBtn,
  578. innerText: t('form.submit')
  579. }
  580. }
  581. if (typeof option.resetBtn === 'object') {
  582. option.resetBtn.show = option.form.formCreateResetBtn
  583. } else {
  584. option.resetBtn = {
  585. show: option.form.formCreateResetBtn,
  586. innerText: t('form.reset')
  587. }
  588. }
  589. delete option.form.formCreateSubmitBtn
  590. delete option.form.formCreateResetBtn
  591. delete option._submitBtn
  592. delete option._resetBtn
  593. return option
  594. },
  595. getOptions() {
  596. methods.getOption()
  597. },
  598. setRule(rules) {
  599. if (!rules) {
  600. rules = []
  601. }
  602. data.children = methods.makeChildren(methods.loadRule(is.String(rules) ? designerForm.parseJson(rules) : deepCopy(rules)))
  603. methods.clearActiveRule()
  604. data.dragForm.rule = methods.makeDragRule(data.children)
  605. },
  606. setBaseRuleConfig(rule, append) {
  607. baseRule.value = { rule, append }
  608. data.baseForm.rule = tidyRuleConfig(field, baseRule.value, { t })
  609. },
  610. setComponentRuleConfig(name, rule, append) {
  611. componentRule.value[name] = { rule, append }
  612. data.cacheProps = {}
  613. const activeRule = data.activeRule
  614. if (activeRule) {
  615. const propsVal = data.propsForm.api.formData && data.propsForm.api.formData()
  616. data.propsForm.rule = data.cacheProps[activeRule._id] = tidyRuleConfig(
  617. activeRule.config.config.props,
  618. componentRule.value && componentRule.value[activeRule.config.config.name],
  619. activeRule,
  620. { t, api: data.dragForm.api }
  621. )
  622. nextTick(() => {
  623. propsVal && data.propsForm.api.setValue(propsVal)
  624. })
  625. }
  626. },
  627. setValidateRuleConfig(rule, append) {
  628. validateRule.value = { rule, append }
  629. data.validateForm.rule = tidyRuleConfig(field, validateRule.value, { t })
  630. },
  631. setFormRuleConfig(rule, append) {
  632. formRule.value = { rule, append }
  633. data.form.rule = tidyRuleConfig(field, formRule.value, { t })
  634. },
  635. clearActiveRule() {
  636. data.activeRule = null
  637. data.activeTab = 'form'
  638. },
  639. setOption(opt) {
  640. let option = { ...opt }
  641. option.form.formCreateSubmitBtn =
  642. typeof option.submitBtn === 'object' ? (option.submitBtn.show === undefined ? true : !!option.submitBtn.show) : !!option.submitBtn
  643. option.form.formCreateResetBtn = typeof option.resetBtn === 'object' ? !!option.resetBtn.show : !!option.resetBtn
  644. option._resetBtn = option.resetBtn
  645. option.resetBtn = false
  646. option._submitBtn = option.submitBtn
  647. option.submitBtn = false
  648. data.form.value = option
  649. },
  650. setOptions(opt) {
  651. methods.setOption(opt)
  652. },
  653. loadRule(rules) {
  654. const loadRule = []
  655. rules.forEach(rule => {
  656. if (is.String(rule)) {
  657. return loadRule.push(rule)
  658. }
  659. const config = ruleList[rule._fc_drag_tag] || ruleList[rule.type]
  660. const _children = rule.children
  661. rule.children = []
  662. if (rule.control) {
  663. rule._control = rule.control
  664. delete rule.control
  665. }
  666. if (config) {
  667. rule = methods.makeRule(config, rule)
  668. if (_children) {
  669. let children = rule.children[0].children
  670. if (config.drag) {
  671. children = children[0].children
  672. }
  673. children.push(...methods.loadRule(_children))
  674. }
  675. } else if (_children) {
  676. rule.children = methods.loadRule(_children)
  677. }
  678. loadRule.push(rule)
  679. })
  680. return loadRule
  681. },
  682. parseRule(children) {
  683. return [...children].reduce((initial, rule) => {
  684. if (is.String(rule)) {
  685. initial.push(rule)
  686. return initial
  687. } else if (rule.type === 'DragBox') {
  688. initial.push(...methods.parseRule(rule.children))
  689. return initial
  690. } else if (rule.type === 'DragTool') {
  691. rule = rule.children[0]
  692. if (rule.type === 'DragBox') {
  693. initial.push(...methods.parseRule(rule.children))
  694. return initial
  695. }
  696. }
  697. if (!rule) return initial
  698. rule = { ...rule }
  699. if (rule.children.length) {
  700. rule.children = methods.parseRule(rule.children)
  701. }
  702. delete rule._id
  703. delete rule.key
  704. delete rule.component
  705. if (rule.config) {
  706. delete rule.config.config
  707. }
  708. if (rule.effect) {
  709. delete rule.effect._fc
  710. delete rule.effect._fc_tool
  711. }
  712. if (rule._control) {
  713. rule.control = rule._control
  714. delete rule._control
  715. }
  716. Object.keys(rule)
  717. .filter(k => (Array.isArray(rule[k]) && rule[k].length === 0) || (is.Object(rule[k]) && Object.keys(rule[k]).length === 0))
  718. .forEach(k => {
  719. delete rule[k]
  720. })
  721. initial.push(rule)
  722. return initial
  723. }, [])
  724. },
  725. baseChange(field, value, _, fapi) {
  726. if (data.activeRule && fapi[data.activeRule._id] === data.activeRule) {
  727. methods.unWatchActiveRule()
  728. data.activeRule[field] = value
  729. methods.watchActiveRule()
  730. data.activeRule.config.config?.watch?.['$' + field]?.({
  731. field,
  732. value,
  733. api: fapi,
  734. rule: data.activeRule
  735. })
  736. }
  737. },
  738. propRemoveField(field, _, fapi) {
  739. if (data.activeRule && fapi[data.activeRule._id] === data.activeRule) {
  740. methods.unWatchActiveRule()
  741. const org = field
  742. data.dragForm.api.sync(data.activeRule)
  743. if (field.indexOf('formCreate') === 0) {
  744. field = field.replace('formCreate', '')
  745. if (!field) return
  746. field = lower(field)
  747. if (field.indexOf('effect') === 0 && field.indexOf('>') > -1) {
  748. delete data.activeRule.effect[field.split('>')[1]]
  749. } else if (field.indexOf('props') === 0 && field.indexOf('>') > -1) {
  750. delete data.activeRule.props[field.split('>')[1]]
  751. } else if (field.indexOf('attrs') === 0 && field.indexOf('>') > -1) {
  752. data.activeRule.attrs[field.split('>')[1]] = value
  753. } else if (field === 'child') {
  754. delete data.activeRule.children[0]
  755. } else if (field) {
  756. data.activeRule[field] = undefined
  757. }
  758. } else {
  759. delete data.activeRule.props[field]
  760. }
  761. methods.watchActiveRule()
  762. data.activeRule.config.config?.watch?.[org]?.({
  763. field: org,
  764. value: undefined,
  765. api: fapi,
  766. rule: data.activeRule
  767. })
  768. }
  769. },
  770. propChange(field, value, _, fapi) {
  771. if (data.activeRule && fapi[data.activeRule._id] === data.activeRule) {
  772. methods.unWatchActiveRule()
  773. const org = field
  774. if (field.indexOf('formCreate') === 0) {
  775. field = field.replace('formCreate', '')
  776. if (!field) return
  777. field = lower(field)
  778. if (field.indexOf('effect') === 0 && field.indexOf('>') > -1) {
  779. data.activeRule.effect[field.split('>')[1]] = value
  780. } else if (field.indexOf('props') === 0 && field.indexOf('>') > -1) {
  781. data.activeRule.props[field.split('>')[1]] = value
  782. } else if (field.indexOf('attrs') === 0 && field.indexOf('>') > -1) {
  783. data.activeRule.attrs[field.split('>')[1]] = value
  784. } else if (field === 'child') {
  785. data.activeRule.children[0] = value
  786. } else {
  787. data.activeRule[field] = value
  788. }
  789. } else {
  790. data.activeRule.props[field] = value
  791. }
  792. methods.watchActiveRule()
  793. data.activeRule.config.config?.watch?.[org]?.({
  794. field: org,
  795. value,
  796. api: fapi,
  797. rule: data.activeRule
  798. })
  799. }
  800. },
  801. validateChange(formData) {
  802. if (!data.activeRule || data.validateForm.api[data.activeRule._id] !== data.activeRule) return
  803. data.activeRule.validate = formData.validate || []
  804. data.dragForm.api.refreshValidate()
  805. data.dragForm.api.nextTick(() => {
  806. data.dragForm.api.clearValidateState(data.activeRule.__fc__.id)
  807. })
  808. },
  809. toolActive(rule) {
  810. methods.unWatchActiveRule()
  811. if (data.activeRule) {
  812. delete data.propsForm.api[data.activeRule._id]
  813. delete data.baseForm.api[data.activeRule._id]
  814. delete data.validateForm.api[data.activeRule._id]
  815. delete data.dragForm.api.activeRule
  816. }
  817. data.activeRule = rule
  818. data.dragForm.api.activeRule = rule
  819. nextTick(() => {
  820. data.activeTab = 'props'
  821. nextTick(() => {
  822. data.propsForm.api[data.activeRule._id] = data.activeRule
  823. data.baseForm.api[data.activeRule._id] = data.activeRule
  824. data.validateForm.api[data.activeRule._id] = data.activeRule
  825. })
  826. })
  827. if (!data.cacheProps[rule._id]) {
  828. data.cacheProps[rule._id] = tidyRuleConfig(
  829. rule.config.config.props,
  830. componentRule.value && componentRule.value[rule.config.config.name],
  831. rule,
  832. { t, api: data.dragForm.api }
  833. ) // rule.config.config.props(rule, {t, api: data.dragForm.api});
  834. }
  835. data.propsForm.rule = data.cacheProps[rule._id]
  836. methods.updateRuleFormData()
  837. methods.watchActiveRule()
  838. },
  839. updateRuleFormData() {
  840. const rule = data.activeRule
  841. const formData = { ...rule.props, formCreateChild: deepCopy(rule.children[0]) }
  842. Object.keys(rule).forEach(k => {
  843. if (['effect', 'config', 'payload', 'id', 'type'].indexOf(k) < 0) formData['formCreate' + upper(k)] = deepCopy(rule[k])
  844. })
  845. ;['props', 'effect', 'attrs'].forEach(name => {
  846. rule[name] &&
  847. Object.keys(rule[name]).forEach(k => {
  848. formData['formCreate' + upper(name) + '>' + k] = deepCopy(rule[name][k])
  849. })
  850. })
  851. data.propsForm.value = formData
  852. data.showBaseRule = hasProperty(rule, 'field') && rule.input !== false && (!config.value || config.value.showBaseForm !== false)
  853. if (data.showBaseRule) {
  854. data.baseForm.value = {
  855. field: rule.field,
  856. title: rule.title || '',
  857. info: rule.info,
  858. _control: rule._control
  859. }
  860. data.validateForm.value = { validate: rule.validate ? [...rule.validate] : [] }
  861. data.dragForm.api.refreshValidate()
  862. data.dragForm.api.nextTick(() => {
  863. data.dragForm.api.clearValidateState(rule.__fc__.id)
  864. })
  865. }
  866. },
  867. dragStart(children) {
  868. data.moveRule = children
  869. data.added = false
  870. },
  871. dragUnchoose(children, evt) {
  872. data.addRule = {
  873. children,
  874. oldIndex: evt.oldIndex
  875. }
  876. },
  877. dragAdd(children, evt) {
  878. const newIndex = evt.newIndex
  879. const menu = evt.item._underlying_vm_
  880. if (!menu || menu.__fc__) {
  881. if (data.addRule) {
  882. const rule = data.addRule.children.splice(data.addRule.oldIndex, 1)
  883. children.splice(newIndex, 0, rule[0])
  884. }
  885. } else {
  886. const rule = methods.makeRule(ruleList[menu.name])
  887. children.splice(newIndex, 0, rule)
  888. }
  889. data.added = true
  890. // data.dragForm.api.refresh();
  891. },
  892. dragEnd(children, { newIndex, oldIndex }) {
  893. if (!data.added && !(data.moveRule === children && newIndex === oldIndex)) {
  894. const rule = data.moveRule.splice(oldIndex, 1)
  895. children.splice(newIndex, 0, rule[0])
  896. }
  897. data.moveRule = null
  898. data.addRule = null
  899. data.added = false
  900. // data.dragForm.api.refresh();
  901. },
  902. makeRule(config, _rule) {
  903. const rule = _rule || config.rule({ t })
  904. rule.config = { config }
  905. if (config.component) {
  906. rule.component = markRaw(config.component)
  907. }
  908. if (!rule.effect) rule.effect = {}
  909. rule.effect._fc = true
  910. rule._fc_drag_tag = config.name
  911. let drag
  912. if (config.drag) {
  913. rule.children.push(
  914. (drag = methods.makeDrag(config.drag, rule.type, methods.makeChildren([]), {
  915. end: (inject, evt) => methods.dragEnd(inject.self.children, evt),
  916. add: (inject, evt) => methods.dragAdd(inject.self.children, evt),
  917. start: (inject, evt) => methods.dragStart(inject.self.children, evt),
  918. unchoose: (inject, evt) => methods.dragUnchoose(inject.self.children, evt)
  919. }))
  920. )
  921. }
  922. if (config.children && !_rule) {
  923. for (let i = 0; i < (config.childrenLen || 1); i++) {
  924. const child = methods.makeRule(ruleList[config.children])
  925. ;(drag || rule).children.push(child)
  926. }
  927. }
  928. const dragMask = mask.value !== undefined ? mask.value !== false : config.mask !== false
  929. if (config.inside) {
  930. rule.children = methods.makeChildren([
  931. {
  932. type: 'DragTool',
  933. props: {
  934. dragBtn: config.dragBtn !== false,
  935. children: config.children,
  936. mask: dragMask
  937. },
  938. effect: {
  939. _fc_tool: true
  940. },
  941. inject: true,
  942. on: {
  943. delete: ({ self }) => {
  944. const parent = methods.getParent(self).parent
  945. parent.__fc__.rm()
  946. vm.emit('delete', parent)
  947. methods.clearActiveRule()
  948. },
  949. create: ({ self }) => {
  950. const top = methods.getParent(self)
  951. vm.emit('create', top.parent)
  952. top.root.children.splice(top.root.children.indexOf(top.parent) + 1, 0, methods.makeRule(top.parent.config.config))
  953. },
  954. addChild: ({ self }) => {
  955. const top = methods.getParent(self)
  956. const config = top.parent.config.config
  957. const item = ruleList[config.children]
  958. if (!item) return
  959. ;(!config.drag ? top.parent : top.parent.children[0]).children[0].children.push(methods.makeRule(item))
  960. },
  961. copy: ({ self }) => {
  962. const top = methods.getParent(self)
  963. vm.emit('copy', top.parent)
  964. top.root.children.splice(top.root.children.indexOf(top.parent) + 1, 0, designerForm.copyRule(top.parent))
  965. },
  966. active: ({ self }) => {
  967. const top = methods.getParent(self)
  968. vm.emit('active', top.parent)
  969. methods.toolActive(top.parent)
  970. }
  971. },
  972. children: rule.children
  973. }
  974. ])
  975. return rule
  976. } else {
  977. return {
  978. type: 'DragTool',
  979. props: {
  980. dragBtn: config.dragBtn !== false,
  981. children: config.children,
  982. mask: dragMask
  983. },
  984. effect: {
  985. _fc_tool: true
  986. },
  987. inject: true,
  988. on: {
  989. delete: ({ self }) => {
  990. vm.emit('delete', self.children[0])
  991. self.__fc__.rm()
  992. methods.clearActiveRule()
  993. },
  994. create: ({ self }) => {
  995. vm.emit('create', self.children[0])
  996. const top = methods.getParent(self)
  997. top.root.children.splice(top.root.children.indexOf(top.parent) + 1, 0, methods.makeRule(self.children[0].config.config))
  998. },
  999. addChild: ({ self }) => {
  1000. const config = self.children[0].config.config
  1001. const item = ruleList[config.children]
  1002. if (!item) return
  1003. ;(!config.drag ? self : self.children[0]).children[0].children.push(methods.makeRule(item))
  1004. },
  1005. copy: ({ self }) => {
  1006. vm.emit('copy', self.children[0])
  1007. const top = methods.getParent(self)
  1008. top.root.children.splice(top.root.children.indexOf(top.parent) + 1, 0, designerForm.copyRule(top.parent))
  1009. },
  1010. active: ({ self }) => {
  1011. vm.emit('active', self.children[0])
  1012. methods.toolActive(self.children[0])
  1013. }
  1014. },
  1015. children: methods.makeChildren([rule])
  1016. }
  1017. }
  1018. }
  1019. }
  1020. data.dragForm.rule = methods.makeDragRule(methods.makeChildren(data.children))
  1021. return {
  1022. ...toRefs(data),
  1023. ...methods,
  1024. dragHeight,
  1025. t
  1026. }
  1027. },
  1028. created() {
  1029. document.body.ondrop = e => {
  1030. e.preventDefault()
  1031. e.stopPropagation()
  1032. }
  1033. }
  1034. })
  1035. </script>