This commit is contained in:
fan
2022-05-26 01:29:57 +08:00
parent cf854bffbe
commit 101da419dd
59 changed files with 0 additions and 36209 deletions
-11
View File
@@ -1,11 +0,0 @@
{
"presets": [
"@vue/app",
[
"@babel/preset-env",
{
"modules": false
}
]
]
}
-3
View File
@@ -1,3 +0,0 @@
> 1%
last 2 versions
not ie <= 8
-14
View File
@@ -1,14 +0,0 @@
module.exports = {
root: true,
env: {
node: true
},
extends: ["plugin:vue/essential", "@vue/prettier"],
rules: {
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
},
parserOptions: {
parser: "babel-eslint"
}
};
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2019 june
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-10
View File
@@ -1,10 +0,0 @@
<meta charset="utf-8">
<title>index demo</title>
<script src="./index.umd.js"></script>
<link rel="stylesheet" href="./index.css">
<script>
console.log(index)
</script>
-9046
View File
File diff suppressed because it is too large Load Diff
-1
View File
File diff suppressed because one or more lines are too long
-9056
View File
File diff suppressed because it is too large Load Diff
-1
View File
File diff suppressed because one or more lines are too long
-1813
View File
File diff suppressed because it is too large Load Diff
-147
View File
@@ -1,147 +0,0 @@
export default [
{
id: 1,
displayName: "像梦一样自由",
avatar: "https://p.qqan.com/up/2020-2/2020022821001845128.jpg",
index: "X",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "你开心吗",
},
{
id: 2,
displayName: "梦醒时分、",
avatar: "https://p.qqan.com/up/2021-1/20211301122243621.jpg",
index: "M",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "",
},
{
id: 3,
displayName: "凌云",
avatar: "https://p.qqan.com/up/2021-1/2021129102387841.jpg",
index: "L",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "",
},
{
id: 4,
displayName: "小郭",
avatar: "https://p.qqan.com/up/2021-1/2021122135507881.jpg",
index: "X",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "",
},
{
id: 5,
displayName: "杨玉泉",
avatar: "https://p.qqan.com/up/2021-1/20211211131598147.jpg",
index: "Y",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "",
},
{
id: 6,
displayName: "森系Style",
avatar: "https://p.qqan.com/up/2021-1/2021113104111220.jpg",
index: "S",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "",
},
{
id: 7,
displayName: "霸王花",
avatar: "https://p.qqan.com/up/2021-1/20211411391666.jpg",
index: "B",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "你怎么还不睡呀?",
},
{
id: 8,
displayName: "曾平",
avatar: "https://p.qqan.com/up/2020-12/202012291044425822.jpg",
index: "Z",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "",
},
{
id: 9,
displayName: "淡然",
avatar: "https://p.qqan.com/up/2020-12/202012141813343503.jpg",
index: "D",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "",
},
{
id: 10,
displayName: "叶子。",
avatar: "https://p.qqan.com/up/2021-1/20211301122243621.jpg",
index: "Y",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "",
},
{
id: 11,
displayName: "土豆",
avatar: "https://p.qqan.com/up/2020-12/202012111157268739.jpg",
index: "T",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "",
},
{
id: 12,
displayName: "清沫",
avatar: "https://p.qqan.com/up/2020-12/202012415467996.jpg",
index: "Q",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "",
},
{
id: 13,
displayName: "Lemon-imui交流群",
avatar: "https://p.qqan.com/up/2020-11/20201127157109035.jpg",
index: "L",
isGroup: true,
unread: 0,
lastSendTime: 1566047865417,
lastContent: "这个咋处理啊?",
},
{
id: 14,
displayName: "系统通知",
avatar: "https://p.qqan.com/up/2020-6/2020061117234279854.jpg",
index: "[1]系統通知",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "宁静致远通过了你的好友请求",
renderContainer() {
return (
<div style="padding:15px;">
<div>宁静致远通过了你的好友请求</div>
<div>宁静致远通过了你的好友请求</div>
<div>宁静致远通过了你的好友请求</div>
</div>
);
},
},
{
id: 15,
displayName: "宁静致远。",
avatar: "https://p.qqan.com/up/2020-6/2020060308522797777.jpg",
index: "N",
unread: 0,
lastSendTime: 1566047865417,
lastContent: "",
},
];
-292
View File
@@ -1,292 +0,0 @@
export default [
{
label: "表情",
children: [
{
name: "1f600",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f600.png",
},
{
name: "1f62c",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f62c.png",
},
{
name: "1f601",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f601.png",
},
{
name: "1f602",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f602.png",
},
{
name: "1f923",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f923.png",
},
{
name: "1f973",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f973.png",
},
{
name: "1f603",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f603.png",
},
{
name: "1f604",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f604.png",
},
{
name: "1f605",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f605.png",
},
{
name: "1f606",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f606.png",
},
{
name: "1f607",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f607.png",
},
{
name: "1f609",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f609.png",
},
{
name: "1f60a",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f60a.png",
},
{
name: "1f642",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f642.png",
},
{
name: "1f643",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f643.png",
},
{
name: "1263a",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/263a.png",
},
{
name: "1f60b",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f60b.png",
},
{
name: "1f60c",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f60c.png",
},
{
name: "1f60d",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f60d.png",
},
{
name: "1f970",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f970.png",
},
{
name: "1f618",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f618.png",
},
{
name: "1f617",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f617.png",
},
{
name: "1f619",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f619.png",
},
{
name: "1f61a",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f61a.png",
},
{
name: "1f61c",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f61c.png",
},
{
name: "1f92a",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f92a.png",
},
{
name: "1f928",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f928.png",
},
{
name: "1f9d0",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f9d0.png",
},
{
name: "1f61d",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f61d.png",
},
{
name: "1f61b",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f61b.png",
},
{
name: "1f911",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f911.png",
},
{
name: "1f913",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f913.png",
},
{
name: "1f60e",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f60e.png",
},
{
name: "1f929",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f929.png",
},
{
name: "1f921",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f921.png",
},
{
name: "1f920",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f920.png",
},
{
name: "1f917",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f917.png",
},
{
name: "1f60f",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f60f.png",
},
{
name: "1f636",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f636.png",
},
{
name: "1f610",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f610.png",
},
{
name: "1f611",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f611.png",
},
{
name: "1f612",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f612.png",
},
{
name: "1f644",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f644.png",
},
{
name: "1f914",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f914.png",
},
{
name: "1f925",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f925.png",
},
{
name: "1f92d",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f92d.png",
},
{
name: "1f92b",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f92b.png",
},
{
name: "1f92c",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f92c.png",
},
{
name: "1f92f",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f92f.png",
},
{
name: "1f633",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f633.png",
},
{
name: "1f61e",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f61e.png",
},
{
name: "1f61f",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f61f.png",
},
{
name: "1f620",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f620.png",
},
{
name: "1f621",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f621.png",
},
],
},
{
label: "收藏",
children: [
{
name: "1f62c",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f62c.png",
},
{
name: "1f621",
title: "微笑",
src: "https://twemoji.maxcdn.com/2/72x72/1f621.png",
},
],
},
];
-156
View File
@@ -1,156 +0,0 @@
import ContactsData from "./contacts";
import UserData from "./user";
const generateRandId = () => {
return Math.random()
.toString(36)
.substr(-8);
};
const getContact = id => {
const data = ContactsData.find(contact => contact.id == id);
return { id: data.id, avatar: data.avatar, displayName: data.displayName };
};
export default {
1: [
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "问你件事",
toContactId: 1,
fromUser: UserData,
},
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "啥子。",
toContactId: 1,
fromUser: getContact(1),
},
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "为什么",
toContactId: 1,
fromUser: UserData,
},
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "你穿了高跟鞋还这么矮",
toContactId: 1,
fromUser: UserData,
},
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "因为我矮啊。[!1f600][!1f600][!1f600]",
toContactId: 1,
fromUser: getContact(1),
},
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "你开心吗",
toContactId: 1,
fromUser: getContact(1),
},
],
2: [],
3: [],
4: [],
5: [],
6: [],
7: [],
8: [],
9: [],
10: [],
11: [],
12: [],
13: [
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "我是测试时候看到的",
toContactId: 1,
fromUser: getContact(4),
},
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "上新版本了,玩玩",
toContactId: 1,
fromUser: getContact(4),
},
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "项目内没有搞这个",
toContactId: 1,
fromUser: getContact(4),
},
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "@awesome 最新的,不然哪有这功能",
toContactId: 1,
fromUser: getContact(5),
},
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "其实是跟你的遮罩层有冲突",
toContactId: 1,
fromUser: getContact(4),
},
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "自己修改index哈",
toContactId: 1,
fromUser: UserData,
},
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "你们升级到最近版了吗?",
toContactId: 1,
fromUser: getContact(6),
},
{
id: generateRandId(),
status: "succeed",
type: "text",
sendTime: 1566047865417,
content: "wo 现在用的142",
toContactId: 1,
fromUser: getContact(7),
},
],
14: [],
15: [],
};
-5
View File
@@ -1,5 +0,0 @@
export default {
id: 1000,
avatar: "https://p.qqan.com/up/2018-4/15244505348390471.jpg",
displayName: "野火。",
};
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

-1
View File
@@ -1 +0,0 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>Lemon IMUI</title><link href=css/index.296428a2.css rel=preload as=style><link href=js/chunk-vendors.7b679a24.js rel=preload as=script><link href=js/index.d839a334.js rel=preload as=script><link href=css/index.296428a2.css rel=stylesheet></head><body><noscript><strong>We're sorry but flat-im doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=js/chunk-vendors.7b679a24.js></script><script src=js/index.d839a334.js></script></body></html>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-30
View File
@@ -1,30 +0,0 @@
<script>
export default {
name: "lemonMessageVoice",
inheritAttrs: false,
inject: ["IMUI"],
render() {
return (
<lemon-message-basic
class="lemon-message-voice"
props={{ ...this.$attrs }}
scopedSlots={{
content: props => {
return <span>{props.content}&nbsp;🔈</span>;
}
}}
/>
);
}
};
</script>
<style lang="stylus">
.lemon-message.lemon-message-voice
user-select none
.lemon-message__content
border 2px solid #000
font-size 12px
cursor pointer
&::before
display none
</style>
-10
View File
@@ -1,10 +0,0 @@
import Vue from "vue";
import App from "./App.vue";
import LemonIMUI from "../packages";
Vue.use(LemonIMUI);
Vue.config.productionTip = false;
new Vue({
render: h => h(App)
}).$mount("#app");
-205
View File
@@ -1,205 +0,0 @@
<script>
import UserData from "../database/user";
import ContactsData from "../database/contacts";
import MessagesData from "../database/messages";
import EmojiData from "../database/emoji";
export default {
name: "QqImui",
components: {},
data() {
return {};
},
render() {
return (
<div class="qq-lemon-imui">
<lemon-imui
class="lemon-slot"
user={UserData}
width={900}
avatar-cricle
hide-message-name
hide-message-time
ref="IMUI"
on={{
"pull-messages": this.handlePullMessages,
"change-contact": this.handleChangeContact,
send: this.handleSend,
}}
scopedSlots={{
"message-title": contact => {
return (
<div>
<div style="display:flex;justify-content:space-between">
<span>{contact.displayName}</span>
<span style="font-size:12px;">
<span>打开抽屉</span>
<span
class="cursor:pointer;"
on-click={() => this.openDrawer("right")}
>
右侧 |{" "}
</span>
<span
class="cursor:pointer;"
on-click={() => this.openDrawer("rightInside")}
>
右侧内部 |{" "}
</span>
<span
class="cursor:pointer;"
on-click={() => this.openDrawer("center")}
>
居中
</span>
</span>
</div>
{contact.isGroup && (
<div class="slot-group-menu">
<span>聊天</span>
<span>公告</span>
<span>相册</span>
<span>文件</span>
<span>活动</span>
<span
v-lemon-contextmenu_click={[
{
text: "操作一",
click(e, instance, hide) {
hide();
},
},
{
text: "操作二",
},
]}
>
设置(左键弹出菜单)
</span>
</div>
)}
</div>
);
},
"sidebar-contact-fixedtop": contact => {
return (
<div class="slot-contact-fixedtop">
<input class="slot-search" placeholder="搜索通讯录" />
</div>
);
},
"message-side": contact => {
if (contact.isGroup) {
return (
<div class="slot-group">
<div class="slot-group-title">群通知</div>
<div class="slot-group-notice">
进群请改备注格式工作地点-姓名请大家配合谢谢
</div>
<div class="slot-group-title">群成员</div>
<div class="slot-group-panel">
<input class="slot-search" placeholder="搜索群成员" />
<div class="slot-group-member">河南-96-十里青山</div>
<div class="slot-group-member">河南-96-十里青山</div>
<div class="slot-group-member">河南-96-十里青山</div>
<div class="slot-group-member">河南-96-十里青山</div>
<div class="slot-group-member">河南-96-十里青山</div>
<div class="slot-group-member">河南-96-十里青山</div>
<div class="slot-group-member">河南-96-十里青山</div>
<div class="slot-group-member">河南-96-十里青山</div>
<div class="slot-group-member">河南-96-十里青山</div>
<div class="slot-group-member">河南-96-十里青山</div>
</div>
</div>
);
}
},
}}
/>
</div>
);
},
computed: {},
watch: {},
created() {},
mounted() {
const IMUI = this.$refs.IMUI;
IMUI.initContacts(ContactsData);
IMUI.initEmoji(EmojiData);
IMUI.changeContact(13);
},
methods: {
openDrawer(position) {
const IMUI = this.$refs.IMUI;
const params = {
position,
render: contact => {
return (
<div style="padding:15px">
<h5>{contact.displayName}</h5>
<span on-click={IMUI.closeDrawer}>关闭抽屉</span>
</div>
);
},
};
if (position == "center") {
params.width = "50%";
params.height = "50%";
} else if (position == "rightInside") {
params.height = "90%";
params.offsetY = "10%";
}
IMUI.openDrawer(params);
},
handlePullMessages(contact, next) {
const { IMUI } = this.$refs;
setTimeout(() => {
next(MessagesData[contact.id], true);
}, 3000);
},
handleChangeContact() {},
handleSend(message, next, file) {
console.log(message, next, file);
setTimeout(() => {
next();
}, 1000);
},
},
};
</script>
<style lang="stylus">
.slot-group
width 170px
border-left 1px solid #ddd;
height 100%
box-sizing border-box
padding 10px
.slot-search
margin 5px 0
.slot-group-notice
color #999
padding 6px 0
font-size 12px
.slot-group-title
font-size 12px
.slot-group-member
font-size 12px
line-height 18px
.slot-group-menu span
display inline-block
cursor pointer
color #888
margin 4px 10px 0 0
border-bottom 2px solid transparent;
&:hover
color #000
border-color #333
.slot-contact-fixedtop
padding 10px
border-bottom 1px solid #ddd
.slot-search
width 100%
box-sizing border-box
font-size 14px
border 1px solid #bbb
padding 5px 10px
</style>
-11747
View File
File diff suppressed because it is too large Load Diff
-43
View File
@@ -1,43 +0,0 @@
{
"name": "lemon-imui",
"version": "1.7.4",
"main": "dist/index.umd.min.js",
"description": "基于 VUE2.0 的 IM 聊天组件",
"homepage": "http://june000.gitee.io/lemon-im/",
"keywords": [
"vue",
"im",
"chat",
"vueim",
"vuechat",
"webim",
"webchat"
],
"repository": {
"type": "git",
"url": "git+https://github.com/fanjyy/lemon-imui.git"
},
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build --target lib --name index packages/index.js",
"build-examples": "vue-cli-service build --dest examples/dist examples/main.js",
"lint": "vue-cli-service lint"
},
"peerDependencies": {
"vue": "^2.6.10"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.6.0",
"@vue/cli-plugin-eslint": "^3.6.0",
"@vue/cli-service": "^3.6.0",
"@vue/eslint-config-prettier": "^4.0.1",
"babel-eslint": "^10.0.2",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.2.3",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.2",
"vue-template-compiler": "^2.5.21"
},
"author": "fanjun",
"license": "MIT"
}
-81
View File
@@ -1,81 +0,0 @@
<script>
export default {
name: "LemonAvatar",
inject: ["IMUI"],
props: {
src: String,
icon: {
type: String,
default: "lemon-icon-people",
},
circle: {
type: Boolean,
default() {
return this.IMUI ? this.IMUI.avatarCricle : false;
},
},
size: {
type: Number,
default: 32,
},
},
data() {
return {
imageFinishLoad: true,
};
},
render() {
return (
<span
style={this.style}
class={["lemon-avatar", { "lemon-avatar--circle": this.circle }]}
on-click={e => this.$emit("click", e)}
>
{(this.imageFinishLoad || !this.src) && <i class={this.icon} />}
<img src={this.src} onLoad={this._handleLoad} />
</span>
);
},
computed: {
style() {
const size = `${this.size}px`;
return {
width: size,
height: size,
lineHeight: size,
fontSize: `${this.size / 2}px`,
};
},
},
methods: {
_handleLoad() {
this.imageFinishLoad = false;
},
},
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
+b(lemon-avatar)
font-variant tabular-nums
line-height 1.5
box-sizing border-box
margin 0
padding 0
list-style none
display inline-block
text-align center
background #ccc
color rgba(255,255,255,0.7)
white-space nowrap
position relative
overflow hidden
vertical-align middle
border-radius 4px
+m(circle)
border-radius 50%
img
width 100%
height 100%
display block
</style>
-74
View File
@@ -1,74 +0,0 @@
<script>
export default {
name: "LemonBadge",
props: {
count: [Number, Boolean],
overflowCount: {
type: Number,
default: 99
}
},
render() {
return (
<span class="lemon-badge">
{this.$slots.default}
{this.count !== 0 && this.count !== undefined && (
<span
class={[
"lemon-badge__label",
this.isDot && "lemon-badge__label--dot"
]}
>
{this.label}
</span>
)}
</span>
);
},
computed: {
isDot() {
return this.count === true;
},
label() {
if (this.isDot) return "";
return this.count > this.overflowCount
? `${this.overflowCount}+`
: this.count;
}
},
methods: {}
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
+b(lemon-badge)
position relative
display inline-block
+e(label)
border-radius 10px
background #f5222d
color #fff
text-align center
font-size 12px
font-weight normal
white-space nowrap
box-shadow 0 0 0 1px #fff
z-index 10
position absolute
transform translateX(50%)
transform-origin 100%
display inline-block
padding 0 4px
height 18px
line-height 17px
min-width 10px
top -4px
right 6px
+m(dot)
width 10px
height 10px
min-width auto
padding 0
top -3px
right 2px
</style>
-70
View File
@@ -1,70 +0,0 @@
<script>
export default {
name: "LemonButton",
props: {
color: {
type: String,
default: "default"
},
disabled: Boolean
},
render() {
return (
<button
class={["lemon-button", `lemon-button--color-${this.color}`]}
disabled={this.disabled}
type="button"
on-click={this._handleClick}
>
{this.$slots.default}
</button>
);
},
methods: {
_handleClick(e) {
this.$emit("click", e);
}
}
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
+b(lemon-button)
outline none
line-height 1.499
display inline-block
font-weight 400
text-align center
touch-action manipulation
cursor pointer
background-image none
border 1px solid #ddd
box-sizing border-box
white-space nowrap
padding 0 15px
font-size 14px
border-radius 4px
height 32px
user-select none
transition all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1)
color rgba(0, 0, 0, 0.65)
background-color #fff
box-shadow 0 2px 0 rgba(0, 0, 0, 0.015)
text-shadow 0 -1px 0 rgba(0, 0, 0, 0.12)
+m(color-default)
&:hover:not([disabled])
border-color #666
color #333
&:active
background-color #ddd
&[disabled]
cursor not-allowed
color #aaa
background #eee
+m(color-grey)
background #e1e1e1
border-color #e1e1e1
color #666
&:hover:not([disabled])
border-color #bbb
</style>
-145
View File
@@ -1,145 +0,0 @@
<script>
import { isString, isToday } from "utils/validate";
import { timeFormat, useScopedSlot } from "utils";
export default {
name: "LemonContact",
components: {},
inject: {
IMUI: {
from: "IMUI",
default() {
return this;
},
},
},
data() {
return {};
},
props: {
contact: Object,
simple: Boolean,
timeFormat: {
type: Function,
default(val) {
return timeFormat(val, isToday(val) ? "h:i" : "y/m/d");
},
},
},
render() {
return (
<div
class={["lemon-contact", { "lemon-contact--name-center": this.simple }]}
title={this.contact.displayName}
on-click={e => this._handleClick(e, this.contact)}
>
{useScopedSlot(
this.$scopedSlots.default,
this._renderInner(),
this.contact,
)}
</div>
);
},
created() {},
mounted() {},
computed: {},
watch: {},
methods: {
_renderInner() {
const { contact } = this;
return [
<lemon-badge
count={!this.simple ? contact.unread : 0}
class="lemon-contact__avatar"
>
<lemon-avatar size={40} src={contact.avatar} />
</lemon-badge>,
<div class="lemon-contact__inner">
<p class="lemon-contact__label">
<span class="lemon-contact__name">{contact.displayName}</span>
{!this.simple && (
<span class="lemon-contact__time">
{this.timeFormat(contact.lastSendTime)}
</span>
)}
</p>
{!this.simple && (
<p class="lemon-contact__content">
{isString(contact.lastContent) ? (
<span domProps={{ innerHTML: contact.lastContent }} />
) : (
contact.lastContent
)}
</p>
)}
</div>,
];
},
_handleClick(e, data) {
this.$emit("click", data);
},
},
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
+b(lemon-contact)
padding 10px 14px
cursor pointer
user-select none
box-sizing border-box
overflow hidden
background #efefef
text-align left
p
margin 0
+m(active)
background #bebdbd
&:hover:not(.lemon-contact--active)
background #e3e3e3
.el-badge__content
border-color #ddd
+e(avatar)
float left
margin-right 10px
img
display block
.ant-badge-count
display inline-block
padding 0 4px
height 18px
line-height 18px
min-width 18px
top -4px
right 7px
+e(label)
display flex
+e(time)
font-size 12px
line-height 18px
padding-left 6px
color #999
white-space nowrap
+e(name)
display block
width 100%
ellipsis()
+e(content)
font-size 12px
color #999
height 18px
line-height 18px
margin-top 1px !important
ellipsis()
img
height 14px
display inline-block
vertical-align middle
margin 0 1px
position relative
top -1px
+m(name-center)
+e(label)
padding-bottom 0
line-height 38px
</style>
-512
View File
@@ -1,512 +0,0 @@
<script>
import { useScopedSlot, messageToHtml, clearHtmlExcludeImg } from "utils";
const command = (command, val) => {
document.execCommand(command, false, val);
};
const selection = window.getSelection();
let range;
let emojiData = [];
let isInitTool = false;
export default {
name: "LemonEditor",
inject: {
IMUI: {
from: "IMUI",
default() {
return this;
},
},
},
components: {},
props: {
tools: {
type: Array,
default: () => [],
},
sendText: {
type: String,
default: "发 送",
},
wrapKey: {
type: Function,
default: function(e) {
return e.keyCode == 13 && e.ctrlKey == false && e.shiftKey == false;
},
},
sendKey: {
type: Function,
default(e) {
return e.keyCode == 13 && e.ctrlKey === true;
},
},
},
data() {
this.clipboardBlob = null;
return {
//剪切板图片URL
clipboardUrl: "",
submitDisabled: true,
//proxyTools: [],
accept: "",
};
},
created() {
this.IMUI.$on("change-contact", () => {
this.closeClipboardImage();
});
},
render() {
const toolLeft = [];
const toolRight = [];
this.proxyTools.forEach(({ name, title, render, click, isRight }) => {
click = click || new Function();
const classes = [
"lemon-editor__tool-item",
{ "lemon-editor__tool-item--right": isRight },
];
let node;
if (name == "emoji") {
node =
emojiData.length == 0 ? (
""
) : (
<lemon-popover class="lemon-editor__emoji">
<template slot="content">{this._renderEmojiTabs()}</template>
<div class={classes} title={title}>
{render()}
</div>
</lemon-popover>
);
} else {
node = (
<div class={classes} on-click={click} title={title}>
{render()}
</div>
);
}
if (isRight) {
toolRight.push(node);
} else {
toolLeft.push(node);
}
});
return (
<div class="lemon-editor">
{this.clipboardUrl && (
<div class="lemon-editor__clipboard-image">
<img src={this.clipboardUrl} />
<div>
<lemon-button
style={{ marginRight: "10px" }}
on-click={this.closeClipboardImage}
color="grey"
>
取消
</lemon-button>
<lemon-button on-click={this.sendClipboardImage}>
发送图片
</lemon-button>
</div>
</div>
)}
<input
style="display:none"
type="file"
multiple="multiple"
ref="fileInput"
accept={this.accept}
onChange={this._handleChangeFile}
/>
<div class="lemon-editor__tool">
<div class="lemon-editor__tool-left">{toolLeft}</div>
<div class="lemon-editor__tool-right">{toolRight}</div>
</div>
<div class="lemon-editor__inner">
<div
class="lemon-editor__input"
ref="textarea"
contenteditable="true"
on-keyup={this._handleKeyup}
on-keydown={this._handleKeydown}
on-paste={this._handlePaste}
on-click={this._handleClick}
spellcheck="false"
/>
</div>
<div class="lemon-editor__footer">
<div class="lemon-editor__tip">
{useScopedSlot(
this.IMUI.$scopedSlots["editor-footer"],
"使用 ctrl + enter 快捷发送消息",
)}
</div>
<div class="lemon-editor__submit">
<lemon-button
disabled={this.submitDisabled}
on-click={this._handleSend}
>
{this.sendText}
</lemon-button>
</div>
</div>
</div>
);
},
computed: {
proxyTools() {
console.log("this.tools", this.tools);
if (!this.tools) return [];
const defaultTools = [
{
name: "emoji",
title: "表情",
click: null,
render: menu => {
return <i class="lemon-icon-emoji" />;
},
},
{
name: "uploadFile",
title: "文件上传",
click: () => this.selectFile("*"),
render: menu => {
return <i class="lemon-icon-folder" />;
},
},
{
name: "uploadImage",
title: "图片上传",
click: () => this.selectFile("image/*"),
render: menu => {
return <i class="lemon-icon-image" />;
},
},
];
let tools = [];
if (Array.isArray(this.tools)) {
const indexMap = {
emoji: 0,
uploadFile: 1,
uploadImage: 2,
};
const indexKeys = Object.keys(indexMap);
tools = this.tools.map(item => {
if (indexKeys.includes(item.name)) {
return {
...defaultTools[indexMap[item.name]],
...item,
};
}
return item;
});
} else {
tools = defaultTools;
}
return tools;
},
},
methods: {
closeClipboardImage() {
this.clipboardUrl = "";
this.clipboardBlob = null;
},
sendClipboardImage() {
if (!this.clipboardBlob) return;
this.$emit("upload", this.clipboardBlob);
this.closeClipboardImage();
},
saveRangeToLast() {
if (!range) {
range = document.createRange();
}
range.selectNodeContents(textarea.value);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
},
inertContent(val, toLast = false) {
if (toLast) saveRangeToLast();
this.focusRange();
command("insertHTML", val);
this.saveRange();
},
saveRange() {
range = selection.getRangeAt(0);
},
focusRange() {
this.$refs.textarea.focus();
if (range) {
selection.removeAllRanges();
selection.addRange(range);
}
},
_handleClick() {
this.saveRange();
},
_renderEmojiTabs() {
const renderImageGrid = items => {
return items.map(item => (
<img
src={item.src}
title={item.title}
class="lemon-editor__emoji-item"
on-click={() => this._handleSelectEmoji(item)}
/>
));
};
if (emojiData[0].label) {
const nodes = emojiData.map((item, index) => {
return (
<div slot="tab-pane" index={index} tab={item.label}>
{renderImageGrid(item.children)}
</div>
);
});
return <lemon-tabs style="width: 412px">{nodes}</lemon-tabs>;
} else {
return (
<div class="lemon-tabs-content" style="width:406px">
{renderImageGrid(emojiData)}
</div>
);
}
},
_handleSelectEmoji(item) {
this.inertContent(
`<img emoji-name="${item.name}" src="${item.src}"></img>`,
);
this._checkSubmitDisabled();
},
async selectFile(accept) {
this.accept = accept;
await this.$nextTick();
this.$refs.fileInput.click();
},
_handlePaste(e) {
e.preventDefault();
const clipboardData = e.clipboardData || window.clipboardData;
const text = clipboardData.getData("Text");
if (text) {
if (window.clipboardData) {
this.$refs.textarea.innerHTML = text;
} else {
command("insertText", text);
}
} else {
const { blob, blobUrl } = this._getClipboardBlob(clipboardData);
this.clipboardBlob = blob;
this.clipboardUrl = blobUrl;
}
},
_getClipboardBlob(clipboard) {
let blob, blobUrl;
for (var i = 0; i < clipboard.items.length; ++i) {
if (
clipboard.items[i].kind == "file" &&
clipboard.items[i].type.indexOf("image/") !== -1
) {
blob = clipboard.items[i].getAsFile();
blobUrl = (window.URL || window.webkitURL).createObjectURL(blob);
}
}
return { blob, blobUrl };
},
_handleKeyup(e) {
this.saveRange();
this._checkSubmitDisabled();
},
_handleKeydown(e) {
const ATing = false;
if (ATing) {
if (e.keyCode == 38 || e.keyCode == 40) {
e.preventDefault();
if (e.keyCode == 38) {
ATSelectedPrev();
}
if (e.keyCode == 40) {
ATSelectedNext();
}
return;
}
if (e.keyCode == 13) {
e.preventDefault();
ATSelected();
return;
}
if (e.keyCode == 37 || e.keyCode == 39) {
ATPopupClose();
}
}
if (e.keyCode == 13 || (e.keyCode == 13 && e.shiftKey)) {
e.preventDefault();
}
if (this.wrapKey(e)) {
e.preventDefault();
command("insertLineBreak");
}
if (this.at && (e.key == "@" || (e.shiftKey && e.keyCode == 229))) {
setTimeout(() => (ATing = true), 300);
}
if (this.submitDisabled == false && this.sendKey(e)) {
this._handleSend();
}
},
getFormatValue() {
// return toEmojiName(
// this.$refs.textarea.innerHTML
// .replace(/<br>|<\/br>/, "")
// .replace(/<div>|<p>/g, "\r\n")
// .replace(/<\/div>|<\/p>/g, "")
// );
return this.IMUI.emojiImageToName(this.$refs.textarea.innerHTML);
},
_checkSubmitDisabled() {
this.submitDisabled = !clearHtmlExcludeImg(
this.$refs.textarea.innerHTML.trim(),
);
},
_handleSend(e) {
const text = this.getFormatValue();
this.$emit("send", text);
this.clear();
this._checkSubmitDisabled();
},
_handleChangeFile(e) {
const { fileInput } = this.$refs;
Array.from(fileInput.files).forEach(file => {
this.$emit("upload", file);
});
fileInput.value = "";
},
clear() {
this.$refs.textarea.innerHTML = "";
},
initEmoji(data) {
emojiData = data;
this.$forceUpdate();
},
setValue(val) {
this.$refs.textarea.innerHTML = this.IMUI.emojiNameToImage(val);
this._checkSubmitDisabled();
},
},
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
gap = 10px;
+b(lemon-editor)
height 200px
position relative
flex-column()
+e(tool)
display flex
height 40px
align-items center
justify-content space-between
padding 0 5px
+e(tool-left){
display flex
}
+e(tool-right){
display flex
}
+e(tool-item)
cursor pointer
padding 4px gap
height 28px
line-height 24px;
color #999
transition all ease .3s
font-size 12px
[class^='lemon-icon-']
line-height 26px
font-size 22px
&:hover
color #333
+m(right){
margin-left:auto;
}
+e(inner)
flex 1
overflow-x hidden
overflow-y auto
scrollbar-light()
+e(clipboard-image)
position absolute
top 0
left 0
width 100%
height 100%
flex-column()
justify-content center
align-items center
background #f4f4f4
z-index 1
img
max-height 66%
max-width 80%
background #e9e9e9
//box-shadow 0 0 20px rgba(0,0,0,0.15)
user-select none
cursor pointer
border-radius 4px
margin-bottom 10px
border 3px dashed #ddd !important
box-sizing border-box
.clipboard-popover-title
font-size 14px
color #333
+e(input)
height 100%
box-sizing border-box
border none
outline none
padding 0 gap
scrollbar-light()
p,div
margin 0
img
height 20px
padding 0 2px
pointer-events none
position relative
top -1px
vertical-align middle
+e(footer)
display flex
height 52px
justify-content flex-end
padding 0 gap
align-items center
+e(tip)
margin-right 10px
font-size 12px
color #999
user-select none
+e(emoji)
user-select none
.lemon-popover
background #f6f6f6
.lemon-popover__content
padding 0
.lemon-popover__arrow
background #f6f6f6
.lemon-tabs-content
box-sizing border-box
padding 8px
height 200px
overflow-x hidden
overflow-y auto
scrollbar-light()
margin-bottom 8px
+e(emoji-item)
cursor pointer
width 22px
padding 4px
border-radius 4px
&:hover
background #e9e9e9
</style>
File diff suppressed because it is too large Load Diff
-240
View File
@@ -1,240 +0,0 @@
<script>
import { useScopedSlot } from "utils";
export default {
name: "lemonMessageBasic",
inject: {
IMUI: {
from: "IMUI",
default() {
return this;
},
},
},
props: {
contextmenu: Array,
message: {
type: Object,
default: () => {
return {};
},
},
timeFormat: {
type: Function,
default: () => "",
},
reverse: Boolean,
hideName: Boolean,
hideTime: Boolean,
},
data() {
return {};
},
render() {
const { fromUser, status, sendTime } = this.message;
const hideTitle = this.hideName == true && this.hideTime == true;
return (
<div
class={[
"lemon-message",
`lemon-message--status-${status}`,
{
"lemon-message--reverse": this.reverse,
"lemon-message--hide-title": hideTitle,
},
]}
>
<div class="lemon-message__avatar">
<lemon-avatar
size={36}
shape="square"
src={fromUser.avatar}
on-click={e => {
this._emitClick(e, "avatar");
}}
/>
</div>
<div class="lemon-message__inner">
<div class="lemon-message__title">
{this.hideName == false && (
<span
on-click={e => {
this._emitClick(e, "displayName");
}}
>
{fromUser.displayName}
</span>
)}
{this.hideTime == false && (
<span
class="lemon-message__time"
on-click={e => {
this._emitClick(e, "sendTime");
}}
>
{this.timeFormat(sendTime)}
</span>
)}
</div>
<div class="lemon-message__content-flex">
<div
v-lemon-contextmenu_message={this.IMUI.contextmenu}
class="lemon-message__content"
on-click={e => {
this._emitClick(e, "content");
}}
>
{useScopedSlot(this.$scopedSlots["content"], null, this.message)}
</div>
<div class="lemon-message__content-after">
{useScopedSlot(
this.IMUI.$scopedSlots["message-after"],
null,
this.message,
)}
</div>
<div
class="lemon-message__status"
on-click={e => {
this._emitClick(e, "status");
}}
>
<i class="lemon-icon-loading lemonani-spin" />
<i
class="lemon-icon-prompt"
title="重发消息"
style={{
color: "#ff2525",
cursor: "pointer",
}}
/>
</div>
</div>
</div>
</div>
);
},
created() {},
mounted() {},
computed: {},
watch: {},
methods: {
_emitClick(e, key) {
this.IMUI.$emit("message-click", e, key, this.message, this.IMUI);
},
},
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
arrow()
content ' '
position absolute
top 6px
width 0
height 0
border 4px solid transparent
+b(lemon-message)
display flex
padding 10px 0
+e(time)
color #b9b9b9
padding 0 5px
+e(inner)
position relative
+e(avatar)
padding-right 10px
user-select none
.lemon-avatar
cursor pointer
+e(title)
display flex
font-size 12px
line-height 16px
height 16px
padding-bottom 4px
user-select none
color #666
+e(content-flex)
display flex
+e(content)
font-size 14px
line-height 20px
padding 8px 10px
background #fff
border-radius 4px
position relative
margin 0
img
video
background #e9e9e9
height 100px
&:before
arrow()
left -4px
border-left none
border-right-color #fff
+e(content-after)
display block
width 48px
height 36px
padding-left 6px
flex none
font-size 12px
color #aaa
overflow hidden
visibility hidden
+e(status)
position absolute
top 23px
right 20px
color #aaa
font-size 20px
.lemon-icon-loading
.lemon-icon-prompt
display none
+m(status-going)
.lemon-icon-loading
display inline-block
+m(status-failed)
.lemon-icon-prompt
display inline-block
+m(status-succeed)
+e(content-after)
visibility visible
+m(reverse)
flex-direction row-reverse
+e(content-flex)
flex-direction row-reverse
+e(content-after)
padding-right 6px
padding-left 0
text-align right
+e(title)
flex-direction row-reverse
+e(status)
left 26px
right auto
+e(content)
background #35d863
&:before
arrow()
left auto
right -4px
border-right none
border-left-color #35d863
+e(title)
text-align right
+e(avatar)
padding-right 0
padding-left 10px
+m(hide-title)
+e(avatar)
padding-top 10px
+e(status)
top 14px
+e(content)
position relative
top -10px
&:before
top 14px
</style>
-38
View File
@@ -1,38 +0,0 @@
<script>
export default {
name: "lemonMessageEvent",
inheritAttrs: false,
inject: ["IMUI"],
render() {
const { content } = this.$attrs.message;
return (
<div class="lemon-message lemon-message-event">
<span
class="lemon-message-event__content"
on-click={e => this._emitClick(e, "content")}
>
{content}
</span>
</div>
);
},
methods: {
_emitClick(e, key) {
this.IMUI.$emit("message-click", e, key, this.$attrs.message, this.IMUI);
}
}
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
+b(lemon-message-event)
+e(content)
user-select none
display inline-block
background #e9e9e9
color #aaa
font-size 12px
margin 0 auto
padding 5px 10px
border-radius 4px
</style>
-59
View File
@@ -1,59 +0,0 @@
<script>
import { formatByte } from "utils";
export default {
name: "lemonMessageFile",
inheritAttrs: false,
render() {
return (
<lemon-message-basic
class="lemon-message-file"
props={{ ...this.$attrs }}
scopedSlots={{
content: props => [
<div class="lemon-message-file__inner">
<p class="lemon-message-file__name">{props.fileName}</p>
<p class="lemon-message-file__byte">
{formatByte(props.fileSize)}
</p>
</div>,
<div class="lemon-message-file__sfx">
<i class="lemon-icon-attah" />
</div>
]
}}
/>
);
}
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
+b(lemon-message-file)
+b(lemon-message)
+e(content)
display flex
cursor pointer
width 200px
background #fff
padding 12px 18px
overflow hidden
p
margin 0
+e(tip)
display none
+e(inner)
flex 1
+e(name)
font-size 14px
+e(byte)
font-size 12px
color #aaa
+e(sfx)
display flex
align-items center
justify-content center
font-weight bold
user-select none
font-size 34px
color #ccc
</style>
-30
View File
@@ -1,30 +0,0 @@
<script>
export default {
name: "lemonMessageImage",
inheritAttrs: false,
render() {
return (
<lemon-message-basic
class="lemon-message-image"
props={{ ...this.$attrs }}
scopedSlots={{
content: props => <img src={props.content} />
}}
/>
);
}
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
+b(lemon-message-image)
+b(lemon-message)
+e(content)
padding 0
cursor pointer
overflow hidden
img
max-width 100%
min-width 100px
display block
</style>
-36
View File
@@ -1,36 +0,0 @@
<script>
export default {
name: "lemonMessageText",
inheritAttrs: false,
inject: ["IMUI"],
render() {
return (
<lemon-message-basic
class="lemon-message-text"
props={{ ...this.$attrs }}
scopedSlots={{
content: props => {
const content = this.IMUI.emojiNameToImage(props.content);
return <span domProps={{ innerHTML: content }} />;
}
}}
/>
);
}
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
+b(lemon-message-text)
+b(lemon-message)
+e(content)
img
width 18px
height 18px
display inline-block
background transparent
position relative
top -1px
padding 0 2px
vertical-align middle
</style>
-187
View File
@@ -1,187 +0,0 @@
<script>
import { hoursTimeFormat } from "utils";
import { isString } from "utils/validate";
import contextmenu from "../directives/contextmenu";
export default {
name: "LemonMessages",
components: {},
props: {
//
hideName: Boolean,
//
hideTime: Boolean,
reverseUserId: [String, Number],
timeRange: {
type: Number,
default: 1,
},
timeFormat: {
type: Function,
default(val) {
return hoursTimeFormat(val);
},
},
loadingText: {
type: [String, Function],
},
loadendText: {
type: [String, Function],
default: "暂无更多消息",
},
messages: {
type: Array,
default: () => [],
},
},
data() {
this._lockScroll = false;
return {
_loading: false,
_loadend: false,
};
},
render() {
return (
<div class="lemon-messages" ref="wrap" on-scroll={this._handleScroll}>
<div
class={[
"lemon-messages__load",
`lemon-messages__load--${this._loadend ? "end" : "ing"}`,
]}
>
<span class="lemon-messages__loadend">
{isString(this.loadendText) ? this.loadendText : this.loadendText()}
</span>
<span class="lemon-messages__loading">
{this.loadingText ? (
isString(this.loadingText) ? (
this.loadingText
) : (
this.loadingText()
)
) : (
<i class="lemon-icon-loading lemonani-spin" />
)}
</span>
</div>
{this.messages.map((message, index) => {
const node = [];
const tagName = `lemon-message-${message.type}`;
const prev = this.messages[index - 1];
if (
prev &&
this.msecRange &&
message.sendTime - prev.sendTime > this.msecRange
) {
node.push(
<lemon-message-event
attrs={{
message: {
id: "__time__",
type: "event",
content: hoursTimeFormat(message.sendTime),
},
}}
/>,
);
}
let attrs;
if (message.type == "event") {
attrs = { message: message };
} else {
attrs = {
timeFormat: this.timeFormat,
message: message,
reverse: this.reverseUserId == message.fromUser.id,
hideTime: this.hideTime,
hideName: this.hideName,
};
}
node.push(<tagName ref="message" refInFor={true} attrs={attrs} />);
return node;
})}
</div>
);
},
computed: {
msecRange() {
return this.timeRange * 1000 * 60;
},
},
watch: {},
methods: {
loaded() {
this._loadend = true;
this.$forceUpdate();
},
resetLoadState() {
this._lockScroll = true;
this._loading = false;
this._loadend = false;
setTimeout(() => {
this._lockScroll = false;
}, 200);
},
async _handleScroll(e) {
if (this._lockScroll) return;
const { target } = e;
contextmenu.hide();
if (
target.scrollTop == 0 &&
this._loading == false &&
this._loadend == false
) {
this._loading = true;
await this.$nextTick();
const hst = target.scrollHeight;
this.$emit("reach-top", async isEnd => {
await this.$nextTick();
target.scrollTop = target.scrollHeight - hst;
this._loading = false;
this._loadend = !!isEnd;
});
}
},
async scrollToBottom() {
await this.$nextTick();
const { wrap } = this.$refs;
if (wrap) {
wrap.scrollTop = wrap.scrollHeight;
}
},
},
created() {},
mounted() {},
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
+b(lemon-messages)
height 400px
overflow-x hidden
overflow-y auto
scrollbar-light()
padding 10px 15px
+e(time)
text-align center
font-size 12px
+e(load)
user-select none
font-size 12px
text-align center
color #999
line-height 30px
.lemon-messages__loading
.lemon-messages__loadend
display none
+m(ing)
.lemon-icon-loading
font-size 22px
.lemon-messages__loading
display block
+m(end)
.lemon-messages__loadend
display block
</style>
-146
View File
@@ -1,146 +0,0 @@
<script>
const popoverCloseQueue = [];
import contextmenu from "../directives/contextmenu";
const triggerEvents = {
hover(el) {},
focus(el) {
el.addEventListener("focus", e => {
this.changeVisible();
});
el.addEventListener("blur", e => {
this.changeVisible();
});
},
click(el) {
el.addEventListener("click", e => {
e.stopPropagation();
contextmenu.hide();
this.changeVisible();
});
},
contextmenu(el) {
el.addEventListener("contextmenu", e => {
e.preventDefault();
this.changeVisible();
});
}
};
export default {
name: "LemonPopover",
props: {
trigger: {
type: String,
default: "click",
validator(val) {
return Object.keys(triggerEvents).includes(val);
}
}
},
data() {
return {
popoverStyle: {},
visible: false
};
},
created() {
document.addEventListener("click", this._documentClickEvent);
popoverCloseQueue.push(this.close);
},
mounted() {
triggerEvents[this.trigger].call(this, this.$slots.default[0].elm);
},
render() {
return (
<span style="position:relative">
<transition name="lemon-slide-top">
{this.visible && (
<div
class="lemon-popover"
ref="popover"
style={this.popoverStyle}
on-click={e => e.stopPropagation()}
>
<div class="lemon-popover__content">{this.$slots.content}</div>
<div class="lemon-popover__arrow" />
</div>
)}
</transition>
{this.$slots.default}
</span>
);
},
destroyed() {
document.removeEventListener("click", this._documentClickEvent);
},
computed: {},
watch: {
async visible(val) {
if (val) {
await this.$nextTick();
const defaultEl = this.$slots.default[0].elm;
const contentEl = this.$refs.popover;
this.popoverStyle = {
top: `-${contentEl.offsetHeight + 10}px`,
left: `${defaultEl.offsetWidth / 2 - contentEl.offsetWidth / 2}px`
};
}
}
},
methods: {
_documentClickEvent(e) {
e.stopPropagation();
if (this.visible) this.close();
},
changeVisible() {
this.visible ? this.close() : this.open();
},
open() {
this.closeAll();
this.visible = true;
},
closeAll() {
popoverCloseQueue.forEach(callback => callback());
},
close() {
this.visible = false;
}
}
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
+b(lemon-popover)
border 1px solid #eee
border-radius 4px
font-size 14px
font-variant tabular-nums
line-height 1.5
color rgba(0, 0, 0, 0.65)
z-index 10
background-color #fff
border-radius 4px
box-shadow 0 2px 8px rgba(0, 0, 0, 0.08)
position absolute
transform-origin 50% 150%
+e(content)
padding 15px
box-sizing border-box
position relative
z-index 1
+e(arrow)
left 50%
transform translateX(-50%) rotate(45deg)
position absolute
z-index 0
bottom -4px
box-shadow 3px 3px 7px rgba(0, 0, 0, 0.07)
width 8px
height 8px
background #fff
.lemon-slide-top-leave-active ,.lemon-slide-top-enter-active
transition all .2s cubic-bezier(0.645, 0.045, 0.355, 1)
.lemon-slide-top-enter, .lemon-slide-top-leave-to
transform translateY(-10px) scale(.8)
opacity 0
</style>
-77
View File
@@ -1,77 +0,0 @@
<script>
export default {
name: "LemonTabs",
props: {
activeIndex: String
},
data() {
return {
active: this.activeIndex
};
},
mounted() {
if (!this.active) {
this.active = this.$slots["tab-pane"][0].data.attrs.index;
}
},
render() {
const pane = [];
const nav = [];
this.$slots["tab-pane"].map(vnode => {
const { tab, index } = vnode.data.attrs;
pane.push(
<div class="lemon-tabs-content__pane" v-show={this.active == index}>
{vnode}
</div>
);
nav.push(
<div
class={[
"lemon-tabs-nav__item",
this.active == index && "lemon-tabs-nav__item--active"
]}
on-click={() => this._handleNavClick(index)}
>
{tab}
</div>
);
});
return (
<div class="lemon-tabs">
<div class="lemon-tabs-content">{pane}</div>
<div class="lemon-tabs-nav">{nav}</div>
</div>
);
},
methods: {
_handleNavClick(index) {
this.active = index;
}
}
};
</script>
<style lang="stylus">
@import '~styles/utils/index'
pane-color = #f6f6f6
+b(lemon-tabs)
background pane-color
+b(lemon-tabs-content)
width 100%
height 100%
padding 15px
+e(pane)
//scrollbar-light()
//overflow-y auto
height 100%
width 100%
+b(lemon-tabs-nav)
display flex
background #eee
+e(item)
line-height 38px
padding 0 15px
cursor pointer
transition all .3s cubic-bezier(0.645, 0.045, 0.355, 1)
+m(active)
background pane-color
</style>
-83
View File
@@ -1,83 +0,0 @@
// import Vue from "vue";
import { isFunction, isEmpty } from "utils/validate";
import LemonPopover from "../components/popover.vue";
let popover;
const hidePopover = () => {
if (popover) popover.style.display = "none";
};
const showPopover = () => {
if (popover) popover.style.display = "block";
};
document.addEventListener("click", e => {
hidePopover();
});
export default {
hide: hidePopover,
bind(el, binding, vnode) {
el.addEventListener(
binding.modifiers.click ? "click" : "contextmenu",
e => {
if (isEmpty(binding.value) || !Array.isArray(binding.value)) return;
if (binding.modifiers.click) e.stopPropagation();
e.preventDefault();
LemonPopover.methods.closeAll();
let component;
let visibleItems = [];
if (binding.modifiers.message) component = vnode.context;
else if (binding.modifiers.contact) component = vnode.child;
if (!popover) {
popover = document.createElement("div");
popover.className = "lemon-contextmenu";
document.body.appendChild(popover);
}
popover.innerHTML = binding.value
.map(item => {
let visible;
if (isFunction(item.visible)) {
visible = item.visible(component);
} else {
visible = item.visible === undefined ? true : item.visible;
}
if (visible) {
visibleItems.push(item);
const icon = item.icon
? `<i class="lemon-contextmenu__icon ${item.icon}"></i>`
: "";
return `<div style="color:${item.color}" title="${
item.text
}" class="lemon-contextmenu__item">${icon}<span>${
item.text
}</span></div>`;
}
return "";
})
.join("");
popover.style.top = `${e.pageY}px`;
popover.style.left = `${e.pageX}px`;
popover.childNodes.forEach((node, index) => {
const { click, render } = visibleItems[index];
node.addEventListener("click", e => {
e.stopPropagation();
if (isFunction(click)) click(e, component, hidePopover);
});
// if (isFunction(render)) {
// const ins = Vue.extend({
// render: h => {
// return render(h, component, hidePopover);
// },
// });
// const renderComponent = new ins().$mount();
// node.querySelector("span").innerHTML =
// renderComponent.$el.outerHTML;
// }
});
showPopover();
},
);
},
};
-49
View File
@@ -1,49 +0,0 @@
import Contextmenu from "./directives/contextmenu";
import LemonTabs from "./components/tabs";
import LemonPopover from "./components/popover";
import LemonButton from "./components/button";
import LemonBadge from "./components/badge";
import LemonAvatar from "./components/avatar";
import LemonContact from "./components/contact";
import LemonEditor from "./components/editor";
import LemonMessages from "./components/messages";
import LemonMessageBasic from "./components/message/basic";
import LemonMessageText from "./components/message/text";
import lemonMessageImage from "./components/message/image";
import lemonMessageFile from "./components/message/file";
import lemonMessageEvent from "./components/message/event";
import LemonIMUI from "./components/index";
import "./styles/common/index.styl";
const version = "1.4.2";
const components = [
LemonIMUI,
LemonContact,
LemonMessages,
LemonEditor,
LemonAvatar,
LemonBadge,
LemonButton,
LemonPopover,
LemonTabs,
LemonMessageBasic,
LemonMessageText,
lemonMessageImage,
lemonMessageFile,
lemonMessageEvent
];
const install = (Vue) => {
Vue.directive("LemonContextmenu", Contextmenu);
components.forEach(component => {
Vue.component(component.name, component);
});
};
if (typeof window !== "undefined" && window.Vue) {
install(window.Vue);
}
export default {
version,
install
};
-15
View File
@@ -1,15 +0,0 @@
import {clearHtml } from 'utils';
export default {
file(message) {
return "[文件]";
},
image(message) {
return "[图片]";
},
text(message) {
return this.emojiNameToImage(clearHtml(message.content));
},
event(message){
return '[通知]';
},
};
-13
View File
@@ -1,13 +0,0 @@
.lemonani-spin
display inline-block
animation lemonani-spin 1s infinite
@keyframes lemonani-spin{
0%{
transform rotate(0deg)
}
100%{
transform rotate(360deg)
}
}
-45
View File
@@ -1,45 +0,0 @@
// @font-face {
// font-family: 'lemon-icons';
// src: url('//at.alicdn.com/t/font_1312162_neqltsj20an.eot');
// src: url('//at.alicdn.com/t/font_1312162_neqltsj20an.eot?#iefix') format('embedded-opentype'),
// url('//at.alicdn.com/t/font_1312162_neqltsj20an.woff2') format('woff2'),
// url('//at.alicdn.com/t/font_1312162_neqltsj20an.woff') format('woff'),
// url('//at.alicdn.com/t/font_1312162_neqltsj20an.ttf') format('truetype'),
// url('//at.alicdn.com/t/font_1312162_neqltsj20an.svg#iconfont') format('svg');
// }
@font-face {
font-family: 'lemon-icons';
src:url('../fonts/icon.woff') format('woff');
}
[class^='lemon-icon-'],
[class*=' lemon-icon-']
font-family lemon-icons !important
speak none
font-style normal
font-weight 400
font-variant normal
text-transform none
line-height 1
vertical-align baseline
display inline-block
.lemon-icon-loading:before
content '\e633'
.lemon-icon-prompt:before
content '\e71b'
.lemon-icon-message:before
content '\e84a'
.lemon-icon-emoji:before
content '\e6f6'
.lemon-icon-attah:before
content '\e7e1'
.lemon-icon-image:before
content '\e7de'
.lemon-icon-folder:before
content '\e7d1'
.lemon-icon-people:before
content '\e715'
.lemon-icon-group:before
content '\e6ff'
.lemon-icon-addressbook:before
content '\e6e2'
-3
View File
@@ -1,3 +0,0 @@
//@import './normalize';
@import './animate';
@import './icons';
-23
View File
@@ -1,23 +0,0 @@
html
-webkit-tap-highlight-color transparent
body
margin 0
a
text-decoration none
a
input
button
textarea
&:focus
outline none
ol
ul
margin 0
padding 0
list-style none
input
button
textarea
font inherit
color inherit
Binary file not shown.
-67
View File
@@ -1,67 +0,0 @@
// -----------------------------------------------------------------------------
// bem-sugar.styl --- Bem mixins for stylus language
//
// Copyright (c) 2017 Ilya Obuhov
//
// Author: Ilya Obuhov <iobuhov.mail@gmail.com>
// URL: https://github.com/iobuhov/stylus-bem-sugar
e-prefix ?= '__'
m-prefix ?= '--'
m-delimiter ?= '_'
group-store = ()
str()
join('', arguments)
b(name)
.{name}
{block}
group()
caller = called-from[0]
level = length(called-from) + 1
elements = group-store[level]
selector = ()
parent = null
{join(',', elements)}
{block}
group-store[level] = null
m(mod, val=null)
val = val && m-delimiter + val
mod = m-prefix + mod
mod = val ? mod + val : mod
caller = called-from[0]
if caller in ('group')
level = length(called-from)
mod = str('&', mod)
if group-store[level] == null
group-store[level] = mod
else
push(group-store[level], mod)
&{mod}
{block}
e(element)
element = e-prefix + element
caller = called-from[0]
gcaller = called-from[1]
if caller in ('group')
level = length(called-from)
if gcaller in ('e' 'm')
element = str('& ^[0]', element)
else
element = str('^[0]', element)
if group-store[level] == null
group-store[level] = element
else
push(group-store[level], element)
else
if caller in ('e' 'm')
& ^[0]{element}
{block}
else
&{element}
{block}
-47
View File
@@ -1,47 +0,0 @@
flex-column()
display flex
flex-direction column
scrollbar-theme($color=#1f252d, $background=#6d6d6d)
&::-webkit-scrollbar
width 5px
height 5px
&::-webkit-scrollbar-track-piece
background-color $background
&::-webkit-scrollbar-thumb:vertical
height 5px
background-color $color
&::-webkit-scrollbar-thumb:horizontal
width 5px
background-color $background
scrollbar-dark()
scrollbar-theme()
scrollbar-light()
scrollbar-theme(#aaa, transparent)
vertical-center()
&::after
display inline-block
content ''
height 100%
vertical-align middle
position-center($type fixed)
position $type
top 50%
left 50%
transform translate(-50%, -50%)
ellipsis()
text-overflow ellipsis
overflow hidden
white-space nowrap
word-break()
word-break break-all
word-wrap break-word
white-space pre-wrap
-5
View File
@@ -1,5 +0,0 @@
@import './functional';
@import './bem';
@import './var';
-26
View File
@@ -1,26 +0,0 @@
//color-primary #2977fa
color-primary = #1bc213
color-light = #fff
/* */
avatar-size = 45px
avatar-radius = 50%
/** */
title-background = color-primary
title-color = color-light
title-height = 44px
/* */
bubble-background = color-primary
bubble-color = color-light
bubble-radius = 12px
bubble-self-background = #e7ebef
bubble-self-color = #606d84
/* */
editor-textarea-height = 40px
editor-textarea-radius = 5px
editor-submit-disable-color = #bcbcbc
editor-submit-disable-background = #ebebeb
editor-submit-radius = editor-textarea-radius
-24
View File
@@ -1,24 +0,0 @@
export default class MemoryCache {
constructor() {
this.table = {};
}
get(key) {
return key ? this.table[key] : this.table;
}
set(key, val) {
this.table[key] = val;
}
// setOnly(key, val) {
// if (!this.has(key)) this.set(key, val);
// }
remove(key) {
if (key) {
delete this.table[key];
} else {
this.table = {};
}
}
has(key) {
return !!this.table[key];
}
}
-16
View File
@@ -1,16 +0,0 @@
export const EMIT_AVATAR_CLICK = "avatar-click";
export const DEFAULT_MENU_LASTMESSAGES = "messages";
export const DEFAULT_MENU_CONTACTS = "contacts";
export const DEFAULT_MENUS = [DEFAULT_MENU_LASTMESSAGES, DEFAULT_MENU_CONTACTS];
/**
* 聊天消息类型
*/
export const MESSAGE_TYPE = ["voice", "file", "video", "image", "text"];
/**
* 聊天消息状态
*/
export const MESSAGE_STATUS = ["going", "succeed", "failed"];
export const CONTACT_TYPE = ["many", "single"];
-134
View File
@@ -1,134 +0,0 @@
import { isPlainObject, isFunction } from "utils/validate";
export function messageToHtml() {}
export function messageToText() {}
/**
* 使用某个组件上的作用域插槽
* @param {VueComponent} inject
* @param {String} slotName
* @param {Node} defaultElement
* @param {Object} props
*/
export function useScopedSlot(slot, def, props) {
return slot ? slot(props) : def;
}
export function padZero(val) {
return val < 10 ? `0${val}` : val;
}
export function hoursTimeFormat(t) {
const date = new Date(t);
const nowDate = new Date();
const Y = t => {
return t.getFullYear();
};
const MD = t => {
return `${t.getMonth() + 1}-${t.getDate()}`;
};
const dateY = Y(date);
const nowDateY = Y(nowDate);
let format;
if (dateY !== nowDateY) {
format = "y年m月d日 h:i";
} else if (`${dateY}-${MD(date)}` === `${nowDateY}-${MD(nowDate)}`) {
format = "h:i";
} else {
format = "m月d日 h:i";
}
return timeFormat(t, format);
}
export function timeFormat(t, format) {
if (!format) format = "y-m-d h:i:s";
if (t) t = new Date(t);
else t = new Date();
const formatArr = [
t.getFullYear().toString(),
padZero((t.getMonth() + 1).toString()),
padZero(t.getDate().toString()),
padZero(t.getHours().toString()),
padZero(t.getMinutes().toString()),
padZero(t.getSeconds().toString()),
];
const reg = "ymdhis";
for (let i = 0; i < formatArr.length; i++) {
format = format.replace(reg.charAt(i), formatArr[i]);
}
return format;
}
export function funCall(event, callback) {
if (isFunction(event)) {
event(() => {
callback();
});
} else {
callback();
}
}
/**
* 获取数组相交的值组成新数组
* @param {Array} a
* @param {Array} b
*/
export function arrayIntersect(a, b) {
return a.filter(x => b.includes(x));
}
//清除字符串内的所有HTML标签
export function clearHtml(str) {
return str.replace(/<.*?>/gi, "");
}
//清除字符串内的所有HTML标签,除了IMG
export function clearHtmlExcludeImg(str) {
return str.replace(/<(?!img).*?>/gi, "");
}
export function error(text) {
throw new Error(text);
}
export function cloneDeep(obj) {
const newobj = { ...obj };
for (const key in newobj) {
const val = newobj[key];
if (isPlainObject(val)) {
newobj[key] = cloneDeep(val);
}
}
return newobj;
}
export function mergeDeep(o1, o2) {
for (const key in o2) {
if (isPlainObject(o1[key])) {
o1[key] = mergeDeep(o1[key], o2[key]);
} else {
o1[key] = o2[key];
}
}
return o1;
}
export function formatByte(value) {
if (null == value || value == "") {
return "0 Bytes";
}
var unitArr = ["B", "K", "M", "G", "T", "P", "E", "Z", "Y"];
var index = 0;
var srcsize = parseFloat(value);
index = Math.floor(Math.log(srcsize) / Math.log(1024));
var size = srcsize / Math.pow(1024, index);
size = parseFloat(size.toFixed(2));
return size + unitArr[index];
}
export function generateUUID() {
var d = new Date().getTime();
if (window.performance && typeof window.performance.now === "function") {
d += performance.now(); //use high-precision timer if available
}
var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(
c,
) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c == "x" ? r : (r & 0x3) | 0x8).toString(16);
});
return uuid;
}
-37
View File
@@ -1,37 +0,0 @@
export function isPlainObject(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
}
export function isString(str) {
return typeof str == "string";
}
export function isToday(time) {
return new Date().getTime() - time < 86400000;
}
export function isEmpty(obj) {
if (!obj) return true;
if (Array.isArray(obj) && obj.length == 0) return true;
if (isPlainObject(obj) && Object.values(obj).length == 0) return true;
return false;
}
export function isUrl(str) {
const reg =
"^((https|http|ftp|rtsp|mms)?://)" +
"?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" + //ftp的user@
"(([0-9]{1,3}.){3}[0-9]{1,3}" + // IP形式的URL- 199.194.52.184
"|" + // 允许IP和DOMAIN(域名)
"([0-9a-z_!~*'()-]+.)*" + // 域名- www.
"([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]." + // 二级域名
"[a-z]{2,6})" + // first level domain- .com or .museum
"(:[0-9]{1,4})?" + // 端口- :80
"((/?)|" + // 如果没有文件名,则不需要斜杠
"(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$";
return new RegExp(reg).test(str) ? true : false;
}
export function isFunction(val) {
return val && typeof val === "function";
}
export function isEng(val) {
return /^[A-Za-z]+$/.test(val);
}
-5
View File
@@ -1,5 +0,0 @@
module.exports = {
plugins: {
autoprefixer: {}
}
};
-11
View File
@@ -1,11 +0,0 @@
module.exports = {
printWidth: 80, // 每行代码长度(默认80
tabWidth: 2, // 每个tab相当于多少个空格(默认2)
useTabs: false, // 是否使用tab进行缩进(默认false)
singleQuote: false, // 使用单引号(默认false
semi: true, // 声明结尾使用分号(默认true)
trailingComma: 'all', // 多行使用拖尾逗号(默认none)
bracketSpacing: true, // 对象字面量的大括号间使用空格(默认true)
jsxBracketSameLine: false, // 多行JSX中的>放置在最后一行的结尾,而不是另起一行(默认false)
arrowParens: 'avoid', // 只有一个参数的箭头函数的参数是否带圆括号(默认avoid)
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

-23
View File
@@ -1,23 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0,maximum-scale=1.0, user-scalable=no"
/>
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title>Lemon IMUI</title>
</head>
<body>
<noscript>
<strong
>We're sorry but flat-im doesn't work properly without JavaScript
enabled. Please enable it to continue.</strong
>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
-25
View File
@@ -1,25 +0,0 @@
const path = require("path");
function resolve(dir) {
return path.join(__dirname, "", dir);
}
module.exports = {
pages: {
index: {
entry: "examples/main.js",
template: "public/index.html",
filename: "index.html"
}
},
publicPath:'',
productionSourceMap:false,
configureWebpack: {
resolve: {
alias: {
components: resolve("packages/components"),
mixins: resolve("packages/mixins"),
styles: resolve("packages/styles"),
utils: resolve("packages/utils")
}
}
}
};