This commit is contained in:
fan
2019-05-06 20:27:53 +08:00
parent ad3c67e0d2
commit f4bda4688c
32 changed files with 13533 additions and 1 deletions
+20
View File
@@ -0,0 +1,20 @@
{
"presets": [
"@vue/app",
[
"@babel/preset-env",
{
"modules": false
}
]
],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
+3
View File
@@ -0,0 +1,3 @@
> 1%
last 2 versions
not ie <= 8
+14
View File
@@ -0,0 +1,14 @@
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
@@ -0,0 +1,21 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+23
View File
@@ -0,0 +1,23 @@
//监听聊天信息滚动到顶部
@pullingTop({pageNum,closePullTop})
//点击头像
@avatarClick
//点击信息内容
@messageClick
//点击信息状态
@statusClick
//文本框变化
@inputChange
//聚焦輸入框
inputBlur()
//更新消息
updateMessage(msgData)
//刪除消息
removeMessage(id)
//刪除全部消息
removeAllMessage()
//在底部增加消息
appendMessage([msgData])
//在顶部增加消息
prependMessage([msgData])
+4
View File
@@ -1,4 +1,8 @@
<<<<<<< HEAD
# lemon-im # lemon-im
=======
# flat-im
>>>>>>> INIT
#### Description #### Description
{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} {**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
+42
View File
@@ -1,8 +1,45 @@
<<<<<<< HEAD
# lemon-im # lemon-im
#### 介绍 #### 介绍
{**以下是码云平台说明,您可以替换此简介** {**以下是码云平台说明,您可以替换此简介**
码云是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 码云是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
=======
# flat-im
<<<<<<< HEAD
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Run your tests
```
npm run test
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
=======
#### 介绍
{**以下是码云平台说明,您可以替换此简介**
码云是开源中国推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
>>>>>>> INIT
无论是个人、团队、或是企业,都能够用码云实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} 无论是个人、团队、或是企业,都能够用码云实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
#### 软件架构 #### 软件架构
@@ -36,4 +73,9 @@
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目 3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目 4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目
5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) 5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
<<<<<<< HEAD
6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) 6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
=======
6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
>>>>>>> 81b7591d59fe4d8d71e2902e65eedd796f943ffc
>>>>>>> INIT
+44
View File
@@ -0,0 +1,44 @@
<template>
<div id="app">
<lemon-im v-bind="IMData"
@pull-friends-message="pullFriendsMessage"></lemon-im>
</div>
</template>
<script>
export default {
name: "app",
data () {
this.friendData = {
"id": 1,
"diaplayName": "贤心",
"avatarPath": "a.jpg",
"sign": "这些都是测试数据,实际使用请严格按照该格式返回"
}
return {
IMData: {
friends: [],
groups: [],
}
};
},
mounted: function () {
setInterval(() => {
this.IMData.friends.push({ ...this.friendData })
this.friendData.id++
}, 1000)
},
methods: {
pullFriendsMessage (data, resolve, reject) {
resolve([{ ...this.friendData }])
//resolve([...this.friendData.id])
this.friendData.id++
},
pullGroups () {
}
}
};
</script>
<style lang="sass"></style>
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

+114
View File
@@ -0,0 +1,114 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br />
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener"
>vue-cli documentation</a
>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
target="_blank"
rel="noopener"
>babel</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
target="_blank"
rel="noopener"
>eslint</a
>
</li>
</ul>
<h3>Essential Links</h3>
<ul>
<li>
<a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
</li>
<li>
<a href="https://forum.vuejs.org" target="_blank" rel="noopener"
>Forum</a
>
</li>
<li>
<a href="https://chat.vuejs.org" target="_blank" rel="noopener"
>Community Chat</a
>
</li>
<li>
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener"
>Twitter</a
>
</li>
<li>
<a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
</li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li>
<a href="https://router.vuejs.org" target="_blank" rel="noopener"
>vue-router</a
>
</li>
<li>
<a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
</li>
<li>
<a
href="https://github.com/vuejs/vue-devtools#vue-devtools"
target="_blank"
rel="noopener"
>vue-devtools</a
>
</li>
<li>
<a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener"
>vue-loader</a
>
</li>
<li>
<a
href="https://github.com/vuejs/awesome-vue"
target="_blank"
rel="noopener"
>awesome-vue</a
>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
+10
View File
@@ -0,0 +1,10 @@
import Vue from "vue";
import App from "./App.vue";
import LemonIM from "../packages";
Vue.use(LemonIM);
Vue.config.productionTip = false;
new Vue({
render: h => h(App)
}).$mount("#app");
Submodule
+1
Submodule lemon-im added at ad3c67e0d2
+12540
View File
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
{
"name": "lemon-im",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"element-ui": "^2.8.2",
"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.1",
"babel-plugin-component": "^1.1.1",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"node-sass": "^4.9.0",
"sass-loader": "^7.1.0",
"vue-template-compiler": "^2.5.21"
}
}
@@ -0,0 +1,43 @@
<template>
<div class='lemon-contact-list'>
<div class="lemon-contact-item"
v-for="item in control.friends"
:key="item.id"
@click="control._changeMessageView(item)">
{{item.diaplayName}}
</div>
</div>
</template>
<script>
export default {
name: 'ContactList',
inject: ["control"],
data () {
return {
};
},
computed: {},
watch: {},
methods: {
},
created () {
},
mounted () {
},
}
</script>
<style lang='scss'>
@import '~styles/utils/index';
@include b(contact-item) {
padding: 10px 15px;
cursor: pointer;
&:hover {
background: #000;
}
}
</style>
+30
View File
@@ -0,0 +1,30 @@
<template>
<div class=''>
LemonGroupList
</div>
</template>
<script>
export default {
name: 'GroupList',
components: {},
data () {
return {
};
},
computed: {},
watch: {},
methods: {
},
created () {
},
mounted () {
},
}
</script>
<style lang='scss'>
</style>
+226
View File
@@ -0,0 +1,226 @@
<template>
<el-container class="lemon-container lemon-container--center"
ref="container">
<el-aside class="lemon-sidebar"
width="240px">
<ul class="lemon-tab">
<li v-for="item in tabList"
:key="item.name"
:tab-name="item.name"
:class="['lemon-tab__item', item.name == currentTab && 'lemon-tab__item--active']"
@click="tabChange(item.name)">
<span :class="item.icon"></span>
</li>
</ul>
<div class="lemon-tabview">
<div class="lemon-tabview__item"
v-for="item in tabList"
v-show="item.name == currentTab"
:key="item.name"
:tabview-name="item.name">
<component :is="item.componentName"
@changeMessageView="_changeMessageView"></component>
</div>
</div>
</el-aside>
<el-container class="lemon-main">
<el-header class="lemon-header"
height="48px">
宜宾劲越二手车市场上江北 (500)
</el-header>
<el-main>
<lemon-message-view></lemon-message-view>
</el-main>
<el-main>工具欄</el-main>
</el-container>
</el-container>
</template>
<script>
import LemonContactList from '../contact-list'
import LemonGroupList from '../group-list'
import LemonMessageList from '../message-list'
import LemonMessageView from '../message-view'
const components = {
LemonContactList,
LemonGroupList,
LemonMessageList,
LemonMessageView
}
export default {
name: 'LemonIm',
components,
provide () {
return {
control: this
}
},
props: {
friends: {
type: Array,
default: () => []
},
groups: {
type: Array,
default: () => []
}
},
data () {
this.tabList = [{
name: 'message',
icon: 'el-icon-s-comment',
componentName: 'lemon-message-list',
}, {
name: 'contact',
icon: 'el-icon-user-solid',
componentName: 'lemon-contact-list',
}, {
name: 'group',
icon: 'el-icon-message-solid',
componentName: 'lemon-group-list',
}]
return {
//当前会话对象的ID 根据 currentTab
currentTab: 'contact',
//当前聊天用户ID 根据聊天类型不一样 代表用户ID 群组ID 讨论组ID
currentMessageId: null,
//聊天视图是否加载中
messageViewloading: true,
//群聊天记录 groupId => [message]
groupMessageData: {
},
//好友聊天记录 friendId => [message]
friendMessageData: {
}
};
},
created () {
},
mounted () {
this._initialize()
},
computed: {
//聊天窗口中的数据
messageViewData () {
return this.friendMessageData[this.currentMessageId]
}
},
watch: {},
methods: {
//左侧选项卡切换
tabChange (name) {
this.currentTab = name;
this.findNode(`.lemon-tabview`).scrollTop = '0px'
},
//打开一个群组聊天窗口
openGroupMessageView (group) {
this._changeMessageViewComplete(group.id)
},
//打开普通聊天窗口
openFriendMessageView (friend) {
if (!this.friendMessageData[friend.id]) {
this.$emit('pull-friends-message', friend, (newData) => {
this._changeMessageViewComplete(friend.id)
this.friendMessageData[friend.id] = [...newData]
})
} else {
this._changeMessageViewComplete(friend.id)
}
},
_changeMessageViewComplete (id) {
this.messageViewloading = false
this.currentMessageId = id
},
_changeMessageView (item) {
this.openFriendMessageView(item)
/**
const resolve = (newData) => {
this.messageViewloading = false
this.
}
const reject = () => {
this.messageViewloading = false
}
this.$emit('pullFriendsMessage', item, {
resolve,
reject
})
*/
},
_openMessageView () {
},
findNode (query) {
return this.$refs.container.$el.querySelector(query)
},
_initialize () {
},
//将左侧的滚动条重置到顶部
_tabviewScrollToTop () {
},
}
}
</script>
<style lang='scss'>
@import '~styles/utils/index';
body {
background: #8d9198;
}
@include b(container) {
width: 900px;
height: 700px;
@include m(center) {
@include position-center(fixed);
}
}
@include b(sidebar) {
background: #1f252d;
display: flex;
flex-direction: column;
color: #fff;
overflow: hidden;
.el-tabs--card {
.el-tabs__nav,
.el-tabs__item,
.el-tabs__header {
border: none;
}
}
}
@include b(tab) {
display: flex;
width: 100%;
@include e(item) {
cursor: pointer;
line-height: 56px;
text-align: center;
flex: 1;
transition: all ease-in-out 0.3s;
font-size: 22px;
color: #6d6d6d;
@include m(active) {
color: #11d207;
}
}
}
@include b(tabview) {
flex: 1;
overflow-y: auto;
@include scrollbar-dark();
@include e(item) {
}
}
@include b(main) {
background: #eceef1;
}
@include b(header) {
line-height: 48px;
}
</style>
@@ -0,0 +1,29 @@
<template>
<div class='lemon-message-list'>
</div>
</template>
<script>
export default {
name: 'MessageList',
inject: ["control"],
data () {
return {
};
},
created () {
},
mounted () {
},
computed: {},
watch: {},
methods: {
}
}
</script>
<style lang='scss'>
</style>
@@ -0,0 +1,31 @@
<template>
<div class='lemon-message-view'
v-loading="control.messageViewloading"
element-loading-background="transparent">
{{ control.messageViewData }}
</div>
</template>
<script>
export default {
name: 'MessageView',
components: {},
inject: ["control"],
created () {
},
mounted () {
},
computed: {},
watch: {},
methods: {
}
}
</script>
<style lang='scss'>
@import '~styles/utils/index';
@include b(message-view) {
height: 100%;
}
</style>
+24
View File
@@ -0,0 +1,24 @@
import Vue from "vue";
import {
Button,
Icon,
Row,
Col,
Container,
Header,
Main,
Aside,
Loading
} from "element-ui";
Vue.use(Button);
Vue.use(Icon);
Vue.use(Row);
Vue.use(Col);
Vue.use(Container);
Vue.use(Header);
Vue.use(Main);
Vue.use(Aside);
Vue.use(Loading.directive);
Vue.prototype.$loading = Loading.service;
+19
View File
@@ -0,0 +1,19 @@
import "./styles/common/index.scss";
import "./element-ui";
import Lemon from "./components/lemon";
const version = "0.1";
const components = [Lemon];
const install = (Vue, opts = {}) => {
components.forEach(component => {
Vue.component(component.name, component);
});
};
if (typeof window !== "undefined" && window.Vue) {
install(window.Vue);
}
export default {
version,
install
};
+48
View File
@@ -0,0 +1,48 @@
/**
*
textMessage = {
msgId: "msgid",
status: "send_going",
msgType: "text",
isOutgoing: true,
text: "text",
fromUser: {},
extras: {}// option
}
imageMessage = {
msgId: "msgid",
msgType: "image",
isOutGoing: true,
mediaPath: "image path",
fromUser: {}
extras: {}// option
}
videoMessage = { // video message
msgId: "msgid",
status: "send_failed",
msgType: "video",
isOutGoing: true,
druation: number,
mediaPath: "voice path",
fromUser: {},
extras: {}// option
}
customMessage = { // custom message
msgId: "msgid",
msgType: "custom",
status: "send_failed",
isOutgoing: true,
contentSize: {height: 100, width: 100},
content: "<h1>custom message will render html string</h1>", // content is html format, avoid to use <script>
fromUser: {},
extras: {}// option
}
eventMessage = { // event message
msgId: "msgid",
msgType: "event",
text: "the event text"
}
*/
+1
View File
@@ -0,0 +1 @@
@import './normalize';
+38
View File
@@ -0,0 +1,38 @@
/**
* 基本样式入口
*/
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;
}
+2
View File
@@ -0,0 +1,2 @@
@import './mixins';
@import './var';
+94
View File
@@ -0,0 +1,94 @@
//BEM
$fi-namespace: 'lemon';
@mixin b($block) {
$selector: $fi-namespace + '-' + $block;
.#{$selector} {
@content;
}
}
@mixin e($element) {
$selector: '__' + $element;
&#{$selector} {
@content;
}
}
@mixin m($modifier) {
$selector: '--' + $modifier;
&#{$selector} {
@content;
}
}
@mixin bem($block, $element: false, $modifier: false) {
$selector: '.';
@if $block {
$selector: $fi-namespace + '-' + $block;
}
@if $element {
$selector: $selector + '__' + $element;
}
@if $modifier {
$selector: $selector + '--' + $modifier;
}
& .#{$selector} {
@content;
}
}
@mixin user-select($value) {
-moz-user-select: $value;
-webkit-user-select: $value;
-ms-user-select: $value;
}
@mixin 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;
}
}
@mixin scrollbar-dark() {
@include scrollbar-theme();
}
@mixin scrollbar-light() {
@include scrollbar-theme(#aaa, #fff);
}
@mixin vertical-center {
$selector: &;
@at-root {
#{$selector}::after {
display: inline-block;
content: '';
height: 100%;
vertical-align: middle;
}
}
}
@mixin position-center($type: fixed) {
position: $type;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
@mixin ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@mixin arrow {
}
+30
View File
@@ -0,0 +1,30 @@
//$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;
/*
$bubble-self-background: #e7ebef;
$bubble-self-color: #3a465d;
*/
+3
View File
@@ -0,0 +1,3 @@
export default function(a, b) {
return a.filter(x => b.includes(x));
}
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+23
View File
@@ -0,0 +1,23 @@
<!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>在线客服</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>
+23
View File
@@ -0,0 +1,23 @@
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"
}
},
configureWebpack: {
resolve: {
alias: {
components: resolve("packages/components"),
mixins: resolve("packages/mixins"),
styles: resolve("packages/styles"),
utils: resolve("packages/utils")
}
}
}
};