276 lines
6.1 KiB
Vue
276 lines
6.1 KiB
Vue
|
|
<template>
|
|||
|
|
<view>
|
|||
|
|
<TMessageItem
|
|||
|
|
v-for="(item) in messageList"
|
|||
|
|
:key="item.id"
|
|||
|
|
:ref="item.id"
|
|||
|
|
@close-btn-click="handleClose($event, { tagId: item.id })"
|
|||
|
|
@link-click="handleLinkClick($event, { tagId: item.id })"
|
|||
|
|
@duration-end="handleDurationEnd($event, { tagId: item.id })"
|
|||
|
|
>
|
|||
|
|
<template #icon>
|
|||
|
|
<slot
|
|||
|
|
name="icon"
|
|||
|
|
/>
|
|||
|
|
</template>
|
|||
|
|
<template #content>
|
|||
|
|
<slot
|
|||
|
|
name="content"
|
|||
|
|
/>
|
|||
|
|
</template>
|
|||
|
|
<slot />
|
|||
|
|
<template #link>
|
|||
|
|
<slot
|
|||
|
|
name="link"
|
|||
|
|
/>
|
|||
|
|
</template>
|
|||
|
|
<template #close-btn>
|
|||
|
|
<slot
|
|||
|
|
name="close-btn"
|
|||
|
|
/>
|
|||
|
|
</template>
|
|||
|
|
</TMessageItem>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
import TMessageItem from '../message-item/message-item.vue';
|
|||
|
|
import { uniComponent } from '../common/src/index';
|
|||
|
|
import { prefix } from '../common/config';
|
|||
|
|
import { MessageType } from './message.interface';
|
|||
|
|
import props from './props';
|
|||
|
|
import { unitConvert } from '../common/utils';
|
|||
|
|
|
|||
|
|
|
|||
|
|
const SHOW_DURATION = 400;
|
|||
|
|
const name = `${prefix}-message`;
|
|||
|
|
|
|||
|
|
export default uniComponent({
|
|||
|
|
name,
|
|||
|
|
options: {
|
|||
|
|
styleIsolation: 'shared',
|
|||
|
|
},
|
|||
|
|
components: {
|
|||
|
|
TMessageItem,
|
|||
|
|
},
|
|||
|
|
props: {
|
|||
|
|
...props,
|
|||
|
|
},
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
prefix,
|
|||
|
|
classPrefix: name,
|
|||
|
|
messageList: [],
|
|||
|
|
instances: [],
|
|||
|
|
index: 0,
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
watch: {
|
|||
|
|
visible: {
|
|||
|
|
handler(value) {
|
|||
|
|
if (value) {
|
|||
|
|
const data = Object.keys(props).reduce((acc, key) => ({
|
|||
|
|
...acc,
|
|||
|
|
[key]: this[key],
|
|||
|
|
}), {});
|
|||
|
|
|
|||
|
|
this.setMessage(data, this.theme);
|
|||
|
|
} else {
|
|||
|
|
this.messageList = [];
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
immediate: true,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
pageLifetimes: {
|
|||
|
|
show() {
|
|||
|
|
this.hideAll();
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
mounted() {
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
methods: {
|
|||
|
|
/**
|
|||
|
|
* 设置消息信息
|
|||
|
|
* @param msg
|
|||
|
|
* @param theme
|
|||
|
|
*/
|
|||
|
|
setMessage(msg, theme = MessageType.info) {
|
|||
|
|
let id = `${name}_${this.index}`;
|
|||
|
|
if (msg.single) {
|
|||
|
|
// 不能与外层的 ref 相同,否则抖音小程序报错
|
|||
|
|
id = `${name}_inner`;
|
|||
|
|
}
|
|||
|
|
const gap = unitConvert(msg.gap || this.gap);
|
|||
|
|
const msgObj = {
|
|||
|
|
...msg,
|
|||
|
|
theme,
|
|||
|
|
id,
|
|||
|
|
gap,
|
|||
|
|
};
|
|||
|
|
const instanceIndex = this.instances.findIndex(x => x.id === id);
|
|||
|
|
if (instanceIndex < 0) {
|
|||
|
|
this.addMessage(msgObj);
|
|||
|
|
} else {
|
|||
|
|
// 更新消息
|
|||
|
|
const instance = this.instances[instanceIndex];
|
|||
|
|
const offsetHeight = this.getOffsetHeight(instanceIndex);
|
|||
|
|
instance.resetData(() => {
|
|||
|
|
Object.keys(msgObj).forEach((key) => {
|
|||
|
|
instance[key] = msgObj[key];
|
|||
|
|
});
|
|||
|
|
setTimeout(() => {
|
|||
|
|
instance.show.call(instance, offsetHeight);
|
|||
|
|
});
|
|||
|
|
instance.onHide = () => {
|
|||
|
|
this.close(id);
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 新增消息
|
|||
|
|
* @param msgObj
|
|||
|
|
*/
|
|||
|
|
addMessage(msgObj) {
|
|||
|
|
const list = [...this.messageList, { id: msgObj.id }];
|
|||
|
|
this.messageList = list;
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
const offsetHeight = this.getOffsetHeight();
|
|||
|
|
const instance = this.showMessageItem(msgObj, msgObj.id, offsetHeight);
|
|||
|
|
if (this.instances) {
|
|||
|
|
this.instances.push(instance);
|
|||
|
|
this.index += 1;
|
|||
|
|
}
|
|||
|
|
}, 33);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取消息显示top偏移距离
|
|||
|
|
* @param index
|
|||
|
|
* @returns
|
|||
|
|
*/
|
|||
|
|
getOffsetHeight(index = -1) {
|
|||
|
|
let offsetHeight = 0;
|
|||
|
|
let len = index;
|
|||
|
|
if (len === -1 || len > this.instances.length) {
|
|||
|
|
len = this.instances.length;
|
|||
|
|
}
|
|||
|
|
for (let i = 0; i < len; i += 1) {
|
|||
|
|
const instance = this.instances[i];
|
|||
|
|
offsetHeight += instance.height + instance.gap;
|
|||
|
|
}
|
|||
|
|
return offsetHeight;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 新增消息显示
|
|||
|
|
* @param options
|
|||
|
|
* @param id
|
|||
|
|
* @param offsetHeight
|
|||
|
|
* @returns
|
|||
|
|
*/
|
|||
|
|
showMessageItem(options, id, offsetHeight) {
|
|||
|
|
let instance = this.$refs[`${id}`];
|
|||
|
|
if (Array.isArray(instance)) {
|
|||
|
|
instance = instance[0];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (instance) {
|
|||
|
|
instance.resetData(() => {
|
|||
|
|
Object.keys(options).forEach((key) => {
|
|||
|
|
instance[key] = options[key];
|
|||
|
|
});
|
|||
|
|
setTimeout(() => {
|
|||
|
|
instance.show.call(instance, offsetHeight);
|
|||
|
|
});
|
|||
|
|
instance.onHide = () => {
|
|||
|
|
this.close(id);
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return instance;
|
|||
|
|
}
|
|||
|
|
console.error('未找到组件,请确认 selector && context 是否正确');
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
close(id) {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
this.removeMsg(id);
|
|||
|
|
}, SHOW_DURATION);
|
|||
|
|
this.removeInstance(id);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 移除指定消息,id为空则删除全部消息
|
|||
|
|
* @param id
|
|||
|
|
*/
|
|||
|
|
hide(id) {
|
|||
|
|
if (!id) {
|
|||
|
|
this.hideAll();
|
|||
|
|
}
|
|||
|
|
const instance = this.instances.find(x => x.id === id);
|
|||
|
|
if (instance) {
|
|||
|
|
instance.hide();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 移除全部消息
|
|||
|
|
*/
|
|||
|
|
hideAll() {
|
|||
|
|
// 消息移除后也会移除instance,下标不用增加,直至全部删除
|
|||
|
|
for (let i = 0; i < this.instances.length;) {
|
|||
|
|
const instance = this.instances[i];
|
|||
|
|
instance.hide();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 移除message实例
|
|||
|
|
*/
|
|||
|
|
removeInstance(id) {
|
|||
|
|
const index = this.instances.findIndex(x => x.id === id);
|
|||
|
|
if (index < 0) return;
|
|||
|
|
const instance = this.instances[index];
|
|||
|
|
const removedHeight = instance.height;
|
|||
|
|
this.instances.splice(index, 1);
|
|||
|
|
for (let i = index; i < this.instances.length; i += 1) {
|
|||
|
|
const instance = this.instances[i];
|
|||
|
|
instance.wrapTop = instance.wrapTop - removedHeight - instance.gap;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 移除页面元素
|
|||
|
|
* @param id
|
|||
|
|
*/
|
|||
|
|
removeMsg(id) {
|
|||
|
|
this.messageList = this.messageList.filter(item => item.id !== id);
|
|||
|
|
// #ifdef VUE2
|
|||
|
|
this.$set(this, 'messageList', this.messageList);
|
|||
|
|
// #endif
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
handleClose(e) {
|
|||
|
|
this.$emit('close-btn-click', { e });
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
handleLinkClick(e) {
|
|||
|
|
this.$emit('link-click', { e });
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
handleDurationEnd(e) {
|
|||
|
|
this.$emit('duration-end', { e });
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
<style scoped>
|
|||
|
|
</style>
|