index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. <template>
  2. <a-config-provider :locale="locale">
  3. <div class="form-designer-container-9136076486841527">
  4. <k-header v-if="showHead" :title="title" />
  5. <!-- 操作区域 start -->
  6. <operatingArea
  7. v-if="toolbarsTop"
  8. :showToolbarsText="showToolbarsText"
  9. :toolbars="toolbars"
  10. @handleSave="handleSave"
  11. @handlePreview="handlePreview"
  12. @handleOpenImportJsonModal="handleOpenImportJsonModal"
  13. @handleOpenCodeModal="handleOpenCodeModal"
  14. @handleOpenJsonModal="handleOpenJsonModal"
  15. @handleReset="handleReset"
  16. @handleClose="handleClose"
  17. @handleUndo="handleUndo"
  18. @handleRedo="handleRedo"
  19. :recordList="recordList"
  20. :redoList="redoList"
  21. >
  22. <template slot="left-action">
  23. <slot name="left-action"></slot>
  24. </template>
  25. <template slot="right-action">
  26. <slot name="right-action"></slot>
  27. </template>
  28. </operatingArea>
  29. <!-- 操作区域 end -->
  30. <div
  31. class="content"
  32. :class="{
  33. 'show-head': showHead,
  34. 'toolbars-top': toolbarsTop,
  35. 'show-head-and-toolbars-top': toolbarsTop && showHead
  36. }"
  37. >
  38. <!-- 左侧控件区域 start -->
  39. <aside class="left">
  40. <a-collapse @change="collapseChange" :defaultActiveKey="collapseDefaultActiveKey">
  41. <!-- 基础控件 start -->
  42. <a-collapse-panel v-if="basicsArray.length > 0" header="基础控件" key="1">
  43. <collapseItem
  44. :list="basicsArray"
  45. @generateKey="generateKey"
  46. @handleListPush="handleListPush"
  47. @start="handleStart"
  48. />
  49. </a-collapse-panel>
  50. <!-- 基础控件 end -->
  51. <!-- 自定义控件 start -->
  52. <a-collapse-panel
  53. v-if="customComponents.list.length > 0"
  54. :header="customComponents.title"
  55. key="3"
  56. >
  57. <collapseItem
  58. :list="customComponents.list"
  59. @generateKey="generateKey"
  60. @handleListPush="handleListPush"
  61. @start="handleStart"
  62. />
  63. </a-collapse-panel>
  64. <!-- 自定义控件 end -->
  65. <!-- 布局控件 start -->
  66. <a-collapse-panel v-if="layoutArray.length > 0" header="布局控件" key="4">
  67. <collapseItem
  68. :list="layoutArray"
  69. @generateKey="generateKey"
  70. @handleListPush="handleListPush"
  71. @start="handleStart"
  72. />
  73. </a-collapse-panel>
  74. <!-- 布局控件 end -->
  75. </a-collapse>
  76. </aside>
  77. <!-- 左侧控件区域 end -->
  78. <!-- 中间面板区域 start -->
  79. <section>
  80. <!-- 操作区域 start -->
  81. <operatingArea
  82. v-if="!toolbarsTop"
  83. :showToolbarsText="showToolbarsText"
  84. :toolbars="toolbars"
  85. @handleSave="handleSave"
  86. @handlePreview="handlePreview"
  87. @handleOpenImportJsonModal="handleOpenImportJsonModal"
  88. @handleOpenCodeModal="handleOpenCodeModal"
  89. @handleOpenJsonModal="handleOpenJsonModal"
  90. @handleReset="handleReset"
  91. @handleClose="handleClose"
  92. @handleUndo="handleUndo"
  93. @handleRedo="handleRedo"
  94. @handleQuery="handleQuery"
  95. :recordList="recordList"
  96. :redoList="redoList"
  97. >
  98. <template slot="left-action">
  99. <slot name="left-action"></slot>
  100. </template>
  101. <template slot="right-action">
  102. <slot name="right-action"></slot>
  103. </template>
  104. </operatingArea>
  105. <!-- 操作区域 end -->
  106. <k-form-component-panel
  107. :class="{ 'no-toolbars-top': !toolbarsTop }"
  108. :data="data"
  109. :selectItem="selectItem"
  110. :noModel="noModel"
  111. :hideModel="hideModel"
  112. :startType="startType"
  113. ref="KFCP"
  114. @handleSetSelectItem="handleSetSelectItem"
  115. />
  116. <!-- 操作区域 start -->
  117. <k-json-modal ref="jsonModal" />
  118. <k-code-modal ref="codeModal" />
  119. <importJsonModal ref="importJsonModal" />
  120. <previewModal ref="previewModal" />
  121. <!-- 查询弹框 -->
  122. <queryModal ref="queryModal" />
  123. </section>
  124. <!-- 中间面板区域 end -->
  125. <!-- 右侧控件属性区域 start -->
  126. <aside class="right">
  127. <a-tabs :activeKey="activeKey" @change="changeTab" :tabBarStyle="{ margin: 0 }">
  128. <a-tab-pane :key="1" tab="表单属性设置">
  129. <formProperties :config="data.config" :formData="formData" :previewOptions="previewOptions" />
  130. </a-tab-pane>
  131. <a-tab-pane :key="2" tab="控件属性设置">
  132. <formItemProperties
  133. class="form-item-properties"
  134. :selectItem="selectItem"
  135. :hideModel="hideModel"
  136. :tableColumnList="tableColumnList"
  137. />
  138. </a-tab-pane>
  139. </a-tabs>
  140. </aside>
  141. <!-- 右侧控件属性区域 end -->
  142. </div>
  143. <!-- <k-footer /> -->
  144. </div>
  145. </a-config-provider>
  146. </template>
  147. <script>
  148. /*
  149. * author kcz
  150. * date 2019-11-20
  151. * description 表单设计器
  152. */
  153. import kHeader from "./module/header";
  154. import operatingArea from "./module/operatingArea";
  155. // import kFooter from "./module/footer";
  156. import kFormComponentPanel from "./module/formComponentPanel";
  157. import kJsonModal from "./module/jsonModal";
  158. import kCodeModal from "./module/codeModal";
  159. import collapseItem from "./module/collapseItem";
  160. import importJsonModal from "./module/importJsonModal";
  161. import queryModal from "./module/queryModal";
  162. import previewModal from "../KFormPreview/index.vue";
  163. import zhCN from "ant-design-vue/lib/locale-provider/zh_CN";
  164. import { Revoke } from "../core/revoke";
  165. import {
  166. basicsList,
  167. layoutList,
  168. customComponents
  169. } from "./config/formItemsConfig";
  170. import formItemProperties from "./module/formItemProperties";
  171. import formProperties from "./module/formProperties";
  172. import { getFormByBusinessTable, getTableColumnList } from "../api/api";
  173. export default {
  174. name: "KFormDesign",
  175. props: {
  176. title: {
  177. type: String,
  178. default: "表单设计器"
  179. },
  180. showHead: {
  181. type: Boolean,
  182. default: true
  183. },
  184. hideResetHint: {
  185. type: Boolean,
  186. default: false
  187. },
  188. toolbarsTop: {
  189. type: Boolean,
  190. default: false
  191. },
  192. toolbars: {
  193. type: Array,
  194. default: () => [
  195. "save",
  196. "preview",
  197. "importJson",
  198. "exportJson",
  199. "exportCode",
  200. "reset",
  201. "close",
  202. "undo",
  203. "redo"
  204. ]
  205. },
  206. showToolbarsText: {
  207. type: Boolean,
  208. default: false
  209. },
  210. fields: {
  211. type: Array,
  212. default: () => [
  213. "input",
  214. "textarea",
  215. "number",
  216. "select",
  217. "checkbox",
  218. "radio",
  219. "date",
  220. "time",
  221. "rate",
  222. "slider",
  223. "uploadFile",
  224. "uploadImg",
  225. "cascader",
  226. "treeSelect",
  227. "batch",
  228. "selectInputList",
  229. "editor",
  230. "switch",
  231. "button",
  232. "alert",
  233. "text",
  234. "html",
  235. "divider",
  236. "card",
  237. "tabs",
  238. "grid",
  239. "table"
  240. ]
  241. },
  242. hideModel: {
  243. // 隐藏数据字段
  244. type: Boolean,
  245. default: false
  246. }
  247. },
  248. data() {
  249. return {
  250. tableColumnList:[],//数据表字段集合
  251. formData: {}, //表单数据
  252. locale: zhCN,
  253. customComponents,
  254. activeKey: 1,
  255. updateTime: 0,
  256. updateRecordTime: 0,
  257. startType: "",
  258. revoke: null,
  259. recordList: [],
  260. redoList: [],
  261. noModel: [
  262. "button",
  263. "divider",
  264. "card",
  265. "grid",
  266. "tabs",
  267. "table",
  268. "alert",
  269. "text",
  270. "html"
  271. ],
  272. data: {
  273. list: [],
  274. config: {
  275. layout: "horizontal",
  276. labelCol: { xs: 4, sm: 4, md: 4, lg: 4, xl: 4, xxl: 4 },
  277. labelWidth: 100,
  278. labelLayout: "flex",
  279. wrapperCol: { xs: 18, sm: 18, md: 18, lg: 18, xl: 18, xxl: 18 },
  280. hideRequiredMark: false,
  281. customStyle: ""
  282. }
  283. },
  284. previewOptions: {
  285. width: 850
  286. },
  287. selectItem: {
  288. key: ""
  289. }
  290. };
  291. },
  292. components: {
  293. kHeader,
  294. // kFooter,
  295. operatingArea,
  296. collapseItem,
  297. kJsonModal,
  298. kCodeModal,
  299. importJsonModal,
  300. previewModal,
  301. kFormComponentPanel,
  302. formItemProperties,
  303. formProperties,
  304. queryModal
  305. },
  306. watch: {
  307. data: {
  308. handler(e) {
  309. this.$nextTick(() => {
  310. this.revoke.push(e);
  311. });
  312. },
  313. deep: true,
  314. immediate: true
  315. }
  316. },
  317. computed: {
  318. basicsArray() {
  319. // 计算需要显示的基础字段
  320. return basicsList.filter(item => this.fields.includes(item.type));
  321. },
  322. layoutArray() {
  323. // 计算需要显示的布局字段
  324. return layoutList.filter(item => this.fields.includes(item.type));
  325. },
  326. collapseDefaultActiveKey() {
  327. // 计算当前展开的控件列表
  328. const defaultActiveKey = window.localStorage.getItem(
  329. "collapseDefaultActiveKey"
  330. );
  331. if (defaultActiveKey) {
  332. return defaultActiveKey.split(",");
  333. } else {
  334. return ["1"];
  335. }
  336. }
  337. },
  338. methods: {
  339. generateKey(list, index) {
  340. // 生成key值
  341. const key = list[index].type + "_" + new Date().getTime();
  342. this.$set(list, index, {
  343. ...list[index],
  344. key,
  345. model: key
  346. });
  347. if (this.noModel.includes(list[index].type)) {
  348. // 删除不需要的model属性
  349. delete list[index].model;
  350. }
  351. },
  352. handleListPush(item) {
  353. if(this.formData.businessTable){
  354. // 双击控件按钮push到list
  355. // 生成key值
  356. if (!this.selectItem.key) {
  357. // 在没有选择表单时,将数据push到this.data.list
  358. const key = item.type + "_" + new Date().getTime();
  359. item = {
  360. ...item,
  361. key,
  362. model: key,
  363. };
  364. if (this.noModel.includes(item.type)) {
  365. // 删除不需要的model属性
  366. delete item.model;
  367. }
  368. const itemString = JSON.stringify(item);
  369. const record = JSON.parse(itemString);
  370. // 删除icon及compoent属性
  371. delete record.icon;
  372. delete record.component;
  373. this.data.list.push(record);
  374. this.handleSetSelectItem(record);
  375. return false;
  376. }
  377. this.$refs.KFCP.handleCopy(false, item);
  378. }else{
  379. this.$message.error("请先选择表单属性中的关联数据表")
  380. }
  381. },
  382. handleOpenJsonModal() {
  383. // 打开json预览模态框
  384. this.$refs.jsonModal.jsonData = this.data;
  385. this.$refs.jsonModal.formData = this.formData;
  386. this.$refs.jsonModal.visible = true;
  387. },
  388. handleOpenCodeModal() {
  389. // 打开代码预览模态框
  390. this.$refs.codeModal.jsonData = this.data;
  391. this.$refs.codeModal.visible = true;
  392. },
  393. handleOpenImportJsonModal() {
  394. // 打开json预览模态框
  395. this.$refs.importJsonModal.jsonData = this.data;
  396. this.$refs.importJsonModal.handleSetSelectItem = this.handleSetSelectItem;
  397. this.$refs.importJsonModal.visible = true;
  398. },
  399. handlePreview() {
  400. // 打开预览模态框
  401. this.$refs.previewModal.jsonData = this.data;
  402. this.$refs.previewModal.previewWidth = this.previewOptions.width;
  403. this.$refs.previewModal.visible = true;
  404. },
  405. handleQuery() {
  406. // 打开查询模态框
  407. this.$refs.queryModal.jsonData = this.data;
  408. this.$refs.queryModal.formData = this.formData;
  409. // this.$refs.queryModal.previewWidth = this.previewOptions.width;
  410. this.$refs.queryModal.visible = true;
  411. },
  412. handleReset() {
  413. // 清空
  414. if (this.hideResetHint) {
  415. this.formData = {};
  416. this.$refs.queryModal.formData = {};
  417. this.tableColumnList=[];
  418. // 不显示提示直接清空
  419. this.resetData();
  420. return;
  421. }
  422. this.$confirm({
  423. title: "警告",
  424. content: "是否确认清空内容?",
  425. okText: "是",
  426. okType: "danger",
  427. cancelText: "否",
  428. onOk: () => {
  429. this.resetData();
  430. }
  431. });
  432. },
  433. resetData() {
  434. this.data = {
  435. list: [],
  436. config: {
  437. layout: "horizontal",
  438. labelCol: { xs: 4, sm: 4, md: 4, lg: 4, xl: 4, xxl: 4 },
  439. labelWidth: 100,
  440. labelLayout: "flex",
  441. wrapperCol: { xs: 18, sm: 18, md: 18, lg: 18, xl: 18, xxl: 18 },
  442. hideRequiredMark: false,
  443. customStyle: ""
  444. }
  445. };
  446. this.handleSetSelectItem({ key: "" });
  447. this.$message.success("已清空");
  448. },
  449. handleSetSelectItem(record) {
  450. // 操作间隔不能低于100毫秒
  451. const newTime = new Date().getTime();
  452. if (newTime - this.updateTime < 100) {
  453. return false;
  454. }
  455. this.updateTime = newTime;
  456. // 设置selectItem的值
  457. this.selectItem = record;
  458. ///获取表字段的下拉数据
  459. //判断字段下拉数据是否有集合
  460. if(!this.tableColumnList||this.tableColumnList.length<=0){//没有则查询
  461. //获取表字段下拉数据接口
  462. getTableColumnList(this.formData.businessTable).then(res=>{
  463. this.tableColumnList=res.data.result;//表字段下拉数据
  464. });
  465. }
  466. // 判断是否选中控件,如果选中则弹出属性面板,否则关闭属性面板
  467. if (record.key) {
  468. this.startType = record.type;
  469. this.changeTab(2);
  470. } else {
  471. this.changeTab(1);
  472. }
  473. },
  474. /**
  475. * @description: 切换属性设置面板
  476. * @param {*}
  477. * @return {*}
  478. */
  479. changeTab(e) {
  480. this.activeKey = e;
  481. },
  482. /**
  483. * @Author: kcz
  484. * @description: 遍历json结构,获取所有字段
  485. * @param {*}
  486. * @return {*} Array
  487. */
  488. getFieldSchema() {
  489. const fields = [];
  490. const traverse = array => {
  491. array.forEach(element => {
  492. if (element.type === "grid" || element.type === "tabs") {
  493. // 栅格布局
  494. element.columns.forEach(item => {
  495. traverse(item.list);
  496. });
  497. } else if (element.type === "card") {
  498. // 卡片布局
  499. traverse(element.list);
  500. } else if (element.type === "batch") {
  501. // 动态表格内复制
  502. traverse(element.list);
  503. } else if (element.type === "table") {
  504. // 表格布局
  505. element.trs.forEach(item => {
  506. item.tds.forEach(val => {
  507. traverse(val.list);
  508. });
  509. });
  510. } else {
  511. if (element.model) {
  512. fields.push(element);
  513. }
  514. }
  515. });
  516. };
  517. traverse(this.data.list);
  518. return fields;
  519. },
  520. handleSetData(data) {
  521. // 用于父组件赋值
  522. try {
  523. if (typeof data !== "object") {
  524. return false;
  525. } else {
  526. this.data = data;
  527. // 导入json数据后,需要清除已选择key
  528. this.handleSetSelectItem({ key: "" });
  529. }
  530. return true;
  531. } catch (error) {
  532. console.error(error);
  533. return false;
  534. }
  535. },
  536. collapseChange(val) {
  537. // 点击collapse时,保存当前collapse状态
  538. window.localStorage.setItem("collapseDefaultActiveKey", val);
  539. },
  540. handleStart(type) {
  541. this.startType = type;
  542. },
  543. /**
  544. * @description: 撤销
  545. * @param {*}
  546. * @return {*}
  547. */
  548. handleUndo() {
  549. const record = this.revoke.undo();
  550. if (!record) {
  551. return false;
  552. }
  553. this.data = record;
  554. this.handleSetSelectItem({ key: "" });
  555. },
  556. /**
  557. * @description: 重做
  558. * @param {*}
  559. * @return {*}
  560. */
  561. handleRedo() {
  562. const record = this.revoke.redo();
  563. if (!record) {
  564. return false;
  565. }
  566. this.data = record;
  567. },
  568. handleSave() {
  569. // 保存函数
  570. this.$emit("save", JSON.stringify(this.data));
  571. },
  572. getValue() {
  573. // 获取数据
  574. return this.data;
  575. },
  576. handleClose() {
  577. this.$emit("close");
  578. }
  579. },
  580. created() {
  581. this.revoke = new Revoke();
  582. this.recordList = this.revoke.recordList;
  583. this.redoList = this.revoke.redoList;
  584. console.log(this.$route.query.tableName);
  585. //判断是否有接收到表明
  586. if (this.$route.query.tableName) {
  587. //有则根据表明查询表单json以及相关数据
  588. getFormByBusinessTable(this.$route.query.tableName).then(res => {
  589. if (res.data.success) {
  590. //获取Json字符串装json格式
  591. const editorJsonData = res.data.result.jsonContent;
  592. const jsonData = {};
  593. //获取json展示的数据
  594. jsonData.list = editorJsonData.list;
  595. jsonData.config = editorJsonData.config;
  596. jsonData.config.layout = editorJsonData.config.layout;
  597. this.data = jsonData;
  598. //当前填写数据存储,用于其他页面获取
  599. this.formData.businessTable = this.$route.query.tableName; //数据库表明
  600. this.formData.text = res.data.result.text; //表单标题
  601. // this.$message.success("查询成功");
  602. } else {
  603. this.$message.error(res.data.message);
  604. }
  605. });
  606. }
  607. }
  608. };
  609. //根据表名称获取表字段与表说明
  610. // function getTableColumnListShow(item,formData){
  611. // getTableColumnList(formData.businessTable).then(res=>{
  612. // if(res.data.success){
  613. // item.tableList=res.data.result;
  614. // }
  615. // });
  616. // item.tableList=[
  617. // {columnName:"123",columnComment:"123"}
  618. // ]
  619. // }
  620. </script>