|
|
@@ -14,9 +14,9 @@
|
|
|
@touchstart="onTouchStart"
|
|
|
@touchmove="onTouchMove"
|
|
|
@touchend="onTouchEnd" />
|
|
|
- <!-- + 添加成员提示(触摸自身节点时显示) -->
|
|
|
+ <!-- ⚙ 维护页提示(触摸自身节点时显示) -->
|
|
|
<view class="add-hint" :class="{ 'add-hint-visible': touchingSelf }" :style="addHintPosition">
|
|
|
- <text class="add-hint-text">+</text>
|
|
|
+ <text class="add-hint-text">⚙</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<!-- 空状态 -->
|
|
|
@@ -94,7 +94,7 @@ export default {
|
|
|
dragging: null, // { nodeIndex, offsetX, offsetY }
|
|
|
dragStartPos: null, // 用于判断是否真的拖拽了
|
|
|
|
|
|
- // 触摸/悬停状态(显示 + 号)
|
|
|
+ // 触摸/悬停状态(显示 ⚙ 提示)
|
|
|
touchingSelf: false,
|
|
|
hoveringSelf: false,
|
|
|
|
|
|
@@ -120,7 +120,7 @@ export default {
|
|
|
return this.themeColors[this.dimensionCode] || '#8D6E63'
|
|
|
},
|
|
|
|
|
|
- // + 提示位置(跟随中心节点偏移)
|
|
|
+ // ⚙ 提示位置(跟随自身节点偏移)
|
|
|
addHintPosition: function() {
|
|
|
var selfNode = this.nodeData.find(function(n) { return n.isSelf })
|
|
|
if (!selfNode) return {}
|
|
|
@@ -164,13 +164,16 @@ export default {
|
|
|
displayName: displayName,
|
|
|
isSelf: isSelf,
|
|
|
memberType: m.memberType || 'child',
|
|
|
+ effectiveRole: m.effectiveRole || m.relativeLabel || '',
|
|
|
+ gender: m.gender || '',
|
|
|
+ relativeLabel: m.relativeLabel || '',
|
|
|
radius: radius,
|
|
|
x: initX,
|
|
|
y: initY,
|
|
|
vx: 0,
|
|
|
vy: 0,
|
|
|
- fx: 0, // 固定 x (拖拽时)
|
|
|
- fy: 0 // 固定 y (拖拽时)
|
|
|
+ fx: 0,
|
|
|
+ fy: 0
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
@@ -461,14 +464,29 @@ simulationTick: function() {
|
|
|
var s = edge.source
|
|
|
var t = edge.target
|
|
|
|
|
|
- // 边颜色
|
|
|
+ // 连线粗细 = 信任度 (1-6px, 信任越高越粗)
|
|
|
var trust = edge.trust || 75
|
|
|
+ ctx.lineWidth = 1 + (trust / 100) * 5
|
|
|
+
|
|
|
+ // 虚线密度 = 沟通频率 (0=稀疏虚线, 100=实线)
|
|
|
var comm = edge.communication || 50
|
|
|
- var hue = 120 * trust / 100
|
|
|
- ctx.strokeStyle = 'hsla(' + hue + ', 75%, 45%, 0.6)'
|
|
|
- ctx.lineWidth = 1 + (comm / 100) * 4
|
|
|
+ if (comm >= 90) {
|
|
|
+ ctx.setLineDash([])
|
|
|
+ } else if (comm >= 70) {
|
|
|
+ ctx.setLineDash([8, 3])
|
|
|
+ } else if (comm >= 50) {
|
|
|
+ ctx.setLineDash([5, 5])
|
|
|
+ } else if (comm >= 30) {
|
|
|
+ ctx.setLineDash([3, 8])
|
|
|
+ } else {
|
|
|
+ ctx.setLineDash([2, 12])
|
|
|
+ }
|
|
|
+
|
|
|
+ // 连线颜色 = 亲密度 (低=冷色蓝紫, 高=暖色橙红)
|
|
|
+ var closeness = edge.closeness || 50
|
|
|
+ var hue = 220 - (closeness / 100) * 200
|
|
|
+ ctx.strokeStyle = 'hsla(' + hue + ', 75%, 50%, 0.65)'
|
|
|
|
|
|
- // 画线
|
|
|
ctx.beginPath()
|
|
|
ctx.moveTo(s.x, s.y)
|
|
|
ctx.lineTo(t.x, t.y)
|
|
|
@@ -476,16 +494,85 @@ simulationTick: function() {
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+ // ===== 身份 → 形状映射 =====
|
|
|
+
|
|
|
+ getNodeShapeType: function(node) {
|
|
|
+ var role = node.effectiveRole || node.relativeLabel || ''
|
|
|
+ var type = node.memberType || ''
|
|
|
+ // 长辈 (爷爷/奶奶/外公/外婆)
|
|
|
+ if (role.indexOf('爷爷') !== -1 || role.indexOf('奶奶') !== -1 ||
|
|
|
+ role.indexOf('外公') !== -1 || role.indexOf('外婆') !== -1 ||
|
|
|
+ role.indexOf('祖父') !== -1 || role.indexOf('祖母') !== -1) {
|
|
|
+ return 'diamond'
|
|
|
+ }
|
|
|
+ // 父亲/男性家长
|
|
|
+ if (role.indexOf('爸爸') !== -1 || role.indexOf('父亲') !== -1 ||
|
|
|
+ role.indexOf('爸') !== -1 || role.indexOf('爹') !== -1) {
|
|
|
+ return 'roundedRect'
|
|
|
+ }
|
|
|
+ // 母亲/女性家长
|
|
|
+ if (role.indexOf('妈妈') !== -1 || role.indexOf('母亲') !== -1 ||
|
|
|
+ role.indexOf('妈') !== -1 || role.indexOf('娘') !== -1) {
|
|
|
+ return 'circle'
|
|
|
+ }
|
|
|
+ // 通用 parent → 根据性别区分
|
|
|
+ if (type === 'parent') {
|
|
|
+ return node.gender === 'male' ? 'roundedRect' : 'circle'
|
|
|
+ }
|
|
|
+ // child → 圆形
|
|
|
+ if (type === 'child') {
|
|
|
+ return 'childCircle'
|
|
|
+ }
|
|
|
+ return 'circle'
|
|
|
+ },
|
|
|
+
|
|
|
+ // 绘制圆角矩形
|
|
|
+ drawRoundedRectNode: function(ctx, x, y, r) {
|
|
|
+ var w = r * 1.8
|
|
|
+ var h = r * 1.8
|
|
|
+ var cr = r * 0.3
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.moveTo(x - w/2 + cr, y - h/2)
|
|
|
+ ctx.lineTo(x + w/2 - cr, y - h/2)
|
|
|
+ ctx.arcTo(x + w/2, y - h/2, x + w/2, y - h/2 + cr, cr)
|
|
|
+ ctx.lineTo(x + w/2, y + h/2 - cr)
|
|
|
+ ctx.arcTo(x + w/2, y + h/2, x + w/2 - cr, y + h/2, cr)
|
|
|
+ ctx.lineTo(x - w/2 + cr, y + h/2)
|
|
|
+ ctx.arcTo(x - w/2, y + h/2, x - w/2, y + h/2 - cr, cr)
|
|
|
+ ctx.lineTo(x - w/2, y - h/2 + cr)
|
|
|
+ ctx.arcTo(x - w/2, y - h/2, x - w/2 + cr, y - h/2, cr)
|
|
|
+ ctx.closePath()
|
|
|
+ },
|
|
|
+
|
|
|
+ // 绘制菱形
|
|
|
+ drawDiamondNode: function(ctx, x, y, r) {
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.moveTo(x, y - r * 1.2)
|
|
|
+ ctx.lineTo(x + r * 1.2, y)
|
|
|
+ ctx.lineTo(x, y + r * 1.2)
|
|
|
+ ctx.lineTo(x - r * 1.2, y)
|
|
|
+ ctx.closePath()
|
|
|
+ },
|
|
|
+
|
|
|
drawNodes: function(ctx) {
|
|
|
var nodes = this.nodeData
|
|
|
for (var i = 0; i < nodes.length; i++) {
|
|
|
var node = nodes[i]
|
|
|
var r = node.radius
|
|
|
+ var shapeType = this.getNodeShapeType(node)
|
|
|
|
|
|
- // 节点背景
|
|
|
- ctx.beginPath()
|
|
|
- ctx.arc(node.x, node.y, r, 0, 2 * Math.PI)
|
|
|
+ // 按身份绘制不同形状
|
|
|
+ if (shapeType === 'roundedRect') {
|
|
|
+ this.drawRoundedRectNode(ctx, node.x, node.y, r)
|
|
|
+ } else if (shapeType === 'diamond') {
|
|
|
+ this.drawDiamondNode(ctx, node.x, node.y, r)
|
|
|
+ } else {
|
|
|
+ // 默认圆形 (母亲/女性家长/孩子)
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.arc(node.x, node.y, r, 0, 2 * Math.PI)
|
|
|
+ }
|
|
|
|
|
|
+ // 填充 + 描边
|
|
|
if (node.isSelf) {
|
|
|
ctx.fillStyle = '#FFFFFF'
|
|
|
ctx.fill()
|
|
|
@@ -497,7 +584,7 @@ simulationTick: function() {
|
|
|
ctx.fill()
|
|
|
}
|
|
|
|
|
|
- // 字母
|
|
|
+ // 第一字符 (昵称首字)
|
|
|
ctx.fillStyle = node.isSelf ? this.themeColor : '#4A5568'
|
|
|
ctx.font = 'bold ' + (r * 0.9) + 'px sans-serif'
|
|
|
ctx.textAlign = 'center'
|
|
|
@@ -526,9 +613,28 @@ simulationTick: function() {
|
|
|
var edge = edges[i]
|
|
|
var trust = edge.trust || 75
|
|
|
var comm = edge.communication || 50
|
|
|
- var hue = 120 * trust / 100
|
|
|
- ctx.setStrokeStyle('hsla(' + hue + ', 75%, 45%, 0.6)')
|
|
|
- ctx.setLineWidth(1 + (comm / 100) * 4)
|
|
|
+ var closeness = edge.closeness || 50
|
|
|
+
|
|
|
+ // 连线粗细 = 信任度
|
|
|
+ ctx.setLineWidth(1 + (trust / 100) * 5)
|
|
|
+
|
|
|
+ // 虚线密度 = 沟通频率
|
|
|
+ if (comm >= 90) {
|
|
|
+ ctx.setLineDash([])
|
|
|
+ } else if (comm >= 70) {
|
|
|
+ ctx.setLineDash([8, 3])
|
|
|
+ } else if (comm >= 50) {
|
|
|
+ ctx.setLineDash([5, 5])
|
|
|
+ } else if (comm >= 30) {
|
|
|
+ ctx.setLineDash([3, 8])
|
|
|
+ } else {
|
|
|
+ ctx.setLineDash([2, 12])
|
|
|
+ }
|
|
|
+
|
|
|
+ // 连线颜色 = 亲密度 (低=冷色蓝紫, 高=暖色橙红)
|
|
|
+ var hue = 220 - (closeness / 100) * 200
|
|
|
+ ctx.setStrokeStyle('hsla(' + hue + ', 75%, 50%, 0.65)')
|
|
|
+
|
|
|
ctx.beginPath()
|
|
|
ctx.moveTo(edge.source.x, edge.source.y)
|
|
|
ctx.lineTo(edge.target.x, edge.target.y)
|
|
|
@@ -539,9 +645,36 @@ simulationTick: function() {
|
|
|
for (var j = 0; j < nodes.length; j++) {
|
|
|
var node = nodes[j]
|
|
|
var r = node.radius
|
|
|
+ var shapeType = this.getNodeShapeType(node)
|
|
|
+
|
|
|
+ // legacy API 的形状绘制
|
|
|
+ if (shapeType === 'roundedRect') {
|
|
|
+ var w = r * 1.8
|
|
|
+ var h = r * 1.8
|
|
|
+ var cr = r * 0.3
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.moveTo(node.x - w/2 + cr, node.y - h/2)
|
|
|
+ ctx.lineTo(node.x + w/2 - cr, node.y - h/2)
|
|
|
+ ctx.arcTo(node.x + w/2, node.y - h/2, node.x + w/2, node.y - h/2 + cr, cr)
|
|
|
+ ctx.lineTo(node.x + w/2, node.y + h/2 - cr)
|
|
|
+ ctx.arcTo(node.x + w/2, node.y + h/2, node.x + w/2 - cr, node.y + h/2, cr)
|
|
|
+ ctx.lineTo(node.x - w/2 + cr, node.y + h/2)
|
|
|
+ ctx.arcTo(node.x - w/2, node.y + h/2, node.x - w/2, node.y + h/2 - cr, cr)
|
|
|
+ ctx.lineTo(node.x - w/2, node.y - h/2 + cr)
|
|
|
+ ctx.arcTo(node.x - w/2, node.y - h/2, node.x - w/2 + cr, node.y - h/2, cr)
|
|
|
+ ctx.closePath()
|
|
|
+ } else if (shapeType === 'diamond') {
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.moveTo(node.x, node.y - r * 1.2)
|
|
|
+ ctx.lineTo(node.x + r * 1.2, node.y)
|
|
|
+ ctx.lineTo(node.x, node.y + r * 1.2)
|
|
|
+ ctx.lineTo(node.x - r * 1.2, node.y)
|
|
|
+ ctx.closePath()
|
|
|
+ } else {
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.arc(node.x, node.y, r, 0, 2 * Math.PI)
|
|
|
+ }
|
|
|
|
|
|
- ctx.beginPath()
|
|
|
- ctx.arc(node.x, node.y, r, 0, 2 * Math.PI)
|
|
|
if (node.isSelf) {
|
|
|
ctx.setFillStyle('#FFFFFF')
|
|
|
ctx.fill()
|
|
|
@@ -648,12 +781,11 @@ simulationTick: function() {
|
|
|
},
|
|
|
|
|
|
onTouchEnd: function(e) {
|
|
|
- // 如果正在触摸自身节点 → 跳转添加成员
|
|
|
+ // 如果正在触摸自身节点 → 跳转成员维护页
|
|
|
if (this.touchingSelf) {
|
|
|
this.touchingSelf = false
|
|
|
if (this.interactive) {
|
|
|
- this.$emit('addMemberTap')
|
|
|
- this.goAddMember()
|
|
|
+ this.goSelfMaintenance()
|
|
|
}
|
|
|
this.dragging = null
|
|
|
this.dragStartPos = null
|
|
|
@@ -685,10 +817,10 @@ simulationTick: function() {
|
|
|
this.dragStartPos = null
|
|
|
},
|
|
|
|
|
|
- // 跳转到添加成员页面
|
|
|
- goAddMember: function() {
|
|
|
+ // 跳转到成员维护页面 (点自己进入维护)
|
|
|
+ goSelfMaintenance: function() {
|
|
|
uni.navigateTo({
|
|
|
- url: '/pages/family/add-member'
|
|
|
+ url: '/pages/profile-extra/family-members'
|
|
|
})
|
|
|
},
|
|
|
|
|
|
@@ -770,7 +902,7 @@ simulationTick: function() {
|
|
|
font-size: 32rpx;
|
|
|
color: #94A3B8;
|
|
|
}
|
|
|
-/* + 添加成员提示 */
|
|
|
+/* ⚙ 维护页提示 */
|
|
|
.add-hint {
|
|
|
position: absolute;
|
|
|
z-index: 5;
|