feat(edc-web): 线圈参数/模拟车辆参数管理 + 工装关联 + 测试环境显示
新增功能: - 线圈参数管理页 (/coil-info): 增删改查,日志记录 - 模拟车辆管理页 (/simulate-car): 增删改查,日志记录 - 工装配置页新增线圈/模拟车辆选择区,保存时关联到 tb_fixture_param - 测试信息查询页新增「测试环境」列,显示当前线圈和模拟车辆信息 - edc_server 写入测试数据时自动从 fixture 获取线圈/车辆关联 数据库: - 新增 tb_coil_info、tb_simulate_car 表 - tb_fixture_param 增加 coil_id/simulate_car_id 字段 - tb_state_tst 增加 coil_id/simulate_car_id 字段 后端: - models.py 新增线圈/模拟车辆 CRUD - get_fixture_param 改为 LEFT JOIN 返回线圈/车辆详情 - upsert_fixture_param 支持 coil_id/simulate_car_id - 测试数据查询 LEFT JOIN 线圈/车辆信息
This commit is contained in:
@@ -192,8 +192,13 @@ def get_test_data(page: int = 1, per_page: int = 20,
|
||||
# data
|
||||
offset = (page - 1) * per_page
|
||||
cur.execute(
|
||||
f"SELECT t.*, d.serial FROM tb_state_tst t "
|
||||
f"SELECT t.*, d.serial, "
|
||||
f"c.coil_num, c.name as coil_name, "
|
||||
f"sc.simulate_num, sc.name as car_name "
|
||||
f"FROM tb_state_tst t "
|
||||
f"JOIN dnt_info d ON t.dnt_id = d.id "
|
||||
f"LEFT JOIN tb_coil_info c ON t.coil_id = c.id "
|
||||
f"LEFT JOIN tb_simulate_car sc ON t.simulate_car_id = sc.id "
|
||||
f"WHERE {where_clause} "
|
||||
f"ORDER BY t.id DESC LIMIT %s OFFSET %s",
|
||||
params + [per_page, offset],
|
||||
@@ -236,8 +241,13 @@ def get_all_test_data_for_export(serial: str = "", date_from: str = "",
|
||||
|
||||
where_clause = " AND ".join(where) if where else "1=1"
|
||||
cur.execute(
|
||||
f"SELECT t.*, d.serial FROM tb_state_tst t "
|
||||
f"SELECT t.*, d.serial, "
|
||||
f"c.coil_num, c.name as coil_name, "
|
||||
f"sc.simulate_num, sc.name as car_name "
|
||||
f"FROM tb_state_tst t "
|
||||
f"JOIN dnt_info d ON t.dnt_id = d.id "
|
||||
f"LEFT JOIN tb_coil_info c ON t.coil_id = c.id "
|
||||
f"LEFT JOIN tb_simulate_car sc ON t.simulate_car_id = sc.id "
|
||||
f"WHERE {where_clause} ORDER BY t.id DESC",
|
||||
params,
|
||||
)
|
||||
@@ -446,12 +456,24 @@ def get_logs(page: int = 1, per_page: int = 30,
|
||||
# ─── tb_fixture_param ──────────────────────────────────────────────
|
||||
|
||||
def get_fixture_param(dnt_id: int) -> dict | None:
|
||||
"""获取设备的工装测试参数"""
|
||||
"""获取设备的工装测试参数(含线圈和模拟车辆信息)"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT * FROM tb_fixture_param WHERE dnt_id=%s", (dnt_id,),
|
||||
"SELECT fp.*, "
|
||||
"c.coil_num, c.name as coil_name, c.shape as coil_shape, "
|
||||
"c.length as coil_length, c.width as coil_width, c.radius as coil_radius, "
|
||||
"c.turns as coil_turns, c.resistance as coil_resistance, "
|
||||
"c.material as coil_material, "
|
||||
"sc.simulate_num, sc.name as car_name, sc.shape as car_shape, "
|
||||
"sc.length as car_length, sc.width as car_width, sc.radius as car_radius, "
|
||||
"sc.material as car_material "
|
||||
"FROM tb_fixture_param fp "
|
||||
"LEFT JOIN tb_coil_info c ON fp.coil_id = c.id "
|
||||
"LEFT JOIN tb_simulate_car sc ON fp.simulate_car_id = sc.id "
|
||||
"WHERE fp.dnt_id=%s",
|
||||
(dnt_id,),
|
||||
)
|
||||
return cur.fetchone()
|
||||
finally:
|
||||
@@ -467,6 +489,7 @@ def upsert_fixture_param(dnt_id: int, **kwargs):
|
||||
"SELECT id FROM tb_fixture_param WHERE dnt_id=%s", (dnt_id,),
|
||||
)
|
||||
existing = cur.fetchone()
|
||||
# 主线参数字段(不含 coil_id/simulate_car_id,后面单独处理)
|
||||
fields = [
|
||||
"Addr", "DevType", "TestMode", "RestDis", "MinusDis",
|
||||
"SensMin", "SensMax", "FreMin", "FreMax", "PeakMin", "PeakMax",
|
||||
@@ -488,6 +511,17 @@ def upsert_fixture_param(dnt_id: int, **kwargs):
|
||||
f"VALUES (%s, {placeholders})",
|
||||
[dnt_id] + values,
|
||||
)
|
||||
# 单独处理线圈/模拟车辆关联(可选,不覆盖已有值)
|
||||
if "coil_id" in kwargs:
|
||||
cur.execute(
|
||||
"UPDATE tb_fixture_param SET coil_id=%s WHERE dnt_id=%s",
|
||||
(kwargs["coil_id"], dnt_id),
|
||||
)
|
||||
if "simulate_car_id" in kwargs:
|
||||
cur.execute(
|
||||
"UPDATE tb_fixture_param SET simulate_car_id=%s WHERE dnt_id=%s",
|
||||
(kwargs["simulate_car_id"], dnt_id),
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
@@ -584,6 +618,172 @@ def delete_vehicle_base_test(test_id: int):
|
||||
conn.close()
|
||||
|
||||
|
||||
# ─── 线圈参数 CRUD ──────────────────────────────────────────────────
|
||||
|
||||
def get_coil_info_list(search: str = "") -> list[dict]:
|
||||
"""获取线圈参数列表"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
if search:
|
||||
cur.execute(
|
||||
"SELECT * FROM tb_coil_info WHERE coil_num LIKE %s OR name LIKE %s "
|
||||
"ORDER BY id DESC",
|
||||
(f"%{search}%", f"%{search}%"),
|
||||
)
|
||||
else:
|
||||
cur.execute("SELECT * FROM tb_coil_info ORDER BY id DESC")
|
||||
return cur.fetchall()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def get_coil_info_by_id(coil_id: int) -> dict | None:
|
||||
"""获取单个线圈参数"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT * FROM tb_coil_info WHERE id=%s", (coil_id,))
|
||||
return cur.fetchone()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def create_coil_info(**kwargs) -> int:
|
||||
"""创建线圈参数,返回新 ID"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
fields = [
|
||||
"coil_num", "name", "induct", "shape", "length", "width",
|
||||
"radius", "turns", "resistance", "material", "remark",
|
||||
]
|
||||
col_names = ", ".join(f"`{f}`" for f in fields)
|
||||
placeholders = ", ".join(["%s"] * len(fields))
|
||||
values = [kwargs.get(f, "" if f in ("coil_num", "name", "shape", "material", "remark") else 0) for f in fields]
|
||||
cur.execute(
|
||||
f"INSERT INTO tb_coil_info ({col_names}) VALUES ({placeholders})",
|
||||
values,
|
||||
)
|
||||
conn.commit()
|
||||
return cur.lastrowid
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def update_coil_info(coil_id: int, **kwargs):
|
||||
"""更新线圈参数"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
fields = [
|
||||
"coil_num", "name", "induct", "shape", "length", "width",
|
||||
"radius", "turns", "resistance", "material", "remark",
|
||||
]
|
||||
sets = ", ".join(f"`{f}`=%s" for f in fields)
|
||||
values = [kwargs.get(f, "" if f in ("coil_num", "name", "shape", "material", "remark") else 0) for f in fields] + [coil_id]
|
||||
cur.execute(
|
||||
f"UPDATE tb_coil_info SET {sets} WHERE id=%s", values,
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def delete_coil_info(coil_id: int):
|
||||
"""删除线圈参数"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("DELETE FROM tb_coil_info WHERE id=%s", (coil_id,))
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
# ─── 模拟车辆参数 CRUD ──────────────────────────────────────────────
|
||||
|
||||
def get_simulate_car_list(search: str = "") -> list[dict]:
|
||||
"""获取模拟车辆参数列表"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
if search:
|
||||
cur.execute(
|
||||
"SELECT * FROM tb_simulate_car WHERE simulate_num LIKE %s OR name LIKE %s "
|
||||
"ORDER BY id DESC",
|
||||
(f"%{search}%", f"%{search}%"),
|
||||
)
|
||||
else:
|
||||
cur.execute("SELECT * FROM tb_simulate_car ORDER BY id DESC")
|
||||
return cur.fetchall()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def get_simulate_car_by_id(car_id: int) -> dict | None:
|
||||
"""获取单个模拟车辆参数"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT * FROM tb_simulate_car WHERE id=%s", (car_id,))
|
||||
return cur.fetchone()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def create_simulate_car(**kwargs) -> int:
|
||||
"""创建模拟车辆参数,返回新 ID"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
fields = [
|
||||
"simulate_num", "name", "shape", "length", "width",
|
||||
"radius", "material", "remark",
|
||||
]
|
||||
col_names = ", ".join(f"`{f}`" for f in fields)
|
||||
placeholders = ", ".join(["%s"] * len(fields))
|
||||
values = [kwargs.get(f, "" if f in ("simulate_num", "name", "shape", "material", "remark") else 0) for f in fields]
|
||||
cur.execute(
|
||||
f"INSERT INTO tb_simulate_car ({col_names}) VALUES ({placeholders})",
|
||||
values,
|
||||
)
|
||||
conn.commit()
|
||||
return cur.lastrowid
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def update_simulate_car(car_id: int, **kwargs):
|
||||
"""更新模拟车辆参数"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
fields = [
|
||||
"simulate_num", "name", "shape", "length", "width",
|
||||
"radius", "material", "remark",
|
||||
]
|
||||
sets = ", ".join(f"`{f}`=%s" for f in fields)
|
||||
values = [kwargs.get(f, "" if f in ("simulate_num", "name", "shape", "material", "remark") else 0) for f in fields] + [car_id]
|
||||
cur.execute(
|
||||
f"UPDATE tb_simulate_car SET {sets} WHERE id=%s", values,
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def delete_simulate_car(car_id: int):
|
||||
"""删除模拟车辆参数"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("DELETE FROM tb_simulate_car WHERE id=%s", (car_id,))
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
# ─── 测试数据删除 ──────────────────────────────────────────────
|
||||
|
||||
def delete_test_data(serial: str = "", date_from: str = "",
|
||||
|
||||
@@ -14,6 +14,16 @@ from app.models import (
|
||||
delete_vehicle_base_test,
|
||||
get_serialnet_by_id,
|
||||
insert_log,
|
||||
get_coil_info_list,
|
||||
get_coil_info_by_id,
|
||||
create_coil_info,
|
||||
update_coil_info,
|
||||
delete_coil_info,
|
||||
get_simulate_car_list,
|
||||
get_simulate_car_by_id,
|
||||
create_simulate_car,
|
||||
update_simulate_car,
|
||||
delete_simulate_car,
|
||||
)
|
||||
|
||||
bp = Blueprint("fixture", __name__)
|
||||
@@ -275,3 +285,181 @@ def api_delete_vehicle_base_test(test_id):
|
||||
return jsonify({"ok": True})
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
# ─── 线圈参数页面 ──────────────────────────────────────────────────
|
||||
|
||||
@bp.route("/coil-info")
|
||||
@login_required
|
||||
def coil_info_page():
|
||||
"""线圈参数管理页面"""
|
||||
return render_template("coil_info.html")
|
||||
|
||||
|
||||
@bp.route("/api/coil-info")
|
||||
@login_required
|
||||
def api_list_coil_info():
|
||||
"""列出线圈参数"""
|
||||
search = request.args.get("search", "")
|
||||
items = get_coil_info_list(search)
|
||||
return jsonify(items)
|
||||
|
||||
|
||||
@bp.route("/api/coil-info/<int:coil_id>")
|
||||
@login_required
|
||||
def api_get_coil_info(coil_id):
|
||||
"""获取单个线圈参数"""
|
||||
item = get_coil_info_by_id(coil_id)
|
||||
if not item:
|
||||
return jsonify({"error": "不存在"}), 404
|
||||
return jsonify(item)
|
||||
|
||||
|
||||
@bp.route("/api/coil-info", methods=["POST"])
|
||||
@login_required
|
||||
def api_create_coil_info():
|
||||
"""创建线圈参数"""
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"ok": False, "error": "数据为空"}), 400
|
||||
try:
|
||||
coil_id = create_coil_info(**data)
|
||||
insert_log(
|
||||
current_user.id, current_user.username, "create",
|
||||
target="coil_info",
|
||||
detail=f"创建线圈: {data.get('coil_num','')} {data.get('name','')}",
|
||||
result="ok", ip=request.remote_addr or "",
|
||||
)
|
||||
return jsonify({"ok": True, "id": coil_id})
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@bp.route("/api/coil-info/<int:coil_id>", methods=["PUT"])
|
||||
@login_required
|
||||
def api_update_coil_info(coil_id):
|
||||
"""更新线圈参数"""
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"ok": False, "error": "数据为空"}), 400
|
||||
try:
|
||||
update_coil_info(coil_id, **data)
|
||||
insert_log(
|
||||
current_user.id, current_user.username, "update",
|
||||
target="coil_info",
|
||||
detail=f"更新线圈 id={coil_id}: {data.get('coil_num','')} {data.get('name','')}",
|
||||
result="ok", ip=request.remote_addr or "",
|
||||
)
|
||||
return jsonify({"ok": True})
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@bp.route("/api/coil-info/<int:coil_id>", methods=["DELETE"])
|
||||
@login_required
|
||||
def api_delete_coil_info(coil_id):
|
||||
"""删除线圈参数"""
|
||||
try:
|
||||
item = get_coil_info_by_id(coil_id)
|
||||
detail = f"删除线圈 id={coil_id}"
|
||||
if item:
|
||||
detail += f": {item.get('coil_num','')} {item.get('name','')}"
|
||||
delete_coil_info(coil_id)
|
||||
insert_log(
|
||||
current_user.id, current_user.username, "delete",
|
||||
target="coil_info",
|
||||
detail=detail,
|
||||
result="ok", ip=request.remote_addr or "",
|
||||
)
|
||||
return jsonify({"ok": True})
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
# ─── 模拟车辆参数页面 ──────────────────────────────────────────────
|
||||
|
||||
@bp.route("/simulate-car")
|
||||
@login_required
|
||||
def simulate_car_page():
|
||||
"""模拟车辆参数管理页面"""
|
||||
return render_template("simulate_car.html")
|
||||
|
||||
|
||||
@bp.route("/api/simulate-car")
|
||||
@login_required
|
||||
def api_list_simulate_car():
|
||||
"""列出模拟车辆参数"""
|
||||
search = request.args.get("search", "")
|
||||
items = get_simulate_car_list(search)
|
||||
return jsonify(items)
|
||||
|
||||
|
||||
@bp.route("/api/simulate-car/<int:car_id>")
|
||||
@login_required
|
||||
def api_get_simulate_car(car_id):
|
||||
"""获取单个模拟车辆参数"""
|
||||
item = get_simulate_car_by_id(car_id)
|
||||
if not item:
|
||||
return jsonify({"error": "不存在"}), 404
|
||||
return jsonify(item)
|
||||
|
||||
|
||||
@bp.route("/api/simulate-car", methods=["POST"])
|
||||
@login_required
|
||||
def api_create_simulate_car():
|
||||
"""创建模拟车辆参数"""
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"ok": False, "error": "数据为空"}), 400
|
||||
try:
|
||||
car_id = create_simulate_car(**data)
|
||||
insert_log(
|
||||
current_user.id, current_user.username, "create",
|
||||
target="simulate_car",
|
||||
detail=f"创建模拟车辆: {data.get('simulate_num','')} {data.get('name','')}",
|
||||
result="ok", ip=request.remote_addr or "",
|
||||
)
|
||||
return jsonify({"ok": True, "id": car_id})
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@bp.route("/api/simulate-car/<int:car_id>", methods=["PUT"])
|
||||
@login_required
|
||||
def api_update_simulate_car(car_id):
|
||||
"""更新模拟车辆参数"""
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"ok": False, "error": "数据为空"}), 400
|
||||
try:
|
||||
update_simulate_car(car_id, **data)
|
||||
insert_log(
|
||||
current_user.id, current_user.username, "update",
|
||||
target="simulate_car",
|
||||
detail=f"更新模拟车辆 id={car_id}: {data.get('simulate_num','')} {data.get('name','')}",
|
||||
result="ok", ip=request.remote_addr or "",
|
||||
)
|
||||
return jsonify({"ok": True})
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@bp.route("/api/simulate-car/<int:car_id>", methods=["DELETE"])
|
||||
@login_required
|
||||
def api_delete_simulate_car(car_id):
|
||||
"""删除模拟车辆参数"""
|
||||
try:
|
||||
item = get_simulate_car_by_id(car_id)
|
||||
detail = f"删除模拟车辆 id={car_id}"
|
||||
if item:
|
||||
detail += f": {item.get('simulate_num','')} {item.get('name','')}"
|
||||
delete_simulate_car(car_id)
|
||||
insert_log(
|
||||
current_user.id, current_user.username, "delete",
|
||||
target="simulate_car",
|
||||
detail=detail,
|
||||
result="ok", ip=request.remote_addr or "",
|
||||
)
|
||||
return jsonify({"ok": True})
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": str(e)}), 500
|
||||
|
||||
173
edc-web/app/static/js/coil_info.js
Normal file
173
edc-web/app/static/js/coil_info.js
Normal file
@@ -0,0 +1,173 @@
|
||||
// 线圈参数管理
|
||||
|
||||
let editId = null; // null=新增, number=编辑
|
||||
|
||||
// ─── Toast ───────────────────────────────────
|
||||
|
||||
function toast(msg, isError = false) {
|
||||
const el = document.getElementById("toast");
|
||||
el.textContent = msg;
|
||||
el.className = "msg-toast " + (isError ? "error" : "") + " show";
|
||||
clearTimeout(el._timeout);
|
||||
el._timeout = setTimeout(() => { el.className = "msg-toast"; }, 3000);
|
||||
}
|
||||
|
||||
// ─── 列表加载 ────────────────────────────────
|
||||
|
||||
async function loadList() {
|
||||
const search = document.getElementById("search-input").value;
|
||||
try {
|
||||
const resp = await fetch(`/api/coil-info?search=${encodeURIComponent(search)}`);
|
||||
const data = await resp.json();
|
||||
renderTable(data);
|
||||
} catch (e) {
|
||||
console.error("加载失败:", e);
|
||||
}
|
||||
}
|
||||
|
||||
function sizeLabel(item) {
|
||||
if (item.shape === '圆形') return `半径${item.radius || 0}cm`;
|
||||
if (item.shape === '矩形') return `${item.length || 0}×${item.width || 0}cm`;
|
||||
return '-';
|
||||
}
|
||||
|
||||
function renderTable(data) {
|
||||
const tbody = document.querySelector("#coil-table tbody");
|
||||
if (!data.length) {
|
||||
tbody.innerHTML = '<tr><td colspan="10" style="color:#999;text-align:center;">暂无数据,点右上角「新增」添加</td></tr>';
|
||||
return;
|
||||
}
|
||||
tbody.innerHTML = data.map(t => `
|
||||
<tr>
|
||||
<td>${esc(t.coil_num)}</td>
|
||||
<td>${esc(t.name)}</td>
|
||||
<td>${t.induct || '-'}</td>
|
||||
<td>${t.shape || '-'}</td>
|
||||
<td>${sizeLabel(t)}</td>
|
||||
<td>${t.turns || '-'}</td>
|
||||
<td>${t.resistance || '-'}</td>
|
||||
<td>${esc(t.material || '-')}</td>
|
||||
<td>${esc(t.remark || '-')}</td>
|
||||
<td>
|
||||
<button class="btn-edit" onclick="openModal(${t.id})">编辑</button>
|
||||
<button class="btn-del" onclick="deleteRecord(${t.id}, '${esc(t.coil_num || t.name)}')">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join("");
|
||||
}
|
||||
|
||||
// ─── 弹窗 ────────────────────────────────────
|
||||
|
||||
function openModal(id = null) {
|
||||
editId = id;
|
||||
document.getElementById("modal-title").textContent = id ? "编辑线圈参数" : "新增线圈参数";
|
||||
document.getElementById("edit-modal").style.display = "flex";
|
||||
|
||||
if (id) {
|
||||
fetch(`/api/coil-info/${id}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
document.getElementById("edit-coil-num").value = data.coil_num || "";
|
||||
document.getElementById("edit-name").value = data.name || "";
|
||||
document.getElementById("edit-induct").value = data.induct || 0;
|
||||
document.getElementById("edit-shape").value = data.shape || "";
|
||||
document.getElementById("edit-length").value = data.length || 0;
|
||||
document.getElementById("edit-width").value = data.width || 0;
|
||||
document.getElementById("edit-radius").value = data.radius || 0;
|
||||
document.getElementById("edit-turns").value = data.turns || 0;
|
||||
document.getElementById("edit-resistance").value = data.resistance || 0;
|
||||
document.getElementById("edit-material").value = data.material || "";
|
||||
document.getElementById("edit-remark").value = data.remark || "";
|
||||
});
|
||||
} else {
|
||||
document.getElementById("edit-coil-num").value = "";
|
||||
document.getElementById("edit-name").value = "";
|
||||
document.getElementById("edit-induct").value = "0";
|
||||
document.getElementById("edit-shape").value = "";
|
||||
document.getElementById("edit-length").value = "0";
|
||||
document.getElementById("edit-width").value = "0";
|
||||
document.getElementById("edit-radius").value = "0";
|
||||
document.getElementById("edit-turns").value = "0";
|
||||
document.getElementById("edit-resistance").value = "0";
|
||||
document.getElementById("edit-material").value = "";
|
||||
document.getElementById("edit-remark").value = "";
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById("edit-modal").style.display = "none";
|
||||
editId = null;
|
||||
}
|
||||
|
||||
// ─── 保存 ────────────────────────────────────
|
||||
|
||||
async function saveRecord() {
|
||||
const data = {
|
||||
coil_num: document.getElementById("edit-coil-num").value.trim(),
|
||||
name: document.getElementById("edit-name").value.trim(),
|
||||
induct: parseFloat(document.getElementById("edit-induct").value) || 0,
|
||||
shape: document.getElementById("edit-shape").value,
|
||||
length: parseFloat(document.getElementById("edit-length").value) || 0,
|
||||
width: parseFloat(document.getElementById("edit-width").value) || 0,
|
||||
radius: parseFloat(document.getElementById("edit-radius").value) || 0,
|
||||
turns: parseInt(document.getElementById("edit-turns").value) || 0,
|
||||
resistance: parseFloat(document.getElementById("edit-resistance").value) || 0,
|
||||
material: document.getElementById("edit-material").value.trim(),
|
||||
remark: document.getElementById("edit-remark").value.trim(),
|
||||
};
|
||||
|
||||
if (!data.coil_num && !data.name) {
|
||||
toast("请输入线圈编号或名称", true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let resp;
|
||||
if (editId) {
|
||||
resp = await fetch(`/api/coil-info/${editId}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
} else {
|
||||
resp = await fetch("/api/coil-info", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
const result = await resp.json();
|
||||
if (result.ok || resp.ok) {
|
||||
toast(editId ? "更新成功" : "新增成功");
|
||||
closeModal();
|
||||
loadList();
|
||||
} else {
|
||||
toast("保存失败: " + (result.error || "未知错误"), true);
|
||||
}
|
||||
} catch (e) {
|
||||
toast("保存失败: " + e.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 删除 ────────────────────────────────────
|
||||
|
||||
async function deleteRecord(id, label) {
|
||||
if (!confirm(`确定要删除「${label}」吗?`)) return;
|
||||
|
||||
try {
|
||||
const resp = await fetch(`/api/coil-info/${id}`, { method: "DELETE" });
|
||||
const result = await resp.json();
|
||||
if (result.ok) {
|
||||
toast("删除成功");
|
||||
loadList();
|
||||
} else {
|
||||
toast("删除失败: " + (result.error || "未知错误"), true);
|
||||
}
|
||||
} catch (e) {
|
||||
toast("删除失败: " + e.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
function esc(s) { return (s || "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """); }
|
||||
|
||||
loadList();
|
||||
@@ -9,6 +9,8 @@ let pollTimers = {}; // record_id → timer (指令响应轮询)
|
||||
|
||||
async function init() {
|
||||
await loadBaseTests();
|
||||
await loadCoilList();
|
||||
await loadCarList();
|
||||
await loadFixtureParam();
|
||||
}
|
||||
|
||||
@@ -119,6 +121,66 @@ function onDevTypeChange() {
|
||||
else { selectedBaseTest = null; renderBaseTestTable(); }
|
||||
}
|
||||
|
||||
// ─── 线圈列表 ────────────────────────────────
|
||||
|
||||
let coilList = [];
|
||||
|
||||
async function loadCoilList() {
|
||||
try {
|
||||
const resp = await fetch("/api/coil-info");
|
||||
coilList = await resp.json();
|
||||
populateCoilSelect();
|
||||
} catch (e) { console.error("加载线圈列表失败:", e); }
|
||||
}
|
||||
|
||||
function populateCoilSelect() {
|
||||
const sel = document.getElementById("coil-select");
|
||||
sel.innerHTML = '<option value="">-- 选择线圈 --</option>' +
|
||||
coilList.map(c => `<option value="${c.id}">${esc(c.coil_num || c.name || `#${c.id}`)}</option>`).join("");
|
||||
}
|
||||
|
||||
function onCoilChange() {
|
||||
const id = parseInt(document.getElementById("coil-select").value);
|
||||
const coil = coilList.find(c => c.id === id);
|
||||
const detail = document.getElementById("coil-detail");
|
||||
if (coil) {
|
||||
const sizeText = coil.shape === '圆形' ? `半径${coil.radius}cm` : `${coil.length}×${coil.width}cm`;
|
||||
detail.innerHTML = `${coil.shape || '-'} / ${sizeText} / ${coil.turns || 0}圈 / ${coil.resistance || 0}Ω / ${coil.material || ''}`;
|
||||
} else {
|
||||
detail.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 模拟车辆列表 ────────────────────────────
|
||||
|
||||
let carList = [];
|
||||
|
||||
async function loadCarList() {
|
||||
try {
|
||||
const resp = await fetch("/api/simulate-car");
|
||||
carList = await resp.json();
|
||||
populateCarSelect();
|
||||
} catch (e) { console.error("加载模拟车辆列表失败:", e); }
|
||||
}
|
||||
|
||||
function populateCarSelect() {
|
||||
const sel = document.getElementById("car-select");
|
||||
sel.innerHTML = '<option value="">-- 选择模拟车辆 --</option>' +
|
||||
carList.map(c => `<option value="${c.id}">${esc(c.simulate_num || c.name || `#${c.id}`)}</option>`).join("");
|
||||
}
|
||||
|
||||
function onCarChange() {
|
||||
const id = parseInt(document.getElementById("car-select").value);
|
||||
const car = carList.find(c => c.id === id);
|
||||
const detail = document.getElementById("car-detail");
|
||||
if (car) {
|
||||
const sizeText = car.shape === '圆形' ? `半径${car.radius}cm` : `${car.length}×${car.width}cm`;
|
||||
detail.innerHTML = `${car.shape || '-'} / ${sizeText} / ${car.material || ''}`;
|
||||
} else {
|
||||
detail.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 从 DB 加载/刷新/保存 ────────────────────
|
||||
|
||||
async function loadFixtureParam() {
|
||||
@@ -152,6 +214,17 @@ function fillFormFromParam(param) {
|
||||
document.getElementById("param-far-stay").value = param.FarStay || 0;
|
||||
const matched = baseTests.find(t => t.type_num === param.DevType);
|
||||
if (matched) { selectedBaseTest = matched; renderBaseTestTable(); }
|
||||
// 设置线圈和模拟车辆选中
|
||||
if (param.coil_id) {
|
||||
document.getElementById("coil-select").value = param.coil_id;
|
||||
onCoilChange();
|
||||
}
|
||||
if (param.simulate_car_id) {
|
||||
document.getElementById("car-select").value = param.simulate_car_id;
|
||||
onCarChange();
|
||||
}
|
||||
// 更新当前关联标签
|
||||
updateCurrentLabels(param);
|
||||
}
|
||||
|
||||
async function refreshParams() {
|
||||
@@ -168,31 +241,55 @@ async function refreshParams() {
|
||||
|
||||
async function saveToDb() {
|
||||
const data = getFormParams();
|
||||
const coilId = parseInt(document.getElementById("coil-select").value) || null;
|
||||
const carId = parseInt(document.getElementById("car-select").value) || null;
|
||||
const body = {
|
||||
Addr: data.addr, DevType: data.dev_type, TestMode: data.test_mode,
|
||||
RestDis: data.reset_dis, MinusDis: data.minus_dis,
|
||||
SensMin: data.sens_min, SensMax: data.sens_max,
|
||||
FreMin: data.fre_min, FreMax: data.fre_max,
|
||||
PeakMin: data.peak_min, PeakMax: data.peak_max,
|
||||
FarTol: data.far_tol, NearTol: data.near_tol,
|
||||
StepTol: data.step_tol, BackForth: data.back_forth,
|
||||
NearStay: data.near_stay, FarStay: data.far_stay,
|
||||
};
|
||||
if (coilId) body.coil_id = coilId;
|
||||
if (carId) body.simulate_car_id = carId;
|
||||
try {
|
||||
const resp = await fetch(`/api/fixture/param/${DNT_ID}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
Addr: data.addr, DevType: data.dev_type, TestMode: data.test_mode,
|
||||
RestDis: data.reset_dis, MinusDis: data.minus_dis,
|
||||
SensMin: data.sens_min, SensMax: data.sens_max,
|
||||
FreMin: data.fre_min, FreMax: data.fre_max,
|
||||
PeakMin: data.peak_min, PeakMax: data.peak_max,
|
||||
FarTol: data.far_tol, NearTol: data.near_tol,
|
||||
StepTol: data.step_tol, BackForth: data.back_forth,
|
||||
NearStay: data.near_stay, FarStay: data.far_stay,
|
||||
}),
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const result = await resp.json();
|
||||
if (result.ok) {
|
||||
commLog('info', null, '参数已保存到数据库');
|
||||
toast("已保存到数据库");
|
||||
updateCurrentLabels();
|
||||
} else {
|
||||
toast("保存失败: " + (result.error || ""), true);
|
||||
}
|
||||
} catch (e) { toast("保存失败: " + e.message, true); }
|
||||
}
|
||||
|
||||
/** 更新页面上的当前线圈/车辆标签 */
|
||||
function updateCurrentLabels(param) {
|
||||
const coilLabel = document.getElementById("current-coil-label");
|
||||
const carLabel = document.getElementById("current-car-label");
|
||||
if (param) {
|
||||
coilLabel.textContent = param.coil_name || param.coil_num || '未设置';
|
||||
carLabel.textContent = param.car_name || param.simulate_num || '未设置';
|
||||
} else {
|
||||
// 从 DOM 读取当前选择
|
||||
const coilId = parseInt(document.getElementById("coil-select").value);
|
||||
const carId = parseInt(document.getElementById("car-select").value);
|
||||
const coil = coilId ? coilList.find(c => c.id === coilId) : null;
|
||||
const car = carId ? carList.find(c => c.id === carId) : null;
|
||||
coilLabel.textContent = coil ? (coil.coil_num || coil.name) : '未设置';
|
||||
carLabel.textContent = car ? (car.simulate_num || car.name) : '未设置';
|
||||
}
|
||||
}
|
||||
|
||||
function getFormParams() {
|
||||
return {
|
||||
addr: parseInt(document.getElementById("param-addr").value) || 1,
|
||||
@@ -324,19 +421,24 @@ async function sendConfig() {
|
||||
startRespPolling(data.record_id, "4B");
|
||||
|
||||
// 同时保存到数据库
|
||||
const coilId = parseInt(document.getElementById("coil-select").value) || null;
|
||||
const carId = parseInt(document.getElementById("car-select").value) || null;
|
||||
const saveBody = {
|
||||
Addr: params.addr, DevType: params.dev_type, TestMode: params.test_mode,
|
||||
RestDis: params.reset_dis, MinusDis: params.minus_dis,
|
||||
SensMin: params.sens_min, SensMax: params.sens_max,
|
||||
FreMin: params.fre_min, FreMax: params.fre_max,
|
||||
PeakMin: params.peak_min, PeakMax: params.peak_max,
|
||||
FarTol: params.far_tol, NearTol: params.near_tol,
|
||||
StepTol: params.step_tol, BackForth: params.back_forth,
|
||||
NearStay: params.near_stay, FarStay: params.far_stay,
|
||||
};
|
||||
if (coilId) saveBody.coil_id = coilId;
|
||||
if (carId) saveBody.simulate_car_id = carId;
|
||||
await fetch(`/api/fixture/param/${DNT_ID}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
Addr: params.addr, DevType: params.dev_type, TestMode: params.test_mode,
|
||||
RestDis: params.reset_dis, MinusDis: params.minus_dis,
|
||||
SensMin: params.sens_min, SensMax: params.sens_max,
|
||||
FreMin: params.fre_min, FreMax: params.fre_max,
|
||||
PeakMin: params.peak_min, PeakMax: params.peak_max,
|
||||
FarTol: params.far_tol, NearTol: params.near_tol,
|
||||
StepTol: params.step_tol, BackForth: params.back_forth,
|
||||
NearStay: params.near_stay, FarStay: params.far_stay,
|
||||
}),
|
||||
body: JSON.stringify(saveBody),
|
||||
});
|
||||
} else {
|
||||
toast(`失败: ${data.error}`, true);
|
||||
|
||||
151
edc-web/app/static/js/simulate_car.js
Normal file
151
edc-web/app/static/js/simulate_car.js
Normal file
@@ -0,0 +1,151 @@
|
||||
// 模拟车辆参数管理
|
||||
|
||||
let editId = null;
|
||||
|
||||
function toast(msg, isError = false) {
|
||||
const el = document.getElementById("toast");
|
||||
el.textContent = msg;
|
||||
el.className = "msg-toast " + (isError ? "error" : "") + " show";
|
||||
clearTimeout(el._timeout);
|
||||
el._timeout = setTimeout(() => { el.className = "msg-toast"; }, 3000);
|
||||
}
|
||||
|
||||
async function loadList() {
|
||||
const search = document.getElementById("search-input").value;
|
||||
try {
|
||||
const resp = await fetch(`/api/simulate-car?search=${encodeURIComponent(search)}`);
|
||||
const data = await resp.json();
|
||||
renderTable(data);
|
||||
} catch (e) {
|
||||
console.error("加载失败:", e);
|
||||
}
|
||||
}
|
||||
|
||||
function sizeLabel(item) {
|
||||
if (item.shape === '圆形') return `半径${item.radius || 0}cm`;
|
||||
if (item.shape === '矩形') return `${item.length || 0}×${item.width || 0}cm`;
|
||||
return '-';
|
||||
}
|
||||
|
||||
function renderTable(data) {
|
||||
const tbody = document.querySelector("#car-table tbody");
|
||||
if (!data.length) {
|
||||
tbody.innerHTML = '<tr><td colspan="7" style="color:#999;text-align:center;">暂无数据,点右上角「新增」添加</td></tr>';
|
||||
return;
|
||||
}
|
||||
tbody.innerHTML = data.map(t => `
|
||||
<tr>
|
||||
<td>${esc(t.simulate_num)}</td>
|
||||
<td>${esc(t.name)}</td>
|
||||
<td>${t.shape || '-'}</td>
|
||||
<td>${sizeLabel(t)}</td>
|
||||
<td>${esc(t.material || '-')}</td>
|
||||
<td>${esc(t.remark || '-')}</td>
|
||||
<td>
|
||||
<button class="btn-edit" onclick="openModal(${t.id})">编辑</button>
|
||||
<button class="btn-del" onclick="deleteRecord(${t.id}, '${esc(t.simulate_num || t.name)}')">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join("");
|
||||
}
|
||||
|
||||
function openModal(id = null) {
|
||||
editId = id;
|
||||
document.getElementById("modal-title").textContent = id ? "编辑模拟车辆参数" : "新增模拟车辆参数";
|
||||
document.getElementById("edit-modal").style.display = "flex";
|
||||
|
||||
if (id) {
|
||||
fetch(`/api/simulate-car/${id}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
document.getElementById("edit-simulate-num").value = data.simulate_num || "";
|
||||
document.getElementById("edit-name").value = data.name || "";
|
||||
document.getElementById("edit-shape").value = data.shape || "";
|
||||
document.getElementById("edit-length").value = data.length || 0;
|
||||
document.getElementById("edit-width").value = data.width || 0;
|
||||
document.getElementById("edit-radius").value = data.radius || 0;
|
||||
document.getElementById("edit-material").value = data.material || "";
|
||||
document.getElementById("edit-remark").value = data.remark || "";
|
||||
});
|
||||
} else {
|
||||
document.getElementById("edit-simulate-num").value = "";
|
||||
document.getElementById("edit-name").value = "";
|
||||
document.getElementById("edit-shape").value = "";
|
||||
document.getElementById("edit-length").value = "0";
|
||||
document.getElementById("edit-width").value = "0";
|
||||
document.getElementById("edit-radius").value = "0";
|
||||
document.getElementById("edit-material").value = "";
|
||||
document.getElementById("edit-remark").value = "";
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById("edit-modal").style.display = "none";
|
||||
editId = null;
|
||||
}
|
||||
|
||||
async function saveRecord() {
|
||||
const data = {
|
||||
simulate_num: document.getElementById("edit-simulate-num").value.trim(),
|
||||
name: document.getElementById("edit-name").value.trim(),
|
||||
shape: document.getElementById("edit-shape").value,
|
||||
length: parseFloat(document.getElementById("edit-length").value) || 0,
|
||||
width: parseFloat(document.getElementById("edit-width").value) || 0,
|
||||
radius: parseFloat(document.getElementById("edit-radius").value) || 0,
|
||||
material: document.getElementById("edit-material").value.trim(),
|
||||
remark: document.getElementById("edit-remark").value.trim(),
|
||||
};
|
||||
|
||||
if (!data.simulate_num && !data.name) {
|
||||
toast("请输入模拟编号或名称", true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let resp;
|
||||
if (editId) {
|
||||
resp = await fetch(`/api/simulate-car/${editId}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
} else {
|
||||
resp = await fetch("/api/simulate-car", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
const result = await resp.json();
|
||||
if (result.ok || resp.ok) {
|
||||
toast(editId ? "更新成功" : "新增成功");
|
||||
closeModal();
|
||||
loadList();
|
||||
} else {
|
||||
toast("保存失败: " + (result.error || "未知错误"), true);
|
||||
}
|
||||
} catch (e) {
|
||||
toast("保存失败: " + e.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteRecord(id, label) {
|
||||
if (!confirm(`确定要删除「${label}」吗?`)) return;
|
||||
|
||||
try {
|
||||
const resp = await fetch(`/api/simulate-car/${id}`, { method: "DELETE" });
|
||||
const result = await resp.json();
|
||||
if (result.ok) {
|
||||
toast("删除成功");
|
||||
loadList();
|
||||
} else {
|
||||
toast("删除失败: " + (result.error || "未知错误"), true);
|
||||
}
|
||||
} catch (e) {
|
||||
toast("删除失败: " + e.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
function esc(s) { return (s || "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """); }
|
||||
|
||||
loadList();
|
||||
@@ -20,6 +20,7 @@ const VIEWS = {
|
||||
{ key: 'exit_dist', title: '离开距离' },
|
||||
{ key: 'remain_count', title: '剩余次数' },
|
||||
{ key: 'curr_dist', title: '当前距离' },
|
||||
{ key: 'env', title: '测试环境', render: r => envLabel(r) },
|
||||
{ key: 'create_time', title: '时间', render: r => fmtTime(r.create_time) },
|
||||
],
|
||||
},
|
||||
@@ -44,6 +45,7 @@ const VIEWS = {
|
||||
{ key: 'exit_dist', title: '离开距离' },
|
||||
{ key: 'enter_speed', title: '进入速度', render: r => toSpeed(r.enter_speed) },
|
||||
{ key: 'exit_speed', title: '离开速度', render: r => toSpeed(r.exit_speed) },
|
||||
{ key: 'env', title: '测试环境', render: r => envLabel(r) },
|
||||
{ key: 'create_time', title: '时间', render: r => fmtTime(r.create_time) },
|
||||
],
|
||||
},
|
||||
@@ -63,6 +65,7 @@ const VIEWS = {
|
||||
{ key: 'b4_enter_dist', title: '进入高度(mm)' },
|
||||
{ key: 'b4_leave_dist', title: '离开高度(mm)' },
|
||||
{ key: 'relay_out', title: '继电器', render: r => decodeRelay(r.relay_code) },
|
||||
{ key: 'env', title: '测试环境', render: r => envLabel(r) },
|
||||
{ key: 'create_time', title: '时间', render: r => fmtTime(r.create_time) },
|
||||
],
|
||||
},
|
||||
@@ -106,6 +109,18 @@ function decodeRelay(v) {
|
||||
return RELAY_MAP[parseInt(v)] || `0x${parseInt(v).toString(16).toUpperCase().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
/** 构建测试环境标签 (线圈 + 模拟车辆) */
|
||||
function envLabel(r) {
|
||||
const parts = [];
|
||||
if (r.coil_num || r.coil_name) {
|
||||
parts.push('🧵' + (r.coil_num || r.coil_name));
|
||||
}
|
||||
if (r.simulate_num || r.car_name) {
|
||||
parts.push('🚗' + (r.simulate_num || r.car_name));
|
||||
}
|
||||
return parts.join(' ') || '-';
|
||||
}
|
||||
|
||||
// ─── 视图切换 ────────────────────────────────────
|
||||
|
||||
function switchView(view) {
|
||||
|
||||
105
edc-web/app/templates/coil_info.html
Normal file
105
edc-web/app/templates/coil_info.html
Normal file
@@ -0,0 +1,105 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}线圈参数管理 - EDC 工装管理系统{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="test-header">
|
||||
<a href="/">← 返回设备列表</a>
|
||||
<h2>线圈参数管理</h2>
|
||||
</div>
|
||||
|
||||
<div class="fixture-card">
|
||||
<div class="vbt-header">
|
||||
<div style="display:flex; gap:8px; align-items:center;">
|
||||
<input type="text" id="search-input" placeholder="搜索编号/名称..."
|
||||
style="padding:6px 10px; border:1px solid #ddd; border-radius:4px; font-size:13px; width:200px;"
|
||||
oninput="loadList()">
|
||||
</div>
|
||||
<button class="btn-add" onclick="openModal()">+ 新增</button>
|
||||
</div>
|
||||
|
||||
<table id="coil-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>线圈编号</th>
|
||||
<th>名称</th>
|
||||
<th>电感量</th>
|
||||
<th>形状</th>
|
||||
<th>尺寸 (cm)</th>
|
||||
<th>圈数</th>
|
||||
<th>电阻 (Ω)</th>
|
||||
<th>材质</th>
|
||||
<th>备注</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<div id="edit-modal" class="modal-overlay" style="display:none;" onclick="if(event.target===this)closeModal()">
|
||||
<div class="modal-box">
|
||||
<h3 id="modal-title">新增线圈参数</h3>
|
||||
<div class="modal-form">
|
||||
<div class="form-group">
|
||||
<label>线圈编号 *</label>
|
||||
<input type="text" id="edit-coil-num">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>名称</label>
|
||||
<input type="text" id="edit-name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>电感量</label>
|
||||
<input type="number" id="edit-induct" step="0.01" value="0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>形状</label>
|
||||
<select id="edit-shape">
|
||||
<option value="">-- 请选择 --</option>
|
||||
<option value="矩形">矩形</option>
|
||||
<option value="圆形">圆形</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>长度 (cm,矩形有效)</label>
|
||||
<input type="number" id="edit-length" step="0.1" value="0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宽度 (cm,矩形有效)</label>
|
||||
<input type="number" id="edit-width" step="0.1" value="0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>半径 (cm,圆形有效)</label>
|
||||
<input type="number" id="edit-radius" step="0.1" value="0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>圈数</label>
|
||||
<input type="number" id="edit-turns" value="0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>电阻 (Ω)</label>
|
||||
<input type="number" id="edit-resistance" step="0.01" value="0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>材质</label>
|
||||
<input type="text" id="edit-material" placeholder="如铜线">
|
||||
</div>
|
||||
<div class="form-group full">
|
||||
<label>备注</label>
|
||||
<textarea id="edit-remark" rows="2" style="resize:vertical;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button class="btn-cancel" onclick="closeModal()">取消</button>
|
||||
<button class="btn-save" onclick="saveRecord()">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="toast" class="msg-toast"></div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/coil_info.js') }}"></script>
|
||||
{% endblock %}
|
||||
@@ -140,6 +140,42 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 线圈参数选择区 -->
|
||||
<div class="fixture-card">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px;">
|
||||
<h3 style="margin:0;">关联线圈参数</h3>
|
||||
<button class="btn-config" style="padding:4px 12px; font-size:12px;"
|
||||
onclick="location.href='/coil-info'">管理</button>
|
||||
</div>
|
||||
<div style="margin-bottom:8px;">
|
||||
<label style="font-size:12px; color:#666;">当前线圈:</label>
|
||||
<span id="current-coil-label" style="font-weight:600; font-size:13px;">未设置</span>
|
||||
</div>
|
||||
<select id="coil-select" style="width:100%; padding:4px; border:1px solid #ddd; border-radius:4px; font-size:12px;"
|
||||
onchange="onCoilChange()">
|
||||
<option value="">-- 选择线圈 --</option>
|
||||
</select>
|
||||
<div id="coil-detail" style="font-size:12px; color:#888; margin-top:4px;"></div>
|
||||
</div>
|
||||
|
||||
<!-- 模拟车辆参数选择区 -->
|
||||
<div class="fixture-card">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px;">
|
||||
<h3 style="margin:0;">关联模拟车辆参数</h3>
|
||||
<button class="btn-config" style="padding:4px 12px; font-size:12px;"
|
||||
onclick="location.href='/simulate-car'">管理</button>
|
||||
</div>
|
||||
<div style="margin-bottom:8px;">
|
||||
<label style="font-size:12px; color:#666;">当前车辆:</label>
|
||||
<span id="current-car-label" style="font-weight:600; font-size:13px;">未设置</span>
|
||||
</div>
|
||||
<select id="car-select" style="width:100%; padding:4px; border:1px solid #ddd; border-radius:4px; font-size:12px;"
|
||||
onchange="onCarChange()">
|
||||
<option value="">-- 选择模拟车辆 --</option>
|
||||
</select>
|
||||
<div id="car-detail" style="font-size:12px; color:#888; margin-top:4px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
90
edc-web/app/templates/simulate_car.html
Normal file
90
edc-web/app/templates/simulate_car.html
Normal file
@@ -0,0 +1,90 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}模拟车辆参数管理 - EDC 工装管理系统{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="test-header">
|
||||
<a href="/">← 返回设备列表</a>
|
||||
<h2>模拟车辆参数管理</h2>
|
||||
</div>
|
||||
|
||||
<div class="fixture-card">
|
||||
<div class="vbt-header">
|
||||
<div style="display:flex; gap:8px; align-items:center;">
|
||||
<input type="text" id="search-input" placeholder="搜索编号/名称..."
|
||||
style="padding:6px 10px; border:1px solid #ddd; border-radius:4px; font-size:13px; width:200px;"
|
||||
oninput="loadList()">
|
||||
</div>
|
||||
<button class="btn-add" onclick="openModal()">+ 新增</button>
|
||||
</div>
|
||||
|
||||
<table id="car-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>模拟编号</th>
|
||||
<th>名称</th>
|
||||
<th>形状</th>
|
||||
<th>尺寸 (cm)</th>
|
||||
<th>材质</th>
|
||||
<th>备注</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<div id="edit-modal" class="modal-overlay" style="display:none;" onclick="if(event.target===this)closeModal()">
|
||||
<div class="modal-box">
|
||||
<h3 id="modal-title">新增模拟车辆参数</h3>
|
||||
<div class="modal-form">
|
||||
<div class="form-group">
|
||||
<label>模拟编号 *</label>
|
||||
<input type="text" id="edit-simulate-num">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>名称</label>
|
||||
<input type="text" id="edit-name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>形状</label>
|
||||
<select id="edit-shape">
|
||||
<option value="">-- 请选择 --</option>
|
||||
<option value="矩形">矩形</option>
|
||||
<option value="圆形">圆形</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>长度 (cm,矩形有效)</label>
|
||||
<input type="number" id="edit-length" step="0.1" value="0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宽度 (cm,矩形有效)</label>
|
||||
<input type="number" id="edit-width" step="0.1" value="0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>半径 (cm,圆形有效)</label>
|
||||
<input type="number" id="edit-radius" step="0.1" value="0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>材质</label>
|
||||
<input type="text" id="edit-material" placeholder="如铁板、合金">
|
||||
</div>
|
||||
<div class="form-group full">
|
||||
<label>备注</label>
|
||||
<textarea id="edit-remark" rows="2" style="resize:vertical;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button class="btn-cancel" onclick="closeModal()">取消</button>
|
||||
<button class="btn-save" onclick="saveRecord()">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="toast" class="msg-toast"></div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/simulate_car.js') }}"></script>
|
||||
{% endblock %}
|
||||
Submodule edc_server updated: 844de70017...68c6d0bcfe
Reference in New Issue
Block a user