chao 1 month ago
parent
commit
26973cf668
5 changed files with 289 additions and 66 deletions
  1. 1 1
      model/mysql.js
  2. 105 1
      package-lock.json
  3. 2 1
      package.json
  4. 130 49
      public/index.html
  5. 51 14
      routes/router.js

+ 1 - 1
model/mysql.js

@@ -15,7 +15,7 @@ us.mysqlconnection = function (host, database) {
             user: "root", //用户名
             password: "cocorobo", //密码
             database: database, //数据库名称
-            port: 3306 //端口 11111
+            port: 20007 //端口 20007
         }); //连接超时和错误从连
     }
     //connectionLimit: 1000, 

+ 105 - 1
package-lock.json

@@ -15,7 +15,8 @@
         "fs": "^0.0.1-security",
         "mime-types": "^3.0.1",
         "mongoose": "^8.18.1",
-        "mysql": "^2.18.1"
+        "mysql": "^2.18.1",
+        "xlsx": "^0.18.5"
       }
     },
     "node_modules/@mongodb-js/saslprep": {
@@ -55,6 +56,15 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/adler-32": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
+      "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/bcryptjs": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz",
@@ -140,6 +150,28 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/cfb": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
+      "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "adler-32": "~1.3.0",
+        "crc-32": "~1.2.0"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/codepage": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
+      "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/content-disposition": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
@@ -198,6 +230,18 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/crc-32": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+      "license": "Apache-2.0",
+      "bin": {
+        "crc32": "bin/crc32.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/debug": {
       "version": "4.4.1",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
@@ -366,6 +410,15 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/frac": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
+      "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/fresh": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
@@ -1074,6 +1127,18 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/ssf": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
+      "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "frac": "~1.1.2"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/statuses": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
@@ -1179,11 +1244,50 @@
         "node": ">=18"
       }
     },
+    "node_modules/wmf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
+      "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/word": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
+      "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
       "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
       "license": "ISC"
+    },
+    "node_modules/xlsx": {
+      "version": "0.18.5",
+      "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
+      "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "adler-32": "~1.3.0",
+        "cfb": "~1.2.1",
+        "codepage": "~1.15.0",
+        "crc-32": "~1.2.1",
+        "ssf": "~0.11.2",
+        "wmf": "~1.0.1",
+        "word": "~0.3.0"
+      },
+      "bin": {
+        "xlsx": "bin/xlsx.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
     }
   }
 }

+ 2 - 1
package.json

@@ -15,6 +15,7 @@
     "fs": "^0.0.1-security",
     "mime-types": "^3.0.1",
     "mongoose": "^8.18.1",
-    "mysql": "^2.18.1"
+    "mysql": "^2.18.1",
+    "xlsx": "^0.18.5"
   }
 }

+ 130 - 49
public/index.html

@@ -461,9 +461,7 @@
 <body>
     <!-- 导航栏 -->
     <nav>
-        <img src="./logo2.png" alt="logo" style="width: 200px;margin-right: 15px;"></img>
-        <img src="./logo1.png" alt="logo" style="width: 200px;margin-right: 15px;"></img>
-        <div class="event-name">第六届广东省青少年创新思维及科技实践大赛(创新思维类选手报名)</div>
+        
 
     </nav>
 
@@ -520,6 +518,15 @@
                         </div>
                         <a target="_blank" href="https://meeting.tencent.com/crm/l71mRRMd92" class="download-btn">观看</a>
                     </div>
+                    <div class="download-item">
+                        <div class="file-icon">📊</div>
+                        <div class="file-info">
+                            <div class="file-title">智能体应用备赛平台登录指引</div>
+                        </div>
+                        <a target="_blank"
+                            href="https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/0960e9b6-2687-b858-2285-455c29eac369/%E6%99%BA%E8%83%BD%E4%BD%93%E5%BA%94%E7%94%A8%E5%A4%87%E8%B5%9B%E5%B9%B3%E5%8F%B0%E7%99%BB%E5%BD%95%E6%8C%87%E5%BC%95.pdf"
+                            class="download-btn" download="智能体应用备赛平台登录指引.pdf">下载</a>
+                    </div>
                 </div>
             </div>
 
@@ -698,9 +705,19 @@
                         </div>
                     </div>
 
-                    <div class="file-upload">
-                        <label class="form-label required" for="photo">上传文件</label>
-                        <input type="file" id="registration" required>
+                    <div class="form-row">
+                        <div class="form-col">
+                            <div class="file-upload">
+                                <label class="form-label required" for="photo">上传文件</label>
+                                <input type="file" id="uploadFile" required>
+                            </div>
+                        </div>
+                        <div class="form-col" style="display: none;" id="videoUploadDiv">
+                            <div class="file-upload">
+                                <label class="form-label required" for="photo">上传视频</label>
+                                <input type="file" id="uploadVideo" accept="video/*" required>
+                            </div>
+                        </div>
                     </div>
                     <button type="submit" class="submit-btn" onclick="submitRegistration()">提交报名表</button>
                 </div>
@@ -712,6 +729,21 @@
         const requesturl = window.location.href.indexOf("localhost") > -1 ? "http://localhost:11111/" : "http://183.36.25.93:888/";
         console.log("requesturl:", requesturl);
 
+        // 获取url参数
+        const urlSearch = window.location.search;
+        if (urlSearch.slice(1) == "baoan") {
+            document.getElementsByTagName("nav")[0].innerHTML = `<div class="event-name">宝安区智能体应用赛</div>`
+        } else if (urlSearch.slice(1) == "guangming") {
+            document.getElementsByTagName("nav")[0].innerHTML = `<div class="event-name">光明确智能体应用赛</div>`
+        } else if (urlSearch.slice(1) == "pingshan") {
+            document.getElementsByTagName("nav")[0].innerHTML = `<div class="event-name">坪山区智能体应用赛</div>`
+        } else if (urlSearch.slice(1) == "longhua") {
+            document.getElementsByTagName("nav")[0].innerHTML = `<div class="event-name">龙华区智能体应用赛</div>`
+        }else{
+            document.getElementsByTagName("nav")[0].innerHTML = `<img src="./logo2.png" alt="logo" style="width: 200px;margin-right: 15px;"></img>
+        <img src="./logo1.png" alt="logo" style="width: 200px;margin-right: 15px;"></img>
+        <div class="event-name">第六届广东省青少年创新思维及科技实践大赛(创新思维类选手报名)</div>`
+        }
         const nowTime = new Date();
         const deadline = new Date("2025-09-22");
 
@@ -809,10 +841,10 @@
             // 删除添加的表单
             addList.forEach((item, idx) => {
                 let formDiv = html.querySelector(`div#addformContainer${item}`);
-                if(formDiv){
+                if (formDiv) {
                     html.removeChild(formDiv);
                 }
-                
+
             });
             addList = []
             num = 1
@@ -1170,7 +1202,6 @@
                     phone: document.getElementById('phone' + i).value,
                 })
             }
-
             try {
                 // 收集表单数据
                 const formDataObj = {
@@ -1211,42 +1242,6 @@
                 alert(`报名失败: ${error.message}`);
             }
         }
-
-
-        let RegistrationUrl = "";
-        document.getElementById('registration').addEventListener('change', function (e) {
-            const idNumber = document.getElementById('id_number').value;
-            if (!idNumber) {
-                alert('请先填写身份证号');
-                e.target.value = ''; // 清空已选择的文件
-                return;
-            }
-            const file = e.target.files[0];
-            if (!file) return;
-            console.log('选择文件:', file);
-
-            const fileName = `${idNumber}/${file.name}`;
-
-            var bucket = new window.AWS.S3({ params: { Bucket: 'ccrb' } });
-            var params = {
-                Key: fileName,
-                ContentType: file.type,
-                Body: file,
-                'Access-Control-Allow-Credentials': '*',
-                'ACL': 'public-read'
-            };
-            bucket.upload(params, function (err, data) {
-                if (err) {
-                    console.error('上传失败:', err);
-                    alert('文件上传失败');
-                } else {
-                    console.log('上传成功:', data);
-                    RegistrationUrl = data.Location;
-                    alert('文件上传成功');
-                }
-            });
-        });
-
         async function submitRegistration() {
             const idNumber = document.getElementById('id_number').value;
             if (!idNumber) {
@@ -1254,13 +1249,13 @@
                 e.target.value = ''; // 清空已选择的文件
                 return;
             }
-            if (!RegistrationUrl) {
-                alert('请先上传文件');
+            if (uploadFileUrl1 == "" || uploadFileUrl1 == "") {
+                alert('请上传报名文件和视频');
+                return;
             }
-            if (!confirm('确定提交吗?')) {
+            if (!confirm('如已上传过文件,本次上传会覆盖之前上传,是否确定提交吗?')) {
                 return;
             }
-
             try {
                 const response = await fetch(requesturl + "api/update", {
                     method: "POST",
@@ -1295,6 +1290,89 @@
             // const video = `<video style="width:100%;height:100%" src="https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/f41a07d8-2cab-76b2-2b5b-8211d98bdb0c/%E9%A6%AC%E6%88%B2%E5%9C%98%E6%A9%9F%E5%99%A8%E4%BA%BA.mp4" controls autoplay></video>`
             showModal("宣讲视频暂无...")
         }
+
+        let uploadFileUrl1 = "";
+        let uploadVideoUrl = "";
+        document.getElementById('uploadFile').addEventListener('change', async function (e) {
+            const idNumber = document.getElementById('idNumber1');
+            if (!idNumber.value) {
+                alert('请先填写身份证号');
+                e.target.value = ''; // 清空已选择的文件
+                return;
+            }
+            const file = e.target.files[0];
+            if (!file) return;
+            const fileName = `${idNumber}/${file.name}`;
+            uploadFileUrl1 = await uplod(fileName, file);
+            console.log('选择文件:', uploadFileUrl1);
+        });
+        document.getElementById('uploadVideo').addEventListener('change', async function (e) {
+            const idNumber = document.getElementById('idNumber1');
+            if (!idNumber.value) {
+                alert('请先填写身份证号');
+                e.target.value = ''; // 清空已选择的文件
+                return;
+            }
+            const file = e.target.files[0];
+            if (!file) return;
+            const fileName = `${idNumber}/${file.name}`;
+            uploadVideoUrl = await uplod(fileName, file);
+            console.log('视频文件:', uploadVideoUrl);
+        })
+        async function uplod(fileName, file) {
+            var bucket = new window.AWS.S3({ params: { Bucket: 'ccrb' } });
+            var params = {
+                Key: fileName,
+                ContentType: file.type,
+                Body: file,
+                'Access-Control-Allow-Credentials': '*',
+                'ACL': 'public-read'
+            };
+            try {
+                const data = await bucket.upload(params).promise();
+                console.log('上传成功:', data);
+                alert('文件上传成功');
+                return data.Location;
+            } catch (err) {
+                console.error('上传失败:', err);
+                alert('文件上传失败');
+                return '';
+            }
+        }
+        function styleHide() {
+            document.getElementById('registerBtn').click();
+            document.getElementById('downloadBtn').style.display = 'none';
+            // document.getElementById('registerBtn1').style.display = 'none';
+            document.getElementById('prevToStep1').style.display = 'none';
+            addContestant()
+            selectedCompetitionId = 2
+            updateGroupSelect(selectedCompetitionId);
+            // 切换到步骤2
+            document.getElementById('step1').classList.remove('active');
+            document.getElementById('step2').classList.add('active');
+            document.getElementsByClassName("button-Contestant")[0].classList.add('active');
+            document.getElementsByClassName("button-group")[1].style.textAlign = "center";
+            document.getElementsByClassName("button-group")[1].style.display = "block";
+            document.getElementsByClassName("step-title")[1].style.display = "none";
+            document.getElementById("videoUploadDiv").style.display = "block";
+            buttonContestant = true
+        }
+        window.onload = function () {
+            // updateGroupSelect("1")
+            if (urlSearch.slice(1) == "baoan") {
+                styleHide()
+                document.getElementsByTagName("nav")[0].innerHTML = `<div class="event-name">宝安区智能体应用赛</div>`
+            } else if (urlSearch.slice(1) == "guangming") {
+                styleHide()
+                document.getElementsByTagName("nav")[0].innerHTML = `<div class="event-name">光明确智能体应用赛</div>`
+            } else if (urlSearch.slice(1) == "pingshan") {
+                styleHide()
+                document.getElementsByTagName("nav")[0].innerHTML = `<div class="event-name">坪山区智能体应用赛</div>`
+            } else if (urlSearch.slice(1) == "longhua") {
+                styleHide()
+                document.getElementsByTagName("nav")[0].innerHTML = `<div class="event-name">龙华区智能体应用赛</div>`
+            }
+        }
     </script>
 
     <!-- Modal -->
@@ -1363,4 +1441,7 @@
 
 </script>
 
+<script>
+</script>
+
 </html>

+ 51 - 14
routes/router.js

@@ -1,10 +1,11 @@
 var express = require('express');
 var router = express.Router();
 var mysql = require('../model/mysql');
-const _mysqluser = ["172.16.12.5", "registration"]; //用戶數據庫信息
+// const _mysqluser = ["172.16.12.5", "registration"]; //用戶數據庫信息
 
-// const _mysqluser = ["183.36.25.93", "registration"]; //本地测试用戶數據庫信息
+const _mysqluser = ["183.36.25.93", "registration"]; //本地测试用戶數據庫信息
 const crypto = require('crypto');
+const XLSX = require('xlsx');
 
 router.route('/user').post(async function (req, res) {
     const signUpData = [];
@@ -47,13 +48,13 @@ router.route('/user').post(async function (req, res) {
             });
         }
     })
-    
-    
+
+
 });
 
 router.route('/update').post(async function (req, res) {
     const { url, id_number } = req.body;
-    const queryList = [_mysqluser[0], _mysqluser[1], "update_registration",url, id_number];
+    const queryList = [_mysqluser[0], _mysqluser[1], "update_registration", url, id_number];
     mysql.usselect(queryList, function (data) {
         console.log(data);
         if (data == 1) {
@@ -65,15 +66,51 @@ router.route('/update').post(async function (req, res) {
 });
 
 
-// router.route('/exportData').get(async function (req, res) { 
-//     console.log("导出数据", req.query.start);
-//     const { start, end } = req.query;
-//     const queryList = [_mysqluser[0], _mysqluser[1], "sp_export_registration_data",start, end];
-//     mysql.usselect(queryList, function (data) {
-//         console.log(data);
-//     });
-//     res.send({ status: 'ok', message: '导出数据成功' });
-// });
+router.route('/exportData').get(async function (req, res) {
+    const { start, end } = req.query;
+    const queryList = [_mysqluser[0], _mysqluser[1], "sp_export_registration_data", start, end];
+    let dataList = [];
+    await mysql.usselect(queryList, function (data) {
+        if (data[0] && data[0][0]) {
+            dataList = data[0];
+            dataList = dataList.map(item => {
+                let user_group = ""
+                if(item.type == 1){
+                    user_group = item.user_group == 1?"小学低龄":item.user_group == 2?"小学高龄":item.user_group == 3?"中学":"高校"
+                }else{
+                    user_group = item.user_group == 1?"小学组(4-6年级)":item.user_group == 2?"初中组(7-9年级)":item.user_group == 3?"高中组(10-12年级,含中职)":"高校组(含高职)"
+
+                }
+                return {
+                    小组id: item.user_groupid,
+                    小组人数: item.id_card.split(",").length,
+                    指导老师: item.upload_url,
+                    比赛类型: item.type == 1 ? "桌游挑战赛" : "智能体应用",
+                    姓名: item.user_name,
+                    邮箱: item.user_email,
+                    身份证号: item.user_id_number,
+                    组别: user_group,
+                    年级: item.user_grade,
+                    学校: item.user_school,
+                    学校地区: item.user_school_region,
+                    电话: item.user_phone,
+                    上传文件: item.registration_form_url,
+                    报名时间: new Date(item.created_at).toLocaleString()
+                }
+            });
+            const workbook = XLSX.utils.book_new();
+            const worksheet = XLSX.utils.json_to_sheet(dataList);
+            XLSX.utils.book_append_sheet(workbook, worksheet, '报名数据');
+            const buffer = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' });
+
+            res.setHeader('Content-Disposition', 'attachment; filename="' + start + ':' + end + '.xlsx"');
+            res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+            res.send(buffer);
+            return;
+        }
+    });
+
+});
 
 
 module.exports = router;