edit.php 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682
  1. <script src="/resources/js/html2canvas.min.js"></script>
  2. <script>
  3. function getRandomId(list = []) {
  4. var initArr = [] // getInitArr(); // 拿到已有的id -- 一般不会冲突
  5. // 随机id不能和以前编辑冲突
  6. function generateId() {
  7. const s = [];
  8. const hexDigits = '0123456789abcdef';
  9. for (let i = 0; i < 36; i++) {
  10. s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
  11. }
  12. s[14] = '4';
  13. s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
  14. s[8] = s[13] = s[18] = s[23] = '-';
  15. const uuid = s.join('');
  16. if (initArr.some(str => (str === uuid))) { // 存在已有数据
  17. return generateId()
  18. }
  19. return uuid;
  20. }
  21. function getInitArr() {
  22. return list.map(item => {
  23. return item.id
  24. })
  25. }
  26. var id = generateId()
  27. return id
  28. }
  29. function deepClone(obj) {
  30. var o, i, j, k;
  31. if (typeof(obj) != "object" || obj === null) return obj;
  32. if (obj instanceof(Array)) {
  33. o = [];
  34. i = 0;
  35. j = obj.length;
  36. for (; i < j; i++) {
  37. if (typeof(obj[i]) == "object" && obj[i] != null) {
  38. o[i] = arguments.callee(obj[i]);
  39. } else {
  40. o[i] = obj[i];
  41. }
  42. }
  43. } else {
  44. o = {};
  45. for (i in obj) {
  46. if (typeof(obj[i]) == "object" && obj[i] != null) {
  47. o[i] = arguments.callee(obj[i]);
  48. } else {
  49. o[i] = obj[i];
  50. }
  51. }
  52. }
  53. return o;
  54. }
  55. function getObjValue(target = {}, keys = [], def) {
  56. let obj = deepClone(target)
  57. try {
  58. value = keys.reduce((prev, key) => {
  59. return prev[key]
  60. }, target)
  61. } catch (error) {
  62. value = def
  63. }
  64. return value || def
  65. }
  66. function deepEqual(x, y) {
  67. // 指向同一内存时
  68. if (x === y) {
  69. return true;
  70. } else if ((typeof x == "object" && x != null) && (typeof y == "object" && y != null)) {
  71. if (Object.keys(x).length !== Object.keys(y).length) {
  72. return false;
  73. }
  74. for (var prop in x) {
  75. if (y.hasOwnProperty(prop)) {
  76. if (!deepEqual(x[prop], y[prop])) return false;
  77. } else {
  78. return false;
  79. }
  80. }
  81. return true;
  82. } else {
  83. return false;
  84. }
  85. }
  86. var basicMixins = {
  87. data() {
  88. return {
  89. defaultForm: {
  90. // default_color: "transparer",
  91. colorInfos: ['底部背景'],
  92. sizeInfos: ['上边距', '下边距', '左右边距'],
  93. radiusInfos: ['上圆角', '下圆角'],
  94. defColor: "transparent",
  95. tabItem: null,
  96. },
  97. result: {},
  98. searchIpts: {},
  99. diy_img_prefix: '<?= Yii::getAlias('@attachurl') ?>'
  100. }
  101. },
  102. created() {
  103. this.init()
  104. },
  105. // 这里是基础的修改 - 需要其他自行添加对应方法-数据
  106. methods: {
  107. init() {
  108. // 设置默认值
  109. this.result = deepClone(this.activeItem)
  110. this.searchIpts = (this.result && this.result.computedStyle && this.result.computedStyle.searchIpts) || {}
  111. // 描边颜色默认值
  112. let arr = ['computedStyle', 'tabItem']
  113. this.defaultForm.tabItem = getObjValue(this.result, arr, this.defaultForm.tabItem)
  114. // 选择颜色样式
  115. let arr1 = ['computedStyle', 'searchBox']
  116. var colorArr = [{
  117. name: '底部背景', //名字
  118. color: getObjValue(this.result, arr1, "transparent"), //颜色
  119. showAlpha: false, //是否开启透明度
  120. }, ]
  121. this.defaultForm.colorInfos = colorArr
  122. // 边距
  123. let arr2 = ['computedStyle', 'searchIpts', 'marginBottom']
  124. let arr3 = ['computedStyle', 'searchIpts', 'marginLeft']
  125. let arr4 = ['computedStyle', 'searchIpts', 'marginTop']
  126. var marginArr = [{
  127. name: '上边距', //名字
  128. value: parseFloat(getObjValue(this.result, arr4, 0)), //值
  129. unit: "px", //单位
  130. disabled: false, //是否禁用
  131. maxValue: 50,
  132. }, {
  133. name: '下边距', //名字
  134. value: parseFloat(getObjValue(this.result, arr2, 0)), //值
  135. unit: "px", //单位
  136. disabled: false, //是否禁用
  137. maxValue: 50,
  138. }, {
  139. name: '左右边距', //名字
  140. value: parseFloat(getObjValue(this.result, arr3, 0)), //值
  141. unit: "px", //单位
  142. disabled: false, //是否禁用
  143. maxValue: 20,
  144. }]
  145. this.defaultForm.sizeInfos = marginArr
  146. // 圆角
  147. let arr5 = ['computedStyle', 'searchIpts', 'border-top-left-radius']
  148. let arr6 = ['computedStyle', 'searchIpts', 'border-bottom-right-radius']
  149. var radiusArr = [{
  150. name: '上圆角', //名字
  151. value: parseFloat(getObjValue(this.result, arr5, 0)), //值
  152. unit: "px", //单位
  153. disabled: false, //是否禁用
  154. maxValue: 20,
  155. }, {
  156. name: '下圆角', //名字
  157. value: parseFloat(getObjValue(this.result, arr6, 0)), //值
  158. unit: "px", //单位
  159. disabled: false, //是否禁用
  160. maxValue: 20,
  161. }]
  162. this.defaultForm.radiusInfos = radiusArr
  163. },
  164. // 基础逻辑
  165. colorSelect(e) { // 样式
  166. let tabItem = this.defaultForm.tabItem || {}
  167. this.defaultForm.tabItem = Object.assign(tabItem, e.item)
  168. this.updataResult(this.defaultForm.tabItem, 'tabItem')
  169. },
  170. sliderChange(arr, type, showUnit = true) { // 边距
  171. let top = arr[0] && (showUnit ? (arr[0].value + arr[0].unit) : arr[0].value)
  172. let bottom = arr[1] && (showUnit ? (arr[1].value + arr[1].unit) : arr[1].value)
  173. let lr = arr[2] && (showUnit ? (arr[2].value + arr[2].unit) : arr[2].value)
  174. const style = {
  175. marginTop: top,
  176. marginBottom: bottom,
  177. marginLeft: lr,
  178. marginRight: lr
  179. }
  180. this.updataResult(Object.assign(this.searchIpts, style), 'searchIpts')
  181. },
  182. RadiusChange(arr) {
  183. let top = arr[0].value + arr[0].unit
  184. let bottom = arr[1].value + arr[1].unit
  185. const style = {
  186. 'border-top-left-radius': top,
  187. 'border-top-right-radius': top,
  188. 'border-bottom-right-radius': bottom,
  189. 'border-bottom-left-radius': bottom
  190. }
  191. this.updataResult(Object.assign(this.searchIpts, style), 'searchIpts')
  192. },
  193. updateColor(e) { // 选择颜色
  194. this.result.computedStyle.searchBox = e[0].color
  195. this.$emit("update", this.result)
  196. },
  197. updataResult(style, name) {
  198. var result = deepClone(style)
  199. this.result.computedStyle[name] = result
  200. this.$emit("update", this.result)
  201. },
  202. iptSelect(e) {
  203. e.currentTarget.select();
  204. },
  205. }
  206. }
  207. //16进制颜色转化为RGB颜色
  208. function colorRgb(str) {
  209. var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
  210. var sColor = str.toLowerCase();
  211. if (sColor && reg.test(sColor)) {
  212. if (sColor.length === 4) {
  213. var sColorNew = "#";
  214. for (var i = 1; i < 4; i += 1) {
  215. sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
  216. }
  217. sColor = sColorNew;
  218. }
  219. //处理六位的颜色值
  220. var sColorChange = [];
  221. for (var i = 1; i < 7; i += 2) {
  222. sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2)));
  223. }
  224. return "rgb(" + sColorChange.join(",") + ")";
  225. } else {
  226. return sColor;
  227. }
  228. }
  229. //判断是否为亮色
  230. function getContrastYIQ(hexcolor) {
  231. try {
  232. var colorrgb = colorRgb(hexcolor);
  233. var colors = colorrgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
  234. var red = colors[1];
  235. var green = colors[2];
  236. var blue = colors[3];
  237. var brightness;
  238. brightness = (red * 299) + (green * 587) + (blue * 114);
  239. brightness = brightness / 255000;
  240. if (brightness >= 0.5) {
  241. return "light";
  242. } else {
  243. return "dark";
  244. }
  245. } catch (e) {
  246. return "light";
  247. }
  248. }
  249. // 获取链接选择其他链接类型中文
  250. function getOtherUrlName(type) {
  251. let text = "";
  252. if (type == "call") {
  253. text = "拨打电话"
  254. } else if (type == "web") {
  255. text = "自定义链接"
  256. } else if (type == "applet") {
  257. text = "小程序"
  258. }
  259. return text;
  260. }
  261. // dom生成canvas
  262. function domToCanvas(contain) {
  263. var canvas2 = document.createElement("canvas");
  264. var w = $(contain).outerWidth();
  265. var h = $(contain).outerHeight();
  266. //先放大2倍,后期缩小,处理模糊问题
  267. canvas2.width = w * 2;
  268. canvas2.height = h * 2;
  269. canvas2.style.width = w + "px";
  270. canvas2.style.height = h + "px";
  271. var context = canvas2.getContext("2d");
  272. // 进行缩放
  273. context.scale(2, 2);
  274. return new Promise((resolve, reject) => {
  275. html2canvas(document.querySelector(contain), {
  276. canvas: canvas2,
  277. allowTaint: true, //允许污染
  278. taintTest: true, //在渲染前测试图片
  279. useCORS: true //使用跨域
  280. }).then(function(canvas) {
  281. resolve(canvas)
  282. });
  283. })
  284. }
  285. function getNavData() {
  286. const result = {
  287. "id": getRandomId(),
  288. "data": {
  289. "logoSrc": "",
  290. "backSrc": "",
  291. "isShowName": "1",
  292. "isShowSearch": "1",
  293. "mainPage": { // 主页面设置
  294. "defColor": "#fff",
  295. "imageUrl": [],
  296. "name": "标题",
  297. "type": "纯色"
  298. },
  299. },
  300. "computedStyle": {
  301. "mode": 1,
  302. "style": 1,
  303. "titleSwitch": true,
  304. "titleMode": 1,
  305. "imageUrl": [],
  306. "position": 1,
  307. "searchSwitch": true,
  308. "searchBgColor": "",
  309. "defaultForm": {
  310. "defColor": "transparer"
  311. },
  312. "titleDistance": {
  313. "fontSize": 16,
  314. "height": 30,
  315. "width": 57,
  316. "marginLeft": 20
  317. },
  318. "statusBar": "1",
  319. "searchMarPad": {
  320. "marginLeft": 20,
  321. "paddingLeft": 14
  322. }
  323. },
  324. "permission": [],
  325. "funcListItem": {
  326. "type": "nav",
  327. "title": "顶部导航",
  328. "is_top": 1
  329. },
  330. "isShow": true,
  331. "is_show_permission": false,
  332. "is_drag": false,
  333. "disabled": true,
  334. }
  335. return result
  336. }
  337. function getNavLocationData() {
  338. const result = {
  339. "id": getRandomId(),
  340. "data": {
  341. "logoSrc": "",
  342. "backSrc": "",
  343. "isShowName": "1",
  344. "isShowSearch": "1",
  345. "locationStyle": "1",
  346. "mainPage": { // 主页面设置
  347. "defColor": "#fff",
  348. "imageUrl": [],
  349. "name": "标题",
  350. "type": "纯色"
  351. },
  352. },
  353. "computedStyle": {
  354. "mode": 1,
  355. "style": 1,
  356. "titleSwitch": true,
  357. "titleMode": 1,
  358. "imageUrl": [],
  359. "position": 1,
  360. "searchSwitch": true,
  361. "searchBgColor": "",
  362. "defaultForm": {
  363. "defColor": "transparer"
  364. },
  365. "titleDistance": {
  366. "fontSize": 16,
  367. "height": 30,
  368. "width": 57,
  369. "marginLeft": 20
  370. },
  371. "statusBar": "1",
  372. "statusCapsule": "1",
  373. "searchMarPad": {
  374. "marginLeft": 20,
  375. "paddingLeft": 14
  376. }
  377. },
  378. "permission": [],
  379. "funcListItem": {
  380. "type": "nav-location",
  381. "title": "顶部定位导航",
  382. "is_top": 1
  383. },
  384. "isShow": true,
  385. "is_show_permission": false,
  386. "is_drag": false,
  387. "disabled": true,
  388. }
  389. return result
  390. }
  391. function getGoodsParse() {
  392. const result = {
  393. "id": getRandomId(),
  394. "data": {
  395. "sizeInfos": [{
  396. "disabled": false,
  397. "hidden": false,
  398. "maxValue": 50,
  399. "name": "上边距",
  400. "unit": "px",
  401. "value": 0
  402. },
  403. {
  404. "disabled": false,
  405. "hidden": false,
  406. "maxValue": 50,
  407. "name": "下边距",
  408. "unit": "px",
  409. "value": 0
  410. },
  411. {
  412. "disabled": false,
  413. "hidden": false,
  414. "maxValue": 20,
  415. "name": "左右边距",
  416. "unit": "px",
  417. "value": 0
  418. }
  419. ]
  420. },
  421. "computedStyle": {
  422. "aroundMargin": "0px",
  423. "marginBottom": "0px",
  424. "marginTop": "0px"
  425. },
  426. "permission": [],
  427. "funcListItem": {
  428. "count": 1,
  429. "img": "resources/img/decorate/func/goods-info.png",
  430. "type": "goods-parse",
  431. "title": "商品详情",
  432. "permission": {
  433. "is_copy": false,
  434. "is_delete": false,
  435. "is_show": false
  436. }
  437. },
  438. "isShow": true,
  439. "is_copy": false,
  440. "is_delete": false,
  441. "is_show": false,
  442. "is_show_permission": true,
  443. "visible": false,
  444. }
  445. return result
  446. }
  447. function debounce(func, wait) {
  448. let timer;
  449. return function() {
  450. let context = this; // 注意 this 指向
  451. let args = arguments; // arguments中存着e
  452. if (timer) clearTimeout(timer);
  453. timer = setTimeout(() => {
  454. func.apply(this, args)
  455. }, wait)
  456. }
  457. }
  458. </script>
  459. <?php
  460. use common\helpers\ComponentHelper;
  461. use common\models\diy\DiyComponents;
  462. $diyPath = Yii::$app->viewPath . '/components/common/diy/common';
  463. $stylePath = Yii::$app->viewPath . '/components/common/diy/style-components';
  464. $components = DiyComponents::getDir($diyPath, $diyPath);
  465. $styleComponents = DiyComponents::getDir($stylePath, $stylePath);
  466. ComponentHelper::loadComponentView('diy/diy-func-list');
  467. ComponentHelper::loadComponentView('diy/diy-preview');
  468. ComponentHelper::loadComponentView('diy/diy-style-preview');
  469. ComponentHelper::loadComponentView('diy/popup-advertisement');
  470. ComponentHelper::loadComponentView('diy/bargain-popup');
  471. if (!empty($addons_components)) {
  472. // 组件ID
  473. $view_arr = ['preview', 'style'];
  474. foreach ($addons_components as $item) {
  475. $basc_path = Yii::getAlias('@addons') . "/{$item['source']}/backend/views/components/diy/{$item['code']}";
  476. // 加载组件
  477. foreach ($view_arr as $view) {
  478. // 文件
  479. $file_path = $basc_path . DIRECTORY_SEPARATOR . $view . '.php';
  480. if (is_file($file_path)) {
  481. ComponentHelper::loadComponentView($view, $basc_path);
  482. }
  483. }
  484. }
  485. }
  486. foreach ($components as $item) {
  487. ComponentHelper::loadComponentView("diy/common/{$item}");
  488. }
  489. foreach ($styleComponents as $item) {
  490. ComponentHelper::loadComponentView("diy/style-components/{$item}");
  491. }
  492. $currentRoute = Yii::$app->controller->route;
  493. ?>
  494. <div id="app" class="fixed">
  495. <div class="diy-header flex">
  496. <div class="flex flex-y-center" style="width: 300px;">
  497. <div @click="jumpList('gohistory')" class="diy-header-backs">
  498. <i class="el-icon-arrow-left"></i>
  499. <span>返回</span>
  500. </div>
  501. <div class="temp-select" v-if="pageType == 2">
  502. <el-select size="small" v-model="cate_id" placeholder="请选择">
  503. <el-option v-for="item in cate_arr" :key="item.label" :label="item.label" :value="item.value">
  504. </el-option>
  505. </el-select>
  506. <div class="cate">所属分类:</div>
  507. </div>
  508. </div>
  509. <div class="custom-page">自定义页面装修</div>
  510. <div class="flex flex-y-center diy_nav_right" style="width: 445px; justify-content: flex-end;">
  511. <div class="page_set xiaoshou" @click="pageSet">
  512. <i class="el-icon-setting "></i>
  513. <span>页面设置</span>
  514. </div>
  515. <!-- <div class="save_as_template xiaoshou" @click="popupVisible = true">
  516. 另存为模板
  517. </div> -->
  518. <div style="width: 215px; text-align: right;">
  519. <el-button type="info" size="small" @click="savePreview">生成预览图</el-button>
  520. </div>
  521. <div class="sava_btn">
  522. <el-button style="padding: 6px 15px;" type="primary" size="small" @click="jumpList('save')" :loading="saveLoading">
  523. <i class="el-icon-s-promotion save"></i>
  524. <span>保存设置</span>
  525. </el-button>
  526. </div>
  527. </div>
  528. </div>
  529. <div class="diy-contain flex" v-loading="loading">
  530. <div class="diy-func-list">
  531. <diy-func-list @end="onEnd" :test-data="onClick" :list="pageComponentsList" @add-prev-item="onClick"></diy-func-list>
  532. </div>
  533. <diy-preview :active-index="activeIndex" :list="previewList" @select-item="selectItme" @change-move="changeMove" :is-show-location="isShowLocation"></diy-preview>
  534. <transition name="fade">
  535. <diy-style-preview :active-item="activeItem" :active-index="activeIndex" :permission="permission" :other-page-data="otherPageData" @update="changeDiyItem" @updateother="changeOtherDiyItem" v-if="showUpdate"></diy-style-preview>
  536. </transition>
  537. </div>
  538. <!-- 弹窗广告弹窗 -->
  539. <el-dialog :close-on-click-modal="false" title="弹窗广告设置" :width="'634px'" append-to-body :visible.sync="popupVisible">
  540. <popup-advertisement :data="initAlearData" @change="onAlertChange"></popup-advertisement>
  541. <span slot="footer" class="dialog-footer">
  542. <el-button @click="popupVisible = false">取 消</el-button>
  543. <el-button type="primary" @click="popupVisible = false">确 定</el-button>
  544. </span>
  545. </el-dialog>
  546. <!-- 砍价弹窗 -->
  547. <el-dialog :close-on-click-modal="false" title="砍价成功提示设置" :width="'634px'" append-to-body :visible.sync="bargainPopupVisible">
  548. <bargain-popup :data="initBargainData" @change="onBargainChange"></bargain-popup>
  549. <span slot="footer" class="dialog-footer">
  550. <el-button @click="bargainPopupVisible = false">取 消</el-button>
  551. <el-button type="primary" @click="bargainPopupVisible = false">确 定</el-button>
  552. </span>
  553. </el-dialog>
  554. <!-- 预览弹窗 -->
  555. <el-dialog :close-on-click-modal="false" title="预览弹窗" :modal-append-to-body="false" :visible.sync="dialogFormVisible">
  556. <div v-loading="previewloading" class="flex flex-x-center" style="height: 600px;overflow:auto">
  557. <img id="diy-test-preview" :src="previewSrc" />
  558. </div>
  559. <div slot="footer" class="dialog-footer">
  560. <el-button @click="dialogFormVisible = false">取 消</el-button>
  561. <el-button type="primary" @click="dialogFormVisible = false">确 定</el-button>
  562. </div>
  563. </el-dialog>
  564. </div>
  565. <script>
  566. Vue.prototype.$vailComm = false; // 是否开启校验组件
  567. Vue.prototype.$capitals = []; // 用户信息对象 - member-info
  568. Vue.prototype.$level_sel = []; // 用户信息对象 - member-info
  569. Vue.prototype.$eventBus = new Vue();
  570. var PageShareData = {
  571. pageIndex: false // 点击页面设置标识 => nav/style
  572. }; // 全局共享数据 - 没有响应式
  573. new Vue({
  574. el: '#app',
  575. data() {
  576. return {
  577. capital: [],
  578. dialogFormVisible: false,
  579. /**
  580. * @descriptions 浮动()、普通()、置顶(isTop)、弹窗(alert)四种显示组件
  581. * @param {string} type // diy组件类型
  582. * @param {string} title // 二级标题
  583. * @param {string} img // diy组件图片
  584. * @param {number} count // 可添加diy组件数量
  585. * @param {boolean} is_top // diy组件是否置顶组件 - 待添加 - 分添加等级 1 2 3 数字靠前权限越高
  586. * @param {object} permission // 组件的不可操作权限 {"is_delete": false,"is_drag": false,"disabled": true, // 禁用操作}
  587. */
  588. pageComponentsList: [{
  589. title: '一级标题', // 一级标题
  590. list: [{
  591. type: 'search', // diy组件类型
  592. title: '搜索', // 二级标题
  593. img: 'resources/img/decorate/func/search.png',
  594. }, {
  595. type: 'button-group', // diy组件类型
  596. title: '按钮组', // 二级标题
  597. img: 'resources/img/decorate/func/button-group.png',
  598. }, {
  599. type: 'placard', // diy组件类型
  600. title: '公告', // 二级标题
  601. img: 'resources/img/decorate/func/placard.png',
  602. }, {
  603. type: 'video', // diy组件类型
  604. title: '视频', // 二级标题
  605. img: 'resources/img/decorate/func/video.png',
  606. },
  607. {
  608. type: 'graphic-details', // diy组件类型
  609. title: '图文详情', // 二级标题
  610. img: 'resources/img/decorate/func/graphic-details.png',
  611. },
  612. {
  613. type: 'auxiliary-blank', // diy组件类型
  614. title: '辅助空白', // 二级标题
  615. img: 'resources/img/decorate/func/auxiliary-blank.png',
  616. },
  617. {
  618. type: 'auxiliary-line', // diy组件类型
  619. title: '辅助线', // 二级标题
  620. img: 'resources/img/decorate/func/auxiliary-line.png',
  621. },
  622. {
  623. type: "title-block",
  624. title: "标签栏",
  625. img: 'resources/img/decorate/func/title-block.png',
  626. },
  627. {
  628. type: 'rubik-cube', // diy组件类型
  629. title: '魔方', // 二级标题
  630. img: 'resources/img/decorate/func/rubik-cube.png',
  631. },
  632. {
  633. type: 'img-ad', // diy组件类型
  634. title: '图片广告', // 二级标题
  635. img: 'resources/img/decorate/func/img-ad.png',
  636. },
  637. {
  638. type: 'micro-theme', // diy组件类型
  639. title: '微主题', // 二级标题
  640. img: 'resources/img/decorate/func/micro-theme.png',
  641. },
  642. {
  643. type: 'carousel-img', // diy组件类型
  644. title: '轮播广告', // 二级标题
  645. img: 'resources/img/decorate/func/carousel-img.png',
  646. },
  647. {
  648. type: 'graphic-details', // diy组件类型
  649. title: '模板', // 二级标题
  650. img: 'resources/img/decorate/func/search.png',
  651. },
  652. {
  653. type: 'goods-info', // diy组件类型
  654. title: '商品信息', // 二级标题
  655. count: 1,
  656. is_top: 2,
  657. img: 'resources/img/decorate/func/goods-info.png',
  658. permission: {
  659. "is_drag": false,
  660. "is_up": false,
  661. "is_down": false,
  662. "is_copy": false
  663. } // 禁用操作
  664. },
  665. {
  666. type: 'goods-comment', // diy组件类型
  667. title: '商品评价', // 二级标题
  668. img: 'resources/img/decorate/func/goods-comment.png'
  669. },
  670. {
  671. type: 'clock-in', // diy组件类型
  672. title: '打卡', // 二级标题
  673. img: 'resources/img/decorate/func/goods-comment.png'
  674. }
  675. ]
  676. }, {
  677. title: '二级标题', // 一级标题
  678. list: [{
  679. type: 'commodity-group', // diy组件类型
  680. title: '商品组', // 二级标题
  681. img: 'resources/img/decorate/func/commodity-group.png',
  682. },
  683. {
  684. type: 'order-broadcast', // diy组件类型
  685. title: '订单播报', // 二级标题
  686. img: 'resources/img/decorate/func/order-broadcast.png',
  687. count: 1,
  688. },
  689. {
  690. type: 'commodity-group-tab', // diy组件类型
  691. title: '选项卡', // 二级标题
  692. img: 'resources/img/decorate/func/commodity-group-tab.png',
  693. },
  694. /* {
  695. type: 'goods-comment', // diy组件类型
  696. title: '商品评论', // 二级标题
  697. img: '', // icon图片
  698. }, */
  699. ]
  700. }, {
  701. title: '营销类', // 一级标题
  702. list: [{
  703. type: 'merchant-info', // diy组件类型
  704. title: '商户信息', // 二级标题
  705. img: 'resources/img/decorate/func/merchant-info.png',
  706. },
  707. {
  708. type: 'member-info', // diy组件类型
  709. title: '会员信息', // 二级标题
  710. img: 'resources/img/decorate/func/member-info.png',
  711. count: 1,
  712. is_top: 2,
  713. permission: {
  714. "is_drag": false,
  715. "is_up": false,
  716. "is_down": false,
  717. "is_copy": false
  718. } // 禁用操作
  719. },
  720. {
  721. type: 'order-icon-group', // diy组件类型
  722. title: '订单图标组', // 二级标题
  723. img: 'resources/img/decorate/func/order-icon-group.png',
  724. count: 1,
  725. },
  726. {
  727. type: 'commonly-icon-group', // diy组件类型
  728. title: '常用图标组', // 二级标题
  729. img: 'resources/img/decorate/func/commonly-icon-group.png',
  730. },
  731. {
  732. type: 'group-goods', // diy组件类型
  733. title: '拼团', // 二级标题
  734. img: 'resources/img/decorate/func/commonly-icon-group.png',
  735. },
  736. {
  737. type: 'notes-articles', // diy组件类型
  738. title: '运营笔记', // 二级标题
  739. img: 'resources/img/decorate/func/commonly-icon-group.png',
  740. },
  741. ]
  742. },
  743. {
  744. title: '其他', // 一级标题
  745. list: [{
  746. type: 'position', // diy组件类型
  747. title: '定位', // 二级标题
  748. img: 'resources/img/decorate/func/merchant-info.png',
  749. }, {
  750. type: 'follow-official', // diy组件类型
  751. title: '关注公众号', // 二级标题
  752. img: 'resources/img/decorate/func/follow-official.png',
  753. }, {
  754. type: 'popup-advertisement', // diy组件类型
  755. title: '弹窗广告', // 二级标题
  756. img: 'resources/img/decorate/func/popup-advertisement.png'
  757. }, {
  758. type: 'smart-form', // diy组件类型
  759. title: '表单', // 二级标题
  760. img: "<?= Yii::getAlias('@attachurl') ?>/static/addons/smartform/smartform.png"
  761. }, {
  762. type: 'car-search', // diy组件类型
  763. title: '汽车搜索', // 二级标题
  764. img: "resources/img/decorate/func/popup-advertisement.png"
  765. }, {
  766. type: 'cloud-shop', // diy组件类型
  767. title: '云店', // 二级标题
  768. img: "resources/img/decorate/func/cloud-shop.png"
  769. }]
  770. }
  771. ],
  772. /**
  773. * @param {boolean} is_show_permission // 显示权限
  774. * @param {boolean} is_delete // 是否可删除
  775. * @param {boolean} is_up // 是否可上移
  776. * @param {boolean} is_down // 是否可下移
  777. * @param {boolean} is_drag // 是否可拖拽
  778. * @param {boolean} is_copy // 是否可复制
  779. * @param {boolean} disabled // 全部禁用操作
  780. */
  781. previewList: [], // 预览数组对象
  782. activeIndex: -1, // 预览激活索引
  783. activeItem: null, // 预览激活对象
  784. otherPageData: {}, // 其他组件对象
  785. type: "add", // add新增 - edit编辑
  786. test: ["a", "b", "c"],
  787. showUpdate: true,
  788. responentData: null, // 详情响应的总数据
  789. permission: [], // 权限数组
  790. isClickPage: false, // 是否点击分页设置
  791. loading: false, // 数据加载
  792. saveLoading: false, // 保存数据
  793. previewloading: false, // 预览
  794. fields: {
  795. upload_type: 'images'
  796. },
  797. previewSrc: "",
  798. popupVisible: false,
  799. bargainPopupVisible: false,
  800. initAlearData: null,
  801. initBargainData: null,
  802. currentRoute: "<?= $currentRoute; ?>",
  803. pageType: -1, // 1 普通页面,2 模板市场
  804. cate_arr: [],
  805. cate_id: '',
  806. isShowLocation: '', //控制是否显示导航切换按钮
  807. diytemplate: '' //当前模版类型
  808. }
  809. },
  810. watch: {
  811. activeIndex() {
  812. this.showUpdate = false
  813. setTimeout(() => {
  814. this.showUpdate = true
  815. }, 0)
  816. },
  817. },
  818. created() {
  819. // 添加不同的模板nav
  820. this.previewList.push(getNavData())
  821. this.init()
  822. this.initPageType()
  823. this.type = getQuery('id') ? "edit" : "add"
  824. const id = getQuery('id')
  825. this.detail(id)
  826. },
  827. methods: {
  828. initPageType() {
  829. const obj = {
  830. 'admin/template/edit': 2
  831. }
  832. let type = obj[this.currentRoute] || 1
  833. this.pageType = type
  834. },
  835. initAlertComData(list) {
  836. // 初始化弹窗广告数据
  837. const adItem = list.find(item => {
  838. return item.funcListItem.type === "popup-advertisement"
  839. })
  840. adItem && (this.initAlearData = deepClone(adItem.data))
  841. // 初始化砍价弹窗数据
  842. const bargainItem = list.find(item => {
  843. return item.funcListItem.type === "bargain-popup"
  844. })
  845. bargainItem && (this.initBargainData = deepClone(bargainItem.data))
  846. },
  847. onAlertChange(data) {
  848. const index = this.previewList.findIndex(item => {
  849. return item.funcListItem.type === "popup-advertisement"
  850. })
  851. let item = this.previewList[index]
  852. item.data = data
  853. this.previewList[index] = item
  854. },
  855. onBargainChange(data) {
  856. const index = this.previewList.findIndex(item => {
  857. return item.funcListItem.type === "bargain-popup"
  858. })
  859. let item = this.previewList[index]
  860. item.data = data
  861. this.previewList[index] = item
  862. },
  863. savePreview() {
  864. this.activeIndex = -1
  865. this.activeItem = null
  866. this.previewloading = true
  867. this.$nextTick(() => {
  868. const contain = document.getElementById("diy-preview-contain");
  869. this.deepNode(contain);
  870. setTimeout(() => {
  871. this.dialogFormVisible = true
  872. html2canvas(contain, {
  873. useCORS: true
  874. }).then(canvas => {
  875. const src = canvas.toDataURL("image/png")
  876. this.upload(this.dataURLtoFile(src)).then(res => {
  877. if (res.code == 0) {
  878. this.previewSrc = res.data.url
  879. // this.save(false, 'preview')
  880. } else {
  881. this.$message({
  882. message: res.msg,
  883. type: 'warning'
  884. });
  885. }
  886. }).finally(res => {
  887. this.previewloading = false
  888. })
  889. })
  890. }, 0)
  891. })
  892. },
  893. dataURLtoFile(dataurl) { // 生成Blob
  894. var arr = dataurl.split(',');
  895. var mime = arr[0].match(/:(.*?);/)[1];
  896. var bstr = atob(arr[1]);
  897. var n = bstr.length;
  898. var u8arr = new Uint8Array(n);
  899. while (n--) {
  900. u8arr[n] = bstr.charCodeAt(n);
  901. }
  902. return new Blob([u8arr], {
  903. type: mime
  904. });
  905. },
  906. upload(file) {
  907. let formData = new FormData();
  908. const params = {
  909. upload_type: 'images'
  910. };
  911. params['r'] = 'common/file/upload';
  912. for (let i in this.fields) {
  913. formData.append(i, this.fields[i]);
  914. }
  915. formData.append('file', file, "image.png");
  916. return new Promise(successCallback => {
  917. this.$request({
  918. headers: {
  919. 'Content-Type': 'multipart/form-data'
  920. },
  921. params: params,
  922. method: 'post',
  923. data: formData,
  924. }).then(e => {
  925. successCallback(e.data)
  926. }).catch(e => {
  927. file._complete = true;
  928. });
  929. })
  930. },
  931. deepNode(contain) {
  932. if (contain.tagName === "IMG") {
  933. contain.src = contain.src + '?t=' + Date.now()
  934. contain.setAttribute('crossorigin', 'anonymous')
  935. }
  936. if (contain.children && contain.children.length) {
  937. [].forEach.call(contain.children, (item => {
  938. this.deepNode(item)
  939. }))
  940. }
  941. },
  942. pageSet() {
  943. PageShareData.pageIndex = true
  944. this.$eventBus.$emit("onPageClick")
  945. var index = 0;
  946. this.activeIndex = index
  947. this.activeItem = this.previewList[index]
  948. // this.changeDiyItem()
  949. },
  950. detail(id) {
  951. // 详情
  952. this.loading = true
  953. request({
  954. params: {
  955. r: this.currentRoute,
  956. id
  957. },
  958. method: 'get',
  959. }).then(res => {
  960. const {
  961. data
  962. } = res.data
  963. if (res.data.code != 0) {
  964. this.$message({
  965. message: res.data.msg,
  966. type: 'error'
  967. });
  968. return false
  969. }
  970. this.responentData = data;
  971. if (id) {
  972. if (data.diypage.content) {
  973. const previewList = JSON.parse(data.diypage.content);
  974. this.diytemplate = data.diypage.template
  975. // 兼容以前数据
  976. const memberInfo = this.addIsTop(previewList, "nav")
  977. this.previewList = this.addIsTop(memberInfo, "member-info")
  978. this.initAlertComData(this.previewList)
  979. let diytemplate = data.diypage.template
  980. this.isShowLocation = !(diytemplate == 'user_center' || diytemplate == 'goods_detail')
  981. }
  982. }
  983. if (data.components && data.components.length) {
  984. this.pageComponentsList = data.components;
  985. Vue.prototype.$capitals = data.capital
  986. Vue.prototype.$level_sel = data.level_sel
  987. }
  988. this.permission = data.auth
  989. // 模板市场分类
  990. if (this.pageType == 2) {
  991. let obj = res.data.data
  992. for (const key in obj.cate_arr) {
  993. let value = obj.cate_arr[key]
  994. this.cate_arr.push({
  995. value: key * 1,
  996. label: value
  997. })
  998. }
  999. this.cate_id = obj.diypage.cate_id
  1000. }
  1001. }).finally(res => {
  1002. this.loading = false
  1003. })
  1004. },
  1005. updateTips() { // 对应组件上绑定该事件
  1006. Vue.prototype.$vailComm = true
  1007. this.$eventBus.$emit("updateTips")
  1008. },
  1009. CommVail() { // 组件校验目前只是单个nav
  1010. const item = this.previewList[0]
  1011. const title = item.data.name
  1012. const type = item.data.contentType
  1013. if ((type != 2) && !title) {
  1014. // 目前只是标题校验后续需要,在拓展出来
  1015. if (this.activeIndex != 0) {
  1016. const index = 0
  1017. this.selectItme(this.previewList[index], index)
  1018. }
  1019. this.$nextTick(() => {
  1020. this.updateTips()
  1021. })
  1022. return '请输入标题'
  1023. }
  1024. const logoSrc = item.data.logoSrc
  1025. if (type == 2 && !logoSrc) {
  1026. return '请上传标题图片';
  1027. }
  1028. },
  1029. jumpList(type = 'save') {
  1030. if (type == 'save') {
  1031. this.$confirm('是否保存数据并离开当前页?', '提示', {
  1032. type: 'warning'
  1033. }).then(() => {
  1034. this.save()
  1035. })
  1036. } else {
  1037. this.$confirm('是否返回装修列表?', '提示', {
  1038. type: 'warning'
  1039. }).then(() => {
  1040. historyGo(-1)
  1041. })
  1042. }
  1043. },
  1044. /**
  1045. * @params { string } type // save(保存) preview(预览)
  1046. */
  1047. save: debounce(function(isLoading = true, type = 'save') {
  1048. // 对接保存数据接口
  1049. if (this.saveFlag) {
  1050. return
  1051. }
  1052. const item = this.previewList[0]
  1053. const title = item.data.name
  1054. let msg = this.CommVail()
  1055. if (msg) {
  1056. this.$message({
  1057. message: msg,
  1058. type: 'warning'
  1059. });
  1060. return msg
  1061. }
  1062. this.saveFlag = true // 防止重复提交限制
  1063. isLoading && (this.saveLoading = true)
  1064. let data = {
  1065. id: getQuery('id') || null,
  1066. title,
  1067. content: JSON.stringify(this.previewList),
  1068. cover: this.previewSrc,
  1069. }
  1070. if (this.cate_id) {
  1071. data.cate_id = this.cate_id
  1072. }
  1073. request({
  1074. params: {
  1075. r: this.currentRoute
  1076. },
  1077. method: 'post',
  1078. data
  1079. }).then(res => {
  1080. if (res.data.code != 0) {
  1081. this.$message({
  1082. message: res.data.msg,
  1083. type: 'error'
  1084. });
  1085. } else {
  1086. this.$message({
  1087. message: res.data.msg,
  1088. type: 'success'
  1089. });
  1090. if (type === 'save') {
  1091. let urlObJ = {
  1092. 1: 'mall/diy/index',
  1093. 2: 'admin/template/mall',
  1094. }
  1095. navigateTo({
  1096. r: urlObJ[this.pageType]
  1097. })
  1098. }
  1099. }
  1100. }).finally(res => {
  1101. this.saveFlag = false
  1102. this.saveLoading = false
  1103. })
  1104. },
  1105. 400),
  1106. init() {
  1107. this.$eventBus.$on("diy-preview-copy", (item = {}) => {
  1108. this.copy(item)
  1109. })
  1110. this.$eventBus.$on("diy-preview-operateFn", (item = {}) => {
  1111. this.operateFn(item)
  1112. })
  1113. this.$eventBus.$on("diy-back-set", () => {
  1114. this.updateView(null, -1)
  1115. })
  1116. },
  1117. validateCount(e) {
  1118. // 校验组件能添加几个
  1119. var msg = "";
  1120. var list = this.previewList.filter(item => {
  1121. return item.funcListItem.type === e.type
  1122. });
  1123. if (e && e.count && list.length >= e.count) {
  1124. msg = `该组件只能添加${e.count}个哦`
  1125. }
  1126. if (msg) {
  1127. this.$message({
  1128. message: msg,
  1129. type: 'warning'
  1130. });
  1131. }
  1132. return msg
  1133. },
  1134. setInitObj(data, e) {
  1135. if (!e.permission) {
  1136. return false
  1137. }
  1138. const target = typeof e.permission === "object" ? e.permission : JSON.parse(e.permission)
  1139. for (const key in target) {
  1140. data[key] = e.permission[key]
  1141. }
  1142. },
  1143. isAlearComponents(e) {
  1144. const isAlertArr = ['popup-advertisement', 'bargain-popup'] // 弹窗组件
  1145. return isAlertArr.some(item => {
  1146. return item === e.type
  1147. })
  1148. },
  1149. handleAlertComponent(initObj) {
  1150. // 检查是否已存在相同类型的弹窗组件
  1151. const existingIndex = this.previewList.findIndex(item =>
  1152. item.funcListItem.type === initObj.funcListItem.type
  1153. );
  1154. if (existingIndex === -1) {
  1155. // 如果不存在相同类型的弹窗组件,则添加新组件
  1156. this.previewList.push(initObj);
  1157. } else {
  1158. // 如果已存在相同类型的弹窗组件,则更新activeItem为该组件
  1159. this.activeItem = this.previewList[existingIndex];
  1160. }
  1161. // 根据组件类型打开对应的弹窗
  1162. if (initObj.funcListItem.type === 'popup-advertisement') {
  1163. this.popupVisible = true;
  1164. } else if (initObj.funcListItem.type === 'bargain-popup') {
  1165. this.bargainPopupVisible = true;
  1166. }
  1167. },
  1168. onEnd(e, index) { // 左侧拖拽结束手动添加进去
  1169. this.activeIndex = index - 1
  1170. this.onClick(e)
  1171. },
  1172. onClick(e) {
  1173. const flag = this.validateCount(e) // 校验组件可添加个数
  1174. if (flag) return // 不通过
  1175. var initObj = {
  1176. id: getRandomId(this.previewList),
  1177. data: {}, // 对应数据
  1178. computedStyle: {}, // 对应组件样式-尺寸
  1179. permission: [], // 展示权限列表 ==> 对应用户等级or其他
  1180. funcListItem: e,
  1181. isShow: true, // 前端是否显示
  1182. is_show_permission: true, // 是否显示权限
  1183. } // 创建的初始数据
  1184. this.setInitObj(initObj, e) // 设置可操作方法
  1185. // 弹窗组件
  1186. if (e.type === "popup-advertisement" || e.type === "bargain-popup") {
  1187. this.handleAlertComponent(initObj) // 单独处理全局弹窗组件
  1188. return false
  1189. }
  1190. if (e.is_top) { // 置顶组件操作 - 不操作以前
  1191. const index = this.previewList.findIndex(item => {
  1192. const e1 = item.funcListItem || {}
  1193. return e1.is_top === undefined || (e1.is_top >= e.is_top)
  1194. })
  1195. this.activeItem = initObj
  1196. if (index < 0) {
  1197. this.previewList.push(initObj)
  1198. this.activeIndex = Math.max(0, this.previewList.length - 1)
  1199. } else {
  1200. this.previewList.splice(index, 0, initObj)
  1201. this.activeIndex = index
  1202. }
  1203. return false
  1204. }
  1205. // 不置顶组件添加
  1206. this.activeIndex = Math.max(this.getIsTopArrLength() - 1, this.activeIndex) // 限制普通组件到istop前面
  1207. var length = this.previewList.length;
  1208. var isLast = length - 1 === this.activeIndex;
  1209. this.previewList = this.addIndexItem(this.previewList, initObj, this.activeIndex)
  1210. if (length && (isLast || this.activeIndex == -1)) {
  1211. // 有值是否最后一项
  1212. this.activeIndex = length - 1
  1213. }
  1214. this.activeItem = initObj
  1215. this.activeIndex += 1
  1216. },
  1217. getIsTopArrLength() {
  1218. return this.previewList.filter(item => {
  1219. const e1 = item.funcListItem || {}
  1220. return e1.is_top
  1221. }).length
  1222. },
  1223. changeDiyItem(item, index, params = {}) {
  1224. var {
  1225. type
  1226. } = params
  1227. var list = deepClone(this.previewList) // 克隆一下
  1228. if (type !== "isShow") {
  1229. // 显示问题 -- 样式组件不让修改
  1230. item.isShow = list[this.activeIndex].isShow;
  1231. }
  1232. // 直接全部覆盖
  1233. list[this.activeIndex] = deepClone(item)
  1234. this.activeItem = deepClone(item)
  1235. this.previewList = list
  1236. },
  1237. changeOtherDiyItem(item) {
  1238. let name = item.funcListItem.type
  1239. var list = deepClone(this.previewList) // 克隆一下
  1240. let resultIndex = list.findIndex(item => item.funcListItem.type == name)
  1241. // 直接全部覆盖
  1242. list[resultIndex] = deepClone(item)
  1243. this.previewList = list
  1244. },
  1245. selectItme(item, index) {
  1246. if (this.activeIndex == index) {
  1247. this.activeIndex = -1
  1248. this.activeItem = null
  1249. } else {
  1250. this.activeIndex = index
  1251. this.activeItem = item
  1252. if (this.activeItem.funcListItem.type == 'member-info') {
  1253. let resultIndex = this.previewList.findIndex(item => item.funcListItem.type == 'nav')
  1254. this.otherPageData = this.previewList[resultIndex]
  1255. }
  1256. }
  1257. this.$forceUpdate()
  1258. },
  1259. pushItem(list = [], target = {}) {
  1260. var result = deepClone(list)
  1261. result.push(target)
  1262. return result
  1263. },
  1264. /**
  1265. * 两种形式,一种push,追加index
  1266. */
  1267. addIndexItem(list = [], target = {}, index) { // 往指定的index添加item
  1268. var result = deepClone(list)
  1269. if (this.activeIndex < 0) {
  1270. result.push(target)
  1271. } else {
  1272. var handleArr = [deepClone(result[index]), target]
  1273. result.splice(index, 1, ...handleArr)
  1274. }
  1275. return result
  1276. },
  1277. // 操作相关
  1278. copy() {
  1279. if (!this.activeItem || this.activeItem.disabled || this.activeItem.is_copy === false) {
  1280. return
  1281. }
  1282. var item = deepClone(this.activeItem)
  1283. item.id = getRandomId()
  1284. this.previewList = this.addIndexItem(this.previewList, item, this.activeIndex)
  1285. },
  1286. /**
  1287. * @params {string} obj.type up - down - show
  1288. */
  1289. operateFn(obj) {
  1290. const methods = {
  1291. show: 'showItem',
  1292. up: 'upItem',
  1293. down: 'downItem',
  1294. delete: 'deleteItem',
  1295. replace: 'replaceItem'
  1296. }
  1297. this[methods[obj.type]](obj.index)
  1298. },
  1299. replaceItem(index) {
  1300. let type = this.activeItem.funcListItem.type;
  1301. let activeItem = type === 'nav' ? getNavLocationData() : getNavData();
  1302. this.previewList.splice(index, 1, activeItem);
  1303. this.updateView(this.previewList[index], index);
  1304. },
  1305. showItem(index) {
  1306. var item = deepClone(this.activeItem);
  1307. item.isShow = !item.isShow
  1308. this.changeDiyItem(item, index, {
  1309. type: "isShow"
  1310. })
  1311. },
  1312. updateView(item, index) {
  1313. this.activeItem = item
  1314. this.activeIndex = index
  1315. },
  1316. deleteItem(index) {
  1317. if (this.activeItem.disabled || this.activeItem.is_delete === false) {
  1318. return false
  1319. }
  1320. this.previewList.splice(index, 1);
  1321. // 删除后失去焦点
  1322. this.activeIndex = -1;
  1323. this.activeItem = null;
  1324. },
  1325. upItem(index) {
  1326. const topArr = this.previewList.filter(item => {
  1327. const e1 = item.funcListItem || {}
  1328. return e1.is_top
  1329. }) // 操作上限
  1330. const topIndex = Math.max(0, topArr.length)
  1331. if (index <= 0 || this.activeItem.disabled || this.activeItem.is_up === false) {
  1332. return false
  1333. } else if (index <= topIndex) {
  1334. alert("不可以再操作了噢");
  1335. return false
  1336. }
  1337. var previewList = deepClone(this.previewList);
  1338. this.previewList = this.swapItems(previewList, index, index - 1);
  1339. this.updateView(this.previewList[index - 1], index - 1);
  1340. },
  1341. downItem(index) {
  1342. if (this.activeItem.disabled || this.activeItem.is_down === false) {
  1343. return false
  1344. } else if (index === this.previewList.length - 1) {
  1345. alert("不可以再操作了噢");
  1346. return false
  1347. }
  1348. var previewList = deepClone(this.previewList);
  1349. this.previewList = this.swapItems(previewList, index, index + 1);
  1350. this.updateView(this.previewList[index + 1], index + 1);
  1351. },
  1352. swapItems(arr, index1, index2) {
  1353. var item = deepClone(arr[index1]);
  1354. arr[index1] = arr[index2]
  1355. arr[index2] = item
  1356. return arr
  1357. },
  1358. changeMove(e) {
  1359. const {
  1360. newIndex,
  1361. list
  1362. } = e
  1363. // 移动拖拽改变索引
  1364. this.previewList = deepClone(list);
  1365. this.updateView(this.previewList[newIndex], newIndex);
  1366. },
  1367. // 更新主页面
  1368. updateMainPage(item) {
  1369. this.mainPage = deepClone(item);
  1370. },
  1371. // 处理兼容老数据
  1372. addIsTop(list, type) {
  1373. const obj = {
  1374. ['nav']: 1,
  1375. ['member-info']: 2
  1376. }
  1377. const top = obj[type]
  1378. list.forEach(item => {
  1379. if (item.funcListItem.type === 'nav') {
  1380. item.data.mainPage.diytemplate = this.diytemplate
  1381. }
  1382. if (item.funcListItem.type === type && !item.funcListItem.is_top) {
  1383. item.funcListItem.is_top = top
  1384. }
  1385. });
  1386. return list
  1387. }
  1388. }
  1389. })
  1390. // 拦截页面卸载
  1391. // window.onbeforeunload = function(e) {
  1392. // var e = window.event || e;
  1393. // e.returnValue = ("确定离开当前页面吗?");
  1394. // }
  1395. </script>
  1396. <style>
  1397. #app {
  1398. display: flex;
  1399. flex-direction: column;
  1400. min-width: 1250px;
  1401. }
  1402. #diy-test-preview {
  1403. margin: auto;
  1404. }
  1405. .diy-header-backs {
  1406. cursor: pointer;
  1407. }
  1408. .fixed {
  1409. position: fixed;
  1410. left: 0;
  1411. top: 0;
  1412. width: 100%;
  1413. height: 100%;
  1414. z-index: 999;
  1415. }
  1416. .temp-select {
  1417. position: relative;
  1418. margin-left: 20px;
  1419. }
  1420. .temp-select .cate {
  1421. position: absolute;
  1422. top: 0;
  1423. bottom: 0;
  1424. left: 6px;
  1425. color: #fff;
  1426. display: inline-block;
  1427. font-size: inherit;
  1428. height: 30px;
  1429. line-height: 30px;
  1430. cursor: pointer;
  1431. pointer-events: none;
  1432. font-weight: 400;
  1433. }
  1434. .temp-select .el-input__inner {
  1435. background: hsla(0, 0%, 100%, .2);
  1436. color: #FFFFFF;
  1437. }
  1438. .temp-select .el-input.is-focus .el-input__inner,
  1439. .temp-select .el-input .el-input__inner:focus,
  1440. .temp-select .el-input__inner {
  1441. border-color: transparent !important;
  1442. border: none;
  1443. text-indent: 54px;
  1444. }
  1445. .diy-header {
  1446. height: 55px;
  1447. background: #313131;
  1448. opacity: 1;
  1449. border-radius: 0px;
  1450. justify-content: space-between;
  1451. font-size: 13px;
  1452. font-family: PingFang SC;
  1453. /*font-weight: bold;*/
  1454. color: #FFFFFF;
  1455. padding: 0 25px;
  1456. }
  1457. .diy-contain {
  1458. flex: 1;
  1459. overflow: auto;
  1460. background: #F4F6F8;
  1461. justify-content: space-between;
  1462. }
  1463. .diy-func-list {
  1464. box-sizing: border-box;
  1465. width: 300px;
  1466. background: #FFFFFF;
  1467. border-radius: 0px;
  1468. }
  1469. .diy-preview {
  1470. flex: 1;
  1471. background: #F4F6F8;
  1472. justify-content: space-around;
  1473. }
  1474. .fill-box {
  1475. position: relative;
  1476. width: 467px;
  1477. height: 100%;
  1478. }
  1479. .flex {
  1480. display: flex;
  1481. }
  1482. .flex-x-center {
  1483. justify-content: center;
  1484. }
  1485. .flex-y-center {
  1486. align-items: center;
  1487. }
  1488. /* */
  1489. .fade-enter-active,
  1490. .fade-leave-active {
  1491. transition: opacity 1s;
  1492. }
  1493. .fade-enter,
  1494. .fade-leave-to {
  1495. opacity: 0;
  1496. }
  1497. .diy-text-font {
  1498. font-size: 13px;
  1499. font-weight: 700;
  1500. color: #999999;
  1501. }
  1502. .fill-border-top {
  1503. border-top: 10px solid #F4F6F8;
  1504. }
  1505. .fill-border-bottom {
  1506. border-bottom: 10px solid #F4F6F8;
  1507. }
  1508. /* 导航栏 */
  1509. .custom-page {
  1510. /*width: 181px;*/
  1511. /*height: 60px;*/
  1512. line-height: 60px;
  1513. /*background: var(--diy-theme);*/
  1514. /*font-weight: 700;*/
  1515. /*border-radius: 0px;*/
  1516. text-align: center;
  1517. }
  1518. .save {
  1519. font-size: 18px;
  1520. vertical-align: middle;
  1521. }
  1522. .save+span {
  1523. vertical-align: middle;
  1524. }
  1525. .sava_btn .el-button--primary {
  1526. background: var(--diy-theme);
  1527. border-color: var(--diy-theme);
  1528. /*padding: 8px 14px;*/
  1529. }
  1530. .diy_nav_right>div {
  1531. padding: 8px 14px;
  1532. }
  1533. .diy_nav_right .sava_btn {
  1534. padding: 0;
  1535. }
  1536. .page_set>i {
  1537. font-size: 24px;
  1538. vertical-align: middle;
  1539. }
  1540. .page_set>span {
  1541. vertical-align: middle;
  1542. }
  1543. .xiaoshou {
  1544. cursor: pointer;
  1545. }
  1546. :root {
  1547. /* 主题色 */
  1548. --diy-theme: #F10009;
  1549. /* 主要字体颜色 */
  1550. --diy-color: #F10009;
  1551. }
  1552. /* 修改ele默认样式 */
  1553. /* slider组件 */
  1554. .el-slider__bar {
  1555. background-color: var(--diy-theme);
  1556. }
  1557. .el-slider__button {
  1558. border-color: var(--diy-theme);
  1559. }
  1560. /* input组件 */
  1561. .el-input .el-input__inner:focus {
  1562. border: 1px solid var(--diy-theme);
  1563. }
  1564. /* textarea */
  1565. .el-textarea .el-textarea__inner:focus {
  1566. border: 1px solid var(--diy-theme);
  1567. }
  1568. /* radio单选组件 */
  1569. .el-radio__input.is-checked+.el-radio__label {
  1570. color: var(--diy-color);
  1571. }
  1572. .el-radio__input.is-checked .el-radio__inner {
  1573. border-color: var(--diy-theme);
  1574. background: var(--diy-theme);
  1575. }
  1576. .el-radio-button__orig-radio:checked+.el-radio-button__inner {
  1577. border-color: var(--diy-theme);
  1578. box-shadow: -1px 0 0 0 var(--diy-theme);
  1579. }
  1580. /* 多选 */
  1581. .el-checkbox__input.is-checked+.el-checkbox__label {
  1582. color: var(--diy-color);
  1583. }
  1584. .el-checkbox__input.is-checked .el-checkbox__inner,
  1585. .el-checkbox__input.is-indeterminate .el-checkbox__inner {
  1586. border-color: var(--diy-theme);
  1587. background: var(--diy-theme);
  1588. }
  1589. </style>