edit.vue 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. <script lang="ts">
  2. import Vue from 'vue'
  3. import guide from '@/views/guide/index'
  4. import draggable from 'vuedraggable'
  5. import countryCode from '@/lib/countryCode.json'
  6. import {
  7. pcTextArr,
  8. pcaTextArr
  9. } from "element-china-area-data"
  10. export default Vue.extend({
  11. name: 'Edit',
  12. components: {
  13. draggable,
  14. guide
  15. },
  16. data() {
  17. return {
  18. compList: [
  19. {
  20. type: 'tips',
  21. label: '将我拖拽至右边试试吧',
  22. width: 6,
  23. required: false
  24. },
  25. {
  26. type: 'input',
  27. label: '请在下方输入内容',
  28. value: '',
  29. width: 6,
  30. placeholder: '请输入',
  31. pattern: '^[\\s\\S]*$',
  32. required: false
  33. },
  34. {
  35. type: 'phone',
  36. label: '请在下方输入手机号码',
  37. value: '',
  38. country: 'CN',
  39. width: 6,
  40. placeholder: '请输入手机号',
  41. required: false
  42. },
  43. {
  44. type: 'email',
  45. label: '请在下方输入邮箱',
  46. value: '',
  47. width: 6,
  48. placeholder: '请输入邮箱',
  49. codePlaceholder: '请输入验证码',
  50. required: false
  51. },
  52. {
  53. type: 'select',
  54. label: '请在下方选择内容',
  55. value: '',
  56. width: 6,
  57. options: [
  58. { label: '东坡肉', value: '东坡肉' },
  59. { label: '酱肘子', value: '酱肘子' },
  60. { label: '白切鸡', value: '白切鸡' }
  61. ],
  62. placeholder: '请选择',
  63. required: false
  64. },
  65. {
  66. type: 'checkbox',
  67. label: '请选择一至多项内容',
  68. value: '',
  69. width: 6,
  70. options: [
  71. { label: '东坡肉', value: '东坡肉' },
  72. { label: '酱肘子', value: '酱肘子' },
  73. { label: '白切鸡', value: '白切鸡' }
  74. ],
  75. required: false
  76. },
  77. {
  78. type: 'radio',
  79. label: '请选择一项内容',
  80. value: '',
  81. width: 6,
  82. options: [
  83. { label: '东坡肉', value: '东坡肉' },
  84. { label: '酱肘子', value: '酱肘子' },
  85. { label: '白切鸡', value: '白切鸡' }
  86. ],
  87. required: false
  88. },
  89. {
  90. type: 'number',
  91. label: '请在下方输入数字',
  92. max: 10,
  93. min: 0,
  94. step: 1,
  95. value: 0,
  96. width: 6,
  97. required: false
  98. },
  99. {
  100. type: 'slider',
  101. label: '请在下方滑动',
  102. max: 10,
  103. min: 0,
  104. step: 1,
  105. value: 0,
  106. width: 6,
  107. required: false
  108. },
  109. {
  110. type: 'textarea',
  111. label: '请在下方输入内容',
  112. value: '',
  113. maxRows: 4,
  114. minRows: 2,
  115. width: 6,
  116. placeholder: '请输入',
  117. required: false,
  118. pattern: '^[\\s\\S]*$'
  119. },
  120. {
  121. type: 'region',
  122. label: '请在下方选择位置',
  123. range: 2,
  124. width: 6,
  125. value: [],
  126. placeholder: '请选择所在区域',
  127. required: false
  128. },
  129. {
  130. type: 'time',
  131. label: '请在下方选择时间',
  132. value: '',
  133. width: 6,
  134. placeholder: '请选择时间',
  135. required: false
  136. },
  137. {
  138. type: 'timeRange',
  139. label: '请在下方选择时间范围',
  140. value: '',
  141. width: 6,
  142. rangeSeparator: '至',
  143. placeholder: '开始时间',
  144. endPlaceholder: '结束时间',
  145. required: false
  146. },
  147. {
  148. type: 'date',
  149. label: '请在下方选择日期',
  150. value: '',
  151. width: 6,
  152. placeholder: '请选择日期',
  153. required: false
  154. },
  155. {
  156. type: 'dateRange',
  157. label: '请在下方选择日期范围',
  158. value: '',
  159. width: 6,
  160. rangeSeparator: '至',
  161. placeholder: '开始日期',
  162. endPlaceholder: '结束日期',
  163. required: false
  164. }
  165. ],
  166. formData: [],
  167. trashData: [],
  168. formInfo: {
  169. name: '未命名表单',
  170. desc: '从右侧拖动组件到表单区域'
  171. },
  172. isDrag: false,
  173. currentKey: '',
  174. currentData: {},
  175. hoverKey: '',
  176. selectInput: '',
  177. pcTextArr,
  178. pcaTextArr,
  179. countryCode
  180. }
  181. },
  182. methods: {
  183. choseComp(element) {
  184. this.currentKey = element.key
  185. this.currentData = element
  186. },
  187. removeComp(element) {
  188. this.$confirm('确定删除吗?', {
  189. confirmButtonText: '确定',
  190. cancelButtonText: '取消',
  191. type: 'warning',
  192. callback: action => {
  193. if (action === 'confirm') {
  194. let index = this.getIndexByKey(element.key)
  195. this.formData.splice(index, 1)
  196. this.currentKey = ''
  197. }
  198. }
  199. })
  200. },
  201. addSelectItem() {
  202. if (this.selectInput.length === 0) { return }
  203. this.currentData.options.push({
  204. label: this.selectInput,
  205. value: this.selectInput
  206. })
  207. this.selectInput = ''
  208. },
  209. removeSelectItem(index) {
  210. this.currentData.options.splice(index, 1)
  211. },
  212. cloneItem(item) {
  213. const newItem = JSON.parse(JSON.stringify(item))
  214. newItem.key = newItem.type + new Date().getTime()
  215. return newItem
  216. },
  217. removeTrash(element){
  218. let index = this.getIndexByKey(element.key)
  219. this.trashData.splice(index,1)
  220. },
  221. hover(element) {
  222. if (this.isDrag) return
  223. this.hoverKey = element.key
  224. },
  225. blur(element) {
  226. this.hoverKey = ''
  227. },
  228. getIndexByKey(key) {
  229. return this.formData.indexOf(this.formData.find(item => item.key === key))
  230. },
  231. handleStart() {
  232. this.isDrag = true
  233. this.formData.forEach(item => {
  234. item.hover = false
  235. })
  236. },
  237. handleEnd() {
  238. this.isDrag = false
  239. }
  240. }
  241. })
  242. </script>
  243. <template>
  244. <div class="main-box">
  245. <div class="comp-lib">
  246. <div class="title">
  247. 组件库
  248. </div>
  249. <div class="list">
  250. <draggable v-model="compList" :options="{sort:false}" :group="{name:'form',put:false,pull:'clone'}" :clone="cloneItem" class="drag-list">
  251. <transition-group class="drag-cont">
  252. <div v-for="(element) in compList" :key="element.type">
  253. <div v-if="element.type==='tips'" :class="[element.type,'form-item']">
  254. <div class="tips">{{ element.label }}</div>
  255. </div>
  256. <div v-if="element.type==='input'" :class="[element.type,'form-item']">
  257. <div class="tips">{{ element.label }}</div>
  258. <el-input :value="element.value" :placeholder="element.placeholder" />
  259. </div>
  260. <div v-if="element.type==='email'" :class="[element.type,'form-item']">
  261. <div class="tips">{{ element.label }}</div>
  262. <div>
  263. <el-input v-model="element.value" :placeholder="element.placeholder" />
  264. </div>
  265. <div class="code-input">
  266. <el-input v-model="element.value" :placeholder="element.codePlaceholder" />
  267. <el-button>获取验证码</el-button>
  268. </div>
  269. </div>
  270. <div v-if="element.type==='phone'" :class="[element.type,'form-item']">
  271. <div class="tips">{{ element.label }}</div>
  272. <div class="phone-input">
  273. <el-select v-model="element.country" :placeholder="element.placeholder">
  274. <el-option v-for="(item,index) in countryCode" :key="item.country_code+index" :label="'+'+item.phone_code+'('+item.chinese_name+')'" :value="item.country_code" />
  275. </el-select>
  276. <el-input v-model="element.value" :placeholder="element.placeholder" />
  277. </div>
  278. </div>
  279. <div v-if="element.type==='select'" :class="[element.type,'form-item']">
  280. <div class="tips">{{ element.label }}</div>
  281. <el-select :value="element.value" :placeholder="element.placeholder">
  282. <el-option v-for="item in element.options" :key="item.value" :label="item.label" :value="item.value" />
  283. </el-select>
  284. </div>
  285. <div v-if="element.type==='checkbox'" :class="[element.type,'form-item']">
  286. <div class="tips">{{ element.label }}</div>
  287. <el-checkbox-group :value="element.value">
  288. <el-checkbox v-for="item in element.options" :key="item.value" :label="item.label" />
  289. </el-checkbox-group>
  290. </div>
  291. <div v-if="element.type==='radio'" :class="[element.type,'form-item']">
  292. <div class="tips">{{ element.label }}</div>
  293. <el-radio-group :value="element.value">
  294. <el-radio v-for="item in element.options" :key="item.value" :label="item.label" />
  295. </el-radio-group>
  296. </div>
  297. <div v-if="element.type==='number'" :class="[element.type,'form-item']">
  298. <div class="tips">{{ element.label }}</div>
  299. <el-input-number :value="element.value" :max="element.max" :min="element.min" :step="element.step" prefix-icon="el-icon-user" :placeholder="element.placeholder" />
  300. </div>
  301. <div v-if="element.type==='slider'" :class="[element.type,'form-item']">
  302. <div class="tips">{{ element.label }}</div>
  303. <el-slider :value="element.value" :max="element.max" :min="element.min" :step="element.step" prefix-icon="el-icon-user" :placeholder="element.placeholder" />
  304. </div>
  305. <div v-if="element.type==='textarea'" :class="[element.type,'form-item']">
  306. <div class="tips">{{ element.label }}</div>
  307. <el-input :value="element.value" type="textarea" :placeholder="element.placeholder" :autosize="{ minRows: element.minRows, maxRows: element.maxRows}" />
  308. </div>
  309. <div v-if="element.type==='region'" :class="[element.type,'form-item']">
  310. <div class="tips">{{ element.label }}</div>
  311. <el-cascader
  312. :placeholder="element.placeholder"
  313. size="large"
  314. :options="element.range===2?pcTextArr:pcaTextArr"
  315. value="element.value">
  316. </el-cascader>
  317. </div>
  318. <div v-if="element.type==='time'" :class="[element.type,'form-item']">
  319. <div class="tips">{{ element.label }}</div>
  320. <el-time-picker :value="element.value" :placeholder="element.placeholder" />
  321. </div>
  322. <div v-if="element.type==='timeRange'" :class="[element.type,'form-item']">
  323. <div class="tips">{{ element.label }}</div>
  324. <el-time-picker is-range :value="element.value" :start-placeholder="element.placeholder" :end-placeholder="element.endPlaceholder" :range-separator="element.rangeSeparator" />
  325. </div>
  326. <div v-if="element.type==='date'" :class="[element.type,'form-item']">
  327. <div class="tips">{{ element.label }}</div>
  328. <el-date-picker :value="element.value" :placeholder="element.placeholder" />
  329. </div>
  330. <div v-if="element.type==='dateRange'" :class="[element.type,'form-item']">
  331. <div class="tips">{{ element.label }}</div>
  332. <el-date-picker :value="element.value" type="daterange" :start-placeholder="element.placeholder" :end-placeholder="element.endPlaceholder" :range-separator="element.rangeSeparator" />
  333. </div>
  334. </div>
  335. </transition-group>
  336. </draggable>
  337. </div>
  338. </div>
  339. <div class="form-view">
  340. <div class="scroll-view">
  341. <div class="form-cont">
  342. <div :class="['form-head',getIndexByKey(currentKey) === -1?'active':'']" @click="currentKey=''">
  343. <div class="title">{{ formInfo.name }}</div>
  344. <div class="tips">{{ formInfo.desc }}</div>
  345. </div>
  346. <draggable v-model="formData" :group="{name:'form'}" :options="{sort:true,animation:300}" class="form-body" @start="handleStart" @end="handleEnd">
  347. <transition-group class="drag-cont">
  348. <div v-for="element in formData" :key="element.key" :style="{gridColumn:'span '+element.width}" :class="[element.required?'required':'']" @click="choseComp(element)" @mouseenter="hover(element)" @mouseleave="blur(element)">
  349. <div v-if="element.type==='tips'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  350. <div class="tips">{{ element.label }}</div>
  351. </div>
  352. <div v-if="element.type==='input'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  353. <div class="tips">{{ element.label }}</div>
  354. <el-input :value="element.value" :placeholder="element.placeholder" />
  355. </div>
  356. <div v-if="element.type==='email'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  357. <div class="tips">{{ element.label }}</div>
  358. <div>
  359. <el-input v-model="element.value" :placeholder="element.placeholder" />
  360. </div>
  361. <div class="code-input">
  362. <el-input v-model="element.value" :placeholder="element.codePlaceholder" />
  363. <el-button>获取验证码</el-button>
  364. </div>
  365. </div>
  366. <div v-if="element.type==='phone'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  367. <div class="tips">{{ element.label }}</div>
  368. <div class="phone-input">
  369. <el-select v-model="element.country" :placeholder="element.placeholder">
  370. <el-option v-for="(item,index) in countryCode" :key="item.country_code+index" :label="'+'+item.phone_code+'('+item.chinese_name+')'" :value="item.country_code" />
  371. </el-select>
  372. <el-input v-model="element.value" :placeholder="element.placeholder" />
  373. </div>
  374. </div>
  375. <div v-if="element.type==='select'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  376. <div class="tips">{{ element.label }}</div>
  377. <el-select :value="element.value" :placeholder="element.placeholder">
  378. <el-option v-for="item in element.options" :key="item.value" :label="item.label" :value="item.value" />
  379. </el-select>
  380. </div>
  381. <div v-if="element.type==='checkbox'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  382. <div class="tips">{{ element.label }}</div>
  383. <el-checkbox-group :value="element.value">
  384. <el-checkbox v-for="item in element.options" :key="item.value" :label="item.label" />
  385. </el-checkbox-group>
  386. </div>
  387. <div v-if="element.type==='radio'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  388. <div class="tips">{{ element.label }}</div>
  389. <el-radio-group :value="element.value">
  390. <el-radio v-for="item in element.options" :key="item.value" :label="item.label" />
  391. </el-radio-group>
  392. </div>
  393. <div v-if="element.type==='number'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  394. <div class="tips">{{ element.label }}</div>
  395. <el-input-number :value="element.value" :max="element.max" :min="element.min" :step="element.step" prefix-icon="el-icon-user" :placeholder="element.placeholder" />
  396. </div>
  397. <div v-if="element.type==='slider'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  398. <div class="tips">{{ element.label }}</div>
  399. <el-slider :value="element.value" :max="element.max" :min="element.min" :step="element.step" prefix-icon="el-icon-user" :placeholder="element.placeholder" />
  400. </div>
  401. <div v-if="element.type==='textarea'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  402. <div class="tips">{{ element.label }}</div>
  403. <el-input :value="element.value" type="textarea" :placeholder="element.placeholder" :autosize="{ minRows: element.minRows, maxRows: element.maxRows}" />
  404. </div>
  405. <div v-if="element.type==='region'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  406. <div class="tips">{{ element.label }}</div>
  407. <el-cascader
  408. :placeholder="element.placeholder"
  409. size="large"
  410. :options="element.range===2?pcTextArr:pcaTextArr"
  411. value="element.value">
  412. </el-cascader>
  413. </div>
  414. <div v-if="element.type==='time'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  415. <div class="tips">{{ element.label }}</div>
  416. <el-time-picker :value="element.value" :placeholder="element.placeholder" />
  417. </div>
  418. <div v-if="element.type==='timeRange'" :class="[element.type,'view','form-item',{'active':element.key===hoverKey||element.key===currentKey}]">
  419. <div class="tips">{{ element.label }}</div>
  420. <el-time-picker is-range :value="element.value" :start-placeholder="element.placeholder" :end-placeholder="element.endPlaceholder" :range-separator="element.rangeSeparator" />
  421. </div>
  422. <div v-if="element.type==='date'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  423. <div class="tips">{{ element.label }}</div>
  424. <el-date-picker :value="element.value" :placeholder="element.placeholder" />
  425. </div>
  426. <div v-if="element.type==='dateRange'" :class="[element.type,'form-item','view',{'active':element.key===hoverKey||element.key===currentKey}]">
  427. <div class="tips">{{ element.label }}</div>
  428. <el-date-picker :value="element.value" type="daterange" :start-placeholder="element.placeholder" :end-placeholder="element.endPlaceholder" :range-separator="element.rangeSeparator" />
  429. </div>
  430. </div>
  431. </transition-group>
  432. </draggable>
  433. </div>
  434. </div>
  435. </div>
  436. <div class="comp-edit">
  437. <template v-if="getIndexByKey(currentKey) === -1">
  438. <div class="button">
  439. <el-button type="primary">保存表单</el-button>
  440. </div>
  441. <div class="title">表单设定</div>
  442. <div class="body">
  443. <div class="tips">表单名称</div>
  444. <el-input v-model="formInfo.name" placeholder="请输入表单名称" />
  445. <div class="tips">表单介绍</div>
  446. <el-input type="textarea" v-model="formInfo.desc" placeholder="请输入表单介绍" />
  447. </div>
  448. </template>
  449. <template v-else>
  450. <div></div>
  451. <div class="title">
  452. <span class="el-icon-arrow-left icon" @click="currentKey=''" />
  453. 组件设定
  454. </div>
  455. <div class="body">
  456. <div class="tips">
  457. 表单项介绍
  458. <guide
  459. video="/static/guide/表单项介绍.jpg"
  460. title="表单项介绍"
  461. text="展示在输入框上方的大段文本,用于介绍和提示该处应输入的内容。"
  462. ></guide>
  463. </div>
  464. <el-input v-model="currentData.label" type="textarea" placeholder="请输入表单项介绍"></el-input>
  465. <template>
  466. <div class="tips">是否必填</div>
  467. <el-switch v-model="currentData.required" />
  468. </template>
  469. <template v-if="['input','select','textarea','time','date','region','email','phone'].includes(currentData.type)">
  470. <div class="tips">
  471. 提示文字
  472. <guide
  473. video="/static/guide/提示文字.jpg"
  474. title="提示文字"
  475. text="用户输入内容前,展示在输入框中的简短文本,用于提示该处应输入的内容。"
  476. ></guide>
  477. </div>
  478. <el-input v-model="currentData.placeholder" placeholder="请输入提示文字"></el-input>
  479. </template>
  480. <template v-if="['input','textarea'].includes(currentData.type)">
  481. <div class="tips">验证规则</div>
  482. <el-input v-model="currentData.pattern" :rows="3" type="textarea" placeholder="规则正则表达式"></el-input>
  483. <el-select class="pattern-select" @change="currentData.pattern=$event" placeholder="选择预设规则">
  484. <el-option label="不做判断" value="^[\s\S]*$"></el-option>
  485. <el-option label="国内手机号" value="^1\d{10}$"></el-option>
  486. <el-option label="国内身份证号码" value="^[1-9]\d{5}(19\d{2}|20\d{2})((0[1-9])|(1[0-2]))((0[1-9])|([1-2]\d)|(3[0-1]))\d{3}(\d|X|x)$"></el-option>
  487. <el-option label="电子邮箱" value="^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"></el-option>
  488. </el-select>
  489. </template>
  490. <template v-if="currentData.type==='region'">
  491. <div class="tips">
  492. 地区范围
  493. </div>
  494. <el-radio-group size="small" v-model="currentData.range">
  495. <el-radio-button :label="2">省/市</el-radio-button>
  496. <el-radio-button :label="3">省/市/县</el-radio-button>
  497. </el-radio-group>
  498. </template>
  499. <template v-if="['timeRange','dateRange'].includes(currentData.type)">
  500. <div class="tips">
  501. 起始提示文字
  502. <guide
  503. video="/static/guide/起始提示文字.jpg"
  504. title="起始提示文字"
  505. text="时间与日期类型组件输入内容前,显示在开始时间输入框中,用于提示的文字内容"
  506. ></guide>
  507. </div>
  508. <el-input v-model="currentData.placeholder" placeholder="请输入起始提示文字"></el-input>
  509. <div class="tips">
  510. 结束提示文字
  511. <guide
  512. video="/static/guide/结束提示文字.jpg"
  513. title="结束提示文字"
  514. text="时间与日期类型组件输入内容前,显示在结束时间输入框中,用于提示的文字内容"
  515. ></guide>
  516. </div>
  517. <el-input v-model="currentData.endPlaceholder" placeholder="请输入结束提示文字"></el-input>
  518. <div class="tips">
  519. 范围分隔符
  520. <guide
  521. video="/static/guide/范围分隔符.jpg"
  522. title="范围分隔符"
  523. text="时间与日期类型组件中,开始与结束提示文字中间的分隔文字。"
  524. ></guide>
  525. </div>
  526. <el-input v-model="currentData.rangeSeparator" placeholder="请输入范围分隔符"></el-input>
  527. </template>
  528. <template v-if="['select','radio','checkbox'].includes(currentData.type)">
  529. <div class="tips">
  530. 选项设定
  531. <guide
  532. video="/static/guide/选项设定.mp4"
  533. title="选项设定"
  534. text="在单选、多选、选择器组件中,编辑可供用户选择的选择项目。"
  535. ></guide>
  536. </div>
  537. <div class="select-list">
  538. <div class="item">
  539. <draggable :options="{sort:true,animation:300}" v-model="currentData.options">
  540. <transition-group class="select-inner">
  541. <el-input v-for="(item,index) in currentData.options" :key="item.value" :value="item.label" disabled>
  542. <div slot="prefix" class="handel">⠿</div>
  543. <el-button type="danger" @click="removeSelectItem(index)" slot="append" icon="el-icon-delete"></el-button>
  544. </el-input>
  545. </transition-group>
  546. </draggable>
  547. <el-input v-model="selectInput" @keyup.enter.native="addSelectItem" placeholder="请输入选项">
  548. <el-button @click="addSelectItem" slot="append" icon="el-icon-plus"></el-button>
  549. </el-input>
  550. </div>
  551. </div>
  552. </template>
  553. <template v-if="['number','slider'].includes(currentData.type)">
  554. <div class="tips">
  555. 最小值
  556. <guide
  557. video="/static/guide/最小值.jpg"
  558. title="最小值"
  559. text="数字输入类型的组件中,限制可以输入的最小数字。"
  560. ></guide>
  561. </div>
  562. <el-input v-model="currentData.min" @change="currentData.value=$event-0"/>
  563. <div class="tips">
  564. 最大值
  565. <guide
  566. video="/static/guide/最大值.jpg"
  567. title="最大值"
  568. text="数字输入类型的组件中,限制可以输入的最大数字。"
  569. ></guide>
  570. </div>
  571. <el-input v-model="currentData.max"/>
  572. <div class="tips">
  573. 步进值
  574. <guide
  575. video="/static/guide/步进值.mp4"
  576. title="步进值"
  577. text="数字输入类型的组件中,限制输入的数字为多少的倍数。"
  578. ></guide>
  579. </div>
  580. <el-input v-model="currentData.step"/>
  581. </template>
  582. <div class="tips">
  583. 组件宽度
  584. <guide
  585. video="/static/guide/组件宽度.mp4"
  586. title="组件宽度"
  587. text="设置所选定组件的宽度,有1/3、1/2、2/3、占据全部宽度,总共四种尺寸供选择。"
  588. ></guide>
  589. </div>
  590. <el-radio-group size="small" v-model="currentData.width">
  591. <el-radio-button :label="2">窄</el-radio-button>
  592. <el-radio-button :label="3">中</el-radio-button>
  593. <el-radio-button :label="4">宽</el-radio-button>
  594. <el-radio-button :label="6">长</el-radio-button>
  595. </el-radio-group>
  596. </div>
  597. <div class="button">
  598. <el-button @click="removeComp(currentData)" type="danger">删除组件</el-button>
  599. </div>
  600. </template>
  601. </div>
  602. <div :class="['trash-bin',isDrag?'':'hide']">
  603. <div class="trash-inner">
  604. <draggable v-model="trashData" :group="{name:'form'}" :options="{sort:false,animation:300}" :class="['trash-cont',trashData.length?'':'hide']">
  605. <transition-group class="drag-cont">
  606. <div v-for="element in trashData" :key="element.key" class="form-item view">
  607. <div class="tips">{{element.label}}</div>
  608. <div @click="removeTrash(element)" class="del el-icon-delete"></div>
  609. </div>
  610. </transition-group>
  611. </draggable>
  612. </div>
  613. <div class="text">回收站</div>
  614. </div>
  615. </div>
  616. </template>
  617. <style scoped>
  618. .main-box{
  619. position: relative;
  620. overflow: hidden;
  621. padding: 0 !important;
  622. height: 100%;
  623. width: 100%;
  624. display: grid;
  625. grid-template-columns: 360px 1fr 360px;
  626. grid-template-rows: 1fr;
  627. .form-item{
  628. cursor: grab;
  629. background: white;
  630. margin-bottom: -1px;
  631. border-top: 1px solid lightgrey;
  632. border-bottom: 1px solid lightgrey;
  633. padding: 24px;
  634. .tips{
  635. font-size: 15px;
  636. color: #989898;
  637. margin-bottom: 8px;
  638. }
  639. &.email{
  640. .code-input{
  641. display: flex;
  642. grid-gap: 8px;
  643. margin-top: 8px;
  644. }
  645. }
  646. &.phone{
  647. .phone-input{
  648. display: flex;
  649. grid-gap: 8px;
  650. }
  651. }
  652. &.select{
  653. .el-select{
  654. width: 100%;
  655. }
  656. }
  657. &.number{
  658. .el-input-number{
  659. width: 100%;
  660. }
  661. }
  662. &.time,&.timeRange{
  663. .el-date-editor{
  664. width: 100%;
  665. }
  666. }
  667. &.date,&.dateRange{
  668. .el-date-editor{
  669. width: 100%;
  670. }
  671. }
  672. &.region{
  673. .el-cascader{
  674. width: 100%;
  675. }
  676. }
  677. &.view{
  678. transition-duration: 300ms;
  679. border-radius: 8px;
  680. border: 3px solid #2563EB00;
  681. padding: 12px 12px;
  682. margin-bottom: 0;
  683. &.tips{
  684. padding: 12px 12px;
  685. }
  686. &.active{
  687. border: 3px solid #2563EB;
  688. }
  689. }
  690. }
  691. .trash-bin{
  692. box-shadow: -4px 8px 12px 0 #00000044;
  693. padding: 8px;
  694. position: absolute;
  695. border-radius: 32px 32px 0 0;
  696. z-index: 3;
  697. right: 200px;
  698. bottom: 0;
  699. width: 360px;
  700. height: 150px;
  701. background: lightgrey;
  702. transition-duration: 300ms;
  703. .trash-inner{
  704. position: relative;
  705. outline: #e4e4e4 5px solid;
  706. border-radius: 24px;
  707. width: 100%;
  708. height: 60px;
  709. background-image: linear-gradient( #b3b3b3, #2b2b2b);
  710. .trash-cont{
  711. overflow: hidden;
  712. border-radius: 24px;
  713. position: absolute;
  714. width: 100%;
  715. height: 200%;
  716. left: 0;
  717. bottom: 0;
  718. &.hide{
  719. height: 100%;
  720. }
  721. .drag-cont{
  722. padding: 6px 12px 0;
  723. overflow: hidden;
  724. overflow-y: scroll;
  725. display: block;
  726. width: calc(100% + 20px);
  727. height: 100%;
  728. .form-item{
  729. position: sticky;
  730. left: 0;
  731. top: 0;
  732. border-bottom-right-radius: 0;
  733. border-bottom-left-radius: 0;
  734. border: 1px solid lightgrey;
  735. height: 100%;
  736. .del{
  737. position: absolute;
  738. right: 16px;
  739. top: 16px;
  740. cursor: pointer;
  741. }
  742. }
  743. }
  744. }
  745. }
  746. .text{
  747. width: 100%;
  748. text-align: center;
  749. margin-top: 16px;
  750. font-size: 36px;
  751. font-weight: bold;
  752. color: gray;
  753. text-shadow: 4px 4px 4px 4px #ffffff;
  754. }
  755. &.hide{
  756. bottom: -160px;
  757. }
  758. &:hover{
  759. bottom: -60px;
  760. }
  761. }
  762. .comp-lib{
  763. z-index: 1;
  764. position: relative;
  765. display: grid;
  766. grid-template-rows: auto 1fr;
  767. box-shadow: 0 1px 4px 0 #00000022;
  768. .title{
  769. padding: 24px;
  770. }
  771. .list{
  772. position: relative;
  773. .drag-list{
  774. position: absolute;
  775. top: 0;
  776. left: 0;
  777. height: 100%;
  778. width: 100%;
  779. overflow: hidden;
  780. overflow-y: auto;
  781. .drag-cont{
  782. width: 100%;
  783. height: 100%;
  784. display: block;
  785. }
  786. }
  787. }
  788. }
  789. .form-view{
  790. position: relative;
  791. background: #F9FAFB;
  792. .scroll-view{
  793. position: absolute;
  794. left: 0;
  795. top: 0;
  796. height: 100%;
  797. width: 100%;
  798. overflow: hidden;
  799. overflow-y: scroll;
  800. .form-cont{
  801. padding: 12px;
  802. border-radius: 16px;
  803. margin: 24px;
  804. background: white;
  805. width: calc(100% - 48px);
  806. box-shadow: 0 1px 4px 0 #00000022;
  807. .form-head{
  808. padding: 12px;
  809. border-radius: 8px;
  810. transition-duration: 300ms;
  811. border: 3px solid #2563EB00;
  812. &:hover,&.active{
  813. border: 3px solid #2563EB;
  814. }
  815. .title{
  816. font-size: 36px;
  817. font-weight: bold;
  818. }
  819. .tips{
  820. font-size: 20px;
  821. color: #989898;
  822. margin-bottom: 6px;
  823. }
  824. }
  825. .form-body{
  826. .drag-cont{
  827. display: grid;
  828. grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
  829. width: 100%;
  830. min-height: 300px;
  831. align-content: start;
  832. .form-item{
  833. height: fit-content;
  834. }
  835. .required{
  836. .tips{
  837. &::after{
  838. margin-left: 4px;
  839. content: '*';
  840. color: red;
  841. }
  842. }
  843. }
  844. }
  845. }
  846. }
  847. }
  848. }
  849. .comp-edit{
  850. z-index: 1;
  851. position: relative;
  852. box-shadow: 0 1px 4px 0 #00000022;
  853. display: grid;
  854. grid-template-rows: auto auto 1fr auto;
  855. .body{
  856. padding: 0 16px;
  857. display: flex;
  858. flex-direction: column;
  859. align-items: flex-end;
  860. .tips{
  861. width: 100%;
  862. font-size: 16px;
  863. color: #505050;
  864. margin-bottom: 6px;
  865. margin-top: 12px;
  866. }
  867. .pattern-select{
  868. margin-top: 6px;
  869. width: 100%;
  870. }
  871. .select-list{
  872. width: 100%;
  873. .handel{
  874. height: 100%;
  875. display: flex;
  876. align-items: center;
  877. }
  878. .select-inner{
  879. .el-input__inner{
  880. cursor: grab;
  881. }
  882. }
  883. }
  884. }
  885. .button{
  886. display: flex;
  887. justify-content: flex-end;
  888. padding: 16px;
  889. }
  890. .title{
  891. padding: 16px;
  892. display: flex;
  893. align-items: center;
  894. .icon{
  895. margin-left: -4px;
  896. margin-top: -4px;
  897. margin-right: 8px;
  898. font-size: 24px;
  899. cursor: pointer;
  900. padding: 4px;
  901. border-radius: 8px;
  902. transition-duration: 300ms;
  903. &:hover{
  904. color: #2563EB;
  905. background: #ececec;
  906. }
  907. }
  908. }
  909. }
  910. }
  911. </style>