2024/02/27

Gg / 透過Google Apps Script 建立上傳檔案頁面

這個使用Google Apps Script 建立的上傳檔案頁面,參考很多範例跟參考資源 
也因為需求而修改、新增功能,例如將檔案資訊呈現在頁面上、限制上傳者、特定檔案格式與檔案大小、可以同時多個檔案上傳 
不過詳細參考哪些內容,已經不可考了 
唯一能確認的是有參考《Google Apps Script雲端自動化與動態網頁實戰》的範例,這本書也是目前正體中文唯一的相關書籍 
整體而言,都是使用Google的相關功能:
以google雲端硬碟為儲存空間,並且將檔案資訊紀錄在google 試算表,呈現在Google Apps Script網頁程式建構的網頁   
 


整體架構

嚴格來說只有2個部分:程式碼.gs跟formHtml.html 
style.css.html跟scrpt.js.html都會寫入 formHtml.html 分別出來是為了讓程式更為容易閱讀跟修改
 

運作流程圖


1.程式碼.gs

以Google Apps Script作為後端,執行跟google雲端硬碟與試算表之間的操作

#1~4 function doGet()

這個是讓網頁程式能夠在開啟時就能夠讀取formHtml

#8~10 function include(filename)

自定義函式,可以讓formHtml讀取 Google Apps Script的其他資料,例如:style.css、script.js

#13~120 function uploadFile(form)

接收前端傳過來的資料,在這裡判斷是否為有權限使用者、檔案類型、檔案大小等 如果有任何錯誤訊息,立即中止程式並傳回錯誤訊息給前端 其實也可以寫在前端網頁,但是這樣就很吃瀏覽器的資源

#124~162 function getInt(e)

透過前端網頁載入所有資料並建立DOM之後所觸發的function init() 將目前的google試算表資料回傳到前端的 function onStart() 作為開啟網頁時的初始資料

#166~171 function getScriptURL()

前端重新整理的按鈕觸發時,傳回網頁程式的網址 備註:當網頁程式被嵌入在其他網頁時,這個功能沒有作用,即使是回傳特定網址也是如此

#175~283 function moreFiles(obj)

跟function uploadFile(form)的差別在於function moreFiles(obj)是用來處理多個檔案 兩者接收的資料結構不一樣 function uploadFile(form)是直接接收前端單一檔案的表單資料 多個檔案的時候,在前端先分割檔案,再分別傳到 function moreFiles(obj) 應該也是可以直接傳到後端,只是就是在後端進行檔案分割
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
function doGet() {
  var output = HtmlService.createTemplateFromFile("formHtml").evaluate().setTitle("第一組研發資料上傳");
  output.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);//可以將發布成html介面的程式  崁入其他網站
  return output;
}
//
//
function include(filename) {
    return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
//
//一個檔案
function uploadFile(form) {
  //Logger.log(form.uploadFile.length);  
  //
  var userId = ["trico"];
  var antiMsg = "<span class='notie'>※※【請注意】※※<br></span>";
  var antiNum = 0;
  for(var u =0 ; u < userId.length ; u++){
      if(form.id !== userId[u]){ 
        //Logger.log(userId[u]);
        //Logger.log(userId.length);
        antiNum += 1;
        antiMsg = "<span class='notie'>無權限使用者!!</span></br>"; 
      }
  }
  if(antiNum > userId.length-1){
        return antiMsg;
      }
  //
  try {
      var sheetName = "第一組研發資料上傳列表";
      var foldername = "第一組研發資料";
      var folder, folders;

      var msg = "<span class='notie'>※※【請注意】※※<br></span>";

      folders = DriveApp.getFoldersByName(foldername);

      if (folders.hasNext()) {
          folder = folders.next();
      } else {
          folder = DriveApp.createFolder(foldername);
      }

      var fileExt = form.uploadFile.name;
      //取出副檔名
      fileExt = fileExt.substring(fileExt.lastIndexOf('.') + 1);
      
      var validExts = new Array("jpg", "doc", "docx", "pdf", "ppt", "pptx", "xls", "xlsx", "txt", "odt", "ods", "rar", "zip");
      
      if (form.id !== "" && form.uploadFile.length !== 0 && form.uploadFile.length < 10485760 && validExts.indexOf(fileExt) > -1) {
          var blob = form.uploadFile;
          var file = folder.createFile(blob);
          //var phone = "'" + form.phone.toString();
          file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
          file.setDescription("上傳者: " + form.id);
      } else {
          if (form.id === "")
              msg += "<span class='notie'>姓名遺漏</span></br>";
         // if (form.phone === "")
         //     msg += "<span class='notie'>電話遺漏</span></br>";
         // if (form.email === "")
         //     msg += "<span class='notie'>信箱遺漏</span></br>";
          if (form.uploadFile.length === 0)
              msg += "<span class='notie'>檔案遺漏</span></br>";
          if (form.uploadFile.length >= 10485760)
              msg += "<span class='notie'>檔案超過10MB</span></br>";
          if (validExts.indexOf(fileExt) < 0 && form.uploadFile.length !== 0)
              msg += "<span class='notie'>上傳的檔案格式為" + fileExt + ",只接受<strong  style=\"color:red\">" + validExts.toString() + "</strong></span></br>";
          return msg;
      }
      var fileUrl = file.getUrl();
      var fileName = file.getName();
      var FileIterator = DriveApp.getFilesByName(sheetName);
      var sheetApp = "";
      while (FileIterator.hasNext()) {
          var sheetFile = FileIterator.next();
          if (sheetFile.getName() == sheetName && sheetFile.getMimeType()=="application/vnd.google-apps.spreadsheet") {
              sheetApp = SpreadsheetApp.open(sheetFile);
          }
      }
      if (sheetApp == "") {
          sheetApp = SpreadsheetApp.create(sheetName);
          sheetApp.getSheets()[0].getRange(1, 1, 1, 4).setValues([["上傳時間", "姓名", "檔案名稱", "檔案網址"]]);
      }
      var sheet = sheetApp.getSheets()[0];
      var lastRow = sheet.getLastRow();

      //寫入資料表
      var targetRange = sheet.getRange(lastRow + 1, 1, 1, 4).setValues([[new Date().toLocaleString(), form.id, fileName, fileUrl]]);

      //寫出資料到網頁      
      var Sheet = sheetApp.getSheets()[0]
      var LastRow = Sheet.getLastRow();
      var LastColumn = Sheet.getLastColumn();
      var data = [];
      data.push("檔案上傳成功!");
      var listAll = Sheet.getSheetValues(1, 1, LastRow, LastColumn);
      var listA = [];
      for (var j = 0; j < listAll[0].length; j++) {
          listA.push(listAll[0][j]);
      }
      data.push(listA);
      for (var i = 1; i < listAll.length; i++) {
          var listOne = [];
          for (var k = 0; k < listAll[0].length; k++) {
            if(k==3){ //檔案網址 欄位
              listAll[i][k]= "<a href='" + listAll[i][k] +"' target='_blank'>" + listAll[i][k].trim() + "</a> ";
              listOne.push(listAll[i][k]);
            }else{
              listOne.push(listAll[i][k]);
            }
          }
          data.push(listOne);      }
      return JSON.parse(JSON.stringify(data));
  } catch (error) {
      return "檔案上傳失敗! 原因:" + error.toString();
  }  
}
//
//網頁初始 抓取資料
//
function getInt(e) {
    var sheetName = "第一組研發資料上傳列表";
    var FileIterator = DriveApp.getFilesByName(sheetName);
    var sheetApp = "";
    while (FileIterator.hasNext()) {
        var sheetFile = FileIterator.next();
        if (sheetFile.getName() == sheetName) {
            sheetApp = SpreadsheetApp.open(sheetFile);
        }
    }    
    if (sheetApp == "") {
          sheetApp = SpreadsheetApp.create(sheetName);
          sheetApp.getSheets()[0].getRange(1, 1, 1, 4).setValues([["上傳時間", "姓名", "檔案名稱", "檔案網址"]]);
    }    
    var Sheet = sheetApp.getSheets()[0]
    var LastRow = Sheet.getLastRow();
    var LastColumn = Sheet.getLastColumn();
    var data = [];
    data.push("int");
    var listAll = Sheet.getSheetValues(1, 1, LastRow, LastColumn);
    var listA = [];
    for (var j = 0; j < listAll[0].length; j++) {
        listA.push(listAll[0][j]);
    }
    data.push(listA);
    for (var i = 1; i < listAll.length; i++) {
        var listOne = [];
        for (var k = 0; k < listAll[0].length; k++) {
          if(k==3){ //檔案網址
            listAll[i][k]= "<a href='" + listAll[i][k] +"' target='_blank'>" + listAll[i][k].trim() + "</a> ";
            listOne.push(listAll[i][k]);
          }else{
            listOne.push(listAll[i][k]);
          }            
        }
        data.push(listOne);
    }
    return JSON.parse(JSON.stringify(data));  
}
//
//重新整理網頁
//嵌入網頁 不能使用
function getScriptURL() {
  return ScriptApp.getService().getUrl();

  //前往特定網頁
  //return "https://sites.google.com/view/ntnupriori/%E7%AC%AC%E4%B8%80%E7%B5%84/%E6%9C%83%E8%AD%B0%E8%A8%98%E9%8C%84"; //https://sites.google.com/view/ntnupriori/第一組/會議記錄
}
//
// 超過一個檔案
//
function moreFiles(obj) {
  //Logger.log("超過一個檔案");  
  var userObj = obj.userName;
  //Logger.log(userObj);
  //Logger.log(obj.fileName);
  //Logger.log(obj.data.length);

  var userId = ["trico"];
  var antiMsg = "<span class='notie'>※※【請注意】※※<br></span>";
  var antiNum = 0;
  for(var u =0 ; u < userId.length ; u++){
      if(userObj !== userId[u]){ 
        //Logger.log(userId[u]);
        //Logger.log(userId.length);
        antiNum += 1;
        antiMsg = "<span class='notie'>無權限使用者!!</span></br>"; 
      }
  }
  if(antiNum > userId.length-1){
        return antiMsg;
  }
  //
  try {
      var sheetName = "第一組研發資料上傳列表";
      var foldername = "第一組研發資料";
      var folder, folders;

      var msg = "<span class='notie'>※※【請注意】※※<br></span>";

      folders = DriveApp.getFoldersByName(foldername);
      if (folders.hasNext()) {
          folder = folders.next();
      } else {
          folder = DriveApp.createFolder(foldername);
      }

      var fileExt = obj.fileName;
      fileExt = fileExt.substring(fileExt.lastIndexOf('.') + 1);
      
      var validExts = new Array("jpg", "doc", "docx", "pdf", "ppt", "pptx", "xls", "xlsx", "txt", "odt", "ods", "rar", "zip");
      
      if (userObj !== "" &&  obj.data.length < 10485760 && validExts.indexOf(fileExt) > -1) {
          var blob = Utilities.newBlob(Utilities.base64Decode(obj.data), obj.mimeType, obj.fileName);
          var file = folder.createFile(blob);
          //var phone = "'" + form.phone.toString();
          file.setDescription("上傳者: " + userObj.id);
      } else {
          if (userObj === "")
              msg += "<span class='notie'>姓名遺漏</span></br>";
         // if (form.phone === "")
         //     msg += "<span class='notie'>電話遺漏</span></br>";
         // if (form.email === "")
         //     msg += "<span class='notie'>信箱遺漏</span></br>";
          if (obj.data.length === 0)
              msg += "<span class='notie'>檔案遺漏</span></br>";
          if (obj.data.length >= 10485760)
              msg += "<span class='notie'>檔案超過10MB</span></br>";
          if (validExts.indexOf(fileExt) < 0 && obj.data.length !== 0)
              msg += "<span class='notie'>上傳的檔案「"+ obj.fileName +"」格式為" + fileExt + ",只接受<strong  style=\"color:red\">" + validExts.toString() + "</strong></span></br>";
          return msg;
      }
      var fileUrl = file.getUrl();
      var fileName = file.getName();
      var FileIterator = DriveApp.getFilesByName(sheetName);
      var sheetApp = "";
      while (FileIterator.hasNext()) {
          var sheetFile = FileIterator.next();
          if (sheetFile.getName() == sheetName) {
              sheetApp = SpreadsheetApp.open(sheetFile);
          }
      }
      if (sheetApp == "") {
          sheetApp = SpreadsheetApp.create(sheetName);
          sheetApp.getSheets()[0].getRange(1, 1, 1, 4).setValues([["上傳時間", "姓名", "檔案名稱", "檔案網址"]]);
      }
      var sheet = sheetApp.getSheets()[0];
      var lastRow = sheet.getLastRow();
      
      var targetRange = sheet.getRange(lastRow + 1, 1, 1, 4).setValues([[new Date().toLocaleString(), userObj, fileName, fileUrl]]);
      
      var Sheet = sheetApp.getSheets()[0]
      var LastRow = Sheet.getLastRow();
      var LastColumn = Sheet.getLastColumn();
      var data = [];
      data.push("檔案上傳成功!");
      var listAll = Sheet.getSheetValues(1, 1, LastRow, LastColumn);
      var listA = [];
      for (var j = 0; j < listAll[0].length; j++) {
          listA.push(listAll[0][j]);
      }
      data.push(listA);
      for (var i = 1; i < listAll.length; i++) {
          var listOne = [];
          for (var k = 0; k < listAll[0].length; k++) {
            if(k==3){ //檔案網址
              listAll[i][k]= "<a href='" + listAll[i][k] +"' target='_blank'>" + listAll[i][k].trim() + "</a> ";
              listOne.push(listAll[i][k]);
            }else{
              listOne.push(listAll[i][k]);
            }
          }
          data.push(listOne);
      }
      return JSON.parse(JSON.stringify(data));
  } catch (error) {
      return "檔案上傳失敗! 原因:" + error.toString();
  }
  //return DriveApp.createFile(blob).getId();
}
 

2.formHtml

#7 <?!= include('style.css'); ?>載入自訂的css

#8~11 載入Bootstrap的js、css檔案

#15~57 查詢頁面

#58 <?!= include('script.js'); ?>載入自訂的script

這裡的函式主要是接收後端傳回的資料並處理

#59~181 自定義的function

這裡的函式主要是處理頁面表單的資料,以及將資料傳到後端 比較需要說明的是當觸發"上傳檔案"(#67~148),會先檢驗表單內容是否有資料 如果有遺漏就會終止程序並顯示錯誤訊息 沒有遺漏值就會接著上傳檔案的流程 會先判斷檔案數量再分別執行對應的程式 1個檔案:google.script.run.withSuccessHandler(fileUploaded).uploadFile(this.parentNode); 2個檔案以上:google.script.run.withSuccessHandler(fileUploaded).moreFiles(obj); 其實可以用2個檔案以上的程序來處理1個檔案,這樣程式碼可以更為精簡
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
<!doctype html>
<html lang="zh-Hant">
  <head>
     <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  <?!= include('style.css'); ?>
    <!-- Bootstrap -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js" integrity="sha384-q2kxQ16AaE6UbzuKqyBE9/u/KzioAlnx2maXQHiDX9d4/zp8Ok3f+M7DPm+Ib6IU" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.min.js" integrity="sha384-pQQkAEnwaBkjpqZ8RU1fF1AKtTcHJwFl3pblpTlHXybJjHpMYo79HY3hIi4NKxyj" crossorigin="anonymous"></script>
   <base target="_top">
  </head>
  <body>
    <div id="content">
      <div id="wait">請稍後...</div>
      <div id="bardiv">
        <input class="form-control" type="button" name="button1"  id="showLayout" value="上傳檔案">
        <input class="form-control" type="button" name="button2"  id="offLayout" value="關閉">
      </div>
      <p>
      <div id="layout">
          <h1>第一組研發資料上傳</h1>
          <form id="upload">
            <div class="input-group mb-3">
              <label class="input-group-text" for="id" class="form-label">請輸入你的姓名:</label>
              <input class="form-control" type="text" name="id" id="fId" placeholder="姓名" tabindex="1">
            </div>
            <!--<div class="input-group mb-3">
              <label class="input-group-text" for="phone" class="form-label">請輸入你的手機電話:</label>
              <input class="form-control" type="text" name="phone" id="fPhone" placeholder="0912-123-456" tabindex="2">
            </div>-->
            <!--<div class="input-group mb-3">
              <label class="input-group-text" for="email" class="form-label">請輸入你的Email:</label>
              <input class="form-control" type="text" name="email" id="fEmail" placeholder="123@abc.com" tabindex="3">
            </div>-->
            <div class="input-group mb-3">
              <input class="form-control" type="file" name="uploadFile" id="fFile" multiple>
            </div>
            <input id="send" class="form-control" type="submit" value="上傳檔案">
          </form>
          <div id="output"></div>
          <!--<input class="form-control" type="button" name="button3" id="f5" onclick="ftnOn()" value="重新整理">-->
          <input class="form-control" type="button" name="button3" id="f5" value="重新整理"><!-- 監聽事件 -->
          <div id="output2"></div>
          <div class="toast align-items-center" role="alert" aria-live="assertive" aria-atomic="true">
            <div class="d-flex">
              <div id="output3" class="toast-body"></div>
              <button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
            </div>
          </div>
      </div>
   </div>
   <p>
   <div id="fileList">    
   </div>
  </body>
  <?!= include('script.js'); ?>
  <script>
    //寫出目前的資料
    function init() {
      google.script.run.withSuccessHandler(onStart).getInt(1);
    }
    //觸發監控 當DOMContentLoaded執行 function init()
    document.addEventListener("DOMContentLoaded", init);
    //
    var btn = document.getElementById("send");  
    btn.addEventListener("click", function(e){
      //
      btn.disabled = true;
      document.getElementById('output').innerHTML = "";
      var fId = document.getElementById("fId").value;
      //var fPhone = document.getElementById("fPhone").value;
      //var fEmail = document.getElementById("fEmail").value;
      var fFile = document.getElementById("fFile").value;
      //console.log(fId);
      //console.log(fPhone);
      //console.log(fEmail);
      //console.log(document.getElementById("fFile").files[0]);
      //console.log(document.getElementById("fFile").files[1]);
      
      var e1="";      
      //console.log("------------------");
      if(fId == ""){
        e1 += "<span class='notie'>請輸入姓名</span></br>";
      }
      /*
      fPhone = fPhone.replace(/\s+/g, ",");
      var reM = /09\d{2}-\d{3}-\d{3}/;
      var foundM = fPhone.match(reM);
      //console.log(foundM);

      if(foundM == null){
        e1 += "<span class='notie'>請輸入正確電話格式</span></br>";
      }      
      //console.log(e1);
      //
      fEmail = fEmail.replace(/\s+/g, ",");
      var reE = /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z]+$/;
      var foundE = fEmail.match(reE);      
      if(foundE == null){
        e1 += "<span class='notie'>請輸入正確電子信箱格式</span></br>";
      }
      //console.log(fFile);
      */

      if(fFile ==""){
        e1 += "<span class='notie'>請選擇檔案</span></br>";
      }
      
      if(e1 !=""){        
        document.getElementById('output').innerHTML = e1;
        btn.disabled = false;
      }else{
        this.value='上傳中...'; 
        //console.log(this.parentNode);
        //
        //google.script.run.withSuccessHandler(fileUploaded).uploadFile(this.parentNode);
        var f = document.getElementById('fFile');
        var fNum = f.files.length;
        //console.log(f.files.length);
        if(fNum ==1){
          //1個檔案
          console.log("this.parentNode");
          console.log(this.parentNode);
          console.log("this.parentNode");
          google.script.run.withSuccessHandler(fileUploaded).uploadFile(this.parentNode);

        } else {
            //兩個以上檔案
            //console.log(f.files.length);
            [...f.files].forEach((file, i) => { //[...Array] ES6 Spread Syntax 陣列展開語法  ps.物件{...object}
              var fr = new FileReader();
              fr.onload = (e) => {
                var data = e.target.result.split(",");
                var obj = {userName: fId ,fileName: f.files[i].name, mimeType: data[0].match(/:(\w.+);/)[1], data: data[1]};
                console.log("obj");
                console.log(obj);
                console.log("obj");
                google.script.run.withSuccessHandler(fileUploaded).moreFiles(obj);
              }
            fr.readAsDataURL(file);
          });
        }
        //
      }
        e.preventDefault();
      }, false); 
    //
    var stn = document.getElementById("showLayout");
    stn.addEventListener("click", function(e){      
      document.getElementById("layout").style.display='block';
      stn.style.display='none';
      otn.style.display='block';
      e.preventDefault();
    }, false); 
    //
    var otn = document.getElementById("offLayout");
    otn.addEventListener("click", function(e){      
      document.getElementById("layout").style.display='none';
      stn.style.display='block';
      otn.style.display='none';
      e.preventDefault();
    }, false); 
    
    //重新整理網頁
    //嵌入網頁 不能使用
    var ftn = document.getElementById("f5");
    ftn.addEventListener("click", function(e){    
      ftn.disabled = true;
      google.script.run.withSuccessHandler(function(url){window.open(url,'_top');}).getScriptURL();
      e.preventDefault();
    }, false); 
    //
    /*  
    //嵌入網頁 使用 onclick 不能使用
    function ftnOn(){
        google.script.run.withSuccessHandler(function(url){window.open(url,'_top');}).getScriptURL();
    }
    */ 
  </script> 
</html>
 

3.style.css

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
<style>
#content {
  width :800px;
  margin:10px auto;
  align-items: center;
  justify-content:  center;
}
#layout{
  display:  none;
  position: relative;
  text-align: center;
  padding:10px;
  border: 2px solid #FF0000;
  //width: 520px;
  height: 450px;
}
#send {
  color: green;
}
.notie{
  color:blue;  
}
#output{
  padding-top:5px;
  color:red;
}
#output2{
  position: absolute;
  width:220px;
  height:50px;
  display:none;
  background-color:#DBABFF;
  //bottom:45px;
  left:280px;
  font-size: 10px;
  padding-top:2px;
  border-radius:10px;
  animation-name: MoveToUp;    /*動畫名稱,需與 keyframe 名稱對應*/
  animation-duration: 4s;      /*動畫持續時間,單位為秒*/
  animation-delay: 0s;         /*動畫延遲開始時間*/
  //animation-iteration-count: infinite;    /*動畫次數,infinite 為無限次*/ 
  animation-fill-mode: forwards;   
}
/* 關鍵影格(@keyframe) */
@keyframes MoveToUp {
    from { bottom: 0px; }
    to { bottom: 45px; }
}
#fileList {
  //display:  none;
  //display:  flex;
  width :1000px;
  margin:10px auto;
  align-items: center;
  justify-content:  center;
}
.table,.table * {
      padding: 5px;
      font-size: 12px;
      font-family: "Noto Sans CJK TC", "Microsoft JhengHei", PingFang, STHeiti, sans-serif, serif;
}
.table {
      display: table;
      width: 100%;
      border-collapse: collapse;
}
.table-tr {
      display: table-row;
      height: 30px;
}
.table-th {
      display: table-cell;
      font-weight: bold;
      height: 100%;
      border: 1px solid gray;
      text-align: center;
      vertical-align: middle;
      background-color: #E5E5E5;
}
.table-td {
      display: table-cell;
      height: 100%;
      border: 1px solid gray;
      text-align: start;
      vertical-align: middle;
      padding-left: 4px;
}
#showLayout {
  display:none;  
}
#offLayout {
  display:none;  
}
#wait {
  display:block;  
}
.notie{
  color:blue;  
}
#f5 {
  display:none;  
}
</style>
 

4.script.js

#2~49 變數dateReviver接收函式表達式處理日期資料型態後的回傳值

這個程式在網路上可以找到非常多的資料,幾乎是萬用函式了

#51~60 處理錯誤訊息msg裡 class為notie的資料

[].slice.call可以將後面的物件轉換成陣列 (詳見:12) 所以 toastElList紀錄了class為notie的資料 toastList紀錄了建立的bootstrap.Toast物件 最後會在#108觸發 #137~141的函式 function showToast()

#63~89 function onStart(e)

接收網頁開啟時被fuction init()觸發的後端函式 function getint()傳回的初始資料

#91~136 function fileUploaded(status)

處理後端函式fileUploaded()或fileUploaded()傳回的資料

#137~141 function showToast()

遍歷toastList執行bootstrap.Toast的show(),會顯示在class為 toast-body的 div output3
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<script>
  var dateReviver = function (key, value) {
  //將日期字串轉為日期資料型態
  //console.log("key", key);
  //console.log("value", value);
  var a;
  if (typeof value === "string") {
    a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); //比對是否符合日期型態
    //console.log(a);
    if (a) {
      var dataString = new Date(
          Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]) //使用+符號 將文字形數字轉為數值類型
      );
      //console.log("dataString", dataString);
      var weekday = new Array(6);
      weekday[0] = "日";
      weekday[1] = "一";
      weekday[2] = "二";
      weekday[3] = "三";
      weekday[4] = "四";
      weekday[5] = "五";
      weekday[6] = "六";
      var hour = dataString.getHours();
      var min = dataString.getMinutes();
      var sec = dataString.getSeconds();
      // 轉成字串,如果低於10,前面加上'0'
      var hourString = hour < 10 ? "0" + hour : "" + hour;
      var minString = min < 10 ? "0" + min : "" + min;
      var secString = sec < 10 ? "0" + sec : "" + sec;
      var formatted_date =
        dataString.getFullYear() +
        "/" +
        (dataString.getMonth() + 1) +
        "/" +
        dataString.getDate() +
        "(" +
        weekday[dataString.getDay()] +
        ")" +
        " " +
        hourString +
        ":" +
        minString +
        ":" +
        secString;
      return formatted_date;
    }
  }
  return value;
}
//
  var option={
    animation:true,
    delay:5000,
  }  
  //option其實是物件
    
  var toastElList = [].slice.call(document.querySelectorAll('.toast'))
  var toastList = toastElList.map(function (toastEl) {
    return new bootstrap.Toast(toastEl, option)
  })
  //

function onStart(e){
  //console.log("onStart");
  //console.log(e);
  //console.log("onStart");
  var msg = JSON.parse(JSON.stringify(e, dateReviver));
  var html="";
  html += '<div class="table"><div class="table-tr">';
  //取出標題
  for (var k = 0; k < msg[1].length; k++) {
        html +=
          '<div class="table-th">' +
          msg[1][k] +
          "</div>";
      }
  html += "</div>";
  //取出內容    
  for(var i = 2; i < msg.length ; i++){
    html += '<div class="table-tr">'; //
    for(var j = 0; j < msg[2].length ; j++){
        html += '<div class="table-td">' +msg[i][j] +  "</div>";
    }
    html += "</div>";
  }
  document.getElementById('fileList').innerHTML = html;
  document.getElementById("showLayout").style.display='block';;
  document.getElementById("wait").style.display='none';;
}
  //
function fileUploaded(status) {
  var msg = JSON.parse(JSON.stringify(status, dateReviver));
  //console.log(msg);
  //console.log(msg[0]);
  //console.log(msg[1]);
  //console.log(msg[2]);
  //console.log(msg[3]);
  
  var html="";    
  
  if (msg[0]!=="檔案上傳成功!"){
      document.getElementById('send').value = "重新上傳作業";
      document.getElementById('send').disabled = false;
      document.getElementById('output').innerHTML += msg;  //上傳失敗 msg只會有一個字串資料
      //document.getElementById('output2').style.display = 'block';
      //document.getElementById('output2').innerHTML = msg;
      //document.getElementById('output3').innerHTML = msg+"<br><span>請確認資料</span>";
      //showToast();      
    }else{
      document.getElementById('upload').style.display = 'none';
      //document.getElementById('layout').style.height = '200px';
      document.getElementById('output').innerHTML += msg[0] +"</br><span class='notie'>如果需要新增檔案,請重新整理網頁!!</span></br>";
      document.getElementById('output2').style.display = 'none'; 
      document.getElementById('fileList').style.display = 'block';
      document.getElementById('f5').style.display = 'block'; //內嵌 不能使用
      
      html += '<div class="table"><div class="table-tr">';
      //取出標題
      for (var k = 0; k < msg[1].length; k++) {
            html +=
              '<div class="table-th">' +
              msg[1][k] +
              "</div>";
      }
      html += "</div>";
      //取出內容    
      for(var i = 2; i < msg.length ; i++){
        html += '<div class="table-tr">'; //
        for(var j = 0; j < msg[2].length ; j++){
            html += '<div class="table-td">' +msg[i][j] +  "</div>";
        }
        html += "</div>";
      }
      document.getElementById('fileList').innerHTML = html;
    }  
  }
function showToast(){
  for( var i=0;i<toastList.length;i++){
    toastList[i].show();}
    return false;
  }
</script>

0 comments:

張貼留言