index.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. <script src="/resources/js/html2canvas.min.js"></script>
  2. <!-- 装修所需的数据 -->
  3. <script type="text/javascript" src="/resources/js/diy-util.js"></script>
  4. <script type="text/javascript" src="/resources/js/hotzone.js"></script>
  5. <?php
  6. use common\helpers\ComponentHelper;
  7. use common\models\diy\DiyComponents;
  8. use common\helpers\AddonHelper;
  9. $diyPath = Yii::$app->viewPath . '/components/common/diy/common';
  10. $stylePath = Yii::$app->viewPath . '/components/common/diy/style-components';
  11. $components = DiyComponents::getDir($diyPath, $diyPath);
  12. $styleComponents = DiyComponents::getDir($stylePath, $stylePath);
  13. ComponentHelper::loadComponentView('diy/diy-func-list');
  14. ComponentHelper::loadComponentView('diy/diy-preview');
  15. ComponentHelper::loadComponentView('diy/diy-style-preview');
  16. if (!empty($addons_components)) {
  17. // 组件ID
  18. $view_arr = ['preview', 'style'];
  19. foreach ($addons_components as $item) {
  20. $basc_path = Yii::getAlias('@addons') . "/{$item['source']}/backend/views/components/diy/{$item['code']}";
  21. // 加载组件
  22. foreach ($view_arr as $view) {
  23. // 文件
  24. $file_path = $basc_path . DIRECTORY_SEPARATOR . $view . '.php';
  25. if (is_file($file_path)) {
  26. ComponentHelper::loadComponentView($view, $basc_path);
  27. }
  28. }
  29. }
  30. }
  31. foreach ($components as $item) {
  32. ComponentHelper::loadComponentView("diy/common/{$item}");
  33. }
  34. foreach ($styleComponents as $item) {
  35. ComponentHelper::loadComponentView("diy/style-components/{$item}");
  36. }
  37. $currentRoute = Yii::$app->controller->route;
  38. ?>
  39. <div id="app">
  40. <div style="padding-bottom: 10px;padding-right: 20px;background: #F4F6F8;" @click="save" flex="main:right">
  41. <el-button type="primary" size="small" style="width: 80px;">保存</el-button>
  42. </div>
  43. <div class="diy-contain flex" v-loading="loading">
  44. <div class="diy-func-list">
  45. <diy-func-list @end="onEnd" :test-data="onClick" :list="pageComponentsList"
  46. @add-prev-item="onClick"></diy-func-list>
  47. </div>
  48. <diy-preview :active-index="activeIndex" :list="previewList" @select-item="selectItme"
  49. @change-move="changeMove"></diy-preview>
  50. <transition name="fade">
  51. <diy-style-preview :active-item="activeItem" :active-index="activeIndex" :permission="permission"
  52. @update="changeDiyItem" v-if="showUpdate"></diy-style-preview>
  53. </transition>
  54. </div>
  55. <!-- 预览弹窗 -->
  56. <el-dialog title="预览弹窗" :modal-append-to-body="false" :visible.sync="dialogFormVisible">
  57. <div class="flex flex-x-center" style="height: 600px;overflow:auto">
  58. <img id="diy-test-preview" :src="previewSrc"></img>
  59. </div>
  60. <div slot="footer" class="dialog-footer">
  61. <el-button @click="dialogFormVisible = false">取 消</el-button>
  62. <el-button type="primary" @click="dialogFormVisible = false">确 定</el-button>
  63. </div>
  64. </el-dialog>
  65. </div>
  66. <script>
  67. function getNavData() {
  68. const result = {
  69. "id": getRandomId(),
  70. "data": {
  71. "logoSrc": "",
  72. "backSrc": "",
  73. "isShowName": "1",
  74. "isShowSearch": "1",
  75. "mainPage": { // 主页面设置
  76. "defColor": "#fff",
  77. "imageUrl": [],
  78. "name": "标题",
  79. "type": "纯色"
  80. },
  81. },
  82. "computedStyle": {
  83. "mode": 1,
  84. "style": 1,
  85. "titleSwitch": true,
  86. "titleMode": 1,
  87. "imageUrl": [],
  88. "position": 1,
  89. "searchSwitch": true,
  90. "searchBgColor": "",
  91. "defaultForm": {
  92. "defColor": "transparer"
  93. },
  94. "titleDistance": {
  95. "fontSize": 16,
  96. "height": 30,
  97. "width": 57,
  98. "marginLeft": 20
  99. },
  100. "statusBar": "1",
  101. "searchMarPad": {
  102. "marginLeft": 20,
  103. "paddingLeft": 14
  104. }
  105. },
  106. "permission": [],
  107. "funcListItem": {
  108. // "type": "nav",
  109. // "title": "顶部导航",
  110. // "is_top": 1
  111. },
  112. "isShow": true,
  113. "is_show_permission": false,
  114. "is_drag": false,
  115. "disabled": true,
  116. }
  117. return result
  118. }
  119. function getNavLocationData() {
  120. const result = {
  121. "id": getRandomId(),
  122. "data": {
  123. "logoSrc": "",
  124. "backSrc": "",
  125. "isShowName": "1",
  126. "isShowSearch": "1",
  127. "locationStyle": "1",
  128. "mainPage": { // 主页面设置
  129. "defColor": "#fff",
  130. "imageUrl": [],
  131. "name": "标题",
  132. "type": "纯色"
  133. },
  134. },
  135. "computedStyle": {
  136. "mode": 1,
  137. "style": 1,
  138. "titleSwitch": true,
  139. "titleMode": 1,
  140. "imageUrl": [],
  141. "position": 1,
  142. "searchSwitch": true,
  143. "searchBgColor": "",
  144. "defaultForm": {
  145. "defColor": "transparer"
  146. },
  147. "titleDistance": {
  148. "fontSize": 16,
  149. "height": 30,
  150. "width": 57,
  151. "marginLeft": 20
  152. },
  153. "statusBar": "1",
  154. "statusCapsule": "1",
  155. "searchMarPad": {
  156. "marginLeft": 20,
  157. "paddingLeft": 14
  158. }
  159. },
  160. "permission": [],
  161. "funcListItem": {
  162. "type": "nav-location",
  163. "title": "顶部定位导航",
  164. "is_top": 1
  165. },
  166. "isShow": true,
  167. "is_show_permission": false,
  168. "is_drag": false,
  169. "disabled": true,
  170. }
  171. return result
  172. }
  173. </script>
  174. <script>
  175. Vue.prototype.$capitals = []; // 用户信息对象
  176. Vue.prototype.$eventBus = new Vue();
  177. var PageShareData = {
  178. pageIndex: false // 点击页面设置标识 => nav/style
  179. }; // 全局共享数据 - 没有响应式
  180. new Vue({
  181. el: '#app',
  182. data() {
  183. return {
  184. /**
  185. * @descriptions 浮动()、普通()、置顶(isTop)、弹窗(alert)四种显示组件
  186. * @param {string} type // diy组件类型
  187. * @param {string} title // 二级标题
  188. * @param {string} img // diy组件图片
  189. * @param {number} count // 可添加diy组件数量
  190. * @param {boolean} is_top // diy组件是否置顶组件 - 待添加 - 分添加等级 1 2 3 数字靠前权限越高
  191. * @param {object} permission // 组件的不可操作权限 {"is_delete": false,"is_drag": false,"disabled": true, // 禁用操作}
  192. */
  193. pageComponentsList: [{
  194. title: '常用组件', // 一级标题
  195. list: [{
  196. type: 'search', // diy组件类型
  197. title: '搜索', // 二级标题
  198. img: 'resources/img/decorate/func/search.png',
  199. }, {
  200. type: 'button-group', // diy组件类型
  201. title: '按钮组', // 二级标题
  202. img: 'resources/img/decorate/func/button-group.png',
  203. }, {
  204. type: 'placard', // diy组件类型
  205. title: '公告', // 二级标题
  206. img: 'resources/img/decorate/func/placard.png',
  207. }, {
  208. type: 'video', // diy组件类型
  209. title: '视频', // 二级标题
  210. img: 'resources/img/decorate/func/video.png',
  211. },
  212. {
  213. type: 'graphic-details', // diy组件类型
  214. title: '图文详情', // 二级标题
  215. img: 'resources/img/decorate/func/graphic-details.png',
  216. },
  217. {
  218. type: 'auxiliary-blank', // diy组件类型
  219. title: '辅助空白', // 二级标题
  220. img: 'resources/img/decorate/func/auxiliary-blank.png',
  221. },
  222. {
  223. type: 'auxiliary-line', // diy组件类型
  224. title: '辅助线', // 二级标题
  225. img: 'resources/img/decorate/func/auxiliary-line.png',
  226. },
  227. {
  228. type: "title-block",
  229. title: "标签栏",
  230. img: 'resources/img/decorate/func/title-block.png',
  231. },
  232. {
  233. type: 'rubik-cube', // diy组件类型
  234. title: '魔方', // 二级标题
  235. img: 'resources/img/decorate/func/rubik-cube.png',
  236. },
  237. {
  238. type: 'img-ad', // diy组件类型
  239. title: '图片广告', // 二级标题
  240. img: 'resources/img/decorate/func/img-ad.png',
  241. },
  242. {
  243. type: 'micro-theme', // diy组件类型
  244. title: '微主题', // 二级标题
  245. img: 'resources/img/decorate/func/micro-theme.png',
  246. },
  247. {
  248. type: 'carousel-img', // diy组件类型
  249. title: '轮播广告', // 二级标题
  250. img: 'resources/img/decorate/func/carousel-img.png',
  251. },
  252. {
  253. type: 'graphic-details', // diy组件类型
  254. title: '模板', // 二级标题
  255. img: 'resources/img/decorate/func/search.png',
  256. },
  257. {
  258. type: 'commodity-group', // diy组件类型
  259. title: '商品组', // 二级标题
  260. img: 'resources/img/decorate/func/commodity-group.png',
  261. }
  262. ]
  263. },
  264. {
  265. title: '小店组件', // 一级标题
  266. list: [{
  267. type: 'happiness-store-goods', // diy组件类型
  268. title: '小店商品', // 二级标题
  269. img: 'resources/img/decorate/func/happiness-store-goods.png',
  270. }, {
  271. type: 'happiness-member-card', // diy组件类型
  272. title: '小店会员卡', // 二级标题
  273. img: 'resources/img/decorate/func/happiness-member-card.png',
  274. }
  275. ]
  276. },
  277. ],
  278. capital: [],
  279. dialogFormVisible: false,
  280. /**
  281. * @param {boolean} is_show_permission // 显示权限
  282. * @param {boolean} is_delete // 是否可删除
  283. * @param {boolean} is_up // 是否可上移
  284. * @param {boolean} is_down // 是否可下移
  285. * @param {boolean} is_drag // 是否可拖拽
  286. * @param {boolean} is_copy // 是否可复制
  287. * @param {boolean} disabled // 全部禁用操作
  288. */
  289. previewList: [], // 预览数组对象
  290. activeIndex: -1, // 预览激活索引
  291. activeItem: null, // 预览激活对象
  292. type: "add", // add新增 - edit编辑
  293. test: ["a", "b", "c"],
  294. showUpdate: true,
  295. responentData: null, // 详情响应的总数据
  296. permission: [], // 权限数组
  297. isClickPage: false, // 是否点击分页设置
  298. loading: false, // 数据加载
  299. saveLoading: false, // 保存数据
  300. fields: {
  301. upload_type: 'images'
  302. },
  303. previewSrc: "",
  304. }
  305. },
  306. provide() {
  307. return {
  308. capitals: this.capital
  309. }
  310. },
  311. watch: {
  312. activeIndex() {
  313. this.showUpdate = false
  314. setTimeout(() => {
  315. this.showUpdate = true
  316. }, 0)
  317. },
  318. },
  319. created() {
  320. // console.log(this.dynamicImgSrc('/abc/ss'));
  321. // 添加不同的模板nav
  322. this.previewList.push(getNavData())
  323. this.init()
  324. this.detail()
  325. },
  326. methods: {
  327. savePreview() {
  328. this.activeIndex = -1
  329. this.activeItem = null
  330. this.$nextTick(() => {
  331. const contain = document.getElementById("diy-preview-contain");
  332. this.deepNode(contain);
  333. setTimeout(() => {
  334. this.dialogFormVisible = true
  335. html2canvas(contain, {
  336. useCORS: true
  337. }).then(canvas => {
  338. const src = canvas.toDataURL("image/png")
  339. this.upload(this.dataURLtoFile(src)).then(res => {
  340. if (res.code == 0) {
  341. this.previewSrc = res.data.url
  342. this.save(false)
  343. } else {
  344. this.$message({
  345. message: res.msg,
  346. type: 'warning'
  347. });
  348. }
  349. })
  350. })
  351. }, 0)
  352. })
  353. },
  354. dataURLtoFile(dataurl) { // 生成Blob
  355. var arr = dataurl.split(',');
  356. var mime = arr[0].match(/:(.*?);/)[1];
  357. var bstr = atob(arr[1]);
  358. var n = bstr.length;
  359. var u8arr = new Uint8Array(n);
  360. while (n--) {
  361. u8arr[n] = bstr.charCodeAt(n);
  362. }
  363. return new Blob([u8arr], {
  364. type: mime
  365. });
  366. },
  367. upload(file) {
  368. let formData = new FormData();
  369. const params = {
  370. upload_type: 'images'
  371. };
  372. params['r'] = 'common/file/upload';
  373. for (let i in this.fields) {
  374. formData.append(i, this.fields[i]);
  375. }
  376. formData.append('file', file, "image.png");
  377. return new Promise(successCallback => {
  378. this.$request({
  379. headers: {
  380. 'Content-Type': 'multipart/form-data'
  381. },
  382. params: params,
  383. method: 'post',
  384. data: formData,
  385. }).then(e => {
  386. successCallback(e.data)
  387. }).catch(e => {
  388. file._complete = true;
  389. });
  390. })
  391. },
  392. deepNode(contain) {
  393. if (contain.tagName === "IMG") {
  394. contain.src = contain.src + '?t=' + Date.now()
  395. contain.setAttribute('crossorigin', 'anonymous')
  396. }
  397. if (contain.children && contain.children.length) {
  398. [].forEach.call(contain.children, (item => {
  399. this.deepNode(item)
  400. }))
  401. }
  402. },
  403. pageSet() {
  404. PageShareData.pageIndex = true
  405. this.$eventBus.$emit("onPageClick")
  406. var index = 0;
  407. this.activeIndex = index
  408. this.activeItem = this.previewList[index]
  409. // this.changeDiyItem()
  410. },
  411. detail() {
  412. // 详情
  413. this.loading = true
  414. request({
  415. params: {
  416. r: 'happiness/diy/index'
  417. },
  418. method: 'get',
  419. }).then(res => {
  420. if (res.data.code == 0) {
  421. if (res.data.data.length == 0) {
  422. return;
  423. }
  424. const previewList = JSON.parse(res.data.data.content)
  425. if (!previewList.length) return
  426. this.previewList = previewList
  427. } else {
  428. this.$message({
  429. message: res.data.msg,
  430. type: 'error'
  431. });
  432. }
  433. /* const {
  434. data
  435. } = res.data
  436. if (res.data.code != 0) {
  437. this.$message({
  438. message: res.data.msg,
  439. type: 'error'
  440. });
  441. return false
  442. }
  443. const previewList = JSON.parse(data.content)
  444. this.previewList = previewList */
  445. }).finally(res => {
  446. this.loading = false
  447. })
  448. },
  449. save: debounce(function (isLoading = true) {
  450. // 对接保存数据接口
  451. if (this.saveFlag) {
  452. console.log("重复提交");
  453. return
  454. }
  455. const title = this.previewList[0].data.name
  456. // if (!title) {
  457. // this.$message({
  458. // message: '请输入标题',
  459. // type: 'warning'
  460. // });
  461. // return
  462. // }
  463. isLoading && (this.saveLoading = true)
  464. this.saveFlag = true // 防止重复提交限制
  465. request({
  466. params: {
  467. r: 'happiness/diy/index'
  468. },
  469. method: 'post',
  470. data: {
  471. content: JSON.stringify(this.previewList),
  472. }
  473. }).then(res => {
  474. if (res.data.code != 0) {
  475. this.$message({
  476. message: res.data.msg,
  477. type: 'error'
  478. });
  479. } else {
  480. this.$message({
  481. message: res.data.msg,
  482. type: 'success'
  483. });
  484. }
  485. }).finally(res => {
  486. this.saveFlag = false
  487. this.saveLoading = false
  488. })
  489. }, 400),
  490. init() {
  491. this.$eventBus.$on("diy-preview-copy", (item = {}) => {
  492. this.copy(item)
  493. })
  494. this.$eventBus.$on("diy-preview-operateFn", (item = {}) => {
  495. this.operateFn(item)
  496. })
  497. this.$eventBus.$on("diy-back-set", () => {
  498. this.updateView(null, -1)
  499. })
  500. },
  501. onEnd(e, index) { // 左侧拖拽结束手动添加进去
  502. this.activeIndex = index - 1
  503. console.log(this.activeIndex, "=======> activeIndex");
  504. this.onClick(e)
  505. },
  506. setInitObj(data, e) {
  507. if (!e.permission) {
  508. return false
  509. }
  510. const target = typeof e.permission === "object" ? e.permission : JSON.parse(e.permission)
  511. for (const key in target) {
  512. data[key] = e.permission[key]
  513. }
  514. },
  515. onClick(e) {
  516. console.log(e, "=======> e");
  517. const flag = this.validateCount(e) // 校验组件可添加个数
  518. if (flag) return // 不通过
  519. var initObj = {
  520. id: getRandomId(this.previewList),
  521. data: {}, // 对应数据
  522. computedStyle: {}, // 对应组件样式-尺寸
  523. permission: [], // 展示权限列表 ==> 对应用户等级or其他
  524. funcListItem: e,
  525. isShow: true, // 前端是否显示
  526. is_show_permission: true, // 是否显示权限
  527. } // 创建的初始数据
  528. this.setInitObj(initObj, e) // 设置可操作方法
  529. // 弹窗组件
  530. if (e.type === "popup-advertisement") {
  531. this.handleAlertComponent(initObj) // 单独处理全局弹窗组件
  532. return false
  533. }
  534. if (e.is_top) { // 置顶组件操作 - 不操作以前
  535. const index = this.previewList.findIndex(item => {
  536. const e1 = item.funcListItem || {}
  537. return e1.is_top === undefined || (e1.is_top >= e.is_top)
  538. })
  539. this.activeItem = initObj
  540. if (index < 0) {
  541. this.previewList.push(initObj)
  542. this.activeIndex = Math.max(0, this.previewList.length - 1)
  543. } else {
  544. this.previewList.splice(index, 0, initObj)
  545. this.activeIndex = index
  546. }
  547. return false
  548. }
  549. // 不置顶组件添加
  550. this.activeIndex = Math.max(this.getIsTopArrLength() - 1, this.activeIndex) // 限制普通组件到istop前面
  551. var length = this.previewList.length;
  552. var isLast = length - 1 === this.activeIndex;
  553. this.previewList = this.addIndexItem(this.previewList, initObj, this.activeIndex)
  554. if (length && (isLast || this.activeIndex == -1)) {
  555. // 有值是否最后一项
  556. this.activeIndex = length - 1
  557. }
  558. this.activeItem = initObj
  559. this.activeIndex += 1
  560. },
  561. validateCount(e) {
  562. // 校验组件能添加几个
  563. var msg = "";
  564. var list = this.previewList.filter(item => {
  565. return item.funcListItem.type === e.type
  566. });
  567. if (e && e.count && list.length >= e.count) {
  568. msg = `该组件只能添加${e.count}个哦`
  569. }
  570. if (msg) {
  571. this.$message({
  572. message: msg,
  573. type: 'warning'
  574. });
  575. }
  576. return msg
  577. },
  578. getIsTopArrLength() {
  579. return this.previewList.filter(item => {
  580. const e1 = item.funcListItem || {}
  581. return e1.is_top
  582. }).length
  583. },
  584. changeDiyItem(item, index, params = {}) {
  585. var {
  586. type
  587. } = params
  588. var list = deepClone(this.previewList) // 克隆一下
  589. if (type !== "isShow") {
  590. // 显示问题 -- 样式组件不让修改
  591. item.isShow = list[this.activeIndex].isShow;
  592. }
  593. // 直接全部覆盖
  594. list[this.activeIndex] = deepClone(item)
  595. this.activeItem = deepClone(item)
  596. this.previewList = list
  597. },
  598. selectItme(item, index) {
  599. console.info(item);
  600. if (this.activeIndex == index) {
  601. this.activeIndex = -1
  602. this.activeItem = null
  603. } else {
  604. this.activeIndex = index
  605. this.activeItem = item
  606. }
  607. console.info();
  608. this.$forceUpdate()
  609. },
  610. pushItem(list = [], target = {}) {
  611. var result = deepClone(list)
  612. result.push(target)
  613. return result
  614. },
  615. /**
  616. * 两种形式,一种push,追加index
  617. */
  618. addIndexItem(list = [], target = {}, index) { // 往指定的index添加item
  619. var result = deepClone(list)
  620. if (this.activeIndex < 0) {
  621. result.push(target)
  622. } else {
  623. var handleArr = [deepClone(result[index]), target]
  624. result.splice(index, 1, ...handleArr)
  625. }
  626. return result
  627. },
  628. // 操作相关
  629. copy() {
  630. if (!this.activeItem || this.activeItem.disabled || this.activeItem.is_copy === false) {
  631. return
  632. }
  633. var item = deepClone(this.activeItem)
  634. item.id = getRandomId()
  635. this.previewList = this.addIndexItem(this.previewList, item, this.activeIndex)
  636. },
  637. /**
  638. * @params {string} obj.type up - down - show
  639. */
  640. operateFn(obj) {
  641. const methods = {
  642. show: 'showItem',
  643. up: 'upItem',
  644. down: 'downItem',
  645. delete: 'deleteItem',
  646. replace: 'replaceItem'
  647. }
  648. this[methods[obj.type]](obj.index)
  649. },
  650. replaceItem(index) {
  651. let type = this.activeItem.funcListItem.type;
  652. let activeItem = type === 'nav' ? getNavLocationData() : getNavData();
  653. this.previewList.splice(index, 1, activeItem);
  654. this.updateView(this.previewList[index], index);
  655. },
  656. showItem(index) {
  657. var item = deepClone(this.activeItem);
  658. item.isShow = !item.isShow
  659. this.changeDiyItem(item, index, {
  660. type: "isShow"
  661. })
  662. },
  663. updateView(item, index) {
  664. this.activeItem = item
  665. this.activeIndex = index
  666. },
  667. deleteItem(index) {
  668. if (this.activeItem.disabled || this.activeItem.is_delete === false) {
  669. return false
  670. }
  671. this.previewList.splice(index, 1);
  672. // 删除后失去焦点
  673. this.activeIndex = -1;
  674. this.activeItem = null;
  675. },
  676. upItem(index) {
  677. const topArr = this.previewList.filter(item => {
  678. const e1 = item.funcListItem || {}
  679. return e1.is_top
  680. }) // 操作上限
  681. const topIndex = Math.max(0, topArr.length)
  682. if (index <= 0 || this.activeItem.disabled || this.activeItem.is_up === false) {
  683. return false
  684. } else if (index <= topIndex) {
  685. alert("不可以再操作了噢");
  686. return false
  687. }
  688. var previewList = deepClone(this.previewList);
  689. this.previewList = this.swapItems(previewList, index, index - 1);
  690. this.updateView(this.previewList[index - 1], index - 1);
  691. },
  692. downItem(index) {
  693. if (this.activeItem.disabled || this.activeItem.is_down === false) {
  694. return false
  695. } else if (index === this.previewList.length - 1) {
  696. alert("不可以再操作了噢");
  697. return false
  698. }
  699. var previewList = deepClone(this.previewList);
  700. this.previewList = this.swapItems(previewList, index, index + 1);
  701. this.updateView(this.previewList[index + 1], index + 1);
  702. },
  703. swapItems(arr, index1, index2) {
  704. var item = deepClone(arr[index1]);
  705. arr[index1] = arr[index2]
  706. arr[index2] = item
  707. return arr
  708. },
  709. changeMove(e) {
  710. const {
  711. newIndex,
  712. list
  713. } = e
  714. // 移动拖拽改变索引
  715. this.previewList = deepClone(list);
  716. this.updateView(this.previewList[newIndex], newIndex);
  717. },
  718. // 更新主页面
  719. updateMainPage(item) {
  720. this.mainPage = deepClone(item);
  721. },
  722. // 处理兼容老数据
  723. addIsTop(list, type) {
  724. const obj = {
  725. ['nav']: 1,
  726. ['member-info']: 2
  727. }
  728. const top = obj[type]
  729. list.forEach(item => {
  730. if (item.funcListItem.type === type && !item.funcListItem.is_top) {
  731. item.funcListItem.is_top = top
  732. }
  733. });
  734. return list
  735. },
  736. }
  737. })
  738. // 拦截页面卸载
  739. /* window.onbeforeunload = function(e) {
  740. var e = window.event || e;
  741. e.returnValue = ("确定离开当前页面吗?");
  742. } */
  743. </script>
  744. <style>
  745. #app {
  746. display: flex;
  747. flex-direction: column;
  748. min-width: 1250px;
  749. }
  750. #diy-test-preview {
  751. margin: auto;
  752. }
  753. .diy-header-backs {
  754. cursor: pointer;
  755. }
  756. .fixed {
  757. position: fixed;
  758. left: 0;
  759. top: 0;
  760. width: 100%;
  761. height: 100%;
  762. z-index: 999;
  763. }
  764. .diy-header {
  765. height: 60px;
  766. background: #313131;
  767. opacity: 1;
  768. border-radius: 0px;
  769. justify-content: space-between;
  770. font-size: 13px;
  771. font-family: PingFang SC;
  772. font-weight: bold;
  773. color: #FFFFFF;
  774. padding: 0 30px;
  775. }
  776. .diy-contain {
  777. flex: 1;
  778. overflow: auto;
  779. background: #F4F6F8;
  780. justify-content: space-between;
  781. }
  782. .diy-func-list {
  783. box-sizing: border-box;
  784. width: 354px;
  785. background: #FFFFFF;
  786. border-radius: 0px;
  787. }
  788. .diy-preview {
  789. flex: 1;
  790. background: #F4F6F8;
  791. justify-content: space-around;
  792. }
  793. .fill-box {
  794. position: relative;
  795. width: 467px;
  796. height: 100%;
  797. }
  798. .flex {
  799. display: flex;
  800. }
  801. .flex-x-center {
  802. justify-content: center;
  803. }
  804. .flex-y-center {
  805. align-items: center;
  806. }
  807. /* */
  808. .fade-enter-active,
  809. .fade-leave-active {
  810. transition: opacity 1s;
  811. }
  812. .fade-enter,
  813. .fade-leave-to {
  814. opacity: 0;
  815. }
  816. .diy-text-font {
  817. font-size: 13px;
  818. font-weight: 700;
  819. color: #999999;
  820. }
  821. .fill-border-top {
  822. border-top: 10px solid #F4F6F8;
  823. }
  824. .fill-border-bottom {
  825. border-bottom: 10px solid #F4F6F8;
  826. }
  827. /* 导航栏 */
  828. .custom-page {
  829. width: 181px;
  830. height: 60px;
  831. line-height: 60px;
  832. background: var(--diy-theme);
  833. font-weight: 700;
  834. border-radius: 0px;
  835. text-align: center;
  836. }
  837. .save {
  838. font-size: 24px;
  839. vertical-align: middle;
  840. }
  841. .save + span {
  842. vertical-align: middle;
  843. }
  844. .sava_btn .el-button--primary {
  845. background: var(--diy-theme);
  846. border-color: var(--diy-theme);
  847. padding: 8px 14px;
  848. }
  849. .diy_nav_right > div {
  850. padding: 8px 14px;
  851. }
  852. .diy_nav_right .sava_btn {
  853. padding: 0;
  854. }
  855. .page_set > i {
  856. font-size: 24px;
  857. vertical-align: middle;
  858. }
  859. .page_set > span {
  860. vertical-align: middle;
  861. }
  862. .xiaoshou {
  863. cursor: pointer;
  864. }
  865. :root {
  866. /* 主题色 */
  867. --diy-theme: #F10009;
  868. /* 主要字体颜色 */
  869. --diy-color: #F10009;
  870. }
  871. /* 修改ele默认样式 */
  872. /* slider组件 */
  873. .el-slider__bar {
  874. background-color: var(--diy-theme);
  875. }
  876. .el-slider__button {
  877. border-color: var(--diy-theme);
  878. }
  879. /* input组件 */
  880. .el-input .el-input__inner:focus {
  881. border: 1px solid var(--diy-theme);
  882. }
  883. /* textarea */
  884. .el-textarea .el-textarea__inner:focus {
  885. border: 1px solid var(--diy-theme);
  886. }
  887. /* radio单选组件 */
  888. .el-radio__input.is-checked + .el-radio__label {
  889. color: var(--diy-color);
  890. }
  891. .el-radio__input.is-checked .el-radio__inner {
  892. border-color: var(--diy-theme);
  893. background: var(--diy-theme);
  894. }
  895. .el-radio-button__orig-radio:checked + .el-radio-button__inner {
  896. border-color: var(--diy-theme);
  897. box-shadow: -1px 0 0 0 var(--diy-theme);
  898. }
  899. /* 多选 */
  900. .el-checkbox__input.is-checked + .el-checkbox__label {
  901. color: var(--diy-color);
  902. }
  903. .el-checkbox__input.is-checked .el-checkbox__inner,
  904. .el-checkbox__input.is-indeterminate .el-checkbox__inner {
  905. border-color: var(--diy-theme);
  906. background: var(--diy-theme);
  907. }
  908. </style>