custom_template.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861
  1. //最外层组件
  2. var ncComponentHtml = '<div v-show="data.lazyLoadCss && data.lazyLoad" :key="data.id">';
  3. ncComponentHtml += '<div class="preview-draggable" ' +
  4. ':style="{ ' +
  5. 'backgroundColor : data.pageBgColor, ' +
  6. 'paddingTop : data.margin.top + \'px\', ' +
  7. 'paddingBottom : data.margin.bottom + \'px\', ' +
  8. 'paddingLeft : data.margin.both + \'px\', ' +
  9. 'paddingRight : data.margin.both + \'px\' }" @click="$parent.changeCurrentIndex(data.index)">'; // 拖拽区域
  10. ncComponentHtml += '<slot name="preview"></slot>';
  11. ncComponentHtml += '<i class="del" v-show="data.isDelete !== 1" @click.stop="$parent.delComponent(data.index)" data-disabled="1">x</i>';
  12. ncComponentHtml += '<div class="comp-title">{{ data.componentTitle }}</div>';
  13. ncComponentHtml += '</div>';
  14. ncComponentHtml += '<div class="edit-attribute" :data-have-edit="1">';
  15. ncComponentHtml += '<div class="attr-wrap">';
  16. ncComponentHtml += '<div class="restore-wrap">';
  17. ncComponentHtml += '<div class="attr-title">';
  18. ncComponentHtml += '<span class="title">{{data.componentTitle}}</span>';
  19. ncComponentHtml += '<div class="tab-wrap">';
  20. ncComponentHtml += '<span class="active bg-color" data-type="content">内容</span>';
  21. ncComponentHtml += '<span data-type="style">样式</span>';
  22. ncComponentHtml += '</div>';
  23. ncComponentHtml += '</div>';
  24. // 内容
  25. ncComponentHtml += '<div class="edit-content-wrap">';
  26. ncComponentHtml += '<slot name="edit-content"></slot>';
  27. ncComponentHtml += '</div>';
  28. // 样式
  29. ncComponentHtml += '<div class="edit-style-wrap" style="display: none;">';
  30. ncComponentHtml += '<slot name="edit-style"></slot>';
  31. ncComponentHtml += '<common-set v-if="data.ignoreLoad" :ignore="data.ignore"></common-set>';
  32. ncComponentHtml += '</div>';
  33. ncComponentHtml += '</div>';
  34. ncComponentHtml += '</div>';
  35. ncComponentHtml += '</div>';
  36. ncComponentHtml += '<div style="display:none;">';
  37. ncComponentHtml += '<slot name="resource"></slot>';
  38. ncComponentHtml += '</div>';
  39. ncComponentHtml += '</div>';
  40. var ncComponent = {
  41. props: ["data"],
  42. template: ncComponentHtml,
  43. created: function () {
  44. //如果当前添加的组件没有添加过资源
  45. if (!this.$slots.resource) {
  46. this.data.lazyLoadCss = true;
  47. this.data.lazyLoad = true;
  48. } else {
  49. //检测是否只添加了JS或者CSS,没有添加默认为true
  50. var countCss = 0, countJs = 0, outerCountJs = 0;
  51. for (var i = 0; i < this.$slots.resource.length; i++) {
  52. if (this.$slots.resource[i].componentOptions) {
  53. if (this.$slots.resource[i].componentOptions.tag === "css") {
  54. countCss++;
  55. } else if (this.$slots.resource[i].componentOptions.tag === "js") {
  56. countJs++;
  57. //统计外部JS数量
  58. if (!$.isEmptyObject(this.$slots.resource[i].componentOptions.propsData)) outerCountJs++;
  59. }
  60. }
  61. }
  62. if (countCss === 0) this.data.lazyLoadCss = true;
  63. if (countJs === 0) this.data.lazyLoad = true;
  64. this.data.outerCountJs = outerCountJs;
  65. }
  66. }
  67. };
  68. /**
  69. * 手机端自定义模板Vue对象
  70. */
  71. var vue = new Vue({
  72. el: "#diyView",
  73. data: {
  74. //当前编辑的组件位置
  75. currentIndex: -99,
  76. globalLazyLoad: false,
  77. //全局属性
  78. global: {
  79. title: "页面" + $('#name').val().replace('DIY_VIEW_RANDOM_', ''),
  80. pageBgColor: "#ffffff", // 页面背景颜色
  81. topNavColor: "#ffffff",
  82. topNavBg: false,
  83. navBarSwitch: true, // 导航栏是否显示
  84. navStyle: 1, // 导航栏风格
  85. textNavColor: "#333333",
  86. topNavImg: "",
  87. moreLink: {
  88. name: ""
  89. },
  90. //是否显示底部导航标识
  91. openBottomNav: true,
  92. textImgPosLink: 'center',
  93. mpCollect: false,
  94. // 弹框形式,不弹出 -1,首次弹出 1,每次弹出 0
  95. popWindow: {
  96. imageUrl: "",
  97. count: -1,
  98. show: 0,
  99. link: {
  100. name: ""
  101. },
  102. imgWidth: '',
  103. imgHeight: ''
  104. },
  105. bgUrl: '',
  106. imgWidth: '',
  107. imgHeight: '',
  108. // 公共模板属性,所有组件都继承,不需要重复定义,组件内部根据业务自行调用
  109. template: {
  110. pageBgColor: '', // 底部背景颜色
  111. textColor: "#303133", // 文字颜色
  112. componentBgColor: '', // 组件背景颜色
  113. componentAngle: 'round', // 组件角标(round:圆角,right:直角)
  114. topAroundRadius: 0, // 组件上圆角,命名缩减
  115. bottomAroundRadius: 0, // 组件下圆角,命名缩减
  116. elementBgColor: '', // 元素背景颜色
  117. elementAngle: 'round', // 元素角标(round:圆角,right:直角)
  118. topElementAroundRadius: 0, // 元素上圆角,命名缩减
  119. bottomElementAroundRadius: 0, // 元素下圆角,命名缩减
  120. margin: {
  121. top: 0, // 上边距
  122. bottom: 0, // 下边距
  123. both: 0, // 左右边距
  124. }
  125. }
  126. },
  127. //自定义组件集合
  128. data: [],
  129. },
  130. components: {
  131. 'nc-component': ncComponent,//最外层组件
  132. },
  133. created: function () {
  134. if ($("#info").length) {
  135. setTimeout(function () {
  136. $('#diyView').css('visibility', 'visible');
  137. }, 50);
  138. } else {
  139. $('#diyView').css('visibility', 'visible');
  140. $('.loading-layer').hide();
  141. $('.preview-wrap .preview-restore-wrap').css('visibility', 'visible');
  142. }
  143. },
  144. mounted: function () {
  145. this.refresh();
  146. },
  147. methods: {
  148. addComponent: function (obj, other) {
  149. //附加公共字段
  150. obj.index = 0;
  151. obj.sort = 0;
  152. obj.lazyLoadCss = false; // 资源懒加载,防止看到界面缓慢加载
  153. obj.lazyLoad = false; // 资源懒加载,防止看到界面缓慢加载
  154. obj.outerCountJs = 0;
  155. obj.ignore = []; // 忽略模板属性
  156. // obj.hidden = []; // 隐藏公共属性
  157. obj.tempData = {}; // 临时数据
  158. obj.id = ns.gen_non_duplicate(5);
  159. //第一次添加组件时,添加以下字段
  160. if (other) {
  161. // 第一次添加组件时,添加以下字段
  162. obj.addonName = other.addon_name; // 如果插件不存在,后台则会清除组件
  163. obj.componentName = other.name;
  164. obj.componentTitle = other.title;
  165. obj.isDelete = parseInt(other.is_delete);
  166. for (var key in this.global.template) {
  167. obj[key] = typeof this.global.template[key] == 'object' ? JSON.parse(JSON.stringify(this.global.template[key])) : this.global.template[key];
  168. }
  169. if (!this.checkComponentIsAdd(obj.componentName)) {
  170. this.autoSelected(obj.componentName);
  171. return;
  172. }
  173. }
  174. if (this.currentIndex === -99 || this.currentIndex === -98) {
  175. this.data.push(obj);
  176. // 添加组件后(不是编辑调用的),选择最后一个
  177. if (other) this.currentIndex = this.data.length - 1;
  178. }else {
  179. // 查询当前选中索引,插入到指定位置
  180. if (other && other.index) {
  181. this.data.splice(other.index, 0, obj);// 指定下标
  182. } else {
  183. this.data.splice(++this.currentIndex, 0, obj);
  184. }
  185. }
  186. $('.loading-layer').hide();
  187. $('.preview-wrap .preview-restore-wrap').css('visibility', 'visible');
  188. this.refresh();
  189. var self = this;
  190. setTimeout(function () {
  191. if (obj.componentName !== "FloatBtn" && obj.componentName !== "FollowOfficialAccount") {
  192. // 如果在末尾添加组件,则定位到最后的位置
  193. if (self.currentIndex === -99 || ((self.currentIndex + 1) === self.data.length)) {
  194. $(".preview-wrap .preview-restore-wrap .div-wrap").scrollTop($(".diy-view-wrap").height());
  195. } else {
  196. // 如果在其他位置添加组件,则定位到组件位置
  197. var element = $(".draggable-element[data-index=" + self.currentIndex + "]");
  198. var warp = $(".preview-wrap .preview-restore-wrap .div-wrap");
  199. var height = 0;
  200. $(".draggable-element:lt(" + (element.index() + 1) + ")").each(function (i) {
  201. height += $(this).outerHeight();
  202. });
  203. height -= element.outerHeight() + 30;
  204. warp.animate({scrollTop: height}, 300);
  205. }
  206. }
  207. }, 50);
  208. },
  209. //检测组件是否允许添加,true:允许 false:不允许
  210. checkComponentIsAdd: function (componentName) {
  211. var component = $('.component-list ul li[data-name="' + componentName + '"]');
  212. var maxCount = parseInt(component.attr('data-max-count'));
  213. //maxCount为0时不处理
  214. if (maxCount === 0) return true;
  215. var count = 0;
  216. //遍历已添加的自定义组件,检测是否超出数量
  217. for (var i in this.data) if (this.data[i].componentName === componentName) count++;
  218. if (count >= maxCount) return false;
  219. else return true;
  220. },
  221. // 获取组件添加数量
  222. getComponentAddCount: function (componentName) {
  223. var count = 0;
  224. //遍历已添加的自定义组件,检测是否超出数量
  225. for (var i in this.data) if (this.data[i].componentName === componentName) count++;
  226. return count;
  227. },
  228. //改变当前编辑的组件选中
  229. changeCurrentIndex: function (sort) {
  230. this.currentIndex = parseInt(sort);
  231. this.refresh();
  232. },
  233. //改变当前的删除弹出框的显示状态
  234. delComponent: function (i) {
  235. if (i < -1) return; // 从0开始
  236. var self = this;
  237. layer.confirm('确定要删除吗?', {title: '操作提示'}, function (index) {
  238. self.data.splice(i, 1);
  239. // 如果组件全部删除,则选中页面设置
  240. if(self.data.length === 0){
  241. self.currentIndex = -99;
  242. }
  243. // 如果当前选中的组件不存在,则选择上一个
  244. if(self.currentIndex === self.data.length){
  245. self.currentIndex--;
  246. }
  247. self.refresh();
  248. self.refreshQuick(true);
  249. layer.close(index);
  250. });
  251. },
  252. // 重置当前组件数据
  253. resetComponent: function () {
  254. if (this.currentIndex < 0) return; // 从0开始
  255. // if (self.data.length < 1) return; // 重置全部用
  256. var self = this;
  257. layer.confirm('确认要重置组件默认数据吗?', {title: '操作提示'}, function (index) {
  258. // 重置当前选中的组件数据
  259. var current = $(".draggable-element[data-index=" + self.currentIndex + "]");
  260. var id = current.attr('data-id');
  261. var temp = {};
  262. if ($("#info").length) {
  263. var info = JSON.parse($("#info").val().toString());
  264. if (Object.keys(info).length) {
  265. for (var i = 0; i < info.value.length; i++) {
  266. if (info.value[i].id === id) {
  267. info.value[i].index = self.currentIndex;
  268. info.value[i].sort = self.data[self.currentIndex].sort;
  269. info.value[i].lazyLoadCss = true; // 资源懒加载,防止看到界面缓慢加载
  270. info.value[i].lazyLoad = true; // 资源懒加载,防止看到界面缓慢加载
  271. info.value[i].outerCountJs = self.data[self.currentIndex].outerCountJs;
  272. info.value[i].ignore = self.data[self.currentIndex].ignore; // 忽略模板属性
  273. info.value[i].tempData = self.data[self.currentIndex].tempData; // 临时数据
  274. info.value[i].id = ns.gen_non_duplicate(5);
  275. temp = info.value[i];
  276. break;
  277. }
  278. }
  279. $("#info").val(JSON.stringify(info));
  280. }
  281. }
  282. // 如果是新添加的组件,要重置数据
  283. if (Object.keys(temp).length === 0) {
  284. var component = $('.component-list ul li[data-name="' + self.data[self.currentIndex].componentName + '"]');
  285. var value = JSON.parse(component.attr('data-value'));
  286. let index = self.currentIndex;
  287. self.data.splice(index, 1);
  288. self.addComponent(value, {
  289. index: index, // 指定下标
  290. name: component.attr('data-name'),
  291. title: component.attr('title'),
  292. addon_name: component.attr('data-addon-name'),
  293. max_count: component.attr('data-max-count'),
  294. is_delete: component.attr('data-is-delete')
  295. });
  296. } else {
  297. self.data.splice(self.currentIndex, 1, temp);
  298. }
  299. setTimeout(function () {
  300. fullScreenSize();
  301. self.refreshQuick(true);
  302. }, 10);
  303. // 以下是重置全部数据,需要时放开,勿删!
  304. // self.data = [];
  305. // self.currentIndex = -99;
  306. // setTimeout(function () {
  307. // self.refreshComponent();
  308. // }, 10 * 2);
  309. layer.close(index);
  310. });
  311. },
  312. // 上移组件
  313. moveUpComponent: function () {
  314. if ((this.currentIndex - 1) < 0) return; // 从0开始
  315. var element = $(".draggable-element[data-index=" + this.currentIndex + "]");
  316. var prev = element.prev('.draggable-element'); // 上一个组件
  317. if(prev.length === 0) return;
  318. var prevIndex = parseInt(prev.attr('data-index'));
  319. var temp = this.deepClone(this.data[this.currentIndex]); // 当前选中组件
  320. temp.id = ns.gen_non_duplicate(5); // 更新id,刷新组件数据
  321. var temp2 = this.deepClone(this.data[prevIndex]); // 上个组件
  322. temp2.id = ns.gen_non_duplicate(5); // 更新id,刷新组件数据
  323. this.data[this.currentIndex] = temp2;
  324. this.data[prevIndex] = temp;
  325. this.changeCurrentIndex(prevIndex);
  326. this.refreshQuick();
  327. var self = this;
  328. setTimeout(function () {
  329. self.$forceUpdate();
  330. },10);
  331. },
  332. // 下移组件
  333. moveDownComponent: function () {
  334. var element = $(".draggable-element[data-index=" + this.currentIndex + "]");
  335. var next = element.next('.draggable-element'); // 上一个组件
  336. if(next.length === 0) return; // 最后一个不能下移
  337. var nextIndex = parseInt(next.attr('data-index'));
  338. var temp = this.deepClone(this.data[this.currentIndex]); // 当前选中组件
  339. temp.id = ns.gen_non_duplicate(5); // 更新id,刷新组件数据
  340. var temp2 = this.deepClone(this.data[nextIndex]); // 下个组件
  341. temp2.id = ns.gen_non_duplicate(5); // 更新id,刷新组件数据
  342. this.data[this.currentIndex] = temp2;
  343. this.data[nextIndex] = temp;
  344. this.changeCurrentIndex(nextIndex);
  345. this.refreshQuick();
  346. var self = this;
  347. setTimeout(function () {
  348. self.$forceUpdate();
  349. },10);
  350. },
  351. // 复制组件
  352. copyComponent: function () {
  353. if (this.currentIndex < 0) return; // 从0开始
  354. var temp = this.deepClone(this.data[this.currentIndex]); // 当前选中组件
  355. temp.index++;
  356. temp.id = ns.gen_non_duplicate(5); // 更新id,刷新组件数据
  357. var component = $('.component-list ul li[data-name="' + temp.componentName + '"]');
  358. var maxCount = parseInt(component.attr('data-max-count'));
  359. if (!this.checkComponentIsAdd(temp.componentName)) {
  360. layer.msg(`无法复制,${temp.componentTitle}组件只能添加${maxCount}个`);
  361. return;
  362. }
  363. var index = this.currentIndex + 1;
  364. this.data.splice(index, 0, temp);
  365. this.changeCurrentIndex(index);
  366. this.refreshQuick(true);
  367. },
  368. // 深度拷贝对象
  369. deepClone(source) {
  370. if (typeof source !== 'object' || source == null) {
  371. return source;
  372. }
  373. const target = Array.isArray(source) ? [] : {};
  374. for (const key in source) {
  375. if (Object.prototype.hasOwnProperty.call(source, key)) {
  376. if (typeof source[key] === 'object' && source[key] !== null) {
  377. target[key] = this.deepClone(source[key]);
  378. } else {
  379. target[key] = source[key];
  380. }
  381. }
  382. }
  383. return target;
  384. },
  385. //刷新数据排序
  386. refresh: function () {
  387. var self = this;
  388. //vue框架执行,异步操作组件列表的排序
  389. setTimeout(function () {
  390. $(".draggable-element").each(function (i) {
  391. $(this).attr("data-sort", i);
  392. });
  393. for (var i = 0; i < self.data.length; i++) {
  394. self.data[i].index = $(".draggable-element[data-index=" + i + "]").attr("data-index");
  395. self.data[i].sort = $(".draggable-element[data-index=" + i + "]").attr("data-sort");
  396. }
  397. // 如果当前编辑的组件不存在了,则选中最后一个
  398. if (parseInt(self.currentIndex) >= self.data.length) self.currentIndex--;
  399. $(".draggable-element[data-index=" + self.currentIndex + "] .edit-attribute .attr-wrap").css("height", ($(window).height() - 135) + "px");
  400. }, 50);
  401. },
  402. //转换图片路径
  403. changeImgUrl: function (url) {
  404. if (url == null || url === "") return '';
  405. if (url.indexOf("static/img/") > -1) return ns.img(ns_url.staticImg + "/" + url.replace("public/static/img/", ""));
  406. else return ns.img(url);
  407. },
  408. //设置全局对象属性
  409. setGlobal: function (obj) {
  410. for (var k in obj) {
  411. if (k) this.$set(this.global, k, obj[k]);
  412. }
  413. this.globalLazyLoad = true;
  414. },
  415. verify: function () {
  416. if (this.global.title === "") {
  417. layer.msg('请输入页面名称');
  418. this.currentIndex = -99;
  419. this.refresh();
  420. return false;
  421. } else if (this.global.title.length > 15) {
  422. layer.msg('页面名称最多15个字符');
  423. this.currentIndex = -99;
  424. this.refresh();
  425. return false;
  426. }
  427. if (this.global.popWindow.count !== -1 && this.global.popWindow.imageUrl === '') {
  428. layer.msg('请上传弹框广告');
  429. this.currentIndex = -99;
  430. this.refresh();
  431. return false;
  432. }
  433. for (var i = 0; i < this.data.length; i++) {
  434. try {
  435. if (this.data[i].verify) {
  436. for (var j = 0; j < this.data[i].verify.length; j++) {
  437. var res = this.data[i].verify[j](i);
  438. if (!res.code) {
  439. this.currentIndex = i;
  440. this.refresh();
  441. layer.msg(res.message);
  442. return false;
  443. }
  444. }
  445. }
  446. } catch (e) {
  447. console.log("verify Error:", e, i, this.data[i]);
  448. }
  449. }
  450. return true;
  451. },
  452. // 定位组件位置
  453. autoSelected(componentName) {
  454. for (var i in this.data) {
  455. if (this.data[i].componentName === componentName) {
  456. this.changeCurrentIndex(this.data[i].index);
  457. var element = $('.preview-wrap .preview-restore-wrap [data-index="' + this.data[i].index + '"]'),
  458. warp = $(".preview-wrap .preview-restore-wrap .div-wrap"),
  459. warpTop = warp.offset().top,
  460. warpBottom = warpTop + warp.height(),
  461. elementTop = element.offset().top,
  462. elementBottom = elementTop + element.height(),
  463. scrollTop = warp.scrollTop();
  464. if (elementBottom > warpBottom) {
  465. scrollTop += (elementBottom - warpBottom) + 2;
  466. } else if (warpTop > elementTop) {
  467. scrollTop -= (warpTop - elementTop);
  468. }
  469. warp.animate({scrollTop: scrollTop}, 300);
  470. return;
  471. }
  472. }
  473. },
  474. // 刷新组件数据
  475. refreshComponent: function () {
  476. if ($("#info").length === 0) return;
  477. var info = JSON.parse($("#info").val().toString());// .replace(/\@/g, "'"));
  478. if (Object.keys(info).length) {
  479. for (var i = 0; i < info.value.length; i++) {
  480. info.value[i].index = 0;
  481. info.value[i].sort = 0;
  482. info.value[i].lazyLoadCss = false; // 资源懒加载,防止看到界面缓慢加载
  483. info.value[i].lazyLoad = false; // 资源懒加载,防止看到界面缓慢加载
  484. info.value[i].outerCountJs = 0;
  485. info.value[i].ignore = []; // 忽略模板属性
  486. info.value[i].tempData = {}; // 临时数据
  487. // info.value[i].id = ns.gen_non_duplicate(5);
  488. }
  489. }
  490. this.setGlobal(info.global);
  491. if(info.value.length) {
  492. this.data = info.value;
  493. this.changeCurrentIndex(0); // 选择第一个
  494. this.refresh();
  495. }
  496. fullScreenSize();
  497. $('.loading-layer').hide();
  498. $('.preview-wrap .preview-restore-wrap').css('visibility', 'visible');
  499. },
  500. /**
  501. * 刷新快捷操作后的展示
  502. * @param isScroll false:滚动,true:不滚动
  503. */
  504. refreshQuick: function (isScroll) {
  505. var self = this;
  506. vue.$nextTick(function () {
  507. if (!isScroll) {
  508. var element = $(".draggable-element[data-index=" + self.currentIndex + "]");
  509. var warp = $(".preview-wrap .preview-restore-wrap .div-wrap");
  510. var height = 0;
  511. $(".draggable-element:lt(" + (element.index() + 1) + ")").each(function (i) {
  512. height += $(this).outerHeight();
  513. });
  514. height -= element.outerHeight() + 30;
  515. warp.animate({scrollTop: height}, 300);
  516. }
  517. });
  518. }
  519. }
  520. });
  521. // 自适应全屏宽高
  522. function fullScreenSize(isFull) {
  523. var size = 0;
  524. if (baseStyle === 'app/shop/view/base/style1.html') size = 189; // 公式:头部layui-header(61px)+ 面包屑crumbs(44px) + 自定义模板区域上内边距diyview(20px) + 底部保存按钮(90px)
  525. else size = 139; // 公式:二级面包屑layui-header-crumbs-second (55px)+ 自定义模板区域上内边距diyview(20px) + 底部保存按钮(90px)
  526. if (isFull) size = 75; // 顶部面包屑(55px) + 自定义模板区域上内边距diyview(20px)
  527. var commonHeight = $(window).height() - size;
  528. ['.preview-wrap .preview-restore-wrap .div-wrap'].forEach(function (obj) {
  529. $(obj).css("height", (commonHeight) + "px");
  530. });
  531. $('.loading-layer').css('height', (commonHeight - 70) + "px"); // 70px是头部高度
  532. $(".component-list nav").css("height", (commonHeight + 20 - 55) + "px");// 20px是自定义模板区域上内边距,55px是标准/第三方组件tab切换高度
  533. $(".edit-attribute .attr-wrap").css("height", (commonHeight - 1) + "px");// 1px是上边框
  534. $(".preview-block").css("min-height", (commonHeight - 104) + "px"); // 公式:高度 - 自定义模板区域上内边距(20px) - 自定义模板区域下外编辑(20px)- 自定义模板头部(64px)
  535. }
  536. var form, repeat_flag = false;//防重复标识
  537. layui.use(['form'], function () {
  538. form = layui.form;
  539. form.render();
  540. fullScreenSize();
  541. if ($("#info").val()) {
  542. vue.refreshComponent();
  543. } else {
  544. if($("#title").val()) vue.global.title = $("#title").val();
  545. vue.globalLazyLoad = true;
  546. }
  547. // 标准/第三方组件切换
  548. $("body").on("click", ".component-list .tab span", function () {
  549. var type = $(this).attr("data-type");
  550. $('.component-list h3').hide();
  551. $('.component-list ul').hide();
  552. if(type === 'EXTEND'){
  553. $('.component-list h3[data-type="EXTEND"]').show();
  554. $('.component-list ul[data-type="EXTEND"]').show();
  555. }else{
  556. $('.component-list h3[data-type!="EXTEND"]').show();
  557. $('.component-list ul[data-type!="EXTEND"]').show();
  558. }
  559. $(this).addClass('selected').siblings().removeClass('selected');
  560. });
  561. // 组件列表
  562. $("body").on("click", ".component-list h3", function () {
  563. var index = $(this).attr("data-index");
  564. var ul = $(".component-list ul[data-index='" + index + "']");
  565. if (ul.height()) {
  566. $(this).find("img").attr("src", ns_url.staticExt + "/diyview/img/component_right.png");
  567. if (!ul.attr("data-height")) ul.attr("data-height", ul.height());
  568. ul.animate({opacity: 0, height: 0}, 100);
  569. } else {
  570. $(this).find("img").attr("src", ns_url.staticExt + "/diyview/img/component_down.png");
  571. ul.animate({opacity: 1, height: ul.attr("data-height") + "px"}, 100);
  572. }
  573. });
  574. $("body").on("click", ".edit-attribute .attr-wrap .restore-wrap .attr-title .tab-wrap span", function () {
  575. $(this).addClass('active bg-color').siblings().removeClass('active bg-color');
  576. var type = $(this).attr('data-type');
  577. $(this).parent().parent().parent().find('.edit-content-wrap').hide();
  578. $(this).parent().parent().parent().find('.edit-style-wrap').hide();
  579. $(this).parent().parent().parent().find(`.edit-${type}-wrap`).show();
  580. });
  581. // 处理全屏切换事件
  582. // 事件开始,通过添加顶级样式控制按钮相关展示
  583. // 底部全屏按钮,隐藏菜单,添加顶级样式,top-full-screen
  584. $('body').on('click', '.full-screen-btn', function () {
  585. $('body').find('.layui-header').hide(); //顶部菜单
  586. $('body').find('.layui-side').hide(); //侧边菜单
  587. $('body').find('.layui-layout-admin .crumbs').hide(); //面包屑
  588. $('body').addClass('top-full-screen'); //添加顶级样式,处理大板块结构
  589. // 隐藏底部按钮,放开头部按钮
  590. $('body').find('.js-bottom-custom-save').hide(); //底部按钮隐藏
  591. $('body').find('.js-top-custom-save').removeClass('layui-hide'); //顶部按钮放开
  592. $('body').find('.main-contact').hide(); //全屏右侧帮助不展示
  593. // 全屏需处理适应大小
  594. fullScreenSize(true);
  595. });
  596. // 顶部恢复按钮,与底部按钮完全相反
  597. $('body').on('click', '.cancel-btn', function () {
  598. $('body').find('.layui-header').show(); //顶部菜单
  599. $('body').find('.layui-side').show(); //侧边菜单
  600. $('body').find('.layui-layout-admin .crumbs').show(); //面包屑
  601. $('body').removeClass('top-full-screen'); //添加顶级样式,处理大板块结构
  602. // 隐藏底部按钮,放开头部按钮
  603. $('body').find('.js-bottom-custom-save').show(); //底部按钮隐藏
  604. $('body').find('.js-top-custom-save').addClass('layui-hide'); //顶部按钮放开
  605. // 全屏需处理适应大小
  606. fullScreenSize();
  607. });
  608. /**
  609. * 绑定拖拽事件
  610. */
  611. $('.preview-block').DDSort({
  612. //拖拽数据源
  613. target: '.draggable-element',
  614. //拖拽时显示的样式
  615. floatStyle: {
  616. 'border': '1px solid',
  617. 'background-color': '#ffffff'
  618. },
  619. //设置可拖拽区域
  620. draggableArea: "preview-draggable",
  621. //拖拽中,隐藏右侧编辑属性栏
  622. move: function (index) {
  623. var curr = $(".draggable-element[data-index='" + index + "'] .edit-attribute");
  624. if (curr.attr("data-have-edit") == 1) curr.hide();
  625. },
  626. //拖拽结束后,选择当前拖拽,并且显示右侧编辑属性栏,刷新数据
  627. up: function (beforeIndex,afterIndex) {
  628. var temp = [];
  629. $('.draggable-element').each(function (index) {
  630. var dIndex = $(this).attr('data-index');
  631. temp[index] = vue.deepClone(vue.data[dIndex]);
  632. });
  633. temp.forEach(function (item, index) {
  634. item.index = index;
  635. item.id = ns.gen_non_duplicate(5); // 更新id,刷新组件数据
  636. vue.$set(vue.data, index, item)
  637. });
  638. vue.currentIndex = afterIndex;
  639. $(".draggable-element.selected .edit-attribute").show();
  640. }
  641. });
  642. // 保存
  643. $("button.save").click(function () {
  644. // 刷新排序
  645. vue.refresh();
  646. setTimeout(function () {
  647. if (vue.verify()) {
  648. //全局属性
  649. var global = JSON.stringify(vue.global);
  650. global = eval("(" + global + ")");
  651. //组件属性
  652. var value = JSON.stringify(vue.data); // .replace(/\@/g, "");
  653. value = JSON.parse(value);
  654. //重新排序
  655. value.sort(function (a, b) {
  656. return a.sort - b.sort;
  657. });
  658. for (var k in value) {
  659. value[k].ignore.forEach((item, index) => {
  660. if (item.indexOf('margin') !== -1) delete value[k].margin[item.split('margin')[1].toLowerCase()];
  661. else delete value[k][item];
  662. });
  663. delete value[k].ignore;
  664. // delete value[k].hidden;
  665. delete value[k].ignoreLoad;
  666. delete value[k].verify;
  667. delete value[k].lazyLoad;
  668. delete value[k].lazyLoadCss;
  669. delete value[k].index;
  670. delete value[k].sort;
  671. delete value[k].outerCountJs;
  672. delete value[k].tempData; // 临时数据
  673. }
  674. var v = {
  675. global: global,
  676. value: value
  677. };
  678. // console.log(v);
  679. // console.log(JSON.stringify(v));
  680. // return false;
  681. if (repeat_flag) return;
  682. repeat_flag = true;
  683. $.ajax({
  684. type: "post",
  685. url: ns.url(requestUrl),
  686. data: {
  687. id: $("#id").val(),
  688. name: $("#name").val(),
  689. template_id: $("#template_id").val(),
  690. page_type: $("#page_type").val(), // 页面类型
  691. title: vue.global.title,
  692. value: JSON.stringify(v), // .replace(/\'/g, "@")
  693. site_id: ns_url.siteId,
  694. app_module: ns_url.appModule
  695. },
  696. dataType: "JSON",
  697. success: function (res) {
  698. layer.msg(res.message);
  699. if (res.code == 0) {
  700. if ($("#id").val() || $("#name").val().indexOf('DIY_VIEW_RANDOM_') == -1) {
  701. // location.reload(); // 不刷新
  702. repeat_flag = false;
  703. } else {
  704. location.href = ns.url("shop/diy/lists");
  705. // location.href = ns.url(returnList);
  706. }
  707. } else {
  708. repeat_flag = false;
  709. }
  710. }
  711. });
  712. }
  713. }, 100);
  714. });
  715. });
  716. // 预览
  717. function preview() {
  718. window.open(ns.url('index/index/h5preview', {id: $('#id').val(), type: 'page'}));
  719. }