吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2535|回复: 18
收起左侧

[其他原创] DS生成的代码-考场监考人员抽签系统

  [复制链接]
andy118 发表于 2025-3-10 20:06
本帖最后由 andy118 于 2025-3-10 20:10 编辑

先上源代码
[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>智能监考抽签系统 V2.3</title>
    <style>
        body {
            font-family: "微软雅黑", Arial, sans-serif;
            margin: 20px;
            background: #f0f3f5;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
            background: white;
            border-radius: 10px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        h1 {
            text-align: center;
            color: #2c3e50;
            margin-bottom: 30px;
        }
        .input-section {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 30px;
            margin-bottom: 30px;
        }
        .file-upload {
            border: 2px dashed #3498db;
            padding: 20px;
            border-radius: 8px;
            text-align: center;
        }
        button {
            background: #3498db;
            color: white;
            padding: 14px 24px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 16px;
            transition: background 0.3s;
        }
        button:hover {
            background: #2980b9;
        }
        #resultTable {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
        }
        #resultTable th, #resultTable td {
            border: 1px solid #ddd;
            padding: 12px;
            text-align: center;
        }
        #resultTable th {
            background: #3498db;
            color: white;
        }
        #resultTable tr:nth-child(even) {
            background: #f8f9fa;
        }
        .highlight {
            font-weight: bold;
            color: #E41F0B;
        }
        .warning {
            color: #e67e22;
            margin: 10px 0;
            padding: 10px;
            background: #fdf2e9;
            border-radius: 5px;
        }
        .status-bar {
            text-align: right;
            padding: 10px;
            color: #7f8c8d;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1 id="titleHeader">【请填写活动标题】</h1>
        
        <div class="input-section">
            <div>
                <input type="text" id="eventTitle" placeholder="输入活动标题" style="width:100%;padding:8px;margin-bottom:20px;">
                <div class="file-upload">
                    <h3>考场信息文件</h3>
                    <input type="file" id="examRoomFile" accept=".txt">
                    <p class="warning">格式要求:每行格式:考场编号,考场地址,主监考人数,副监考人数<br>示例:101,逸夫楼201教室,1,2</p>
                </div>
            </div>
            <div class="file-upload">
                <h3>监考名单文件</h3>
                <input type="file" id="teacherFile" accept=".txt">
                <p class="warning">格式示例:<br>
                --主监考名单--<br>
                张三<br>
                李四<br>
                --副监考名单--<br>
                王五<br>
                赵六</p>
            </div>
        </div>

        <div style="text-align: center; margin: 20px 0;">
            <button>开始抽签</button>
        </div>

        <div class="status-bar" id="statusInfo"></div>

        <div id="resultArea" style="display: none;">
            <h3>抽签结果</h3>
            <table id="resultTable">
                <thead>
                    <tr>
                        <th>序号</th>
                        <th>考场编号</th>
                        <th>考场地址</th>
                        <th>主监考</th>
                        <th>副监考</th>
                    </tr>
                </thead>
                <tbody id="resultBody"></tbody>
            </table>
        </div>
    </div>

    <script>
        let examRooms = [];
        let originalMainTeachers = [];
        let originalSubTeachers = [];
        let currentResult = [];

        function parseExamRoomFile(content) {
            return content.split('\n').filter(line => line.trim()).map(line => {
                const parts = line.split(',');
                if (parts.length !== 4) {
                    alert("考场信息格式错误,请检查文件格式");
                    return null;
                }
                return {
                    code: parts[0].trim(),
                    address: parts[1].trim(),
                    mainNum: parseInt(parts[2]),
                    subNum: parseInt(parts[3])
                };
            }).filter(item => item);
        }

        function parseTeacherFile(content) {
            const mainTag = '--主监考名单--';
            const subTag = '--副监考名单--';
            
            const mainStart = content.indexOf(mainTag);
            const subStart = content.indexOf(subTag);

            if (mainStart === -1 || subStart === -1) {
                alert(`请严格使用分隔符:\n${mainTag}\n${subTag}`);
                return { main: [], sub: [] };
            }

            // 精确计算分隔符长度
            const mainLen = mainTag.length;
            const subLen = subTag.length;
            
            // 获取主监考名单(排除分隔符)
            const mainEnd = mainStart + mainLen;
            const mainSection = content.slice(mainEnd, subStart);
            
            // 获取副监考名单(排除分隔符)
            const subEnd = subStart + subLen;
            const subSection = content.slice(subEnd);

            return {
                main: mainSection.split('\n')
                         .map(n => n.trim())
                         .filter(n => n && !n.includes('--')),
                sub: subSection.split('\n')
                        .map(n => n.trim())
                        .filter(n => n && !n.includes('--'))
            };
        }

        function startLottery() {
            // 状态重置
            document.getElementById('statusInfo').textContent = '';
            
            // 验证数据完整性
            if(examRooms.length === 0){
                alert("请先上传考场信息文件");
                return;
            }
            if(originalMainTeachers.length === 0 || originalSubTeachers.length === 0){
                alert("请先上传监考名单文件");
                return;
            }

            // 更新标题
            const title = document.getElementById('eventTitle').value;
            document.getElementById('titleHeader').textContent = title || "【未命名监考活动】";
            
            // 初始化抽签池
            let mainPool = [...originalMainTeachers];
            let subPool = [...originalSubTeachers];
            currentResult = [];

            examRooms.forEach((room, index) => {
                const mainSelected = [];
                const subSelected = [];
                
                // 主监考抽签
                for(let i=0; i<room.mainNum; i++){
                    if(mainPool.length === 0) break;
                    const randomIndex = Math.floor(Math.random() * mainPool.length);
                    mainSelected.push(mainPool.splice(randomIndex, 1)[0]);
                }
                
                // 副监考抽签
                for(let i=0; i<room.subNum; i++){
                    if(subPool.length === 0) break;
                    const randomIndex = Math.floor(Math.random() * subPool.length);
                    subSelected.push(subPool.splice(randomIndex, 1)[0]);
                }

                currentResult.push({
                    seq: index + 1,
                    code: room.code,
                    address: room.address,
                    main: mainSelected.join(', ') || '&#9888;&#65039;名单不足',
                    sub: subSelected.join(', ') || '&#9888;&#65039;名单不足'
                });
            });

            // 显示统计信息
            const usedMain = originalMainTeachers.length - mainPool.length;
            const usedSub = originalSubTeachers.length - subPool.length;
            document.getElementById('statusInfo').innerHTML = `
                已分配:主监考 ${usedMain}/${originalMainTeachers.length} 
                | 副监考 ${usedSub}/${originalSubTeachers.length}
            `;

            displayResult();
        }

        function displayResult() {
            const tbody = document.getElementById('resultBody');
            tbody.innerHTML = '';
            
            currentResult.forEach(item => {
                const row = `<tr>
                    <td>${item.seq}</td>
                    <td>${item.code}</td>
                    <td>${item.address}</td>
                    <td class="highlight">${item.main}</td>
                    <td>${item.sub}</td>
                </tr>`;
                tbody.innerHTML += row;
            });
            
            document.getElementById('resultArea').style.display = 'block';
        }

        // 文件处理事件
        document.getElementById('examRoomFile').addEventListener('change', function(e) {
            const file = e.target.files[0];
            const reader = new FileReader();
            reader.onload = function() {
                examRooms = parseExamRoomFile(this.result);
                document.getElementById('statusInfo').textContent = 
                    `已加载 ${examRooms.length} 个考场信息`;
            };
            reader.readAsText(file);
        });

        document.getElementById('teacherFile').addEventListener('change', function(e) {
            const file = e.target.files[0];
            const reader = new FileReader();
            reader.onload = function() {
                const teachers = parseTeacherFile(this.result);
                originalMainTeachers = teachers.main;
                originalSubTeachers = teachers.sub;
                document.getElementById('statusInfo').textContent = 
                    `已加载 主监考 ${teachers.main.length} 人,副监考 ${teachers.sub.length} 人`;
            };
            reader.readAsText(file);
        });
    </script>
</body>
</html>


经过DS多次调试,功能已经正确使用,以及验证导入的TXT文本格式内容,避免出错。
使用效果如图所示
image.png
image.png

以上所有文字信息均由DS虚拟生成,不存在任何隐私泄露。
以上源码可直接复制到HTML文件里使用。导入模板为TXT文件,具体格式见第一张图片提示内容,欢迎大家进一步优化交流。
考场监考人员抽签系统.7z (2.86 KB, 下载次数: 71)

免费评分

参与人数 2吾爱币 +6 热心值 +2 收起 理由
苏紫方璇 + 5 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
long8586 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

xyxy1111 发表于 2026-2-16 15:14
对楼主的代码进行了一些修改。希望对大家有所帮助,不足之处望指教。
压缩包中包含:考场监考人员抽签系统.html、考场信息表.xlsx、主监考名单.txt、副监考名单.txt、js文件夹(xlsx.full.min.js)。
主要修改点:1、考场信息改成了excel的xlsx文件(使用xlsx.full.min.js库)。2、主监考和副监考各使用一个txt文本。3、生成的结果可以下载为excel文件。4、增加了帮助信息弹窗。5、增加了读取文件后的状态信息提示。
[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>智能监考抽签系统</title>
    <style>
        * {box-sizing: border-box;}
        body {
            font-family: "微软雅黑", Arial, sans-serif;
            margin: 20px;
            background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
            min-height: 100vh;
        }
        .container {
            width: 95%;
            max-width: 1200px;
            margin: 20px auto;
            padding: 30px;
            background: white;
            border-radius: 15px;
            box-shadow: 0px 8px 30px rgba(0,0,0,0.12);
        }
        #eventTitle {
            width:100%;
            padding:15px;
            margin-bottom:15px;
            border-radius:8px;
            border:1px solid #ccc;
            outline: none;
            font-size:1.5em;
            font-weight: 100;
            color: #2c3e50;
            text-align: center;
            transition: all 0.3s;
        }
        h1 {
            text-align: center;
        }

        .input-section {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 25px;
            margin-bottom: 35px;
        }
        .file-upload {
            padding: 25px 15px;
            border-radius: 15px;
            text-align: center;
            background: #fff;
            transition: all 0.3s;
            position: relative;
            overflow: hidden;
            box-shadow: 0px 0px 15px rgba(0,0,0,0.2);
        }
        .file-upload:hover {
            background: #fff9f2;
            transform: translateY(-2px);
            box-shadow: 0 10px 20px rgba(0,0,0,0.3);
        }
        .file-upload h3 {
            margin-top: 0;
            color: #2c3e50;
            font-size: 1.3em;
        }
        .example {
            font-size: 0.85em;
            color: #7f8c8d;
            margin-top: 12px;
            display: block;
            line-height: 1.5;
        }
        input[type="file"] {
            margin: 15px 0;
            width: 100%;
            padding: 10px;
            border-radius: 6px;
            border: 1px solid #ddd;
            background: white;
        }
        button {
            background: #3498db;
            color: white;
            padding: 16px 35px;
            border: none;
            border-radius: 50px;
            cursor: pointer;
            font-size: 18px;
            font-weight: bold;
            transition: all 0.2s;
            display: block;
            margin: 30px auto;
            letter-spacing: 1px;
        }
        button:hover {
            background: #2a5caa;
            transform: translateY(-2px);
        }
        button:active {
            transform: translateY(1px);
        }
        #resultTable {
            width: 100%;
            border-collapse: collapse;
            margin-top: 25px;
            border-radius: 10px;
            overflow: hidden;
        }
        #resultTable th, #resultTable td {
            border: 1px solid #e1e4e8;
            padding: 6px;
            text-align: center;
        }
        #resultTable th {
            background: linear-gradient(180deg, #3498db, #2980b9);
            color: white;
            font-weight: bold;
        }
        #resultTable tr:nth-child(even) {
            background: #f8f9fa;
        }
        #resultTable tr:hover {
            background-color: #e3f2fd;
        }

        #toExcel {
            text-decoration: none;
            text-align: center;
            background: #1d953f;
            border: none;
            color: #fff;
            font-size: 16px;
            font-weight: 100;
            padding: 10px 20px;
            padding: 16px 35px;
            border-radius: 50px;
            cursor: pointer;
            transition: all 0.2s;
            display: block;
            margin: 30px auto;
            box-shadow: 0 4px 15px rgba(52, 152, 219, 0.3);
            letter-spacing: 1px;
        }
        #toExcel:hover {
            background: #007947;
        }

        .warning {
            text-align: left;
            color: #e67e22;
            margin: 15px 0;
            padding: 15px;
            background: #fdf2e9;
            border-radius: 8px;
            font-size: 0.95em;
        }
        .status-bar {
            text-align: center;
            padding: 12px 20px;
            color: #7f8c8d;
            background: #f8f9fa;
            border-radius: 8px;
            margin: 20px 0;
            font-size: 0.95em;
        }
        .file-info {
            font-size: 0.9em;
            color: #27ae60;
            margin-top: 8px;
            font-weight: bold;
        }
        .instructions {
            background: #f6f5ec;font-weight:900;
            padding: 0px;width:600px;
            border:none;
            border-radius:15px;
            box-shadow:0px 0px 20px rgba(0,0,0,0.5);
        }
        .instructions h2 {
            text-align:center;padding:0;margin:0;line-height:3em;over-flow:hidden;
            background-color: #ff6600;color:#fff;
        }
        .instructions ul {
            padding: 0 30px;
        }
        .instructions li {
            list-style: none;
            line-height: 2;
            font-weight:100;font-size:0.8em;
        }
        .instructions h3 {
            font-weight:900;font-size:1.5em;padding:0;margin:10px 0;
        }
        .footer {
            text-align: center;
            margin-top: 30px;
            color: #7f8c8d;
            font-size: 0.9em;
        }
        .stats-box {
            background: #e8f4fc;
            padding: 15px;
            border-radius: 8px;
            margin: 20px 0;
            display: flex;
            justify-content: space-around;
            flex-wrap: wrap;
        }
        .stat-item {
            text-align: center;
            padding: 10px;
        }
        .stat-value {
            font-size: 1.5em;
            font-weight: bold;
            color: #3498db;
        }
        .stat-label {
            font-size: 0.9em;
            color: #7f8c8d;
        }
        .popover {
            display: none;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 80%;
            max-width: 800px;
            max-height: 80%;
            overflow-y: auto;
            background: white;
            border-radius: 15px;
            box-shadow: 0 10px 50px rgba(0,0,0,0.3);
            z-index: 1000;
            padding: 30px;
        }
        .popover.active {
            display: block;
        }
        .popover-content {
            position: relative;
        }
        .popover-content h2 {
            margin-top: 0;
            color: #2c3e50;
        }
        .popover-content pre {
            background: #f5f5f5;
            padding: 15px;
            border-radius: 8px;
            overflow-x: auto;
            font-family: monospace;
        }
        .popover-overlay {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.5);
            z-index: 999;
        }
        .popover-overlay.active {
            display: block;
        }
        [url=home.php?mod=space&uid=945662]@media[/url] (max-width: 768px) {
            .input-section {
                grid-template-columns: 1fr;
            }
            .container {
                padding: 20px 15px;
            }
            h1 {
                font-size: 1.8em;
            }
            .stats-box {
                flex-direction: column;
            }
            .popover {
                width: 95%;
                padding: 20px;
            }
        }
    </style>

    <script src="js/xlsx.full.min.js"></script>
</head>
<body>

<div class="container" style="position: relative;">
    <!-- 帮助按钮 -->
    <button popovertarget="help" title="帮助" style="position:absolute;top:0px;right:30px;width:40px;height:40px;padding:0;border-radius:50%;background-color:#ef5b9c;font-weight:900;">?</button>
        <div class="instructions" id="help" popover>
            <h2>使用说明</h2>
            <ul>
                <li><h3>考试名称</h3>首先,请输入需要安排的考试名称。</li><hr>
                <li><h3>考场信息表.xlsx</h3>不得修改表格格式!第一行为表头(包括:考场编号 | 考场地址 | 主监考人数 | 副监考人数)。</li><hr>
                <li><h3>主监考名单.txt、副监考名单.txt</h3>每行一个监考姓名。如不需要副监考,请将“考场信息表.xlsx”的“副监考”栏的人数填写“0”。</li><hr>
                <li><h3>点击 <span style="border:1px solid #000;border-radius:20px;padding:2px 6px;">开始抽签</span> 按钮后</h3>系统将随机分配监考人员,并可下载excel格式的监考安排表。<hr></li>
            </ul>
            <button popovertarget="help" popovertargetaction="hide">明白了</button>
        </div>

    <!-- 主容器 -->
    <div class="content-wrapper">
        <h1>智能监考抽签系统</h1>
        
        <!-- 考试名称输入 -->
        <input 
            type="text" 
            id="eventTitle" 
            placeholder="请输入考试名称"
            style="margin-bottom:20px;"
        >

        <!-- 文件上传区域 -->
        <div class="input-section">
            <!-- 考场信息表上传 -->
            <div class="file-upload">
                <h3>上传 考场信息表.xlsx</h3>
                <input type="file" id="examRoomFile" accept=".xlsx, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
                <div id="examRoomInfo" class="file-info"></div>
            </div>

            <!-- 主监考名单上传 -->
            <div class="file-upload">
                <h3>上传 主监考名单.txt</h3>
                <input type="file" id="mainTeacherFile" accept=".txt">
                <div id="mainTeacherInfo" class="file-info"></div>
            </div>

            <!-- 副监考名单上传 -->
            <div class="file-upload">
                <h3>上传 副监考名单.txt</h3>
                <input type="file" id="subTeacherFile" accept=".txt">
                <div id="subTeacherInfo" class="file-info"></div>
            </div>
        </div>

        <!-- 统计信息 -->
        <div class="stats-box" id="statsBox" style="display: none;">
            <div class="stat-item">
                <div class="stat-value" id="totalMainNeeded">0</div>
                <div class="stat-label">需要主监考总数</div>
            </div>
            <div class="stat-item">
                <div class="stat-value" id="totalSubNeeded">0</div>
                <div class="stat-label">需要副监考总数</div>
            </div>
            <div class="stat-item">
                <div class="stat-value" id="mainTeachersCount">0</div>
                <div class="stat-label">主监考名单人数</div>
            </div>
            <div class="stat-item">
                <div class="stat-value" id="subTeachersCount">0</div>
                <div class="stat-label">副监考名单人数</div>
            </div>
        </div>

        <!-- 操作按钮 -->
        <div style="text-align: center; margin-top: 30px;">
            <button onclick="startLottery()">开始抽签</button>
        </div>

        <!-- 结果展示 -->
        <div class="status-bar" id="statusInfo">等待上传文件...</div>
        <div id="resultArea" style="display: none;">
            <table id="resultTable">
                <thead>
                    <tr>
                        <th colspan="5" style="text-align: center;">
                            <h2 id="titleHeader"></h2>
                            <span id="fullTime"></span>
                        </th>
                    </tr>
                    <tr>
                        <th width="8%">序号</th>
                        <th width="20%">考场编号</th>
                        <th width="20%">考场地址</th>
                        <th width="25%">主监考</th>
                        <th width="25%">副监考</th>
                    </tr>
                </thead>
                <tbody id="resultBody"></tbody>
            </table>
            <a href="javascript:void(0);" id="toExcel" onclick="exportToExcel()"style="margin-top: 15px;">导出Excel表格</a>
            <div class="warning" id="warningMessage"></div>
        </div>
    </div>
</div>

<script>
// 数据存储
let examRooms = [];
let originalMainTeachers = [];
let originalSubTeachers = [];
let currentResult = [];
let totalMainNeeded = 0;
let totalSubNeeded = 0;

// 解析Excel文件
function parseExamRoomFile(content) {
    const workbook = XLSX.read(content, { type: 'array' });
    const sheetName = workbook.SheetNames[0];
    const worksheet = workbook.Sheets[sheetName];
    
    let headers = [];
    const data = [];
    
    // 解析表头和数据
    const rows = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
    
    if (rows.length === 0) {
        throw new Error('Excel文件为空或格式不正确');
    }
    
    // 获取表头
    headers = rows[0].filter(cell => cell && cell.toString().trim() !== '');
    
    if (headers.length === 0) {
        throw new Error('Excel文件缺少表头行');
    }
    
    // 解析数据行
    for (let i = 1; i < rows.length; i++) {
        const row = rows[i];
        if (!row || row.length === 0) continue;
        
        const obj = {};
        for (let j = 0; j < headers.length; j++) {
            if (headers[j] && row[j] !== undefined) {
                obj[headers[j].trim()] = row[j];
            }
        }
        
        // 验证必填字段
        if (
            obj['考场编号'] &&
            obj['考场地址'] &&
            obj['主监考人数'] !== undefined &&
            obj['副监考人数'] !== undefined
        ) {
            // 为每行数据添加序号(从1开始)
            obj.seq = data.length + 1;
            data.push(obj);
        }
    }
    
    if (data.length === 0) {
        throw new Error('Excel文件中没有有效的数据行');
    }
    
    // 转换为最终数据格式
    return data.map(item => ({
        seq: item.seq,  // 添加seq字段
        code: item['考场编号'].toString().trim(),
        address: item['考场地址'].toString().trim(),
        mainNum: parseInt(item['主监考人数']) || 0,
        subNum: parseInt(item['副监考人数']) || 0
    }));
}

// 解析教师名单文件
function parseTeacherFile(content) {
    return content.split('\n')
        .map(line => line.trim())
        .filter(line => line !== '');
}

// 检查数据有效性
function checkDataValidity() {
    if (examRooms.length && originalMainTeachers.length && originalSubTeachers.length) {
        calculateTotals();
        showStats();
    }
}

// 计算监考需求总数
function calculateTotals() {
    totalMainNeeded = examRooms.reduce((sum, room) => sum + room.mainNum, 0);
    totalSubNeeded = examRooms.reduce((sum, room) => sum + room.subNum, 0);
    
    // 更新统计信息
    document.getElementById('totalMainNeeded').textContent = totalMainNeeded;
    document.getElementById('totalSubNeeded').textContent = totalSubNeeded;
    document.getElementById('mainTeachersCount').textContent = originalMainTeachers.length;
    document.getElementById('subTeachersCount').textContent = originalSubTeachers.length;
}

// 显示统计信息
function showStats() {
    document.getElementById('statsBox').style.display = 'flex';
    
    let statusHTML = `已加载数据:`;
    statusHTML += `考场:${examRooms.length}个 `;
    statusHTML += `主监考:${originalMainTeachers.length}人 `;
    statusHTML += `副监考:${originalSubTeachers.length}人 `;
    statusHTML += `需求:主监考${totalMainNeeded}人 | 副监考${totalSubNeeded}人`;
    
    document.getElementById('statusInfo').innerHTML = statusHTML;
    
    const warningElement = document.getElementById('warningMessage');
    
    if (totalMainNeeded > originalMainTeachers.length) {
        warningElement.style.display = 'block';
        warningElement.innerHTML = `
            <div style="color:#e74c3c; font-weight:bold;">
                <h4 style="margin:0 0 10px 0;">&#9888;&#65039; 主监考不足</h4>
                <p style="margin:0;">缺少 ${totalMainNeeded - originalMainTeachers.length} 人</p>
            </div>
        `;
    } else if (totalSubNeeded > originalSubTeachers.length) {
        warningElement.style.display = 'block';
        warningElement.innerHTML = `
            <div style="color:#e74c3c; font-weight:bold;">
                <h4 style="margin:0 0 10px 0;">&#9888;&#65039; 副监考不足</h4>
                <p style="margin:0;">缺少 ${totalSubNeeded - originalSubTeachers.length} 人</p>
            </div>
        `;
    } else {
        warningElement.style.display = 'block';
        warningElement.innerHTML = `
            <div style="color:#27ae60; font-weight:bold;">
                <h4 style="margin:0 0 10px 0;">&#10003; 数据完整</h4>
                <p style="margin:0;">可以开始抽签</p>
            </div>
        `;
    }
}

// 显示错误提示
function showErrorAlert(title, message) {
    alert(`${title}\n\n${message}`);
    document.getElementById('statusInfo').innerHTML = 
        `<span style="color:#e74c3c">${title}: ${message}</span>`;
}

// 开始抽签
function startLottery() {
    if (!validateData()) return;
    
    // 重置状态
    resetStatus();
    
    // 初始化抽签池
    let mainPool = [...originalMainTeachers];
    let subPool = [...originalSubTeachers];
    currentResult = [];
    
    // 执行抽签
    examRooms.forEach(room => {
        const mainSelected = selectTeachers(mainPool, room.mainNum);
        const subSelected = selectTeachers(subPool, room.subNum);
        
        currentResult.push({
            seq: room.seq,  // 使用从Excel解析的seq
            code: room.code,
            address: room.address,
            main: mainSelected.join(', '),
            sub: subSelected.join(', ')
        });
    });
    
    // 显示结果
    showResult();
}

// 随机选择教师
function selectTeachers(pool, count) {
    if (count === 0) return [];
    if (pool.length < count) {
        throw new Error(`需要${count}人,但仅剩${pool.length}人`);
    }
    
    const selected = [];
    for (let i = 0; i < count; i++) {
        const randIndex = Math.floor(Math.random() * pool.length);
        selected.push(pool.splice(randIndex, 1)[0]);
    }
    return selected;
}

// 验证数据
function validateData() {
    if (examRooms.length === 0) {
        showErrorAlert('错误', '请先上传考场信息表');
        return false;
    }
    if (originalMainTeachers.length === 0) {
        showErrorAlert('错误', '请先上传主监考名单');
        return false;
    }
    if (originalSubTeachers.length === 0) {
        showErrorAlert('错误', '请先上传副监考名单');
        return false;
    }
    if (totalMainNeeded > originalMainTeachers.length) {
        showErrorAlert('错误', `主监考不足:需要${totalMainNeeded}人,但只有${originalMainTeachers.length}人`);
        return false;
    }
    if (totalSubNeeded > originalSubTeachers.length) {
        showErrorAlert('错误', `副监考不足:需要${totalSubNeeded}人,但只有${originalSubTeachers.length}人`);
        return false;
    }
    return true;
}

// 重置状态
function resetStatus() {
    document.getElementById('statusInfo').innerHTML = '正在抽签...';
    document.getElementById('warningMessage').style.display = 'none';
    document.getElementById('resultArea').style.display = 'none';
}

// 显示结果
function showResult() {
    const resultBody = document.getElementById('resultBody');
    resultBody.innerHTML = '';
    
    // 设置标题
    const eventTitle = document.getElementById('eventTitle').value.trim();
    document.getElementById('titleHeader').textContent = `${eventTitle} - 监考安排表`;
    document.getElementById('fullTime').textContent = `抽签时间:${new Date().toLocaleString('zh-CN')}`;
    
    // 填充表格数据
    currentResult.forEach(item => {
        const row = document.createElement('tr');
        row.innerHTML = `
            <td>${item.seq}</td>
            <td>${item.code}</td>
            <td>${item.address}</td>
            <td>${item.main || '-'}</td>
            <td>${item.sub || '-'}</td>
        `;
        resultBody.appendChild(row);
    });
    
    document.getElementById('resultArea').style.display = 'block';
            // 滚动到结果区域
            document.getElementById('resultArea').scrollIntoView({ 
                behavior: 'smooth' 
            });
    document.getElementById('statusInfo').innerHTML = 
        `<span style="color:#27ae60; font-weight:bold;">&#10003; 抽签完成!共为 ${examRooms.length} 个考场分配了监考人员。</span>`;
}

// 导出到Excel
function exportToExcel() {
    if (currentResult.length === 0) {
        alert('请先完成抽签再导出');
        return;
    }
    
    const eventTitle = document.getElementById('eventTitle').value.trim();
    const title =  `${eventTitle} - 监考安排表`;
    
    const tableData = [
        [title],
        [`抽签时间:${new Date().toLocaleString('zh-CN')}`],
        ['序号', '考场编号', '考场地址', '主监考', '副监考'],
        ...currentResult.map(item => [
            item.seq,
            item.code,
            item.address,
            item.main || '-',
            item.sub || '-'
        ])
    ];
    
    const ws = XLSX.utils.aoa_to_sheet(tableData);
    
    // 设置列宽
    const wscols = [
        {wch: 8},  // 序号
        {wch: 15}, // 考场编号
        {wch: 25}, // 考场地址
        {wch: 25}, // 主监考
        {wch: 25}  // 副监考
    ];
    ws['!cols'] = wscols;
    
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, '监考安排');
    
    const excelBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
    const blob = new Blob([excelBuffer], { type: 'application/octet-stream' });
    
    const link = document.createElement('a');
    link.href = URL.createObjectURL(blob);
    link.download = `监考安排_${title}.xlsx`;
    link.click();
}


// 文件处理事件
document.getElementById('examRoomFile').addEventListener('change', function(e) {
    const file = e.target.files[0];
    if (!file) return;
    
    const reader = new FileReader();
    reader.onload = function() {
        try {
            examRooms = parseExamRoomFile(reader.result);
            if (examRooms.length > 0) {
                document.getElementById('examRoomInfo').innerHTML = 
                    `&#10003; 已加载 <span style="color:#2ecc71">${examRooms.length}</span> 个考场`;
                document.getElementById('statusInfo').innerHTML = 
                    `已加载考场信息:${examRooms.length}个考场`;
                checkDataValidity();
            } else {
                document.getElementById('examRoomInfo').innerHTML = 
                    '&#10060; 文件解析失败,请检查格式';
            }
        } catch (error) {
            showErrorAlert('解析考场信息文件时出错', error.message);
            document.getElementById('examRoomInfo').innerHTML = 
                '&#10060; 解析错误:' + error.message;
        }
    };
    reader.readAsArrayBuffer(file);
});

document.getElementById('mainTeacherFile').addEventListener('change', function(e) {
    const file = e.target.files[0];
    if (!file) return;
    
    const reader = new FileReader();
    reader.onload = function() {
        try {
            originalMainTeachers = parseTeacherFile(reader.result);
            document.getElementById('mainTeacherInfo').innerHTML = 
                `&#10003; 已加载 <span style="color:#2ecc71">${originalMainTeachers.length}</span> 位主监考`;
            checkDataValidity();
        } catch (error) {
            showErrorAlert('解析主监考文件时出错', error.message);
            document.getElementById('mainTeacherInfo').innerHTML = 
                '&#10060; 解析错误:' + error.message;
        }
    };
    reader.readAsText(file);
});

document.getElementById('subTeacherFile').addEventListener('change', function(e) {
    const file = e.target.files[0];
    if (!file) return;
    
    const reader = new FileReader();
    reader.onload = function() {
        try {
            originalSubTeachers = parseTeacherFile(reader.result);
            document.getElementById('subTeacherInfo').innerHTML = 
                `&#10003; 已加载 <span style="color:#2ecc71">${originalSubTeachers.length}</span> 位副监考`;
            checkDataValidity();
        } catch (error) {
            showErrorAlert('解析副监考文件时出错', error.message);
            document.getElementById('subTeacherInfo').innerHTML = 
                '&#10060; 解析错误:' + error.message;
        }
    };
    reader.readAsText(file);
});

// 初始化页面
window.onload = function() {
    document.getElementById('eventTitle').focus();
    document.getElementById('statusInfo').innerHTML = '请上传考场信息表(XLSX格式)和监考名单';
    
    // 初始化帮助弹窗
    const helpButton = document.querySelector('[onclick="toggleHelp()"]');
    if (helpButton) {
        helpButton.addEventListener('click', toggleHelp);
    }
    
    // 添加键盘快捷键支持
    document.addEventListener('keydown', function(e) {
        if (e.key === 'Escape') {
            const popover = document.getElementById('helpPopover');
            if (popover.classList.contains('active')) {
                toggleHelp();
            }
        }
    });
};
</script>

</body>
</html>
001.png
000.png
002.png

考场监考人员抽签系统html.rar

253.15 KB, 下载次数: 8, 下载积分: 吾爱币 -1 CB

街望 发表于 2025-3-11 05:39
[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>智能监考系统 Pro 2025</title>
    <style>
        :root {
            --primary: #4f46e5;
            --secondary: #818cf8;
            --glass: rgba(255,255,255,0.9);
        }

        /* 动态渐变背景 */
        body {
            background: linear-gradient(135deg, #e0e7ff 0%, #f3e8ff 100%);
            min-height: 100vh;
        }

        .container {
            backdrop-filter: blur(20px);
            background: var(--glass);
            border-radius: 1.5rem;
            box-shadow: 0 10px 30px rgba(0,0,0,0.1);
        }

        /* 智能表格 */
        .smart-table {
            --stripe: #f8fafc;
            --hover: #f1f5f9;
            border-collapse: separate;
            border-spacing: 0 0.5rem;
        }
        .smart-table tr {
            transition: all 0.3s ease;
            background: white;
        }
        .smart-table tr:hover {
            transform: translateX(10px);
            box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
        }

        /* 动态按钮 */
        .action-btn {
            background-image: linear-gradient(to right, var(--primary), var(--secondary));
            clip-path: polygon(0 0, 100% 0, 95% 100%, 5% 100%);
            transition: all 0.3s ease;
        }
        .action-btn:hover {
            clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
        }
    </style>
</head>
<body>
    <div class="container mx-auto p-8 max-w-4xl">
        <!-- 智能头部 -->
        <header class="text-center mb-12">
            <h1 class="text-4xl font-bold bg-gradient-to-r from-purple-600 to-blue-600 bg-clip-text text-transparent">
                &#127919; 智能监考分配系统
            </h1>
            <input type="text" id="eventTitle" placeholder="输入考试名称..." 
                   class="mt-4 px-6 py-3 rounded-full border-2 border-dashed focus:outline-none focus:border-purple-500">
        </header>

        <!-- 双面板布局 -->
        <div class="grid md:grid-cols-2 gap-8 mb-8">
            <!-- 文件上传 -->
            <div class="p-6 bg-white/90 rounded-xl shadow-lg">
                <h3 class="text-xl font-semibold mb-4 flex items-center">
                    &#128228; 数据导入
                    <span class="ml-auto text-sm text-gray-500">支持.txt/.csv</span>
                </h3>
                <div class="space-y-4">
                    <div class="file-upload">
                        <input type="file" id="examFile" class="hidden">
                        <label for="examFile" class="block p-4 border-2 border-dashed rounded-lg hover:bg-gray-50 cursor-pointer transition-colors">
                            &#127979; 考场配置文件
                            <span class="block mt-2 text-sm text-gray-500">格式:编号,地址,主监考数,副监考数</span>
                        </label>
                    </div>
                    <div class="file-upload">
                        <input type="file" id="teacherFile" class="hidden">
                        <label for="teacherFile" class="block p-4 border-2 border-dashed rounded-lg hover:bg-gray-50 cursor-pointer transition-colors">
                            &#128105;&#127979; 监考名单文件
                            <span class="block mt-2 text-sm text-gray-500">使用"--主监考名单--"分隔</span>
                        </label>
                    </div>
                </div>
            </div>

            <!-- 规则设置 -->
            <div class="p-6 bg-white/90 rounded-xl shadow-lg">
                <h3 class="text-xl font-semibold mb-4">&#9881;&#65039; 分配规则</h3>
                <div class="space-y-4">
                    <div class="rule-item">
                        <label class="block mb-2">分配模式:</label>
                        <select id="modeSelect" class="w-full p-2 rounded-lg border">
                            <option value="random">随机分配</option>
                            <option value="order">顺序轮转</option>
                        </select>
                    </div>
                    <div class="rule-item">
                        <label class="flex items-center space-x-2">
                            <input type="checkbox" id="preventRepeat" checked>
                            <span>启用防重复分配</span>
                        </label>
                    </div>
                </div>
            </div>
        </div>

        <!-- 操作中心 -->
        <div class="text-center mb-8 space-x-4">
            <button class="action-btn px-8 py-3 text-white font-semibold rounded-lg">
                &#127922; 开始智能分配
            </button>
            <button class="px-6 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors">
                &#128228; 导出Excel
            </button>
        </div>

        <!-- 结果展示 -->
        <div class="result-panel bg-white rounded-xl shadow-lg overflow-hidden">
            <table class="smart-table w-full">
                <thead class="bg-gray-50">
                    <tr>
                        <th class="p-4 text-left">考场</th>
                        <th class="p-4 text-left">地址</th>
                        <th class="p-4">主监考</th>
                        <th class="p-4">副监考</th>
                    </tr>
                </thead>
                <tbody id="resultBody" class="divide-y divide-gray-200">
                    <!-- 动态插入结果 -->
                </tbody>
            </table>
        </div>
    </div>

    <script>
        // 状态管理
        let state = {
            examRooms: [],
            mainTeachers: [],
            subTeachers: [],
            history: [],
            config: {
                mode: 'random',
                preventRepeat: true
            }
        }

        // 核心算法(融合VBA逻辑[1](@ref))
        const allocationEngine = {
            // 随机分配算法
            randomAllocate() {
                const mainPool = [...state.mainTeachers];
                const subPool = [...state.subTeachers];
                
                return state.examRooms.map(room => {
                    const select = (pool, count) => {
                        const selected = [];
                        while(selected.length < count && pool.length) {
                            const idx = Math.floor(Math.random() * pool.length);
                            selected.push(pool.splice(idx, 1)[0]);
                        }
                        return selected;
                    }

                    return {
                        ...room,
                        main: select(mainPool, room.mainNum),
                        sub: select(subPool, room.subNum)
                    }
                });
            },

            // 顺序分配算法(基于VBA顺序逻辑[1](@ref))
            orderedAllocate() {
                let mainIndex = Math.floor(Math.random() * state.mainTeachers.length);
                let subIndex = Math.floor(Math.random() * state.subTeachers.length);
                
                return state.examRooms.map(room => {
                    const select = (pool, count, index) => {
                        const selected = [];
                        for(let i=0; i<count; i++) {
                            if(index >= pool.length) index = 0;
                            selected.push(pool[index]);
                            index++;
                        }
                        return selected;
                    }

                    const main = select(state.mainTeachers, room.mainNum, mainIndex);
                    const sub = select(state.subTeachers, room.subNum, subIndex);
                    
                    mainIndex += room.mainNum;
                    subIndex += room.subNum;

                    return { ...room, main, sub }
                });
            }
        }

        // 文件解析器
        const fileParser = {
            parseExam(content) {
                return content.split('\n').filter(l => l.trim()).map(line => {
                    const [code, address, mainNum, subNum] = line.split(',');
                    return {
                        code: code.trim(),
                        address: address.trim(),
                        mainNum: parseInt(mainNum),
                        subNum: parseInt(subNum)
                    }
                });
            },

            parseTeachers(content) {
                const sections = content.split(/--+([^-]+)--+/g).slice(1);
                const result = { main: [], sub: [] };
                
                for(let i=0; i<sections.length; i+=2) {
                    const type = sections[i].includes('主') ? 'main' : 'sub';
                    result[type] = sections[i+1].split('\n')
                        .map(n => n.trim())
                        .filter(n => n);
                }
                return result;
            }
        }

        // 导出功能(基于XLSX.js)
        function exportExcel() {
            const data = state.history[0].map(item => ({
                '考场编号': item.code,
                '考场地址': item.address,
                '主监考': item.main.join(','),
                '副监考': item.sub.join(',')
            }));

            const ws = XLSX.utils.json_to_sheet(data);
            const wb = XLSX.utils.book_new();
            XLSX.utils.book_append_sheet(wb, ws, "监考分配");
            XLSX.writeFile(wb, `监考分配_${new Date().toLocaleDateString()}.xlsx`);
        }

        // 事件监听
        document.getElementById('examFile').addEventListener('change', async e => {
            const content = await e.target.files[0].text();
            state.examRooms = fileParser.parseExam(content);
        });

        document.getElementById('teacherFile').addEventListener('change', async e => {
            const content = await e.target.files[0].text();
            const { main, sub } = fileParser.parseTeachers(content);
            state.mainTeachers = main;
            state.subTeachers = sub;
        });

        // 初始化
        document.querySelector('#modeSelect').addEventListener('change', e => {
            state.config.mode = e.target.value;
        });
    </script>
</body>
</html>
kenkenss 发表于 2025-3-11 07:57
anning666 发表于 2025-3-11 08:35
以前的程序员专注于写代码,有了AI工具以后,提需求的能力就变得相当重要了,代码让AI来写,开发者再缝缝补补,补充一下,项目基本落地了.
wangpeng484 发表于 2025-3-11 09:12
有时间学习一下
jsj08 发表于 2025-3-11 13:02
已经下载,学习中。
liaoticai 发表于 2025-3-11 14:49
代码这块总是学不会
 楼主| andy118 发表于 2025-3-11 20:37
街望 发表于 2025-3-11 05:39
[mw_shl_code=html,true]

你的代码测试了吗?按txt模板好像不行哦。
dustin0911 发表于 2025-4-2 16:00
我来试试看好不好用,先谢谢楼主
茄子小果 发表于 2025-7-2 14:21
谢谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-2-20 16:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表