mirror of
https://gitcode.com/JianFeeeee/TrulyMEM-TrueHumanMEM.git
synced 2026-10-30 22:07:40 +00:00
fix: 登录问题修复 - session cookie配置 + 前端调试日志 + 力导向图布局优化
- 显式配置 session cookie (HTTPOnly/SameSite/Secure/Name) - 前端 fetch 增加 credentials: same-origin - 登录成功后备 cookie 设置 - 服务端 api_login 增加调试日志 - 星图布局基于 mention_count 优化节点间距 - 保留旧数据库数据
This commit is contained in:
@ -0,0 +1 @@
|
||||
[{"hapName":"phone-default-unsigned.hap","deviceTypes":["phone","tablet","2in1"],"isSigned":false}]
|
||||
@ -0,0 +1,105 @@
|
||||
{
|
||||
"modulePathMap": {
|
||||
"common": "/home/program/TrulyMEM-TrueHumanMEM/common",
|
||||
"graph": "/home/program/TrulyMEM-TrueHumanMEM/features/graph",
|
||||
"chat": "/home/program/TrulyMEM-TrueHumanMEM/features/chat",
|
||||
"settings": "/home/program/TrulyMEM-TrueHumanMEM/features/settings",
|
||||
"phone": "/home/program/TrulyMEM-TrueHumanMEM/products/phone"
|
||||
},
|
||||
"compileMode": "esmodule",
|
||||
"projectRootPath": "/home/program/TrulyMEM-TrueHumanMEM",
|
||||
"nodeModulesPath": "/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/intermediates/loader_out/default/node_modules",
|
||||
"byteCodeHarInfo": {},
|
||||
"declarationEntry": [],
|
||||
"moduleName": "phone",
|
||||
"hspNameOhmMap": {},
|
||||
"harNameOhmMap": {},
|
||||
"packageManagerType": "ohpm",
|
||||
"compileEntry": [
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/common/Index.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/component/ImmersiveTabNavigation.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/constant/TrulyMEMConstants.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/model/GraphDatabase.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/routermanager/PageContext.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/service/AIAgentService.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/service/GraphMemoryService.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/util/BreakpointSystem.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/util/Logger.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/common/src/main/ets/viewmodel/BaseViewModel.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/features/graph/Index.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/features/graph/src/main/ets/components/GraphComponents.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/features/graph/src/main/ets/pages/GraphPage.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/features/chat/Index.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/features/chat/src/main/ets/components/ChatComponents.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/features/chat/src/main/ets/pages/ChatPage.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/features/settings/Index.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/features/settings/src/main/ets/components/SettingsComponents.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/features/settings/src/main/ets/pages/SettingsPage.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/products/phone/src/main/ets/pages/MainPage.ets",
|
||||
"/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/generated/r/default/ResourceTable.ts"
|
||||
],
|
||||
"otherCompileFiles": [],
|
||||
"dynamicImportLibInfo": {
|
||||
"@ohos/common": {
|
||||
"hostModulesInfo": [
|
||||
{
|
||||
"hostDependencyName": "@ohos/common",
|
||||
"hostModuleName": "phone"
|
||||
}
|
||||
],
|
||||
"moduleName": "common",
|
||||
"entryFilePath": "/home/program/TrulyMEM-TrueHumanMEM/common/Index.ets",
|
||||
"isLocalDependency": true,
|
||||
"pkgPath": "/home/program/TrulyMEM-TrueHumanMEM/common"
|
||||
},
|
||||
"@ohos/graph": {
|
||||
"hostModulesInfo": [
|
||||
{
|
||||
"hostDependencyName": "@ohos/graph",
|
||||
"hostModuleName": "phone"
|
||||
}
|
||||
],
|
||||
"moduleName": "graph",
|
||||
"entryFilePath": "/home/program/TrulyMEM-TrueHumanMEM/features/graph/Index.ets",
|
||||
"isLocalDependency": true,
|
||||
"pkgPath": "/home/program/TrulyMEM-TrueHumanMEM/features/graph"
|
||||
},
|
||||
"@ohos/chat": {
|
||||
"hostModulesInfo": [
|
||||
{
|
||||
"hostDependencyName": "@ohos/chat",
|
||||
"hostModuleName": "phone"
|
||||
}
|
||||
],
|
||||
"moduleName": "chat",
|
||||
"entryFilePath": "/home/program/TrulyMEM-TrueHumanMEM/features/chat/Index.ets",
|
||||
"isLocalDependency": true,
|
||||
"pkgPath": "/home/program/TrulyMEM-TrueHumanMEM/features/chat"
|
||||
},
|
||||
"@ohos/settings": {
|
||||
"hostModulesInfo": [
|
||||
{
|
||||
"hostDependencyName": "@ohos/settings",
|
||||
"hostModuleName": "phone"
|
||||
}
|
||||
],
|
||||
"moduleName": "settings",
|
||||
"entryFilePath": "/home/program/TrulyMEM-TrueHumanMEM/features/settings/Index.ets",
|
||||
"isLocalDependency": true,
|
||||
"pkgPath": "/home/program/TrulyMEM-TrueHumanMEM/features/settings"
|
||||
}
|
||||
},
|
||||
"routerMap": [
|
||||
{
|
||||
"ohmurl": "@normalized:N&&&@ohos/phone/src/main/ets/pages/MainPage&",
|
||||
"name": "MainPage",
|
||||
"pageSourceFile": "/home/program/TrulyMEM-TrueHumanMEM/products/phone/src/main/ets/pages/MainPage.ets",
|
||||
"buildFunction": "MainPageBuilder"
|
||||
}
|
||||
],
|
||||
"hspResourcesMap": {},
|
||||
"updateVersionInfo": {},
|
||||
"customizedHar": false,
|
||||
"anBuildOutPut": "/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/intermediates/loader_out/default/an/arm64-v8a",
|
||||
"anBuildMode": "type"
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
{"@ohos/common":{"packageName":"@ohos/common","bundleName":"","moduleName":"","version":"1.0.0","entryPath":"Index.ets","isSO":false,"dependencyAlias":""},"@ohos/graph":{"packageName":"@ohos/graph","bundleName":"","moduleName":"","version":"1.0.0","entryPath":"Index.ets","isSO":false,"dependencyAlias":""},"@ohos/chat":{"packageName":"@ohos/chat","bundleName":"","moduleName":"","version":"1.0.0","entryPath":"Index.ets","isSO":false,"dependencyAlias":""},"@ohos/settings":{"packageName":"@ohos/settings","bundleName":"","moduleName":"","version":"1.0.0","entryPath":"Index.ets","isSO":false,"dependencyAlias":""},"@ohos/hypium":{"packageName":"@ohos/hypium","bundleName":"","moduleName":"","version":"1.0.24","entryPath":"index.js","isSO":false,"dependencyAlias":""},"@ohos/hamock":{"packageName":"@ohos/hamock","bundleName":"","moduleName":"","version":"1.0.0","entryPath":"index.ets","isSO":false,"dependencyAlias":""},"@ohos/phone":{"packageName":"@ohos/phone","bundleName":"","moduleName":"","version":"","entryPath":"src/main/","isSO":false,"dependencyAlias":""}}
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -0,0 +1,66 @@
|
||||
{
|
||||
"app": {
|
||||
"bundleName": "com.trulymem.app",
|
||||
"vendor": "trulymem",
|
||||
"versionCode": 1000001,
|
||||
"versionName": "1.0.0",
|
||||
"icon": "$media:layered_image",
|
||||
"label": "$string:app_name",
|
||||
"apiReleaseType": "Release",
|
||||
"compileSdkVersion": "6.1.0.105",
|
||||
"targetAPIVersion": 60100023,
|
||||
"minAPIVersion": 60100023,
|
||||
"compileSdkType": "HarmonyOS",
|
||||
"targetMinorAPIVersion": 0,
|
||||
"targetPatchAPIVersion": 0,
|
||||
"appEnvironments": [],
|
||||
"bundleType": "app",
|
||||
"buildMode": "debug",
|
||||
"debug": true
|
||||
},
|
||||
"module": {
|
||||
"name": "phone",
|
||||
"type": "entry",
|
||||
"description": "TrulyMEM phone entry module",
|
||||
"mainElement": "EntryAbility",
|
||||
"deviceTypes": [
|
||||
"phone",
|
||||
"tablet",
|
||||
"2in1"
|
||||
],
|
||||
"deliveryWithInstall": true,
|
||||
"installationFree": false,
|
||||
"pages": "$profile:main_pages",
|
||||
"routerMap": "$profile:router_map",
|
||||
"abilities": [
|
||||
{
|
||||
"name": "EntryAbility",
|
||||
"srcEntry": "./ets/entryability/EntryAbility.ets",
|
||||
"description": "$string:EntryAbility_desc",
|
||||
"label": "$string:EntryAbility_label",
|
||||
"startWindowIcon": "$media:startIcon",
|
||||
"startWindowBackground": "$color:start_window_background",
|
||||
"exported": true,
|
||||
"skills": [
|
||||
{
|
||||
"entities": [
|
||||
"entity.system.home"
|
||||
],
|
||||
"actions": [
|
||||
"ohos.want.action.home"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"requestPermissions": [
|
||||
{
|
||||
"name": "ohos.permission.INTERNET"
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.GET_NETWORK_INFO"
|
||||
}
|
||||
],
|
||||
"packageName": "@ohos/phone"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
{"app":{"bundleName":"com.trulymem.app","vendor":"trulymem","versionCode":1000001,"versionName":"1.0.0","icon":"$media:layered_image","label":"$string:app_name","apiReleaseType":"Release","compileSdkVersion":"6.1.0.105","targetAPIVersion":60100023,"minAPIVersion":60100023,"compileSdkType":"HarmonyOS","targetMinorAPIVersion":0,"targetPatchAPIVersion":0,"appEnvironments":[],"bundleType":"app","buildMode":"debug","debug":true,"iconId":67108864,"labelId":67108869},"module":{"name":"phone","type":"entry","description":"TrulyMEM phone entry module","mainElement":"EntryAbility","deviceTypes":["phone","tablet","2in1"],"deliveryWithInstall":true,"installationFree":false,"pages":"$profile:main_pages","routerMap":"$profile:router_map","abilities":[{"name":"EntryAbility","srcEntry":"./ets/entryability/EntryAbility.ets","description":"$string:EntryAbility_desc","label":"$string:EntryAbility_label","startWindowIcon":"$media:startIcon","startWindowBackground":"$color:start_window_background","exported":true,"skills":[{"entities":["entity.system.home"],"actions":["ohos.want.action.home"]}],"descriptionId":67108865,"labelId":67108866,"startWindowIconId":67108872,"startWindowBackgroundId":67108871}],"requestPermissions":[{"name":"ohos.permission.INTERNET"},{"name":"ohos.permission.GET_NETWORK_INFO"}],"packageName":"@ohos/phone","virtualMachine":"ark13.0.1.0","compileMode":"esmodule","dependencies":[]}}
|
||||
@ -0,0 +1 @@
|
||||
{"libs":{},"stripped":{}}
|
||||
@ -0,0 +1 @@
|
||||
{"libs":{},"binxo":{},"binxoSymbol":{"enableAsanBinxo":false,"excludeSoFromBinxo":[]}}
|
||||
@ -0,0 +1,69 @@
|
||||
{
|
||||
"app": {
|
||||
"bundleName": "com.trulymem.app",
|
||||
"vendor": "trulymem",
|
||||
"versionCode": 1000001,
|
||||
"versionName": "1.0.0",
|
||||
"icon": "$media:layered_image",
|
||||
"label": "$string:app_name",
|
||||
"apiReleaseType": "Release",
|
||||
"compileSdkVersion": "6.1.0.105",
|
||||
"targetAPIVersion": 60100023,
|
||||
"minAPIVersion": 60100023,
|
||||
"compileSdkType": "HarmonyOS",
|
||||
"targetMinorAPIVersion": 0,
|
||||
"targetPatchAPIVersion": 0,
|
||||
"appEnvironments": [],
|
||||
"bundleType": "app",
|
||||
"buildMode": "debug",
|
||||
"debug": true
|
||||
},
|
||||
"module": {
|
||||
"name": "phone",
|
||||
"type": "entry",
|
||||
"description": "TrulyMEM phone entry module",
|
||||
"mainElement": "EntryAbility",
|
||||
"deviceTypes": [
|
||||
"phone",
|
||||
"tablet",
|
||||
"2in1"
|
||||
],
|
||||
"deliveryWithInstall": true,
|
||||
"installationFree": false,
|
||||
"pages": "$profile:main_pages",
|
||||
"routerMap": "$profile:router_map",
|
||||
"abilities": [
|
||||
{
|
||||
"name": "EntryAbility",
|
||||
"srcEntry": "./ets/entryability/EntryAbility.ets",
|
||||
"description": "$string:EntryAbility_desc",
|
||||
"label": "$string:EntryAbility_label",
|
||||
"startWindowIcon": "$media:startIcon",
|
||||
"startWindowBackground": "$color:start_window_background",
|
||||
"exported": true,
|
||||
"skills": [
|
||||
{
|
||||
"entities": [
|
||||
"entity.system.home"
|
||||
],
|
||||
"actions": [
|
||||
"ohos.want.action.home"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"requestPermissions": [
|
||||
{
|
||||
"name": "ohos.permission.INTERNET"
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.GET_NETWORK_INFO"
|
||||
}
|
||||
],
|
||||
"packageName": "@ohos/phone",
|
||||
"virtualMachine": "ark13.0.1.0",
|
||||
"compileMode": "esmodule",
|
||||
"dependencies": []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
string EntryAbility_desc 0x04000001
|
||||
string EntryAbility_label 0x04000002
|
||||
string MainAbility_desc 0x04000003
|
||||
string MainAbility_label 0x04000004
|
||||
string app_name 0x04000005
|
||||
string module_desc 0x04000006
|
||||
color start_window_background 0x04000007
|
||||
media layered_image 0x04000000
|
||||
media startIcon 0x04000008
|
||||
profile main_pages 0x04000009
|
||||
profile router_map 0x0400000a
|
||||
@ -0,0 +1 @@
|
||||
{"app":{"bundleName":"com.trulymem.app","vendor":"trulymem","versionCode":1000001,"versionName":"1.0.0","icon":"$media:layered_image","label":"$string:app_name","apiReleaseType":"Release","compileSdkVersion":"6.1.0.105","targetAPIVersion":60100023,"minAPIVersion":60100023,"compileSdkType":"HarmonyOS","targetMinorAPIVersion":0,"targetPatchAPIVersion":0,"appEnvironments":[],"bundleType":"app","buildMode":"debug","debug":true},"module":{"name":"phone","type":"entry","description":"TrulyMEM phone entry module","mainElement":"EntryAbility","deviceTypes":["phone","tablet","2in1"],"deliveryWithInstall":true,"installationFree":false,"pages":"$profile:main_pages","routerMap":"$profile:router_map","abilities":[{"name":"EntryAbility","srcEntry":"./ets/entryability/EntryAbility.ets","description":"$string:EntryAbility_desc","label":"$string:EntryAbility_label","startWindowIcon":"$media:startIcon","startWindowBackground":"$color:start_window_background","exported":true,"skills":[{"entities":["entity.system.home"],"actions":["ohos.want.action.home"]}]}],"requestPermissions":[{"name":"ohos.permission.INTERNET"},{"name":"ohos.permission.GET_NETWORK_INFO"}],"packageName":"@ohos/phone","virtualMachine":"ark13.0.1.0","compileMode":"esmodule","dependencies":[]}}
|
||||
@ -0,0 +1,47 @@
|
||||
{
|
||||
"record": [{
|
||||
"type": "media",
|
||||
"name": "layered_image",
|
||||
"id": "0x04000000"
|
||||
}, {
|
||||
"type": "string",
|
||||
"name": "EntryAbility_desc",
|
||||
"id": "0x04000001"
|
||||
}, {
|
||||
"type": "string",
|
||||
"name": "EntryAbility_label",
|
||||
"id": "0x04000002"
|
||||
}, {
|
||||
"type": "string",
|
||||
"name": "MainAbility_desc",
|
||||
"id": "0x04000003"
|
||||
}, {
|
||||
"type": "string",
|
||||
"name": "MainAbility_label",
|
||||
"id": "0x04000004"
|
||||
}, {
|
||||
"type": "string",
|
||||
"name": "app_name",
|
||||
"id": "0x04000005"
|
||||
}, {
|
||||
"type": "string",
|
||||
"name": "module_desc",
|
||||
"id": "0x04000006"
|
||||
}, {
|
||||
"type": "color",
|
||||
"name": "start_window_background",
|
||||
"id": "0x04000007"
|
||||
}, {
|
||||
"type": "media",
|
||||
"name": "startIcon",
|
||||
"id": "0x04000008"
|
||||
}, {
|
||||
"type": "profile",
|
||||
"name": "main_pages",
|
||||
"id": "0x04000009"
|
||||
}, {
|
||||
"type": "profile",
|
||||
"name": "router_map",
|
||||
"id": "0x0400000A"
|
||||
}]
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
{"app":{"bundleName":"com.trulymem.app","vendor":"trulymem","versionCode":1000001,"versionName":"1.0.0","icon":"$media:layered_image","label":"$string:app_name","apiReleaseType":"Release","compileSdkVersion":"6.1.0.105","targetAPIVersion":60100023,"minAPIVersion":60100023,"compileSdkType":"HarmonyOS","targetMinorAPIVersion":0,"targetPatchAPIVersion":0,"appEnvironments":[],"bundleType":"app","buildMode":"debug","debug":true,"iconId":67108864,"labelId":67108869},"module":{"name":"phone","type":"entry","description":"TrulyMEM phone entry module","mainElement":"EntryAbility","deviceTypes":["phone","tablet","2in1"],"deliveryWithInstall":true,"installationFree":false,"pages":"$profile:main_pages","routerMap":"$profile:router_map","abilities":[{"name":"EntryAbility","srcEntry":"./ets/entryability/EntryAbility.ets","description":"$string:EntryAbility_desc","label":"$string:EntryAbility_label","startWindowIcon":"$media:startIcon","startWindowBackground":"$color:start_window_background","exported":true,"skills":[{"entities":["entity.system.home"],"actions":["ohos.want.action.home"]}],"descriptionId":67108865,"labelId":67108866,"startWindowIconId":67108872,"startWindowBackgroundId":67108871}],"requestPermissions":[{"name":"ohos.permission.INTERNET"},{"name":"ohos.permission.GET_NETWORK_INFO"}],"packageName":"@ohos/phone","virtualMachine":"ark13.0.1.0","compileMode":"esmodule","dependencies":[]}}
|
||||
@ -0,0 +1 @@
|
||||
{"context":{"extensionPath":"/opt/harmonyos/ohos-sdk/linux/command-line-tools/sdk/default/hms/toolchains/lib/libimage_transcoder_shared.so"},"compression":{"media":{"enable":false},"filters":[]}}
|
||||
@ -0,0 +1 @@
|
||||
{"configPath":"/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/intermediates/process_profile/default/module.json","packageName":"com.trulymem.app","output":"/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/intermediates/res/default","moduleNames":"phone,common,graph,chat,settings","ResourceTable":["/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/generated/r/default/ResourceTable.h","/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/generated/r/default/ResourceTable.ts"],"applicationResource":"/home/program/TrulyMEM-TrueHumanMEM/AppScope/resources","moduleResources":["/home/program/TrulyMEM-TrueHumanMEM/products/phone/src/main/resources"],"dependencies":["/home/program/TrulyMEM-TrueHumanMEM/features/graph/src/main/resources"],"iconCheck":true,"compression":"/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/intermediates/res/default/opt-compression.json","ids":"/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/intermediates/res/default/ids_map","definedIds":"/home/program/TrulyMEM-TrueHumanMEM/products/phone/build/default/intermediates/res/default/ids_map/id_defined.json","definedSysIds":"/opt/harmonyos/ohos-sdk/linux/command-line-tools/sdk/default/hms/toolchains/id_defined.json"}
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
{"layered": true, "background": ":startIcon", "foreground": ":startIcon"}
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
|
||||
<rect width="200" height="200" rx="30" fill="#6366f1"/>
|
||||
<text x="100" y="130" font-family="Arial" font-size="80" fill="white" text-anchor="middle" font-weight="bold">T</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 313 B |
Binary file not shown.
|
After Width: | Height: | Size: 70 B |
@ -0,0 +1,6 @@
|
||||
{
|
||||
"src": [
|
||||
"pages/SplashPage",
|
||||
"pages/Index"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
{"routerMap":[{"name":"MainPage","pageSourceFile":"src/main/ets/pages/MainPage.ets","buildFunction":"MainPageBuilder","ohmurl":"@normalized:N&&&@ohos/phone/src/main/ets/pages/MainPage&","bundleName":"com.trulymem.app","moduleName":"phone"}]}
|
||||
@ -0,0 +1,558 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>记忆星图 - TrulyMEM</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: 'Courier New', monospace; background: #0a0a1a; overflow: hidden; width: 100vw; height: 100vh; }
|
||||
#canvas-container { width: 100%; height: 100%; position: relative; }
|
||||
canvas { display: block; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="canvas-container"></div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<script>
|
||||
let scene, camera, renderer, controls;
|
||||
let nodes = [], edges = [];
|
||||
let nodeMeshes = [], edgeLines = [];
|
||||
let starField, nebulaParticles;
|
||||
let raycaster, mouse;
|
||||
let hoveredNode = null, selectedNode = null;
|
||||
let animationId;
|
||||
let highlightPulse = 0;
|
||||
let searchTerm = '';
|
||||
let activeTypeFilter = '全部';
|
||||
let isDragging = false;
|
||||
let dragNode = null;
|
||||
let nodePositions = {}; // 存储节点位置用于拖拽
|
||||
let nodeCount = 0;
|
||||
let edgeCount = 0;
|
||||
|
||||
const typeColors = { 'person': 0x4488ff, 'task': 0xff8844, 'ai': 0xaa44ff, 'concept': 0x44ff88, 'object': 0xff4444 };
|
||||
const defaultColor = 0xcccccc;
|
||||
const edgeColors = { '喜欢': 0xff6b6b, '学习': 0x4ecdc4, '属于': 0x45b7d1, '相关': 0x96ceb4, '使用': 0xfeca57, '创建': 0xff9ff3 };
|
||||
const defaultEdgeColor = 0x444466;
|
||||
|
||||
function init() {
|
||||
scene = new THREE.Scene();
|
||||
scene.fog = new THREE.FogExp2(0x0a0a1a, 0.015);
|
||||
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 2000);
|
||||
camera.position.set(0, 30, 60);
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setClearColor(0x0a0a1a, 1);
|
||||
document.getElementById('canvas-container').appendChild(renderer.domElement);
|
||||
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
controls.dampingFactor = 0.05;
|
||||
const ambientLight = new THREE.AmbientLight(0x444466, 0.6);
|
||||
scene.add(ambientLight);
|
||||
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
||||
directionalLight.position.set(50, 100, 50);
|
||||
scene.add(directionalLight);
|
||||
raycaster = new THREE.Raycaster();
|
||||
mouse = new THREE.Vector2();
|
||||
createStarField();
|
||||
createNebula();
|
||||
initResizeObserver();
|
||||
renderer.domElement.addEventListener('mousemove', onMouseMove);
|
||||
renderer.domElement.addEventListener('click', onMouseClick);
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.data.type === 'graph_data') {
|
||||
window.__graphData = event.data.payload;
|
||||
loadGraphData(event.data.payload);
|
||||
}
|
||||
if (event.data.type === 'search_nodes') {
|
||||
searchTerm = event.data.query || '';
|
||||
applyFilters();
|
||||
}
|
||||
if (event.data.type === 'highlight_node') {
|
||||
highlightNodeById(event.data.nodeId);
|
||||
}
|
||||
});
|
||||
try {
|
||||
if (window.nativeBridge && window.nativeBridge.requestGraphData) {
|
||||
window.nativeBridge.requestGraphData();
|
||||
}
|
||||
} catch(e) {}
|
||||
initTouchEvents();
|
||||
initDragFunctionality();
|
||||
animate();
|
||||
}
|
||||
|
||||
function createStarField() {
|
||||
const starCount = 3000;
|
||||
const positions = new Float32Array(starCount * 3);
|
||||
const colors = new Float32Array(starCount * 3);
|
||||
for (let i = 0; i < starCount; i++) {
|
||||
const i3 = i * 3;
|
||||
const radius = 400 + Math.random() * 600;
|
||||
const theta = Math.random() * Math.PI * 2;
|
||||
const phi = Math.acos(2 * Math.random() - 1);
|
||||
positions[i3] = radius * Math.sin(phi) * Math.cos(theta);
|
||||
positions[i3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
|
||||
positions[i3 + 2] = radius * Math.cos(phi);
|
||||
const colorChoice = Math.random();
|
||||
if (colorChoice < 0.7) {
|
||||
colors[i3] = 0.8 + Math.random() * 0.2;
|
||||
colors[i3 + 1] = 0.8 + Math.random() * 0.2;
|
||||
colors[i3 + 2] = 1.0;
|
||||
} else {
|
||||
colors[i3] = 1.0;
|
||||
colors[i3 + 1] = 0.9 + Math.random() * 0.1;
|
||||
colors[i3 + 2] = 0.8 + Math.random() * 0.2;
|
||||
}
|
||||
}
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||||
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||||
const material = new THREE.PointsMaterial({ size: 1.5, vertexColors: true, transparent: true, opacity: 0.8, sizeAttenuation: true });
|
||||
starField = new THREE.Points(geometry, material);
|
||||
scene.add(starField);
|
||||
}
|
||||
|
||||
function createNebula() {
|
||||
const nebulaCount = 500;
|
||||
const positions = new Float32Array(nebulaCount * 3);
|
||||
const colors = new Float32Array(nebulaCount * 3);
|
||||
for (let i = 0; i < nebulaCount; i++) {
|
||||
const i3 = i * 3;
|
||||
positions[i3] = (Math.random() - 0.5) * 800;
|
||||
positions[i3 + 1] = (Math.random() - 0.5) * 800;
|
||||
positions[i3 + 2] = (Math.random() - 0.5) * 800;
|
||||
const colorChoice = Math.random();
|
||||
if (colorChoice < 0.33) {
|
||||
colors[i3] = 0.5 + Math.random() * 0.3; colors[i3 + 1] = 0.2 + Math.random() * 0.2; colors[i3 + 2] = 0.7 + Math.random() * 0.3;
|
||||
} else if (colorChoice < 0.66) {
|
||||
colors[i3] = 0.2 + Math.random() * 0.2; colors[i3 + 1] = 0.3 + Math.random() * 0.3; colors[i3 + 2] = 0.8 + Math.random() * 0.2;
|
||||
} else {
|
||||
colors[i3] = 0.7 + Math.random() * 0.3; colors[i3 + 1] = 0.2 + Math.random() * 0.2; colors[i3 + 2] = 0.5 + Math.random() * 0.3;
|
||||
}
|
||||
}
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||||
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||||
const material = new THREE.PointsMaterial({ size: 8, vertexColors: true, transparent: true, opacity: 0.15, sizeAttenuation: true, blending: THREE.AdditiveBlending });
|
||||
nebulaParticles = new THREE.Points(geometry, material);
|
||||
scene.add(nebulaParticles);
|
||||
}
|
||||
|
||||
window.loadGraphData = function(data) {
|
||||
if (data && data.nodes && data.edges) {
|
||||
nodes = data.nodes.map(n => ({ id: n.id, name: n.label || n.name, type: n.type, mention_count: n.mentions || 1 }));
|
||||
edges = data.edges.map(e => ({ id: e.id, source: e.from || e.source, target: e.to || e.target, relation_type: e.label || e.relation }));
|
||||
nodeCount = nodes.length;
|
||||
edgeCount = edges.length;
|
||||
createGraphVisualization();
|
||||
// 通知 ArkTS 统计信息
|
||||
try {
|
||||
if (window.nativeBridge && window.nativeBridge.onStatsUpdate) {
|
||||
window.nativeBridge.onStatsUpdate(nodeCount, edgeCount);
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function createGraphVisualization() {
|
||||
nodeMeshes.forEach(mesh => scene.remove(mesh));
|
||||
edgeLines.forEach(line => scene.remove(line));
|
||||
nodeMeshes = [];
|
||||
edgeLines = [];
|
||||
if (nodes.length === 0) return;
|
||||
const nodeDegrees = {};
|
||||
nodes.forEach(n => nodeDegrees[n.id] = 0);
|
||||
edges.forEach(e => {
|
||||
nodeDegrees[e.source] = (nodeDegrees[e.source] || 0) + 1;
|
||||
nodeDegrees[e.target] = (nodeDegrees[e.target] || 0) + 1;
|
||||
});
|
||||
const positions = {};
|
||||
nodePositions = positions;
|
||||
const maxDegree = Math.max(...Object.values(nodeDegrees), 1);
|
||||
nodes.forEach((node, i) => {
|
||||
const angle = (i / nodes.length) * Math.PI * 2;
|
||||
const radius = 15 + (nodeDegrees[node.id] / maxDegree) * 35;
|
||||
positions[node.id] = { x: radius * Math.cos(angle), y: (Math.random() - 0.5) * 10, z: radius * Math.sin(angle) };
|
||||
});
|
||||
for (let iter = 0; iter < 200; iter++) {
|
||||
Object.keys(positions).forEach(id1 => {
|
||||
Object.keys(positions).forEach(id2 => {
|
||||
if (id1 >= id2) return;
|
||||
const pos1 = positions[id1], pos2 = positions[id2];
|
||||
const dx = pos1.x - pos2.x, dy = pos1.y - pos2.y, dz = pos1.z - pos2.z;
|
||||
const dist = Math.sqrt(dx*dx + dy*dy + dz*dz) + 0.1;
|
||||
if (dist < 20) {
|
||||
const force = 0.3 / (dist * dist);
|
||||
pos1.x += (dx/dist)*force; pos1.y += (dy/dist)*force; pos1.z += (dz/dist)*force;
|
||||
pos2.x -= (dx/dist)*force; pos2.y -= (dy/dist)*force; pos2.z -= (dz/dist)*force;
|
||||
}
|
||||
});
|
||||
});
|
||||
edges.forEach(edge => {
|
||||
const pos1 = positions[edge.source], pos2 = positions[edge.target];
|
||||
if (!pos1 || !pos2) return;
|
||||
const dx = pos2.x - pos1.x, dy = pos2.y - pos1.y, dz = pos2.z - pos1.z;
|
||||
const dist = Math.sqrt(dx*dx + dy*dy + dz*dz) + 0.1;
|
||||
if (dist > 15) {
|
||||
const force = 0.08;
|
||||
pos1.x += (dx/dist)*force; pos1.y += (dy/dist)*force; pos1.z += (dz/dist)*force;
|
||||
pos2.x -= (dx/dist)*force; pos2.y -= (dy/dist)*force; pos2.z -= (dz/dist)*force;
|
||||
}
|
||||
});
|
||||
}
|
||||
nodes.forEach(node => {
|
||||
const pos = positions[node.id];
|
||||
if (!pos) return;
|
||||
const radius = 0.4 + Math.min(node.mention_count * 0.15, 1.5);
|
||||
const color = typeColors[node.type] || defaultColor;
|
||||
const geometry = new THREE.SphereGeometry(radius, 16, 12);
|
||||
const material = new THREE.MeshPhongMaterial({ color: color, emissive: color, emissiveIntensity: 0.5 + Math.min(node.mention_count * 0.05, 0.3), shininess: 30, transparent: true, opacity: 0 });
|
||||
const sphere = new THREE.Mesh(geometry, material);
|
||||
sphere.position.set(pos.x, pos.y, pos.z);
|
||||
sphere.userData = { nodeId: node.id, nodeData: node };
|
||||
scene.add(sphere);
|
||||
nodeMeshes.push(sphere);
|
||||
fadeInObject(sphere, 500);
|
||||
});
|
||||
edges.forEach(edge => {
|
||||
const pos1 = positions[edge.source], pos2 = positions[edge.target];
|
||||
if (!pos1 || !pos2) return;
|
||||
const color = edgeColors[edge.relation_type] || defaultEdgeColor;
|
||||
const geometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(pos1.x, pos1.y, pos1.z), new THREE.Vector3(pos2.x, pos2.y, pos2.z)]);
|
||||
const material = new THREE.LineBasicMaterial({ color: color, transparent: true, opacity: 0, linewidth: 1 });
|
||||
const line = new THREE.Line(geometry, material);
|
||||
line.userData = { edgeId: edge.id, edgeData: edge };
|
||||
scene.add(line);
|
||||
edgeLines.push(line);
|
||||
fadeInLine(line, 500);
|
||||
});
|
||||
const allPositions = Object.values(positions);
|
||||
if (allPositions.length > 0) {
|
||||
let maxDist = 0;
|
||||
allPositions.forEach(pos => { maxDist = Math.max(maxDist, Math.sqrt(pos.x*pos.x + pos.y*pos.y + pos.z*pos.z)); });
|
||||
camera.position.set(maxDist * 2.2, maxDist * 1.5, maxDist * 2.2);
|
||||
controls.target.set(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseMove(event) {
|
||||
const rect = renderer.domElement.getBoundingClientRect();
|
||||
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
||||
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const intersects = raycaster.intersectObjects(nodeMeshes);
|
||||
if (intersects.length > 0) {
|
||||
const node = intersects[0].object;
|
||||
if (hoveredNode !== node) {
|
||||
if (hoveredNode) hoveredNode.scale.set(1, 1, 1);
|
||||
hoveredNode = node;
|
||||
node.scale.set(1.2, 1.2, 1.2);
|
||||
}
|
||||
} else {
|
||||
if (hoveredNode) { hoveredNode.scale.set(1, 1, 1); hoveredNode = null; }
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseClick(event) {
|
||||
if (isDragging) return;
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const intersects = raycaster.intersectObjects(nodeMeshes);
|
||||
if (intersects.length > 0) {
|
||||
const node = intersects[0].object;
|
||||
if (selectedNode === node) {
|
||||
selectedNode = null;
|
||||
resetHighlight();
|
||||
// 通知 ArkTS 取消选中
|
||||
try {
|
||||
if (window.nativeBridge && window.nativeBridge.onNodeClick) {
|
||||
window.nativeBridge.onNodeClick(-1, '');
|
||||
}
|
||||
} catch(e) {}
|
||||
} else {
|
||||
selectedNode = node;
|
||||
highlightNodeConnections(node.userData.nodeData);
|
||||
// 通知 ArkTS 节点被点击
|
||||
try {
|
||||
if (window.nativeBridge && window.nativeBridge.onNodeClick) {
|
||||
window.nativeBridge.onNodeClick(node.userData.nodeData.id, node.userData.nodeData.name);
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
} else {
|
||||
selectedNode = null;
|
||||
resetHighlight();
|
||||
try {
|
||||
if (window.nativeBridge && window.nativeBridge.onNodeClick) {
|
||||
window.nativeBridge.onNodeClick(-1, '');
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function fadeInObject(obj, duration) {
|
||||
const endOpacity = obj.material.transparent ? obj.material.opacity : 1;
|
||||
obj.material.opacity = 0;
|
||||
obj.material.transparent = true;
|
||||
const startTime = Date.now();
|
||||
function animateFade() {
|
||||
const elapsed = Date.now() - startTime;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
obj.material.opacity = progress * endOpacity;
|
||||
if (progress < 1) requestAnimationFrame(animateFade);
|
||||
else obj.material.transparent = endOpacity < 1;
|
||||
}
|
||||
animateFade();
|
||||
}
|
||||
|
||||
function fadeInLine(line, duration) {
|
||||
const endOpacity = 0.4;
|
||||
line.material.opacity = 0;
|
||||
const startTime = Date.now();
|
||||
function animateFade() {
|
||||
const elapsed = Date.now() - startTime;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
line.material.opacity = progress * endOpacity;
|
||||
if (progress < 1) requestAnimationFrame(animateFade);
|
||||
}
|
||||
animateFade();
|
||||
}
|
||||
|
||||
function highlightNodeById(nodeId) {
|
||||
const mesh = nodeMeshes.find(m => m.userData.nodeData.id === nodeId);
|
||||
if (mesh) {
|
||||
selectedNode = mesh;
|
||||
highlightNodeConnections(mesh.userData.nodeData);
|
||||
}
|
||||
}
|
||||
|
||||
function initDragFunctionality() {
|
||||
let dragStartPos = { x: 0, y: 0 };
|
||||
renderer.domElement.addEventListener('mousedown', (event) => {
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const intersects = raycaster.intersectObjects(nodeMeshes);
|
||||
if (intersects.length > 0) {
|
||||
isDragging = false;
|
||||
dragNode = intersects[0].object;
|
||||
dragStartPos = { x: event.clientX, y: event.clientY };
|
||||
}
|
||||
});
|
||||
renderer.domElement.addEventListener('mousemove', (event) => {
|
||||
if (dragNode) {
|
||||
const dx = event.clientX - dragStartPos.x;
|
||||
const dy = event.clientY - dragStartPos.y;
|
||||
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) isDragging = true;
|
||||
if (isDragging) {
|
||||
const rect = renderer.domElement.getBoundingClientRect();
|
||||
const mouseX = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
||||
const mouseY = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
||||
const vector = new THREE.Vector3(mouseX, mouseY, 0.5);
|
||||
vector.unproject(camera);
|
||||
const dir = vector.sub(camera.position).normalize();
|
||||
const distance = -camera.position.z / dir.z;
|
||||
const newPos = camera.position.clone().add(dir.multiplyScalar(distance));
|
||||
dragNode.position.copy(newPos);
|
||||
if (nodePositions[dragNode.userData.nodeId]) {
|
||||
nodePositions[dragNode.userData.nodeId] = { x: newPos.x, y: newPos.y, z: newPos.z };
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
renderer.domElement.addEventListener('mouseup', () => {
|
||||
if (dragNode) { dragNode = null; isDragging = false; }
|
||||
});
|
||||
}
|
||||
|
||||
function initTouchEvents() {
|
||||
let touchStartPos = { x: 0, y: 0 };
|
||||
let touchStartDistance = 0;
|
||||
let isTouchDrag = false;
|
||||
renderer.domElement.addEventListener('touchstart', (event) => {
|
||||
event.preventDefault();
|
||||
if (event.touches.length === 1) {
|
||||
const touch = event.touches[0];
|
||||
touchStartPos = { x: touch.clientX, y: touch.clientY };
|
||||
isTouchDrag = false;
|
||||
const rect = renderer.domElement.getBoundingClientRect();
|
||||
mouse.x = ((touch.clientX - rect.left) / rect.width) * 2 - 1;
|
||||
mouse.y = -((touch.clientY - rect.top) / rect.height) * 2 + 1;
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const intersects = raycaster.intersectObjects(nodeMeshes);
|
||||
if (intersects.length > 0) {
|
||||
const node = intersects[0].object;
|
||||
if (selectedNode === node) {
|
||||
selectedNode = null;
|
||||
resetHighlight();
|
||||
try { if (window.nativeBridge && window.nativeBridge.onNodeClick) window.nativeBridge.onNodeClick(-1, ''); } catch(e) {}
|
||||
} else {
|
||||
selectedNode = node;
|
||||
highlightNodeConnections(node.userData.nodeData);
|
||||
try { if (window.nativeBridge && window.nativeBridge.onNodeClick) window.nativeBridge.onNodeClick(node.userData.nodeData.id, node.userData.nodeData.name); } catch(e) {}
|
||||
}
|
||||
}
|
||||
} else if (event.touches.length === 2) {
|
||||
const dx = event.touches[0].clientX - event.touches[1].clientX;
|
||||
const dy = event.touches[0].clientY - event.touches[1].clientY;
|
||||
touchStartDistance = Math.sqrt(dx*dx + dy*dy);
|
||||
}
|
||||
}, { passive: false });
|
||||
renderer.domElement.addEventListener('touchmove', (event) => {
|
||||
event.preventDefault();
|
||||
if (event.touches.length === 1 && controls) {
|
||||
const touch = event.touches[0];
|
||||
const dx = touch.clientX - touchStartPos.x;
|
||||
const dy = touch.clientY - touchStartPos.y;
|
||||
if (Math.abs(dx) > 5 || Math.abs(dy) > 5) isTouchDrag = true;
|
||||
if (isTouchDrag) {
|
||||
const rotateSpeed = 0.005;
|
||||
controls.rotateLeft(-dx * rotateSpeed);
|
||||
controls.rotateUp(-dy * rotateSpeed);
|
||||
controls.update();
|
||||
touchStartPos = { x: touch.clientX, y: touch.clientY };
|
||||
}
|
||||
} else if (event.touches.length === 2 && controls) {
|
||||
const dx = event.touches[0].clientX - event.touches[1].clientX;
|
||||
const dy = event.touches[0].clientY - event.touches[1].clientY;
|
||||
const distance = Math.sqrt(dx*dx + dy*dy);
|
||||
const scale = touchStartDistance / distance;
|
||||
camera.position.multiplyScalar(scale);
|
||||
controls.update();
|
||||
touchStartDistance = distance;
|
||||
}
|
||||
}, { passive: false });
|
||||
renderer.domElement.addEventListener('touchend', (event) => {
|
||||
if (event.touches.length === 0) isTouchDrag = false;
|
||||
}, { passive: false });
|
||||
}
|
||||
|
||||
function applyFilters() {
|
||||
nodeMeshes.forEach(mesh => {
|
||||
const nodeData = mesh.userData.nodeData;
|
||||
const nameMatch = nodeData.name.toLowerCase().includes(searchTerm);
|
||||
const typeMatch = activeTypeFilter === '全部' || nodeData.type === activeTypeFilter;
|
||||
if (nameMatch && typeMatch) {
|
||||
mesh.material.transparent = false;
|
||||
mesh.material.opacity = 1;
|
||||
mesh.scale.setScalar(1);
|
||||
} else {
|
||||
mesh.material.transparent = true;
|
||||
mesh.material.opacity = 0.2;
|
||||
mesh.scale.setScalar(0.8);
|
||||
}
|
||||
});
|
||||
edgeLines.forEach(line => {
|
||||
const edgeData = line.userData.edgeData;
|
||||
const sourceNode = nodes.find(n => n.id === edgeData.source);
|
||||
const targetNode = nodes.find(n => n.id === edgeData.target);
|
||||
const sourceMatch = sourceNode && sourceNode.name.toLowerCase().includes(searchTerm) && (activeTypeFilter === '全部' || sourceNode.type === activeTypeFilter);
|
||||
const targetMatch = targetNode && targetNode.name.toLowerCase().includes(searchTerm) && (activeTypeFilter === '全部' || targetNode.type === activeTypeFilter);
|
||||
if ((sourceMatch || targetMatch) && searchTerm === '' && activeTypeFilter === '全部') {
|
||||
line.material.transparent = true;
|
||||
line.material.opacity = 0.4;
|
||||
} else if (sourceMatch || targetMatch) {
|
||||
line.material.transparent = true;
|
||||
line.material.opacity = 0.6;
|
||||
} else {
|
||||
line.material.transparent = true;
|
||||
line.material.opacity = 0.1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function highlightNodeConnections(nodeData) {
|
||||
const connectedNodeIds = new Set();
|
||||
const connectedEdgeIds = new Set();
|
||||
edges.forEach(edge => {
|
||||
if (edge.source === nodeData.id || edge.target === nodeData.id) {
|
||||
connectedNodeIds.add(edge.source);
|
||||
connectedNodeIds.add(edge.target);
|
||||
connectedEdgeIds.add(edge.id);
|
||||
}
|
||||
});
|
||||
nodeMeshes.forEach(mesh => {
|
||||
const meshNodeId = mesh.userData.nodeData.id;
|
||||
if (meshNodeId === nodeData.id) {
|
||||
mesh.material.emissiveIntensity = 1.0;
|
||||
mesh.scale.setScalar(1.5);
|
||||
} else if (connectedNodeIds.has(meshNodeId)) {
|
||||
mesh.material.emissiveIntensity = 0.8;
|
||||
mesh.scale.setScalar(1.3);
|
||||
mesh.material.transparent = false;
|
||||
mesh.material.opacity = 1;
|
||||
} else {
|
||||
mesh.material.transparent = true;
|
||||
mesh.material.opacity = 0.15;
|
||||
mesh.material.emissiveIntensity = 0.2;
|
||||
mesh.scale.setScalar(0.9);
|
||||
}
|
||||
});
|
||||
edgeLines.forEach(line => {
|
||||
if (connectedEdgeIds.has(line.userData.edgeData.id)) {
|
||||
line.material.transparent = true;
|
||||
line.material.opacity = 0.8;
|
||||
} else {
|
||||
line.material.transparent = true;
|
||||
line.material.opacity = 0.1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetHighlight() {
|
||||
nodeMeshes.forEach(mesh => {
|
||||
const nodeData = mesh.userData.nodeData;
|
||||
const nameMatch = nodeData.name.toLowerCase().includes(searchTerm);
|
||||
const typeMatch = activeTypeFilter === '全部' || nodeData.type === activeTypeFilter;
|
||||
mesh.material.emissiveIntensity = 0.5 + Math.min(nodeData.mention_count * 0.05, 0.3);
|
||||
if (nameMatch && typeMatch) {
|
||||
mesh.material.transparent = false;
|
||||
mesh.material.opacity = 1;
|
||||
mesh.scale.setScalar(1);
|
||||
} else {
|
||||
mesh.material.transparent = true;
|
||||
mesh.material.opacity = 0.2;
|
||||
mesh.scale.setScalar(0.8);
|
||||
}
|
||||
});
|
||||
edgeLines.forEach(line => {
|
||||
line.material.transparent = true;
|
||||
line.material.opacity = 0.4;
|
||||
});
|
||||
}
|
||||
|
||||
let containerObserver = null;
|
||||
function initResizeObserver() {
|
||||
const container = document.getElementById('canvas-container');
|
||||
if (!container) return;
|
||||
containerObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const { width, height } = entry.contentRect;
|
||||
if (width > 0 && height > 0) onContainerResize(width, height);
|
||||
}
|
||||
});
|
||||
containerObserver.observe(container);
|
||||
}
|
||||
|
||||
function onContainerResize(width, height) {
|
||||
if (!camera || !renderer) return;
|
||||
const aspect = width / height;
|
||||
camera.aspect = aspect;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(width, height);
|
||||
}
|
||||
|
||||
function animate() {
|
||||
animationId = requestAnimationFrame(animate);
|
||||
highlightPulse = (highlightPulse + 0.02) % (Math.PI * 2);
|
||||
controls.update();
|
||||
if (starField) starField.rotation.y += 0.0001;
|
||||
if (hoveredNode) { const pulse = 1 + Math.sin(highlightPulse * 3) * 0.05; hoveredNode.scale.set(pulse * 1.2, pulse * 1.2, pulse * 1.2); }
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1 @@
|
||||
{"routerMap":[{"ohmurl":"@normalized:N&&&@ohos/phone/src/main/ets/pages/MainPage&","name":"MainPage","pageSourceFile":"/home/program/TrulyMEM-TrueHumanMEM/products/phone/src/main/ets/pages/MainPage.ets","buildFunction":"MainPageBuilder"}]}
|
||||
@ -0,0 +1 @@
|
||||
{"routerMap":[{"name":"MainPage","pageSourceFile":"src/main/ets/pages/MainPage.ets","buildFunction":"MainPageBuilder","moduleNodeDir":"/home/program/TrulyMEM-TrueHumanMEM/products/phone","ohmurl":"@normalized:N&&&@ohos/phone/src/main/ets/pages/MainPage&","bundleName":"com.trulymem.app","moduleName":"phone"}]}
|
||||
@ -0,0 +1 @@
|
||||
{"crossAppSharedConfig":[]}
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user