TrackManagementEdit.blade.php 43KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974
  1. @extends('admin.master')
  2. @section('content')
  3. <div class="row">
  4. <div class="col-xl-12">
  5. <form id="EditForm" class="form-horizontal" method="post"
  6. action="{{ url('/backend/dataManagement/track/store') }}" enctype="multipart/form-data">
  7. {{ csrf_field() }}
  8. <div class="panel _panel-primary">
  9. <div class="panel-heading _panel-heading">
  10. <h4 class="panel-title m-0">{{ ($operdata == "") ? "新增 " : "修改 " }}議程資料</h4>
  11. </div>
  12. <div class="panel-body row">
  13. <div>
  14. <table class="table" style="border-collapse:collapse;">
  15. <tbody class="msgTbody">
  16. <input type="hidden" id="operateMode" value="edit"/>
  17. <input type="hidden" id="id" value="{{$id}}"/>
  18. <!-- 欄位:圖文頭貼設定 -->
  19. <tr class="msgTemplate">
  20. <td class="header-require col-xl-2">議程資料設定</td>
  21. <td>
  22. <div id="imgmapcontent-tab1">
  23. <input type="hidden" id="tab1MenuFlag"/>
  24. <input type="hidden" id="tab1ImagemapModelType" value="m1">
  25. <input type="hidden" id="tab1ImagemapImgSize" value='{"width":800,"height":800}'>
  26. <!-- 表格本體 -->
  27. <table class="table" id="DetailsView1" style="border-collapse:collapse;">
  28. <tbody>
  29. <!-- 序號 -->
  30. @if ($operdata == "")
  31. <!-- Insert Mode -->
  32. <input type="hidden" name="mode" value="insert"/>
  33. @else
  34. <!-- Edit Mode -->
  35. <input type="hidden" name="mode" value="edit"/>
  36. <tr>
  37. <td class="col-xl-2">序號</td>
  38. <td>
  39. <input name="id" type="hidden" value="{{ $operdata['id'] }}" id="id"/>
  40. {{ $operdata['id'] }}
  41. </td>
  42. </tr>
  43. @endif
  44. <!-- 欄位:標題 -->
  45. <!-- ALL Mode -->
  46. <tr>
  47. <td class="header-require col-xl-2">trackNo</td>
  48. <td>
  49. <div class="col-xl-3 nopadding">
  50. @if ($operdata == "")
  51. <input name="trackNo" type="text" value="" maxlength="100"
  52. id="trackNo" class="form-control">
  53. @else
  54. <input name="trackNo" type="text"
  55. value="{{ $operdata['trackNo'] }}" maxlength="100"
  56. id="trackNo" class="form-control">
  57. @endif
  58. <label class="error" for="trackNo"></label>
  59. </div>
  60. </td>
  61. </tr>
  62. <!-- 欄位:說明 -->
  63. <!-- ALL Mode -->
  64. <tr>
  65. <td class="header-require col-xl-2">trackTitle</td>
  66. <td>
  67. <div class="col-xl-3 nopadding">
  68. @if ($operdata == "")
  69. <input name="trackTitle" type="text" value="" maxlength="500"
  70. id="trackTitle"
  71. class="form-control">
  72. @else
  73. <input name="trackTitle" type="text"
  74. value="{{ $operdata['trackTitle'] }}"
  75. maxlength="500" id="trackTitle" class="form-control">
  76. @endif
  77. <label class="error" for="trackTitle"></label>
  78. </div>
  79. </td>
  80. </tr>
  81. <!-- 欄位:說明 -->
  82. <!-- ALL Mode -->
  83. <tr>
  84. <td class="header-require col-xl-2">trackLimit</td>
  85. <td>
  86. <div class="col-xl-3 nopadding">
  87. @if ($operdata == "")
  88. <input name="trackLimit" type="text" value="" maxlength="500"
  89. id="trackLimit"
  90. class="form-control">
  91. @else
  92. <input name="trackLimit" type="text"
  93. value="{{ $operdata['trackLimit'] }}"
  94. maxlength="500" id="trackLimit" class="form-control">
  95. @endif
  96. <label class="error" for="trackLimit"></label>
  97. </div>
  98. </td>
  99. </tr>
  100. </tbody>
  101. </table>
  102. <hr>
  103. </div>
  104. </td>
  105. </tr>
  106. <!-- 下控制按鈕 -->
  107. <tr>
  108. <td>&nbsp;</td>
  109. <td>
  110. <div style="text-align: right">
  111. <input type="button" id="btnSave" value="儲存" class="btn btn-primary" style="margin-right: 5px;">
  112. <input type="button" id="btnBackToList" value="返回" class="btn btn-secondary">
  113. </div>
  114. </td>
  115. </tr>
  116. </tbody>
  117. </table>
  118. </div>
  119. </div>
  120. </div>
  121. </form>
  122. </div>
  123. </div>
  124. <div id="ajax-spinner" style="display:none;">
  125. <img src="{{ asset('assets/images/backend_spinner.svg') }}" />
  126. </div>
  127. <link rel="stylesheet" href="{{url('assets/resize-controller/project/resizeControl.css')}}">
  128. <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
  129. <style>
  130. /* --------------- 全域 --------------- */
  131. .msgTemplate div.form-group _form-group {
  132. margin: 0;
  133. }
  134. .tab-content {
  135. -webkit-box-shadow: none;
  136. -moz-box-shadow: none;
  137. box-shadow: none;
  138. }
  139. /* --------------- Imagemap --------------- */
  140. .imagemap-model-small-container {
  141. display: flex;
  142. flex-wrap: wrap;
  143. }
  144. .imagemap-model-small-container .imagemap-model-small {
  145. width: 80px;
  146. height: 55px;
  147. margin: 10px;
  148. }
  149. .imagemap-model-small-container .table {
  150. width: 100%;
  151. height: 100%;
  152. cursor: pointer;
  153. }
  154. .imagemap-model-small-container .table td {
  155. background-color: #cecece;
  156. color: #ffffff;
  157. border: 1px solid #858484;
  158. }
  159. .imagemap-model-small-container > .imagemap-model-small.active td {
  160. border-color: #6c62f3;
  161. border-width: 2px;
  162. }
  163. /* --------------- imagemap-model --------------- */
  164. .imagemap-model {
  165. width: 240px;
  166. height: 166px;
  167. }
  168. .imagemap-model > table {
  169. width: 100%;
  170. height: 100%;
  171. text-align: center;
  172. }
  173. .imagemap-model > table > tbody td {
  174. background-color: #bfbfbf;
  175. color: #ffffff;
  176. vertical-align: middle;
  177. }
  178. .imagemap-model > table > tbody td.active {
  179. background-color: #58ae58;
  180. }
  181. .imagemap-model > table > tbody td:not(.active):hover {
  182. background-color: #898989c9;
  183. cursor: default;
  184. }
  185. /* --------------- uri 綁定標籤 ----------------- */
  186. .select2-container--default .select2-selection--multiple .select2-selection__choice {
  187. background-color: #5897fb;
  188. border-radius: 10px;
  189. color: white;
  190. padding-top: 5px;
  191. padding-bottom: 5px;
  192. }
  193. .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
  194. border-right: unset;
  195. color: #fff;
  196. font-size: 1.2em;
  197. left: 1px;
  198. top: 3px;
  199. }
  200. .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover, .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:focus {
  201. background-color: #5897fb;
  202. color: #fff;
  203. }
  204. .select2-selection.select2-selection--multiple {
  205. min-height: 24px;
  206. }
  207. .select2-container--default .select2-search--inline .select2-search__field {
  208. min-height: 24px;
  209. font-size: 14px;
  210. }
  211. /* --------------- AJAX upload --------------- */
  212. .image-preview-wrapper{
  213. position: relative;
  214. cursor: pointer;
  215. }
  216. .spinner-wrapper {
  217. position: absolute;
  218. opacity: 0;
  219. margin: 0;
  220. padding: 0;
  221. /*left: 50%;*/
  222. /*top: 50%;*/
  223. transform: translate(-50%, -50%);
  224. width: 100px;
  225. height: 100px;
  226. background-image: url(/assets/images/backend_spinner.svg);
  227. background-size: 50px;
  228. background-repeat: no-repeat;
  229. background-position: center;
  230. }
  231. .opacity-1 {
  232. opacity: 1;
  233. }
  234. </style>
  235. @endsection
  236. @section('extjs')
  237. <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
  238. <script src="{{url('assets/js/jquery.mousewheel-3.0.6.min.js')}}"></script>
  239. <script src="{{url('assets/js/jquery.mCustomScrollbar.min.js')}}"></script>
  240. <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.18.1/babel.min.js"></script>
  241. <script src="{{url('assets/resize-controller/project/index.js')}}"></script>
  242. <script>
  243. var imagemapBoxes = {};
  244. $(document).ready(function () {
  245. // 下控制按鈕 儲存
  246. $("#btnSave").click(function () {
  247. finishEdit();
  248. });
  249. // 下控制按鈕 返回
  250. $("#btnBackToList").click(function () {
  251. location.href = '/backend/dataManagement/track';
  252. });
  253. // 註冊事件
  254. eventImagemap();
  255. eventUriAction();
  256. });
  257. function restoreMsgInfo(msgInfo) {
  258. // 初始化
  259. var tabId = 'tab1';
  260. var menuInfoObj = JSON.parse(msgInfo).menuInfo;
  261. // console.log(menuInfoObj);
  262. // var menuFlag = menuInfoObj.menuFlag;
  263. // var modelType = menuInfoObj.modelType;
  264. // var chatBarText = menuInfoObj.chatBarText;
  265. // var displayByDefault = menuInfoObj.displayByDefault;
  266. var imgUrl = menuInfoObj.imgUrl;
  267. // var imgSize = menuInfoObj.imgSize;
  268. // var imagemapActions = menuInfoObj.actions;
  269. // 還原內容
  270. $('#'+tabId+'MenuFlag').val(menuFlag);
  271. $("#"+tabId+"ImagemapModelType").val(modelType);
  272. $("#chatBarText").val(chatBarText);
  273. if (!displayByDefault) {
  274. $("input[name='displayByDefault']").filter('[value="0"]').prop('checked', true);
  275. }
  276. $("img[data-target='"+tabId+"-image-preview-imagemapImg']").attr("src", imgUrl);
  277. // 點擊 選擇的版型
  278. // trigger: $('.imagemap-model-small').click()
  279. var $chosen_model = $("#imgmapcontent-"+tabId).find('.imagemap-model-small').filter("[data-model_type='"+modelType+"']");
  280. if (modelType === 'mcustom') {
  281. $chosen_model.trigger('click', [menuInfoObj]);
  282. } else {
  283. $chosen_model.click();
  284. }
  285. if (modelType == 'mjson') {
  286. // todo 重新組合 ImagemapJson
  287. var imagemapJson = '';
  288. $("#"+tabId+"ImagemapJson").val(imagemapJson);
  289. } else {
  290. // 還原 每個按鈕的值
  291. $.each(imagemapActions, function(i, actionObj) {
  292. var btnNum = i + 1;
  293. $("#"+tabId+"Imagemapbtn"+btnNum+"Act").val(actionObj.type);
  294. $("#"+tabId+"Imagemapbtn"+btnNum+"Act").trigger('change'); // 觸發事件切換按鈕N動作
  295. $("#"+tabId+"Imagemapbtn"+btnNum+"Content").val(actionObj.content);
  296. // 前往選單
  297. if (actionObj.type == 'richmenu') {
  298. $("#"+tabId+"Imagemapbtn"+btnNum+"Submenu").val(actionObj.content);
  299. $("#"+tabId+"Imagemapbtn"+btnNum+"Submenu").trigger('change');
  300. if ('keywordValue' in actionObj) {
  301. $("#"+tabId+"Imagemapbtn"+btnNum+"Content").attr('data-keyword_value', actionObj.keywordValue);
  302. }
  303. }
  304. // 貼標 (開啟連結)
  305. if ('tags' in actionObj) {
  306. $("#"+tabId+"Imagemapbtn"+btnNum+"Tags").val(actionObj.tags);
  307. $("#"+tabId+"Imagemapbtn"+btnNum+"Tags").trigger('change');
  308. }
  309. });
  310. }
  311. }
  312. // 完成編輯
  313. function finishEdit() {
  314. var msgInfoObj = msgInfo();
  315. // console.log(msgInfoObj);
  316. // return;
  317. if ('err' in msgInfoObj) {
  318. alert("有錯誤:\n" + msgInfoObj.err.join("\n"));
  319. return;
  320. }
  321. $.blockUI({
  322. message: $('#ajax-spinner'),
  323. css: {
  324. backgroundColor: 'transparent',
  325. border: '0'
  326. },
  327. });
  328. $.ajax({
  329. type: "POST",
  330. url: '/backend/dataManagement/track/store',
  331. data: {
  332. "operateMode": $("#operateMode").val(),
  333. "trackNo": $("#trackNo").val(),
  334. "trackTitle": $("#trackTitle").val(),
  335. "trackLimit": $("#trackLimit").val(),
  336. "_token":"{{ csrf_token() }}",
  337. "id": $("#id").val(),
  338. },
  339. success: function(result) {
  340. if (result.code === 0) {
  341. alert('儲存成功');
  342. location.href = '/backend/dataManagement/track';
  343. } else {
  344. alert(result.message);
  345. alert('123');
  346. }
  347. },
  348. fail: function(jqXHR, textStatus) {
  349. console.log(jqXHR);
  350. console.log(textStatus);
  351. },
  352. complete: function() {
  353. $.unblockUI();
  354. }
  355. });
  356. }
  357. function eventImagemap() {
  358. // 選擇版型
  359. $('.imagemap-model-small').unbind("click");
  360. $('.imagemap-model-small').click(function (event, imagemapContent) {
  361. var $model_small = $(this);
  362. var $model_small_container = $model_small.parent('.imagemap-model-small-container');
  363. var $imagemap_content = $model_small.closest('div[id^="imgmapcontent-tab"]');
  364. var $imagemap_btns = $imagemap_content.children('.imagemap-btn'); // 此頁籤下所有按鈕區域
  365. var tabNum = 1;
  366. var tabId = 'tab'+tabNum;
  367. var modelType = $model_small.attr('data-model_type');
  368. var imgSizeObj = JSON.parse($('#'+tabId+'ImagemapImgSize').val());
  369. // 三種版型類型的容器
  370. var $imagemap_model = $imagemap_content.find('.imagemap-model');
  371. var $custom_container = $('#'+tabId+'ImagemapBoxContainer');
  372. var $json_container = $('#'+tabId+'ImagemapJson').closest('div.row');
  373. if ($model_small.hasClass('active')) {
  374. return;
  375. }
  376. // 設定選擇的 active
  377. $model_small_container.find('.imagemap-model-small').removeClass('active');
  378. $model_small.addClass('active');
  379. // 設定 model type
  380. $('#'+tabId+'ImagemapModelType').val(modelType);
  381. imagemapClearBtns($imagemap_btns, tabId);
  382. if (modelType == 'mjson') {
  383. $imagemap_model.hide();
  384. $custom_container.hide();
  385. $json_container.show();
  386. $imagemap_btns.hide();
  387. }
  388. else if (modelType == 'mcustom') {
  389. $imagemap_model.hide();
  390. $custom_container.show();
  391. $json_container.hide();
  392. var boxNum = 1; // ImagemapBox1
  393. // 要等 ImagemapBox HTML 完全顯示才能繪製,所以等 300ms 才初始化
  394. setTimeout(function() {
  395. imagemapInitBox(tabNum, boxNum, imgSizeObj, imagemapContent);
  396. // 設定背景圖案 (initImagemapBox 後才能設定)
  397. var imagemapUrl = $('img[data-target="'+tabId+'-image-preview-imagemapImg"]').attr('src');
  398. $('#ImagemapBox'+boxNum).css('background-image', 'url(' + imagemapUrl + ')');
  399. }, 300);
  400. }
  401. else {
  402. $imagemap_model.show();
  403. $custom_container.hide();
  404. $json_container.hide();
  405. // 建立對應的 imagemap-model
  406. $imagemap_model.children('.table').remove();
  407. $model_small.children('.table').clone(false).appendTo($imagemap_model);
  408. $imagemap_model.find('td').each(function (index) {
  409. var btnNum = index + 1;
  410. if (btnNum === 1) $(this).addClass('active');
  411. $(this).html('按鈕'+btnNum);
  412. $(this).attr('data-btn', btnNum);
  413. });
  414. }
  415. });
  416. // 編輯按鈕區域
  417. $('.imagemap-model').unbind("click");
  418. $('.imagemap-model').on('click', 'td', function () {
  419. var $clicked_td = $(this);
  420. var $table = $clicked_td.closest('.table');
  421. var $imagemap_content = $clicked_td.closest('div[id^="imgmapcontent-tab"]');
  422. var tabNum = '1';
  423. var btnNum = $clicked_td.attr('data-btn');
  424. // 設定選擇的 active
  425. $table.find('td').removeClass('active');
  426. $clicked_td.addClass('active');
  427. // 顯示對應按鈕
  428. $imagemap_content.find('.imagemap-btn').hide();
  429. $('#tab'+'1'+'Imagemapbtn'+btnNum).show();
  430. });
  431. // 切換 按鈕N動作
  432. $('.imagemap-btn').find('select.imagemapBtnAct').unbind("change");
  433. $('.imagemap-btn').find('select.imagemapBtnAct').change(function () {
  434. var tabNum = 1;
  435. var btnNum = $(this).attr('id').split('Imagemapbtn')[1].replace(/\D/g, ''); // tab1Imagemapbtn1Act
  436. var type = $(this).val();
  437. var $btnContent = $('#tab1Imagemapbtn'+btnNum+'Content');
  438. var $btnSubmenu = $('#tab1Imagemapbtn'+btnNum+'Submenu');
  439. var $btnTags = $('#tab1Imagemapbtn'+btnNum+'Tags');
  440. // 清空
  441. $btnContent.val('');
  442. $btnSubmenu.val(0);
  443. $btnTags.val(null).trigger('change');
  444. // 顯示按鈕對應的元件
  445. imagemapShowBtnInputsByAction(type, $btnContent, $btnTags, $btnSubmenu);
  446. });
  447. }
  448. function eventUriAction() {
  449. $('.js-example-basic-multiple').select2({
  450. placeholder: '請選擇或輸入以搜尋標籤'
  451. });
  452. }
  453. function msgInfo() {
  454. var result = {
  455. menuInfo: {},
  456. err: []
  457. };
  458. var msgObj = {};
  459. var tabNum = '1';
  460. var tabId = 'tab' + tabNum;
  461. // 取資料
  462. var menuFlag = $('#'+tabId+'MenuFlag').val();
  463. var modelType = $("#"+tabId+"ImagemapModelType").val();
  464. var imgUrl = $("img[data-target$='"+tabId+"-image-preview-imagemapImg']").attr("src");
  465. var json = $("#"+tabId+"ImagemapJson").val();
  466. var menuTitle = $("#menuTitle").val();
  467. var chatBarText = $("#chatBarText").val();
  468. var displayByDefault = $("input[name='displayByDefault']:checked").val();
  469. // 檢查
  470. if (menuTitle === '') result.err.push('標題內容為空');
  471. if (imgUrl === '/assets/images/placeholder_upload_380_260.png') result.err.push('圖片沒上傳');
  472. if (modelType == 'mjson' && json === '') result.err.push('JSON內容為空');
  473. if (chatBarText === '') result.err.push('選單顯示名稱為空');
  474. var imagemapActions = '';
  475. if (modelType == 'mjson') {
  476. // todo msgInfoImagemapActionsFromJson(json)
  477. imagemapActions = '';
  478. } else if (modelType == 'mcustom') {
  479. imagemapActions = msgInfoImagemapActions(tabNum, modelType, result);
  480. } else { // 預設版型
  481. imagemapActions = msgInfoImagemapActions(tabNum, modelType, result);
  482. }
  483. msgObj = {
  484. // menuFlag: menuFlag,
  485. // modelType: modelType,
  486. // name: menuTitle,
  487. // chatBarText: chatBarText,
  488. // displayByDefault: !!parseInt(displayByDefault),
  489. imgUrl: imgUrl,
  490. // imgSize: {
  491. // "width": 800,
  492. // "height": 800
  493. // },
  494. // actions: imagemapActions,
  495. };
  496. result.menuInfo = msgObj;
  497. if (result.err.length === 0) {
  498. delete result.err;
  499. }
  500. return result;
  501. }
  502. function msgInfoImagemapActions(tabNum, modelType, result) {
  503. var tabId = 'tab'+tabNum;
  504. var areas = [];
  505. if (modelType == 'mcustom') {
  506. areas = imagemapBoxes[tabNum].getResultPos();
  507. } else if (modelType != 'mjson') {
  508. // 樣板按鈕區域定義 (依照頁面顯示的按鈕順序)
  509. var modelAreaMap = {
  510. "m1": [
  511. {"x": 0, "y": 0, "width": 833, "height": 843},
  512. {"x": 833, "y": 0, "width": 833, "height": 843},
  513. {"x": 1666, "y": 0, "width": 834, "height": 843},
  514. {"x": 0, "y": 843, "width": 833, "height": 843},
  515. {"x": 833, "y": 843, "width": 833, "height": 843},
  516. {"x": 1666, "y": 843, "width": 834, "height": 843},
  517. ],
  518. "m2": [
  519. {"x": 0, "y": 0, "width": 1250, "height": 843},
  520. {"x": 1250, "y": 0, "width": 1250, "height": 843},
  521. {"x": 0, "y": 843, "width": 1250, "height": 843},
  522. {"x": 1250, "y": 843, "width": 1250, "height": 843},
  523. ],
  524. "m3": [
  525. {"x": 0, "y": 0, "width": 2500, "height": 843},
  526. {"x": 0, "y": 843, "width": 833, "height": 843},
  527. {"x": 833, "y": 843, "width": 833, "height": 843},
  528. {"x": 1666, "y": 843, "width": 834, "height": 843},
  529. ],
  530. "m4": [
  531. {"x": 0, "y": 0, "width": 1250, "height": 1686},
  532. {"x": 1250, "y": 0, "width": 1250, "height": 843},
  533. {"x": 1250, "y": 843, "width": 1250, "height": 843},
  534. ],
  535. "m5": [
  536. {"x": 0, "y": 0, "width": 2500, "height": 843},
  537. {"x": 0, "y": 843, "width": 2500, "height": 843},
  538. ],
  539. "m6": [
  540. {"x": 0, "y": 0, "width": 1250, "height": 1686},
  541. {"x": 1250, "y": 0, "width": 1250, "height": 1686},
  542. ],
  543. "m7": [
  544. {"x": 0, "y": 0, "width": 2500, "height": 1686},
  545. ]
  546. };
  547. areas = modelAreaMap[modelType];
  548. }
  549. var actions = [];
  550. for (var btnNum = 1; btnNum <= areas.length; btnNum++) {
  551. var btnId = 'btn'+btnNum;
  552. var type = $("#"+tabId+"Imagemap"+btnId+"Act").val();
  553. var content = $("#"+tabId+"Imagemap"+btnId+"Content").val();
  554. var submenuId = $("#"+tabId+"Imagemap"+btnId+"Submenu").val();
  555. var tagsData = $("#"+tabId+"Imagemap"+btnId+"Tags").select2('data');
  556. var area = areas[btnNum-1];
  557. if (type != 'richmenu' && content === '') result.err.push('按鈕'+btnNum+'的內容為空');
  558. if (type == 'uri'
  559. && !content.startsWith('https://')
  560. && !content.startsWith('tel:')
  561. && !content.startsWith('line://')
  562. ) {
  563. result.err.push('按鈕'+btnNum+'的連結網址須為 https:// 或 tel:// 或 line://');
  564. }
  565. var action = {
  566. "type": type,
  567. "content": content,
  568. "area": {
  569. "x": area.x,
  570. "y": area.y,
  571. "width": area.width,
  572. "height": area.height
  573. }
  574. };
  575. if (type == 'uri' && tagsData.length !== 0) { // uri 貼標
  576. var tags = [];
  577. $.each(tagsData, function(i, item) {
  578. tags.push(item.id);
  579. });
  580. action.tags = tags;
  581. } else if (type == 'richmenu') { // 前往子選單
  582. action.content = submenuId;
  583. var keywordValue = $("#"+tabId+"Imagemapbtn"+btnNum+"Content").attr('data-keyword_value');
  584. if (typeof keywordValue === 'undefined') {
  585. var menuFlag = $('#tab1MenuFlag').val();
  586. keywordValue = 'richmenu-' + menuFlag.replace(/menu-/g, '') + '-' + Date.now();
  587. }
  588. action.keywordValue = keywordValue;
  589. }
  590. actions.push(action);
  591. }
  592. // console.log(actions);
  593. return actions
  594. }
  595. function generateFlag(length) {
  596. var result = '';
  597. var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  598. var charactersLength = characters.length;
  599. for ( var i = 0; i < length; i++ ) {
  600. result += characters.charAt(Math.floor(Math.random() * charactersLength));
  601. }
  602. return result;
  603. }
  604. function imagemapInitBox(tabNum, boxNum, imgSizeObj, imagemapContent) {
  605. let imagemapBox;
  606. var mapData = null;
  607. // console.log(imagemapContent);
  608. if (typeof imagemapContent !== 'undefined') {
  609. mapData = imagemapContent;
  610. }
  611. imagemapBox = new resizer({
  612. container: '#ImagemapBox'+boxNum,
  613. wrap: imgSizeObj,
  614. wrapperWidth: 380,
  615. itemDefaultSize: 380,
  616. add: '#AddImagemapBox'+boxNum,
  617. delete: {
  618. selector: '#DeleteImagemapBox'+boxNum,
  619. getDelete: function(res) {
  620. // console.log(res);
  621. var btnNum = res.index + 1;
  622. var tabId = 'tab'+tabNum;
  623. // 重設按鈕的值 (從刪除的下一個的開始往前遞補)
  624. for (btnNum; btnNum < 12; btnNum++) {
  625. var nextNum = btnNum + 1;
  626. var $currentAct = $('#'+tabId+'Imagemapbtn'+btnNum+'Act');
  627. var $currentContent = $('#'+tabId+'Imagemapbtn'+btnNum+'Content');
  628. var $currentSubmenu = $('#'+tabId+'Imagemapbtn'+btnNum+'Submenu');
  629. var $currentTags = $('#'+tabId+'Imagemapbtn'+btnNum+'Tags');
  630. var $nextAct = $('#'+tabId+'Imagemapbtn'+nextNum+'Act');
  631. var $nextContent = $('#'+tabId+'Imagemapbtn'+nextNum+'Content');
  632. var $nextSubmenu = $('#'+tabId+'Imagemapbtn'+nextNum+'Submenu');
  633. var $nextTags = $('#'+tabId+'Imagemapbtn'+nextNum+'Tags');
  634. // 內容
  635. $currentAct.val($nextAct.val());
  636. $currentContent.val($nextContent.val());
  637. $currentSubmenu.val($nextSubmenu.val());
  638. // 標籤
  639. var nextTagsData = $nextTags.select2('data');
  640. if (nextTagsData.length !== 0) {
  641. var nextTags = [];
  642. $.each(nextTagsData, function(i, item) {
  643. nextTags.push(item.id);
  644. });
  645. $currentTags.val(nextTags);
  646. $currentTags.trigger('change');
  647. }
  648. // 顯示按鈕對應的元件
  649. imagemapShowBtnInputsByAction($currentAct.val(), $currentContent, $currentTags, $currentSubmenu);
  650. }
  651. // 清除最後一個按鈕
  652. $('#'+tabId+'Imagemapbtn12Act').val('postback'); // richmenu 預設
  653. $('#'+tabId+'Imagemapbtn12Content').val('');
  654. $('#'+tabId+'Imagemapbtn12Submenu').val(0);
  655. $('#'+tabId+'Imagemapbtn12Tags').val(null).trigger('change');
  656. // 顯示最後一個按鈕對應的元件
  657. imagemapShowBtnInputsByAction('postback', $('#'+tabId+'Imagemapbtn12Content'), $('#'+tabId+'Imagemapbtn12Tags'), $('#'+tabId+'Imagemapbtn12Submenu'));
  658. }
  659. },
  660. get: '#GetImagemapBox'+boxNum,
  661. map: mapData,
  662. getActive: function(res) {
  663. // console.log(res);
  664. var $imagemapContent = $('#imgmapcontent-tab'+tabNum);
  665. var btnNum = res.index + 1;
  666. // 顯示對應按鈕
  667. $imagemapContent.find('.imagemap-btn').hide();
  668. $('#tab'+tabNum+'Imagemapbtn'+btnNum).show();
  669. }
  670. });
  671. imagemapBoxes[tabNum] = imagemapBox;
  672. }
  673. function imagemapShowBtnInputsByAction(action, $btnContent, $btnTags, $btnSubmenu) {
  674. if (action == 'uri') {
  675. $btnContent.closest('div[class="row"]').show();
  676. $btnTags.closest('div[class="row"]').show();
  677. $btnSubmenu.closest('div[class="row"]').hide();
  678. } else if (action == 'richmenu') {
  679. $btnContent.closest('div[class="row"]').hide();
  680. $btnTags.closest('div[class="row"]').hide();
  681. $btnSubmenu.closest('div[class="row"]').show();
  682. } else {
  683. $btnContent.closest('div[class="row"]').show();
  684. $btnTags.closest('div[class="row"]').hide();
  685. $btnSubmenu.closest('div[class="row"]').hide();
  686. }
  687. }
  688. function imagemapClearBtns($imagemap_btns, tabId) {
  689. var $btnContents = $imagemap_btns.find('.imagemapBtnContent');
  690. var $btnActs = $imagemap_btns.find('.imagemapBtnAct');
  691. var $btnSubmenus = $imagemap_btns.find('select.imagemapBtnSubmenu');
  692. var $btnTags = $imagemap_btns.find('.imagemapBtnTags');
  693. // 清除按鈕區域內容
  694. $btnContents.val('');
  695. $btnActs.val('postback'); // richmenu 預設
  696. $btnSubmenus.val(0)
  697. $btnTags.val(null).trigger('change');
  698. // 顯示按鈕對應的元件 (這邊是一次設定所有按鈕下所有元件)
  699. imagemapShowBtnInputsByAction('postback', $btnContents, $btnTags, $btnSubmenus);
  700. // 顯示按鈕1
  701. $imagemap_btns.hide();
  702. $('#'+tabId+'Imagemapbtn1').show();
  703. }
  704. // AJAX 上傳圖片/影片
  705. // http://arsee88.blogspot.com/2020_09_20_archive.html
  706. var imagePreview = [];
  707. var spinner = [];
  708. var fileUploader = [];
  709. // 註冊上傳圖片的功能(可以重複註冊)
  710. var imagemapImgId = 'imagemapImg' + '1';
  711. imagePreview[imagemapImgId] = document.querySelector('[data-target="tab'+'1'+'-image-preview-imagemapImg"]');
  712. spinner[imagemapImgId] = document.querySelector('[data-target="tab'+'1'+'-spinner-imagemapImg"]');
  713. fileUploader[imagemapImgId] = document.querySelector('[data-target="tab'+'1'+'-file-uploader-imagemapImg"]');
  714. fileUploader[imagemapImgId].addEventListener("change", handleFileUpload);
  715. // File input 上傳檔案時綁定的處理函式
  716. async function handleFileUpload(e) {
  717. e.preventDefault();
  718. var $file_uploader = $(this);
  719. var splitItems = $file_uploader.attr('id').split('-');
  720. // tab1-file-uploader-imagemapImg
  721. var msgType = splitItems[splitItems.length-1]; // lastItem
  722. console.log(msgType);
  723. var msgNum = '1';
  724. var itemId = msgType + msgNum;
  725. console.log(itemId);
  726. try {
  727. const file = e.target.files[0];
  728. if (!file) {
  729. return;
  730. }
  731. setUploading(true, itemId);
  732. // 檢查
  733. const beforeUploadCheck = await checkBeforeUpload(file, msgType, msgNum);
  734. if (!beforeUploadCheck.isValid) throw beforeUploadCheck.errorMessages;
  735. // 上傳
  736. const arrayBuffer = await getArrayBuffer(file);
  737. var response = await uploadFileAJAX(arrayBuffer);
  738. alert("上傳成功");
  739. setUploading(false, itemId);
  740. showPreviewImage(response.url, itemId);
  741. // 更新 ImagemapBox
  742. var $imgPreview = $(imagePreview[itemId]);
  743. if ($imgPreview.attr('data-target').indexOf('imagemap') !== -1) {
  744. $imgPreview.closest('#imgmapcontent-tab1').find('.imagemapBox').css('background-image', 'url(' + response.url + ')');
  745. }
  746. } catch (error) {
  747. setUploading(false, itemId);
  748. alert(error);
  749. console.log("上傳失敗: ", error);
  750. } finally {
  751. e.target.value = '';
  752. }
  753. }
  754. // If you prefer Base64 image, use "FileReader.readAsDataURL"
  755. function showPreviewImage(url, itemId) {
  756. imagePreview[itemId].src = url;
  757. }
  758. // Change file object into ArrayBuffer
  759. function getArrayBuffer(fileObj) {
  760. return new Promise((resolve, reject) => {
  761. const reader = new FileReader();
  762. // Get ArrayBuffer when FileReader on load
  763. reader.addEventListener("load", () => {
  764. resolve(reader.result);
  765. });
  766. // Get Error when FileReader on error
  767. reader.addEventListener("error", () => {
  768. reject("error occurred in getArrayBuffer");
  769. });
  770. // read the blob object as ArrayBuffer
  771. // if you nedd Base64, use reader.readAsDataURL
  772. reader.readAsArrayBuffer(fileObj);
  773. });
  774. }
  775. // Use "new Uint8Array()"" to change ArrayBuffer into TypedArray (TypedArray is not a truly Array)
  776. // Use "Array.from()" to change it into Array
  777. function uploadFileAJAX(arrayBuffer) {
  778. return fetch("{{ env('APP_URL') }}/backend/dataManagement/eLecturerTeachersList/saveImg", {
  779. headers: {
  780. version: 1,
  781. "content-type": "application/json"
  782. },
  783. method: "POST",
  784. body: JSON.stringify({
  785. image: Array.from(new Uint8Array(arrayBuffer))
  786. })
  787. })
  788. .then(res => {
  789. if (!res.ok) {
  790. throw res.statusText;
  791. }
  792. return res.json();
  793. })
  794. .then(data => data)
  795. .catch(err => console.log("err", err));
  796. }
  797. function checkBeforeUpload(fileObject, msgType, msgNum) {
  798. return new Promise(resolve => {
  799. let errorMessages = [];
  800. // isValidFileType
  801. const validFileTypes = ["image/jpeg", "image/png" ];
  802. const isValidFileType = validFileTypes.includes(fileObject.type);
  803. if (!isValidFileType) {
  804. errorMessages.push("只支援 JPG / PNG 格式圖片");
  805. resolve({
  806. isValid: isValidFileType,
  807. errorMessages: errorMessages.join("\n")
  808. });
  809. return;
  810. }
  811. // isValidFileSize
  812. const isValidFileSize = (fileObject.size / 1024 / 1024) < 1; // 1 MB
  813. if (!isValidFileSize) {
  814. errorMessages.push("圖片最大 1MB");
  815. }
  816. // isValidDimension
  817. var isValidDimension = true;
  818. const dim = Promise.resolve(getImageSize(fileObject));
  819. dim.then(function(result) {
  820. switch (msgType)
  821. {
  822. case "imagemapImg":
  823. if (result.width != 800 || result.height != 800) {
  824. errorMessages.push("圖片尺寸必須為 800x800");
  825. isValidDimension = false;
  826. }
  827. break;
  828. }
  829. resolve({
  830. isValid: isValidFileType && isValidFileSize && isValidDimension,
  831. errorMessages: errorMessages.join("\n")
  832. });
  833. });
  834. });
  835. }
  836. function setUploading(isUploading, itemId) {
  837. if (isUploading === true) {
  838. spinner[itemId].classList.add("opacity-1");
  839. } else {
  840. spinner[itemId].classList.remove("opacity-1");
  841. }
  842. }
  843. function getImageSize(file) {
  844. return new Promise((resolve, reject) => {
  845. var _URL = window.URL || window.webkitURL;
  846. var img = new Image();
  847. img.onload = () => resolve({ height: img.height, width: img.width });
  848. img.onerror = reject;
  849. img.src = _URL.createObjectURL(file);
  850. });
  851. }
  852. </script>
  853. @endsection