Compare commits
14 Commits
18fc8fcb0e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94080e71ae | ||
|
|
395d364a8f | ||
|
|
7c2728c004 | ||
|
|
8f67a3dd0a | ||
|
|
36139d4ab5 | ||
|
|
6471c066df | ||
|
|
3465cda361 | ||
|
|
aed5c5e3cb | ||
|
|
d5a9bb8798 | ||
|
|
e4da22868d | ||
|
|
568d7d3ef6 | ||
|
|
9d69b1bad2 | ||
|
|
d1baa87ae4 | ||
|
|
195beb3520 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -15,3 +15,7 @@ node_modules
|
|||||||
*.log
|
*.log
|
||||||
*.lock
|
*.lock
|
||||||
*.pdf
|
*.pdf
|
||||||
|
lightening.ico
|
||||||
|
metadata.yml
|
||||||
|
VERSION
|
||||||
|
生成exe图标.png
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -2,7 +2,7 @@ gui: build
|
|||||||
uv run python update_version.py
|
uv run python update_version.py
|
||||||
cd webui && npm run build
|
cd webui && npm run build
|
||||||
cd ..
|
cd ..
|
||||||
uv run pyinstaller webview_app.py -n LighteningGUI --noconsole --add-data "webui/dist;webui/dist" -y
|
uv run pyinstaller webview_app.py -n LighteningGUI --noconsole --add-data "webui/dist;webui/dist" -y --icon lightening.ico
|
||||||
|
|
||||||
console: dist build
|
console: dist build
|
||||||
uv run python update_version.py
|
uv run python update_version.py
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ pip install -r requirements.txt
|
|||||||
|
|
||||||
- ezdxf - DXF文件生成
|
- ezdxf - DXF文件生成
|
||||||
- loguru - 日志记录
|
- loguru - 日志记录
|
||||||
- matplotlib - 数据可视化和动画
|
|
||||||
- numpy - 数值计算
|
- numpy - 数值计算
|
||||||
- tomli - TOML配置文件解析
|
- tomli - TOML配置文件解析
|
||||||
- pywebview - 图形界面框架
|
- pywebview - 图形界面框架
|
||||||
|
|||||||
94
animation.py
94
animation.py
@@ -1,94 +0,0 @@
|
|||||||
import matplotlib.pyplot as plt
|
|
||||||
from functools import wraps
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
|
|
||||||
class Animation:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
fig, ax = plt.subplots()
|
|
||||||
self._fig = fig
|
|
||||||
self._ax = ax
|
|
||||||
self._ticks = 0
|
|
||||||
self._disable = False
|
|
||||||
self.init_fig()
|
|
||||||
pass
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def switch_decorator(func):
|
|
||||||
@wraps(func)
|
|
||||||
def not_run(cls, *args, **kwargs):
|
|
||||||
# print("not run")
|
|
||||||
pass
|
|
||||||
|
|
||||||
@wraps(func)
|
|
||||||
def wrapTheFunction(cls, *args, **kwargs):
|
|
||||||
if not cls._disable:
|
|
||||||
# print("desc")
|
|
||||||
return func(cls, *args, **kwargs)
|
|
||||||
return not_run(cls, *args, **kwargs)
|
|
||||||
|
|
||||||
return wrapTheFunction
|
|
||||||
|
|
||||||
def enable(self, _enable):
|
|
||||||
self._disable = not _enable
|
|
||||||
|
|
||||||
@switch_decorator
|
|
||||||
def init_fig(self):
|
|
||||||
ax = self._ax
|
|
||||||
ax.set_aspect(1)
|
|
||||||
ax.set_xlim([-500, 500])
|
|
||||||
ax.set_ylim([-500, 500])
|
|
||||||
|
|
||||||
@switch_decorator
|
|
||||||
def show(self):
|
|
||||||
self._fig.show()
|
|
||||||
|
|
||||||
@switch_decorator
|
|
||||||
def add_rg_line(self, line_func):
|
|
||||||
ax = self._ax
|
|
||||||
x = np.linspace(0, 300)
|
|
||||||
y = line_func(x)
|
|
||||||
ax.plot(x, y)
|
|
||||||
|
|
||||||
@switch_decorator
|
|
||||||
def add_rs(self, rs, rs_x, rs_y):
|
|
||||||
ax = self._ax
|
|
||||||
ax.add_artist(plt.Circle((rs_x, rs_y), rs, fill=False))
|
|
||||||
|
|
||||||
@switch_decorator
|
|
||||||
def add_rc(self, rc, rc_x, rc_y):
|
|
||||||
ax = self._ax
|
|
||||||
ax.add_artist(plt.Circle((rc_x, rc_y), rc, fill=False))
|
|
||||||
|
|
||||||
# 增加暴露弧范围
|
|
||||||
@switch_decorator
|
|
||||||
def add_expose_area(
|
|
||||||
self,
|
|
||||||
rc_x,
|
|
||||||
rc_y,
|
|
||||||
intersection_x1,
|
|
||||||
intersection_y1,
|
|
||||||
intersection_x2,
|
|
||||||
intersection_y2,
|
|
||||||
):
|
|
||||||
ax = self._ax
|
|
||||||
ax.plot([rc_x, intersection_x1], [rc_y, intersection_y1], color="red")
|
|
||||||
ax.plot([rc_x, intersection_x2], [rc_y, intersection_y2], color="red")
|
|
||||||
pass
|
|
||||||
|
|
||||||
@switch_decorator
|
|
||||||
def clear(self):
|
|
||||||
ax = self._ax
|
|
||||||
ax.cla()
|
|
||||||
|
|
||||||
@switch_decorator
|
|
||||||
def pause(self):
|
|
||||||
ax = self._ax
|
|
||||||
self._ticks += 1
|
|
||||||
ticks = self._ticks
|
|
||||||
ax.set_title(f"{ticks}")
|
|
||||||
plt.pause(0.02)
|
|
||||||
self.clear()
|
|
||||||
self.init_fig()
|
|
||||||
|
|
||||||
pass
|
|
||||||
BIN
lightening.ico
BIN
lightening.ico
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
18
main.py
18
main.py
@@ -151,19 +151,20 @@ def run_egm(para: Parameter, animation=None) -> dict:
|
|||||||
* 180
|
* 180
|
||||||
/ math.pi
|
/ math.pi
|
||||||
)
|
)
|
||||||
logger.info(f"地线保护角{shield_angle_at_avg_height:.2f}°")
|
logger.info(f"地线保护角(平均高处){shield_angle_at_avg_height:.2f}°")
|
||||||
logger.debug(f"最低相防护标识{rg_type}。(g表示地面,c表示下导线)")
|
logger.debug(f"最低相防护标识{rg_type}。(g表示地面,c表示下导线)")
|
||||||
rated_voltage = para.rated_voltage
|
rated_voltage = para.rated_voltage
|
||||||
logger.info(f"交、直流标识{para.ac_or_dc}")
|
logger.info(f"交、直流标识{para.ac_or_dc}")
|
||||||
for u_bar in range(voltage_n): # 计算不同工作电压下的跳闸率
|
for u_bar in range(voltage_n): # 计算不同工作电压下的跳闸率
|
||||||
if para.ac_or_dc=="AC":
|
if para.ac_or_dc=="AC":
|
||||||
# TODO 需要区分交、直流
|
# TODO 需要区分交、直流
|
||||||
u_ph = (
|
# u_ph = (
|
||||||
math.sqrt(2)
|
# math.sqrt(2)
|
||||||
* rated_voltage
|
# * rated_voltage
|
||||||
* math.cos(2 * math.pi / voltage_n * u_bar)
|
# * math.cos(2 * math.pi / voltage_n * u_bar)
|
||||||
/ 1.732
|
# / 1.732
|
||||||
) # 运行相电压
|
# ) # 运行相电压
|
||||||
|
u_ph = rated_voltage/1.732
|
||||||
else:
|
else:
|
||||||
u_ph = rated_voltage
|
u_ph = rated_voltage
|
||||||
logger.info(f"计算第{phase_conductor_foo + 1}相,电压为{u_ph:.2f}kV")
|
logger.info(f"计算第{phase_conductor_foo + 1}相,电压为{u_ph:.2f}kV")
|
||||||
@@ -260,7 +261,8 @@ def run_egm(para: Parameter, animation=None) -> dict:
|
|||||||
ground_angel,
|
ground_angel,
|
||||||
6,
|
6,
|
||||||
) # 最大电流时
|
) # 最大电流时
|
||||||
cad.save_as(f"egm{phase_conductor_foo + 1}.dxf")
|
# TODO: 暂时先不用。
|
||||||
|
# cad.save_as(f"egm{phase_conductor_foo + 1}.dxf")
|
||||||
min_distance_intersection = (
|
min_distance_intersection = (
|
||||||
np.sum(
|
np.sum(
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
version: 1.0.16
|
|
||||||
company_name: EGM
|
|
||||||
file_description: EGM Lightning Protection Calculator
|
|
||||||
product_name: Lightening
|
|
||||||
100
plot.py
100
plot.py
@@ -1,100 +0,0 @@
|
|||||||
import matplotlib
|
|
||||||
from plot_data import *
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import matplotlib.ticker as mticker
|
|
||||||
|
|
||||||
matplotlib.use("Qt5Agg")
|
|
||||||
# 解决中文乱码
|
|
||||||
plt.rcParams["font.sans-serif"] = ["simsun"]
|
|
||||||
plt.rcParams["font.family"] = "sans-serif"
|
|
||||||
# plt.rcParams["font.weight"] = "bold"
|
|
||||||
# 解决负号无法显示的问题
|
|
||||||
plt.rcParams["axes.unicode_minus"] = False
|
|
||||||
plt.rcParams["savefig.dpi"] = 1200 # 图片像素
|
|
||||||
# plt.savefig("port.png", dpi=600, bbox_inches="tight")
|
|
||||||
fontsize = 12
|
|
||||||
################################################
|
|
||||||
witdh_of_bar=0.3
|
|
||||||
color=plt.cm.BuPu(np.linspace(152/255, 251/255,152/255))
|
|
||||||
percent1 = data_150m塔高_不同地线保护角[:, 1] / data_150m塔高_不同地线保护角[:, 0]
|
|
||||||
# percent1 = data_66m串长_不同塔高[:, 1] / data_66m串长_不同塔高[:, 0]
|
|
||||||
# percent2 = data_68m串长_不同塔高[:, 1] / data_68m串长_不同塔高[:, 0]
|
|
||||||
fig, ax = plt.subplots()
|
|
||||||
x = np.arange(len(category_names_150m塔高_不同地线保护角)) # the label locations
|
|
||||||
p1 = ax.bar(category_names_150m塔高_不同地线保护角, percent1, witdh_of_bar, label="绕击/反击跳闸率比值",color=color,hatch='-')
|
|
||||||
# p1 = ax.bar(x - 0.3 / 2, percent1, 0.3, label="6.6m绝缘距离")
|
|
||||||
# p2 = ax.bar(x + 0.3 / 2, percent2, 0.3, label="6.8m绝缘距离")
|
|
||||||
ax.xaxis.set_major_locator(mticker.FixedLocator(x))
|
|
||||||
ax.set_xticklabels(category_names_150m塔高_不同地线保护角)
|
|
||||||
ax.set_ylabel("比值", fontsize=fontsize)
|
|
||||||
ax.set_xlabel("地线保护角(°)", fontsize=fontsize)
|
|
||||||
# ax.set_xlabel("接地电阻(Ω)", fontsize=fontsize)
|
|
||||||
plt.xticks(fontsize=fontsize)
|
|
||||||
plt.yticks(fontsize=fontsize)
|
|
||||||
ax.bar_label(p1, padding=0, fontsize=fontsize)
|
|
||||||
# ax.bar_label(p2, padding=0, fontsize=fontsize)
|
|
||||||
ax.legend(fontsize=fontsize)
|
|
||||||
|
|
||||||
fig.tight_layout()
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
|
|
||||||
# results = {
|
|
||||||
# "100m": 100 * data[0, :] / np.sum(data[0, :]),
|
|
||||||
# "110m": data[1, :] / np.sum(data[1, :]),
|
|
||||||
# "120m": data[2, :] / np.sum(data[2, :]),
|
|
||||||
# "130m": data[3, :] / np.sum(data[3, :]),
|
|
||||||
# "140m": data[4, :] / np.sum(data[4, :]),
|
|
||||||
# "150m": data[5, :] / np.sum(data[5, :]),
|
|
||||||
# }
|
|
||||||
|
|
||||||
|
|
||||||
# def survey(results, category_names):
|
|
||||||
# """
|
|
||||||
# Parameters
|
|
||||||
# ----------
|
|
||||||
# results : dict
|
|
||||||
# A mapping from question labels to a list of answers per category.
|
|
||||||
# It is assumed all lists contain the same number of entries and that
|
|
||||||
# it matches the length of *category_names*.
|
|
||||||
# category_names : list of str
|
|
||||||
# The category labels.
|
|
||||||
# """
|
|
||||||
# labels = list(results.keys())
|
|
||||||
# data = np.array(list(results.values()))
|
|
||||||
# data_cum = data.cumsum(axis=1)
|
|
||||||
# category_colors = plt.get_cmap("RdYlGn")(np.linspace(0.15, 0.85, data.shape[1]))
|
|
||||||
#
|
|
||||||
# fig, ax = plt.subplots(figsize=(9.2, 5))
|
|
||||||
# ax.invert_yaxis()
|
|
||||||
# ax.xaxis.set_visible(False)
|
|
||||||
# ax.set_xlim(0, np.sum(data, axis=1).max())
|
|
||||||
#
|
|
||||||
# for i, (colname, color) in enumerate(zip(category_names, category_colors)):
|
|
||||||
# widths = data[:, i]
|
|
||||||
# starts = data_cum[:, i] - widths
|
|
||||||
# rects = ax.barh(
|
|
||||||
# labels, widths, left=starts, height=0.5, label=colname, color=color
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# r, g, b, _ = color
|
|
||||||
# text_color = "white" if r * g * b < 0.5 else "darkgrey"
|
|
||||||
# ax.bar_label(rects, label_type="center", color=text_color)
|
|
||||||
# ax.legend(
|
|
||||||
# ncol=len(category_names),
|
|
||||||
# bbox_to_anchor=(0, 1),
|
|
||||||
# loc="lower left",
|
|
||||||
# fontsize="small",
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# return fig, ax
|
|
||||||
|
|
||||||
# percent=data/np.sum(data,axis=1)[:,None]*100
|
|
||||||
# percent = data[:, 1] / data[:, 0]
|
|
||||||
# plt.bar(category_names, percent, 0.3, label="黑")
|
|
||||||
# # plt.bar(category_names, percent[:,0], 0.2, label="r")
|
|
||||||
#
|
|
||||||
# # plt.bar(category_names, [0.014094 / 100, 0.025094 / 100], 0.2, label="h")
|
|
||||||
# plt.legend()
|
|
||||||
# # survey(results, category_names)
|
|
||||||
# plt.show()
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""更新版本号脚本"""
|
"""更新版本号脚本"""
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -20,22 +19,6 @@ def increment_version(version: str) -> str:
|
|||||||
return version
|
return version
|
||||||
|
|
||||||
|
|
||||||
def update_index_html(version: str):
|
|
||||||
"""更新 webui/index.html 中的标题版本号"""
|
|
||||||
index_file = Path(__file__).parent / "webui" / "index.html"
|
|
||||||
content = index_file.read_text(encoding="utf-8")
|
|
||||||
|
|
||||||
# 替换标题中的版本号
|
|
||||||
new_content = re.sub(
|
|
||||||
r"<title>EGM 输电线路绕击跳闸率计算( v[\d.]+)?</title>",
|
|
||||||
f"<title>EGM 输电线路绕击跳闸率计算 v{version}</title>",
|
|
||||||
content
|
|
||||||
)
|
|
||||||
|
|
||||||
index_file.write_text(new_content, encoding="utf-8")
|
|
||||||
print(f"Updated version in {index_file} to v{version}")
|
|
||||||
|
|
||||||
|
|
||||||
def update_version_file(version: str):
|
def update_version_file(version: str):
|
||||||
"""更新 VERSION 文件"""
|
"""更新 VERSION 文件"""
|
||||||
version_file = Path(__file__).parent / "VERSION"
|
version_file = Path(__file__).parent / "VERSION"
|
||||||
@@ -66,7 +49,6 @@ def main():
|
|||||||
|
|
||||||
# 更新所有文件
|
# 更新所有文件
|
||||||
update_version_file(new_version)
|
update_version_file(new_version)
|
||||||
update_index_html(new_version)
|
|
||||||
create_metadata(new_version)
|
create_metadata(new_version)
|
||||||
|
|
||||||
print(f"Version updated: {current_version} -> {new_version}")
|
print(f"Version updated: {current_version} -> {new_version}")
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>EGM 输电线路绕击跳闸率计算 v1.0.16</title>
|
<title>EGM 输电线路绕击跳闸率计算</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -1,7 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<ParameterForm />
|
<div class="app-container">
|
||||||
|
<ParameterForm />
|
||||||
|
<div class="version-footer">v{{ appVersion }}</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ParameterForm from '@/components/ParameterForm.vue'
|
import ParameterForm from '@/components/ParameterForm.vue'
|
||||||
|
|
||||||
|
const appVersion = __APP_VERSION__
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.app-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.version-footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px;
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<q-card class="shadow-2">
|
<q-card class="full-height">
|
||||||
<q-card-section class="bg-indigo-50">
|
<q-card-section class="bg-indigo-50">
|
||||||
<div class="text-h6 text-indigo-900 flex items-center gap-2">
|
<div class="text-h6 text-indigo-900 flex items-center gap-2">
|
||||||
<q-icon name="architecture" />
|
<q-icon name="architecture" />
|
||||||
@@ -368,7 +368,7 @@ const drawShieldingAngle = (range: ReturnType<typeof calculateRange>) => {
|
|||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
|
|
||||||
// 绘制标注文字
|
// 绘制标注文字
|
||||||
const labelText = `保护角: ${Math.abs(shieldingAngle).toFixed(2)}°`
|
const labelText = `保护角(平均高处): ${Math.abs(shieldingAngle).toFixed(2)}°`
|
||||||
ctx.font = 'bold 12px Arial'
|
ctx.font = 'bold 12px Arial'
|
||||||
|
|
||||||
// 绘制标注文字
|
// 绘制标注文字
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<q-page class="q-pa-md">
|
<q-page class="q-pa-md">
|
||||||
<div class="max-w-7xl mx-auto">
|
<div class="max-w-7xl mx-auto">
|
||||||
<!-- 基本参数 + 杆塔几何结构 并排布局 -->
|
<!-- 基本参数 + 杆塔几何结构 并排布局 -->
|
||||||
<div class="row q-col-gutter-md q-mb-md">
|
<div class="row q-col-gutter-md q-mb-md items-stretch" style="min-height: 500px;">
|
||||||
<!-- 左侧:基本参数 -->
|
<!-- 左侧:基本参数 -->
|
||||||
<div class="col-12 col-lg-6">
|
<div class="col-12 col-lg-6" style="display: flex;">
|
||||||
<q-card class="shadow-2 full-height">
|
<q-card class="shadow-2" style="flex: 1;">
|
||||||
<q-card-section class="bg-indigo-50">
|
<q-card-section class="bg-indigo-50">
|
||||||
<div class="text-h6 text-indigo-900 flex items-center gap-2">
|
<div class="text-h6 text-indigo-900 flex items-center gap-2">
|
||||||
<q-icon name="settings" />
|
<q-icon name="settings" />
|
||||||
@@ -204,7 +204,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 右侧:杆塔几何结构可视化 -->
|
<!-- 右侧:杆塔几何结构可视化 -->
|
||||||
<div class="col-12 col-lg-6">
|
<div class="col-12 col-lg-6" style="display: flex;">
|
||||||
<Geometry
|
<Geometry
|
||||||
:h-arm="params.parameter.h_arm"
|
:h-arm="params.parameter.h_arm"
|
||||||
:gc-x="params.parameter.gc_x"
|
:gc-x="params.parameter.gc_x"
|
||||||
@@ -213,6 +213,8 @@
|
|||||||
:string-c-len="params.parameter.string_c_len"
|
:string-c-len="params.parameter.string_c_len"
|
||||||
:string-g-len="params.parameter.string_g_len"
|
:string-g-len="params.parameter.string_g_len"
|
||||||
:ground-angels="params.parameter.ground_angels"
|
:ground-angels="params.parameter.ground_angels"
|
||||||
|
class="shadow-2"
|
||||||
|
style="flex: 1;"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -305,15 +307,6 @@
|
|||||||
|
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="row q-col-gutter-md">
|
<div class="row q-col-gutter-md">
|
||||||
<div class="col-12 col-md-6">
|
|
||||||
<q-input
|
|
||||||
v-model="params.optional.voltage_n"
|
|
||||||
type="number"
|
|
||||||
label="计算时电压分成多少份"
|
|
||||||
>
|
|
||||||
<q-tooltip>将电压波形离散化的份数,即将交流电压在一个周期内的不同值进行计算。</q-tooltip>
|
|
||||||
</q-input>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6">
|
<div class="col-12 col-md-6">
|
||||||
<q-input
|
<q-input
|
||||||
v-model="params.optional.max_i"
|
v-model="params.optional.max_i"
|
||||||
@@ -421,7 +414,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed, onMounted, onUnmounted, watch } from 'vue'
|
import { ref, reactive, computed, onMounted, onUnmounted, watch, toRaw } from 'vue'
|
||||||
import type { AllParameters } from '@/types'
|
import type { AllParameters } from '@/types'
|
||||||
import LogComponent from './Log.vue'
|
import LogComponent from './Log.vue'
|
||||||
import Animation from './Animation.vue'
|
import Animation from './Animation.vue'
|
||||||
@@ -452,7 +445,7 @@ const defaultParams: AllParameters = {
|
|||||||
u_50: -1
|
u_50: -1
|
||||||
},
|
},
|
||||||
optional: {
|
optional: {
|
||||||
voltage_n: 3,
|
voltage_n: 1,
|
||||||
max_i: 300
|
max_i: 300
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -481,14 +474,12 @@ const currentType = computed(() => {
|
|||||||
return params.parameter.rated_voltage.includes('±') ? 'DC' : 'AC'
|
return params.parameter.rated_voltage.includes('±') ? 'DC' : 'AC'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听电压等级变化,同步更新 ac_or_dc 和 voltage_n 字段
|
// 监听电压等级变化,同步更新 ac_or_dc 字段
|
||||||
watch(
|
watch(
|
||||||
() => params.parameter.rated_voltage,
|
() => params.parameter.rated_voltage,
|
||||||
(newVoltage) => {
|
(newVoltage) => {
|
||||||
const isDC = newVoltage.includes('±')
|
const isDC = newVoltage.includes('±')
|
||||||
params.parameter.ac_or_dc = isDC ? 'DC' : 'AC'
|
params.parameter.ac_or_dc = isDC ? 'DC' : 'AC'
|
||||||
// DC 时电压份数为 1,AC 时为 3
|
|
||||||
params.optional.voltage_n = isDC ? 1 : 3
|
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
@@ -638,8 +629,15 @@ const calculate = async () => {
|
|||||||
// 后台线程启动计算,实时日志通过 addLogFromBackend 推送
|
// 后台线程启动计算,实时日志通过 addLogFromBackend 推送
|
||||||
// 结果通过 receiveResult 回调接收
|
// 结果通过 receiveResult 回调接收
|
||||||
// 传递动画启用状态
|
// 传递动画启用状态
|
||||||
|
// 使用 toRaw 解包响应式对象,确保 pywebview 能正确序列化参数
|
||||||
|
const rawParams = toRaw(params)
|
||||||
const paramsWithAnimation = {
|
const paramsWithAnimation = {
|
||||||
...params,
|
parameter: toRaw(rawParams.parameter),
|
||||||
|
advance: toRaw(rawParams.advance),
|
||||||
|
optional: {
|
||||||
|
...toRaw(rawParams.optional),
|
||||||
|
voltage_n: 1 // 强制将电压份数设置为1
|
||||||
|
},
|
||||||
animation_enabled: animationRef.value?.isEnabled() ?? false
|
animation_enabled: animationRef.value?.isEnabled() ?? false
|
||||||
}
|
}
|
||||||
await window.pywebview.api.calculate(paramsWithAnimation)
|
await window.pywebview.api.calculate(paramsWithAnimation)
|
||||||
@@ -794,6 +792,8 @@ const exportConfig = async () => {
|
|||||||
if (window.pywebview) {
|
if (window.pywebview) {
|
||||||
const response = await window.pywebview.api.export_config(params)
|
const response = await window.pywebview.api.export_config(params)
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
|
// 显示导出的文件路径
|
||||||
|
currentFilePath.value = response.file_path || ''
|
||||||
logRef.value?.addLog('info', response.message)
|
logRef.value?.addLog('info', response.message)
|
||||||
} else {
|
} else {
|
||||||
logRef.value?.addLog('warning', response.message)
|
logRef.value?.addLog('warning', response.message)
|
||||||
@@ -905,6 +905,15 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 禁用数字输入框的滚轮调整功能
|
||||||
|
const preventWheelOnNumberInput = (e: Event) => {
|
||||||
|
const target = e.target as HTMLInputElement
|
||||||
|
if (target && target.type === 'number') {
|
||||||
|
e.preventDefault()
|
||||||
|
;(target as HTMLElement).blur()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 注册全局日志接收函数,供后端实时调用
|
// 注册全局日志接收函数,供后端实时调用
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 程序启动时,根据雷暴日初始化地闪密度
|
// 程序启动时,根据雷暴日初始化地闪密度
|
||||||
@@ -912,6 +921,9 @@ onMounted(() => {
|
|||||||
params.advance.ng = Math.round(0.023 * Math.pow(params.parameter.td, 1.3) * 100) / 100
|
params.advance.ng = Math.round(0.023 * Math.pow(params.parameter.td, 1.3) * 100) / 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 禁用数字输入框的滚轮调整
|
||||||
|
document.addEventListener('wheel', preventWheelOnNumberInput, { passive: false })
|
||||||
|
|
||||||
// 实时日志推送
|
// 实时日志推送
|
||||||
window.addLogFromBackend = (log: { level: string; time: string; message: string }) => {
|
window.addLogFromBackend = (log: { level: string; time: string; message: string }) => {
|
||||||
logRef.value?.addLog(log.level as any, log.message)
|
logRef.value?.addLog(log.level as any, log.message)
|
||||||
@@ -934,6 +946,7 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
document.removeEventListener('wheel', preventWheelOnNumberInput)
|
||||||
window.addLogFromBackend = undefined
|
window.addLogFromBackend = undefined
|
||||||
window.receiveResult = undefined
|
window.receiveResult = undefined
|
||||||
})
|
})
|
||||||
@@ -944,4 +957,15 @@ onUnmounted(() => {
|
|||||||
user-select: text;
|
user-select: text;
|
||||||
cursor: text;
|
cursor: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 隐藏数字输入框的上下箭头 */
|
||||||
|
:deep(input[type="number"]) {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(input[type="number"]::-webkit-inner-spin-button),
|
||||||
|
:deep(input[type="number"]::-webkit-outer-spin-button) {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
3
webui/src/vite-env.d.ts
vendored
Normal file
3
webui/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare const __APP_VERSION__: string
|
||||||
@@ -2,14 +2,29 @@ import { defineConfig } from 'vite'
|
|||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import { quasar, transformAssetUrls } from '@quasar/vite-plugin'
|
import { quasar, transformAssetUrls } from '@quasar/vite-plugin'
|
||||||
import { fileURLToPath, URL } from 'node:url'
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
import { readFileSync } from 'node:fs'
|
||||||
|
|
||||||
|
const version = readFileSync(fileURLToPath(new URL('../VERSION', import.meta.url)), 'utf-8').trim()
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
define: {
|
||||||
|
__APP_VERSION__: JSON.stringify(version)
|
||||||
|
},
|
||||||
base: './',
|
base: './',
|
||||||
plugins: [
|
plugins: [
|
||||||
vue({
|
vue({
|
||||||
template: { transformAssetUrls }
|
template: { transformAssetUrls }
|
||||||
}),
|
}),
|
||||||
quasar()
|
quasar(),
|
||||||
|
{
|
||||||
|
name: 'html-version',
|
||||||
|
transformIndexHtml(html) {
|
||||||
|
return html.replace(
|
||||||
|
/<title>EGM 输电线路绕击跳闸率计算( v[\d.]+)?<\/title>/,
|
||||||
|
`<title>EGM 输电线路绕击跳闸率计算 v${version}</title>`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
|||||||
Reference in New Issue
Block a user