|
|
@@ -1,9 +1,9 @@
|
|
|
<template>
|
|
|
- <view class="graph-container">
|
|
|
- <view class="canvas-wrapper">
|
|
|
+ <view class="graph-container r-20">
|
|
|
+ <view class="canvas-wrapper r-20">
|
|
|
<canvas
|
|
|
canvas-id="graphCanvas"
|
|
|
- class="graph-canvas"
|
|
|
+ class="graph-canvas r-20"
|
|
|
@touchstart="onTouchStart"
|
|
|
@touchmove="onTouchMove"
|
|
|
@touchend="onTouchEnd"
|
|
|
@@ -11,62 +11,67 @@
|
|
|
</view>
|
|
|
|
|
|
<!-- 控制面板 -->
|
|
|
- <view class="control-panel">
|
|
|
- <view class="control-group">
|
|
|
- <text class="control-label">布局算法:</text>
|
|
|
- <picker @change="onLayoutChange" :value="layoutIndex" :range="layoutOptions">
|
|
|
- <view class="picker">{{ layoutOptions[layoutIndex] }}</view>
|
|
|
- </picker>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="control-group">
|
|
|
- <text class="control-label">显示标签:</text>
|
|
|
- <switch :checked="showLabels" @change="toggleLabels" />
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="button-group">
|
|
|
- <button class="btn primary" @tap="addRandomNode">添加节点</button>
|
|
|
- <button class="btn secondary" @tap="resetView">重置视图</button>
|
|
|
- <button class="btn warning" @tap="exportData">导出数据</button>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
+<!-- <view class="control-panel">-->
|
|
|
+<!-- <view class="control-group">-->
|
|
|
+<!-- <text class="control-label">布局算法:</text>-->
|
|
|
+<!-- <picker @change="onLayoutChange" :value="layoutIndex" :range="layoutOptions">-->
|
|
|
+<!-- <view class="picker">{{ layoutOptions[layoutIndex] }}</view>-->
|
|
|
+<!-- </picker>-->
|
|
|
+<!-- </view>-->
|
|
|
+
|
|
|
+<!-- <view class="control-group">-->
|
|
|
+<!-- <text class="control-label">显示标签:</text>-->
|
|
|
+<!-- <switch :checked="showLabels" @change="toggleLabels" />-->
|
|
|
+<!-- </view>-->
|
|
|
+
|
|
|
+<!-- <view class="button-group">-->
|
|
|
+<!-- <button class="btn primary" @tap="addRandomNode">添加节点</button>-->
|
|
|
+<!-- <button class="btn secondary" @tap="resetView">重置视图</button>-->
|
|
|
+<!-- <button class="btn warning" @tap="exportData">导出数据</button>-->
|
|
|
+<!-- </view>-->
|
|
|
+<!-- </view>-->
|
|
|
|
|
|
<!-- 节点信息面板 -->
|
|
|
- <view v-if="selectedNode" class="node-info-panel">
|
|
|
- <view class="panel-header">
|
|
|
- <text class="panel-title">节点详情</text>
|
|
|
- <text class="close-btn" @tap="deselectNode">×</text>
|
|
|
- </view>
|
|
|
- <view class="node-details">
|
|
|
- <view class="detail-item">
|
|
|
- <text class="detail-label">名称:</text>
|
|
|
- <text class="detail-value">{{ selectedNode.name }}</text>
|
|
|
- </view>
|
|
|
- <view class="detail-item">
|
|
|
- <text class="detail-label">ID:</text>
|
|
|
- <text class="detail-value">{{ selectedNode.id }}</text>
|
|
|
- </view>
|
|
|
- <view class="detail-item">
|
|
|
- <text class="detail-label">类型:</text>
|
|
|
- <text class="detail-value">{{ selectedNode.type }}</text>
|
|
|
- </view>
|
|
|
- <view class="detail-item">
|
|
|
- <text class="detail-label">连接数:</text>
|
|
|
- <text class="detail-value">{{ getNodeDegree(selectedNode) }}</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
+<!-- <view v-if="selectedNode" class="node-info-panel">-->
|
|
|
+<!-- <view class="panel-header">-->
|
|
|
+<!-- <text class="panel-title">节点详情</text>-->
|
|
|
+<!-- <text class="close-btn" @tap="deselectNode">×</text>-->
|
|
|
+<!-- </view>-->
|
|
|
+<!-- <view class="node-details">-->
|
|
|
+<!-- <view class="detail-item">-->
|
|
|
+<!-- <text class="detail-label">名称:</text>-->
|
|
|
+<!-- <text class="detail-value">{{ selectedNode.name }}</text>-->
|
|
|
+<!-- </view>-->
|
|
|
+<!-- <view class="detail-item">-->
|
|
|
+<!-- <text class="detail-label">ID:</text>-->
|
|
|
+<!-- <text class="detail-value">{{ selectedNode.id }}</text>-->
|
|
|
+<!-- </view>-->
|
|
|
+<!-- <view class="detail-item">-->
|
|
|
+<!-- <text class="detail-label">类型:</text>-->
|
|
|
+<!-- <text class="detail-value">{{ selectedNode.type }}</text>-->
|
|
|
+<!-- </view>-->
|
|
|
+<!-- <view class="detail-item">-->
|
|
|
+<!-- <text class="detail-label">连接数:</text>-->
|
|
|
+<!-- <text class="detail-value">{{ getNodeDegree(selectedNode) }}</text>-->
|
|
|
+<!-- </view>-->
|
|
|
+<!-- </view>-->
|
|
|
+<!-- </view>-->
|
|
|
|
|
|
<!-- 统计信息 -->
|
|
|
<view class="stats-panel">
|
|
|
- <text class="stat-item">节点: {{ graphData.nodes.length }}</text>
|
|
|
- <text class="stat-item">连接: {{ graphData.links.length }}</text>
|
|
|
+ <view class="stats-box"> <view class="stats-icon-g stats-icon"></view> <text class="stat-item">资产</text></view>
|
|
|
+ <view class="stats-box"> <view class="stats-icon-g stats-icon"></view> <text class="stat-item">资产</text></view>
|
|
|
+ <view class="stats-box"> <view class="stats-icon-g stats-icon"></view> <text class="stat-item">资产</text></view>
|
|
|
+ <view class="stats-box"> <view class="stats-icon-g stats-icon"></view> <text class="stat-item">资产</text></view>
|
|
|
+ <view class="stats-box"> <view class="stats-icon-g stats-icon"></view> <text class="stat-item">资产</text></view>
|
|
|
+ <view class="stats-box"> <view class="stats-icon-g stats-icon"></view> <text class="stat-item">资产</text></view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
export default {
|
|
|
+ name:'graph',
|
|
|
data() {
|
|
|
return {
|
|
|
ctx: null,
|
|
|
@@ -79,7 +84,7 @@ export default {
|
|
|
selectedNode: null,
|
|
|
hoveredNode: null,
|
|
|
showLabels: true,
|
|
|
- layoutIndex: 0,
|
|
|
+ layoutIndex: 2,
|
|
|
layoutOptions: ['力导向布局', '环形布局', '树状布局'],
|
|
|
isDragging: false,
|
|
|
dragStartX: 0,
|
|
|
@@ -90,14 +95,15 @@ export default {
|
|
|
animationId: null
|
|
|
}
|
|
|
},
|
|
|
- onReady() {
|
|
|
+
|
|
|
+ onUnload() {
|
|
|
+ this.stopAnimation();
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
this.initCanvas();
|
|
|
this.initGraphData();
|
|
|
this.startAnimation();
|
|
|
},
|
|
|
- onUnload() {
|
|
|
- this.stopAnimation();
|
|
|
- },
|
|
|
methods: {
|
|
|
initCanvas() {
|
|
|
this.ctx = uni.createCanvasContext('graphCanvas', this);
|
|
|
@@ -112,11 +118,11 @@ export default {
|
|
|
// 示例数据
|
|
|
this.graphData = {
|
|
|
nodes: [
|
|
|
- { id: 'node1', name: '中心节点', type: 'center', x: 375, y: 250, size: 20, color: '#ff6b6b' },
|
|
|
+ { id: 'node1', name: '中心节点', type: 'center', x: 275, y: 150, size: 20, color: '#ff6b6b' },
|
|
|
{ id: 'node2', name: '用户节点', type: 'user', x: 275, y: 150, size: 15, color: '#4ecdc4' },
|
|
|
- { id: 'node3', name: '产品节点', type: 'product', x: 475, y: 150, size: 15, color: '#45b7d1' },
|
|
|
- { id: 'node4', name: '分类节点', type: 'category', x: 275, y: 350, size: 15, color: '#96ceb4' },
|
|
|
- { id: 'node5', name: '服务节点', type: 'service', x: 475, y: 350, size: 15, color: '#feca57' }
|
|
|
+ { id: 'node3', name: '产品节点', type: 'product', x: 275, y: 150, size: 15, color: '#45b7d1' },
|
|
|
+ { id: 'node4', name: '分类节点', type: 'category', x: 275, y: 150, size: 15, color: '#96ceb4' },
|
|
|
+ { id: 'node5', name: '服务节点', type: 'service', x: 275, y: 150, size: 15, color: '#feca57' }
|
|
|
],
|
|
|
links: [
|
|
|
{ source: 'node1', target: 'node2' },
|
|
|
@@ -444,8 +450,10 @@ export default {
|
|
|
.graph-container {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- height: 100vh;
|
|
|
+ height: calc(100vh - 225px);
|
|
|
background: #f5f5f5;
|
|
|
+ margin: 0 20rpx ;
|
|
|
+ border-radius: 50rpx;
|
|
|
}
|
|
|
|
|
|
.canvas-wrapper {
|
|
|
@@ -569,13 +577,33 @@ export default {
|
|
|
|
|
|
.stats-panel {
|
|
|
position: absolute;
|
|
|
- bottom: 20rpx;
|
|
|
- left: 20rpx;
|
|
|
+ bottom: 40rpx;
|
|
|
+ left: 30rpx;
|
|
|
background: rgba(0, 0, 0, 0.7);
|
|
|
padding: 20rpx;
|
|
|
border-radius: 8rpx;
|
|
|
z-index: 100;
|
|
|
+ display: flex;
|
|
|
+ justify-content: start;
|
|
|
+ width: 180rpx;
|
|
|
+ flex-wrap: wrap;
|
|
|
+}
|
|
|
+
|
|
|
+.stats-box{
|
|
|
+ width: 90rpx;
|
|
|
+ display: flex;
|
|
|
+ justify-content: start;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+.stats-icon{
|
|
|
+ width: 10rpx;
|
|
|
+ height: 10rpx;
|
|
|
+ margin-right: 8rpx;
|
|
|
}
|
|
|
+.stats-icon-g{
|
|
|
+ background-color:#4ecdc4 ;
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
.stat-item {
|
|
|
display: block;
|