[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-使用-github-gas-建立-liff-問卷":3},{"id":4,"slug":5,"title":6,"description":7,"content":8,"date":9,"image":10,"imageAlt":6,"author":11,"categories":14,"path":19,"minRead":20},3630,"使用-github-gas-建立-liff-問卷","使用 Github + GAS 建立 LIFF 問卷","使用 Github + GAS 建立 LIFF 問卷 之前有個專案需求是透過 LINE bot + Googl […]","\u003Ch1>使用 Github + GAS 建立 LIFF 問卷\u003C\u002Fh1>\n\u003Cp>之前有個專案需求是透過 LINE bot + Google Form 建立一套可以進行線上預約的LINE OA(官方帳號)， 但為了進行 UID 的對應設計一套迂迴的流程，實在不爽快。現在有機會透過 LIFF 導入，能簡化這邊的流程。\u003C\u002Fp>\n\u003Cp>由於團隊適應關係，繼續沿用 Google sheet 來做管理，因此需要使用 GAS 來開發一組 API 。\u003C\u002Fp>\n\u003Cp>基礎概念很簡單：\u003C\u002Fp>\n\u003Col>\n\u003Cli>先部署 LIFF 頁面\u003C\u002Fli>\n\u003Cli>製作 LIFF 頁面的問卷\u003C\u002Fli>\n\u003Cli>部署 GAS 為基礎的 API\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch1>LIFF 頁面部署\u003C\u002Fh1>\n\u003Cp>這邊使用的開發環境為 Vite + Vue3.3 + Tailwind + axios。\u003C\u002Fp>\n\u003Ch3>1. 宣告 LIFF 導入\u003C\u002Fh3>\n\u003Cp>LIFF 的導入可以參考\u003Ca href=\"https:\u002F\u002Fpost.billxu.net\u002Fblog\u002F2023\u002F01\u002F10\u002Fvue%e5%88%9d%e6%8e%a2liff%e7%9a%84%e4%b8%80%e4%ba%9b%e7%ad%86%e8%a8%98-%e9%96%8b%e7%99%bc%e7%af%87\u002F\" title=\"這篇\">這篇\u003C\u002Fa>\u003Cbr \u002F>\n宣告 import liff 後，在 onMounted 時宣告 init，如下\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-jsx\">import liff from &quot;@line\u002Fliff&quot;;\n\nonMounted(async () =&gt; {\n  liff\n  .init({\n    liffId: &quot;&lt;LIFF ID&gt;&quot;, \u002F\u002F 從LIFF頁面中拿到的ID\n  })\n  .then(() =&gt; { \n    if(liff.isLoggedIn()){\n      \u002F\u002F做你想要做的\n      isLogin.value = liff.isLoggedIn();\n      userid.value = liff.getDecodedIDToken().sub; \u002F\u002F這是UID\n    }\n\n  })\n  .catch((err) =&gt; {\n    console.log(err.code, err.message);\n  });\n})\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>2.在問卷頁中加入送出表單\u003C\u002Fh3>\n\u003Cp>就是一個 submitForm 。裡面預留 API 的位置，已經撰寫問卷本體。\u003Cbr \u002F>\n到這邊 LIFF 大致上就做完了。接下來要部署到 Github Page 上\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-jsx\">&lt;form class=&quot;flex flex-col items-center&quot; @submit.prevent=&quot;submitForm&quot;&gt;\n&lt;label for=&quot;name&quot; class=&quot;block mt-4&quot;&gt;請問您的姓名：&lt;span class=&quot;text-red-500 text-sm&quot;&gt;＊（請輸入文字）&lt;\u002Fspan&gt;&lt;\u002Flabel&gt;\n  &lt;input type=&quot;text&quot; id=&quot;name&quot; v-model=&quot;name&quot; placeholder=&quot;姓名&quot; class=&quot;border border-gray-300 rounded-md px-2 py-1 mt-1&quot; pattern=&quot;[A-Za-z]+&quot; title=&quot;請輸入有效的姓名（只能包含字母）&quot; required \u002F&gt;\n\n  &lt;label for=&quot;phone&quot; class=&quot;block mt-4&quot;&gt;請問您的連絡電話：&lt;span class=&quot;text-red-500 text-sm&quot;&gt;＊（請輸入數字）&lt;\u002Fspan&gt;&lt;\u002Flabel&gt;\n  &lt;input type=&quot;text&quot; id=&quot;phone&quot; v-model=&quot;phone&quot; placeholder=&quot;連絡電話&quot; class=&quot;border border-gray-300 rounded-md px-2 py-1 mt-1&quot; pattern=&quot;[0-9]+&quot; title=&quot;請輸入有效的連絡電話（只能包含數字）&quot; required \u002F&gt;\n\n  &lt;label for=&quot;purchaseDate&quot; class=&quot;block mt-4&quot;&gt;請問您的購買時間：&lt;\u002Flabel&gt;\n  &lt;input type=&quot;date&quot; id=&quot;purchaseDate&quot; v-model=&quot;purchaseDate&quot; class=&quot;border border-gray-300 rounded-md px-2 py-1 mt-1&quot; required \u002F&gt;\n\n  &lt;label for=&quot;storeAddress&quot; class=&quot;block mt-4&quot;&gt;請問您購買的經銷門市地址：&lt;\u002Flabel&gt;\n  &lt;select id=&quot;storeAddress&quot; v-model=&quot;storeAddress&quot; class=&quot;border border-gray-300 rounded-md px-2 py-1 mt-1&quot; required&gt;\n      &lt;option value=&quot;&quot;&gt;請選擇&lt;\u002Foption&gt;\n      &lt;option value=&quot;address1&quot;&gt;A門市&lt;\u002Foption&gt;\n      &lt;option value=&quot;address2&quot;&gt;B門市&lt;\u002Foption&gt;\n      &lt;option value=&quot;address3&quot;&gt;C門市&lt;\u002Foption&gt;\n  &lt;\u002Fselect&gt;\n&lt;\u002Fform&gt;\n\nconst submitForm = () =&gt; {\n\n    fconst formData = new FormData();\n  formData.append(&#039;name&#039;, name.value);\n  formData.append(&#039;phone&#039;, phone.value);\n  formData.append(&#039;userid&#039;, user.userid);\n\n    axios({\n            method: &#039;post&#039;,\n            url: &#039;&lt;GAS api&gt;&#039;,\n            data: formData,\n            headers: { &#039;Content-Type&#039;: &#039;multipart\u002Fform-data&#039; }\n        })\n        .then(response =&gt; {\n            \u002F\u002F 成功\n            console.log(response);\n        })\n        .catch(error =&gt; {\n            \u002F\u002F 失敗\n\n        });\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>使用  Github Actions 部署\u003C\u002Fh2>\n\u003Cp>為了省成本而部署在 Github Page 上，使用 Github Actions 來進行，請參考\u003Ca href=\"https:\u002F\u002Fpost.billxu.net\u002Fblog\u002F2024\u002F01\u002F08\u002F%e4%bd%bf%e7%94%a8-github-action-%e9%83%a8%e7%bd%b2-github-pages\u002F\" title=\"這篇\">這篇\u003C\u002Fa>。\u003Cbr \u002F>\ngh-pages-deploy.yml\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yaml\">name: Deploy\n\non:\n  push:\n    branches:\n      - main  # 將此處更改為你的默認分支名稱，例如：master\n\njobs:\n  build-and-deploy:\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout\n      uses: actions\u002Fcheckout@v2\n\n    - name: Install &amp; Build # 執行指令\n      run: |\n        npm install\n        npm run build \n\n    - name: Deploy\n      uses: JamesIves\u002Fgithub-pages-deploy-action@4.1.5\n      with:\n        branch: gh-pages  # GitHub Pages 的分支\n        folder: dist  # build 後的檔案夾\n\n    - name: Debug git error\n      run: git status\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>登記為 LIFF\u003C\u002Fh2>\n\u003Cp>部署完後，記得要去 LINE 開發者功能中登記 LIFF ，可以參考\u003Ca href=\"https:\u002F\u002Fpost.billxu.net\u002Fblog\u002F2023\u002F01\u002F10\u002Fline%e5%88%9d%e6%8e%a2liff%e7%9a%84%e4%b8%80%e4%ba%9b%e7%ad%86%e8%a8%98-%e7%94%b3%e8%ab%8b%e7%af%87\u002F\" title=\"這篇\">這篇\u003C\u002Fa>。\u003Cbr \u002F>\n就拿 Github Page 的 url 就可以。\u003C\u002Fp>\n\u003Ch1>GAS 部署\u003C\u002Fh1>\n\u003Cp>接著要製作 GAS 的 API ，請參考\u003Ca href=\"https:\u002F\u002Fwww.google.com\u002Fscript\u002Fstart\u002F\">官方文件\u003C\u002Fa>。\u003C\u002Fp>\n\u003Ch3>建立 POST 接口\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-jsx\">function doPost(e) {\n  var param = e.parameter;\n\n  const data = {\n    name: param.name,\n    phone: param.phone,\n    userID: param.userid\n  }\n  setData(data); \u002F\u002F將資料放進表單\n  return ContentService.createTextOutput(JSON.stringify({param}))\n                       .setMimeType(ContentService.MimeType.JSON) \u002F\u002F為了debug 先回傳資料\n}\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>將資料放進表單\u003C\u002Fh3>\n\u003Cp>因為我是在別的文件開啟GAS，所以另外指定了 fileId 。簡單說就是看你的 Google sheet 位置，像這樣：\u003Ca href=\"https:\u002F\u002Fdocs.google.com\u002Fspreadsheets\u002Fd\u002F\">https:\u002F\u002Fdocs.google.com\u002Fspreadsheets\u002Fd\u002F\u003C\u002Fa>\u003CID>，取用\u003CID>那段就對了，記得要還要指定工作表喔。\u003C\u002Fp>\n\u003Cp>如果是同一個文件，可以使用\u003Ccode>getActiveSpreadsheet\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-jsx\">function setData(data){\n    var fileId = &#039;&lt;ID&gt;&#039;; \u002F\u002F 請用你的檔案ID替換此處\n    \u002F\u002F 透過ID開啟另一個檔案\n    var file = SpreadsheetApp.openById(fileId);\n    \u002F\u002F 獲取另一個檔案的工作表\n    var sheet = file.getSheetByName(&#039;&lt;工作表名稱&gt;&#039;);\n\n    const userID = data.userID;\n    var lastRow = sheet.getLastRow();\n\n        \u002F\u002F 如果是第一筆，先做標題\n    if (lastRow == 0) {\n      var headerRow = [&#039;name&#039;,&#039;phone&#039;,&#039;UID&#039;];\n      sheet.appendRow(headerRow);\n    }\n\n    \u002F\u002F 整理資料成陣列(注意是二維陣列)\n    var values = [[data.name,data.phone,userID]];\n\n    \u002F\u002F 放資料\n    sheet.getRange(lastRow + 1, 1, values.length, values[0].length).setValues(values);\n}\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>部署位置\u003C\u002Fh3>\n\u003Cp>前面 LIFF 部署時，有個 \u003CGAS api> ，就是這時 GAS 部署完後會有組位置像這樣：\u003Ca href=\"https:\u002F\u002Fscript.google.com\u002Fmacros\u002Fs\u002Fxxxxxxxx\u002Fexec，將這串貼到\">https:\u002F\u002Fscript.google.com\u002Fmacros\u002Fs\u002Fxxxxxxxx\u002Fexec，將這串貼到\u003C\u002Fa> \u003CGAS api> 。\u003C\u002Fp>\n\u003Cp>這樣就可以建立一個可以取得 UID 的 LIFF 頁面，並且透過 GAS 存到 Google sheet 中，但記得 GAS API 有一些\u003Ca href=\"https:\u002F\u002Fdevelopers.google.com\u002Fapps-script\u002Fguides\u002Fservices\u002Fquotas?hl=zh-tw\">使用上限制\u003C\u002Fa>，用來建立微型服務絕對沒問題，但如果有用量上考量，建議不要這樣玩XD\u003C\u002Fp>\n","2024-01-16T16:20:22","\u002Fapi\u002Fproxy-image?url=https%3A%2F%2Fpost.billxu.net%2Fwp-content%2Fuploads%2F2024%2F01%2Fb40e6ed5-cbed-4701-b2c9-68e84f0754d4.jpeg",{"name":12,"link":13},"Chih Fan","http:\u002F\u002Fpost.billxu.net",[15,17],{"name":16,"slug":16},"node",{"name":18,"slug":18},"vue","\u002Fblog\u002F使用-github-gas-建立-liff-問卷",3]