协作增加状态同步
This commit is contained in:
parent
1beb03eaa6
commit
1550f032d9
@ -204,5 +204,10 @@ export const defaultOpt = {
|
|||||||
},
|
},
|
||||||
// 自定义标签的颜色
|
// 自定义标签的颜色
|
||||||
// {pass: 'green, unpass: 'red'}
|
// {pass: 'green, unpass: 'red'}
|
||||||
tagsColorMap: {}
|
tagsColorMap: {},
|
||||||
|
// 节点协作样式配置
|
||||||
|
cooperateStyle: {
|
||||||
|
avatarSize: 22,// 头像大小
|
||||||
|
fontSize: 12,// 如果是文字头像,那么文字的大小
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import nodeExpandBtnMethods from './nodeExpandBtn'
|
|||||||
import nodeCommandWrapsMethods from './nodeCommandWraps'
|
import nodeCommandWrapsMethods from './nodeCommandWraps'
|
||||||
import nodeCreateContentsMethods from './nodeCreateContents'
|
import nodeCreateContentsMethods from './nodeCreateContents'
|
||||||
import nodeExpandBtnPlaceholderRectMethods from './nodeExpandBtnPlaceholderRect'
|
import nodeExpandBtnPlaceholderRectMethods from './nodeExpandBtnPlaceholderRect'
|
||||||
|
import nodeCooperateMethods from './nodeCooperate'
|
||||||
import { CONSTANTS } from '../../../constants/constant'
|
import { CONSTANTS } from '../../../constants/constant'
|
||||||
|
|
||||||
// 节点类
|
// 节点类
|
||||||
@ -55,6 +56,8 @@ class Node {
|
|||||||
this.parent = opt.parent || null
|
this.parent = opt.parent || null
|
||||||
// 子节点
|
// 子节点
|
||||||
this.children = opt.children || []
|
this.children = opt.children || []
|
||||||
|
// 当前同时操作该节点的用户列表
|
||||||
|
this.userList = []
|
||||||
// 节点内容的容器
|
// 节点内容的容器
|
||||||
this.group = null
|
this.group = null
|
||||||
this.shapeNode = null // 节点形状节点
|
this.shapeNode = null // 节点形状节点
|
||||||
@ -74,6 +77,7 @@ class Node {
|
|||||||
this._openExpandNode = null
|
this._openExpandNode = null
|
||||||
this._closeExpandNode = null
|
this._closeExpandNode = null
|
||||||
this._fillExpandNode = null
|
this._fillExpandNode = null
|
||||||
|
this._userListGroup = null
|
||||||
this._lines = []
|
this._lines = []
|
||||||
this._generalizationLine = null
|
this._generalizationLine = null
|
||||||
this._generalizationNode = null
|
this._generalizationNode = null
|
||||||
@ -121,6 +125,10 @@ class Node {
|
|||||||
Object.keys(nodeCreateContentsMethods).forEach(item => {
|
Object.keys(nodeCreateContentsMethods).forEach(item => {
|
||||||
this[item] = nodeCreateContentsMethods[item].bind(this)
|
this[item] = nodeCreateContentsMethods[item].bind(this)
|
||||||
})
|
})
|
||||||
|
// 协同相关
|
||||||
|
Object.keys(nodeCooperateMethods).forEach((item) => {
|
||||||
|
this[item] = nodeCooperateMethods[item].bind(this)
|
||||||
|
})
|
||||||
// 初始化
|
// 初始化
|
||||||
this.getSize()
|
this.getSize()
|
||||||
}
|
}
|
||||||
@ -283,6 +291,7 @@ class Node {
|
|||||||
this.group.add(this.shapeNode)
|
this.group.add(this.shapeNode)
|
||||||
// 渲染一个隐藏的矩形区域,用来触发展开收起按钮的显示
|
// 渲染一个隐藏的矩形区域,用来触发展开收起按钮的显示
|
||||||
this.renderExpandBtnPlaceholderRect()
|
this.renderExpandBtnPlaceholderRect()
|
||||||
|
this.createUserListNode()
|
||||||
// 概要节点添加一个带所属节点id的类名
|
// 概要节点添加一个带所属节点id的类名
|
||||||
if (this.isGeneralization && this.generalizationBelongNode) {
|
if (this.isGeneralization && this.generalizationBelongNode) {
|
||||||
this.group.addClass('generalization_' + this.generalizationBelongNode.uid)
|
this.group.addClass('generalization_' + this.generalizationBelongNode.uid)
|
||||||
@ -527,6 +536,7 @@ class Node {
|
|||||||
}
|
}
|
||||||
// 更新概要
|
// 更新概要
|
||||||
this.renderGeneralization()
|
this.renderGeneralization()
|
||||||
|
this.updateUserListNode()
|
||||||
// 更新节点位置
|
// 更新节点位置
|
||||||
let t = this.group.transform()
|
let t = this.group.transform()
|
||||||
// // 如果上次不在可视区内,且本次也不在,那么直接返回
|
// // 如果上次不在可视区内,且本次也不在,那么直接返回
|
||||||
|
|||||||
104
simple-mind-map/src/core/render/node/nodeCooperate.js
Normal file
104
simple-mind-map/src/core/render/node/nodeCooperate.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import { Circle, G, Text, Image } from '@svgdotjs/svg.js'
|
||||||
|
import { generateColorByContent } from '../../../utils/index'
|
||||||
|
|
||||||
|
// 协同相关功能
|
||||||
|
|
||||||
|
// 创建容器
|
||||||
|
function createUserListNode() {
|
||||||
|
// 如果没有注册协作插件,那么需要创建
|
||||||
|
if (!this.mindMap.cooperate) return
|
||||||
|
this._userListGroup = new G()
|
||||||
|
this.group.add(this._userListGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建文本头像
|
||||||
|
function createTextAvatar(item) {
|
||||||
|
const { avatarSize, fontSize } = this.mindMap.opt.cooperateStyle
|
||||||
|
const g = new G()
|
||||||
|
const str = item.isMore ? item.name : String(item.name)[0]
|
||||||
|
// 圆
|
||||||
|
const circle = new Circle().size(avatarSize, avatarSize)
|
||||||
|
circle.fill({
|
||||||
|
color: item.color || generateColorByContent(str)
|
||||||
|
})
|
||||||
|
// 文本
|
||||||
|
const text = new Text()
|
||||||
|
.text(str)
|
||||||
|
.fill({
|
||||||
|
color: '#fff'
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
'font-size': fontSize
|
||||||
|
})
|
||||||
|
.dx(-fontSize / 2)
|
||||||
|
.dy((avatarSize - fontSize) / 2)
|
||||||
|
g.add(circle).add(text)
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建图片头像
|
||||||
|
function createImageAvatar(item) {
|
||||||
|
const { avatarSize } = this.mindMap.opt.cooperateStyle
|
||||||
|
return new Image().load(item.avatar).size(avatarSize, avatarSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新渲染
|
||||||
|
function updateUserListNode() {
|
||||||
|
if (!this._userListGroup) return
|
||||||
|
const { avatarSize } = this.mindMap.opt.cooperateStyle
|
||||||
|
this._userListGroup.clear()
|
||||||
|
// 根据当前节点长度计算最多能显示几个
|
||||||
|
const length = this.userList.length
|
||||||
|
const maxShowCount = Math.floor(this.width / avatarSize)
|
||||||
|
const list = []
|
||||||
|
if (length > maxShowCount) {
|
||||||
|
// 如果当前用户数量比最多能显示的多,最后需要显示一个提示信息
|
||||||
|
list.push(...this.userList.slice(0, maxShowCount - 1), {
|
||||||
|
isMore: true,
|
||||||
|
name: '+' + (length - maxShowCount + 1)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
list.push(...this.userList)
|
||||||
|
}
|
||||||
|
list.forEach((item, index) => {
|
||||||
|
let node = null
|
||||||
|
if (item.avatar) {
|
||||||
|
node = this.createImageAvatar(item)
|
||||||
|
} else {
|
||||||
|
node = this.createTextAvatar(item)
|
||||||
|
}
|
||||||
|
node.x(index * avatarSize).cy(-avatarSize / 2)
|
||||||
|
this._userListGroup.add(node)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加用户
|
||||||
|
function addUser(userInfo) {
|
||||||
|
if (
|
||||||
|
this.userList.find(item => {
|
||||||
|
return item.id == userInfo.id
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return
|
||||||
|
this.userList.push(userInfo)
|
||||||
|
this.updateUserListNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除用户
|
||||||
|
function removeUser(userInfo) {
|
||||||
|
const index = this.userList.findIndex(item => {
|
||||||
|
return item.id == userInfo.id
|
||||||
|
})
|
||||||
|
if (index === -1) return
|
||||||
|
this.userList.splice(index, 1)
|
||||||
|
this.updateUserListNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createUserListNode,
|
||||||
|
updateUserListNode,
|
||||||
|
createTextAvatar,
|
||||||
|
createImageAvatar,
|
||||||
|
addUser,
|
||||||
|
removeUser
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import * as Y from 'yjs'
|
import * as Y from 'yjs'
|
||||||
import { WebrtcProvider } from 'y-webrtc'
|
import { WebrtcProvider } from 'y-webrtc'
|
||||||
import { isSameObject, simpleDeepClone } from '../utils/index'
|
import { isSameObject, simpleDeepClone, getType, isUndef } from '../utils/index'
|
||||||
|
|
||||||
// 协同插件
|
// 协同插件
|
||||||
class Cooperate {
|
class Cooperate {
|
||||||
@ -12,7 +12,10 @@ class Cooperate {
|
|||||||
this.provider = new WebrtcProvider('demo-room', this.ydoc, {
|
this.provider = new WebrtcProvider('demo-room', this.ydoc, {
|
||||||
signaling: ['ws://10.16.83.11:4444']
|
signaling: ['ws://10.16.83.11:4444']
|
||||||
})
|
})
|
||||||
|
this.awareness = this.provider.awareness
|
||||||
this.currentData = null
|
this.currentData = null
|
||||||
|
this.userInfo = null
|
||||||
|
this.currentAwarenessData = []
|
||||||
|
|
||||||
// 处理数据
|
// 处理数据
|
||||||
if (this.mindMap.opt.data) {
|
if (this.mindMap.opt.data) {
|
||||||
@ -34,6 +37,14 @@ class Cooperate {
|
|||||||
// 监听思维导图改变
|
// 监听思维导图改变
|
||||||
this.onDataChange = this.onDataChange.bind(this)
|
this.onDataChange = this.onDataChange.bind(this)
|
||||||
this.mindMap.on('data_change', this.onDataChange)
|
this.mindMap.on('data_change', this.onDataChange)
|
||||||
|
|
||||||
|
// 监听思维导图节点激活事件
|
||||||
|
this.onNodeActive = this.onNodeActive.bind(this)
|
||||||
|
this.mindMap.on('node_active', this.onNodeActive)
|
||||||
|
|
||||||
|
// 监听状态同步事件
|
||||||
|
this.onAwareness = this.onAwareness.bind(this)
|
||||||
|
this.awareness.on('change', this.onAwareness)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解绑事件
|
// 解绑事件
|
||||||
@ -66,6 +77,72 @@ class Cooperate {
|
|||||||
this.updateChanges(res)
|
this.updateChanges(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 节点激活状态改变后触发状态显示同步
|
||||||
|
onNodeActive(node, nodeList) {
|
||||||
|
if (this.userInfo) {
|
||||||
|
this.awareness.setLocalStateField(this.userInfo.name, {
|
||||||
|
userInfo: {
|
||||||
|
// 用户信息
|
||||||
|
...this.userInfo
|
||||||
|
},
|
||||||
|
nodeIdList: nodeList.map(item => {
|
||||||
|
// 当前激活的节点id列表
|
||||||
|
return item.uid
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置用户信息
|
||||||
|
/**
|
||||||
|
* {
|
||||||
|
* id: '', // 必传,用户唯一的id
|
||||||
|
* name: '', // 用户名称。name和avatar两个只传一个即可,如果都传了,会显示avatar
|
||||||
|
* avatar: '', // 用户头像
|
||||||
|
* color: '' // 如果没有传头像,那么会以一个圆形来显示名称的第一个字,文字的颜色为白色,圆的颜色可以通过该字段设置
|
||||||
|
* }
|
||||||
|
**/
|
||||||
|
setUserInfo(userInfo) {
|
||||||
|
if (
|
||||||
|
getType(userInfo) !== 'Object' ||
|
||||||
|
isUndef(userInfo.id) ||
|
||||||
|
(isUndef(userInfo.name) && isUndef(userInfo.avatar))
|
||||||
|
)
|
||||||
|
return
|
||||||
|
this.userInfo = userInfo || null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听状态同步事件
|
||||||
|
onAwareness() {
|
||||||
|
const walk = (list, callback) => {
|
||||||
|
list.forEach(value => {
|
||||||
|
const userName = Object.keys(value)[0]
|
||||||
|
if (!userName) return
|
||||||
|
const data = value[userName]
|
||||||
|
const userInfo = data.userInfo
|
||||||
|
const nodeIdList = data.nodeIdList
|
||||||
|
nodeIdList.forEach(uid => {
|
||||||
|
const node = this.mindMap.renderer.findNodeByUid(uid)
|
||||||
|
if (node) {
|
||||||
|
callback(node, userInfo)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 清除之前的状态
|
||||||
|
walk(this.currentAwarenessData, (node, userInfo) => {
|
||||||
|
node.removeUser(userInfo)
|
||||||
|
})
|
||||||
|
// 设置当前状态
|
||||||
|
const data = Array.from(this.awareness.getStates().values())
|
||||||
|
this.currentAwarenessData = data
|
||||||
|
walk(data, (node, userInfo) => {
|
||||||
|
// 不显示自己
|
||||||
|
if (userInfo.id === this.userInfo.id) return
|
||||||
|
node.addUser(userInfo)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 将树结构转平级对象
|
// 将树结构转平级对象
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
|
|||||||
@ -389,6 +389,16 @@ export default {
|
|||||||
if (hasFileURL) {
|
if (hasFileURL) {
|
||||||
this.$bus.$emit('handle_file_url')
|
this.$bus.$emit('handle_file_url')
|
||||||
}
|
}
|
||||||
|
if (this.$route.query.userName) {
|
||||||
|
this.mindMap.cooperate.setUserInfo({
|
||||||
|
id: Math.random(),
|
||||||
|
name: this.$route.query.userName,
|
||||||
|
color: ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399'][
|
||||||
|
Math.floor(Math.random() * 5)
|
||||||
|
],
|
||||||
|
avatar: Math.random() > 0.5 ? 'https://img0.baidu.com/it/u=4270674549,2416627993&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1696006800&t=4d32871d14a7224a4591d0c3c7a97311' : ''
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// url中是否存在要打开的文件
|
// url中是否存在要打开的文件
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user