Python tkinter写的《电脑装配单》和 Html版 可打印 可导出 excel 文件

server/2024/12/28 1:52:10/
htmledit_views">

 Python版 样图:

 说明书:

```markdown
# html" title=电脑>电脑配置单使用说明书

## 一、软件简介

html" title=电脑>电脑配置单是一个用于创建和比较两套html" title=电脑>电脑配置方案的工具软件。用户可以选择各种html" title=电脑>电脑配件,输入数量和价格,软件会自动计算总金额,并支持导出和打印配置单。

## 二、主要功能

1. **双配置方案对比**
   - 支持同时编辑两套配置方案
   - 每个配置方案包含CPU、主板、内存等15种常用配件
   - 可以分别查看每套方案的总数量和总金额

2. **配件数据管理**
   - 内置配件型号数据库
   - 支持添加、修改和删除配件型号及价格
   - 配件数据分为配置1和配置2两种方案

3. **导出打印功能** 
   - 支持导出为Excel表格
   - 支持生成打印预览并打印
   - 导出文件包含完整的配置信息和合计数据

## 三、使用说明

### 1. 创建配置单

1. 启动软件后,界面分为左右两个配置区域
2. 在每个配件行:
   - 从下拉框选择配件型号
   - 输入数量
   - 价格会自动填充(也可手动修改)
   - 金额会自动计算
3. 底部会显示每个配置的总数量和总金额

### 2. 编辑配件数据

1. 点击"编辑配件数据"按钮
2. 在弹出窗口中:
   - 选择配件类型
   - 选择配置方案(1或2)
   - 输入型号和价格
   - 点击"添加/更新"保存
3. 可以选中列表中的项目进行删除

### 3. 导出配置单

1. 点击"导出配置"按钮
2. 选择保存位置和文件名
3. 系统会生成包含两套配置方案的Excel文件

### 4. 打印配置单

1. 点击"打印配置"按钮
2. 系统会在浏览器中打开打印预览页面
3. 点击打印按钮执行打印

## 四、注意事项

1. 数量和价格必须输入数字,否则可能导致计算错误
2. 编辑配件数据时请确保输入正确的价格格式
3. 导出前建议检查配置信息是否完整
4. 打印前请先在预览中确认格式是否正确

Python 代码:

html" title=python>python"># 作者:Hoye
# 日期:2024-12-21
# 功能:装机配置单
# 版本:1.0import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import json
from datetime import datetime
import os
import openpyxl
from openpyxl.styles import Alignment, Font, Border, Side
import tempfile
import webbrowserclass ComponentsData:def __init__(self):self.json_file = os.path.join(os.path.dirname(__file__), 'components_data.json')self.load_data()def load_data(self):"""从JSON文件加载配件数据"""try:with open(self.json_file, 'r', encoding='utf-8') as f:self.data = json.load(f)except FileNotFoundError:messagebox.showerror('错误', f'找不到配件数据文件: {self.json_file}')self.data = {}except json.JSONDecodeError:messagebox.showerror('错误', '配件数据文件格式错误')self.data = {}def save_data(self):"""保存配件数据到JSON文件"""try:with open(self.json_file, 'w', encoding='utf-8') as f:json.dump(self.data, f, ensure_ascii=False, indent=4)except Exception as e:messagebox.showerror('错误', f'保存配件数据失败: {str(e)}')def get_models(self, part_name, option_type):"""获取指定配件的型号列表"""return self.data.get(part_name, {}).get(option_type, [])def get_price(self, part_name, option_type, model):"""获取指定配件型号的价格"""models = self.get_models(part_name, option_type)for item in models:if item['model'] == model:return item['price']return 0def update_price(self, part_name, option_type, model, new_price):"""更新指定配件型号的价格"""if part_name in self.data and option_type in self.data[part_name]:for item in self.data[part_name][option_type]:if item['model'] == model:item['price'] = new_priceself.save_data()return Truereturn Falseclass ConfigRow:def __init__(self, parent1, parent2, part_name, row, components_data, on_amount_change=None):self.components_data = components_dataself.on_amount_change = on_amount_changeself.part_name = part_name# 创建配置1的行self.frame1 = ttk.Frame(parent1)self.frame1.pack(fill='x', padx=5, pady=2)# 配置1的组件ttk.Label(self.frame1, text=part_name, width=15).pack(side='left', padx=5)self.model1_var = tk.StringVar()self.model1 = ttk.Combobox(self.frame1, textvariable=self.model1_var, width=30)self.model1.pack(side='left', padx=5)self.qty1_var = tk.StringVar(value='1')self.qty1 = ttk.Entry(self.frame1, textvariable=self.qty1_var, width=8)self.qty1.pack(side='left', padx=5)self.price1_var = tk.StringVar(value='0')self.price1 = ttk.Entry(self.frame1, textvariable=self.price1_var, width=10)self.price1.pack(side='left', padx=5)self.amount1_var = tk.StringVar(value='0')ttk.Label(self.frame1, textvariable=self.amount1_var, width=12).pack(side='left', padx=5)# 创建配置2的行self.frame2 = ttk.Frame(parent2)self.frame2.pack(fill='x', padx=5, pady=2)# 配置2的组件ttk.Label(self.frame2, text=part_name, width=15).pack(side='left', padx=5)self.model2_var = tk.StringVar()self.model2 = ttk.Combobox(self.frame2, textvariable=self.model2_var, width=30)self.model2.pack(side='left', padx=5)self.qty2_var = tk.StringVar(value='1')self.qty2 = ttk.Entry(self.frame2, textvariable=self.qty2_var, width=8)self.qty2.pack(side='left', padx=5)self.price2_var = tk.StringVar(value='0')self.price2 = ttk.Entry(self.frame2, textvariable=self.price2_var, width=10)self.price2.pack(side='left', padx=5)self.amount2_var = tk.StringVar(value='0')ttk.Label(self.frame2, textvariable=self.amount2_var, width=12).pack(side='left', padx=5)# 设置下拉框的值if part_name in components_data.data:models1 = [item['model'] for item in components_data.get_models(part_name, 'option1')]models2 = [item['model'] for item in components_data.get_models(part_name, 'option2')]self.model1['values'] = models1self.model2['values'] = models2# 绑定事件self.model1.bind('<<ComboboxSelected>>', self.update_price1)self.model2.bind('<<ComboboxSelected>>', self.update_price2)self.qty1_var.trace_add('write', self.calculate_amount1)self.qty2_var.trace_add('write', self.calculate_amount2)self.price1_var.trace_add('write', self.calculate_amount1)self.price2_var.trace_add('write', self.calculate_amount2)def update_price1(self, event=None):if self.model1.get() != '其他':price = self.components_data.get_price(self.part_name, 'option1', self.model1.get())self.price1_var.set(str(price))self.calculate_amount1()def update_price2(self, event=None):if self.model2.get() != '其他':price = self.components_data.get_price(self.part_name, 'option2', self.model2.get())self.price2_var.set(str(price))self.calculate_amount2()def calculate_amount1(self, *args):try:qty = float(self.qty1_var.get() or 0)price = float(self.price1_var.get() or 0)self.amount1_var.set(f'{qty * price:.2f}')if self.on_amount_change:  # 调用回调函数self.on_amount_change()except ValueError:self.amount1_var.set('0.00')if self.on_amount_change:self.on_amount_change()def calculate_amount2(self, *args):try:qty = float(self.qty2_var.get() or 0)price = float(self.price2_var.get() or 0)self.amount2_var.set(f'{qty * price:.2f}')if self.on_amount_change:  # 调用回调函数self.on_amount_change()except ValueError:self.amount2_var.set('0.00')if self.on_amount_change:self.on_amount_change()class ComputerConfigApp(tk.Tk):def __init__(self):super().__init__()self.title('html" title=电脑>电脑配置单')self.geometry('1600x900')  # 加大窗口尺寸# 设置整体样式style = ttk.Style()style.configure('Header.TLabel', font=('Arial', 10, 'bold'))style.configure('Title.TLabel', font=('Arial', 12, 'bold'))style.configure('Config.TFrame', padding=5)style.configure('Total.TFrame', padding=10, relief='solid')# 加载配件数据self.components_data = ComponentsData()# 创建主容器main_container = ttk.Frame(self)main_container.pack(fill='both', expand=True, padx=10, pady=5)# 创建标题title_frame = ttk.Frame(main_container)title_frame.pack(fill='x', pady=10)ttk.Label(title_frame, text='html" title=电脑>电脑配置单', style='Title.TLabel').pack()# 创建日期选择date_frame = ttk.Frame(main_container)date_frame.pack(fill='x', pady=5)ttk.Label(date_frame, text='日期:').pack(side='left')self.date_var = tk.StringVar(value=datetime.now().strftime('%Y-%m-%d'))ttk.Entry(date_frame, textvariable=self.date_var, width=15).pack(side='left')# 创建配置区域容器configs_container = ttk.Frame(main_container)configs_container.pack(fill='both', expand=True)# 创建左右两个配置面板self.config1_frame = ttk.LabelFrame(configs_container, text='配置方案1', padding=10)self.config1_frame.pack(side='left', fill='both', expand=True, padx=5)self.config2_frame = ttk.LabelFrame(configs_container, text='配置方案2', padding=10)self.config2_frame.pack(side='left', fill='both', expand=True, padx=5)# 创建滚动区域self.create_scrollable_frame(self.config1_frame, self.config2_frame)# 创建合计区域self.create_total_area(main_container)# 创建按钮区域self.create_buttons(main_container)def create_scrollable_frame(self, frame1, frame2):"""创建可滚动的配置区域"""# 配置1的滚动区域canvas1 = tk.Canvas(frame1)scrollbar1 = ttk.Scrollbar(frame1, orient="vertical", command=canvas1.yview)self.scrollable_frame1 = ttk.Frame(canvas1)canvas1.configure(yscrollcommand=scrollbar1.set)# 配置2的滚动区域canvas2 = tk.Canvas(frame2)scrollbar2 = ttk.Scrollbar(frame2, orient="vertical", command=canvas2.yview)self.scrollable_frame2 = ttk.Frame(canvas2)canvas2.configure(yscrollcommand=scrollbar2.set)# 布局scrollbar1.pack(side="right", fill="y")canvas1.pack(side="left", fill="both", expand=True)canvas1.create_window((0, 0), window=self.scrollable_frame1, anchor="nw")scrollbar2.pack(side="right", fill="y")canvas2.pack(side="left", fill="both", expand=True)canvas2.create_window((0, 0), window=self.scrollable_frame2, anchor="nw")# 创建表头self.create_headers()# 创建配件行self.create_component_rows()# 绑定滚动事件self.scrollable_frame1.bind("<Configure>", lambda e: canvas1.configure(scrollregion=canvas1.bbox("all")))self.scrollable_frame2.bind("<Configure>", lambda e: canvas2.configure(scrollregion=canvas2.bbox("all")))def create_headers(self):"""创建表头"""# 配置1表头header1 = ttk.Frame(self.scrollable_frame1)header1.pack(fill='x', pady=5)ttk.Label(header1, text='配件名称', width=15, style='Header.TLabel').pack(side='left', padx=5)ttk.Label(header1, text='型号', width=30, style='Header.TLabel').pack(side='left', padx=5)ttk.Label(header1, text='数量', width=8, style='Header.TLabel').pack(side='left', padx=5)ttk.Label(header1, text='单价', width=10, style='Header.TLabel').pack(side='left', padx=5)ttk.Label(header1, text='金额', width=12, style='Header.TLabel').pack(side='left', padx=5)# 配置2表头header2 = ttk.Frame(self.scrollable_frame2)header2.pack(fill='x', pady=5)ttk.Label(header2, text='配件名称', width=15, style='Header.TLabel').pack(side='left', padx=5)ttk.Label(header2, text='型号', width=30, style='Header.TLabel').pack(side='left', padx=5)ttk.Label(header2, text='数量', width=8, style='Header.TLabel').pack(side='left', padx=5)ttk.Label(header2, text='单价', width=10, style='Header.TLabel').pack(side='left', padx=5)ttk.Label(header2, text='金额', width=12, style='Header.TLabel').pack(side='left', padx=5)def create_total_area(self, parent):"""创建合计区域"""total_frame = ttk.Frame(parent, style='Total.TFrame')total_frame.pack(fill='x', pady=10)# 配置1合计total1_frame = ttk.LabelFrame(total_frame, text='配置1合计')total1_frame.pack(side='left', padx=20, pady=5)self.total1_var = tk.StringVar(value='0.00')self.qty_total1_var = tk.StringVar(value='0')ttk.Label(total1_frame, text='数量:').pack(side='left')ttk.Label(total1_frame, textvariable=self.qty_total1_var).pack(side='left', padx=5)ttk.Label(total1_frame, text='合计:').pack(side='left', padx=10)ttk.Label(total1_frame, textvariable=self.total1_var).pack(side='left')# 配置2合计total2_frame = ttk.LabelFrame(total_frame, text='配置2合计')total2_frame.pack(side='left', padx=20, pady=5)self.total2_var = tk.StringVar(value='0.00')self.qty_total2_var = tk.StringVar(value='0')ttk.Label(total2_frame, text='数量:').pack(side='left')ttk.Label(total2_frame, textvariable=self.qty_total2_var).pack(side='left', padx=5)ttk.Label(total2_frame, text='合计:').pack(side='left', padx=10)ttk.Label(total2_frame, textvariable=self.total2_var).pack(side='left')def create_buttons(self, parent):"""创建按钮区域"""button_frame = ttk.Frame(parent)button_frame.pack(pady=10)style = ttk.Style()style.configure('Action.TButton', padding=5)ttk.Button(button_frame, text='导出配置', style='Action.TButton', command=self.export_config).pack(side='left', padx=10)ttk.Button(button_frame, text='打印配置', style='Action.TButton',command=self.print_config).pack(side='left', padx=10)ttk.Button(button_frame, text='编辑配件数据', style='Action.TButton',command=self.edit_components_data).pack(side='left', padx=10)def create_component_rows(self):self.rows = []  # 初始化行列表parts = ['CPU', '主板', '内存', '硬盘', 'SSD固态盘', '显卡', '机箱', '电源', '显示器', '键鼠套装', '键盘', '鼠标', '散热器', '音箱', '光存储']for i, part in enumerate(parts):row = ConfigRow(self.scrollable_frame1,  # 配置1的父容器self.scrollable_frame2,  # 配置2的父容器part, i, self.components_data,on_amount_change=self.calculate_totals)self.rows.append(row)def calculate_totals(self):"""计算总金额和总数量"""total1 = 0total2 = 0qty_total1 = 0qty_total2 = 0for row in self.rows:try:# 计算金额total1 += float(row.amount1_var.get() or 0)total2 += float(row.amount2_var.get() or 0)# 计算数量qty1 = float(row.qty1_var.get() or 0)qty2 = float(row.qty2_var.get() or 0)if qty1 > 0:  # 只计算非零数量qty_total1 += qty1if qty2 > 0:  # 只计算非零数量qty_total2 += qty2except ValueError:continue# 更新显示self.total1_var.set(f'{total1:.2f}')self.total2_var.set(f'{total2:.2f}')self.qty_total1_var.set(str(int(qty_total1)))self.qty_total2_var.set(str(int(qty_total2)))def export_config(self):"""导出配置到Excel文件"""try:# 创建新的工作簿wb = openpyxl.Workbook()ws = wb.activews.title = "html" title=电脑>电脑配置单"# 设置列宽ws.column_dimensions['A'].width = 15ws.column_dimensions['B'].width = 25ws.column_dimensions['C'].width = 8ws.column_dimensions['D'].width = 10ws.column_dimensions['E'].width = 12ws.column_dimensions['F'].width = 25ws.column_dimensions['G'].width = 8ws.column_dimensions['H'].width = 10# 设置标题ws['A1'] = "html" title=电脑>电脑配置单"ws.merge_cells('A1:H1')ws['A1'].font = Font(size=14, bold=True)ws['A1'].alignment = Alignment(horizontal='center')# 设置日期ws['A2'] = f"日期:{datetime.now().strftime('%Y-%m-%d')}"ws.merge_cells('A2:H2')# 设置表头headers = ['配件名称', '型号[1]', '数量[1]', '单价[1]', '金额[1]', '型号[2]', '数量[2]', '单价[2]', '金额[2]']for col, header in enumerate(headers, 1):cell = ws.cell(row=3, column=col)cell.value = headercell.font = Font(bold=True)cell.alignment = Alignment(horizontal='center')# 添加数据for row_idx, config_row in enumerate(self.rows, 4):ws.cell(row=row_idx, column=1, value=config_row.part_name)ws.cell(row=row_idx, column=2, value=config_row.model1.get())ws.cell(row=row_idx, column=3, value=float(config_row.qty1_var.get() or 0))ws.cell(row=row_idx, column=4, value=float(config_row.price1_var.get() or 0))ws.cell(row=row_idx, column=5, value=float(config_row.amount1_var.get() or 0))ws.cell(row=row_idx, column=6, value=config_row.model2.get())ws.cell(row=row_idx, column=7, value=float(config_row.qty2_var.get() or 0))ws.cell(row=row_idx, column=8, value=float(config_row.price2_var.get() or 0))ws.cell(row=row_idx, column=9, value=float(config_row.amount2_var.get() or 0))# 添加合计行total_row = len(self.rows) + 4ws.cell(row=total_row, column=1, value="合计")ws.cell(row=total_row, column=3, value=f"数量1:{self.qty_total1_var.get()}")ws.cell(row=total_row, column=5, value=f"金额1:{self.total1_var.get()}")ws.cell(row=total_row, column=7, value=f"数量2:{self.qty_total2_var.get()}")ws.cell(row=total_row, column=9, value=f"金额2:{self.total2_var.get()}")# 设置边框thin_border = Border(left=Side(style='thin'),right=Side(style='thin'),top=Side(style='thin'),bottom=Side(style='thin'))for row in ws.iter_rows(min_row=3, max_row=total_row, min_col=1, max_col=9):for cell in row:cell.border = thin_bordercell.alignment = Alignment(horizontal='center')# 保存文件file_path = filedialog.asksaveasfilename(defaultextension=".xlsx",filetypes=[("Excel files", "*.xlsx")],initialfile=f"html" title=电脑>电脑配置单_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx")if file_path:wb.save(file_path)messagebox.showinfo("成功", "配置已成功导出到Excel文件!")except Exception as e:messagebox.showerror("错误", f"导出失败:{str(e)}")def print_config(self):"""生成打印预览HTML并在浏览器中打开"""try:html_content = f"""<!DOCTYPE html><html><head><meta charset="UTF-8"><title>html" title=电脑>电脑配置单</title><style>body {{ font-family: Arial, sans-serif; margin: 20px; }}table {{ width: 100%; border-collapse: collapse; margin-top: 20px; }}th, td {{ border: 1px solid black; padding: 8px; text-align: center; }}th {{ background-color: #f2f2f2; }}.total-row {{ background-color: #f9f9f9; font-weight: bold; }}@media print {{button {{ display: none; }}}}</style></head><body><h2 style="text-align: center;">html" title=电脑>电脑配置单</h2><p>日期:{datetime.now().strftime('%Y-%m-%d')}</p><table><tr><th>配件名称</th><th>型号[1]</th><th>数量[1]</th><th>单价[1]</th><th>金额[1]</th><th>型号[2]</th><th>数量[2]</th><th>单价[2]</th><th>金额[2]</th></tr>"""# 添加数据行for row in self.rows:html_content += f"""<tr><td>{row.part_name}</td><td>{row.model1.get()}</td><td>{row.qty1_var.get()}</td><td>{row.price1_var.get()}</td><td>{row.amount1_var.get()}</td><td>{row.model2.get()}</td><td>{row.qty2_var.get()}</td><td>{row.price2_var.get()}</td><td>{row.amount2_var.get()}</td></tr>"""# 添加合计行html_content += f"""<tr class="total-row"><td colspan="2">合计</td><td>数量1:{self.qty_total1_var.get()}</td><td></td><td>金额1:{self.total1_var.get()}</td><td></td><td>数量2:{self.qty_total2_var.get()}</td><td></td><td>金额2:{self.total2_var.get()}</td></tr></table><button onclick="window.print()" style="margin-top: 20px;">打印</button></body></html>"""# 创建临时HTML文件with tempfile.NamedTemporaryFile('w', delete=False, suffix='.html', encoding='utf-8') as f:f.write(html_content)temp_path = f.name# 在浏览器中打开webbrowser.open('file://' + temp_path)except Exception as e:messagebox.showerror("错误", f"打印预览失败:{str(e)}")def edit_components_data(self):"""编辑配件数据对话框"""edit_window = tk.Toplevel(self)edit_window.title('编辑配件数据')edit_window.geometry('800x600')# 创建主框架main_frame = ttk.Frame(edit_window, padding=10)main_frame.pack(fill='both', expand=True)# 创建树形视图tree_frame = ttk.Frame(main_frame)tree_frame.pack(fill='both', expand=True)tree = ttk.Treeview(tree_frame, columns=('型号', '价格', '配置'), show='headings')tree.heading('型号', text='型号')tree.heading('价格', text='价格')tree.heading('配置', text='配置方案')# 添加滚动条scrollbar = ttk.Scrollbar(tree_frame, orient='vertical', command=tree.yview)tree.configure(yscrollcommand=scrollbar.set)tree.pack(side='left', fill='both', expand=True)scrollbar.pack(side='right', fill='y')# 创建编辑区域edit_frame = ttk.LabelFrame(main_frame, text='编辑', padding=10)edit_frame.pack(fill='x', pady=10)ttk.Label(edit_frame, text='配件:').grid(row=0, column=0, padx=5, pady=5)part_var = tk.StringVar()part_combo = ttk.Combobox(edit_frame, textvariable=part_var, state='readonly')part_combo['values'] = list(self.components_data.data.keys())part_combo.grid(row=0, column=1, padx=5, pady=5)ttk.Label(edit_frame, text='配置方案:').grid(row=0, column=2, padx=5, pady=5)option_var = tk.StringVar(value='option1')option1_radio = ttk.Radiobutton(edit_frame, text='配置1', variable=option_var, value='option1')option2_radio = ttk.Radiobutton(edit_frame, text='配置2', variable=option_var, value='option2')option1_radio.grid(row=0, column=3, padx=5, pady=5)option2_radio.grid(row=0, column=4, padx=5, pady=5)ttk.Label(edit_frame, text='型号:').grid(row=1, column=0, padx=5, pady=5)model_var = tk.StringVar()model_entry = ttk.Entry(edit_frame, textvariable=model_var)model_entry.grid(row=1, column=1, padx=5, pady=5)ttk.Label(edit_frame, text='价格:').grid(row=1, column=2, padx=5, pady=5)price_var = tk.StringVar()price_entry = ttk.Entry(edit_frame, textvariable=price_var)price_entry.grid(row=1, column=3, padx=5, pady=5)def update_tree(*args):tree.delete(*tree.get_children())part = part_var.get()if part in self.components_data.data:for option in ['option1', 'option2']:for item in self.components_data.data[part][option]:tree.insert('', 'end', values=(item['model'],item['price'],'配置1' if option == 'option1' else '配置2'))def add_item():try:part = part_var.get()option = option_var.get()model = model_var.get()price = float(price_var.get())if not part or not model:messagebox.showwarning('警告', '请选择配件并输入型号')return# 检查是否已存在for item in self.components_data.data[part][option]:if item['model'] == model:item['price'] = pricebreakelse:self.components_data.data[part][option].append({'model': model,'price': price})# 保存并更新self.components_data.save_data()update_tree()# 清空输入model_var.set('')price_var.set('')except ValueError:messagebox.showerror('错误', '价格必须是数字')def delete_item():selected = tree.selection()if not selected:messagebox.showwarning('警告', '请选择要删除的项目')returnif messagebox.askyesno('确认', '确定要删除选中的项目吗?'):for item_id in selected:values = tree.item(item_id)['values']model = values[0]option = 'option1' if values[2] == '配置1' else 'option2'part = part_var.get()# 删除项目self.components_data.data[part][option] = [item for item in self.components_data.data[part][option]if item['model'] != model]# 保存并更新self.components_data.save_data()update_tree()# 添加按钮button_frame = ttk.Frame(main_frame)button_frame.pack(fill='x', pady=10)ttk.Button(button_frame, text='添加/更新', command=add_item).pack(side='left', padx=5)ttk.Button(button_frame, text='删除', command=delete_item).pack(side='left', padx=5)# 绑定事件part_var.trace_add('write', update_tree)def on_tree_select(event):selected = tree.selection()if selected:values = tree.item(selected[0])['values']model_var.set(values[0])price_var.set(values[1])option_var.set('option1' if values[2] == '配置1' else 'option2')tree.bind('<<TreeviewSelect>>', on_tree_select)if __name__ == '__main__':app = ComputerConfigApp()app.mainloop()# pyinstaller --onefile --windowed pc.py

 json/components_data.json

{"CPU": {"option1": [{"model": "Intel i5-13600KF","price": 2099},{"model": "Intel i7-13700K","price": 2899},{"model": "AMD R7 7700X","price": 2499},{"model": "其他","price": 0}],"option2": [{"model": "Intel i9-13900K","price": 4299},{"model": "AMD R9 7950X","price": 4499},{"model": "其他","price": 0}]},"主板": {"option1": [{"model": "华硕 PRIME B760M-K","price": 799},{"model": "微星 PRO B760M-P","price": 699},{"model": "其他","price": 0}],"option2": [{"model": "华硕 ROG STRIX Z790-A","price": 2799},{"model": "微星 MPG Z790","price": 2599},{"model": "其他","price": 0}]},"内存": {"option1": [{"model": "金士顿 16GB DDR4 3200","price": 299},{"model": "海盗船 16GB DDR4 3600","price": 399},{"model": "其他","price": 0}],"option2": [{"model": "芝奇 32GB DDR5 6000","price": 999},{"model": "英睿达 32GB DDR5 5600","price": 899},{"model": "其他","price": 0}]},"硬盘": {"option1": [{"model": "西数蓝盘 2TB","price": 299},{"model": "希捷酷鱼 2TB","price": 319},{"model": "其他","price": 0}],"option2": [{"model": "西数紫盘 4TB","price": 699},{"model": "希捷酷狼 4TB","price": 799},{"model": "其他","price": 0}]},"SSD固态盘": {"option1": [{"model": "三星 980 500GB","price": 299},{"model": "西数 SN570 500GB","price": 279},{"model": "其他","price": 0}],"option2": [{"model": "三星 990 PRO 1TB","price": 799},{"model": "西数 SN850X 1TB","price": 699},{"model": "其他","price": 0}]},"显卡": {"option1": [{"model": "RTX 4060 8GB","price": 2299},{"model": "RX 7600 8GB","price": 1999},{"model": "其他","price": 0}],"option2": [{"model": "RTX 4080 16GB","price": 8999},{"model": "RX 7900 XTX","price": 7999},{"model": "其他","price": 0}]},"机箱": {"option1": [{"model": "先马鲁班3","price": 199},{"model": "爱国者T16","price": 169},{"model": "其他","price": 0}],"option2": [{"model": "联力O11 AIR MINI","price": 499},{"model": "Be quiet! 500DX","price": 599},{"model": "其他","price": 0}]},"电源": {"option1": [{"model": "长城 G6 650W","price": 399},{"model": "航嘉 GX650W","price": 369},{"model": "其他","price": 0}],"option2": [{"model": "海韵 FOCUS 850W","price": 899},{"model": "振华 LEADEX G 850W","price": 799},{"model": "其他","price": 0}]},"显示器": {"option1": [{"model": "AOC 24G2 24寸","price": 999},{"model": "创维 F24G1Q 24寸","price": 799},{"model": "其他","price": 0}],"option2": [{"model": "三星 G7 27寸","price": 3999},{"model": "LG 27GP850 27寸","price": 2999},{"model": "其他","price": 0}]},"键鼠套装": {"option1": [{"model": "罗技 MK120","price": 89},{"model": "双飞燕 KR-85","price": 69},{"model": "其他","price": 0}],"option2": [{"model": "罗技 MK850","price": 599},{"model": "雷蛇 无线套装","price": 799},{"model": "其他","price": 0}]},"键盘": {"option1": [{"model": "达尔优 A87","price": 199},{"model": "黑爵 K870T","price": 169},{"model": "其他","price": 0}],"option2": [{"model": "CHERRY MX 8.0","price": 999},{"model": "FILCO 忍者87","price": 1299},{"model": "其他","price": 0}]},"鼠标": {"option1": [{"model": "罗技 G102","price": 129},{"model": "达尔优 A960","price": 99},{"model": "其他","price": 0}],"option2": [{"model": "罗技 G PRO X","price": 699},{"model": "雷蛇 毒蝰 V3","price": 599},{"model": "其他","price": 0}]},"散热器": {"option1": [{"model": "九州风神 玄冰400","price": 89},{"model": "利民 AX120","price": 129},{"model": "其他","price": 0}],"option2": [{"model": "利民 PA120","price": 299},{"model": "猫头鹰 NH-D15","price": 699},{"model": "其他","price": 0}]},"音箱": {"option1": [{"model": "漫步者 R12U","price": 99},{"model": "惠威 D200","price": 299},{"model": "其他","price": 0}],"option2": [{"model": "惠威 D1090","price": 999},{"model": "漫步者 S2000","price": 799},{"model": "其他","price": 0}]},"光存储": {"option1": [{"model": "华硕 DVD刻录机","price": 129},{"model": "建兴 DVD刻录机","price": 119},{"model": "其他","price": 0}],"option2": [{"model": "先锋 蓝光刻录机","price": 599},{"model": "华硕 蓝光刻录机","price": 699},{"model": "其他","price": 0}]}
}

Html 版

 说明书:

我来为这个装机配置单网页写一个使用说明书:

```markdown
# 装机配置单使用说明书

## 1. 基本功能介绍

这是一个用于创建html" title=电脑>电脑装机配置单的网页工具。用户可以:
- 选择各种html" title=电脑>电脑配件的型号和数量
- 自动计算总价
- 导出Excel文件
- 打印配置单

## 2. 界面说明

界面主要包含以下部分:
- 日期选择框
- 配件清单表格(包含两套配置方案)
- 导出和打印按钮

### 2.1 表格列说明
- 操作:删除当前行
- 配件名称:预设的配件类型
- 品牌型号[1]:第一套配置的型号选择
- 数量[1]:第一套配置的数量
- 单价[1]:第一套配置的单价
- 金额[1]:自动计算的总金额
- 品牌型号[2]:第二套配置的型号选择
- 数量[2]:第二套配置的数量
- 单价[2]:第二套配置的单价
- 金额[2]:自动计算的总金额

## 3. 使用步骤

### 3.1 创建配置单
1. 选择日期
2. 对每个配件:
   - 从下拉菜单选择品牌型号
   - 如选择"其他",可手动输入型号
   - 输入数量
   - 价格会自动填充(选择"其他"时需手动输入)
   - 金额会自动计算

### 3.2 修改配置
- 可随时修改数量和价格
- 点击"删除"按钮可删除不需要的配件行
- 所有修改会自动重新计算总价

### 3.3 导出配置单
1. 点击"导出Excel"按钮
2. 文件会自动下载,文件名格式为:"装机配置单_日期.xlsx"

### 3.4 打印配置单
1. 点击"打印表格"按钮
2. 在打印预览中确认内容
3. 选择打印机进行打印

## 4. 特殊功能说明

### 4.1 自定义型号
- 当预设型号不满足需求时,可选择"其他"
- 选择"其他"后会出现输入框,可输入自定义型号
- 自定义型号需手动输入价格

### 4.2 价格显示
- 非零金额会以红色显示
- 总计会自动更新并显示在表格底部

## 5. 注意事项

1. 导出前请确保所有数据正确
2. 打印前建议先预览
3. 修改数量或价格后会自动重新计算
4. 删除行操作不可撤销,请谨慎操作

## 6. 常见问题

Q: 如何添加自定义型号?
A: 在型号下拉菜单中选择"其他",然后在出现的输入框中输入。

Q: 为什么金额显示为红色?
A: 这是正常的,所有非零金额都会以红色显示,以方便识别。

Q: 如何修改已输入的数据?
A: 直接点击相应的输入框进行修改,系统会自动重新计算。
```
 

Html 版代码: 

html"><!-- 作者:Hoye -->
<!-- 日期:2024-12-21 -->
<!-- 功能:装机配置单 -->
<!-- 版本:1.0 -->
<!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;}.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-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;}</style>
</head>
<body><h2>1.装机配置单</h2><p>日期: <input type="date" id="date" value="2024-12-15"></p><table id="configTable"><tr><th>操作</th><th>配件名称</th><th>品牌型号[1]</th><th>数量[1]</th><th>单价[1]</th><th>金额[1]</th><th>品牌型号[2]</th><th>数量[2]</th><th>单价[2]</th><th>金额[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="1"></td><td><input type="number" class="price1" value="0"></td><td><input type="number" class="amount1" readonly></td><td><input type="text" value=""></td><td><input type="number" class="qty2" value="1"></td><td><input type="number" class="price2" value="0"></td><td><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="1"></td><td><input type="number" class="price1" value="0"></td><td><input type="number" class="amount1" readonly></td><td><input type="text" value=""></td><td><input type="number" class="qty2" value="1"></td><td><input type="number" class="price2" value="0"></td><td><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="1"></td><td><input type="number" class="price1" value="0"></td><td><input type="number" class="amount1" readonly></td><td><input type="text" value=""></td><td><input type="number" class="qty2" value="1"></td><td><input type="number" class="price2" value="0"></td><td><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="1"></td><td><input type="number" class="price1" value="0"></td><td><input type="number" class="amount1" readonly></td><td><input type="text" value=""></td><td><input type="number" class="qty2" value="1"></td><td><input type="number" class="price2" value="0"></td><td><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="1"></td><td><input type="number" class="price1" value="0"></td><td><input type="number" class="amount1" readonly></td><td><input type="text" value=""></td><td><input type="number" class="qty2" value="1"></td><td><input type="number" class="price2" value="0"></td><td><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="1"></td><td><input type="number" class="price1" value="0"></td><td><input type="number" class="amount1" readonly></td><td><input type="text" value=""></td><td><input type="number" class="qty2" value="1"></td><td><input type="number" class="price2" value="0"></td><td><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="1"></td><td><input type="number" class="price1" value="0"></td><td><input type="number" class="amount1" readonly></td><td><input type="text" value=""></td><td><input type="number" class="qty2" value="1"></td><td><input type="number" class="price2" value="0"></td><td><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="1"></td><td><input type="number" class="price1" value="0"></td><td><input type="number" class="amount1" readonly></td><td><input type="text" value=""></td><td><input type="number" class="qty2" value="1"></td><td><input type="number" class="price2" value="0"></td><td><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="1"></td><td><input type="number" class="price1" value="0"></td><td><input type="number" class="amount1" readonly></td><td><input type="text" value=""></td><td><input type="number" class="qty2" value="1"></td><td><input type="number" class="price2" value="0"></td><td><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="1"></td><td><input type="number" class="price1" value="0"></td><td><input type="number" class="amount1" readonly></td><td><input type="text" value=""></td><td><input type="number" class="qty2" value="1"></td><td><input type="number" class="price2" value="0"></td><td><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><input type="text" value=""></td><td><input type="number" class="qty2" value="0"></td><td><input type="number" class="price2" value="0"></td><td><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><input type="text" value=""></td><td><input type="number" class="qty2" value="0"></td><td><input type="number" class="price2" value="0"></td><td><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="1"></td><td><input type="number" class="price1" value="0"></td><td><input type="number" class="amount1" readonly></td><td><input type="text" value=""></td><td><input type="number" class="qty2" value="1"></td><td><input type="number" class="price2" value="0"></td><td><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="1"></td><td><input type="number" class="price1" value="0"></td><td><input type="number" class="amount1" readonly></td><td><input type="text" value=""></td><td><input type="number" class="qty2" value="1"></td><td><input type="number" class="price2" value="0"></td><td><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><input type="text" value=""></td><td><input type="number" class="qty2" value="0"></td><td><input type="number" class="price2" value="0"></td><td><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 colspan="2">数量[2]:<span id="qty_total2">0</span></td><td></td><td>合计[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><script>// 扩展配件选项数组,包含所有配件const partOptions = {CPU: {option1: [{model: "I3 8100", price: 150},{model: "I3 12100", price: 585},{model: "I5 12400", price: 735},{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: "精粤H310M-G D4", price: 275},{model: "技嘉H610M", price: 465},{model: "铭瑄H610M-R", price: 355},{model: "精粤H610", price: 325},{model: "其他", price: 0}],option2: [{model: "华硕PRIME H610M-K(HDMI)", price: 499},{model: "其他", price: 0}]},内存: {option1: [{model: "金百达 3200 DDR4 16G", price: 128},{model: "金百达 3600 DDR4 16G", price: 159},{model: "其他", price: 0}],option2: [{model: "威刚DDR4 16GB", price: 137},{model: "其他", price: 0}]},硬盘: {option1: [{model: "西部数据蓝盘 1TB", price: 345},{model: "西部数据蓝盘 2TB", price: 375},{model: "其他", price: 0}],option2: [{model: "西部数据蓝盘 2TB", price: 375},{model: "其他", price: 0}]},"SSD固态盘": {option1: [{model: "玩翔(PANXIANG)m.2 512G", price: 210},{model: "西数SN580 512G", price: 395},{model: "昂达M.2 512G", price: 175},{model: "梵西 512G", price: 205},{model: "其他", price: 0}],option2: [{model: "闪迪 240 SSD", price: 159},{model: "其他", price: 0}]},显卡: {option1: [{model: "七彩虹GT1030", price: 459},{model: "技嘉GT1030", price: 499},{model: "其他", price: 0}],option2: [{model: "七彩虹GT1030", price: 459},{model: "其他", price: 0}]},机箱: {option1: [{model: "金河田", price: 40},{model: "金刚之星", price: 70},{model: "华硕机箱", price: 75},{model: "小机箱", price: 40},{model: "其他", price: 0}],option2: [{model: "金河田", price: 75},{model: "其他", price: 0}]},电源: {option1: [{model: "长城300W", price: 115},{model: "航嘉300W", price: 118},{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: "其他", price: 0}],option2: [{model: "联想 24寸 显示器", price: 599},{model: "其他", price: 0}]},键鼠套装: {option1: [{model: "双飞燕键鼠套装", price: 75},{model: "普通键鼠套装", price: 30},{model: "DT键鼠套装", price: 38},{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: 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}]}};// 处理号选择变化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');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;}}calculateTotals();}// 生成选择框HTMLfunction 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';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;} else if (originalModel1) {select1.value = "其他";const customInput1 = td3.querySelector('.custom-input');customInput1.value = originalModel1;td3.querySelector('.custom-input-container').classList.add('show');}}// 设置第二列的值const select2 = td7.querySelector('select');if (select2) {const option2 = Array.from(select2.options).find(opt => opt.value === originalModel2);if (option2) {select2.value = originalModel2;} else if (originalModel2) {select2.value = "其他";const customInput2 = td7.querySelector('.custom-input');customInput2.value = originalModel2;td7.querySelector('.custom-input-container').classList.add('show');}}// 设置价格const price1Input = row.querySelector('.price1');const price2Input = row.querySelector('.price2');if (price1Input) price1Input.value = originalPrice1;if (price2Input) price2Input.value = originalPrice2;}});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';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;}// 确保页面加载时自动计算document.addEventListener('DOMContentLoaded', function() {calculateTotals();// 监听数量和价格变化const table = document.getElementById('configTable');table.addEventListener('input', function(e) {if (e.target.matches('.qty1, .qty2, .price1, .price2')) {calculateTotals();}});});// 在删除行时也要重新计算function deleteRow(btn) {const row = btn.parentNode.parentNode;if (confirm('确定要删除这一行吗?')) {row.parentNode.removeChild(row);calculateTotals();}}// 修改导出Excel函数function exportToExcel() {calculateTotals();const data = [];const table = document.getElementById('configTable');const rows = table.getElementsByTagName('tr');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++) {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 || '');// 品牌型号[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,'','数量[2]:' + document.getElementById('qty_total2').textContent,'合计[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() {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 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;}}`;document.head.appendChild(style);window.print();// 打印后移除样式document.head.removeChild(style);}</script>
</body>
</html>

更高版本,以为2025年后 使用过程 再改进。。。


http://www.ppmy.cn/server/153775.html

相关文章

离心式压缩机设计的自动化方法

离心式压缩机设计中的挑战 长期以来&#xff0c;人们一直使用不同形状和尺寸的压缩机&#xff0c;通过向流经转子的连续流体添加能量来产生压力上升。它们也在汽车工业的涡轮增压器中得到广泛使用。图 1 显示了典型的离心式&#xff08;径向&#xff09;压缩机叶轮。 图 1 叶轮…

C++创建型模式之原型模式

C 原型模式&#xff08;Prototype Pattern&#xff09; 1. 解决的问题 原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;用于解决对象创建的问题&#xff0c;特别是在需要创建多个相似对象时&#xff0c;避免使用重复的构造代码。原型模式…

[按键精灵IOS安卓版][脚本基础知识]按键post基本写法

这一期我们来讲按键post的写法&#xff0c;希望通过本期的学习&#xff0c;实现常见的post提交都能编写。 下面开始讲解&#xff1a; 一、使用的命令&#xff1a;url.httppost 选用这个命令的理由是它的参数比较全。 二、post请求都有哪些参数&#xff08;可能用到&#xf…

python大数据国内旅游景点的数据爬虫与可视化分析

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了多年的设计程序开发&#xff0c;开发过上千套设计程序&#xff0c;没有什么华丽的语言&#xff0c;只有实…

金仓数据库安装-Kingbase v9-centos

在很多年前有个项目用的金仓数据库&#xff0c;上线稳定后就没在这个项目了&#xff0c;只有公司的开发环境还在维护&#xff0c;已经好多年没有安装过了&#xff0c;重温一下金仓数据库安装&#xff0c;体验一下最新版本&#xff0c;也做一个新版本的试验环境&#xff1b; 一、…

【C#】try-catch-finally语句的执行顺序,以及在发生异常时的执行顺序

try-catch-finally语句 执行顺序 执行 try 块&#xff1a;程序首先尝试执行 try 块中的代码。如果在此期间没有发生异常&#xff0c;则跳过 catch 块&#xff0c;直接执行 finally 块&#xff08;如果存在&#xff09;。 发生异常时的处理&#xff1a; 如果在 try 块中发生了…

Maven 项目文档

如何创建 Maven 项目文档。 比如我们在 C:/MVN 目录下&#xff0c;创建了 consumerBanking 项目&#xff0c;Maven 使用下面的命令来快速创建 java 项目&#xff1a; mvn archetype:generate -DgroupIdcom.companyname.bank -DartifactIdconsumerBanking -DarchetypeArtifact…

HTML CSS 超链

HTML CSS 超链 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>新闻详情</title><link rel"stylesheet" href"css/newsinfo.css" /></head><body><header><img src…