<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>نظام حضور وانصراف NFC</title>
<style>
*{
box-sizing:border-box;
font-family:Tahoma,Arial,sans-serif;
}
جسم{
margin:0;
الخلفية: #f3f6f9؛
اللون: #222؛
}
رأس الصفحة {
الخلفية: #075985؛
اللون: أبيض؛
text-align:center;
الحشو: 25 بكسل 15 بكسل؛
}
رأس الصفحة h1{
margin:0 0 8px;
}
.حاوية{
الحد الأقصى للعرض: 900 بكسل؛
margin:auto;
الحشو: 15 بكسل؛
}
.بطاقة{
الخلفية: بيضاء؛
margin:15px 0;
الحشو: 20 بكسل؛
border-radius:15px;
box-shadow:0 3px 15px rgba(0,0,0,.08);
}
.موظف{
text-align:center;
}
.اسم-الموظف{
حجم الخط: 27 بكسل؛
وزن الخط: عريض؛
اللون: #075985؛
margin:10px;
}
.ساعة{
حجم الخط: 35 بكسل؛
وزن الخط: عريض؛
margin:15px;
}
زر{
العرض: 100%؛
الحشو: 17 بكسل؛
margin:6px 0;
border:0;
border-radius:10px;
اللون: أبيض؛
الخلفية: #075985؛
حجم الخط: 18 بكسل؛
}
زر: تحويم
الشفافية: 0.9؛
}
.nfc{
الخلفية: #0284c7؛
حجم الخط: 21 بكسل؛
}
.أخضر{
الخلفية: #16a34a؛
}
.أحمر{
الخلفية: #dc2626؛
}
.رمادي{
الخلفية: #475569؛
}
.رسالة{
الحشو: 15 بكسل؛
border-radius:10px;
margin-top:12px;
text-align:center;
}
.نجاح{
الخلفية: #dcfce7؛
اللون: #166534؛
}
.خطأ{
الخلفية: #fee2e2;
اللون: #991b1b؛
}
.معلومات{
الخلفية: #e0f2fe;
اللون: #075985؛
}
.حالة{
display:grid;
grid-template-columns:1fr 1fr;
الفجوة: 12 بكسل؛
}
.status-box{
text-align:center;
الخلفية: #f8fafc;
الحشو: 20 بكسل؛
border-radius:12px;
}
.status-box span{
display:block;
اللون: #64748b؛
}
.status-box strong{
display:block;
حجم الخط: 27 بكسل؛
اللون: #075985؛
margin-top:8px;
}
.stats{
display:grid;
grid-template-columns:repeat(3,1fr);
الفجوة: 10 بكسل؛
}
.stat{
الخلفية: #f0f9ff;
text-align:center;
الحشو: 20 بكسل؛
border-radius:12px;
}
.stat strong{
display:block;
حجم الخط: 30 بكسل؛
اللون: #075985؛
}
مدخل{
العرض: 100%؛
الحشو: 13 بكسل؛
border:1px solid #ccc;
border-radius:8px;
حجم الخط: 16 بكسل؛
margin:5px 0 10px;
}
.طاولة{
overflow-x:auto;
}
طاولة{
العرض: 100%؛
الحد الأدنى للعرض: 600 بكسل؛
border-collapse:collapse;
margin-top:15px;
}
ذ{
الخلفية: #075985؛
اللون: أبيض؛
الحشو: 12 بكسل؛
}
td{
text-align:center;
الحشو: 12 بكسل؛
border-bottom:1px solid #ddd;
}
.uid{
الاتجاه: من اليسار إلى اليمين؛
font-family:monospace;
word-break:break-all;
}
.صغير{
حجم الخط: 13 بكسل؛
ارتفاع السطر: 1.8؛
اللون: #64748b؛
}
@media(max-width:600px){
.حالة{
grid-template-columns:1fr;
}
.stats{
grid-template-columns:1fr;
}
.ساعة{
حجم الخط: 28 بكسل؛
}
}
</style>
</head>
<body>
<header>
<h1> ونظام الحضور النصراف</h1>
<div>تسجيل الموظف بواسطة بطاقة NFC</div>
</header>
<div class="container">
<!-- بيانات الموظف -->
<div class="card employee">
<h2>بيانات الموظف</h2>
<div class="employee-name" id="employeeName">
فوت شو
</div>
<div id="date"></div>
<div class="clock" id="clock">
00:00:00
</div>
</div>
<!-- NFC -->
<div class="card">
📱 اتصال NFC </h2>
<button class="nfc" onclick="startNFC()">
📡 أجهزة NFC
</button>
<div id="nfcMessage"></div>
</div>
<!-- مناسب اليوم -->
<div class="card">
<h2>الاستراتيجية اليوم</h2>
<div class="status">
<div class="status-box">
<span>وقت الحضور</span>
<strong id="inTime">
--
</strong>
</div>
<div class="status-box">
<span>وقت الانصراف</span>
<strong id="outTime">
--
</strong>
</div>
</div>
</div>
<!--الرياضات -->
<div class="card">
<h2> إحصائيات الشهر</h2>
<div class="stats">
<div class="stat">
<strong id="daysPresent">
0
</strong>
ميزت
</div>
<div class="stat">
<strong id="daysComplete">
0
</strong>
خطط مرفوعة
</div>
<div class="stat">
<strong id="hours">
0
</strong>
ركنوك
</div>
</div>
</div>
<!-- خطأ -->
<div class="card">
<h2>📊 تغليف</h2>
<input
اكتب "شهر"
id="month"
onchange="showReport()"
>
<button onclick="exportCSV()">
📥 تصدير التقرير
</button>
<div class="table">
<جدول>
<thead>
<tr>
<th>الجهاز</th>
<th>المينور</th>
<th>الانصراف</th>
<th>ساعات العمل</th>
</tr>
</thead>
<tbody id="report"></tbody>
</table>
</div>
</div>
<!-- اختيار -->
<div class="card">
<h2>⚙️ إعداد الموظف</h2>
<input
id="name"
placeholder="اسم الموظف"
>
<input
id="uid"
class="uid"
العنصر النائب = "بطاقة UID NFC"
>
<button onclick="saveSettings()">
حفظ بيانات الموظف
</button>
<p class="small">
لربط البطاقة بالموظف، شغّل قارئ NFC ثم مرر البطاقة.
سيظهر بطاقة UID هنا مباشرة.
بعد ذلك اضغط على حفظ بيانات الموظف.
</p>
</div>
<!--حذف البيانات -->
<div class="card">
<button class="red" onclick="deleteAll()">
حذف جميع تسجيلات
</button>
</div>
</div>
<script>
/* ==============================
جحيم
============================== */
let settings =
JSON.parse(
localStorage.getItem("nfcEmployee")
) || {
الاسم:"موظف المدرسة"،
uid:""
};
/* ==============================
أليس
============================== */
let records =
JSON.parse(
localStorage.getItem("nfcAttendance")
) || {};
/* ==============================
سبي
============================== */
دالة حفظ(){
localStorage.setItem(
"موظف إن إف سي"
JSON.stringify(settings)
);
localStorage.setItem(
"حضور تقنية الاتصال قريب المدى"،
JSON.stringify(records)
);
}
/* ==============================
الشعب
============================== */
دالة dateKey(){
let d=new Date();
return d.getFullYear()+"-"+
String(d.getMonth()+1).padStart(2,"0")+"-"+
String(d.getDate()).padStart(2,0");
}
/* ==============================
خطر
============================== */
دالة timeString(date){
إرجاع التاريخ.toLocaleTimeString(
"ar-SA"،
{
الساعة: "رقمين"،
دقيقة: "رقمين"،
ثانياً: "رقمين"
});
}
/* ==============================
زعيم
============================== */
دالة الساعة(){
let now=new Date();
document.getElementById("clock")
.textContent=
timeString(now);
document.getElementById("date")
.textContent=
now.toLocaleDateString(
"ar-SA"،
{
أيام الأسبوع: "طويلة"
السنة: "رقمية"،
الشهر: "طويل"
اليوم: "رقمي"
});
}
setInterval(clock,1000);
ساعة()؛
/* ==============================
تحميل
============================== */
دالة تحميل الإعدادات(){
document.getElementById("employeeName")
.textContent=settings.name;
document.getElementById("name")
.value=settings.name;
document.getElementById("uid")
.value=settings.uid;
}
/* ==============================
حفظ الكفال
============================== */
دالة حفظ الإعدادات(){
let name=
document.getElementById("name")
.value.trim();
let uid=
document.getElementById("uid")
.value.trim();
إذا لم يكن هناك اسم {
تنبيه("أدخل اسم الموظف");
يعود؛
}
settings.name=name;
settings.uid=
normalizeUID(uid);
يحفظ()؛
document.getElementById("employeeName")
.textContent=name;
عرض الرسالة (
"تم حفظ بيانات الموظف والبطاقة"،
"نجاح"
);
}
/* ==============================
حساب UID
============================== */
دالة normalizeUID(uid){
أعد String(uid)
.replace(/:/g,"")
.replace(/-/g,"")
.replace(/\s/g,"")
.toUpperCase();
}
/* ==============================
رات
============================== */
دالة عرض الرسالة (النص، النوع) {
document.getElementById("nfcMessage")
.innerHTML=
`<div class="message ${type}">
${text}
</div>`;
}
/* ==============================
ولاية كارولاينا الشمالية
============================== */
دالة تسجيل البطاقة (uid){
uid=normalizeUID(uid);
/* إذا لم تكن هناك بطاقة محفوظة */
إذا لم يكن هناك معرف مستخدم (!settings.uid)
document.getElementById("uid")
.value=uid;
عرض الرسالة (
"تمت إضافة بطاقة فعالة. تم وضع UID في خانة البطاقة. اضغط على حفظ بيانات الموظف.",
"نجاح"
);
يعود؛
}
/* التحقق من البطاقة */
إذا كان (uid !== normalizeUID(settings.uid)){
عرض الرسالة (
"❌ هذه ليست بطاقة الموظف البديل.",
"خطأ"
);
يعود؛
}
/* تصميم اليوم */
let today=dateKey();
/* سجل التاريخ */
إذا لم تكن السجلات موجودة في [اليوم] {
السجلات[اليوم]={
in:null,
الناتج: لا شيء
};
}
let record=records[today];
let now=new Date();
/* ==============================
حضر
============================== */
إذا لم يكن السجل موجودًا،
record.in=now.toISOString();
يحفظ()؛
عرض الرسالة (
""تم تسجيل الحضور للساعة"+
timeString(now),
"نجاح"
);
}
/* ==============================
الصارف
============================== */
وإلا إذا لم يكن السجل موجودًا (!record.out){
record.out=now.toISOString();
يحفظ()؛
عرض الرسالة (
""تم تسجيل الانصراف الساعة"+
timeString(now),
"نجاح"
);
}
/* ==============================
ميو
============================== */
آخر{
عرض الرسالة (
"ℹ️ تم تسجيل الحضور والانصراف لهذا اليوم ليالي.",
"معلومات"
);
}
تحديث اليوم();
showReport();
}
/* ==============================
تقنية NFC
============================== */
async function startNFC(){
/* دراسة القص */
إذا لم يكن "NDEFReader" موجودًا في النافذة،
عرض الرسالة (
"❌ Web NFC غير مدعوم. استخدم Samsung Android + Google Chrome، وتأكد من تشغيل NFC.",
"خطأ"
);
يعود؛
}
يحاول{
const ndef=
new NDEFReader();
عرض الرسالة (
"جاري تشغيل قارئ NFC...",
"معلومات"
);
انتظر ndef.scan();
عرض الرسالة (
"🟢 قارئ NFC جاهز. ضع بطاقة خلف الهاتف.",
"نجاح"
);
/* عالم */
ndef.onreading=
الحدث => {
let uid=
event.serialNumber;
/* إذا لم يتوفر إصدار UID */
إذا لم يكن هناك معرف مستخدم (uid) {
عرض الرسالة (
"❌ لم يتم الحصول على UID من البطاقة.",
"خطأ"
);
يعود؛
}
registerCard(uid);
};
/* معرفة الألف */
ndef.onreadingerror=()=>{
عرض الرسالة (
"❌ عذرت قراءة البطاقة. ضعها ملاصقة للهاتف مرة أخرى.",
"خطأ"
);
};
}catch(error){
console.error(error);
عرض الرسالة (
"❌ خطأ في التشغيل في NFC: "+
رسالة الخطأ،
"خطأ"
);
}
}
/* ==============================
تحديث اليوم
============================== */
دالة تحديث اليوم(){
let today=dateKey();
let record=records[today];
document.getElementById("inTime")
.textContent="--";
document.getElementById("outTime")
.textContent="--";
إذا لم يكن هناك سجل، فقم بالخروج.
إذا كان (record.in) {
document.getElementById("inTime")
.textContent=
timeString(
تاريخ جديد (record.in)
);
}
إذا كان (record.out) {
document.getElementById("outTime")
.textContent=
timeString(
تاريخ جديد (record.out)
);
}
}
/* ==============================
النتيجة
============================== */
دالة عرض التقرير(){
let month=
document.getElementById("month").value;
let table=
document.getElementById("report");
table.innerHTML="";
let present=0;
let complete=0;
لنفترض أن إجمالي الساعات يساوي صفرًا؛
Object.keys(records)
.نوع()
.يعكس()
.forEach(date=>{
إذا لم يبدأ التاريخ بالشهر
يعود؛
let r=records[date];
موجود++;
let hours=0;
إذا كان (r.in && r.out){
let start=
تاريخ جديد(r.in)؛
let end=
new Date(r.out);
ساعات=
(نهاية-بداية)/1000/60/60؛
totalHours+=hours;
مكتمل++;
}
let tr=
document.createElement("tr");
tr.innerHTML=`
<td>${date}</td>
<td>
${r.in ? timeString(new Date(r.in)):"--"}
</td>
<td>
${r.out ? timeString(new Date(r.out)):"--"}
</td>
<td>
${ساعات؟ hours.toFixed(2)+"ساعة":"--"}
</td>
`;
table.appendChild(tr);
});
document.getElementById("daysPresent")
.textContent=present;
document.getElementById("daysComplete")
.textContent=complete;
document.getElementById("hours")
.textContent=
totalHours.toFixed(1);
}
/* ==============================
ستيت لا
============================== */
دالة تصدير ملف CSV(){
let month=
document.getElementById("month").value;
let csv=
"\uFEFFالتاريخ,الحضور,الانصراف,ساعات العمل\n";
Object.keys(records)
.نوع()
.forEach(date=>{
إذا لم يبدأ التاريخ بالشهر
يعود؛
let r=records[date];
let hours="";
إذا كان (r.in && r.out){
ساعات=
(
(تاريخ جديد(r.out)-تاريخ جديد(r.in))
/1000/60/60
).toFixed(2);
}
csv+=
تاريخ+","+
(r.in ? timeString(new Date(r.in)):"")+","+
(r.out ? timeString(new Date(r.out)):"")+","+
ساعات+"\n";
});
let blob=
كائن ثنائي كبير جديد (
[csv],
{
type:"text/csv;charset=utf-8;"
}
);
let url=
URL.createObjectURL(blob);
ليكن a=
document.createElement("a");
a.href=url;
a.download=
"تقرير_الحضور_"+month+".csv";
a.click();
URL.revokeObjectURL(url);
}
/* ==============================
غليتي
============================== */
دالة حذف الكل(){
إذا لم يتم التأكيد
"هل أنت متأكد من حذف جميع السجلات وحضورها وانصراف؟"
)){
يعود؛
}
السجلات = {};
يحفظ()؛
تحديث اليوم();
showReport();
تنبيه("تم حذف جميع أرقام تسجيل الدخول");
}
/* ==============================
تاربت
============================== */
دالة التهيئة(){
loadSettings();
let now=new Date();
let month=
now.getFullYear()+"-"+
String(now.getMonth()+1).padStart(2,"0");
document.getElementById("month")
.value=month;
تحديث اليوم();
showReport();
}
تهيئة();
</script>
</body>
</html>