Compare commits
6 Commits
35050407e9
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2fe86ef332 | |||
| 26397a83dd | |||
| d33216f9b9 | |||
| 5cf27c8cfe | |||
| 803127062e | |||
| 83609aaa20 |
74
build_exe.py
Normal file
74
build_exe.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
"""
|
||||||
|
打包脚本 - 将项目打包为exe文件
|
||||||
|
使用方法: python build_exe.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
def build_exe():
|
||||||
|
"""使用PyInstaller打包为exe"""
|
||||||
|
|
||||||
|
# 项目根目录
|
||||||
|
root_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
os.chdir(root_dir)
|
||||||
|
|
||||||
|
# 主程序文件
|
||||||
|
main_script = "main.py"
|
||||||
|
|
||||||
|
# 打包命令
|
||||||
|
cmd = [
|
||||||
|
"pyinstaller",
|
||||||
|
"--name=服装布料计算管理器",
|
||||||
|
"--onefile", # 打包为单个exe文件
|
||||||
|
"--windowed", # 不显示控制台窗口
|
||||||
|
"--hidden-import=PyQt5.QtCore",
|
||||||
|
"--hidden-import=PyQt5.QtGui",
|
||||||
|
"--hidden-import=PyQt5.QtWidgets",
|
||||||
|
"--hidden-import=sqlite3",
|
||||||
|
"--clean", # 清理临时文件
|
||||||
|
]
|
||||||
|
|
||||||
|
# 如果有图标文件,添加图标参数
|
||||||
|
if os.path.exists("icon.ico"):
|
||||||
|
cmd.append("--icon=icon.ico")
|
||||||
|
|
||||||
|
# 如果数据库文件存在,添加到打包数据中
|
||||||
|
if os.path.exists("fabric_library.db"):
|
||||||
|
# Windows使用分号,Linux/Mac使用冒号
|
||||||
|
separator = ";" if sys.platform == "win32" else ":"
|
||||||
|
cmd.append(f"--add-data=fabric_library.db{separator}.")
|
||||||
|
|
||||||
|
cmd.append(main_script)
|
||||||
|
|
||||||
|
print("开始打包...")
|
||||||
|
print(f"执行命令: {' '.join(cmd)}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 执行打包
|
||||||
|
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||||
|
print("打包成功!")
|
||||||
|
print(f"\n输出文件位置: {os.path.join(root_dir, 'dist', '服装布料计算管理器.exe')}")
|
||||||
|
|
||||||
|
# 复制数据库文件到dist目录(如果存在)
|
||||||
|
if os.path.exists("fabric_library.db"):
|
||||||
|
dist_dir = os.path.join(root_dir, "dist")
|
||||||
|
if os.path.exists(dist_dir):
|
||||||
|
shutil.copy2("fabric_library.db", dist_dir)
|
||||||
|
print(f"数据库文件已复制到: {os.path.join(dist_dir, 'fabric_library.db')}")
|
||||||
|
|
||||||
|
print("\n打包完成!可以在 dist 目录中找到生成的exe文件。")
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"打包失败: {e}")
|
||||||
|
print(f"错误输出: {e.stderr}")
|
||||||
|
sys.exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("错误: 未找到 pyinstaller,请先安装: pip install pyinstaller")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
build_exe()
|
||||||
|
|
||||||
62
build_exe.spec
Normal file
62
build_exe.spec
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
PyInstaller 配置文件
|
||||||
|
可以直接使用: pyinstaller build_exe.spec
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
# 收集所有需要打包的Python文件
|
||||||
|
a = Analysis(
|
||||||
|
['main.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[
|
||||||
|
('fabric_library.db', '.') if os.path.exists('fabric_library.db') else None,
|
||||||
|
],
|
||||||
|
hiddenimports=[
|
||||||
|
'PyQt5.QtCore',
|
||||||
|
'PyQt5.QtGui',
|
||||||
|
'PyQt5.QtWidgets',
|
||||||
|
'sqlite3',
|
||||||
|
],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 过滤掉None值
|
||||||
|
a.datas = [d for d in a.datas if d is not None]
|
||||||
|
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='服装布料计算管理器',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=False, # 不显示控制台窗口
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
icon=None, # 可以指定图标文件路径,例如: 'icon.ico'
|
||||||
|
)
|
||||||
|
|
||||||
@@ -42,7 +42,8 @@ class DatabaseManager:
|
|||||||
unit TEXT DEFAULT '米',
|
unit TEXT DEFAULT '米',
|
||||||
timestamp TEXT,
|
timestamp TEXT,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
is_deleted INTEGER DEFAULT 0
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
@@ -194,6 +195,7 @@ class DatabaseManager:
|
|||||||
note TEXT,
|
note TEXT,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
is_deleted INTEGER DEFAULT 0,
|
||||||
FOREIGN KEY (model) REFERENCES fabrics(model)
|
FOREIGN KEY (model) REFERENCES fabrics(model)
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
@@ -216,6 +218,7 @@ class DatabaseManager:
|
|||||||
unit TEXT,
|
unit TEXT,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
is_deleted INTEGER DEFAULT 0,
|
||||||
FOREIGN KEY (style_number) REFERENCES garments(style_number),
|
FOREIGN KEY (style_number) REFERENCES garments(style_number),
|
||||||
FOREIGN KEY (model) REFERENCES fabrics(model)
|
FOREIGN KEY (model) REFERENCES fabrics(model)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -142,17 +142,24 @@ class GarmentLibraryDialog(QDialog):
|
|||||||
self.garment_table.setRowHeight(i, 140)
|
self.garment_table.setRowHeight(i, 140)
|
||||||
|
|
||||||
for row_idx, (style_number, image_path) in enumerate(rows):
|
for row_idx, (style_number, image_path) in enumerate(rows):
|
||||||
self.garment_table.setItem(row_idx, 0, QTableWidgetItem(style_number))
|
# 款号列 - 设置为只读
|
||||||
|
style_item = QTableWidgetItem(style_number)
|
||||||
|
style_item.setFlags(style_item.flags() & ~Qt.ItemIsEditable)
|
||||||
|
self.garment_table.setItem(row_idx, 0, style_item)
|
||||||
|
|
||||||
# 查询材料数量
|
# 查询材料数量
|
||||||
with self.get_conn() as conn:
|
with self.get_conn() as conn:
|
||||||
cursor2 = conn.execute("SELECT COUNT(*) FROM garment_materials WHERE style_number = ?", (style_number,))
|
cursor2 = conn.execute("SELECT COUNT(*) FROM garment_materials WHERE style_number = ?", (style_number,))
|
||||||
count = cursor2.fetchone()[0]
|
count = cursor2.fetchone()[0]
|
||||||
self.garment_table.setItem(row_idx, 1, QTableWidgetItem(str(count)))
|
# 类目数量列 - 设置为只读
|
||||||
|
count_item = QTableWidgetItem(str(count))
|
||||||
|
count_item.setFlags(count_item.flags() & ~Qt.ItemIsEditable)
|
||||||
|
self.garment_table.setItem(row_idx, 1, count_item)
|
||||||
|
|
||||||
# 显示图片预览
|
# 显示图片预览
|
||||||
image_item = QTableWidgetItem()
|
image_item = QTableWidgetItem()
|
||||||
image_item.setTextAlignment(Qt.AlignCenter)
|
image_item.setTextAlignment(Qt.AlignCenter)
|
||||||
|
image_item.setFlags(image_item.flags() & ~Qt.ItemIsEditable)
|
||||||
|
|
||||||
if image_path and os.path.exists(image_path):
|
if image_path and os.path.exists(image_path):
|
||||||
try:
|
try:
|
||||||
|
|||||||
69
main.py
69
main.py
@@ -234,7 +234,7 @@ class FabricManager(QMainWindow):
|
|||||||
return
|
return
|
||||||
quantity = self.quantity_input.value()
|
quantity = self.quantity_input.value()
|
||||||
loss_rate = self.loss_input.value() / 100
|
loss_rate = self.loss_input.value() / 100
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with self.get_conn() as conn:
|
with self.get_conn() as conn:
|
||||||
cursor = conn.execute('''
|
cursor = conn.execute('''
|
||||||
@@ -243,21 +243,24 @@ class FabricManager(QMainWindow):
|
|||||||
WHERE style_number = ?
|
WHERE style_number = ?
|
||||||
''', (style_number,))
|
''', (style_number,))
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
inserted = 0
|
|
||||||
|
# 第一步:检查所有原料库存是否充足
|
||||||
|
insufficient_materials = []
|
||||||
|
materials_to_record = []
|
||||||
|
|
||||||
for category, fabric_type, usage_per_piece, unit in rows:
|
for category, fabric_type, usage_per_piece, unit in rows:
|
||||||
if usage_per_piece == 0:
|
if usage_per_piece == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 获取该原料在入库时使用的单位
|
# 获取该原料在入库时使用的单位
|
||||||
stock_cursor = conn.execute('''
|
stock_cursor = conn.execute('''
|
||||||
SELECT unit FROM fabric_stock_in
|
SELECT unit FROM fabric_stock_in
|
||||||
WHERE model = ?
|
WHERE model = ?
|
||||||
ORDER BY purchase_date DESC
|
ORDER BY purchase_date DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
''', (category,))
|
''', (category,))
|
||||||
stock_unit_row = stock_cursor.fetchone()
|
stock_unit_row = stock_cursor.fetchone()
|
||||||
|
|
||||||
# 如果有入库记录,使用入库单位;否则使用原来的单位
|
# 如果有入库记录,使用入库单位;否则使用原来的单位
|
||||||
if stock_unit_row:
|
if stock_unit_row:
|
||||||
stock_unit = stock_unit_row[0]
|
stock_unit = stock_unit_row[0]
|
||||||
@@ -272,17 +275,59 @@ class FabricManager(QMainWindow):
|
|||||||
# 没有入库记录,使用原单位
|
# 没有入库记录,使用原单位
|
||||||
consume_qty = usage_per_piece * quantity * (1 + loss_rate)
|
consume_qty = usage_per_piece * quantity * (1 + loss_rate)
|
||||||
final_unit = unit
|
final_unit = unit
|
||||||
|
|
||||||
|
# 检查当前库存
|
||||||
|
stock_info = conn.execute('''
|
||||||
|
SELECT
|
||||||
|
COALESCE(SUM(CASE WHEN si.is_deleted = 0 THEN si.quantity ELSE 0 END), 0) AS total_in,
|
||||||
|
COALESCE(SUM(CASE WHEN c.is_deleted = 0 THEN c.consume_quantity ELSE 0 END), 0) AS total_out
|
||||||
|
FROM fabrics f
|
||||||
|
LEFT JOIN fabric_stock_in si ON f.model = si.model
|
||||||
|
LEFT JOIN fabric_consumption c ON f.model = c.model
|
||||||
|
WHERE f.model = ?
|
||||||
|
GROUP BY f.model
|
||||||
|
''', (category,)).fetchone()
|
||||||
|
|
||||||
|
if stock_info:
|
||||||
|
total_in, total_out = stock_info
|
||||||
|
current_stock = total_in - total_out
|
||||||
|
else:
|
||||||
|
current_stock = 0
|
||||||
|
|
||||||
|
# 如果库存不足,记录下来
|
||||||
|
if current_stock < consume_qty:
|
||||||
|
material_name = category or "未命名材料"
|
||||||
|
insufficient_materials.append(
|
||||||
|
f"{material_name}: 需要 {consume_qty:.3f} {final_unit},库存仅剩 {current_stock:.3f} {final_unit}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# 库存充足,加入待记录列表
|
||||||
|
materials_to_record.append((
|
||||||
|
style_number, category, usage_per_piece, quantity,
|
||||||
|
loss_rate, consume_qty, final_unit
|
||||||
|
))
|
||||||
|
|
||||||
|
# 如果有库存不足的材料,提示用户并中止操作
|
||||||
|
if insufficient_materials:
|
||||||
|
message = "以下原料库存不足,无法记录消耗:\n\n" + "\n".join(insufficient_materials)
|
||||||
|
QMessageBox.warning(self, "库存不足", message)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 第二步:所有库存都充足,执行记录
|
||||||
|
inserted = 0
|
||||||
|
for material_data in materials_to_record:
|
||||||
|
style_num, model, single_usage, qty, loss, consume_qty, final_unit = material_data
|
||||||
conn.execute('''
|
conn.execute('''
|
||||||
INSERT INTO fabric_consumption
|
INSERT INTO fabric_consumption
|
||||||
(style_number, model, single_usage, quantity_made, loss_rate, consume_quantity, consume_date, unit)
|
(style_number, model, single_usage, quantity_made, loss_rate, consume_quantity, consume_date, unit)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
''', (style_number, category, usage_per_piece, quantity, loss_rate, consume_qty, datetime.now().strftime('%Y-%m-%d'), final_unit))
|
''', (style_num, model, single_usage, qty, loss, consume_qty, datetime.now().strftime('%Y-%m-%d'), final_unit))
|
||||||
inserted += 1
|
inserted += 1
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
if inserted > 0:
|
if inserted > 0:
|
||||||
QMessageBox.information(self, "成功", "本次生产消耗已记录到库存!")
|
QMessageBox.information(self, "成功", f"本次生产消耗已记录到库存!共记录 {inserted} 种原料。")
|
||||||
else:
|
else:
|
||||||
QMessageBox.information(self, "提示", "本次没有可记录的原料消耗")
|
QMessageBox.information(self, "提示", "本次没有可记录的原料消耗")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -111,11 +111,8 @@ class RawMaterialEditDialog(QDialog):
|
|||||||
"""加载类目和供应商列表"""
|
"""加载类目和供应商列表"""
|
||||||
try:
|
try:
|
||||||
with self.get_conn() as conn:
|
with self.get_conn() as conn:
|
||||||
# 加载类目
|
# 加载类目 - 设置默认选项为:面料、辅料、其他
|
||||||
cursor = conn.execute("SELECT DISTINCT category FROM fabrics WHERE category IS NOT NULL AND category != '' AND (is_deleted IS NULL OR is_deleted = 0)")
|
self.edit_major_category.addItems(["面料", "辅料", "其他"])
|
||||||
majors = set(row[0] for row in cursor.fetchall() if row[0])
|
|
||||||
majors.update({"布料", "辅料", "其他"})
|
|
||||||
self.edit_major_category.addItems(sorted(majors))
|
|
||||||
|
|
||||||
# 加载供应商
|
# 加载供应商
|
||||||
cursor = conn.execute("SELECT DISTINCT supplier FROM fabrics WHERE supplier IS NOT NULL AND supplier != '' AND (is_deleted IS NULL OR is_deleted = 0) ORDER BY supplier")
|
cursor = conn.execute("SELECT DISTINCT supplier FROM fabrics WHERE supplier IS NOT NULL AND supplier != '' AND (is_deleted IS NULL OR is_deleted = 0) ORDER BY supplier")
|
||||||
@@ -172,6 +169,26 @@ class RawMaterialEditDialog(QDialog):
|
|||||||
major = self.edit_major_category.currentText().strip()
|
major = self.edit_major_category.currentText().strip()
|
||||||
sub = self.edit_sub_category.text().strip()
|
sub = self.edit_sub_category.text().strip()
|
||||||
|
|
||||||
|
# 验证必填字段:类目
|
||||||
|
if not major:
|
||||||
|
QMessageBox.warning(self, "错误", "请选择类目")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 验证必填字段:类型
|
||||||
|
if not sub:
|
||||||
|
QMessageBox.warning(self, "错误", "请输入类型")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 验证必填字段:幅宽
|
||||||
|
if self.edit_width.value() <= 0:
|
||||||
|
QMessageBox.warning(self, "错误", "请输入幅宽")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 验证必填字段:克重
|
||||||
|
if self.edit_gsm.value() <= 0:
|
||||||
|
QMessageBox.warning(self, "错误", "请输入克重")
|
||||||
|
return
|
||||||
|
|
||||||
# 特殊处理胸杯类型
|
# 特殊处理胸杯类型
|
||||||
if "胸杯" in sub:
|
if "胸杯" in sub:
|
||||||
major = "辅料"
|
major = "辅料"
|
||||||
@@ -453,17 +470,17 @@ class RawMaterialLibraryDialog(QDialog):
|
|||||||
self.load_table()
|
self.load_table()
|
||||||
|
|
||||||
def load_major_categories(self):
|
def load_major_categories(self):
|
||||||
"""加载主类目"""
|
"""加载主类目 - 只显示数据库中实际存在的类目"""
|
||||||
try:
|
try:
|
||||||
with self.get_conn() as conn:
|
with self.get_conn() as conn:
|
||||||
cursor = conn.execute("SELECT DISTINCT category FROM fabrics WHERE category IS NOT NULL AND category != '' AND (is_deleted IS NULL OR is_deleted = 0)")
|
cursor = conn.execute("SELECT DISTINCT category FROM fabrics WHERE category IS NOT NULL AND category != '' AND (is_deleted IS NULL OR is_deleted = 0)")
|
||||||
majors = set(row[0] for row in cursor.fetchall() if row[0])
|
majors = set(row[0] for row in cursor.fetchall() if row[0])
|
||||||
majors.update({"布料", "辅料", "其他"})
|
|
||||||
|
|
||||||
self.major_combo.blockSignals(True)
|
self.major_combo.blockSignals(True)
|
||||||
self.major_combo.clear()
|
self.major_combo.clear()
|
||||||
self.major_combo.addItem("全部类目")
|
self.major_combo.addItem("全部类目")
|
||||||
self.major_combo.addItems(sorted(majors))
|
if majors:
|
||||||
|
self.major_combo.addItems(sorted(majors))
|
||||||
self.major_combo.blockSignals(False)
|
self.major_combo.blockSignals(False)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@@ -471,26 +488,12 @@ class RawMaterialLibraryDialog(QDialog):
|
|||||||
|
|
||||||
def load_add_major_categories(self):
|
def load_add_major_categories(self):
|
||||||
"""加载添加界面的主类目"""
|
"""加载添加界面的主类目"""
|
||||||
try:
|
# 设置默认类目选项为:面料、辅料、其他
|
||||||
with self.get_conn() as conn:
|
self.add_major_category.blockSignals(True)
|
||||||
cursor = conn.execute("SELECT DISTINCT category FROM fabrics WHERE category IS NOT NULL AND category != '' AND (is_deleted IS NULL OR is_deleted = 0)")
|
self.add_major_category.clear()
|
||||||
majors = set(row[0] for row in cursor.fetchall() if row[0])
|
self.add_major_category.addItems(["面料", "辅料", "其他"])
|
||||||
majors.update({"布料", "辅料", "其他"})
|
self.add_major_category.setCurrentText("面料")
|
||||||
|
self.add_major_category.blockSignals(False)
|
||||||
self.add_major_category.blockSignals(True)
|
|
||||||
current_text = self.add_major_category.currentText()
|
|
||||||
self.add_major_category.clear()
|
|
||||||
self.add_major_category.addItems(sorted(majors))
|
|
||||||
|
|
||||||
if current_text in majors:
|
|
||||||
self.add_major_category.setCurrentText(current_text)
|
|
||||||
else:
|
|
||||||
self.add_major_category.setCurrentText("布料")
|
|
||||||
self.add_major_category.blockSignals(False)
|
|
||||||
except:
|
|
||||||
self.add_major_category.clear()
|
|
||||||
self.add_major_category.addItems(["布料", "辅料", "其他"])
|
|
||||||
self.add_major_category.setCurrentText("布料")
|
|
||||||
|
|
||||||
def load_sub_categories(self):
|
def load_sub_categories(self):
|
||||||
"""加载子类型"""
|
"""加载子类型"""
|
||||||
@@ -521,16 +524,19 @@ class RawMaterialLibraryDialog(QDialog):
|
|||||||
with self.get_conn() as conn:
|
with self.get_conn() as conn:
|
||||||
cursor = conn.execute("SELECT DISTINCT supplier FROM fabrics WHERE supplier IS NOT NULL AND supplier != '' AND (is_deleted IS NULL OR is_deleted = 0) ORDER BY supplier")
|
cursor = conn.execute("SELECT DISTINCT supplier FROM fabrics WHERE supplier IS NOT NULL AND supplier != '' AND (is_deleted IS NULL OR is_deleted = 0) ORDER BY supplier")
|
||||||
suppliers = [row[0] for row in cursor.fetchall()]
|
suppliers = [row[0] for row in cursor.fetchall()]
|
||||||
|
|
||||||
self.supplier_combo.blockSignals(True)
|
self.supplier_combo.blockSignals(True)
|
||||||
self.supplier_combo.clear()
|
self.supplier_combo.clear()
|
||||||
self.supplier_combo.addItem("全部供应商")
|
self.supplier_combo.addItem("全部供应商")
|
||||||
self.supplier_combo.addItems(suppliers)
|
self.supplier_combo.addItems(suppliers)
|
||||||
self.supplier_combo.blockSignals(False)
|
self.supplier_combo.blockSignals(False)
|
||||||
|
|
||||||
|
# 新增原料界面的供应商下拉框,添加空选项作为默认值
|
||||||
self.add_supplier.blockSignals(True)
|
self.add_supplier.blockSignals(True)
|
||||||
self.add_supplier.clear()
|
self.add_supplier.clear()
|
||||||
|
self.add_supplier.addItem("") # 添加空选项
|
||||||
self.add_supplier.addItems(suppliers)
|
self.add_supplier.addItems(suppliers)
|
||||||
|
self.add_supplier.setCurrentIndex(0) # 默认选中空选项
|
||||||
self.add_supplier.blockSignals(False)
|
self.add_supplier.blockSignals(False)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@@ -647,7 +653,7 @@ class RawMaterialLibraryDialog(QDialog):
|
|||||||
|
|
||||||
def delete_raw(self, model):
|
def delete_raw(self, model):
|
||||||
"""逻辑删除原料"""
|
"""逻辑删除原料"""
|
||||||
reply = QMessageBox.question(self, "确认", f"确定要删除 '{model}' 吗?\n\n此操作将标记删除该原料,不会影响历史数据。")
|
reply = QMessageBox.question(self, "确认", f"确定要删除 '{model}' 吗?")
|
||||||
if reply == QMessageBox.Yes:
|
if reply == QMessageBox.Yes:
|
||||||
try:
|
try:
|
||||||
with self.get_conn() as conn:
|
with self.get_conn() as conn:
|
||||||
@@ -668,6 +674,27 @@ class RawMaterialLibraryDialog(QDialog):
|
|||||||
|
|
||||||
major = self.add_major_category.currentText().strip()
|
major = self.add_major_category.currentText().strip()
|
||||||
sub = self.add_sub_category.text().strip()
|
sub = self.add_sub_category.text().strip()
|
||||||
|
|
||||||
|
# 验证必填字段:类目
|
||||||
|
if not major:
|
||||||
|
QMessageBox.warning(self, "错误", "请选择类目")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 验证必填字段:类型
|
||||||
|
if not sub:
|
||||||
|
QMessageBox.warning(self, "错误", "请输入类型")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 验证必填字段:幅宽
|
||||||
|
if self.add_width.value() <= 0:
|
||||||
|
QMessageBox.warning(self, "错误", "请输入幅宽")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 验证必填字段:克重
|
||||||
|
if self.add_gsm.value() <= 0:
|
||||||
|
QMessageBox.warning(self, "错误", "请输入克重")
|
||||||
|
return
|
||||||
|
|
||||||
if "胸杯" in sub:
|
if "胸杯" in sub:
|
||||||
major = "辅料"
|
major = "辅料"
|
||||||
|
|
||||||
@@ -680,23 +707,42 @@ class RawMaterialLibraryDialog(QDialog):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
with self.get_conn() as conn:
|
with self.get_conn() as conn:
|
||||||
# 检查是否已存在
|
# 检查是否已存在该型号
|
||||||
cursor = conn.execute("SELECT model FROM fabrics WHERE model = ?", (model,))
|
cursor = conn.execute("SELECT model, is_deleted FROM fabrics WHERE model = ?", (model,))
|
||||||
if cursor.fetchone():
|
existing = cursor.fetchone()
|
||||||
QMessageBox.warning(self, "错误", f"型号 '{model}' 已存在,请使用编辑功能修改")
|
|
||||||
return
|
|
||||||
|
|
||||||
conn.execute('''
|
if existing:
|
||||||
INSERT INTO fabrics
|
_, is_deleted = existing
|
||||||
(model, category, fabric_type, supplier, color, width, gsm, retail_price, bulk_price, unit, timestamp)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
||||||
''', (model, category, fabric_type, supplier, color,
|
|
||||||
self.add_width.value() or None, self.add_gsm.value() or None,
|
|
||||||
self.add_retail.value() or None, self.add_bulk.value() or None,
|
|
||||||
unit, datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
QMessageBox.information(self, "成功", f"已保存 '{model}'")
|
# 如果型号存在且未被删除,提示使用编辑功能
|
||||||
|
if not is_deleted or is_deleted == 0:
|
||||||
|
QMessageBox.warning(self, "错误", f"型号 '{model}' 已存在,请使用编辑功能修改")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 如果型号已被删除,直接恢复并更新
|
||||||
|
conn.execute('''
|
||||||
|
UPDATE fabrics
|
||||||
|
SET category=?, fabric_type=?, supplier=?, color=?, width=?, gsm=?,
|
||||||
|
retail_price=?, bulk_price=?, unit=?, timestamp=?, is_deleted=0, updated_at=CURRENT_TIMESTAMP
|
||||||
|
WHERE model=?
|
||||||
|
''', (category, fabric_type, supplier, color,
|
||||||
|
self.add_width.value() or None, self.add_gsm.value() or None,
|
||||||
|
self.add_retail.value() or None, self.add_bulk.value() or None,
|
||||||
|
unit, datetime.now().strftime('%Y-%m-%d %H:%M:%S'), model))
|
||||||
|
conn.commit()
|
||||||
|
QMessageBox.information(self, "成功", f"已保存 '{model}'")
|
||||||
|
else:
|
||||||
|
# 型号不存在,创建新记录
|
||||||
|
conn.execute('''
|
||||||
|
INSERT INTO fabrics
|
||||||
|
(model, category, fabric_type, supplier, color, width, gsm, retail_price, bulk_price, unit, timestamp)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
''', (model, category, fabric_type, supplier, color,
|
||||||
|
self.add_width.value() or None, self.add_gsm.value() or None,
|
||||||
|
self.add_retail.value() or None, self.add_bulk.value() or None,
|
||||||
|
unit, datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
|
||||||
|
conn.commit()
|
||||||
|
QMessageBox.information(self, "成功", f"已保存 '{model}'")
|
||||||
|
|
||||||
# 清空表单
|
# 清空表单
|
||||||
self.add_model.clear()
|
self.add_model.clear()
|
||||||
|
|||||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
PyQt5>=5.15.0
|
||||||
|
pyinstaller>=5.0.0
|
||||||
|
|
||||||
30
打包.bat
Normal file
30
打包.bat
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
echo ========================================
|
||||||
|
echo 服装布料计算管理器 - 打包脚本
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
echo 正在检查依赖...
|
||||||
|
python -c "import PyQt5" 2>nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo 错误: 未安装PyQt5,正在安装...
|
||||||
|
pip install -r requirements.txt
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo 安装失败,请手动执行: pip install -r requirements.txt
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo 开始打包...
|
||||||
|
python build_exe.py
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo 打包完成!
|
||||||
|
echo exe文件位置: dist\服装布料计算管理器.exe
|
||||||
|
echo ========================================
|
||||||
|
pause
|
||||||
|
|
||||||
85
打包说明.md
Normal file
85
打包说明.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# 打包为EXE文件说明
|
||||||
|
|
||||||
|
## 前置要求
|
||||||
|
|
||||||
|
1. 确保已安装Python 3.7或更高版本
|
||||||
|
2. 安装项目依赖:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## 打包方法
|
||||||
|
|
||||||
|
### 方法一:使用打包脚本(推荐)
|
||||||
|
|
||||||
|
直接运行打包脚本:
|
||||||
|
```bash
|
||||||
|
python build_exe.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方法二:使用PyInstaller命令
|
||||||
|
|
||||||
|
使用spec文件打包:
|
||||||
|
```bash
|
||||||
|
pyinstaller build_exe.spec
|
||||||
|
```
|
||||||
|
|
||||||
|
或者直接使用命令行:
|
||||||
|
```bash
|
||||||
|
pyinstaller --name=服装布料计算管理器 --onefile --windowed --add-data=fabric_library.db;. main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方法三:使用spec文件(推荐用于自定义配置)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pyinstaller build_exe.spec
|
||||||
|
```
|
||||||
|
|
||||||
|
## 打包输出
|
||||||
|
|
||||||
|
打包完成后,生成的exe文件位于 `dist` 目录中:
|
||||||
|
- `dist/服装布料计算管理器.exe`
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **数据库文件**:如果项目中有 `fabric_library.db` 文件,打包脚本会自动将其包含在exe同目录下。首次运行exe时,如果数据库不存在,程序会自动创建。
|
||||||
|
|
||||||
|
2. **文件大小**:打包后的exe文件可能较大(通常50-100MB),这是因为包含了Python解释器和所有依赖库。
|
||||||
|
|
||||||
|
3. **杀毒软件**:某些杀毒软件可能会误报,这是正常现象。PyInstaller打包的exe文件需要添加白名单。
|
||||||
|
|
||||||
|
4. **依赖库**:确保所有依赖都已正确安装,特别是PyQt5。
|
||||||
|
|
||||||
|
5. **测试**:打包完成后,建议在干净的Windows系统上测试exe文件是否能正常运行。
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### 问题1:打包失败,提示找不到模块
|
||||||
|
**解决方案**:在 `build_exe.spec` 的 `hiddenimports` 中添加缺失的模块。
|
||||||
|
|
||||||
|
### 问题2:exe运行时缺少DLL文件
|
||||||
|
**解决方案**:确保PyQt5已正确安装,可以尝试重新安装:
|
||||||
|
```bash
|
||||||
|
pip uninstall PyQt5
|
||||||
|
pip install PyQt5
|
||||||
|
```
|
||||||
|
|
||||||
|
### 问题3:exe文件太大
|
||||||
|
**解决方案**:
|
||||||
|
- 使用 `--exclude-module` 排除不需要的模块
|
||||||
|
- 使用 `--onedir` 模式代替 `--onefile`(会生成一个文件夹而不是单个exe)
|
||||||
|
|
||||||
|
### 问题4:数据库路径问题
|
||||||
|
**解决方案**:程序已自动处理数据库路径,exe运行时会在exe同目录下查找或创建数据库文件。
|
||||||
|
|
||||||
|
## 优化建议
|
||||||
|
|
||||||
|
1. **添加图标**:在 `build_exe.spec` 中设置 `icon='icon.ico'`,需要准备一个ico格式的图标文件。
|
||||||
|
|
||||||
|
2. **减小体积**:如果不需要某些功能,可以排除相关模块:
|
||||||
|
```python
|
||||||
|
excludes=['matplotlib', 'numpy', 'pandas'] # 示例
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **版本信息**:可以创建版本信息文件(.rc文件)并添加到spec配置中。
|
||||||
|
|
||||||
Reference in New Issue
Block a user