โค้ดที่แชร์
E-Portfolio
bohi******@gmail.comPRO· แชร์เมื่อ 18 ต.ค. 69
ระบบแฟ้มสะสมผลงานและรางวัลสำหรับสถานศึกษา พร้อมหน้าสรุปสถิติและส่งออกข้อมูลเป็น Excel เหมาะสำหรับรวบรวมผลงานครู นักเรียน และผู้บริหาร หลังโคลนต้องเตรียม Google Sheet และโฟลเดอร์ Google Drive แล้วใส่รหัสอ้างอิงในโค้ด ตั้งชื่อผู้ใช้และรหัสผ่านผู้ดูแลใหม่ พร้อมปรับโลโก้และลิงก์ให้เป็นของตนเองก่อนเผยแพร่เป็นเว็บแอป
โค้ดที่แชร์ · E-Portfolio3 ไฟล์ · 72.5 KB · เวอร์ชัน 1 · 2 คำเตือน · โคลนแล้ว 0 ครั้ง
easygaside.tech/s/sc324gxqbqไฟล์Code.gs423 บรรทัดappsscript.json23 บรรทัดindex.html870 บรรทัด
สิทธิ์ที่ Google จะถามตอนเผยแพร่ไม่ได้ระบุใน appsscript.json — Apps Script จะขอตามที่โค้ดใช้SheetsDrive
ก่อนใช้ เช็กให้ดี: โค้ดนี้มีค่าที่เป็นของคนแชร์ ต้องเปลี่ยนเป็นของคุณหลังโคลน — ID ไฟล์ Google (Sheets/Drive/Docs) (Code.gs:2) · ID ไฟล์ Google (Sheets/Drive/Docs) (Code.gs:3)
โดย bohi******@gmail.comPRO
ไฟล์ทั้งหมด
อ่านก่อนโคลนได้ ใน EasyGAS IDE จะได้ไฟล์ชุดนี้เป็นโปรเจกต์ใหม่ แล้วค่อยเผยแพร่เข้าบัญชี Google ของคุณ
1// --- การตั้งค่าหลัก ---2const SHEET_ID = '1aCNz_GawlSucEA2dU2JTQdbiH369d0isxCRKNeoN3wY'; // <<< ใส่ Sheet ID ของคุณที่นี่3const ROOT_DRIVE_FOLDER_ID = '1DHetP91PYS80RtTTVXL-IyyaBzTDjoWf'; // <<< ใส่ Folder ID หลักของคุณที่นี่45// --- ตั้งค่าผู้ดูแลระบบ ---6const ADMIN_USER = 'admin';7const ADMIN_PASS = '34020077';89const ss = SpreadsheetApp.openById(SHEET_ID);10let dataSheet;1112/**13 * ฟังก์ชัน doGet หลักสำหรับแสดงผลหน้าเว็บ14 */15function doGet(e) {16 const template = HtmlService.createTemplateFromFile('index');17 return template.evaluate()18 .setTitle('E- Portfolio')19 .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);20}2122/**23 * ฟังก์ชันสำหรับสร้างและส่งออกไฟล์ Excel24 * @param {string} period ช่วงเวลาที่ต้องการส่งออก ('YYYY-MM', 'month', 'week', 'all')25 * @returns {object} อ็อบเจกต์ที่ประกอบด้วยข้อมูลไฟล์ Excel แบบ base64 และชื่อไฟล์26 */27function generateExcelExport(period) {28 try {29 initializeSheet_();30 const allValues = dataSheet.getDataRange().getValues();31 if (allValues.length <= 1) {32 throw new Error('ไม่มีข้อมูลสำหรับส่งออก');33 }34 const headers = allValues[0];35 const dataRows = allValues.slice(1);36 const timestampIndex = headers.indexOf('timestamp');3738 // กรองข้อมูลตาม period ที่เลือก39 const filteredRows = filterDataByPeriod_(dataRows, timestampIndex, period);4041 if (filteredRows.length === 0) {42 return { success: false, message: 'ไม่พบข้อมูลในช่วงเวลาที่เลือก' };43 }4445 // สร้าง Spreadsheet ชั่วคราว46 const exportSs = SpreadsheetApp.create(`Export_Awards_${new Date().getTime()}`);47 const exportSheet = exportSs.getSheets()[0];48 49 // เพิ่ม Headers และข้อมูลที่กรองแล้ว50 exportSheet.appendRow(headers);51 filteredRows.forEach(row => exportSheet.appendRow(row));52 53 exportSheet.getRange(1, 1, 1, headers.length).setFontWeight('bold');54 SpreadsheetApp.flush(); // บังคับให้บันทึกการเปลี่ยนแปลง5556 const exportFileId = exportSs.getId();57 const url = `https://docs.google.com/spreadsheets/d/${exportFileId}/export?format=xlsx`;58 const params = {59 method: 'get',60 headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },61 muteHttpExceptions: true62 };63 64 const blob = UrlFetchApp.fetch(url, params).getBlob();65 const base64Data = Utilities.base64Encode(blob.getBytes());66 const fileName = `สรุปผลรางวัล_${period}_${Utilities.formatDate(new Date(), 'GMT+7', 'yyyy-MM-dd')}.xlsx`;67 68 // ลบ Spreadsheet ชั่วคราว69 DriveApp.getFileById(exportFileId).setTrashed(true);7071 return { success: true, data: base64Data, filename: fileName };7273 } catch (error) {74 console.error('Error in generateExcelExport:', error.stack);75 return { success: false, message: `เกิดข้อผิดพลาดในการสร้าง Excel: ${error.message}` };76 }77}7879/**80 * Helper function สำหรับกรองข้อมูลตามช่วงเวลา81 */82function filterDataByPeriod_(dataRows, timestampIndex, period) {83 if (!period || period === 'all') {84 return dataRows;85 }86 87 const now = new Date();88 let startDate, endDate;8990 if (/^\d{4}-\d{2}$/.test(period)) { // รูปแบบ YYYY-MM91 const [year, month] = period.split('-').map(Number);92 startDate = new Date(year, month - 1, 1);93 endDate = new Date(year, month, 0, 23, 59, 59, 999);94 } else if (period === 'month') {95 startDate = new Date(now.getFullYear(), now.getMonth(), 1);96 endDate = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);97 } else if (period === 'week') {98 startDate = new Date(now);99 startDate.setDate(startDate.getDate() - startDate.getDay());100 startDate.setHours(0, 0, 0, 0);101 endDate = new Date(startDate);102 endDate.setDate(endDate.getDate() + 6);103 endDate.setHours(23, 59, 59, 999);104 }105106 if (startDate && endDate) {107 return dataRows.filter(row => {108 const rowDate = new Date(row[timestampIndex]);109 return rowDate >= startDate && rowDate <= endDate;110 });111 }112 return dataRows; // ถ้าไม่มีเงื่อนไข ให้คืนค่าทั้งหมด113}114115116function getSummaryData(period = 'month') {117 try {118 initializeSheet_();119 const range = dataSheet.getDataRange();120 const values = range.getValues();121 if (values.length <= 1) {122 return { success: true, total: 0, teacher: 0, student: 0, executive: 0, levels: { 'ระดับสถานศึกษา': 0, 'ระดับจังหวัด': 0, 'ระดับภาค': 0, 'ระดับชาติ': 0 }, periodTitle: 'ยังไม่มีข้อมูล' };123 }124 const headers = values[0];125 const dataRows = values.slice(1);126 const timestampIndex = headers.indexOf('timestamp');127 const typeIndex = headers.indexOf('type');128 const levelIndex = headers.indexOf('level');129130 if (timestampIndex === -1 || typeIndex === -1 || levelIndex === -1) {131 throw new Error('ไม่พบคอลัมน์ที่จำเป็นในชีต');132 }133 134 let periodTitle = '';135 const filteredRows = filterDataByPeriod_(dataRows, timestampIndex, period);136 const now = new Date();137138 if (/^\d{4}-\d{2}$/.test(period)) {139 const [year, month] = period.split('-').map(Number);140 const date = new Date(year, month - 1, 1);141 periodTitle = 'สรุปผล' + new Intl.DateTimeFormat('th-TH', { month: 'long', year: 'numeric' }).format(date);142 } else if (period === 'month') {143 periodTitle = 'สรุปผล' + new Intl.DateTimeFormat('th-TH', { month: 'long', year: 'numeric' }).format(now);144 } else if (period === 'week') {145 let startDate = new Date(now);146 startDate.setDate(startDate.getDate() - startDate.getDay());147 const startFormatted = new Intl.DateTimeFormat('th-TH', { day: 'numeric', month: 'long', year: 'numeric' }).format(startDate);148 periodTitle = `สรุปผลประจำสัปดาห์ (เริ่ม ${startFormatted})`;149 } else {150 periodTitle = 'สรุปผลรางวัลทั้งหมด';151 }152153 let teacherCount = 0;154 let studentCount = 0;155 let executiveCount = 0;156 const levelsCount = { 'ระดับสถานศึกษา': 0, 'ระดับจังหวัด': 0, 'ระดับภาค': 0, 'ระดับชาติ': 0 };157 filteredRows.forEach(row => {158 const type = row[typeIndex];159 if (type === 'ครู') teacherCount++;160 else if (type === 'นักเรียน') studentCount++;161 else if (type === 'ผู้บริหาร') executiveCount++;162 const level = row[levelIndex];163 if (levelsCount.hasOwnProperty(level)) levelsCount[level]++;164 });165 return { success: true, total: filteredRows.length, teacher: teacherCount, student: studentCount, executive: executiveCount, levels: levelsCount, periodTitle: periodTitle };166 } catch (error) {167 console.error('Error in getSummaryData:', error.stack);168 return { success: false, message: error.message };169 }170}171172// --- ฟังก์ชันจัดการโฟลเดอร์ ---173function getOrCreateFolder_(parentFolder, folderName) {174 const folders = parentFolder.getFoldersByName(folderName);175 if (folders.hasNext()) {176 return folders.next();177 } else {178 return parentFolder.createFolder(folderName);179 }180}181182function getTargetFolder_(level, type) {183 const rootFolder = DriveApp.getFolderById(ROOT_DRIVE_FOLDER_ID);184 185 if (!level || !type) {186 throw new Error('กรุณาระบุ "ระดับรางวัล" และ "ประเภท" ให้ครบถ้วน');187 }188189 const levelFolder = getOrCreateFolder_(rootFolder, level);190 const typeFolder = getOrCreateFolder_(levelFolder, type);191 192 return typeFolder;193}194195196// --- ฟังก์ชันอื่นๆ ---197function updateAward(awardData, certificateFileObject, photoFileObjects) {198 try {199 if (!awardData || !awardData.id) {200 throw new Error('ไม่พบ ID ของข้อมูลที่ต้องการแก้ไข');201 }202 initializeSheet_();203 const range = dataSheet.getDataRange();204 const values = range.getValues();205 const headers = values[0];206 const idColIndex = headers.indexOf('id');207 if (idColIndex === -1) {208 throw new Error('ไม่พบคอลัมน์ "id" ในชีต');209 }210 const rowIndexToUpdate = values.findIndex(row => row[idColIndex] === awardData.id);211 if (rowIndexToUpdate === -1) {212 return { success: false, message: 'ไม่พบข้อมูลที่ต้องการแก้ไข' };213 }214 215 const folder = getTargetFolder_(awardData.level, awardData.type);216217 let currentCertUrl = values[rowIndexToUpdate][headers.indexOf('certificateUrl')];218 let currentPhotoUrls = values[rowIndexToUpdate][headers.indexOf('photoUrls')];219220 if (certificateFileObject) {221 if (currentCertUrl) deleteFileFromDrive_(currentCertUrl);222 const certFileId = uploadFile_(certificateFileObject, folder, 'certificate');223 currentCertUrl = certFileId ? `https://drive.google.com/uc?id=${certFileId}` : '';224 }225 if (photoFileObjects && photoFileObjects.length > 0) {226 if (currentPhotoUrls) {227 currentPhotoUrls.toString().split(',').forEach(url => {228 if (url.trim()) deleteFileFromDrive_(url.trim());229 });230 }231 const newPhotoUrls = [];232 photoFileObjects.forEach((file, i) => {233 if (file && file.name) {234 const photoFileId = uploadFile_(file, folder, `photo_${i + 1}`);235 if (photoFileId) {236 newPhotoUrls.push(`https://drive.google.com/thumbnail?id=${photoFileId}&sz=w1200`);237 }238 }239 });240 currentPhotoUrls = newPhotoUrls.join(',');241 }242 const newRow = headers.map(header => {243 if (awardData.hasOwnProperty(header)) {244 return awardData[header];245 }246 switch(header) {247 case 'certificateUrl': return currentCertUrl;248 case 'photoUrls': return currentPhotoUrls;249 default: return values[rowIndexToUpdate][headers.indexOf(header)];250 }251 });252 dataSheet.getRange(rowIndexToUpdate + 1, 1, 1, newRow.length).setValues([newRow]);253 return { success: true, message: 'แก้ไขข้อมูลสำเร็จแล้ว' };254 } catch (error) {255 console.error('Error in updateAward:', error.stack);256 return { success: false, message: `เกิดข้อผิดพลาดในการแก้ไข: ${error.message}` };257 }258}259function checkAdminLogin(username, password) {260 try {261 if (username === ADMIN_USER && password === ADMIN_PASS) {262 return { success: true, message: 'เข้าสู่ระบบสำเร็จ' };263 }264 return { success: false, message: 'ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง' };265 } catch (error) {266 console.error('Error in checkAdminLogin:', error);267 return { success: false, message: 'เกิดข้อผิดพลาดในการเข้าสู่ระบบ' };268 }269}270function initializeSheet_() {271 try {272 dataSheet = ss.getSheetByName('data');273 if (!dataSheet) {274 dataSheet = ss.insertSheet('data');275 const headers = [276 'timestamp', 'id', 'academicYear', 'awardDate', 'ownerName', 'type', 277 'level', 'awardTitle', 'organization', 'description', 278 'certificateUrl', 'photoUrls', 'status', 'createdBy'279 ];280 dataSheet.appendRow(headers);281 const headerRange = dataSheet.getRange(1, 1, 1, headers.length);282 headerRange.setBackground('#4c1d95').setFontColor('white').setFontWeight('bold');283 }284 return true;285 } catch (error) {286 console.error('Error in initializeSheet_:', error);287 throw new Error(`ไม่สามารถเตรียมชีตข้อมูลได้: ${error.message}`);288 }289}290function saveData(formData, certificateFileObject, photoFileObjects) {291 try {292 initializeSheet_();293 if (!formData || !formData.awardTitle || !formData.ownerName || !formData.awardDate) {294 throw new Error('กรุณากรอกข้อมูลที่จำเป็นให้ครบถ้วน');295 }296 297 const folder = getTargetFolder_(formData.level, formData.type);298 299 let certificateUrl = '';300 if (certificateFileObject) {301 const certFileId = uploadFile_(certificateFileObject, folder, 'certificate');302 certificateUrl = certFileId ? `https://drive.google.com/uc?id=${certFileId}` : '';303 }304 let photoUrls = [];305 if (photoFileObjects && Array.isArray(photoFileObjects) && photoFileObjects.length > 0) {306 photoFileObjects.forEach((file, i) => {307 if (file && file.name) {308 const photoFileId = uploadFile_(file, folder, `photo_${i + 1}`);309 if (photoFileId) {310 photoUrls.push(`https://drive.google.com/thumbnail?id=${photoFileId}&sz=w1200`);311 }312 }313 });314 }315 const timestamp = new Date();316 const uniqueId = Utilities.getUuid();317 let userEmail = 'anonymous';318 try {319 userEmail = Session.getActiveUser().getEmail() || 'anonymous';320 } catch (e) {321 console.log('Cannot get user email, using anonymous');322 }323 const newRow = [324 timestamp.toISOString(), uniqueId, formData.academicYear, formData.awardDate, formData.ownerName, formData.type, 325 formData.level, formData.awardTitle, formData.organization, formData.description, 326 certificateUrl || '', photoUrls.join(','), 'แสดงผล', userEmail327 ];328 dataSheet.appendRow(newRow);329 return { success: true, message: 'บันทึกข้อมูลสำเร็จแล้ว', id: uniqueId };330 } catch (error) {331 console.error('Error in saveData:', error.stack);332 return { success: false, message: `เกิดข้อผิดพลาด: ${error.message}` };333 }334}335function uploadFile_(fileObject, folder, prefix = '') {336 try {337 if (!fileObject || !fileObject.data || !fileObject.name) return null;338 const base64Data = fileObject.data.split(',')[1];339 const decodedData = Utilities.base64Decode(base64Data);340 const blob = Utilities.newBlob(decodedData, fileObject.type, fileObject.name);341 const timestamp = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyyMMdd_HHmmss');342 const newFileName = prefix ? `${prefix}_${timestamp}_${fileObject.name}` : `${timestamp}_${fileObject.name}`;343 const file = folder.createFile(blob);344 file.setName(newFileName);345 file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);346 return file.getId();347 } catch (error) {348 console.error('Error in uploadFile_:', error);349 throw new Error(`ไม่สามารถอัปโหลดไฟล์ได้: ${error.message}`);350 }351}352function getAwards() {353 try {354 initializeSheet_();355 const range = dataSheet.getDataRange();356 const values = range.getValues();357 if (values.length <= 1) return JSON.stringify([]);358 const headers = values[0];359 const dataRows = values.slice(1);360 const data = dataRows.map(row => {361 const obj = {};362 headers.forEach((header, i) => {363 obj[header] = row[i] || '';364 });365 return obj;366 });367 data.sort((a, b) => new Date(b.timestamp || 0) - new Date(a.timestamp || 0));368 return JSON.stringify(data);369 } catch (error) {370 console.error('Error in getAwards:', error);371 return JSON.stringify({ error: true, message: `เกิดข้อผิดพลาด: ${error.message}` });372 }373}374function deleteAward(awardId) {375 try {376 if (!awardId) throw new Error('ไม่พบ ID ของข้อมูลที่ต้องการลบ');377 initializeSheet_();378 const range = dataSheet.getDataRange();379 const values = range.getValues();380 if (values.length <= 1) return { success: false, message: 'ไม่มีข้อมูลในระบบ' };381 const headers = values[0];382 const idColumnIndex = headers.indexOf('id');383 const certUrlIndex = headers.indexOf('certificateUrl');384 const photoUrlsIndex = headers.indexOf('photoUrls');385 if (idColumnIndex === -1) throw new Error('ไม่พบคอลัมน์ ID ในชีต');386 for (let i = values.length - 1; i >= 1; i--) {387 if (values[i][idColumnIndex] === awardId) {388 const rowToDelete = values[i];389 try {390 if (certUrlIndex !== -1 && rowToDelete[certUrlIndex]) {391 deleteFileFromDrive_(rowToDelete[certUrlIndex]);392 }393 if (photoUrlsIndex !== -1 && rowToDelete[photoUrlsIndex]) {394 const photoUrls = rowToDelete[photoUrlsIndex].toString().split(',');395 photoUrls.forEach(url => {396 if (url.trim()) deleteFileFromDrive_(url.trim());397 });398 }399 } catch (fileError) {400 console.error('Error deleting files:', fileError);401 }402 dataSheet.deleteRow(i + 1);403 return { success: true, message: 'ลบข้อมูลสำเร็จ' };404 }405 }406 return { success: false, message: 'ไม่พบข้อมูลที่ต้องการลบ' };407 } catch (error) {408 console.error('Error in deleteAward:', error);409 return { success: false, message: `เกิดข้อผิดพลาด: ${error.message}` };410 }411}412function deleteFileFromDrive_(url) {413 try {414 if (!url || typeof url !== 'string') return;415 const fileIdMatch = url.match(/id=([^&]+)/);416 if (fileIdMatch && fileIdMatch[1]) {417 const file = DriveApp.getFileById(fileIdMatch[1]);418 file.setTrashed(true);419 }420 } catch (error) {421 console.error('ไม่สามารถลบไฟล์ได้:', url, error.message);422 }423}รีวิว
รีวิวได้หลังโคลนไปใช้ · ทำได้ในโปรแกรม EasyGAS IDE
ยังไม่มีรีวิว
ถามตอบ
ถามคนแชร์ เล่าว่าเอาไปใช้แล้วเป็นยังไง หรือแนบเวอร์ชันต่อยอดของคุณ
ยังไม่มีใครถาม เป็นคนแรกก็ได้