高校面积测算工具edit icon

创建者:
用户wodEjcwc
Fork(复制)
下载
嵌入
BUG反馈
index.html
style.css
index.js
md
README.md
index.html
            
            <!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>普通高等学校建筑面积测算工具 | 建标〔2018〕32号</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/xlsx.full.min.js"></script>
    <style>
        body { font-family: "Microsoft YaHei", sans-serif; }
        .input-box:focus { border-color: #2563eb; box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2); }
        .card { box-shadow: 0 2px 15px rgba(0,0,0,0.08); }
    </style>
</head>
<body class="bg-gray-50 min-h-screen">
    <!-- 顶部标题 -->
    <header class="bg-blue-700 text-white py-4 px-6 shadow-md">
        <div class="max-w-7xl mx-auto">
            <h1 class="text-2xl font-bold">普通高等学校建筑面积测算工具</h1>
            <p class="text-blue-100 text-sm mt-1">依据《建标〔2018〕32号 普通高等学校建筑面积指标》开发</p>
        </div>
    </header>

    <!-- 主内容区 -->
    <main class="max-w-7xl mx-auto px-4 py-6 grid grid-cols-1 lg:grid-cols-12 gap-6">
        <!-- 左侧输入区 -->
        <div class="lg:col-span-5 space-y-6">
            <!-- 基础信息卡片 -->
            <div class="card bg-white rounded-lg p-5">
                <h2 class="text-lg font-bold text-gray-800 border-b pb-3 mb-4">一、基础信息配置</h2>
                
                <!-- 院校类别选择 -->
                <div class="mb-4">
                    <label class="block text-sm font-medium text-gray-700 mb-1">院校类别 <span class="text-red-500">*</span></label>
                    <select id="schoolType" class="w-full rounded border border-gray-300 px-3 py-2 input-box">
                        <option value="comprehensive1">综合大学(文法为主,文法60%/理工40%)</option>
                        <option value="comprehensive2">综合大学(理工为主,理工60%/文法40%)</option>
                        <option value="normal">师范、民族院校</option>
                        <option value="science">理工院校</option>
                        <option value="agriculture">农林院校</option>
                        <option value="medical">医药院校</option>
                        <option value="finance">财经、政法院校</option>
                        <option value="foreign">外语院校</option>
                        <option value="sports">体育院校</option>
                        <option value="art">艺术院校</option>
                    </select>
                </div>

                <!-- 办学规模 -->
                <div class="mb-4">
                    <label class="block text-sm font-medium text-gray-700 mb-1">本科生办学规模(人) <span class="text-red-500">*</span></label>
                    <input type="number" id="undergraduateNum" class="w-full rounded border border-gray-300 px-3 py-2 input-box" placeholder="请输入本科生人数" min="1">
                </div>

                <!-- 折叠面板:附加人员配置 -->
                <div class="border border-gray-200 rounded-lg overflow-hidden mb-4">
                    <div class="bg-gray-50 px-3 py-2 cursor-pointer flex justify-between items-center" onclick="togglePanel('extraPerson')">
                        <span class="font-medium text-gray-700">二、附加人员配置(选配)</span>
                        <span id="extraPersonArrow" class="text-gray-500">▼</span>
                    </div>
                    <div id="extraPersonPanel" class="p-3 space-y-3 hidden">
                        <div class="grid grid-cols-2 gap-3">
                            <div>
                                <label class="block text-sm text-gray-700 mb-1">硕士研究生(人)</label>
                                <input type="number" id="masterNum" class="w-full rounded border border-gray-300 px-3 py-2 input-box" placeholder="0" min="0" value="0">
                            </div>
                            <div>
                                <label class="block text-sm text-gray-700 mb-1">博士研究生(人)</label>
                                <input type="number" id="doctorNum" class="w-full rounded border border-gray-300 px-3 py-2 input-box" placeholder="0" min="0" value="0">
                            </div>
                        </div>
                        <div>
                            <label class="block text-sm text-gray-700 mb-1">留学生人数(人)</label>
                            <input type="number" id="studentAbroadNum" class="w-full rounded border border-gray-300 px-3 py-2 input-box" placeholder="0" min="0" value="0">
                        </div>
                        <div class="grid grid-cols-2 gap-3">
                            <div>
                                <label class="block text-sm text-gray-700 mb-1">外籍教师人数(人)</label>
                                <input type="number" id="foreignTeacherNum" class="w-full rounded border border-gray-300 px-3 py-2 input-box" placeholder="0" min="0" value="0">
                            </div>
                            <div>
                                <label class="block text-sm text-gray-700 mb-1">继续教育人员(人)</label>
                                <input type="number" id="continueEduNum" class="w-full rounded border border-gray-300 px-3 py-2 input-box" placeholder="0" min="0" value="0">
                            </div>
                        </div>
                        <div class="grid grid-cols-2 gap-3">
                            <div>
                                <label class="block text-sm text-gray-700 mb-1">理工类专职科研(人)</label>
                                <input type="number" id="scientificScienceNum" class="w-full rounded border border-gray-300 px-3 py-2 input-box" placeholder="0" min="0" value="0">
                            </div>
                            <div>
                                <label class="block text-sm text-gray-700 mb-1">文法类专职科研(人)</label>
                                <input type="number" id="scientificArtNum" class="w-full rounded border border-gray-300 px-3 py-2 input-box" placeholder="0" min="0" value="0">
                            </div>
                        </div>
                    </div>
                </div>

                <!-- 操作按钮 -->
                <div class="flex gap-3 flex-wrap">
                    <button id="calcBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded font-medium flex-1">一键测算</button>
                    <button id="resetBtn" class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-4 py-2 rounded font-medium">重置</button>
                    <button id="exampleBtn" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded font-medium">加载示例</button>
                </div>

                <!-- 规则说明 -->
                <div class="mt-4 text-sm text-gray-500">
                    <p>?? 测算规则严格遵循建标〔2018〕32号文件,规模介于分档之间自动执行线性插值法计算</p>
                </div>
            </div>

            <!-- 规则说明卡片 -->
            <div class="card bg-white rounded-lg p-5">
                <h2 class="text-lg font-bold text-gray-800 border-b pb-3 mb-4">国标核心规则说明</h2>
                <div class="text-sm text-gray-700 space-y-2">
                    <p>1. 必配校舍:教室、实验实习用房、图书馆、室内体育用房、行政办公用房等共12项,为强制配置项</p>
                    <p>2. 规模分档:一般院校5000/10000/20000人,体育院校3000/5000/8000人,艺术院校2000/5000/8000人</p>
                    <p>3. 研究生补助:仅对实验研究、图书馆、学生宿舍3项进行面积补助,分学科匹配对应指标</p>
                    <p>4. 留学生用房:需扣除已在本科生基础面积中核算的宿舍、食堂面积,避免重复计算</p>
                </div>
            </div>
        </div>

        <!-- 右侧结果区 -->
        <div class="lg:col-span-7">
            <div class="card bg-white rounded-lg p-5 min-h-[600px]">
                <div class="flex justify-between items-center border-b pb-3 mb-4">
                    <h2 class="text-lg font-bold text-gray-800">测算结果明细</h2>
                    <div class="flex gap-2">
                        <button id="copyBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded text-sm" disabled>复制结果</button>
                        <button id="exportBtn" class="bg-green-600 hover:bg-green-700 text-white px-3 py-1 rounded text-sm" disabled>导出Excel</button>
                    </div>
                </div>

                <!-- 结果展示区 -->
                <div id="resultEmpty" class="h-[500px] flex flex-col items-center justify-center text-gray-400">
                    <svg class="w-16 h-16 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 14h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
                    </svg>
                    <p>请输入基础信息,点击【一键测算】查看结果</p>
                </div>

                <div id="resultPanel" class="hidden">
                    <!-- 汇总信息 -->
                    <div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-5">
                        <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
                            <div>
                                <p class="text-sm text-gray-600">本科生办学规模</p>
                                <p id="resultScale" class="text-xl font-bold text-gray-800">-</p>
                            </div>
                            <div>
                                <p class="text-sm text-gray-600">必配12项总面积</p>
                                <p id="resultBaseArea" class="text-xl font-bold text-blue-700">-</p>
                            </div>
                            <div>
                                <p class="text-sm text-gray-600">附加用房总面积</p>
                                <p id="resultExtraArea" class="text-xl font-bold text-orange-600">-</p>
                            </div>
                            <div>
                                <p class="text-sm text-gray-600">校舍总建筑面积</p>
                                <p id="resultTotalArea" class="text-xl font-bold text-red-600">-</p>
                            </div>
                        </div>
                    </div>

                    <!-- 明细表格 -->
                    <div class="overflow-x-auto">
                        <h3 class="font-bold text-gray-800 mb-2">1. 必配12项校舍建筑面积明细</h3>
                        <table class="w-full border-collapse border border-gray-300 text-sm mb-6">
                            <thead>
                                <tr class="bg-gray-100">
                                    <th class="border border-gray-300 px-3 py-2 text-left">序号</th>
                                    <th class="border border-gray-300 px-3 py-2 text-left">用房名称</th>
                                    <th class="border border-gray-300 px-3 py-2 text-right">生均指标(m2/生)</th>
                                    <th class="border border-gray-300 px-3 py-2 text-right">总面积(m2)</th>
                                </tr>
                            </thead>
                            <tbody id="baseTableBody">
                                <!-- 动态生成 -->
                            </tbody>
                        </table>

                        <h3 class="font-bold text-gray-800 mb-2">2. 附加用房建筑面积明细</h3>
                        <table class="w-full border-collapse border border-gray-300 text-sm mb-6">
                            <thead>
                                <tr class="bg-gray-100">
                                    <th class="border border-gray-300 px-3 py-2 text-left">序号</th>
                                    <th class="border border-gray-300 px-3 py-2 text-left">用房名称</th>
                                    <th class="border border-gray-300 px-3 py-2 text-right">总面积(m2)</th>
                                    <th class="border border-gray-300 px-3 py-2 text-left">计算依据</th>
                                </tr>
                            </thead>
                            <tbody id="extraTableBody">
                                <!-- 动态生成 -->
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </main>

    <script>
        // ========== 国标核心指标数据(100%来自建标〔2018〕32号) ==========
        // 院校分档规模配置
        const schoolScaleConfig = {
            comprehensive1: [5000, 10000, 20000],
            comprehensive2: [5000, 10000, 20000],
            normal: [5000, 10000, 20000],
            science: [5000, 10000, 20000],
            agriculture: [5000, 10000, 20000],
            medical: [5000, 10000, 20000],
            finance: [5000, 10000, 20000],
            foreign: [5000, 10000, 20000],
            sports: [3000, 5000, 8000],
            art: [2000, 5000, 8000]
        };

        // 12项必配校舍生均指标(附录A)
        const baseQuotaConfig = {
            comprehensive1: {
                name: "综合大学(文法为主)",
                scales: [5000, 10000, 20000],
                items: [
                    { name: "1. 教室", quota: [2.83, 2.83, 2.83] },
                    { name: "2. 实验实习用房", quota: [5.43, 4.63, 4.00] },
                    { name: "3. 图书馆", quota: [2.02, 1.74, 1.54] },
                    { name: "4. 室内体育用房", quota: [1.11, 1.37, 1.05] },
                    { name: "5. 校行政办公用房", quota: [0.80, 0.70, 0.60] },
                    { name: "6. 院系及教师办公用房", quota: [1.31, 1.27, 1.23] },
                    { name: "7. 师生活动用房", quota: [0.40, 0.35, 0.30] },
                    { name: "8. 会堂", quota: [0.36, 0.30, 0.24] },
                    { name: "9. 学生宿舍(公寓)", quota: [10.00, 10.00, 10.00] },
                    { name: "10. 食堂", quota: [1.30, 1.25, 1.20] },
                    { name: "11. 单身教师宿舍(公寓)", quota: [0.50, 0.40, 0.40] },
                    { name: "12. 后勤及附属用房", quota: [1.94, 1.77, 1.57] }
                ]
            },
            comprehensive2: {
                name: "综合大学(理工为主)",
                scales: [5000, 10000, 20000],
                items: [
                    { name: "1. 教室", quota: [2.88, 2.88, 2.88] },
                    { name: "2. 实验实习用房", quota: [6.75, 5.76, 5.02] },
                    { name: "3. 图书馆", quota: [2.00, 1.71, 1.50] },
                    { name: "4. 室内体育用房", quota: [1.11, 1.37, 1.05] },
                    { name: "5. 校行政办公用房", quota: [0.80, 0.70, 0.60] },
                    { name: "6. 院系及教师办公用房", quota: [1.31, 1.27, 1.23] },
                    { name: "7. 师生活动用房", quota: [0.40, 0.35, 0.30] },
                    { name: "8. 会堂", quota: [0.36, 0.30, 0.24] },
                    { name: "9. 学生宿舍(公寓)", quota: [10.00, 10.00, 10.00] },
                    { name: "10. 食堂", quota: [1.30, 1.25, 1.20] },
                    { name: "11. 单身教师宿舍(公寓)", quota: [0.50, 0.40, 0.40] },
                    { name: "12. 后勤及附属用房", quota: [1.94, 1.77, 1.57] }
                ]
            },
            normal: {
                name: "师范、民族院校",
                scales: [5000, 10000, 20000],
                items: [
                    { name: "1. 教室", quota: [2.88, 2.88, 2.88] },
                    { name: "2. 实验实习用房", quota: [5.66, 4.77, 4.02] },
                    { name: "3. 图书馆", quota: [2.02, 1.74, 1.54] },
                    { name: "4. 室内体育用房", quota: [1.11, 1.37, 1.05] },
                    { name: "5. 校行政办公用房", quota: [0.80, 0.70, 0.60] },
                    { name: "6. 院系及教师办公用房", quota: [1.31, 1.27, 1.23] },
                    { name: "7. 师生活动用房", quota: [0.40, 0.35, 0.30] },
                    { name: "8. 会堂", quota: [0.36, 0.30, 0.24] },
                    { name: "9. 学生宿舍(公寓)", quota: [10.00, 10.00, 10.00] },
                    { name: "10. 食堂", quota: [1.30, 1.25, 1.20] },
                    { name: "11. 单身教师宿舍(公寓)", quota: [0.50, 0.40, 0.40] },
                    { name: "12. 后勤及附属用房", quota: [1.94, 1.77, 1.57] }
                ]
            },
            science: {
                name: "理工院校",
                scales: [5000, 10000, 20000],
                items: [
                    { name: "1. 教室", quota: [2.95, 2.95, 2.95] },
                    { name: "2. 实验实习用房", quota: [7.43, 6.33, 5.56] },
                    { name: "3. 图书馆", quota: [2.00, 1.71, 1.50] },
                    { name: "4. 室内体育用房", quota: [1.11, 1.37, 1.05] },
                    { name: "5. 校行政办公用房", quota: [0.80, 0.70, 0.60] },
                    { name: "6. 院系及教师办公用房", quota: [1.31, 1.27, 1.23] },
                    { name: "7. 师生活动用房", quota: [0.40, 0.35, 0.30] },
                    { name: "8. 会堂", quota: [0.36, 0.30, 0.24] },
                    { name: "9. 学生宿舍(公寓)", quota: [10.00, 10.00, 10.00] },
                    { name: "10. 食堂", quota: [1.30, 1.25, 1.20] },
                    { name: "11. 单身教师宿舍(公寓)", quota: [0.50, 0.40, 0.40] },
                    { name: "12. 后勤及附属用房", quota: [1.94, 1.77, 1.57] }
                ]
            },
            agriculture: {
                name: "农林院校",
                scales: [5000, 10000, 20000],
                items: [
                    { name: "1. 教室", quota: [2.84, 2.84, 2.84] },
                    { name: "2. 实验实习用房", quota: [7.43, 6.33, 5.56] },
                    { name: "3. 图书馆", quota: [2.00, 1.71, 1.50] },
                    { name: "4. 室内体育用房", quota: [1.11, 1.37, 1.05] },
                    { name: "5. 校行政办公用房", quota: [0.80, 0.70, 0.60] },
                    { name: "6. 院系及教师办公用房", quota: [1.31, 1.27, 1.23] },
                    { name: "7. 师生活动用房", quota: [0.40, 0.35, 0.30] },
                    { name: "8. 会堂", quota: [0.36, 0.30, 0.24] },
                    { name: "9. 学生宿舍(公寓)", quota: [10.00, 10.00, 10.00] },
                    { name: "10. 食堂", quota: [1.30, 1.25, 1.20] },
                    { name: "11. 单身教师宿舍(公寓)", quota: [0.50, 0.40, 0.40] },
                    { name: "12. 后勤及附属用房", quota: [1.94, 1.77, 1.57] }
                ]
            },
            medical: {
                name: "医药院校",
                scales: [5000, 10000, 20000],
                items: [
                    { name: "1. 教室", quota: [2.75, 2.75, 2.75] },
                    { name: "2. 实验实习用房", quota: [7.40, 6.60, 6.36] },
                    { name: "3. 图书馆", quota: [2.00, 1.71, 1.50] },
                    { name: "4. 室内体育用房", quota: [1.11, 1.37, 1.05] },
                    { name: "5. 校行政办公用房", quota: [0.80, 0.70, 0.60] },
                    { name: "6. 院系及教师办公用房", quota: [1.31, 1.27, 1.23] },
                    { name: "7. 师生活动用房", quota: [0.40, 0.35, 0.30] },
                    { name: "8. 会堂", quota: [0.36, 0.30, 0.24] },
                    { name: "9. 学生宿舍(公寓)", quota: [10.00, 10.00, 10.00] },
                    { name: "10. 食堂", quota: [1.30, 1.25, 1.20] },
                    { name: "11. 单身教师宿舍(公寓)", quota: [0.50, 0.40, 0.40] },
                    { name: "12. 后勤及附属用房", quota: [1.94, 1.77, 1.57] }
                ]
            },
            finance: {
                name: "财经、政法院校",
                scales: [5000, 10000, 20000],
                items: [
                    { name: "1. 教室", quota: [2.66, 2.66, 2.66] },
                    { name: "2. 实验实习用房", quota: [1.54, 1.26, 1.01] },
                    { name: "3. 图书馆", quota: [2.02, 1.74, 1.54] },
                    { name: "4. 室内体育用房", quota: [1.11, 1.37, 1.05] },
                    { name: "5. 校行政办公用房", quota: [0.80, 0.70, 0.60] },
                    { name: "6. 院系及教师办公用房", quota: [1.31, 1.27, 1.23] },
                    { name: "7. 师生活动用房", quota: [0.40, 0.35, 0.30] },
                    { name: "8. 会堂", quota: [0.36, 0.30, 0.24] },
                    { name: "9. 学生宿舍(公寓)", quota: [10.00, 10.00, 10.00] },
                    { name: "10. 食堂", quota: [1.30, 1.25, 1.20] },
                    { name: "11. 单身教师宿舍(公寓)", quota: [0.50, 0.40, 0.40] },
                    { name: "12. 后勤及附属用房", quota: [1.94, 1.77, 1.57] }
                ]
            },
            foreign: {
                name: "外语院校",
                scales: [5000, 10000, 20000],
                items: [
                    { name: "1. 教室", quota: [3.30, 3.30, 3.30] },
                    { name: "2. 实验实习用房", quota: [1.54, 1.26, 1.01] },
                    { name: "3. 图书馆", quota: [2.02, 1.74, 1.54] },
                    { name: "4. 室内体育用房", quota: [1.11, 1.37, 1.05] },
                    { name: "5. 校行政办公用房", quota: [0.80, 0.70, 0.60] },
                    { name: "6. 院系及教师办公用房", quota: [1.31, 1.27, 1.23] },
                    { name: "7. 师生活动用房", quota: [0.40, 0.35, 0.30] },
                    { name: "8. 会堂", quota: [0.36, 0.30, 0.24] },
                    { name: "9. 学生宿舍(公寓)", quota: [10.00, 10.00, 10.00] },
                    { name: "10. 食堂", quota: [1.30, 1.25, 1.20] },
                    { name: "11. 单身教师宿舍(公寓)", quota: [0.50, 0.40, 0.40] },
                    { name: "12. 后勤及附属用房", quota: [1.94, 1.77, 1.57] }
                ]
            },
            sports: {
                name: "体育院校",
                scales: [3000, 5000, 8000],
                items: [
                    { name: "1. 教室", quota: [1.85, 1.85, 1.85] },
                    { name: "2. 实验实习用房", quota: [1.78, 1.59, 1.36] },
                    { name: "3. 图书馆", quota: [1.93, 1.77, 1.62] },
                    { name: "4. 室内体育用房", quota: [11.04, 10.04, 9.12] },
                    { name: "5. 校行政办公用房", quota: [0.95, 0.80, 0.75] },
                    { name: "6. 院系及教师办公用房", quota: [1.34, 1.31, 1.28] },
                    { name: "7. 师生活动用房", quota: [0.45, 0.40, 0.37] },
                    { name: "8. 会堂", quota: [0.48, 0.36, 0.30] },
                    { name: "9. 学生宿舍(公寓)", quota: [10.00, 10.00, 10.00] },
                    { name: "10. 食堂", quota: [1.35, 1.30, 1.27] },
                    { name: "11. 单身教师宿舍(公寓)", quota: [0.50, 0.50, 0.45] },
                    { name: "12. 后勤及附属用房", quota: [2.28, 1.94, 1.84] }
                ]
            },
            art: {
                name: "艺术院校",
                scales: [2000, 5000, 8000],
                items: [
                    { name: "1. 教室", quota: [10.28, 10.28, 10.28] },
                    { name: "2. 实验实习用房", quota: [10.60, 7.77, 6.91] },
                    { name: "3. 图书馆", quota: [2.50, 2.10, 2.00] },
                    { name: "4. 室内体育用房", quota: [1.14, 1.11, 1.09] },
                    { name: "5. 校行政办公用房", quota: [1.00, 0.80, 0.75] },
                    { name: "6. 院系及教师办公用房", quota: [1.90, 1.70, 1.60] },
                    { name: "7. 师生活动用房", quota: [0.50, 0.40, 0.37] },
                    { name: "8. 会堂", quota: [0.48, 0.36, 0.30] },
                    { name: "9. 学生宿舍(公寓)", quota: [10.00, 10.00, 10.00] },
                    { name: "10. 食堂", quota: [1.40, 1.30, 1.27] },
                    { name: "11. 单身教师宿舍(公寓)", quota: [0.50, 0.50, 0.45] },
                    { name: "12. 后勤及附属用房", quota: [2.50, 1.94, 1.84] }
                ]
            }
        };

        // 研究生补助指标(附录B)
        const postgraduateQuota = {
            science: { master: { lab: 6.0, library: 0.5, dorm: 5.0 }, doctor: { lab: 8.0, library: 0.5, dorm: 10.0 } },
            art: { master: { lab: 4.0, library: 0.5, dorm: 5.0 }, doctor: { lab: 6.0, library: 0.5, dorm: 10.0 } }
        };

        // 留学生指标(附录C)
        const abroadStudentQuota = [
            { num: 100, quota: 31.66 },
            { num: 200, quota: 30.00 },
            { num: 300, quota: 28.90 },
            { num: 400, quota: 28.70 },
            { num: 500, quota: 28.70 },
            { num: 1000, quota: 28.50 }
        ];

        // 外籍教师其他用房指标(附录C)
        const foreignTeacherQuota = [
            { num: 5, quota: 12.0 },
            { num: 10, quota: 8.0 },
            { num: 15, quota: 7.0 },
            { num: 20, quota: 6.0 },
            { num: 25, quota: 5.5 },
            { num: 30, quota: 5.0 }
        ];

        // 专职科研、继续教育指标(附录D)
        const scientificQuota = { science: 30, art: 16, design: 15 };
        const continueEduQuota = 12;

        // ========== 核心工具函数 ==========
        // 线性插值法计算
        function linearInterpolation(x, x1, y1, x2, y2) {
            if (x <= x1) return y1;
            if (x >= x2) return y2;
            return y1 + (y2 - y1) * (x - x1) / (x2 - x1);
        }

        // 多段插值计算
        function getInterpolatedValue(x, scaleArray, quotaArray) {
            if (scaleArray.length !== quotaArray.length) return 0;
            if (x <= scaleArray[0]) return quotaArray[0];
            if (x >= scaleArray[scaleArray.length - 1]) return quotaArray[quotaArray.length - 1];
            
            // 找到所在区间
            for (let i = 0; i < scaleArray.length - 1; i++) {
                if (x >= scaleArray[i] && x <= scaleArray[i + 1]) {
                    return linearInterpolation(x, scaleArray[i], quotaArray[i], scaleArray[i + 1], quotaArray[i + 1]);
                }
            }
            return quotaArray[quotaArray.length - 1];
        }

        // 折叠面板切换
        function togglePanel(id) {
            const panel = document.getElementById(`${id}Panel`);
            const arrow = document.getElementById(`${id}Arrow`);
            if (panel.classList.contains('hidden')) {
                panel.classList.remove('hidden');
                arrow.textContent = '▲';
            } else {
                panel.classList.add('hidden');
                arrow.textContent = '▼';
            }
        }

        // ========== 核心测算函数 ==========
        function calculate() {
            // 1. 获取输入值
            const schoolType = document.getElementById('schoolType').value;
            const undergraduateNum = parseInt(document.getElementById('undergraduateNum').value) || 0;
            const masterNum = parseInt(document.getElementById('masterNum').value) || 0;
            const doctorNum = parseInt(document.getElementById('doctorNum').value) || 0;
            const studentAbroadNum = parseInt(document.getElementById('studentAbroadNum').value) || 0;
            const foreignTeacherNum = parseInt(document.getElementById('foreignTeacherNum').value) || 0;
            const continueEduNum = parseInt(document.getElementById('continueEduNum').value) || 0;
            const scientificScienceNum = parseInt(document.getElementById('scientificScienceNum').value) || 0;
            const scientificArtNum = parseInt(document.getElementById('scientificArtNum').value) || 0;

            // 输入校验
            if (undergraduateNum <= 0) {
                alert('请输入有效的本科生办学规模!');
                return;
            }

            // 2. 获取当前院校的指标配置
            const currentConfig = baseQuotaConfig[schoolType];
            if (!currentConfig) {
                alert('院校类别配置错误!');
                return;
            }

            // 3. 计算必配12项校舍面积
            let baseTotalArea = 0;
            let baseTableData = [];
            currentConfig.items.forEach(item => {
                const perQuota = getInterpolatedValue(undergraduateNum, currentConfig.scales, item.quota);
                const totalArea = perQuota * undergraduateNum;
                baseTotalArea += totalArea;
                baseTableData.push({
                    name: item.name,
                    perQuota: perQuota.toFixed(2),
                    totalArea: totalArea.toFixed(2)
                });
            });

            // 4. 计算附加用房面积
            let extraTableData = [];
            let extraTotalArea = 0;

            // 4.1 研究生补助面积
            if (masterNum > 0 || doctorNum > 0) {
                // 按院校类别区分学科类型
                const isScienceType = ['comprehensive2', 'science', 'agriculture', 'medical', 'sports'].includes(schoolType);
                const quotaType = isScienceType ? 'science' : 'art';
                
                const masterLabArea = masterNum * postgraduateQuota[quotaType].master.lab;
                const masterLibArea = masterNum * postgraduateQuota[quotaType].master.library;
                const masterDormArea = masterNum * postgraduateQuota[quotaType].master.dorm;
                const doctorLabArea = doctorNum * postgraduateQuota[quotaType].doctor.lab;
                const doctorLibArea = doctorNum * postgraduateQuota[quotaType].doctor.library;
                const doctorDormArea = doctorNum * postgraduateQuota[quotaType].doctor.dorm;
                
                const postgraduateTotal = masterLabArea + masterLibArea + masterDormArea + doctorLabArea + doctorLibArea + doctorDormArea;
                extraTotalArea += postgraduateTotal;
                extraTableData.push({
                    name: "研究生补助用房",
                    area: postgraduateTotal.toFixed(2),
                    remark: `硕士${masterNum}人+博士${doctorNum}人,含实验、图书馆、宿舍补助`
                });
            }

            // 4.2 留学生生活用房
            if (studentAbroadNum > 0) {
                const perQuota = getInterpolatedValue(studentAbroadNum, abroadStudentQuota.map(i => i.num), abroadStudentQuota.map(i => i.quota));
                // 扣除已计算的宿舍和食堂面积
                const dormPerQuota = getInterpolatedValue(undergraduateNum, currentConfig.scales, currentConfig.items.find(i => i.name.includes("学生宿舍")).quota);
                const canteenPerQuota = getInterpolatedValue(undergraduateNum, currentConfig.scales, currentConfig.items.find(i => i.name.includes("食堂")).quota);
                const actualPerQuota = perQuota - dormPerQuota - canteenPerQuota;
                const abroadTotal = actualPerQuota * studentAbroadNum;
                
                extraTotalArea += abroadTotal;
                extraTableData.push({
                    name: "留学生生活用房",
                    area: abroadTotal.toFixed(2),
                    remark: `留学生${studentAbroadNum}人,已扣除重复计算的宿舍、食堂面积`
                });
            }

            // 4.3 外籍教师用房
            if (foreignTeacherNum > 0) {
                // 按平均一室户55㎡计算,附加其他用房
                const houseArea = foreignTeacherNum * 55;
                const otherPerQuota = getInterpolatedValue(foreignTeacherNum, foreignTeacherQuota.map(i => i.num), foreignTeacherQuota.map(i => i.quota));
                const otherArea = otherPerQuota * foreignTeacherNum;
                const teacherTotal = houseArea + otherArea;
                
                extraTotalArea += teacherTotal;
                extraTableData.push({
                    name: "外籍教师生活用房",
                    area: teacherTotal.toFixed(2),
                    remark: `外籍教师${foreignTeacherNum}人,含住宅及配套用房`
                });
            }

            // 4.4 专职科研机构用房
            if (scientificScienceNum > 0 || scientificArtNum > 0) {
                const scienceArea = scientificScienceNum * scientificQuota.science;
                const artArea = scientificArtNum * scientificQuota.art;
                const scientificTotal = scienceArea + artArea;
                
                extraTotalArea += scientificTotal;
                extraTableData.push({
                    name: "专职科研机构用房",
                    area: scientificTotal.toFixed(2),
                    remark: `理工类${scientificScienceNum}人,文法类${scientificArtNum}人`
                });
            }

            // 4.5 继续教育用房
            if (continueEduNum > 0) {
                const continueEduTotal = continueEduNum * continueEduQuota;
                extraTotalArea += continueEduTotal;
                extraTableData.push({
                    name: "继续教育用房",
                    area: continueEduTotal.toFixed(2),
                    remark: `继续教育工作人员${continueEduNum}人`
                });
            }

            // 5. 总建筑面积
            const totalArea = baseTotalArea + extraTotalArea;

            // 6. 渲染结果
            renderResult(
                undergraduateNum,
                baseTotalArea.toFixed(2),
                extraTotalArea.toFixed(2),
                totalArea.toFixed(2),
                baseTableData,
                extraTableData
            );
        }

        // ========== 结果渲染函数 ==========
        function renderResult(scale, baseArea, extraArea, totalArea, baseTableData, extraTableData) {
            // 隐藏空状态,显示结果
            document.getElementById('resultEmpty').classList.add('hidden');
            document.getElementById('resultPanel').classList.remove('hidden');
            
            // 填充汇总数据
            document.getElementById('resultScale').textContent = `${scale}人`;
            document.getElementById('resultBaseArea').textContent = `${Number(baseArea).toLocaleString()}㎡`;
            document.getElementById('resultExtraArea').textContent = `${Number(extraArea).toLocaleString()}㎡`;
            document.getElementById('resultTotalArea').textContent = `${Number(totalArea).toLocaleString()}㎡`;
            
            // 填充必配项表格
            const baseTableBody = document.getElementById('baseTableBody');
            baseTableBody.innerHTML = '';
            baseTableData.forEach((item, index) => {
                const tr = document.createElement('tr');
                tr.innerHTML = `
                    <td class="border border-gray-300 px-3 py-2">${index + 1}</td>
                    <td class="border border-gray-300 px-3 py-2">${item.name}</td>
                    <td class="border border-gray-300 px-3 py-2 text-right">${item.perQuota}</td>
                    <td class="border border-gray-300 px-3 py-2 text-right">${Number(item.totalArea).toLocaleString()}</td>
                `;
                baseTableBody.appendChild(tr);
            });

            // 填充附加项表格
            const extraTableBody = document.getElementById('extraTableBody');
            extraTableBody.innerHTML = '';
            if (extraTableData.length === 0) {
                const tr = document.createElement('tr');
                tr.innerHTML = `<td colspan="4" class="border border-gray-300 px-3 py-2 text-center text-gray-500">无附加用房配置</td>`;
                extraTableBody.appendChild(tr);
            } else {
                extraTableData.forEach((item, index) => {
                    const tr = document.createElement('tr');
                    tr.innerHTML = `
                        <td class="border border-gray-300 px-3 py-2">${index + 1}</td>
                        <td class="border border-gray-300 px-3 py-2">${item.name}</td>
                        <td class="border border-gray-300 px-3 py-2 text-right">${Number(item.area).toLocaleString()}</td>
                        <td class="border border-gray-300 px-3 py-2">${item.remark}</td>
                    `;
                    extraTableBody.appendChild(tr);
                });
            }

            // 启用复制和导出按钮
            document.getElementById('copyBtn').disabled = false;
            document.getElementById('exportBtn').disabled = false;
        }

        // ========== 辅助功能函数 ==========
        // 重置表单
        function resetForm() {
            document.getElementById('undergraduateNum').value = '';
            document.getElementById('masterNum').value = '0';
            document.getElementById('doctorNum').value = '0';
            document.getElementById('studentAbroadNum').value = '0';
            document.getElementById('foreignTeacherNum').value = '0';
            document.getElementById('continueEduNum').value = '0';
            document.getElementById('scientificScienceNum').value = '0';
            document.getElementById('scientificArtNum').value = '0';
            document.getElementById('schoolType').value = 'comprehensive1';
            
            document.getElementById('resultEmpty').classList.remove('hidden');
            document.getElementById('resultPanel').classList.add('hidden');
            document.getElementById('copyBtn').disabled = true;
            document.getElementById('exportBtn').disabled = true;
        }

        // 加载官方示例(文档实例一:理工院校15000人)
        function loadExample() {
            document.getElementById('schoolType').value = 'science';
            document.getElementById('undergraduateNum').value = '15000';
            document.getElementById('masterNum').value = '1100';
            document.getElementById('doctorNum').value = '400';
            document.getElementById('studentAbroadNum').value = '500';
            document.getElementById('foreignTeacherNum').value = '20';
            document.getElementById('continueEduNum').value = '20';
            document.getElementById('scientificScienceNum').value = '30';
            document.getElementById('scientificArtNum').value = '0';
            alert('已加载官方示例数据,点击【一键测算】即可查看结果,与文档实例结果一致');
        }

        // 复制结果
        function copyResult() {
            const scale = document.getElementById('resultScale').textContent;
            const baseArea = document.getElementById('resultBaseArea').textContent;
            const extraArea = document.getElementById('resultExtraArea').textContent;
            const totalArea = document.getElementById('resultTotalArea').textContent;
            
            let copyText = `普通高等学校建筑面积测算结果\n`;
            copyText += `================================\n`;
            copyText += `本科生办学规模:${scale}\n`;
            copyText += `必配12项总面积:${baseArea}\n`;
            copyText += `附加用房总面积:${extraArea}\n`;
            copyText += `校舍总建筑面积:${totalArea}\n`;
            copyText += `================================\n`;
            copyText += `测算依据:建标〔2018〕32号 普通高等学校建筑面积指标\n`;
            
            navigator.clipboard.writeText(copyText).then(() => {
                alert('测算结果已复制到剪贴板!');
            }).catch(() => {
                alert('复制失败,请手动复制!');
            });
        }

        // 导出Excel
        function exportExcel() {
            // 构造Excel数据
            const wb = XLSX.utils.book_new();
            
            // 汇总表
            const summaryData = [
                ["项目", "数值", "备注"],
                ["本科生办学规模", document.getElementById('resultScale').textContent, ""],
                ["必配12项总面积", document.getElementById('resultBaseArea').textContent, ""],
                ["附加用房总面积", document.getElementById('resultExtraArea').textContent, ""],
                ["校舍总建筑面积", document.getElementById('resultTotalArea').textContent, ""],
                ["", "", ""],
                ["测算依据", "建标〔2018〕32号 普通高等学校建筑面积指标", ""]
            ];
            const summaryWs = XLSX.utils.aoa_to_sheet(summaryData);
            XLSX.utils.book_append_sheet(wb, summaryWs, "测算汇总");
            
            // 必配项明细表
            const baseTableData = [];
            baseTableData.push(["序号", "用房名称", "生均指标(m2/生)", "总面积(m2)"]);
            document.querySelectorAll('#baseTableBody tr').forEach(tr => {
                const tds = tr.querySelectorAll('td');
                baseTableData.push([
                    tds[0].textContent,
                    tds[1].textContent,
                    tds[2].textContent,
                    tds[3].textContent
                ]);
            });
            const baseWs = XLSX.utils.aoa_to_sheet(baseTableData);
            XLSX.utils.book_append_sheet(wb, baseWs, "必配12项明细");
            
            // 附加项明细表
            const extraTableData = [];
            extraTableData.push(["序号", "用房名称", "总面积(m2)", "计算依据"]);
            document.querySelectorAll('#extraTableBody tr').forEach(tr => {
                const tds = tr.querySelectorAll('td');
                extraTableData.push([
                    tds[0].textContent,
                    tds[1].textContent,
                    tds[2].textContent,
                    tds[3].textContent
                ]);
            });
            const extraWs = XLSX.utils.aoa_to_sheet(extraTableData);
            XLSX.utils.book_append_sheet(wb, extraWs, "附加用房明细");
            
            // 导出文件
            XLSX.writeFile(wb, "高校建筑面积测算结果_建标2018-32号.xlsx");
        }

        // ========== 事件绑定 ==========
        document.getElementById('calcBtn').addEventListener('click', calculate);
        document.getElementById('resetBtn').addEventListener('click', resetForm);
        document.getElementById('exampleBtn').addEventListener('click', loadExample);
        document.getElementById('copyBtn').addEventListener('click', copyResult);
        document.getElementById('exportBtn').addEventListener('click', exportExcel);
    </script>
</body>
</html>
        
编辑器加载中
预览
控制台