jiangchengfeiyi-xiaochengxu/uni_modules/p-f-unicom/index.js
2025-04-02 23:45:33 +08:00

370 lines
11 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import _get from 'lodash/get'
import _set from 'lodash/set'
import _isEmpty from 'lodash/isEmpty'
import _isEqual from 'lodash/isEqual'
import _forEach from 'lodash/forEach'
import _isUndefined from 'lodash/isUndefined'
import _flattenDeep from 'lodash/flattenDeep'
import _toString from 'lodash/toString'
import _has from 'lodash/has'
import _includes from 'lodash/includes'
import _keys from 'lodash/keys'
import _isFunction from 'lodash/isFunction'
import _uniq from 'lodash/uniq'
import _flatMap from 'lodash/flatMap'
import _union from 'lodash/union'
/**
* @desc
* Vue 跨组件通信插件
*/
let unicomIdName = ''
let unicomGroupName = ''
// vm容器、分组、事件、命名 唯一、推迟触发的事件
const [vmMap, groupForVm, events, idForVm, sendDefer] = [new Map(), {}, {}, {}, []]
/**
* @desc 触发执行事件
* @param {string} method - 指令的名称
* @param {string} toKey - 目标组件的 unicomId 或者 unicomGroup
* @param {string} aim - 标识 #
* @param {Array} args - 参数
*/
const emitEvent = function (method, toKey, aim, args) {
const evs = _get(events, method, [])
let evLen = 0
const len = evs.length
// 循环已经注册的指令
for (evLen; evLen < len; evLen++) {
// 存储的数据
const { fn, scope } = evs[evLen]
if (_isEqual(aim, '#')) {
// id
if (scope[unicomIdName] !== toKey) {
// 目标不存在
continue
}
} else if (_isEqual(aim, '@')) {
// 分组
const group = _get(vmMap.get(scope), 'group', [])
if (_isEmpty(group) || _isEqual(_includes(group, toKey), false)) {
// 目标不存在
continue
}
}
_isEqual(_isUndefined(scope), false) && fn.apply(scope, args)
}
// 返回被触发的指令
return evLen
}
/**
* @desc 发送容器 或者 获得 目标容器
* @param {string} query - 指令名称 ~instruct1#id1
* @param {...any} args - 可变参数
*/
const unicomQuery = function (query, ...args) {
let [toKey, aim, defer, eventIndex] = ['', '', false, -1]
// query=instruct1#id1
const method = query
.replace(/^([`~])/, function (s0, s1) {
// query=~instruct1#id1
if (_isEqual(s1, '~')) {
defer = true
}
return ''
})
.replace(/([@#])([^@#]*)$/, function (s0, s1, s2) {
// query=instruct1@child-a
// s0=@child-a s1=@ s2=child-a
toKey = s2
aim = s1
return ''
})
// method=instruct1
if (defer) {
sendDefer.push([method, toKey, aim, args, this])
return this
}
if (_isEqual(_isEqual(method, ''), false)) {
args.unshift(this)
eventIndex = emitEvent(method, toKey, aim, args)
}
// 获取目标 vm
switch (aim) {
case '#':
return _get(idForVm, toKey, null)
case '@':
return _get(groupForVm, toKey, [])
}
return eventIndex
}
/**
* @desc 更新分组
* @param {Object} scope - 组件的实例
* @param {string[]} nv - 指令 unicom-group 传入的组数据 (新)
* @param {string[]} [ov] - 指令 unicom-group 传入的组数据 (旧)
*/
const updateName = function (scope, nv, ov) {
// 实例上设置分组
const vmData = vmMap.get(scope) || {}
const group = _get(vmData, 'group', [])
// 删除旧的 vm
if (_isEqual(_isUndefined(ov), false)) {
_uniq(_flattenDeep(_flatMap(ov))).forEach(function (key) {
if (_includes(group, key)) {
const vms = _get(groupForVm, key, [])
_isEqual(_isEmpty(vms), false) && vms.splice(vms.indexOf(scope), 1)
if (_isEmpty(vms)) {
group.splice(group.indexOf(key), 1)
Reflect.deleteProperty(groupForVm, key)
}
}
})
}
// 增加新的
if (_isEqual(_isUndefined(nv), false)) {
_uniq(_flattenDeep(_flatMap(nv))).forEach(function (key) {
const vms = _get(groupForVm, key, [])
if (_isEmpty(vms)) {
_set(groupForVm, key, vms)
}
// 新添加 组件 到组里面
if (_isEqual(_includes(vms, scope), false)) {
vms.push(scope)
}
if (_isEqual(_includes(group, key), false)) {
group.push(key)
}
})
}
}
/**
* @desc 更新 unicomId
* @param {Object} scope - 组件的实例
* @param {string} newValue - 指令 unicom-id 传入的id数据 (新)
* @param {string} [oldValue] - 指令 unicom-id 传入的id数据 (旧)
*/
const updateId = function (scope, newValue, oldValue) {
if (_isEqual(newValue, oldValue)) {
return
}
if (_isEqual(_isUndefined(oldValue), false) && _isEqual(_get(idForVm, oldValue), scope)) {
// watch 监测值修改需要删除,组件销毁时需要删除
Reflect.deleteProperty(idForVm, oldValue)
}
if (_isEqual(_isUndefined(newValue), false) && _isEqual(_has(idForVm, newValue), false)) {
_set(idForVm, newValue, scope)
} else if (_isEqual(_isUndefined(newValue), false) && _has(idForVm, newValue)) {
console.warn(`${unicomIdName}='${newValue}'的组件已经定义并存在。`)
}
}
/**
* @desc 添加事件
* @param {string} method - 指令的名称
* @param {function} fn - 指令名称对应的函数
* @param {Object} scope - 指令名称对应函数所运行的作用域
*/
const appendEvent = function (method, fn, scope) {
if (_isEqual(_has(events, method), false)) {
events[method] = []
}
if (_isFunction(fn)) {
events[method].push({ fn, scope, method })
}
}
/**
* @desc 移除事件
* @param {string} method - 指令的名称
* @param {Object} scope - 指令名称对应函数所运行的作用域
*/
const removeEvent = function (method, scope) {
const evs = _get(events, method, [])
if (_isEqual(_isEmpty(evs), false)) {
for (let i = 0; i < evs.length; i++) {
if (_isEqual(scope, evs[i].scope)) {
evs.splice(i, 1)
}
}
}
}
export default {
install (Vue, { name = 'unicom', idName = `${name}Id`, groupName = `${name}Group` } = {}) {
// 添加原型方法 unicomQuery 函数放在 install 外部不然无法获取调用函数的 this 对象)
Vue.prototype['$' + name] = unicomQuery
// unicom-id
unicomIdName = idName
// 分组 unicom-group
unicomGroupName = groupName
// 全局混入
Vue.mixin({
props: {
// 命名 unicom-id="id1"
[idName]: {
type: String,
default: ''
},
// 分组(纯字符串类数组不能是真实的数组) unicom-group="['child-a', 'child-b']"
[groupName]: {
type: Array,
default: () => []
}
},
watch: {
[idName] (nv, ov) {
updateId(this, nv, ov)
},
[groupName]: {
deep: true,
handler (nv, ov) {
updateName(this, nv, ov);
}
}
},
// 创建的时候加入事件机制
beforeCreate () {
const opt = this.$options
const vmData = {}
const [us, uni, group] = [
_get(opt, name, {}),
(vmData.uni = {}),
(vmData.group = [])
]
if (_isEqual(_isEmpty(us), false)) {
_forEach(us, opt => {
if (_isEmpty(opt)) {
return true
}
_forEach(opt, (handler, key) => {
if (_isEqual(_isUndefined(handler), false)) {
_isUndefined(_get(uni, key)) && _set(uni, key, [])
uni[key].push(handler)
// 添加事件
appendEvent(key, handler, this)
}
})
})
}
// 命名分组
const groupNameOpt = _get(opt, groupName, [])
if (_isEqual(_isEmpty(groupNameOpt), false)) {
_forEach(_uniq(_flattenDeep(_flatMap(groupNameOpt))), item => {
const key = _toString(item)
_isUndefined(_get(groupForVm, key)) && (groupForVm[key] = [])
group.push(key)
groupForVm[key].push(this)
})
}
if (_isEqual(_isEmpty(group), false) || _isEqual(_isEmpty(uni), false)) {
vmMap.set(this, vmData)
}
},
created () {
// 实例命名 唯一
const uId = this[idName]
const uGroupName = this[groupName]
_isEqual(_isEqual(uId, ''), false) && updateId(this, uId)
_isEqual(_isEmpty(uGroupName), false) && updateName(this, uGroupName)
// 实例上设置分组
const vmData = vmMap.get(this) || {}
for (let i = 0; i < sendDefer.length; i++) {
const [method, toKey, aim, args, scope] = sendDefer[i]
let flag = false
if (_isEqual(aim, '#')) {
if (_isEqual(toKey, uId)) {
flag = true
}
} else if (_isEqual(aim, '@')) {
if (
_isEqual(_isUndefined(_get(vmData, 'group')), false) &&
_includes(_get(vmData, 'group'), toKey)
) {
flag = true
}
} else {
if (
_isEqual(method, '') ||
_includes(_keys(_get(vmData, 'uni', {})), method)
) {
flag = true
}
}
if (flag) {
// 延后,并且方法为空
if (_isEqual(method, '')) {
/**
* @desc 监听组件事件
* @example
* this.$unicom('~#id1', function (childScope) {// unicomId=id1的组件创建完成})
*/
if (_isFunction(args[0])) {
args[0](this)
}
} else {
sendDefer.splice(i--, 1)
args.unshift(scope)
emitEvent(method, toKey, aim, args)
}
}
}
},
// 全局混合,销毁实例的时候销毁事件
destroyed () {
// 移除唯一ID
const uId = this[idName]
if (_isEqual(_isEqual(uId, ''), false)) {
updateId(this, undefined, uId)
}
// 移除 命名分组、实例命名
const uGroupName = _get(this, groupName, [])
const optGroupName = _get(this.$options, groupName, [])
if (_isEqual(_isEmpty(uGroupName), false) || _isEqual(_isEmpty(optGroupName), false)) {
updateName(this, undefined, _union(uGroupName, optGroupName))
}
const vmData = vmMap.get(this)
if (_isUndefined(vmData)) {
return
}
vmMap.delete(this)
const uni = _get(vmData, 'uni', {})
// 移除事件
for (const key in uni) {
removeEvent(key, this)
}
// 分组,一对多, 单个vm可以多个分组名称 组件命名
const group = _get(vmData, 'group', [])
_forEach(group, function (name) {
const gs = groupForVm[name]
if (_isEqual(_isUndefined(gs), false)) {
const index = gs.indexOf(this)
if (index > -1) {
gs.splice(index, 1)
}
if (gs.length === 0) {
delete groupForVm[name]
}
}
})
// 监控销毁 method为空
for (let i = 0; i < sendDefer.length;) {
const pms = sendDefer[i]
if (_isEqual(pms[0], '') && _isEqual(pms[4], this)) {
sendDefer.splice(i, 1)
} else {
i += 1
}
}
}
})
// 自定义属性合并策略
// 混入mixins时不是简单的覆盖而是追加
const merge = _get(Vue, 'config.optionMergeStrategies', {})
merge[name] = merge[unicomGroupName] = function (parentVal, childVal) {
const p = parentVal || []
if (_isEqual(_isUndefined(childVal), false)) {
p.push(childVal)
}
return p
}
}
}