Container.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. <template>
  2. <el-container class="fm2-container">
  3. <el-main class="fm2-main">
  4. <el-container>
  5. <el-aside style="width:300px;">
  6. <div class="components-list">
  7. <template v-if="basicFields.length">
  8. <div class="widget-cate">{{$t('fm.components.basic.title')}}</div>
  9. <draggable tag="ul" :list="basicComponents"
  10. v-bind="{group:{ name:'people', pull:'clone',put:false},sort:false, ghostClass: 'ghost'}"
  11. @end="handleMoveEnd"
  12. @start="handleMoveStart"
  13. :move="handleMove"
  14. >
  15. <li @click="handleField(item)" v-if="basicFields.indexOf(item.type)>=0" class="form-edit-widget-label" :class="{'no-put': item.type == 'divider'}" v-for="(item, index) in basicComponents" :key="index">
  16. <a>
  17. <i class="icon iconfont" :class="item.icon"></i>
  18. <span>{{item.name}}</span>
  19. </a>
  20. </li>
  21. </draggable>
  22. </template>
  23. <template v-if="advanceFields.length">
  24. <div class="widget-cate">{{$t('fm.components.advance.title')}}</div>
  25. <draggable tag="ul" :list="advanceComponents"
  26. v-bind="{group:{ name:'people', pull:'clone',put:false},sort:false, ghostClass: 'ghost'}"
  27. @end="handleMoveEnd"
  28. @start="handleMoveStart"
  29. :move="handleMove"
  30. >
  31. <li @click="handleField(item)" v-if="advanceFields.indexOf(item.type) >= 0" class="form-edit-widget-label" :class="{'no-put': item.type == 'table'}" v-for="(item, index) in advanceComponents" :key="index">
  32. <a>
  33. <i class="icon iconfont" :class="item.icon"></i>
  34. <span>{{item.name}}</span>
  35. </a>
  36. </li>
  37. </draggable>
  38. </template>
  39. <template v-if="layoutFields.length">
  40. <div class="widget-cate">{{$t('fm.components.layout.title')}}</div>
  41. <draggable tag="ul" :list="layoutComponents"
  42. v-bind="{group:{ name:'people', pull:'clone',put:false},sort:false, ghostClass: 'ghost'}"
  43. @end="handleMoveEnd"
  44. @start="handleMoveStart"
  45. :move="handleMove"
  46. >
  47. <li @click="handleField(item)" v-if="layoutFields.indexOf(item.type) >=0" class="form-edit-widget-label no-put" v-for="(item, index) in layoutComponents" :key="index">
  48. <a>
  49. <i class="icon iconfont" :class="item.icon"></i>
  50. <span>{{item.name}}</span>
  51. </a>
  52. </li>
  53. </draggable>
  54. </template>
  55. <template v-if="customFields.length">
  56. <div class="widget-cate">{{$t('fm.components.custom.title')}}</div>
  57. <draggable tag="ul" :list="customComponents"
  58. v-bind="{group:{ name:'people', pull:'clone',put:false},sort:false, ghostClass: 'ghost'}"
  59. @end="handleMoveEnd"
  60. @start="handleMoveStart"
  61. :move="handleMove"
  62. >
  63. <li @click="handleField(item)" class="form-edit-widget-label" v-for="(item, index) in customComponents" :key="index">
  64. <a>
  65. <i class="icon iconfont" :class="item.icon"></i>
  66. <span>{{item.name}}</span>
  67. </a>
  68. </li>
  69. </draggable>
  70. </template>
  71. </div>
  72. </el-aside>
  73. <el-container class="center-container" direction="vertical">
  74. <el-header class="btn-bar" style="height: 45px;">
  75. <slot name="action">
  76. </slot>
  77. <el-button type="text" size="medium" icon="el-icon-upload" @click="handleSave">{{$t('fm.actions.save')}}</el-button>
  78. <el-button type="text" :disabled="!undo" size="medium" icon="el-icon-back" @click="handleUndo">{{$t('fm.actions.undo')}}</el-button>
  79. <el-button type="text" :disabled="!redo" size="medium" icon="el-icon-right" @click="handleRedo">{{$t('fm.actions.redo')}}</el-button>
  80. <el-button v-if="upload" type="text" size="medium" icon="el-icon-upload2" @click="handleUpload">{{$t('fm.actions.import')}}</el-button>
  81. <el-button v-if="clearable" type="text" size="medium" icon="el-icon-delete" @click="handleClear">{{$t('fm.actions.clear')}}</el-button>
  82. <el-button v-if="preview" type="text" size="medium" icon="el-icon-view" @click="handlePreview">{{$t('fm.actions.preview')}}</el-button>
  83. <el-button v-if="generateJson" type="text" size="medium" icon="el-icon-tickets" @click="handleGenerateJson">{{$t('fm.actions.json')}}</el-button>
  84. <el-button v-if="generateCode" type="text" size="medium" icon="el-icon-document" @click="handleGenerateCode">{{$t('fm.actions.code')}}</el-button>
  85. </el-header>
  86. <el-main :class="{'widget-empty': widgetForm.list.length == 0}">
  87. <widget-form v-if="!resetJson" ref="widgetForm" :data="widgetForm" :select.sync="widgetFormSelect"></widget-form>
  88. </el-main>
  89. </el-container>
  90. <el-aside class="widget-config-container" style="width:350px;">
  91. <el-container>
  92. <el-header height="45px">
  93. <div class="config-tab" :class="{active: configTab=='widget'}" @click="handleConfigSelect('widget')">{{$t('fm.config.widget.title')}}</div>
  94. <div class="config-tab" :class="{active: configTab=='form'}" @click="handleConfigSelect('form')">{{$t('fm.config.form.title')}}</div>
  95. </el-header>
  96. <el-main class="config-content">
  97. <widget-config v-show="configTab=='widget'" :data="widgetFormSelect" :key="widgetFormSelect ? widgetFormSelect.key : 0"></widget-config>
  98. <form-config v-show="configTab=='form'" :data="widgetForm.config"></form-config>
  99. </el-main>
  100. </el-container>
  101. </el-aside>
  102. <cus-dialog
  103. :visible="previewVisible"
  104. @on-close="previewVisible = false"
  105. ref="widgetPreview"
  106. form
  107. :title="$t('fm.actions.preview')"
  108. fullscreen
  109. >
  110. <generate-form style="margin: 0 auto;" insite="true" v-if="previewVisible && (previewForm.config.ui == 'element' || !previewForm.config.ui)" :data="previewForm" :value="widgetModels" :remote="remoteFuncs" ref="generateForm">
  111. <template v-slot:blank="scope">
  112. Width<el-input v-model="scope.model.blank.width" style="width: 100px"></el-input>
  113. Height:<el-input v-model="scope.model.blank.height" style="width: 100px"></el-input>
  114. </template>
  115. </generate-form>
  116. <antd-generate-form style="margin: 0 auto;" insite="true" v-if="previewVisible && previewForm.config.ui == 'antd'" :data="previewForm" :value="widgetModels" :remote="remoteFuncs" ref="generateAntForm">
  117. <template v-slot:blank="scope">
  118. <a-input
  119. v-decorator="[
  120. 'blank',
  121. {
  122. initialValue: scope.model.blank
  123. }
  124. ]"
  125. style="width: 100px"></a-input>
  126. </template>
  127. </antd-generate-form>
  128. <template slot="action">
  129. <el-button type="primary" @click="handleTest">{{$t('fm.actions.getData')}}</el-button>
  130. <el-button @click="handleReset">{{$t('fm.actions.reset')}}</el-button>
  131. </template>
  132. </cus-dialog>
  133. <cus-dialog
  134. :visible="uploadVisible"
  135. @on-close="uploadVisible = false"
  136. @on-submit="handleUploadJson"
  137. ref="uploadJson"
  138. width="800px"
  139. form
  140. :title="$t('fm.actions.import')"
  141. >
  142. <el-alert type="info" :title="$t('fm.description.uploadJsonInfo')"></el-alert>
  143. <code-editor height="400px" mode="json" v-model="jsonEg"></code-editor>
  144. </cus-dialog>
  145. <cus-dialog
  146. :visible="jsonVisible"
  147. @on-close="jsonVisible = false"
  148. ref="jsonPreview"
  149. width="800px"
  150. form
  151. :title="$t('fm.actions.json')"
  152. >
  153. <code-editor height="400px" mode="json" v-model="jsonTemplate"></code-editor>
  154. <template slot="action">
  155. <el-button type="primary" class="json-btn" :data-clipboard-text="jsonCopyValue">{{$t('fm.actions.copyData')}}</el-button>
  156. </template>
  157. </cus-dialog>
  158. <cus-dialog
  159. :visible="codeVisible"
  160. @on-close="codeVisible = false"
  161. ref="codePreview"
  162. width="800px"
  163. form
  164. :title="$t('fm.actions.code')"
  165. >
  166. <el-tabs type="border-card" style="box-shadow: none;" v-model="codeActiveName">
  167. <el-tab-pane label="Vue Component" name="vue">
  168. <code-editor height="450px" mode="html" v-model="vueTemplate"></code-editor>
  169. </el-tab-pane>
  170. <el-tab-pane label="HTML" name="html">
  171. <code-editor height="450px" mode="html" v-model="htmlTemplate"></code-editor>
  172. </el-tab-pane>
  173. </el-tabs>
  174. <template slot="action">
  175. <el-button type="primary" class="code-btn" :data-clipboard-text="codeCopyValue">{{$t('fm.actions.copyData')}}</el-button>
  176. </template>
  177. </cus-dialog>
  178. </el-container>
  179. </el-main>
  180. </el-container>
  181. </template>
  182. <script>
  183. import Draggable from 'vuedraggable'
  184. import WidgetConfig from './WidgetConfig'
  185. import FormConfig from './FormConfig'
  186. import WidgetForm from './WidgetForm'
  187. import CusDialog from './CusDialog'
  188. import GenerateForm from './GenerateForm'
  189. import AntdGenerateForm from './AntdvGenerator/GenerateForm'
  190. import Clipboard from 'clipboard'
  191. import CodeEditor from '../components/CodeEditor'
  192. import {basicComponents, layoutComponents, advanceComponents} from './componentsConfig.js'
  193. import {loadJs, loadCss} from '../util/index.js'
  194. import { EventBus } from '../util/event-bus.js'
  195. import request from '../util/request.js'
  196. import generateCode from './generateCode.js'
  197. import historyManager from '../util/history-manager.js'
  198. import _ from 'lodash'
  199. import Vue from 'vue'
  200. import customComponent from '../views/tool/model/CustomComponent'
  201. import { updateModel } from "@/api/tool/model";
  202. export default {
  203. name: 'fm-making-form',
  204. components: {
  205. Draggable,
  206. WidgetConfig,
  207. FormConfig,
  208. WidgetForm,
  209. CusDialog,
  210. GenerateForm,
  211. CodeEditor,
  212. AntdGenerateForm
  213. },
  214. props: {
  215. preview: {
  216. type: Boolean,
  217. default: false
  218. },
  219. generateCode: {
  220. type: Boolean,
  221. default: false
  222. },
  223. generateJson: {
  224. type: Boolean,
  225. default: false
  226. },
  227. upload: {
  228. type: Boolean,
  229. default: false
  230. },
  231. clearable: {
  232. type: Boolean,
  233. default: false
  234. },
  235. basicFields: {
  236. type: Array,
  237. default: () => ['input', 'textarea', 'number', 'radio', 'checkbox', 'time', 'date', 'rate', 'color', 'select', 'switch', 'slider', 'text', 'html']
  238. },
  239. advanceFields: {
  240. type: Array,
  241. default: () => ['blank', 'component', 'fileupload', 'imgupload', 'editor', 'cascader', 'table']
  242. },
  243. layoutFields: {
  244. type: Array,
  245. default: () => ['grid', 'tabs', 'divider']
  246. },
  247. customFields: {
  248. type: Array,
  249. default: () => []
  250. }
  251. },
  252. data () {
  253. return {
  254. basicComponents,
  255. layoutComponents,
  256. advanceComponents,
  257. customComponents: [],
  258. resetJson: false,
  259. widgetForm: {
  260. list: [],
  261. config: {
  262. labelWidth: 100,
  263. labelPosition: 'right',
  264. size: 'small',
  265. customClass: '',
  266. ui: 'element',
  267. layout: 'horizontal',
  268. labelCol: 3,
  269. width: '100%'
  270. },
  271. },
  272. previewForm: {},
  273. configTab: 'widget',
  274. widgetFormSelect: null,
  275. previewVisible: false,
  276. jsonVisible: false,
  277. codeVisible: false,
  278. uploadVisible: false,
  279. remoteFuncs: {
  280. func_test (resolve) {
  281. setTimeout(() => {
  282. const options = [
  283. {id: '1', name: '1111'},
  284. {id: '2', name: '2222'},
  285. {id: '3', name: '3333'}
  286. ]
  287. resolve(options)
  288. }, 2000)
  289. },
  290. funcGetToken (resolve) {
  291. request.get('http://tools-server.making.link/api/uptoken').then(res => {
  292. resolve(res.uptoken)
  293. })
  294. },
  295. upload_callback (response, file, fileList) {
  296. console.log('callback', response, file, fileList)
  297. },
  298. },
  299. widgetModels: {},
  300. blank: '',
  301. htmlTemplate: '',
  302. jsonTemplate: '',
  303. vueTemplate: '',
  304. uploadEditor: null,
  305. jsonCopyValue: '',
  306. jsonClipboard: null,
  307. jsonEg: `{
  308. "list": [],
  309. "config": {
  310. "labelWidth": 100,
  311. "labelPosition": "right",
  312. "size": "small",
  313. "customClass": "",
  314. "ui": "element",
  315. "layout": "horizontal",
  316. "labelCol": 3,
  317. "width": "100%"
  318. }
  319. }`,
  320. codeCopyValue: '',
  321. codeClipboard: null,
  322. codeActiveName: 'vue',
  323. undo: false,
  324. redo: false,
  325. form:{}
  326. }
  327. },
  328. created () {
  329. // alert(Vue.config.lang)
  330. const modelId = this.$route.params && this.$route.params.modelId;
  331. // alert(id)
  332. this.form.modelId = modelId;
  333. this._loadComponents();
  334. this.uploadVisible = false
  335. if(Object.keys(sessionStorage.getItem("modelData")).length!=0){
  336. this.setJSON(JSON.parse(sessionStorage.getItem("modelData")))
  337. }
  338. // try {
  339. // this.setJSON(JSON.parse(sessionStorage.getItem("jsonData")))
  340. // } catch (e) {
  341. // this.$message.error(e.message)
  342. // // this.$refs.uploadJson.end()
  343. // }
  344. // var self = this;
  345. // self.widgetForm = JSON.parse(sessionStorage.getItem("jsonData"));
  346. },
  347. mounted () {
  348. const _this = this
  349. historyManager.clear().then(() => {
  350. EventBus.$on('on-history-add', () => {
  351. console.log('xxx', this.widgetFormSelect)
  352. historyManager.add(this.widgetForm, (this.widgetFormSelect && this.widgetFormSelect.key) ? this.widgetFormSelect.key : '').then(() => {
  353. _this.undo = true
  354. _this.redo = false
  355. })
  356. })
  357. })
  358. },
  359. methods: {
  360. handleSave () {
  361. // alert(JSON.stringify(this.widgetForm.config))
  362. // this.jsonTemplate = this.widgetForm
  363. // alert(this.widgetForm.options.remoteOption)
  364. var modelData = JSON.stringify(this.widgetForm);
  365. var modelTable = this.widgetForm.config.table;
  366. this.form.modelData = modelData;
  367. this.form.modelTable = modelTable;
  368. // alert(jsonData)
  369. // sessionStorage.setItem("jsonData", jsonData);
  370. // this.$router.push("/tool/common");
  371. // if(this.form.table==null || this.form.table.length()==0){
  372. // this.$message.error("请选择关联表")
  373. // return false
  374. // }
  375. updateModel(this.form).then(response => {
  376. this.msgSuccess("保存成功");
  377. this.close();
  378. });
  379. },
  380. /** 关闭按钮 */
  381. close() {
  382. this.$store.dispatch("tagsView/delView", this.$route);
  383. this.$router.push({ path: "/model", query: { t: Date.now()}})
  384. },
  385. // handleGoGithub () {
  386. // window.location.href = 'https://github.com/GavinZhuLei/vue-form-making'
  387. // },
  388. handleConfigSelect (value) {
  389. this.configTab = value
  390. },
  391. handleMoveEnd (evt) {
  392. console.log('end', evt)
  393. },
  394. handleMoveStart ({oldIndex}) {
  395. console.log('start', oldIndex, this.basicComponents)
  396. },
  397. handleMove () {
  398. return true
  399. },
  400. handlePreview () {
  401. console.log(this.widgetForm)
  402. this.previewForm = _.cloneDeep(this.widgetForm)
  403. this.previewVisible = true
  404. },
  405. handleReset () {
  406. const $form = this.previewForm.config.ui == 'element' ?
  407. this.$refs.generateForm : this.$refs.generateAntForm
  408. $form.reset()
  409. },
  410. handleTest () {
  411. const $form = this.previewForm.config.ui == 'element' ?
  412. this.$refs.generateForm : this.$refs.generateAntForm
  413. $form.getData().then(data => {
  414. this.jsonVisible = true
  415. this.jsonTemplate = data
  416. this.$nextTick(() => {
  417. if (!this.jsonClipboard) {
  418. this.jsonClipboard = new Clipboard('.json-btn')
  419. this.jsonClipboard.on('success', (e) => {
  420. this.$message.success(this.$t('fm.message.copySuccess'))
  421. })
  422. }
  423. this.jsonCopyValue = JSON.stringify(data)
  424. })
  425. this.$refs.widgetPreview.end()
  426. }).catch(e => {
  427. this.$message.error(e)
  428. this.$refs.widgetPreview.end()
  429. })
  430. },
  431. handleGenerateJson () {
  432. this.jsonVisible = true
  433. this.jsonTemplate = this.widgetForm
  434. console.log(JSON.stringify(this.widgetForm))
  435. this.$nextTick(() => {
  436. if (!this.jsonClipboard) {
  437. this.jsonClipboard = new Clipboard('.json-btn')
  438. this.jsonClipboard.on('success', (e) => {
  439. this.$message.success(this.$t('fm.message.copySuccess'))
  440. })
  441. }
  442. this.jsonCopyValue = JSON.stringify(this.widgetForm)
  443. })
  444. },
  445. handleGenerateCode () {
  446. this.codeVisible = true
  447. this.htmlTemplate = generateCode(JSON.stringify(this.widgetForm), 'html', this.widgetForm.config.ui)
  448. this.vueTemplate = generateCode(JSON.stringify(this.widgetForm), 'vue', this.widgetForm.config.ui)
  449. this.$nextTick(() => {
  450. if (!this.codeClipboard) {
  451. this.codeClipboard = new Clipboard('.code-btn')
  452. this.codeClipboard.on('success', (e) => {
  453. this.$message.success(this.$t('fm.message.copySuccess'))
  454. })
  455. }
  456. this.codeCopyValue = this.codeActiveName == 'vue' ? this.vueTemplate : this.htmlTemplate
  457. })
  458. },
  459. handleUpload () {
  460. this.uploadVisible = true
  461. },
  462. handleUploadJson () {
  463. try {
  464. this.setJSON(this.jsonEg)
  465. this.uploadVisible = false
  466. } catch (e) {
  467. this.$message.error(e.message)
  468. this.$refs.uploadJson.end()
  469. }
  470. },
  471. handleClear () {
  472. this.widgetForm = {
  473. ...this.widgetForm,
  474. list: [],
  475. }
  476. this.widgetFormSelect = {}
  477. this.$nextTick(() => {
  478. EventBus.$emit('on-history-add')
  479. })
  480. },
  481. clear () {
  482. this.handleClear()
  483. },
  484. getJSON () {
  485. return this.widgetForm
  486. },
  487. getHtml () {
  488. return generateCode(JSON.stringify(this.widgetForm))
  489. },
  490. setJSON (json) {
  491. if (typeof json === 'string') {
  492. json = JSON.parse(json)
  493. }
  494. this.widgetForm = json
  495. if (json.list.length> 0) {
  496. this.widgetFormSelect = json.list[0]
  497. } else {
  498. this.widgetFormSelect = {}
  499. }
  500. this.$nextTick(() => { EventBus.$emit('on-history-add') })
  501. },
  502. handleInput (val) {
  503. console.log(val)
  504. this.blank = val
  505. },
  506. handleField (item) {
  507. console.log(item)
  508. EventBus.$emit('on-field-add', item)
  509. },
  510. handleUndo () {
  511. historyManager.updateLatest(this.widgetForm, (this.widgetFormSelect && this.widgetFormSelect.key) ? this.widgetFormSelect.key : '').then(() => {
  512. historyManager.undo().then((data) => {
  513. this.widgetForm = {...data.data}
  514. this.widgetFormSelect = this._findWidgetItem(this.widgetForm.list, data.key)
  515. this.undo = data.undo
  516. this.redo = data.redo
  517. })
  518. })
  519. },
  520. handleRedo () {
  521. historyManager.redo().then((data) => {
  522. this.widgetForm = {...data.data}
  523. this.widgetFormSelect = this._findWidgetItem(this.widgetForm.list, data.key)
  524. this.undo = data.undo
  525. this.redo = data.redo
  526. })
  527. },
  528. _findWidgetItem (list, key) {
  529. const index = list.findIndex(item => item.key == key)
  530. if (index >= 0) {
  531. return list[index]
  532. } else {
  533. for (let m = 0; m < list.length; m++) {
  534. const item = list[m]
  535. if (item.type === 'grid') {
  536. for (let i = 0; i < item.columns.length; i++) {
  537. return this._findWidgetItem(item.columns[i].list, key)
  538. }
  539. }
  540. if (item.type === 'table') {
  541. return this._findWidgetItem(item.tableColumns, key)
  542. }
  543. if (item.type === 'tabs') {
  544. for (let i = 0; i < item.tabs.length; i++) {
  545. return this._findWidgetItem(item.tabs[i].list, key)
  546. }
  547. }
  548. }
  549. return {}
  550. }
  551. },
  552. _loadComponents () {
  553. this.basicComponents = this.basicComponents.map(item => {
  554. return {
  555. ...item,
  556. name: this.$t(`fm.components.fields.${item.type}`)
  557. }
  558. })
  559. this.advanceComponents = this.advanceComponents.map(item => {
  560. return {
  561. ...item,
  562. name: this.$t(`fm.components.fields.${item.type}`)
  563. }
  564. })
  565. this.layoutComponents = this.layoutComponents.map(item => {
  566. return {
  567. ...item,
  568. name: this.$t(`fm.components.fields.${item.type}`)
  569. }
  570. })
  571. this.customComponents = this.customFields.map(item => {
  572. return {
  573. ...item,
  574. type: 'custom',
  575. icon: 'icon-zidingyi',
  576. options: {...item.options}
  577. }
  578. })
  579. }
  580. },
  581. watch: {
  582. '$lang': function (val) {
  583. this._loadComponents()
  584. },
  585. codeActiveName (val) {
  586. this.codeCopyValue = this.codeActiveName == 'vue' ? this.vueTemplate : this.htmlTemplate
  587. }
  588. }
  589. }
  590. </script>
  591. <style lang="scss">
  592. .widget-empty{
  593. background-position: 50%;
  594. }
  595. .custom1 .el-col{
  596. border: 1px solid #ccc;
  597. overflow: hidden;
  598. padding: 5px;
  599. // margin-right:-1px;
  600. // margin-bottom:-1px;
  601. margin-right: -1px;
  602. margin-bottom: -1px;
  603. }
  604. .custom .el-col{
  605. border-top: 1px solid #ccc;
  606. border-left: 1px solid #ccc;
  607. }
  608. </style>