<!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>
index.html
style.css
index.js
md
README.md
index.html