Browse Source

1222

yangrongze
hanyuqing 4 months ago
parent
commit
5237941155
  1. 165
      1111.py
  2. 100
      import.py
  3. 4
      test.py
  4. 39
      test1.py
  5. 173
      test1217.py
  6. 54
      util/neo4j_utils.py
  7. 208
      vue/node_modules/.package-lock.json
  8. 357
      vue/package-lock.json
  9. 1
      vue/package.json
  10. 343474
      vue/public/icd10.json
  11. 9
      vue/src/api/graph.js
  12. 6
      vue/src/main.js
  13. 591
      vue/src/system/GraphDemo.vue

165
1111.py

@ -0,0 +1,165 @@
import json
import re
import os
from neo4j import GraphDatabase
# === 配置 ===
NEO4J_URI = "bolt://localhost:7687"
NEO4J_USER = "neo4j"
NEO4J_PASSWORD = "12345678" # 👈 请确保密码正确
RELATIONSHIP_FOLDER = r"D:\temp\669"
BATCH_SIZE = 100
def sanitize_relationship_type(rel_type: str) -> str:
"""清理关系类型,确保合法"""
if not isinstance(rel_type, str):
rel_type = str(rel_type)
sanitized = re.sub(r"[^a-zA-Z0-9_]", "", rel_type)
if not sanitized or sanitized[0].isdigit():
sanitized = "REL_" + sanitized
return sanitized or "RELATED"
def extract_start_end(rel: dict):
"""兼容多种字段名提取 start/end"""
for s_key, e_key in [("start", "end"), ("source", "target"), ("from", "to")]:
s = rel.get(s_key)
e = rel.get(e_key)
if s is not None and e is not None:
return s, e
return None, None
def load_relationships_from_file(filepath):
"""从单个 JSON 文件加载 relationships"""
with open(filepath, "r", encoding="utf-8-sig") as f:
data = json.load(f)
relationships = []
if isinstance(data, list):
for item in data:
if isinstance(item, dict) and "relationships" in item:
relationships.extend(item["relationships"])
elif isinstance(item, dict):
relationships.append(item)
elif isinstance(data, dict) and "relationships" in data:
relationships = data["relationships"]
else:
relationships = data if isinstance(data, list) else []
return relationships
def process_relationships(relationships):
"""清洗并验证关系列表"""
valid_rels = []
for rel in relationships:
start_id, end_id = extract_start_end(rel)
rel_type = rel.get("type", "RELATED")
props = rel.get("properties", {}) or {}
if start_id is None or end_id is None:
continue
try:
start_id = int(float(start_id))
end_id = int(float(end_id))
except (TypeError, ValueError):
continue
valid_rels.append({
"start": start_id,
"end": end_id,
"type": sanitize_relationship_type(rel_type),
"props": props
})
return valid_rels
def import_relationships_in_batches(tx, rels, batch_size):
total = len(rels)
created_total = 0
for i in range(0, total, batch_size):
batch = rels[i:i + batch_size]
rel_groups = {}
for rel in batch:
rel_groups.setdefault(rel["type"], []).append({
"start": rel["start"],
"end": rel["end"],
"props": rel["props"]
})
created_this_batch = 0
for rel_type, group in rel_groups.items():
cypher = f"""
UNWIND $rels AS r
MATCH (a {{nodeId: r.start}})
MATCH (b {{nodeId: r.end}})
WITH a, b, r
WHERE a IS NOT NULL AND b IS NOT NULL
MERGE (a)-[rel:`{rel_type}`]->(b)
SET rel += r.props
RETURN count(rel) AS c
"""
result = tx.run(cypher, rels=group).single()
created_this_batch += result["c"]
created_total += created_this_batch
print(f" ➤ 本批创建关系: {created_this_batch}")
return created_total
def main():
driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD))
# 获取所有 JSON 文件,并按文件名排序(确保 relations_001.json 先于 002)
json_files = [f for f in os.listdir(RELATIONSHIP_FOLDER) if f.startswith("relations_") and f.endswith(".json")]
json_files.sort() # 按字典序排序,适用于 001, 002... 格式
if not json_files:
print("❌ 文件夹中没有找到 relations_*.json 文件")
return
total_global_created = 0
total_global_processed = 0
print(f"📁 找到 {len(json_files)} 个关系文件,开始逐个导入...\n")
for idx, filename in enumerate(json_files, 1):
filepath = os.path.join(RELATIONSHIP_FOLDER, filename)
print(f"\n📄 [{idx}/{len(json_files)}] 正在处理: {filename}")
try:
raw_rels = load_relationships_from_file(filepath)
print(f" ➤ 原始关系数: {len(raw_rels)}")
valid_rels = process_relationships(raw_rels)
print(f" ➤ 有效关系数: {len(valid_rels)}")
if not valid_rels:
print(" ⚠️ 跳过:无有效关系")
continue
with driver.session() as session:
created = session.execute_write(import_relationships_in_batches, valid_rels, BATCH_SIZE)
total_global_created += created
total_global_processed += len(valid_rels)
print(f" ✅ 文件 {filename} 导入完成,创建 {created} 条关系")
except Exception as e:
print(f" ❌ 处理 {filename} 时出错: {e}")
continue # 继续处理下一个文件
print("\n" + "="*60)
print(f"🎉 全部导入完成!")
print(f"📊 总共处理有效关系: {total_global_processed}")
print(f"✅ 总共成功创建关系: {total_global_created}")
driver.close()
if __name__ == "__main__":
main()

100
import.py

@ -0,0 +1,100 @@
import pandas as pd
from neo4j import GraphDatabase
import numpy as np
# === 配置 ===
EXCEL_PATH = r"C:\Users\hanyuqing\Desktop\最新国家医保ICD编码\最新国家医保ICD编码\ICD-10医保版数据.xlsx"
NEO4J_URI = "bolt://localhost:7687"
NEO4J_USER = "neo4j"
NEO4J_PASSWORD = "12345678"
# === 读取 Excel ===
df = pd.read_excel(
EXCEL_PATH,
header=1,
dtype=str, # 关键:防止 A00.0 变成 A00
engine='openpyxl'
)
# 清理列名
df.columns = df.columns.astype(str).str.strip()
# 必需列
required_cols = [
"条目(诊断)名称", "条目(诊断)代码",
"亚目名称", "亚目代码",
"章代码范围", "节代码范围", "类目代码"
]
# 检查列是否存在
missing = [col for col in required_cols if col not in df.columns]
if missing:
raise ValueError(f"缺少必要列: {missing}")
# 替换 NaN 为 None(便于后续判断)
df = df.replace({np.nan: None})
# === 构造 name 和 code 字段(按你的逻辑)===
def get_disease_name(row):
diag_name = row["条目(诊断)名称"]
subcat_name = row["亚目名称"]
# 如果诊断名称为空(None 或 空白),用亚目名称
if not diag_name or str(diag_name).strip() == "":
return str(subcat_name).strip() if subcat_name else None
return str(diag_name).strip()
def get_diagnosis_code(row):
code = row["条目(诊断)代码"]
# 如果为空,返回空字符串 ""
if not code or str(code).strip() == "":
return ""
return str(code).strip()
# 应用逻辑
df["_disease_name"] = df.apply(get_disease_name, axis=1)
df["_diagnosis_code"] = df.apply(get_diagnosis_code, axis=1)
# 过滤掉 name 仍为 None 的行(即 诊断名 + 亚目名 都为空)
df = df[df["_disease_name"].notna() & (df["_disease_name"] != "")]
print(f"✅ 共准备 {len(df)} 条疾病记录用于导入")
# === Neo4j 连接 ===
driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD))
def upsert_disease(tx, record):
cypher = """
MERGE (d:Disease {name: $name})
ON CREATE SET
d.diagnosisCode = $diagnosisCode,
d.chapterRange = $chapterRange,
d.sectionRange = $sectionRange,
d.categoryCode = $categoryCode,
d.subcategoryCode = $subcategoryCode
ON MATCH SET
d.diagnosisCode = $diagnosisCode,
d.chapterRange = $chapterRange,
d.sectionRange = $sectionRange,
d.categoryCode = $categoryCode,
d.subcategoryCode = $subcategoryCode
"""
tx.run(cypher, {
"name": record["_disease_name"],
"diagnosisCode": record["_diagnosis_code"],
"chapterRange": record.get("章代码范围") or "",
"sectionRange": record.get("节代码范围") or "",
"categoryCode": record.get("类目代码") or "",
"subcategoryCode": record.get("亚目代码") or ""
})
# === 批量导入 ===
with driver.session() as session:
for idx, row in df.iterrows():
try:
session.execute_write(upsert_disease, row.to_dict())
except Exception as e:
print(f"❌ 第 {idx + 2} 行失败: {e}")
continue
print("✅ 数据导入完成!")
driver.close()

4
test.py

@ -82,10 +82,10 @@ from util.neo4j_utils import neo4j_client
# print("关系属性已更新")
# neighbors = neo4j_client.find_neighbors_with_relationships(
# node_label="Disease",
# node_properties={"name": "糖尿病"},
# node_properties={"name": "霍乱"},
# direction="both" # 或 "out"/"in"
# )
#
# print(neighbors)
# for item in neighbors:
# print(f"关系: {item['relationship']['type']}")
# print(f"目标节点: {item['target']['label']} - {item['target']}")

39
test1.py

@ -1,3 +1,5 @@
from datetime import datetime
import robyn
from robyn import Robyn, jsonify, Response
from typing import Optional, List, Any, Dict
@ -93,8 +95,8 @@ def build_g6_graph_data_from_results(
}
def build_g6_subgraph_by_props(
neo4j_util: Neo4jUtil,
node_label: str,
node_properties: Dict[str, Any],
node_label: Optional[str] = None,
direction: str = "both",
rel_type: Optional[str] = None
) -> dict:
@ -137,10 +139,11 @@ def get_data():
graph_data = build_g6_subgraph_by_props(
neo4j_client,
node_label="Disease",
node_properties={"name": "偏头痛"},
node_properties={"name": "埃尔托生物型霍乱"},
direction="both",
rel_type=None
)
return Response(
status_code=200,
description=jsonify(graph_data),
@ -150,7 +153,39 @@ def get_data():
return jsonify({"error": str(e)}), 500
@app.post("/getGraph")
def get_graph(req):
try:
# 1. 获取 JSON body(自动解析为 dict)
body = req.json()
# 2. 提取 label 字段(即疾病名称)
disease_name = body.get("label")
if not disease_name:
return jsonify({"error": "Missing 'label' in request body"}), 400
code = body.get("code")
level = body.get("level")
print("sssssssssss")
print(body.get("type"))
print(code)
print(level)
graph_data = build_g6_subgraph_by_props(
neo4j_client,
node_label=body.get("type"),
node_properties={"name": disease_name},
direction="both",
rel_type=None
)
return Response(
status_code=200,
description=jsonify(graph_data),
headers={"Content-Type": "text/plain; charset=utf-8"}
)
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == "__main__":
app.start(host="0.0.0.0", port=8088)

173
test1217.py

@ -0,0 +1,173 @@
import pandas as pd
import json
import numpy as np
import re
# === 配置 ===
EXCEL_PATH = r"C:\Users\hanyuqing\Desktop\最新国家医保ICD编码\最新国家医保ICD编码\ICD-10医保版数据.xlsx"
OUTPUT_JSON_PATH = "icd10_tree_with_level.json"
# === 章 code 格式化 ===
def extract_chapter_number(text):
if not text:
return None
text = str(text).strip()
match = re.search(r'第([一二三四五六七八九十百\d]+)章', text)
if match:
num_str = match.group(1)
chinese_num_map = {
'': 1, '': 2, '': 3, '': 4, '': 5,
'': 6, '': 7, '': 8, '': 9, '': 10,
'十一': 11, '十二': 12, '十三': 13, '十四': 14, '十五': 15,
'十六': 16, '十七': 17, '十八': 18, '十九': 19, '二十': 20,
'二十一': 21, '二十二': 22
}
if num_str in chinese_num_map:
return chinese_num_map[num_str]
elif num_str.isdigit():
return int(num_str)
if text.isdigit():
return int(text)
if text.endswith('.') and text[:-1].isdigit():
return int(text[:-1])
return None
def format_chapter_code(original):
num = extract_chapter_number(original)
if num is not None:
return f"{num}"
return str(original).strip() if original else ""
def get_clean(val):
if val is None or str(val).strip().lower() in ("", "nan", "none"):
return None
return str(val).strip()
def make_fields(code, name):
code_str = code or ""
label_str = name or ""
title_str = f"{code_str} {label_str}" if code_str and label_str else (code_str or label_str)
return code_str, label_str, title_str
# === 带 level 的树节点 ===
class TreeNode:
def __init__(self, code="", label="", title="", level=""):
self.code = code
self.label = label
self.title = title
self.level = level # 新增字段
self.children = []
self._child_key_set = set() # (code, label)
def add_or_get_child(self, code, label, title, level):
key = (code, label)
if key in self._child_key_set:
for child in self.children:
if child.code == code and child.label == label:
return child
else:
new_child = TreeNode(code=code, label=label, title=title, level=level)
self.children.append(new_child)
self._child_key_set.add(key)
return new_child
return None
# === 构建树 ===
root = TreeNode()
chapter_map = {}
df = pd.read_excel(
EXCEL_PATH,
header=1,
dtype=str,
engine='openpyxl'
)
df.columns = df.columns.astype(str).str.strip()
df = df.replace({np.nan: None})
for idx, row in df.iterrows():
raw_chapter = get_clean(row.get(""))
chapter_name = get_clean(row.get("章的名称"))
section_code = get_clean(row.get("节代码范围"))
section_name = get_clean(row.get("节名称"))
category_code = get_clean(row.get("类目代码"))
category_name = get_clean(row.get("类目名称"))
subcategory_code = get_clean(row.get("亚目代码"))
subcategory_name = get_clean(row.get("亚目名称"))
diagnosis_code = get_clean(row.get("条目(诊断)代码"))
diagnosis_name = get_clean(row.get("条目(诊断)名称"))
if not raw_chapter and not chapter_name:
continue
# === 章 ===
chapter_code = format_chapter_code(raw_chapter)
chap_label = chapter_name or chapter_code
chap_title = f"{chapter_code} {chapter_name}" if chapter_name else chapter_code
chap_key = (chapter_code, chap_label)
if chap_key not in chapter_map:
chapter_node = TreeNode(
code=chapter_code,
label=chap_label,
title=chap_title,
level="chapter"
)
root.children.append(chapter_node)
chapter_map[chap_key] = chapter_node
else:
chapter_node = chapter_map[chap_key]
current = chapter_node
# === 节 ===
if section_code or section_name:
sec_code, sec_label, sec_title = make_fields(section_code, section_name)
current = current.add_or_get_child(sec_code, sec_label, sec_title, "section")
# === 类目 ===
if category_code or category_name:
cat_code, cat_label, cat_title = make_fields(category_code, category_name)
current = current.add_or_get_child(cat_code, cat_label, cat_title, "category")
# === 亚目 ===
if subcategory_code or subcategory_name:
sub_code, sub_label, sub_title = make_fields(subcategory_code, subcategory_name)
current = current.add_or_get_child(sub_code, sub_label, sub_title, "subcategory")
# === 条目 ===
if diagnosis_code or diagnosis_name:
diag_code, diag_label, diag_title = make_fields(diagnosis_code, diagnosis_name)
current.add_or_get_child(diag_code, diag_label, diag_title, "diagnosis")
# === 转为带 id 的 dict ===
next_id = 1
def node_to_dict(node):
global next_id
item = {
"id": next_id,
"code": node.code,
"label": node.label,
"title": node.title,
"level": node.level # 新增
}
next_id += 1
if node.children:
item["children"] = [node_to_dict(child) for child in node.children]
return item
treeData = [node_to_dict(chap) for chap in root.children]
# === 保存 ===
with open(OUTPUT_JSON_PATH, 'w', encoding='utf-8') as f:
json.dump(treeData, f, ensure_ascii=False, indent=2)
print(f"✅ 树形结构已生成,共 {len(treeData)} 个章节点")
print(f"📄 输出文件: {OUTPUT_JSON_PATH}")

54
util/neo4j_utils.py

@ -1,4 +1,5 @@
# neo4j_util.py
from datetime import datetime
import logging
from typing import Dict, List, Optional, Any
from neo4j import GraphDatabase, Driver
@ -100,15 +101,40 @@ class Neo4jUtil:
raw = self.execute_read(cypher)
return [self._merge_id_and_props(row) for row in raw]
def find_nodes_with_element_id(self, label: str, properties: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]:
"""按属性查询节点"""
cypher = f"MATCH (n:`{label}`)"
from typing import Optional, Dict, Any, List
def find_nodes_with_element_id(
self,
label: Optional[str],
properties: Optional[Dict[str, Any]] = None
) -> List[Dict[str, Any]]:
"""
按属性查询节点支持指定标签或不指定标签
Args:
label (Optional[str]): 节点标签若为 None 则匹配任意标签的节点
properties (Optional[Dict[str, Any]]): 节点属性过滤条件
Returns:
List[Dict]: 节点列表每个节点包含 id 和所有属性
"""
# 构建节点模式
if label is not None:
node_pattern = f"(n:`{label}`)"
else:
node_pattern = "(n)"
cypher = f"MATCH {node_pattern}"
params = {}
if properties:
# 安全构建 WHERE 条件(使用参数化查询)
where_clause = " AND ".join([f"n.`{k}` = $prop_{k}" for k in properties])
params = {f"prop_{k}": v for k, v in properties.items()}
cypher += f" WHERE {where_clause}"
cypher += " RETURN elementId(n) AS id, n{.*} AS props"
raw = self.execute_read(cypher, params)
return [self._merge_id_and_props(row) for row in raw]
@ -229,9 +255,11 @@ class Neo4jUtil:
raw = self.execute_read(cypher, params)
return [self._enrich_relationship(row) for row in raw]
from typing import Optional, Dict, Any, List
def find_neighbors_with_relationships(
self,
node_label: str,
node_label: Optional[str],
node_properties: Dict[str, Any],
direction: str = "both", # 可选: "out", "in", "both"
rel_type: Optional[str] = None,
@ -240,7 +268,7 @@ class Neo4jUtil:
查询指定节点的所有邻居节点及其关系包括入边出边或双向
Args:
node_label (str): 节点标签
node_label (Optional[str]): 节点标签若为 None 则匹配任意标签的节点
node_properties (Dict[str, Any]): 节点匹配属性必须能唯一或有效定位节点
direction (str): 关系方向"out" 表示 (n)-[r]->(m)"in" 表示 (n)<-[r]-(m)"both" 表示无向
rel_type (Optional[str]): 可选的关系类型过滤
@ -253,14 +281,23 @@ class Neo4jUtil:
# 构建起始节点匹配条件
where_clause, params = self._build_where_conditions("n", node_properties, "node")
# 构建关系类型过滤
rel_filter = f":`{rel_type}`" if rel_type else ""
# ✅ 动态构建节点模式:支持 node_label=None
if node_label is not None:
node_pattern = f"(n:`{node_label}`)"
else:
node_pattern = "(n)"
# 构建完整 MATCH 模式
if direction == "out":
pattern = f"(n:`{node_label}`)-[r{rel_filter}]->(m)"
pattern = f"{node_pattern}-[r{rel_filter}]->(m)"
elif direction == "in":
pattern = f"(n:`{node_label}`)<-[r{rel_filter}]-(m)"
pattern = f"{node_pattern}<[r{rel_filter}]-(m)"
elif direction == "both":
pattern = f"(n:`{node_label}`)-[r{rel_filter}]-(m)"
pattern = f"{node_pattern}-[r{rel_filter}]-(m)"
else:
raise ValueError("direction 必须是 'out', 'in''both'")
@ -282,6 +319,7 @@ class Neo4jUtil:
raw_results = self.execute_read(cypher, params)
neighbors = []
for row in raw_results:
source = dict(row["sourceProps"])
source.update({"id": row["sourceId"], "label": row["sourceLabel"]})

208
vue/node_modules/.package-lock.json

@ -2114,6 +2114,14 @@
"node": ">=6.9.0"
}
},
"node_modules/@ctrl/tinycolor": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
"engines": {
"node": ">=10"
}
},
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.7",
"resolved": "https://registry.npmmirror.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
@ -2123,6 +2131,14 @@
"node": ">=10.0.0"
}
},
"node_modules/@element-plus/icons-vue": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz",
"integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==",
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
@ -2221,6 +2237,28 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@floating-ui/core": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
"dependencies": {
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
"dependencies": {
"@floating-ui/core": "^1.7.3",
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="
},
"node_modules/@hapi/hoek": {
"version": "9.3.0",
"resolved": "https://registry.npmmirror.com/@hapi/hoek/-/hoek-9.3.0.tgz",
@ -2387,6 +2425,16 @@
"integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==",
"dev": true
},
"node_modules/@popperjs/core": {
"name": "@sxzz/popperjs-es",
"version": "2.11.7",
"resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@sideway/address": {
"version": "4.1.5",
"resolved": "https://registry.npmmirror.com/@sideway/address/-/address-4.1.5.tgz",
@ -2734,6 +2782,19 @@
"resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
},
"node_modules/@types/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ=="
},
"node_modules/@types/lodash-es": {
"version": "4.17.12",
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz",
@ -2838,6 +2899,11 @@
"@types/node": "*"
}
},
"node_modules/@types/web-bluetooth": {
"version": "0.0.16",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
},
"node_modules/@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.5.10.tgz",
@ -4148,6 +4214,89 @@
"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
"dev": true
},
"node_modules/@vueuse/core": {
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
"dependencies": {
"@types/web-bluetooth": "^0.0.16",
"@vueuse/metadata": "9.13.0",
"@vueuse/shared": "9.13.0",
"vue-demi": "*"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/core/node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@vueuse/metadata": {
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared": {
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
"dependencies": {
"vue-demi": "*"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared/node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.11.6.tgz",
@ -4522,6 +4671,11 @@
"lodash": "^4.17.14"
}
},
"node_modules/async-validator": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -6140,6 +6294,11 @@
"lodash": "^4.17.15"
}
},
"node_modules/dayjs": {
"version": "1.11.19",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz",
"integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="
},
"node_modules/debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/debounce/-/debounce-1.2.1.tgz",
@ -6569,6 +6728,30 @@
"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.694.tgz",
"integrity": "sha512-kM3SwvGTYpBFJSc8jm4IYVMIOzDmAGd/Ry96O9elRiM6iEwHKNKhtXyFGzpfMMIGZD84W4/hyaULlMmNVvLQlQ=="
},
"node_modules/element-plus": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.12.0.tgz",
"integrity": "sha512-M9YLSn2np9OnqrSKWsiXvGe3qnF8pd94+TScsHj1aTMCD+nSEvucXermf807qNt6hOP040le0e5Aft7E9ZfHmA==",
"dependencies": {
"@ctrl/tinycolor": "^3.4.1",
"@element-plus/icons-vue": "^2.3.2",
"@floating-ui/dom": "^1.0.1",
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
"@types/lodash": "^4.17.20",
"@types/lodash-es": "^4.17.12",
"@vueuse/core": "^9.1.0",
"async-validator": "^4.2.5",
"dayjs": "^1.11.19",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"lodash-unified": "^1.0.3",
"memoize-one": "^6.0.0",
"normalize-wheel-es": "^1.2.0"
},
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -8813,6 +8996,21 @@
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash-es": {
"version": "4.17.22",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.22.tgz",
"integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q=="
},
"node_modules/lodash-unified": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz",
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
"peerDependencies": {
"@types/lodash-es": "*",
"lodash": "*",
"lodash-es": "*"
}
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@ -9136,6 +9334,11 @@
"node": ">= 4.0.0"
}
},
"node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@ -9575,6 +9778,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/normalize-wheel-es": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="
},
"node_modules/npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz",

357
vue/package-lock.json

@ -11,6 +11,7 @@
"@antv/g6": "^5.0.50",
"axios": "^1.13.2",
"core-js": "^3.8.3",
"element-plus": "^2.12.0",
"vue": "^3.2.13"
},
"devDependencies": {
@ -2140,6 +2141,14 @@
"node": ">=6.9.0"
}
},
"node_modules/@ctrl/tinycolor": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
"engines": {
"node": ">=10"
}
},
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.7",
"resolved": "https://registry.npmmirror.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
@ -2149,6 +2158,14 @@
"node": ">=10.0.0"
}
},
"node_modules/@element-plus/icons-vue": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz",
"integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==",
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
@ -2247,6 +2264,28 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@floating-ui/core": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
"dependencies": {
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
"dependencies": {
"@floating-ui/core": "^1.7.3",
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="
},
"node_modules/@hapi/hoek": {
"version": "9.3.0",
"resolved": "https://registry.npmmirror.com/@hapi/hoek/-/hoek-9.3.0.tgz",
@ -2413,6 +2452,16 @@
"integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==",
"dev": true
},
"node_modules/@popperjs/core": {
"name": "@sxzz/popperjs-es",
"version": "2.11.7",
"resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@sideway/address": {
"version": "4.1.5",
"resolved": "https://registry.npmmirror.com/@sideway/address/-/address-4.1.5.tgz",
@ -2760,6 +2809,19 @@
"resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
},
"node_modules/@types/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ=="
},
"node_modules/@types/lodash-es": {
"version": "4.17.12",
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz",
@ -2864,6 +2926,11 @@
"@types/node": "*"
}
},
"node_modules/@types/web-bluetooth": {
"version": "0.0.16",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
},
"node_modules/@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.5.10.tgz",
@ -4174,6 +4241,89 @@
"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
"dev": true
},
"node_modules/@vueuse/core": {
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
"dependencies": {
"@types/web-bluetooth": "^0.0.16",
"@vueuse/metadata": "9.13.0",
"@vueuse/shared": "9.13.0",
"vue-demi": "*"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/core/node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@vueuse/metadata": {
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared": {
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
"dependencies": {
"vue-demi": "*"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared/node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.11.6.tgz",
@ -4548,6 +4698,11 @@
"lodash": "^4.17.14"
}
},
"node_modules/async-validator": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -6166,6 +6321,11 @@
"lodash": "^4.17.15"
}
},
"node_modules/dayjs": {
"version": "1.11.19",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz",
"integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="
},
"node_modules/debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/debounce/-/debounce-1.2.1.tgz",
@ -6595,6 +6755,30 @@
"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.694.tgz",
"integrity": "sha512-kM3SwvGTYpBFJSc8jm4IYVMIOzDmAGd/Ry96O9elRiM6iEwHKNKhtXyFGzpfMMIGZD84W4/hyaULlMmNVvLQlQ=="
},
"node_modules/element-plus": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.12.0.tgz",
"integrity": "sha512-M9YLSn2np9OnqrSKWsiXvGe3qnF8pd94+TScsHj1aTMCD+nSEvucXermf807qNt6hOP040le0e5Aft7E9ZfHmA==",
"dependencies": {
"@ctrl/tinycolor": "^3.4.1",
"@element-plus/icons-vue": "^2.3.2",
"@floating-ui/dom": "^1.0.1",
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
"@types/lodash": "^4.17.20",
"@types/lodash-es": "^4.17.12",
"@vueuse/core": "^9.1.0",
"async-validator": "^4.2.5",
"dayjs": "^1.11.19",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"lodash-unified": "^1.0.3",
"memoize-one": "^6.0.0",
"normalize-wheel-es": "^1.2.0"
},
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -8853,6 +9037,21 @@
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash-es": {
"version": "4.17.22",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.22.tgz",
"integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q=="
},
"node_modules/lodash-unified": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz",
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
"peerDependencies": {
"@types/lodash-es": "*",
"lodash": "*",
"lodash-es": "*"
}
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@ -9176,6 +9375,11 @@
"node": ">= 4.0.0"
}
},
"node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@ -9615,6 +9819,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/normalize-wheel-es": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="
},
"node_modules/npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz",
@ -14665,12 +14874,23 @@
"@babel/helper-validator-identifier": "^7.28.5"
}
},
"@ctrl/tinycolor": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA=="
},
"@discoveryjs/json-ext": {
"version": "0.5.7",
"resolved": "https://registry.npmmirror.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
"dev": true
},
"@element-plus/icons-vue": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz",
"integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==",
"requires": {}
},
"@eslint-community/eslint-utils": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
@ -14734,6 +14954,28 @@
"integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
"dev": true
},
"@floating-ui/core": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
"requires": {
"@floating-ui/utils": "^0.2.10"
}
},
"@floating-ui/dom": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
"requires": {
"@floating-ui/core": "^1.7.3",
"@floating-ui/utils": "^0.2.10"
}
},
"@floating-ui/utils": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="
},
"@hapi/hoek": {
"version": "9.3.0",
"resolved": "https://registry.npmmirror.com/@hapi/hoek/-/hoek-9.3.0.tgz",
@ -14871,6 +15113,11 @@
"integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==",
"dev": true
},
"@popperjs/core": {
"version": "npm:@sxzz/popperjs-es@2.11.7",
"resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ=="
},
"@sideway/address": {
"version": "4.1.5",
"resolved": "https://registry.npmmirror.com/@sideway/address/-/address-4.1.5.tgz",
@ -15193,6 +15440,19 @@
"resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
},
"@types/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ=="
},
"@types/lodash-es": {
"version": "4.17.12",
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"requires": {
"@types/lodash": "*"
}
},
"@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz",
@ -15297,6 +15557,11 @@
"@types/node": "*"
}
},
"@types/web-bluetooth": {
"version": "0.0.16",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
},
"@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.5.10.tgz",
@ -16222,6 +16487,46 @@
"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
"dev": true
},
"@vueuse/core": {
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
"requires": {
"@types/web-bluetooth": "^0.0.16",
"@vueuse/metadata": "9.13.0",
"@vueuse/shared": "9.13.0",
"vue-demi": "*"
},
"dependencies": {
"vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"requires": {}
}
}
},
"@vueuse/metadata": {
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ=="
},
"@vueuse/shared": {
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
"requires": {
"vue-demi": "*"
},
"dependencies": {
"vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"requires": {}
}
}
},
"@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.11.6.tgz",
@ -16525,6 +16830,11 @@
"lodash": "^4.17.14"
}
},
"async-validator": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -17684,6 +17994,11 @@
"lodash": "^4.17.15"
}
},
"dayjs": {
"version": "1.11.19",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz",
"integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="
},
"debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/debounce/-/debounce-1.2.1.tgz",
@ -17997,6 +18312,27 @@
"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.694.tgz",
"integrity": "sha512-kM3SwvGTYpBFJSc8jm4IYVMIOzDmAGd/Ry96O9elRiM6iEwHKNKhtXyFGzpfMMIGZD84W4/hyaULlMmNVvLQlQ=="
},
"element-plus": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.12.0.tgz",
"integrity": "sha512-M9YLSn2np9OnqrSKWsiXvGe3qnF8pd94+TScsHj1aTMCD+nSEvucXermf807qNt6hOP040le0e5Aft7E9ZfHmA==",
"requires": {
"@ctrl/tinycolor": "^3.4.1",
"@element-plus/icons-vue": "^2.3.2",
"@floating-ui/dom": "^1.0.1",
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
"@types/lodash": "^4.17.20",
"@types/lodash-es": "^4.17.12",
"@vueuse/core": "^9.1.0",
"async-validator": "^4.2.5",
"dayjs": "^1.11.19",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"lodash-unified": "^1.0.3",
"memoize-one": "^6.0.0",
"normalize-wheel-es": "^1.2.0"
}
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -19645,6 +19981,17 @@
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash-es": {
"version": "4.17.22",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.22.tgz",
"integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q=="
},
"lodash-unified": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz",
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
"requires": {}
},
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@ -19897,6 +20244,11 @@
"fs-monkey": "^1.0.4"
}
},
"memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@ -20232,6 +20584,11 @@
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
"dev": true
},
"normalize-wheel-es": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz",

1
vue/package.json

@ -11,6 +11,7 @@
"@antv/g6": "^5.0.50",
"axios": "^1.13.2",
"core-js": "^3.8.3",
"element-plus": "^2.12.0",
"vue": "^3.2.13"
},
"devDependencies": {

343474
vue/public/icd10.json

File diff suppressed because it is too large

9
vue/src/api/graph.js

@ -12,4 +12,13 @@ export function getTestGraphData() {
method: 'get'
// 注意:不传 params!因为后端不接收也不需要
});
}
export function getGraph(data) {
return request({
url: '/getGraph',
method: 'post',
data
// 注意:不传 params!因为后端不接收也不需要
});
}

6
vue/src/main.js

@ -1,4 +1,8 @@
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
createApp(App).mount('#app')
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')

591
vue/src/system/GraphDemo.vue

@ -1,151 +1,181 @@
<template>
<div class="knowledge-graph-container">
<!-- 控制面板 -->
<div class="control-panel">
<!-- 节点控制 -->
<div class="section">
<h5>节点控制</h5>
<label style="text-align: left;margin: 10px 0;display: flex;align-items: center;font-size: 12px;">
<input v-model="nodeShowLabel" type="checkbox" />
显示节点标签
</label>
<div class="form-group">
<label>字体名称:</label>
<select v-model="nodeFontFamily">
<!-- 中文常用字体 -->
<option value="Microsoft YaHei, sans-serif">微软雅黑</option>
<option value="SimSun, serif">宋体SimSun</option>
<option value="SimHei, sans-serif">黑体SimHei</option>
<option value="KaiTi, serif">楷体KaiTi</option>
<option value="FangSong, serif">仿宋FangSong</option>
<option value="PingFang SC, Helvetica Neue, sans-serif">苹方PingFang SC</option>
<!-- 英文/通用字体 -->
<option value="Arial, sans-serif">Arial</option>
<option value="Helvetica, sans-serif">Helvetica</option>
<option value="Times New Roman, serif">Times New Roman</option>
<option value="Georgia, serif">Georgia</option>
<option value="Courier New, monospace">Courier New等宽</option>
<option value="Verdana, sans-serif">Verdana</option>
<option value="Tahoma, sans-serif">Tahoma</option>
<option value="Impact, sans-serif">Impact</option>
</select>
</div>
<div class="slider-group">
<span>字体大小: {{ nodeFontSize }}px</span>
<input
v-model.number="nodeFontSize"
type="range"
min="10"
max="24"
step="1"
/>
</div>
<div class="color-picker">
<label>字体颜色:</label>
<input v-model="nodeFontColor" type="color" />
</div>
<div class="form-group">
<label>图形:</label>
<select v-model="nodeShape">
<option value="circle">圆形</option>
<option value="diamond">菱形</option>
<option value="triangle">三角形</option>
<option value="rect">矩形</option>
<option value="star">星形</option>
<option value="hexagon">六边形</option>
</select>
</div>
<div class="form-group">
<label>尺寸:</label>
<input v-model.number="nodeSize" type="number" min="30" max="100" />
</div>
<div class="color-picker">
<label>填充颜色:</label>
<input v-model="nodeFill" type="color" />
</div>
<div class="color-picker">
<label>边框颜色:</label>
<input v-model="nodeStroke" type="color" />
</div>
<div class="form-group">
<label>边框尺寸:</label>
<input v-model.number="nodeLineWidth" type="number" min="1" max="5" />
</div>
</div>
<!-- 连边控制 -->
<div class="section">
<h5>连边控制</h5>
<label style="text-align: left;margin: 10px 0;display: flex;align-items: center;font-size: 12px;">
<input v-model="edgeShowLabel" type="checkbox" />
显示连边标签
<input v-model="edgeEndArrow" type="checkbox" style="margin-left: 10px"/>
显示端点箭头
</label>
<div class="form-group">
<label>字体名称:</label>
<select v-model="edgeFontFamily">
<option value="Microsoft YaHei, sans-serif">微软雅黑</option>
<option value="SimSun, serif">宋体</option>
<option value="SimHei, sans-serif">黑体</option>
<option value="Arial, sans-serif">Arial</option>
<option value="sans-serif">无衬线默认</option>
</select>
</div>
<div class="slider-group">
<span>字体大小: {{ edgeFontSize }}px</span>
<input
v-model.number="edgeFontSize"
type="range"
min="8"
max="16"
step="1"
/>
</div>
<div class="color-picker">
<label>字体颜色:</label>
<input v-model="edgeFontColor" type="color" />
</div>
<div class="form-group">
<label>连边类型:</label>
<select v-model="edgeType">
<option value="line">直线</option>
<option value="polyline">折线边</option>
<option value="cubic">三次贝塞尔曲线边</option>
<option value="cubic-horizontal">水平三次贝塞尔曲线边</option>
<option value="cubic-vertical">垂直三次贝塞尔曲线边</option>
<option value="quadratic">二次贝塞尔曲线边</option>
</select>
</div>
<div class="form-group">
<label>线粗细:</label>
<input v-model.number="edgeLineWidth" type="number" min="1" max="5" />
</div>
<div class="color-picker">
<label>线条颜色:</label>
<input v-model="edgeStroke" type="color" />
</div>
<!-- <label>-->
<!-- <input v-model="edgeEndArrow" type="checkbox" />-->
<!-- <div class="control-panel">-->
<!-- &lt;!&ndash; 节点控制 &ndash;&gt;-->
<!-- <div class="section">-->
<!-- <h5>节点控制</h5>-->
<!-- <label style="text-align: left;margin: 10px 0;display: flex;align-items: center;font-size: 12px;">-->
<!-- <input v-model="nodeShowLabel" type="checkbox" />-->
<!-- 显示节点标签-->
<!-- </label>-->
<!-- <div class="form-group">-->
<!-- <label>字体名称:</label>-->
<!-- <select v-model="nodeFontFamily">-->
<!-- &lt;!&ndash; 中文常用字体 &ndash;&gt;-->
<!-- <option value="Microsoft YaHei, sans-serif">微软雅黑</option>-->
<!-- <option value="SimSun, serif">宋体SimSun</option>-->
<!-- <option value="SimHei, sans-serif">黑体SimHei</option>-->
<!-- <option value="KaiTi, serif">楷体KaiTi</option>-->
<!-- <option value="FangSong, serif">仿宋FangSong</option>-->
<!-- <option value="PingFang SC, Helvetica Neue, sans-serif">苹方PingFang SC</option>-->
<!-- &lt;!&ndash; 英文/通用字体 &ndash;&gt;-->
<!-- <option value="Arial, sans-serif">Arial</option>-->
<!-- <option value="Helvetica, sans-serif">Helvetica</option>-->
<!-- <option value="Times New Roman, serif">Times New Roman</option>-->
<!-- <option value="Georgia, serif">Georgia</option>-->
<!-- <option value="Courier New, monospace">Courier New等宽</option>-->
<!-- <option value="Verdana, sans-serif">Verdana</option>-->
<!-- <option value="Tahoma, sans-serif">Tahoma</option>-->
<!-- <option value="Impact, sans-serif">Impact</option>-->
<!-- </select>-->
<!-- </div>-->
<!-- <div class="slider-group">-->
<!-- <span>字体大小: {{ nodeFontSize }}px</span>-->
<!-- <input-->
<!-- v-model.number="nodeFontSize"-->
<!-- type="range"-->
<!-- min="10"-->
<!-- max="24"-->
<!-- step="1"-->
<!-- />-->
<!-- </div>-->
<!-- <div class="color-picker">-->
<!-- <label>字体颜色:</label>-->
<!-- <input v-model="nodeFontColor" type="color" />-->
<!-- </div>-->
<!-- <div class="form-group">-->
<!-- <label>图形:</label>-->
<!-- <select v-model="nodeShape">-->
<!-- <option value="circle">圆形</option>-->
<!-- <option value="diamond">菱形</option>-->
<!-- <option value="triangle">三角形</option>-->
<!-- <option value="rect">矩形</option>-->
<!-- <option value="star">星形</option>-->
<!-- <option value="hexagon">六边形</option>-->
<!-- </select>-->
<!-- </div>-->
<!-- <div class="form-group">-->
<!-- <label>尺寸:</label>-->
<!-- <input v-model.number="nodeSize" type="number" min="30" max="100" />-->
<!-- </div>-->
<!-- <div class="color-picker">-->
<!-- <label>填充颜色:</label>-->
<!-- <input v-model="nodeFill" type="color" />-->
<!-- </div>-->
<!-- <div class="color-picker">-->
<!-- <label>边框颜色:</label>-->
<!-- <input v-model="nodeStroke" type="color" />-->
<!-- </div>-->
<!-- <div class="form-group">-->
<!-- <label>边框尺寸:</label>-->
<!-- <input v-model.number="nodeLineWidth" type="number" min="1" max="5" />-->
<!-- </div>-->
<!-- </div>-->
<!-- &lt;!&ndash; 连边控制 &ndash;&gt;-->
<!-- <div class="section">-->
<!-- <h5>连边控制</h5>-->
<!-- <label style="text-align: left;margin: 10px 0;display: flex;align-items: center;font-size: 12px;">-->
<!-- <input v-model="edgeShowLabel" type="checkbox" />-->
<!-- 显示连边标签-->
<!-- <input v-model="edgeEndArrow" type="checkbox" style="margin-left: 10px"/>-->
<!-- 显示端点箭头-->
<!-- </label>-->
<!-- <div class="form-group">-->
<!-- <label>字体名称:</label>-->
<!-- <select v-model="edgeFontFamily">-->
<!-- <option value="Microsoft YaHei, sans-serif">微软雅黑</option>-->
<!-- <option value="SimSun, serif">宋体</option>-->
<!-- <option value="SimHei, sans-serif">黑体</option>-->
<!-- <option value="Arial, sans-serif">Arial</option>-->
<!-- <option value="sans-serif">无衬线默认</option>-->
<!-- </select>-->
<!-- </div>-->
<!-- <div class="slider-group">-->
<!-- <span>字体大小: {{ edgeFontSize }}px</span>-->
<!-- <input-->
<!-- v-model.number="edgeFontSize"-->
<!-- type="range"-->
<!-- min="8"-->
<!-- max="16"-->
<!-- step="1"-->
<!-- />-->
<!-- </div>-->
<!-- <div class="color-picker">-->
<!-- <label>字体颜色:</label>-->
<!-- <input v-model="edgeFontColor" type="color" />-->
<!-- </div>-->
<!-- <div class="form-group">-->
<!-- <label>连边类型:</label>-->
<!-- <select v-model="edgeType">-->
<!-- <option value="line">直线</option>-->
<!-- <option value="polyline">折线边</option>-->
<!-- <option value="cubic">三次贝塞尔曲线边</option>-->
<!-- <option value="cubic-horizontal">水平三次贝塞尔曲线边</option>-->
<!-- <option value="cubic-vertical">垂直三次贝塞尔曲线边</option>-->
<!-- <option value="quadratic">二次贝塞尔曲线边</option>-->
<!-- </select>-->
<!-- </div>-->
<!-- <div class="form-group">-->
<!-- <label>线粗细:</label>-->
<!-- <input v-model.number="edgeLineWidth" type="number" min="1" max="5" />-->
<!-- </div>-->
<!-- <div class="color-picker">-->
<!-- <label>线条颜色:</label>-->
<!-- <input v-model="edgeStroke" type="color" />-->
<!-- </div>-->
<!--&lt;!&ndash; <label>&ndash;&gt;-->
<!--&lt;!&ndash; <input v-model="edgeEndArrow" type="checkbox" />&ndash;&gt;-->
<!--&lt;!&ndash; 显示端点箭头&ndash;&gt;-->
<!--&lt;!&ndash; </label>&ndash;&gt;-->
<!-- </div>-->
<!-- <button @click="resetView">重置视图</button>-->
<!-- <button @click="resetStyle">重置样式</button>-->
<!-- &lt;!&ndash; 操作按钮 &ndash;&gt;-->
<!-- </div>-->
<div class="icd10-tree-container">
<div>
<el-radio-group v-model="typeRadio" @change="changeTree">
<el-radio value="Disease">疾病</el-radio>
<el-radio label="Drug">药品</el-radio>
<el-radio label="Check">检查</el-radio>
</el-radio-group>
</div>
<div v-if="typeRadio='Disease'">
<el-tree
:data="treeData"
:props="treeProps"
:expand-on-click-node="false"
@node-click="handleNodeClick"
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<el-tag size="small" :type="getTagType(data.level)" class="level-tag">
{{ getLevelLabel(data.level) }}
</el-tag>
<span class="code">{{ data.code }}</span>
<span class="label">{{ data.label }}</span>
</span>
</template>
</el-tree>
</div>
<button @click="resetView">重置视图</button>
<button @click="resetStyle">重置样式</button>
<!-- 操作按钮 -->
</div>
<!-- 图谱容器 -->
<div ref="graphContainer" class="graph-container" id="container"></div>
</div>
</template>
<script>
import { getTestGraphData } from "@/api/graph"
import {getGraph, getTestGraphData} from "@/api/graph"
import { Graph } from '@antv/g6';
export default {
@ -156,9 +186,9 @@ export default {
//
nodeShowLabel: true,
nodeFontSize: 12,
nodeFontColor: '#000000',
nodeFontColor: '#fff',
nodeShape: 'circle',
nodeSize: 60,
nodeSize: 50,
nodeFill: '#9FD5FF',
nodeStroke: '#5B8FF9',
nodeLineWidth: 2,
@ -168,9 +198,9 @@ export default {
edgeShowLabel: true,
edgeFontSize: 10,
edgeFontColor: '#666666',
edgeType: 'line',
edgeType: 'quadratic',
edgeLineWidth: 2,
edgeStroke: '#F00',
edgeStroke: '#b6b2b2',
edgeEndArrow: true,
edgeFontFamily: 'Microsoft YaHei, sans-serif',
@ -184,11 +214,19 @@ export default {
{source: 'A', target: 'B', label: '包含'},
{source: 'B', target: 'C', label: '子领域'}
]
}
},
treeData: [],
treeProps: {
children: 'children',
label: 'title' // el-tree
},
typeRadio:"Disease"
}
},
async mounted() {
this.loadTreeData()
await this.$nextTick();
try {
const response = await getTestGraphData(); // Promise
@ -203,10 +241,9 @@ export default {
label: this.nodeShowLabel,
labelFontSize: this.nodeFontSize,
labelFontFamily: this.nodeFontFamily,
labelFontColor: this.nodeFontColor,
labelFill: this.nodeFontColor,
}
}))
const updatedEdges = response.edges.map(edge => ({
...edge,
type: this.edgeType,
@ -262,14 +299,124 @@ export default {
edgeFontFamily: 'updateAllEdges',
},
methods: {
changeTree(){
},
async loadTreeData() {
try {
const res = await fetch('/icd10.json')
if (!res.ok) throw new Error('Failed to load JSON')
this.treeData = await res.json()
console.log(this.treeData)
} catch (error) {
console.error('加载 ICD-10 数据失败:', error)
this.$message.error('加载编码数据失败,请检查文件路径')
}
},
async handleNodeClick(data) {
console.log('点击节点:', data)
// code
if(data.level=="category"||
data.level=="subcategory"||
data.level=="diagnosis"){
data.type="Disease"
const response = await getGraph(data); // Promise
this.formatData(response)
}
this.$message.info(`已选中: ${data.title}`)
},
buildNodeLabelMap(nodes) {
this._nodeLabelMap = new Map();
nodes.forEach(node => {
this._nodeLabelMap.set(node.id, node.data?.label || 'default');
});
},
clearGraphState() {
if (!this._graph) return;
// 1.
this._graph.getNodeData().forEach(node => {
this._graph.setElementState(node.id,[]);
});
this._graph.getEdgeData().forEach(edge => {
this._graph.setElementState(edge.id,[]);
});
// 2. pending
// clearTimeout
},
formatData(data){
this._graph.stopLayout();
this.clearGraphState();
// data.nodes = data.nodes.slice(0, 1000);
// data.edges = data.edges.slice(0, 0);
const updatedEdges = data.edges.map(edge => ({
...edge,
type: this.edgeType,
style: {
endArrow: this.edgeEndArrow,
stroke: this.edgeStroke,
lineWidth: this.edgeLineWidth,
label: this.edgeShowLabel,
labelFontSize: this.edgeFontSize,
labelFontFamily: this.edgeFontFamily,
labelFill: this.edgeFontColor,
},
}))
const updatedNodes = data.nodes.map(node => ({
...node,
type: this.nodeShape,
style:{
size: this.nodeSize,
lineWidth: this.nodeLineWidth,
label: this.nodeShowLabel,
labelFontSize: this.nodeFontSize,
labelFontFamily: this.nodeFontFamily,
labelFill: this.nodeFontColor,
opacity: 1,
}
}))
const updatedData = {
nodes: updatedNodes,
edges: updatedEdges
}
this.buildNodeLabelMap(updatedNodes);
this.updateGraph(updatedData)
},
getLevelLabel(level) {
const map = {
chapter: '章',
section: '节',
category: '类目',
subcategory: '亚目',
diagnosis: '条目'
}
return map[level] || level
},
getTagType(level) {
const map = {
chapter: 'primary',
section: 'success',
category: 'warning',
subcategory: 'info',
diagnosis: 'info'
}
return map[level] || ''
},
initGraph() {
if (this._graph!=null){
this._graph.destroy()
this._graph = null;
}
if (!this._nodeLabelMap) {
this.buildNodeLabelMap(this.defaultData.nodes);
}
console.log(this.defaultData)
console.log(this._nodeLabelMap)
const container = this.$refs.graphContainer;
const width = container.clientWidth || 800;
const height = container.clientHeight || 600;
@ -279,15 +426,24 @@ export default {
width,
height,
layout: {
type: 'radial',
nodeSize: 32,
unitRadius: 100,
linkDistance: 200,
// type: 'force', //
// gravity: 0.3, //
// repulsion: 500, //
// attraction: 20, //
// preventOverlap: true //
// type: 'radial',
// preventOverlap: true,
// unitRadius: 200,
// maxPreventOverlapIteration:100
type: 'force-atlas2',
preventOverlap: true,
kr: 50,
center: [250, 250],
},
behaviors: [ 'zoom-canvas', 'drag-element','click-select','focus-element',
{
behaviors: [ 'zoom-canvas', 'drag-element',
'click-select','focus-element', {
type: 'hover-activate',
degree: 1, // 👈🏻 Activate relations.
degree: 1,
},
{
type: 'drag-canvas',
@ -300,37 +456,69 @@ export default {
node: {
style: {
fill: (d) => {
const label = d.data?.label;
if (label === 'Disease') return '#EF4444'; //
if (label === 'Drug') return '#91cc75'; // 绿
if (label === 'Symptom') return '#fac858'; //
return '#336eee'; //
},
stroke: (d) => {
const label = d.data?.label;
if (label === 'Disease') return '#B91C1C';
if (label === 'Drug') return '#047857';
if (label === 'Symptom') return '#B45309';
return '#1D4ED8';
},
labelText: (d) => d.data.name,
labelPlacement: 'center',
labelWordWrap: true,
labelMaxWidth: '150%',
labelMaxLines: 3,
labelTextOverflow: 'ellipsis',
labelTextAlign: 'center',
opacity: 1
},
state: {
active: {
fill: '#95D6FB',
stroke: '#1890FF',
lineWidth: 2,
shadowColor: '#1890FF',
shadowBlur: 10,
opacity: 1
},
highlight: {
stroke: '#FF6A00',
lineWidth: 3,
},
disabled: {
fill: '#ECECEC',
stroke: '#BFBFBF',
opacity: 0.5,
inactive: {
opacity: 0.3
},
normal:{
opacity: 1
}
},
},
edge: {
style: {
labelText: (d) => d.data.relationship.type,
stroke: (d) => {
// target label
const targetLabel = this._nodeLabelMap.get(d.target); // d.target ID
// target
if (targetLabel === 'Disease') return '#f8d2d2';
if (targetLabel === 'Drug') return '#c0efe2';
if (targetLabel === 'Symptom') return '#f5cebf';
return '#c9d3ee'; // default
},
labelFill: (d) => {
// target label
const targetLabel = this._nodeLabelMap.get(d.target); // d.target ID
// target
if (targetLabel === 'Disease') return '#ff4444';
if (targetLabel === 'Drug') return '#2f9b70';
if (targetLabel === 'Symptom') return '#f89775';
return '#6b91ff'; // default
}
},
state: {
selected: {
@ -338,22 +526,100 @@ export default {
lineWidth: 2,
},
highlight: {
stroke: '#FF6A00',
halo: true,
haloStroke: '#1890FF',
haloLineWidth: 6,
haloStrokeOpacity: 0.3,
lineWidth: 3,
opacity: 1
},
inactive: {
opacity: 0.3
},
normal:{
opacity: 1
}
},
},
data:this.defaultData
data:this.defaultData,
});
graph.render();
// graph.on('node:pointerover', (evt) => {
// const nodeItem = evt.target; //
// const relatedEdges = graph.getRelatedEdgesData(nodeItem.id);
// const relatedEdgeIds = relatedEdges.map(edge => edge.id);
//
// // 3. 'highlight'
// relatedEdgeIds.forEach(edgeId => {
// graph.setElementState(edgeId, 'highlight', true);
// });
// graph.setElementState(nodeItem.id, 'active',true);
//
// // 2. ID
// const neighborNodeIds = new Set();
// relatedEdges.forEach(edge => {
// if (edge.source !== nodeItem.id) neighborNodeIds.add(edge.source);
// if (edge.target !== nodeItem.id) neighborNodeIds.add(edge.target);
// });
//
// neighborNodeIds.forEach(id => {
// graph.setElementState(id, 'active', true);
// });
//
// graph.getEdgeData().forEach(edge => {
// if (!relatedEdgeIds.includes(edge.id)) {
// graph.setElementState(edge.id, 'inactive', true);
// }
// });
// graph.getNodeData().forEach(node => {
// if (node.id !== nodeItem.id && !neighborNodeIds.has(node.id)) {
// graph.setElementState(node.id, 'inactive',true);
// }
// });
// });
// graph.on('node:pointerleave', (evt) => {
// graph.getEdgeData().forEach(edge => {
// graph.setElementState(edge.id, 'highlight', false);
// graph.setElementState(edge.id, 'inactive', false);
// graph.setElementState(edge.id, 'normal', true);
// });
// graph.getNodeData().forEach(node => {
// graph.setElementState(node.id, 'active', false);
// graph.setElementState(node.id, 'inactive', false);
// graph.setElementState(node.id, 'normal', true);
// });
// });
graph.on('node:click', (evt) => {
const nodeItem = evt.target.id; //
let node=graph.getNodeData(nodeItem).data
let data={
label:node.name,
type:node.label
}
getGraph(data).then(response=>{
console.log(response)
this.formatData(response)
}); // Promise
});
this._graph = graph
this._graph?.fitView()
},
updateGraph(data) {
if (!this._graph) return
this._graph.setData(data)
this._graph.render()
},
@ -538,7 +804,6 @@ export default {
button {
width: 45%;
padding: 10px;
//margin: 10px 0;
background: #007bff;
color: white;
border: none;

Loading…
Cancel
Save