图:
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>装机配置单</title>
<script src="https://cdn.sheetjs.com/xlsx-0.19.3/package/dist/xlsx.full.min.js"></script>
<style>
table {
width: 100%;
border-collapse: collapse;
}
th, td {
border: 1px solid #ccc;
padding: 4px;
text-align: left;
}
th {
background-color: #f0f0f0;
}
th:nth-child(2), td:nth-child(2) {
width: 80px;
min-width: 60px;
max-width: 120px;
}
th:nth-child(3), td:nth-child(3),
th:nth-child(7), td:nth-child(7) {
width: 280px;
min-width: 160px;
max-width: 380px;
}
.total-row {
background-color: #e6f3ff;
}
input {
width: 90%;
padding: 4px;
border: 1px solid #ddd;
}
.calculate-btn {
margin: 20px 0;
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
}
.calculate-btn:hover {
background-color: #45a049;
}
.export-btn {
background-color: #2196F3;
}
.export-btn:hover {
background-color: #1976D2;
}
.delete-btn {
background-color: #f44336;
color: white;
border: none;
padding: 4px 8px;
cursor: pointer;
margin-right: 5px;
}
.delete-btn:hover {
background-color: #da190b;
}
@media print {
.calculate-btn, .delete-btn {
display: none;
}
body {
margin: 0;
padding: 20px;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
border: 1px solid #000;
padding: 8px;
}
input {
border: none;
width: 100%;
padding: 0;
}
thead {
display: table-header-group;
}
tr {
page-break-inside: avoid;
}
.print-hide {
display: none !important;
}
}
.print-btn {
background-color: #607D8B;
}
.print-btn:hover {
background-color: #455A64;
}
select {
width: 90%;
padding: 4px;
border: 1px solid #ddd;
}
.custom-input-container {
display: none;
margin-top: 5px;
}
.custom-input-container.show {
display: block;
}
.custom-input {
width: 85%;
padding: 4px;
border: 1px solid #ddd;
}
.amount1[value]:not([value="0"]):not([value=""]),
.amount2[value]:not([value="0"]):not([value=""]) {
color: red;
}
.second-column {
display: none;
}
.show-second-column .second-column {
display: table-cell;
}
</style>
<style>
@page {
margin: 0;
}
</style>
</head>
<body>
<h2>1.装机配置单</h2>
<p>日期: <input type="date" id="date"></p>
<div id="topTotalInfo" style="font-weight:bold;margin-bottom:10px;"></div>
<p class="print-hide">
<label>
<input type="checkbox" id="showSecondColumn" onchange="toggleSecondColumn()">
显示第二列配置
</label>
</p>
<table id="configTable">
<tr>
<th>操作</th>
<th>配件名称</th>
<th>品牌型号[1]</th>
<th>数量[1]</th>
<th>单价[1]</th>
<th>金额[1]</th>
<th class="second-column">品牌型号[2]</th>
<th class="second-column">数量[2]</th>
<th class="second-column">单价[2]</th>
<th class="second-column">金额[2]</th>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="CPU"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="主板"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="内存"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="硬盘"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="SSD固态盘"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="显卡"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="机箱"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="电源"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="显示器"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="键鼠套装"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="键盘"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="鼠标"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="散热器"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="音箱"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr>
<td><button class="delete-btn" onclick="deleteRow(this)">删除</button></td>
<td><input type="text" value="光存储"></td>
<td><input type="text" value=""></td>
<td><input type="number" class="qty1" value="0"></td>
<td><input type="number" class="price1" value="0"></td>
<td><input type="number" class="amount1" readonly></td>
<td class="second-column"><input type="text" value=""></td>
<td class="second-column"><input type="number" class="qty2" value="0"></td>
<td class="second-column"><input type="number" class="price2" value="0"></td>
<td class="second-column"><input type="number" class="amount2" readonly></td>
</tr>
<tr class="total-row">
<td colspan="3">数量[1]:<span id="qty_total1">0</span></td>
<td colspan="2"></td>
<td>合计[1]:<span id="total1">0</span></td>
<td class="second-column" colspan="2">数量[2]:<span id="qty_total2">0</span></td>
<td class="second-column"></td>
<td class="second-column">合计[2]:<span id="total2">0</span></td>
</tr>
</table>
<button class="calculate-btn export-btn" onclick="exportToExcel()">导出Excel</button>
<button class="calculate-btn print-btn" onclick="printTable()">打印表格</button>
<button class="calculate-btn" onclick="showInfoModal()">显示信息</button>
<button class="calculate-btn" onclick="showInfoModalNoPrice()">显示信息(无单价金额)</button>
<button class="calculate-btn" onclick="showInfoModalOrder()">显示信息(订货用)</button>
<button class="calculate-btn print-btn" onclick="printTableNoPrice()">打印(无单价金额)</button>
<!-- 信息弹窗 -->
<div id="infoModal" style="display:none;position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(0,0,0,0.3);z-index:9999;align-items:center;justify-content:center;">
<div style="background:#fff;padding:20px;border-radius:8px;min-width:600px;max-width:1000px;max-height:90vh;box-shadow:0 2px 8px #888;display:flex;flex-direction:column;align-items:stretch;">
<h3 style="margin-top:0;">配置信息</h3>
<textarea id="infoTextArea" style="width:100%;height:500px;resize:vertical;margin-bottom:12px;font-size:16px;font-family:monospace;white-space:pre;overflow:auto;" wrap="off"></textarea>
<button onclick="closeInfoModal()" style="align-self:flex-end;padding:6px 18px;">关闭</button>
</div>
</div>
<script>
// 扩展配件选项数组,包含所有配件
const partOptions = {
CPU: {
option1: [
{model: "I3 8100", price: 150},
{model: "I3 12100", price: 620},
{model: "I3 12100F", price: 470},
{model: "I3 13100", price: 625},
{model: "I3 13100F", price: 305},
{model: "I5 12400", price: 835},
{model: "I5 12400F", price: 645},
{model: "I5 13400F", price: 905},
{model: "i7-13700F散片", price: 1610},
{model: "i7-13700散片", price: 2050},
{model: "I5 12600KF散片", price: 935},
{model: "AMD锐龙 R5 5600", price: 545},
{model: "R5 7500F散片", price: 875},
{model: "R5 9600X散片", price: 1270},
{model: "其他", price: 0}
],
option2: [
{model: "Intel酷睿 i3-10100", price: 520},
{model: "Intel酷睿 i3-12100", price: 585},
{model: "Intel酷睿 i5-12400", price: 735},
{model: "其他", price: 0}
]
},
主板: {
option1: [
{model: "昂达94D-DVH", price: 295},
{model: "技嘉H610M", price: 465},
{model: "技嘉B760M-H D4", price: 515},
{model: "技嘉B760M D2H DDR4", price: 550},
{model: "技嘉 A520M K V2 D4", price: 310},
{model: "铭瑄MS 挑战者B760M D4", price: 520},
{model: "铭瑄MS 挑战者B650M WIFI D5", price: 590},
{model: "铭瑄MS B550M GAMING WIFI ICE D4", price: 590},
{model: "技嘉B650M H", price: 550},
{model: "铭瑄H610M-R", price: 335},
{model: "昂达H610E-V D4", price: 295},
{model: "其他", price: 0}
],
option2: [
{model: "华硕PRIME H610M-K(HDMI)", price: 499},
{model: "其他", price: 0}
]
},
内存: {
option1: [
{model: "金百达 3200 DDR4 16G 银", price: 545},
{model: "金百达 DDR4 3600 16G 银", price: 235},
{model: "阿斯加特 弗雷 3200 DDR4 16G", price: 195},
{model: "金百达DDR5-16G6000银爵马甲", price: 365},
{model: "金百达DDR5-32G6000银爵马甲", price: 670},
{model: "其他", price: 0}
],
option2: [
{model: "威刚DDR4 16GB", price: 565},
{model: "其他", price: 0}
]
},
硬盘: {
option1: [
{model: "西部数据蓝盘 1TB", price: 585},
{model: "西部数据蓝盘 2TB", price: 595},
{model: "其他", price: 0}
],
option2: [
{model: "西部数据蓝盘 2TB", price: 595},
{model: "其他", price: 0}
]
},
"SSD固态盘": {
option1: [
{model: "金士顿 m.2 512G", price: 450},
{model: "西数SN5000 1TB M.2", price: 470},
{model: "昂达M.2 512G", price: 360},
{model: "梵想 SSD 512G", price: 360},
{model: "威刚 m.2 512G", price: 425},
{model: "其他", price: 0}
],
option2: [
{model: "闪迪 240 SSD", price: 159},
{model: "其他", price: 0}
]
},
显卡: {
option1: [
{model: "七彩虹GT1030", price: 459},
{model: "铭瑄MS GTX1050TI TF 4G单风扇", price: 815},
{model: "七彩虹战斧RTX3060 DUO 12GD6", price: 1880},
{model: "七彩虹战斧3050 DUO 8G", price: 1380},
{model: "七彩虹GT730 2G", price: 295},
{model: "七彩虹GT730 4G", price: 315},
{model: "昂达GT730 2G", price: 265},
{model: "铭瑄R5 220 1G", price: 125},
{model: "铭瑄MS GTX1050TI TF 4G单风扇", price: 815},
{model: "七彩虹RTX 5060 8G", price: 2480},
{model: "其他", price: 0}
],
option2: [
{model: "七彩虹GT1030", price: 459},
{model: "其他", price: 0}
]
},
机箱: {
option1: [
{model: "金河田 游戏机箱", price: 95},
{model: "金河田 觉醒侧透", price: 90},
{model: "金刚之星", price: 75},
{model: "华硕机箱", price: 95},
{model: "小机箱", price: 40},
{model: "其他", price: 0}
],
option2: [
{model: "金河田", price: 75},
{model: "其他", price: 0}
]
},
电源: {
option1: [
{model: "长城300W", price: 115},
{model: "航嘉300W", price: 115},
{model: "航嘉ECO650 650W", price: 230},
{model: "航嘉ECO500 500W", price: 210},
{model: "OX 300W", price: 70},
{model: "其他", price: 0}
],
option2: [
{model: "300W电源", price: 75},
{model: "其他", price: 0}
]
},
显示器: {
option1: [
{model: "联想 27寸 显示器", price: 485},
{model: "联想 24寸 显示器", price: 385},
{model: "AOC 24寸 显示器", price: 410},
{model: "AOC aoc 32寸 Q32V3S/BS 显示器", price: 1185},
{model: "AOC 24寸 显示器", price: 410},
{model: "AOC 27寸 27B36H显示器", price: 515},
{model: "其他", price: 0}
],
option2: [
{model: "联想 24寸 显示器", price: 599},
{model: "其他", price: 0}
]
},
键鼠套装: {
option1: [
{model: "双飞燕键鼠套装", price: 75},
{model: "普通键鼠套装", price: 30},
{model: "DT键鼠套装", price: 38},
{model: "游戏无线键鼠套装", price: 125},
{model: "其他", price: 0}
],
option2: [
{model: "无线光电套装", price: 69},
{model: "其他", price: 0}
]
},
键盘: {
option1: [
{model: "双飞燕键盘", price: 25},
{model: "其他", price: 0}
],
option2: [
{model: "无线键盘", price: 49},
{model: "其他", price: 0}
]
},
鼠标: {
option1: [
{model: "双飞燕鼠标", price: 15},
{model: "其他", price: 0}
],
option2: [
{model: "无线鼠标", price: 29},
{model: "其他", price: 0}
]
},
散热器: {
option1: [
{model: "超频三散热器", price: 15},
{model: "双铜管散热器", price: 21},
{model: "四铜管散热器", price: 37},
{model: "六铜管散热器", price: 65},
{model: "大水牛寒战240 水冷散热器", price: 159},
{model: "大水牛寒战120 水冷散热器", price: 109},
{model: "六铜管散热器", price: 65},
{model: "利民AE240 双排水冷 工包", price: 180},
{model: "其他", price: 0}
],
option2: [
{model: "九州风神散热器", price: 39},
{model: "其他", price: 0}
]
},
音箱: {
option1: [
{model: "普通音箱", price: 30},
{model: "小音箱", price: 15},
{model: "其他", price: 0}
],
option2: [
{model: "无线网卡", price: 39},
{model: "其他", price: 0}
]
},
光存储: {
option1: [
{model: "DVD刻录机", price: 100},
{model: "DVD光驱", price: 90},
{model: "其他", price: 0}
],
option2: [
{model: "DVD刻录机", price: 100},
{model: "其他", price: 0}
]
}
};
// 处理型号选择变化 - 修改为选择型号后自动设置数量为1
function handleModelChange(select, priceClass) {
const selectedOption = select.options[select.selectedIndex];
const row = select.closest('tr');
const priceInput = row.querySelector('.' + priceClass);
const customContainer = select.parentNode.querySelector('.custom-input-container');
// 获取对应的数量输入框
const qtyClass = priceClass === 'price1' ? 'qty1' : 'qty2';
const qtyInput = row.querySelector('.' + qtyClass);
if (selectedOption.value === "其他") {
customContainer.classList.add('show');
priceInput.value = 0;
// 如果是"其他"选项,不自动设置数量
} else {
if (customContainer) {
customContainer.classList.remove('show');
}
if (selectedOption.dataset.price) {
priceInput.value = selectedOption.dataset.price;
}
// 只有当有实际选择型号且数量为0时,才自动设置为1
if (selectedOption.value && selectedOption.value !== "" &&
(!qtyInput.value || qtyInput.value == "0")) {
qtyInput.value = 1;
}
}
calculateTotals();
}
// 自定义输入处理
function handleCustomInput(input, select) {
select.value = "其他";
// 自定义输入时,如果数量为0,自动设置为1
const row = input.closest('tr');
const isSecondColumn = input.closest('td:nth-child(7)') !== null;
const qtyClass = isSecondColumn ? 'qty2' : 'qty1';
const qtyInput = row.querySelector('.' + qtyClass);
if (input.value && (!qtyInput.value || qtyInput.value == "0")) {
qtyInput.value = 1;
}
calculateTotals();
}
// 生成选择框HTML
function createSelectHtml(options, priceClass, index) {
return `
<select onchange="handleModelChange(this, '${priceClass}')" style="width: 90%;">
<option value="" selected>请选择型号</option>
${options.map(item =>
`<option value="${item.model}" data-price="${item.price}">${item.model}</option>`
).join('')}
</select>
<div class="custom-input-container">
<input type="text"
class="custom-input"
placeholder="请输入型号"
oninput="handleCustomInput(this, this.parentNode.previousElementSibling)">
</div>
`;
}
// 初始化表格
document.addEventListener('DOMContentLoaded', function() {
const rows = document.querySelectorAll('#configTable tr:not(:first-child)');
rows.forEach(row => {
const partNameInput = row.querySelector('td:nth-child(2) input');
if (partNameInput && partOptions[partNameInput.value]) {
const td3 = row.querySelector('td:nth-child(3)');
const td7 = row.querySelector('td:nth-child(7)');
const originalModel1 = row.querySelector('td:nth-child(3) input')?.value || '';
const originalModel2 = row.querySelector('td:nth-child(7) input')?.value || '';
const originalPrice1 = row.querySelector('.price1')?.value || '0';
const originalPrice2 = row.querySelector('.price2')?.value || '0';
const originalQty1 = row.querySelector('.qty1')?.value || '0';
const originalQty2 = row.querySelector('.qty2')?.value || '0';
td3.innerHTML = createSelectHtml(partOptions[partNameInput.value].option1, 'price1', 1);
td7.innerHTML = createSelectHtml(partOptions[partNameInput.value].option2, 'price2', 2);
// 设置第一列的值
const select1 = td3.querySelector('select');
if (select1) {
const option1 = Array.from(select1.options).find(opt => opt.value === originalModel1);
if (option1) {
select1.value = originalModel1;
// 如果原来有值,恢复原来的数量
if (originalModel1 && originalQty1 !== undefined) {
row.querySelector('.qty1').value = originalQty1;
}
} else if (originalModel1) {
select1.value = "其他";
const customInput1 = td3.querySelector('.custom-input');
customInput1.value = originalModel1;
td3.querySelector('.custom-input-container').classList.add('show');
// 如果原来有值,恢复原来的数量
if (originalModel1 && originalQty1 !== undefined) {
row.querySelector('.qty1').value = originalQty1;
}
}
}
// 设置第二列的值
const select2 = td7.querySelector('select');
if (select2) {
const option2 = Array.from(select2.options).find(opt => opt.value === originalModel2);
if (option2) {
select2.value = originalModel2;
// 如果原来有值,恢复原来的数量
if (originalModel2 && originalQty2 !== undefined) {
row.querySelector('.qty2').value = originalQty2;
}
} else if (originalModel2) {
select2.value = "其他";
const customInput2 = td7.querySelector('.custom-input');
customInput2.value = originalModel2;
td7.querySelector('.custom-input-container').classList.add('show');
// 如果原来有值,恢复原来的数量
if (originalModel2 && originalQty2 !== undefined) {
row.querySelector('.qty2').value = originalQty2;
}
}
}
// 设置价格
const price1Input = row.querySelector('.price1');
const price2Input = row.querySelector('.price2');
if (price1Input) price1Input.value = originalPrice1;
if (price2Input) price2Input.value = originalPrice2;
}
});
calculateTotals();
// 页面加载时设置日期为当前系统日期
const dateInput = document.getElementById('date');
if (dateInput) {
const today = new Date();
const yyyy = today.getFullYear();
const mm = String(today.getMonth() + 1).padStart(2, '0');
const dd = String(today.getDate()).padStart(2, '0');
dateInput.value = `${yyyy}-${mm}-${dd}`;
}
// 监听数量输入框变化,允许用户手动修改数量
const table = document.getElementById('configTable');
table.addEventListener('input', function(e) {
if (e.target.matches('.qty1, .qty2, .price1, .price2')) {
calculateTotals();
}
});
});
// 更新配件选项
function updatePartOptions(select) {
const row = select.closest('tr');
const partName = select.value;
if (partOptions[partName]) {
const td3 = row.querySelector('td:nth-child(3)');
const td7 = row.querySelector('td:nth-child(7)');
td3.innerHTML = createSelectHtml(partOptions[partName].option1, 'price1', 1);
td7.innerHTML = createSelectHtml(partOptions[partName].option2, 'price2', 2);
// 重置价格和数量
row.querySelector('.price1').value = '0';
row.querySelector('.price2').value = '0';
row.querySelector('.qty1').value = '0';
row.querySelector('.qty2').value = '0';
calculateTotals();
}
}
// 修改calculateTotals函数
function calculateTotals() {
let total1 = 0;
let total2 = 0;
let qtyTotal1 = 0;
let qtyTotal2 = 0;
const rows = document.querySelectorAll('#configTable tr:not(:first-child):not(:last-child)');
rows.forEach(row => {
// 第一列计算
const qty1 = parseFloat(row.querySelector('.qty1')?.value) || 0;
const price1 = parseFloat(row.querySelector('.price1')?.value) || 0;
const amount1 = qty1 * price1;
const amount1Input = row.querySelector('.amount1');
if (amount1Input) {
amount1Input.value = amount1.toFixed(2);
amount1Input.style.color = amount1 > 0 ? 'red' : '';
total1 += amount1;
qtyTotal1 += qty1;
}
// 第二列计算
const qty2 = parseFloat(row.querySelector('.qty2')?.value) || 0;
const price2 = parseFloat(row.querySelector('.price2')?.value) || 0;
const amount2 = qty2 * price2;
const amount2Input = row.querySelector('.amount2');
if (amount2Input) {
amount2Input.value = amount2.toFixed(2);
amount2Input.style.color = amount2 > 0 ? 'red' : '';
total2 += amount2;
qtyTotal2 += qty2;
}
});
document.getElementById('total1').textContent = total1.toFixed(2);
document.getElementById('total2').textContent = total2.toFixed(2);
document.getElementById('qty_total1').textContent = qtyTotal1;
document.getElementById('qty_total2').textContent = qtyTotal2;
// 更新顶部合计信息
let topInfo = `数量[1]:${qtyTotal1}`;
topInfo += ` 合计[1]:${total1.toFixed(2)}`;
if (document.getElementById('showSecondColumn').checked) {
topInfo += ` 数量[2]:${qtyTotal2}`;
topInfo += ` 合计[2]:${total2.toFixed(2)}`;
}
document.getElementById('topTotalInfo').innerHTML = topInfo;
}
// 在删除行时也要重新计算
function deleteRow(btn) {
const row = btn.parentNode.parentNode;
if (confirm('确定要删除这一行吗?')) {
row.parentNode.removeChild(row);
calculateTotals();
}
}
// 添加切换第二列显示的函数
function toggleSecondColumn() {
const table = document.getElementById('configTable');
const showSecond = document.getElementById('showSecondColumn').checked;
if (showSecond) {
table.classList.add('show-second-column');
} else {
table.classList.remove('show-second-column');
}
}
// 修改导出Excel函数
function exportToExcel() {
calculateTotals();
const data = [];
const table = document.getElementById('configTable');
const rows = table.getElementsByTagName('tr');
const showSecond = document.getElementById('showSecondColumn').checked;
data.push(['装机配置单']);
data.push(['日期: ' + document.getElementById('date').value]);
data.push([]);
// 添加表头
const headers = [];
const headerCells = rows[0].getElementsByTagName('th');
for (let i = 1; i < headerCells.length; i++) {
if (!showSecond && i >= 6) continue; // 如果不显示第二列,跳过第二列的表头
headers.push(headerCells[i].textContent);
}
data.push(headers);
// 添加数据行
for (let i = 1; i < rows.length - 1; i++) {
const rowData = [];
const cells = rows[i].getElementsByTagName('td');
// 配件名称
rowData.push(cells[1].querySelector('input')?.value || cells[1].querySelector('select')?.value || '');
// 品牌型号[1]
const model1Select = cells[2].querySelector('select');
const model1Custom = cells[2].querySelector('.custom-input');
rowData.push(model1Select?.value === '其他' ? model1Custom?.value : model1Select?.value || '');
// 数量[1]、单价[1]、金额[1]
rowData.push(cells[3].querySelector('input')?.value || '');
rowData.push(cells[4].querySelector('input')?.value || '');
rowData.push(cells[5].querySelector('input')?.value || '');
if (showSecond) {
// 品牌型号[2]
const model2Select = cells[6].querySelector('select');
const model2Custom = cells[6].querySelector('.custom-input');
rowData.push(model2Select?.value === '其他' ? model2Custom?.value : model2Select?.value || '');
// 数量[2]、单价[2]、金额[2]
rowData.push(cells[7].querySelector('input')?.value || '');
rowData.push(cells[8].querySelector('input')?.value || '');
rowData.push(cells[9].querySelector('input')?.value || '');
}
data.push(rowData);
}
// 添加合计行
const totalRow = [
'数量[1]:' + document.getElementById('qty_total1').textContent,
'合计[1]:' + document.getElementById('total1').textContent
];
if (showSecond) {
totalRow.push('');
totalRow.push('数量[2]:' + document.getElementById('qty_total2').textContent);
totalRow.push('合计[2]:' + document.getElementById('total2').textContent);
}
data.push(totalRow);
const ws = XLSX.utils.aoa_to_sheet(data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "配置清单");
XLSX.writeFile(wb, "装机配置单_" + document.getElementById('date').value + ".xlsx");
}
// 修改打印表格的样式
function printTable() {
alert('打印前请在浏览器打印设置中关闭"页眉和页脚"选项,以获得最佳打印效果。');
calculateTotals();
// 在打印前临时保存选择框的值
const selects = document.querySelectorAll('#configTable select');
selects.forEach(select => {
if (select.value === '其他') {
const customInput = select.parentNode.querySelector('.custom-input');
if (customInput) {
select.setAttribute('data-print-value', customInput.value);
}
} else {
select.setAttribute('data-print-value', select.value);
}
});
// ====== 打印前处理:操作列变序号,删除按钮变行号 ======
const table = document.getElementById('configTable');
const headerOp = table.querySelector('tr th:first-child');
const oldHeaderOp = headerOp.textContent;
headerOp.textContent = '序号';
const rows = table.querySelectorAll('tr');
const oldBtnHtml = [];
for (let i = 1; i < rows.length - 1; i++) { // 不处理表头和合计行
const td = rows[i].querySelector('td:first-child');
oldBtnHtml[i] = td.innerHTML;
td.innerHTML = i;
}
// 添加打印样式
const style = document.createElement('style');
style.textContent = `
@media print {
.calculate-btn, .delete-btn, .custom-input-container {
display: none !important;
}
select {
border: none !important;
-webkit-appearance: none !important;
-moz-appearance: none !important;
appearance: none !important;
padding: 0 !important;
}
select::before {
content: attr(data-print-value) !important;
}
.second-column {
display: ${document.getElementById('showSecondColumn').checked ? 'table-cell' : 'none'} !important;
}
}
`;
document.head.appendChild(style);
window.print();
// 打印后还原表头和按钮
headerOp.textContent = oldHeaderOp;
for (let i = 1; i < rows.length - 1; i++) {
const td = rows[i].querySelector('td:first-child');
td.innerHTML = oldBtnHtml[i];
}
// 打印后移除样式
document.head.removeChild(style);
}
// 打印(无单价金额)
function printTableNoPrice() {
alert('打印前请在浏览器打印设置中关闭"页眉和页脚"选项,以获得最佳打印效果。');
calculateTotals();
const table = document.getElementById('configTable');
const showSecond = document.getElementById('showSecondColumn').checked;
// 在打印前临时保存选择框的值
const selects = document.querySelectorAll('#configTable select');
selects.forEach(select => {
if (select.value === '其他') {
const customInput = select.parentNode.querySelector('.custom-input');
if (customInput) {
select.setAttribute('data-print-value', customInput.value);
}
} else {
select.setAttribute('data-print-value', select.value);
}
});
// ====== 打印前处理:操作列变序号,删除按钮变行号 ======
const headerOp = table.querySelector('tr th:first-child');
const oldHeaderOp = headerOp.textContent;
headerOp.textContent = '序号';
const rows = table.querySelectorAll('tr');
const oldBtnHtml = [];
for (let i = 1; i < rows.length - 1; i++) { // 不处理表头和合计行
const td = rows[i].querySelector('td:first-child');
oldBtnHtml[i] = td.innerHTML;
td.innerHTML = i;
}
// ====== 隐藏单价和金额相关列 ======
const ths = table.querySelectorAll('th');
if (ths[4]) ths[4].style.display = 'none';
if (ths[5]) ths[5].style.display = 'none';
if (showSecond) {
if (ths[8]) ths[8].style.display = 'none';
if (ths[9]) ths[9].style.display = 'none';
}
for (let i = 1; i < rows.length; i++) {
const cells = rows[i].querySelectorAll('td');
if (cells[4]) cells[4].style.display = 'none';
if (cells[5]) cells[5].style.display = 'none';
if (showSecond) {
if (cells[8]) cells[8].style.display = 'none';
if (cells[9]) cells[9].style.display = 'none';
}
}
// ====== 添加打印样式 ======
const style = document.createElement('style');
style.textContent = `
@media print {
.calculate-btn, .delete-btn, .custom-input-container {
display: none !important;
}
select {
border: none !important;
-webkit-appearance: none !important;
-moz-appearance: none !important;
appearance: none !important;
padding: 0 !important;
}
select::before {
content: attr(data-print-value) !important;
}
.second-column {
display: ${showSecond ? 'table-cell' : 'none'} !important;
}
.total-row { display: none !important; }
}
`;
document.head.appendChild(style);
window.print();
// 打印后还原表头和按钮
headerOp.textContent = oldHeaderOp;
for (let i = 1; i < rows.length - 1; i++) {
const td = rows[i].querySelector('td:first-child');
td.innerHTML = oldBtnHtml[i];
}
// 打印后还原单价和金额列
if (ths[4]) ths[4].style.display = '';
if (ths[5]) ths[5].style.display = '';
if (showSecond) {
if (ths[8]) ths[8].style.display = '';
if (ths[9]) ths[9].style.display = '';
}
for (let i = 1; i < rows.length; i++) {
const cells = rows[i].querySelectorAll('td');
if (cells[4]) cells[4].style.display = '';
if (cells[5]) cells[5].style.display = '';
if (showSecond) {
if (cells[8]) cells[8].style.display = '';
if (cells[9]) cells[9].style.display = '';
}
}
// 打印后移除样式
document.head.removeChild(style);
}
// 显示信息弹窗
function showInfoModal() {
const table = document.getElementById('configTable');
const rows = table.getElementsByTagName('tr');
const showSecond = document.getElementById('showSecondColumn').checked;
let text = '';
text += '装机配置单\n';
text += '日期: ' + document.getElementById('date').value + '\n\n';
// 表头
let headers = ['配件名称','品牌型号[1]','数量[1]','单价[1]','金额[1]'];
if (showSecond) headers = headers.concat(['品牌型号[2]','数量[2]','单价[2]','金额[2]']);
text += headers.join('\t') + '\n';
// 数据行
for (let i = 1; i < rows.length - 1; i++) {
const cells = rows[i].getElementsByTagName('td');
let rowData = [];
// 配件名称
rowData.push(cells[1].querySelector('input')?.value || cells[1].querySelector('select')?.value || '');
// 品牌型号[1]
const model1Select = cells[2].querySelector('select');
const model1Custom = cells[2].querySelector('.custom-input');
rowData.push(model1Select?.value === '其他' ? model1Custom?.value : model1Select?.value || '');
// 数量[1]、单价[1]、金额[1]
rowData.push(cells[3].querySelector('input')?.value || '');
rowData.push(cells[4].querySelector('input')?.value || '');
rowData.push(cells[5].querySelector('input')?.value || '');
if (showSecond) {
// 品牌型号[2]
const model2Select = cells[6].querySelector('select');
const model2Custom = cells[6].querySelector('.custom-input');
rowData.push(model2Select?.value === '其他' ? model2Custom?.value : model2Select?.value || '');
// 数量[2]、单价[2]、金额[2]
rowData.push(cells[7].querySelector('input')?.value || '');
rowData.push(cells[8].querySelector('input')?.value || '');
rowData.push(cells[9].querySelector('input')?.value || '');
}
text += rowData.join('\t') + '\n';
}
// 合计行
text += '\n数量[1]:' + document.getElementById('qty_total1').textContent;
text += '\t合计[1]:' + document.getElementById('total1').textContent;
if (showSecond) {
text += '\t数量[2]:' + document.getElementById('qty_total2').textContent;
text += '\t合计[2]:' + document.getElementById('total2').textContent;
}
document.getElementById('infoTextArea').value = text;
document.getElementById('infoModal').style.display = 'flex';
}
function closeInfoModal() {
document.getElementById('infoModal').style.display = 'none';
}
// 显示信息弹窗(无单价金额)- 修改为只显示有选型且数量大于0的项目
function showInfoModalNoPrice() {
const table = document.getElementById('configTable');
const rows = table.getElementsByTagName('tr');
const showSecond = document.getElementById('showSecondColumn').checked;
let text = '';
text += '装机配置单\n';
text += '日期: ' + document.getElementById('date').value + '\n\n';
// 表头
let headers = ['配件名称','品牌型号[1]','数量[1]'];
if (showSecond) headers = headers.concat(['品牌型号[2]','数量[2]']);
text += headers.join('\t') + '\n';
// 数据行 - 只显示有型号且数量大于0的项目
for (let i = 1; i < rows.length - 1; i++) {
const cells = rows[i].getElementsByTagName('td');
let rowData = [];
// 配件名称
const partName = cells[1].querySelector('input')?.value || cells[1].querySelector('select')?.value || '';
// 获取第一列的型号和数量
const model1Select = cells[2].querySelector('select');
const model1Custom = cells[2].querySelector('.custom-input');
const model1Value = model1Select?.value === '其他' ? model1Custom?.value : model1Select?.value || '';
const qty1 = parseFloat(cells[3].querySelector('input')?.value) || 0;
// 获取第二列的型号和数量(如果显示)
let model2Value = '';
let qty2 = 0;
if (showSecond) {
const model2Select = cells[6].querySelector('select');
const model2Custom = cells[6].querySelector('.custom-input');
model2Value = model2Select?.value === '其他' ? model2Custom?.value : model2Select?.value || '';
qty2 = parseFloat(cells[7].querySelector('input')?.value) || 0;
}
// 如果两列都没有型号或数量为0,则不显示该行
if ((!model1Value || qty1 <= 0) && (!model2Value || qty2 <= 0)) {
continue;
}
// 添加配件名称
rowData.push(partName);
// 第一列数据
if (model1Value && qty1 > 0) {
rowData.push(model1Value);
rowData.push(cells[3].querySelector('input')?.value || '');
} else {
// 如果第一列没有有效数据,留空
rowData.push('');
rowData.push('');
}
// 第二列数据(如果显示)
if (showSecond) {
if (model2Value && qty2 > 0) {
rowData.push(model2Value);
rowData.push(cells[7].querySelector('input')?.value || '');
} else {
// 如果第二列没有有效数据,留空
rowData.push('');
rowData.push('');
}
}
text += rowData.join('\t') + '\n';
}
// 合计行
text += '\n数量[1]:' + document.getElementById('qty_total1').textContent;
text += '\t合计[1]:' + document.getElementById('total1').textContent;
if (showSecond) {
text += '\t数量[2]:' + document.getElementById('qty_total2').textContent;
text += '\t合计[2]:' + document.getElementById('total2').textContent;
}
document.getElementById('infoTextArea').value = text;
document.getElementById('infoModal').style.display = 'flex';
}
// 显示信息弹窗(订货用,仅品牌型号和数量)- 数量为1时不显示数量,数量大于1时才显示数量
function showInfoModalOrder() {
const table = document.getElementById('configTable');
const rows = table.getElementsByTagName('tr');
const showSecond = document.getElementById('showSecondColumn').checked;
let text = '';
text += '装机订货清单\n';
text += '日期: ' + document.getElementById('date').value + '\n\n';
// 表头
let headers = ['品牌型号[1]','数量[1]'];
if (showSecond) headers = headers.concat(['品牌型号[2]','数量[2]']);
text += headers.join('\t') + '\n';
// 数据行 - 只显示有型号且数量大于0的项目
for (let i = 1; i < rows.length - 1; i++) {
const cells = rows[i].getElementsByTagName('td');
let rowData = [];
// 获取第一列的型号和数量
const model1Select = cells[2].querySelector('select');
const model1Custom = cells[2].querySelector('.custom-input');
const model1Value = model1Select?.value === '其他' ? model1Custom?.value : model1Select?.value || '';
const qty1 = parseFloat(cells[3].querySelector('input')?.value) || 0;
// 获取第二列的型号和数量(如果显示)
let model2Value = '';
let qty2 = 0;
if (showSecond) {
const model2Select = cells[6].querySelector('select');
const model2Custom = cells[6].querySelector('.custom-input');
model2Value = model2Select?.value === '其他' ? model2Custom?.value : model2Select?.value || '';
qty2 = parseFloat(cells[7].querySelector('input')?.value) || 0;
}
// 如果两列都没有型号或数量为0,则不显示该行
if ((!model1Value || qty1 <= 0) && (!model2Value || qty2 <= 0)) {
continue;
}
// 第一列数据
if (model1Value && qty1 > 0) {
rowData.push(model1Value); // 品牌型号总是显示
// 数量:等于1时不显示(留空),大于1时才显示
if (qty1 > 1) {
rowData.push(cells[3].querySelector('input')?.value || '');
} else {
rowData.push(''); // 数量为1时不显示
}
} else {
// 如果第一列没有有效数据,留空
rowData.push('');
rowData.push('');
}
// 第二列数据(如果显示)
if (showSecond) {
if (model2Value && qty2 > 0) {
rowData.push(model2Value); // 品牌型号总是显示
// 数量:等于1时不显示(留空),大于1时才显示
if (qty2 > 1) {
rowData.push(cells[7].querySelector('input')?.value || '');
} else {
rowData.push(''); // 数量为1时不显示
}
} else {
// 如果第二列没有有效数据,留空
rowData.push('');
rowData.push('');
}
}
text += rowData.join('\t') + '\n';
}
document.getElementById('infoTextArea').value = text;
document.getElementById('infoModal').style.display = 'flex';
}
</script>
</body>
</html>
装机配置单系统说明文档
一、系统概述
这是一个基于HTML、CSS和JavaScript的装机配置单管理系统,专为电脑装机店或DIY爱好者设计。系统提供了完整的配件选择、价格计算、数据导出和打印功能。
二、主要功能
1. 核心功能
- 配件管理:预设了15种电脑配件(CPU、主板、内存、硬盘等)
- 双配置方案:支持同一配件展示两种不同配置方案
- 价格计算:自动计算各项金额和总金额
- 配件数据库:内置了丰富的配件型号和参考价格
2. 数据操作
- Excel导出:将配置单导出为Excel文件
- 打印功能:支持完整打印和无单价打印两种模式
- 信息显示:提供三种信息显示模式(完整、无单价、订货用)
- 数据持久化:页面刷新时保留已填写的数据
3. 界面特性
- 响应式设计:适配不同屏幕尺寸
- 第二列切换:可选择性显示第二套配置方案
- 颜色提示:非零金额以红色突出显示
- 操作便捷:支持删除行、自动填充等操作
三、配件数据库
系统内置了两套配件选项:
第一套配置(option1)
- CPU:涵盖Intel i3-i7、AMD Ryzen等15种型号
- 主板:包括技嘉、铭瑄、昂达等品牌10种型号
- 内存:金百达、阿斯加特等DDR4/DDR5内存
- 其他配件:显卡、电源、显示器等均有详细选项
第二套配置(option2)
提供另一套配件选择,价格和型号与第一套有所区别
四、操作指南
1. 基本操作
- 选择日期:默认显示当前系统日期
- 填写配置:
- 在"品牌型号"列选择配件型号
- 选择后数量自动设为1
- 如需自定义型号,选择"其他"后输入
- 调整数量:可手动修改数量,系统自动计算金额
2. 特殊功能
- 显示第二列:勾选复选框显示第二套配置方案
- 删除行:点击"删除"按钮移除不需要的配件
- 自动计算:修改数量或单价后自动更新总金额
3. 输出功能
- 导出Excel:生成包含所有数据的Excel文件
- 打印表格:完整打印配置单
- 打印(无单价金额):隐藏单价和金额的打印版
- 显示信息:三种查看模式:
- 完整信息(含单价金额)
- 无单价信息(仅显示配件和数量)
- 订货信息(数量为1时不显示数量)
五、技术特点
1. 前端技术
- HTML5:结构化页面内容
- CSS3:响应式样式设计,支持打印样式
- JavaScript(ES6):实现交互逻辑
- SheetJS:Excel导出功能
2. 数据结构
partOptions = {
"配件名称": {
option1: [{model: "型号", price: 价格}, ...],
option2: [{model: "型号", price: 价格}, ...]
}
}
3. 智能特性
- 自动填充:选择型号后自动设置数量为1
- 数据恢复:页面刷新时保留已填写数据
- 智能筛选:无单价打印时自动隐藏零值项
- 打印优化:打印时自动隐藏操作按钮
六、使用场景
1. 装机报价
- 为客户提供详细的配置清单和报价
- 支持两套方案对比
2. 订货管理
- 生成订货清单给供应商
- 数量为1的配件不显示数量(默认理解为一个)
3. 内部记录
- 保存历史配置方案
- 导出Excel归档
七、注意事项
- 打印设置:打印前请关闭浏览器的"页眉页脚"选项
- 数据安全:数据存储在本地,刷新页面会保留
- 浏览器兼容:建议使用Chrome、Firefox等现代浏览器
- 配件更新:如需更新配件数据库,需修改JavaScript代码
八、扩展建议
如需扩展功能,可考虑:
- 添加配件分类管理
- 实现云端数据同步
- 增加客户信息管理
- 添加历史记录查询
- 支持配件图片展示
这个系统通过简洁的界面和强大的功能,为装机业务提供了完整的解决方案,从报价到订货再到记录管理,覆盖了装机业务的主要需求。