31909-2002 การออกแบบส่วนต่อประสานสำหรับงาน IoT
ตรวจสอบการส่งงาน LAB ออกแบบส่วนต่อประสานสำหรับงาน IOT
iOS Glassmorphism Interface วิเคราะห์ข้อมูลแบบส่วนบุคคล
เลือกชิ้นงาน LAB
-- กำลังโหลดข้อมูล LAB --
เลือกระดับชั้น / ห้องเรียน
-- กำลังโหลดข้อมูลห้องเรียน --
Export คะแนนเป็น Excel (.csv)
กรุณาเลือกงาน LAB และห้องเรียนเพื่อเปิดใช้งาน Dashboard และตารางข้อมูล
กำลังวิเคราะห์โครงสร้างชิ้นงานค้างส่ง...
เปอร์เซ็นต์การส่งงาน
0%
0/0 คน
ส่งเร็ว (30% แรก)
ปานกลาง (20% ถัดมา)
ส่งช้า (กลุ่มที่เหลือ)
ยังไม่ได้ส่ง
| รหัส ชื่อ สกุล (คลิกดู Dashboard) | สถานะ | ไฟล์งาน | ลิงค์ไฟล์ | รายละเอียด | เวลาการส่งงาน | ลำดับ | คะแนน |
|---|
ไม่มีรายชื่อ
';
if(!counts.slow) slowEl.innerHTML = 'ไม่มีรายชื่อ
';
if(!counts.none) noneEl.innerHTML = 'ส่งครบเรียบร้อย 🎉
';
}
function renderTableRows(list) {
tableBody.innerHTML = '';
list.forEach(s => {
const tr = document.createElement('tr');
// ชื่อ (แบบคลิกได้เป็น iOS Link Dashboard Popup)
const tdName = document.createElement('td');
const cleanStrName = encodeURIComponent(s.name);
tdName.innerHTML = ` ${s.name}`;
tr.appendChild(tdName);
// สถานะ
const tdStatus = document.createElement('td');
tdStatus.style.textAlign = 'center';
tdStatus.innerHTML = s.hasSubmitted ? '' : '';
tr.appendChild(tdStatus);
// ไฟล์อัพโหลด
const tdFile = document.createElement('td');
tdFile.style.textAlign = 'center';
tdFile.innerHTML = (s.hasSubmitted && s.fileUpload.startsWith('http')) ? `` : ``;
tr.appendChild(tdFile);
// ลิงก์
const tdLink = document.createElement('td');
tdLink.style.textAlign = 'center';
tdLink.innerHTML = (s.hasSubmitted && s.fileLink.startsWith('http')) ? `` : ``;
tr.appendChild(tdLink);
// บันทึก
const tdDetails = document.createElement('td');
tdDetails.style.textAlign = 'center';
if (s.hasSubmitted && s.details) {
const encodedTxt = encodeURIComponent(s.details);
tdDetails.innerHTML = ` ดู`;
} else {
tdDetails.innerHTML = ``;
}
tr.appendChild(tdDetails);
// เวลา
const tdTime = document.createElement('td');
tdTime.textContent = s.hasSubmitted ? s.timestamp : '-';
tr.appendChild(tdTime);
// ลำดับส่ง
const tdRank = document.createElement('td');
tdRank.style.textAlign = 'center';
tdRank.style.fontWeight = '600';
tdRank.textContent = s.classRank;
if(s.hasSubmitted) {
if(s.group === 'fast') tdRank.style.color = 'var(--ios-green)';
if(s.group === 'medium') tdRank.style.color = 'var(--ios-orange)';
if(s.group === 'slow') tdRank.style.color = 'var(--ios-red)';
}
tr.appendChild(tdRank);
// คะแนน
const tdScore = document.createElement('td');
tdScore.style.textAlign = 'center';
let cls = 'p0';
if(s.score===10) cls='p10'; else if(s.score===8) cls='p8'; else if(s.score===6) cls='p6';
tdScore.innerHTML = `${s.score} คะแนน`;
tr.appendChild(tdScore);
tableBody.appendChild(tr);
});
}
// ฟังชันเด้งดึ๋ง Dashboard Popup ของแต่ละรายชื่อบุคคล
function openStudentDashboard(encodedName) {
const name = decodeURIComponent(encodedName);
const cleanName = name.replace(/\s+/g, '');
// 1. ตรวจสอบชิ้นงานแล็บทั้งหมดในระบบที่มีการส่งงานเกิดขึ้น
const totalLabsAvailable = allUniqueLabs.length;
// 2. คำนวณชิ้นงานที่ส่งแล้ว ค้างส่ง และงานส่งซ้ำ
let submittedLabs = new Set();
let duplicateMap = {};
rawSubmissions.forEach(sub => {
if (sub.name.replace(/\s+/g, '') === cleanName) {
submittedLabs.add(sub.lab);
duplicateMap[sub.lab] = (duplicateMap[sub.lab] || 0) + 1;
}
});
const sendCount = submittedLabs.size;
const pendingCount = Math.max(0, totalLabsAvailable - sendCount);
// คัดกรองงานส่งซ้ำ (ส่งมากกว่า 1 ครั้งขึ้นไป)
let duplicateItems = [];
for (let labItem in duplicateMap) {
if (duplicateMap[labItem] > 1) {
duplicateItems.push({ labName: labItem, count: duplicateMap[labItem] });
}
}
// ประกอบ HTML โครงสร้าง iOS Component
document.getElementById('modal-title').textContent = "Personal Summary Dashboard";
let html = `
${name}
รายงานผลรวมความคืบหน้าทุกวิชา LAB
${sendCount}
ส่งแล้ว (ชิ้น)
${pendingCount}
ค้างส่ง (ชิ้น)
${duplicateItems.length}
ส่งซ้ำ (รายการ)
รายการชิ้นงานที่ส่งซ้ำซ้อน
- `; if (duplicateItems.length === 0) { html += `
- ไม่พบประวัติการส่งงานซ้ำ -
`;
} else {
duplicateItems.forEach(item => {
html += ` ${item.labName} (ส่งซ้ำเข้ามาทั้งหมด ${item.count} ครั้ง)`;
});
}
html += `
* ข้อมูลถูกดึงจากการจับคู่กับฐานข้อมูลส่งงานหลักล่าสุด
`; document.getElementById('modal-body-container').innerHTML = html; iosModal.style.display = 'flex'; } // ฟังก์ชันเปิดดูบันทึกรายละเอียดธรรมดา function openNotesPopup(encodedName, encodedTxt) { const name = decodeURIComponent(encodedName); const notes = decodeURIComponent(encodedTxt); document.getElementById('modal-title').textContent = "รายละเอียดข้อความแนบ"; document.getElementById('modal-body-container').innerHTML = ` บันทึกจาก: ${name}
${notes}
`;
iosModal.style.display = 'flex';
}
function closeModal() { iosModal.style.display = 'none'; }
window.onclick = e => { if (e.target == iosModal) closeModal(); }
// --- EXPORT TO EXCEL (.CSV) FUNCTION ---
// --- EXPORT TO EXCEL (.CSV) FUNCTION (เวอร์ชันสรุปคะแนนทุก LAB รวมเป็นตารางเดียว) ---
btnExport.addEventListener('click', () => {
if (rawStudents.length === 0) return;
const classTitle = selectClass.value || "ทุกห้องเรียน";
// 1. คัดกรองรายชื่อนักเรียนตามห้องที่เลือก (ถ้าไม่ได้เลือก ให้ดึงทุกห้อง)
const targetStudents = selectClass.value
? rawStudents.filter(s => s.className === selectClass.value)
: rawStudents;
// 2. หาชื่อ LAB ทั้งหมดที่มีอยู่ในระบบเพื่อนำมาทำเป็นหัวคอลัมน์ (เหมือนในรูปตัวอย่าง)
// allUniqueLabs ถูกเก็บค่าไว้ตอนโหลดหน้าเว็บแล้ว
if (allUniqueLabs.length === 0) {
alert("ไม่พบข้อมูลชิ้นงาน LAB ในระบบ");
return;
}
// 3. เริ่มต้นสร้างส่วนหัวตาราง (Header)
let csvContent = "รหัส ชื่อ สกุล";
allUniqueLabs.forEach(lab => {
csvContent += `,${lab}`; // เพิ่มคอลัมน์ LAB 1.1, LAB 1.2, ... ไปเรื่อยๆ
});
csvContent += ",รวม\n"; // เพิ่มคอลัมน์สรุปคะแนนรวมท้ายตาราง
// 4. ขั้นตอนการคำนวณคะแนนสัมพัทธ์ (Relative Scoring 10, 8, 6) ของทุกๆ LAB
// สร้างแผนผังเก็บคะแนนแยกตามชิ้นงาน LAB เพื่อความรวดเร็วในการ lookup
let labScoresLookup = {};
allUniqueLabs.forEach(targetLab => {
const currentLabSubs = rawSubmissions.filter(sub => sub.lab === targetLab);
let sortedSubmissions = [...currentLabSubs].sort((a,b) => new Date(a.timestamp) - new Date(b.timestamp));
// คัดกรองเฉพาะคนที่ส่งและจัดลำดับเพื่อคำนวณคะแนนกลุ่ม
let submittedGroup = targetStudents.map(student => {
const cleanName = student.name.replace(/\s+/g, '');
const matchRecords = currentLabSubs.filter(sub => sub.name.replace(/\s+/g, '') === cleanName);
let globalRank = sortedSubmissions.findIndex(sub => sub.name.replace(/\s+/g, '') === cleanName) + 1;
return {
name: student.name,
hasSubmitted: matchRecords.length > 0,
globalRank: globalRank > 0 ? globalRank : null
};
}).filter(s => s.hasSubmitted);
// เรียงตามเวลาส่งจริงส่วนกลาง
submittedGroup.sort((a, b) => a.globalRank - b.globalRank);
const totalSub = submittedGroup.length;
const limitFast = Math.ceil(totalSub * 0.3);
const limitMedium = Math.ceil(totalSub * 0.5);
submittedGroup.forEach((st, idx) => {
const runningNo = idx + 1;
if (runningNo <= limitFast) st.score = 10;
else if (runningNo <= limitMedium) st.score = 8;
else st.score = 6;
});
// บันทึกลงตาราง Lookup Table [ชื่อนักเรียน][ชื่อLAB] = คะแนน
targetStudents.forEach(student => {
if (!labScoresLookup[student.name]) {
labScoresLookup[student.name] = {};
}
const match = submittedGroup.find(m => m.name === student.name);
labScoresLookup[student.name][targetLab] = match ? match.score : 0; // ถ้าไม่ส่งงานให้เป็น 0 คะแนน
});
});
// 5. นำข้อมูลจาก Lookup มาเขียนลงบรรทัดของนักเรียนแต่ละคน (Row Generation)
targetStudents.forEach(student => {
csvContent += `"${student.name}"`; // ใส่ฟันหนูครอบเพื่อป้องกันเครื่องหมายจุลภาคพังตาราง
let totalStudentScore = 0;
// วนลูปหยอดคะแนนเรียงตามคอลัมน์ LAB
allUniqueLabs.forEach(lab => {
const score = labScoresLookup[student.name][lab] || 0;
csvContent += `,${score}`;
totalStudentScore += score; // สะสมคะแนนรวม
});
csvContent += `,${totalStudentScore}\n`; // หยอดคะแนนรวมปิดท้ายบรรทัด
});
// 6. ใช้ Uint8Array บังคับให้ Microsoft Excel อ่านภาษาไทยออกโดยตรง
const bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
const blob = new Blob([bom, csvContent], { type: 'text/csv;charset=utf-8;' });
// 7. สั่งดาวน์โหลดไฟล์สรุปผลรวม
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", `สรุปคะแนนทุกLAB_ห้อง_${classTitle}.csv`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});