聊天页面

This commit is contained in:
zyronon 2021-07-25 03:05:39 +08:00
parent c81f15f4c9
commit e2ed29ce66
12 changed files with 1303 additions and 158 deletions

15
package-lock.json generated
View File

@ -12338,6 +12338,21 @@
}
}
},
"vue-switches": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/vue-switches/-/vue-switches-2.0.1.tgz",
"integrity": "sha512-rDqBtK3TKy1pEvyZeWmnSHVeXqAcn+ozch7LiNThBzr1QMjg5rhvqBY7uWeli/baDDslf6CXmBJbHPwASJLqoA==",
"requires": {
"vue": "^2.2.6"
},
"dependencies": {
"vue": {
"version": "2.6.14",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz",
"integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ=="
}
}
},
"vue-template-es2015-compiler": {
"version": "1.9.1",
"resolved": "https://registry.npm.taobao.org/vue-template-es2015-compiler/download/vue-template-es2015-compiler-1.9.1.tgz",

View File

@ -15,6 +15,7 @@
"swiper": "^6.7.5",
"vue": "^3.0.0",
"vue-router": "^4.0.8",
"vue-switches": "^2.0.1",
"vuex": "^4.0.1"
},
"devDependencies": {

View File

@ -59,6 +59,7 @@ export default {
'/scan',
'/face-to-face',
'/chat',
'/chat-detail',
'',
];
const toDepth = routeDeep.indexOf(to.path)

View File

@ -2,7 +2,7 @@
<div class="search-ctn">
<div class="search">
<img class="search-icon" src="../assets/img/icon/pause.svg" alt="">
<input autofocus type="text" :placeholder="placeholder" v-model="value">
<input type="text" :placeholder="placeholder" v-model="value">
<div class="suffix">
<slot v-if="$slots.default"></slot>
<img v-if="value.length && (!$slots.default)" src="../assets/img/icon/close.svg" @click="clear">

View File

@ -9,36 +9,40 @@
<div class="right">
<img src="../../assets/img/icon/back.png" alt="">
<img src="../../assets/img/icon/back.png" alt="">
<img src="../../assets/img/icon/back.png" alt="">
<img src="../../assets/img/icon/back.png" alt="" @click="$nav('/chat-detail')">
</div>
</div>
<div class="message-wrapper">
<div class="message" v-for="item in messages">
<div class="time"
v-if="item.type === MESSAGE_TYPE.TIME">
{{ item.time }}
</div>
<div class="chat-text"
:class="item.createBy !== myId ? 'left' : 'right'"
v-if="item.type === MESSAGE_TYPE.TEXT">
<img v-if="item.createBy !== myId" src="../../assets/img/icon/head-image.jpeg" alt="" class="avatar">
<div class="content">{{ item.data }}</div>
<img v-if="item.createBy === myId" src="../../assets/img/icon/head-image.jpeg" alt="" class="avatar">
</div>
<div class="douyin_video"
:class="item.createBy !== myId ? 'left' : 'right'"
v-if="item.type === MESSAGE_TYPE.DOUYIN_VIDEO">
<img v-if="item.createBy !== myId" src="../../assets/img/icon/head-image.jpeg" alt="" class="avatar">
<div class="content">{{ item.data }}</div>
<img v-if="item.createBy === myId" src="../../assets/img/icon/head-image.jpeg" alt="" class="avatar">
<div class="message-wrapper" :class="isExpand ? 'expand' : ''">
<ChatMessage :message="item" v-for="item in messages"></ChatMessage>
</div>
<div class="footer" :class="isTyping ? 'typing' : ''">
<div class="toolbar" v-if="!recording">
<img src="../../assets/img/icon/head-image.jpeg" alt="" class="camera">
<input @click="typing = true"
@blur="typing = false"
type="text" placeholder="发送信息...">
<img @click="recording = true;showOption = false" src="../../assets/img/icon/head-image.jpeg" alt="">
<img src="../../assets/img/icon/head-image.jpeg" alt="">
<img @click="showOption = !showOption" src="../../assets/img/icon/head-image.jpeg" alt="">
</div>
<div class="record" v-else>
<span>按住 说话</span>
<img @click="recording = false" src="../../assets/img/icon/head-image.jpeg" alt="">
</div>
<div class="options" v-if="showOption">
<div class="option-wrapper">
<div class="option" v-for="i in 7">
<img src="../../assets/img/icon/举报.svg" alt="">
<span>照片</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import ChatMessage from "./components/ChatMessage";
let CALL_STATE = {
REJECT: 0,
RESOLVE: 1,
@ -61,7 +65,7 @@ let MESSAGE_TYPE = {
TEXT: 0,
TIME: 1,
VIDEO: 2,
DOUYIN_VIDEO: 2,
DOUYIN_VIDEO: 9,
AUDIO: 3,
IMAGE: 6,
VIDEO_CALL: 4,
@ -69,153 +73,283 @@ let MESSAGE_TYPE = {
MEME: 7,//
RED_PACKET: 8,//
}
let RED_PACKET_MODE = {
SINGLE: 1,
MULTIPLE: 2
}
export default {
name: "Chat",
components: {
ChatMessage
},
data() {
return {
videoCall: [],
MESSAGE_TYPE,
myId: 1,
messages: [
{
type: MESSAGE_TYPE.TIME,
data: '',
time: '2021-01-02 21:21'
time: '2021-01-02 21:21',
user: {
id: 2,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.RED_PACKET,
state: AUDIO_STATE.NORMAL,
mode: RED_PACKET_MODE.MULTIPLE,
data: {
money: 5.11,
title: '大吉大利',
state: '未领取'
},
time: '2021-01-02 21:21',
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.RED_PACKET,
state: AUDIO_STATE.NORMAL,
mode: RED_PACKET_MODE.SINGLE,
data: {
money: 5.11,
title: '大吉大利',
state: '已过期'
},
time: '2021-01-02 21:21',
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.MEME,
state: AUDIO_STATE.NORMAL,
data: require('../../assets/img/poster/1.jpg'),
time: '2021-01-02 21:21',
user: {
id: 2,
avatar: '../../assets/img/icon/head-image.jpg'
},
loved: [
{
id: 2,
avatar: '../../assets/img/icon/head-image.jpg'
},
{
id: 2,
avatar: '../../assets/img/icon/head-image.jpg'
},
]
},
{
type: MESSAGE_TYPE.IMAGE,
state: AUDIO_STATE.NORMAL,
data: require('../../assets/img/poster/1.jpg'),
time: '2021-01-02 21:21',
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.IMAGE,
state: AUDIO_STATE.NORMAL,
data: require('../../assets/img/poster/1.jpg'),
time: '2021-01-02 21:21',
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
},
readState: READ_STATE.ARRIVED
},
{
type: MESSAGE_TYPE.VIDEO_CALL,
state: CALL_STATE.REJECT,
data: '2021-01-02 21:44',
time: '2021-01-02 21:21',
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.VIDEO_CALL,
state: CALL_STATE.RESOLVE,
data: '2021-01-02 21:44',
time: '2021-01-02 21:21',
user: {
id: 2,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.VIDEO_CALL,
state: CALL_STATE.NONE,
data: '2021-01-02 21:44',
time: '2021-01-02 21:21',
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.AUDIO_CALL,
state: CALL_STATE.REJECT,
data: '2021-01-02 21:44',
time: '2021-01-02 21:21',
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.AUDIO_CALL,
state: CALL_STATE.RESOLVE,
data: '2021-01-02 21:44',
time: '2021-01-02 21:21',
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.AUDIO_CALL,
state: CALL_STATE.NONE,
data: '2021-01-02 21:44',
time: '2021-01-02 21:21',
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.AUDIO,
state: AUDIO_STATE.NORMAL,
data: {
duration: 5,
src: '',
},
time: '2021-01-02 21:21',
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.AUDIO,
state: AUDIO_STATE.NORMAL,
data: {
duration: 10,
src: '',
},
time: '2021-01-02 21:21',
user: {
id: 2,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.TEXT,
data: '又在刷抖音',
time: '2021-01-02 21:21',
createBy: 1
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.TEXT,
data: '我昨天@你那个视频发给我下',
time: '2021-01-02 21:21',
createBy: 1
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.TEXT,
data: '我找不到了',
time: '2021-01-02 21:21',
createBy: 1
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.TEXT,
data: '我也找不到了我也找不到了我也找不到了我也找不到了我也找不到了我也找不到了我也找不到了我也找不到了',
time: '2021-01-02 21:21',
createBy: 2
user: {
id: 2,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.DOUYIN_VIDEO,
state: VIDEO_STATE.VALID,
data: {
src: '../../assets/video/1.mp4',
poster: require('../../assets/img/poster/3.jpg'),
author: {
name: 'safasdfas',
avatar: '../../assets/img/icon/head-image.jpg'
name: 'safasdfassafasdfassafasdfassafasdfas',
avatar: require('../../assets/img/icon/head-image.jpeg')
},
title: 'asdfasdfasdfasdf'
title: '服了asd'
},
time: '2021-01-02 21:21',
createBy: 1
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.DOUYIN_VIDEO,
state: VIDEO_STATE.INVALID,
state: VIDEO_STATE.VALID,
data: {
src: '../../assets/video/1.mp4',
poster: require('../../assets/img/poster/3.jpg'),
author: {
name: 'safasdfas',
avatar: '../../assets/img/icon/head-image.jpg'
name: 'safasdfassafasdfassafasdfassafasdfas',
avatar: require('../../assets/img/icon/head-image.jpeg')
},
title: 'asdfasdfasdfasdf'
title: '服了asd'
},
time: '2021-01-02 21:21',
createBy: 1
user: {
id: 2,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.VIDEO,
state: VIDEO_STATE.VALID,
data: '../../assets/video/1.mp4',
data: {
poster: require('../../assets/img/poster/3.jpg'),
},
time: '2021-01-02 21:21',
createBy: 1
user: {
id: 1,
avatar: '../../assets/img/icon/head-image.jpg'
}
},
{
type: MESSAGE_TYPE.AUDIO,
state: AUDIO_STATE.NORMAL,
data: '../../assets/video/1.mp4',
time: '2021-01-02 21:21',
createBy: 1
},
{
type: MESSAGE_TYPE.AUDIO,
state: AUDIO_STATE.SENDING,
data: '../../assets/video/1.mp4',
time: '2021-01-02 21:21',
createBy: 1
},
{
type: MESSAGE_TYPE.VIDEO_CALL,
state: CALL_STATE.REJECT,
data: '2021-01-02 21:44',
time: '2021-01-02 21:21',
createBy: 1
},
{
type: MESSAGE_TYPE.VIDEO_CALL,
state: CALL_STATE.RESOLVE,
data: '2021-01-02 21:44',
time: '2021-01-02 21:21',
createBy: 1
},
{
type: MESSAGE_TYPE.VIDEO_CALL,
state: CALL_STATE.NONE,
data: '2021-01-02 21:44',
time: '2021-01-02 21:21',
createBy: 1
},
{
type: MESSAGE_TYPE.AUDIO_CALL,
state: CALL_STATE.REJECT,
data: '2021-01-02 21:44',
time: '2021-01-02 21:21',
createBy: 1
},
{
type: MESSAGE_TYPE.AUDIO_CALL,
state: CALL_STATE.RESOLVE,
data: '2021-01-02 21:44',
time: '2021-01-02 21:21',
createBy: 1
},
{
type: MESSAGE_TYPE.AUDIO_CALL,
state: CALL_STATE.NONE,
data: '2021-01-02 21:44',
time: '2021-01-02 21:21',
createBy: 1
},
{
type: MESSAGE_TYPE.IMAGE,
state: AUDIO_STATE.NORMAL,
data: '../../assets/video/1.mp4',
time: '2021-01-02 21:21',
createBy: 1
},
{
type: MESSAGE_TYPE.IMAGE,
state: AUDIO_STATE.NORMAL,
data: '../../assets/video/1.mp4',
time: '2021-01-02 21:21',
createBy: 1,
readState: READ_STATE.ARRIVED
},
]
],
typing: false,
recording: false,
showOption: false
}
},
computed: {
isExpand() {
return this.showOption
},
isTyping() {
return this.typing || this.isExpand
}
},
computed: {},
created() {
},
methods: {}
@ -235,6 +369,8 @@ export default {
color: white;
.header {
z-index: 2;
background: $main-bg;
position: fixed;
width: 100%;
box-sizing: border-box;
@ -266,49 +402,119 @@ export default {
background: $second-btn-color;
}
}
}
.message-wrapper {
height: calc(100vh - 12.5rem);
overflow: auto;
padding-top: 6rem;
.message {
padding: 0 1rem;
margin-bottom: 2rem;
&.expand {
height: calc(100vh - (12.5rem + 30vh));
}
}
.time {
color: $second-text-color;
text-align: center;
height: 4rem;
line-height: 6rem;
.footer {
$chat-bg-color: rgb(105, 143, 244);
$typing-bg-color: whitesmoke;
background: $main-bg;
$normal-bg-color: rgb(35, 38, 47);
padding: 1rem 0;
border-top: 1px solid $second-btn-color-tran;
&.typing {
background: white;
.toolbar {
background: $typing-bg-color;
}
.right {
justify-content: flex-end;
input {
background: $typing-bg-color !important;
}
}
.toolbar {
box-sizing: border-box;
height: 4.4rem;
margin: 0 1rem;
padding: .5rem;
background: $normal-bg-color;
border-radius: 2rem;
display: flex;
align-items: center;
img {
width: 2.4rem;
border-radius: 50%;
margin-left: 1.5rem;
}
.left {
justify-content: flex-start;
input {
flex: 1;
outline: none;
border: none;
background: $normal-bg-color;
}
.chat-text {
display: flex;
margin: 1rem 0;
.camera {
margin-left: 0;
margin-right: .5rem;
width: 1.4rem;
padding: .5rem;
border-radius: 50%;
background: $chat-bg-color;
}
.content {
max-width: 60vw;
padding: 1rem;
border-radius: .3rem;
margin: 0 1rem;
}
.record {
box-sizing: border-box;
height: 4.4rem;
margin: 0 1rem;
padding: 1rem .5rem;
background: $normal-bg-color;
border-radius: 2rem;
display: flex;
align-items: center;
justify-content: center;
position: relative;
img {
right: .5rem;
position: absolute;
width: 2.4rem;
border-radius: 50%;
margin-left: 1.5rem;
}
}
.options {
padding-top: 1.5rem;
height: 30vh;
box-sizing: border-box;
.option-wrapper {
padding: 1.5rem;
$grid-width: calc((100vw - 3rem) / 4);
color: black;
display: grid;
grid-template-columns:$grid-width $grid-width $grid-width $grid-width;
.option {
display: flex;
justify-content: center;
align-items: center;
font-size: 1.4rem;
background: dodgerblue;
}
flex-direction: column;
margin-bottom: 1rem;
.avatar {
height: 3.6rem;
border-radius: 50%;
img {
border-radius: .4rem;
background: whitesmoke;
padding: 1rem;
width: 2.5rem;
margin-bottom: 1rem;
}
}
}
}

View File

@ -0,0 +1,130 @@
<template>
<div class="ChatDetail">
<BaseHeader>
<template v-slot:center>
<span class="f16">聊天详情</span>
</template>
</BaseHeader>
<div class="content">
<div class="peoples">
<People v-for="item in list " :people="item"></People>
</div>
<div class="setting">
<div class="row">
<div class="left">消息免打扰</div>
<div class="right">
<switches v-model="noMessage" theme="bootstrap" color="success"></switches>
</div>
</div>
<div class="row">
<div class="left">置顶聊天</div>
<div class="right">
<switches v-model="top" theme="bootstrap" color="success"></switches>
</div>
</div>
<div class="row">
<div class="left">设备备注</div>
<div class="right">
<img src="../../assets/img/icon/back.png" alt="">
</div>
</div>
<div class="row">
<div class="left">举报</div>
<div class="right">
<img src="../../assets/img/icon/back.png" alt="">
</div>
</div>
<div class="row">
<div class="left">拉黑</div>
<div class="right">
<img src="../../assets/img/icon/back.png" alt="">
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Switches from './components/swtich/switches';
import People from "../people/components/People";
export default {
name: "ChatDetail",
components: {
Switches,
People
},
data() {
return {
noMessage: false,
top: false,
list: [
{
type: 1,
name:'A'
},
{
type: 6,
name:'多人聊天'
},
]
}
},
computed: {},
created() {
},
methods: {
t() {
this.enabled = !this.enabled
}
}
}
</script>
<style scoped lang="scss">
@import "../../assets/scss/index";
.ChatDetail {
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
overflow: auto;
color: white;
.content {
padding-top: 6rem;
.peoples {
padding: 0 2rem;
.People {
border-bottom: 1px solid $second-btn-color-tran;
}
}
.setting {
.row {
padding-left: 2rem;
padding-right: 2rem;
display: flex;
align-items: center;
justify-content: space-between;
height: 4rem;
&:active {
opacity: .5;
}
.right {
img {
height: 2rem;
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,408 @@
<template>
<div class="ChatMessage"
:class="!isMe ? 'left' : 'right'"
:style="message.type === MESSAGE_TYPE.TIME && 'margin-bottom: 0;'">
<div class="time" v-if="message.type === MESSAGE_TYPE.TIME">
{{ message.time }}
</div>
<template v-else>
<img v-if="!isMe" src="../../../assets/img/icon/head-image.jpeg" alt="" class="avatar">
<div class="chat-wrapper">
<div class="chat-text"
v-if="message.type === MESSAGE_TYPE.TEXT">
{{ message.data }}
</div>
<div class="douyin_video"
v-if="message.type === MESSAGE_TYPE.DOUYIN_VIDEO">
<img class="poster" :src="message.data.poster" alt=""/>
<div class="title">{{ message.data.title }}</div>
<img src="../../../assets/img/icon/play.svg" class="pause"/>
<div class="author">
<img class="video-avatar" :src="message.data.author.avatar" alt="">
<span class="name">{{ message.data.author.name }}</span>
</div>
</div>
<div class="douyin_video"
v-if="message.type === MESSAGE_TYPE.VIDEO">
<img class="poster" :src="message.data.poster" alt=""/>
<img src="../../../assets/img/icon/play.svg" class="pause"/>
</div>
<div class="audio"
v-if="message.type === MESSAGE_TYPE.AUDIO">
<div v-if="isMe" class="duration">{{ message.data.duration }}'</div>
<div class="horn">
<img src="../../../assets/img/icon/rss.png" alt="" class="avatar">
</div>
<div v-if="!isMe" class="duration">{{ message.data.duration }}'</div>
</div>
<div class="call"
v-if="message.type === MESSAGE_TYPE.VIDEO_CALL ||
message.type === MESSAGE_TYPE.AUDIO_CALL"
>
<div class="resolve" v-if="message.state === CALL_STATE.RESOLVE">
<img class="icon" src="../../../assets/img/icon/head-image.jpeg" alt="">
<span>通话时长 05:32</span>
</div>
<div class="reject" v-if="message.state === CALL_STATE.REJECT||
message.state === CALL_STATE.NONE">
<img class="icon" src="../../../assets/img/icon/collect-gray.png" alt="">
<div class="notice">
<span class="state" v-if="message.state === CALL_STATE.REJECT">对方已拒绝</span>
<span class="state" v-if="message.state === CALL_STATE.NONE">对方未接通</span>
<span>点击呼叫</span>
</div>
</div>
</div>
<div class="image"
v-if="message.type === MESSAGE_TYPE.IMAGE">
<img :src="message.data" alt="">
</div>
<div class="meme"
v-if="message.type === MESSAGE_TYPE.MEME">
<img :src="message.data" alt="">
</div>
<div class="red_packet"
:class="message.data.state !== '未领取' ? 'invalid' : ''"
v-if="message.type === MESSAGE_TYPE.RED_PACKET">
<div class="top">
<img src="../../../assets/img/icon/head-image.jpeg" alt="">
<div class="right">
<div class="title">{{ message.data.title }}</div>
<div v-if="message.data.state !== '未领取'" class="state">{{ message.data.state }}</div>
</div>
</div>
<span class="bottom">抖音红包</span>
</div>
<div class="loves" v-if="message.loved?.length">
<img src="../../../assets/img/icon/loved.svg" alt="">
<img v-for="user in message.loved" src="../../../assets/img/icon/head-image.jpeg" alt="" class="love-avatar">
</div>
</div>
<img v-if="isMe" src="../../../assets/img/icon/head-image.jpeg" alt="" class="avatar">
</template>
</div>
</template>
<script>
import {mapState} from "vuex";
let CALL_STATE = {
REJECT: 0,
RESOLVE: 1,
NONE: 2,
}
let VIDEO_STATE = {
VALID: 0,
INVALID: 1,
}
let AUDIO_STATE = {
NORMAL: 0,
SENDING: 1,
}
let READ_STATE = {
SENDING: 0,
ARRIVED: 1,
READ: 1,
}
let RED_PACKET_MODE = {
SINGLE: 1,
MULTIPLE: 2
}
let MESSAGE_TYPE = {
TEXT: 0,
TIME: 1,
VIDEO: 2,
DOUYIN_VIDEO: 9,
AUDIO: 3,
IMAGE: 6,
VIDEO_CALL: 4,
AUDIO_CALL: 5,
MEME: 7,//
RED_PACKET: 8,//
}
export default {
name: "ChatMessage",
props: {
message: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
MESSAGE_TYPE,
CALL_STATE,
RED_PACKET_MODE
}
},
computed: {
...mapState({
userinfo: 'userinfo',
}),
isMe() {
return this.userinfo.id === this.message.user.id
}
},
created() {
},
methods: {}
}
</script>
<style scoped lang="scss">
@import "../../../assets/scss/index";
.ChatMessage {
padding: 0 1rem;
margin-bottom: 2rem;
display: flex;
//$chat-bg-color: dodgerblue;
$chat-bg-color: rgb(105, 143, 244);
&.right {
justify-content: flex-end;
.avatar {
margin-left: 1rem;
height: 3.6rem;
border-radius: 50%;
}
.horn {
text-align: end;
}
}
&.left {
justify-content: flex-start;
.avatar {
margin-right: 1rem;
height: 3.6rem;
border-radius: 50%;
}
.horn {
text-align: start;
}
}
.time {
width: 100%;
color: $second-text-color;
text-align: center;
height: 4rem;
line-height: 4rem;
}
.red_packet {
border-radius: .3rem;
$not-received: rgb(253, 92, 72);
$received: rgba(253, 92, 72, .8);
width: 60vw;
background: $not-received;
display: flex;
flex-direction: column;
color: rgb(255, 231, 206);
&.invalid {
background: $received;
}
.top {
padding: 1rem;
display: flex;
align-items: center;
border-bottom: 1px solid rgb(253, 124, 81);
img {
height: 4rem;
margin-right: 1rem;
}
.title {
font-size: 1.4rem;
}
.state {
font-size: 1.2rem;
color: rgba(255, 231, 206, .8);
}
}
.bottom {
padding: .5rem 1rem 1rem 1rem;
}
}
.meme {
img {
border-radius: .6rem;
//height: 30vh;
max-width: 40vw;
}
}
.image {
img {
border-radius: .6rem;
//height: 30vh;
max-width: 40vw;
}
}
.call {
padding: 1rem;
border-radius: .3rem;
display: flex;
align-items: center;
font-size: 1.4rem;
background: $chat-bg-color;
.resolve {
display: flex;
align-items: center;
.icon {
margin-right: 1rem;
width: 2rem;
}
}
.reject {
display: flex;
align-items: center;
.icon {
padding: .4rem;
border-radius: 50%;
background: rgba(27, 100, 172, 0.8);
margin-right: 1rem;
width: 2rem;
}
.notice {
font-size: 1.3rem;
display: flex;
flex-direction: column;
color: #dedede;
.state {
margin-bottom: .2rem;
font-size: 1.5rem;
color: white;
}
}
}
}
.audio {
max-width: 60vw;
padding: 1rem;
padding-right: 1.5rem;
border-radius: .3rem;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 1.4rem;
background: $chat-bg-color;
.horn {
width: 5rem;
img {
transform: rotate(45deg) translate3d(1rem, -.5rem, 0);
width: 1.5rem;
height: 1.5rem;
}
}
}
.douyin_video {
background: black;
position: relative;
.pause {
position: absolute;
top: 50%;
left: 50%;
transform: translateY(-50%) translateX(-50%);;
width: 2.4rem;
}
.title {
position: absolute;
font-size: 1.6rem;
bottom: 3.5rem;
width: calc(100% - 2rem);
word-break: break-word;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
left: 1rem;
}
.poster {
border-radius: .6rem;
//height: 30vh;
width: 40vw;
}
.author {
width: calc(100% - 2rem);
left: 1rem;
position: absolute;
bottom: 1rem;
display: flex;
align-items: center;
.video-avatar {
margin-right: .5rem;
height: 1.6rem;
border-radius: 50%;
}
.name {
width: 80%;
text-overflow: ellipsis;
overflow: hidden;
}
}
}
.chat-text {
max-width: 60vw;
padding: 1rem;
border-radius: .3rem;
display: flex;
align-items: center;
font-size: 1.4rem;
background: $chat-bg-color;
}
.loves {
margin-top: 1rem;
img {
width: 1.6rem;
height: 1.6rem;
border-radius: 50%;
margin-right: .5rem;
}
}
}
</style>

View File

@ -0,0 +1,275 @@
/**
* Default
*/
$color-default-default: #aaa;
$color-default-green: #53b96e;
$color-default-blue: #539bb9;
$color-default-red: #b95353;
$color-default-orange: #b97953;
$color-default-yellow: #bab353;
$theme-default-colors: (
default : $color-default-default,
blue : $color-default-blue,
red : $color-default-red,
yellow : $color-default-yellow,
orange : $color-default-orange,
green : $color-default-green
);
/**
* Bulma
*/
$color-bulma-default: #f5f5f5;
$color-bulma-primary: #00d1b2;
$color-bulma-blue: #3273dc;
$color-bulma-red: #ff3860;
$color-bulma-yellow: #ffdd57;
$color-bulma-green: #22c65b;
$theme-bulma-colors: (
default : $color-bulma-default,
primary : $color-bulma-primary,
blue : $color-bulma-blue,
red : $color-bulma-red,
yellow : $color-bulma-yellow,
green : $color-bulma-green
);
/**
* Bootstrap
*/
$color-bootstrap-default: #fff;
$color-bootstrap-primary: #337ab7;
$color-bootstrap-success: #5cb85c;
$color-bootstrap-info: #5bc0de;
$color-bootstrap-warning: #f0ad4e;
$color-bootstrap-danger: #c9302c;
$theme-bootstrap-colors: (
default : $color-bootstrap-default,
primary : $color-bootstrap-primary,
success : $color-bootstrap-success,
info : $color-bootstrap-info,
warning : $color-bootstrap-warning,
danger : $color-bootstrap-danger
);
.vue-switcher {
position: relative;
display: inline-block;
&__label {
display: block;
font-size: 10px;
margin-bottom: 5px;
}
input {
opacity: 0;
width: 100%;
height: 100%;
position: absolute;
z-index: 1;
cursor: pointer;
}
div {
height: 15px;
width: 36px;
position: relative;
border-radius: 30px;
display: -webkit-flex;
display: -ms-flex;
display: flex;
align-items: center;
justify-content: flex-start;
cursor: pointer;
transition: linear .2s, background-color linear .2s;
&:after {
content: '';
height: 20px;
width: 20px;
border-radius: 100px;
display: block;
transition: linear .15s, background-color linear .15s;
position: absolute;
left: 100%;
margin-left: -18px;
cursor: pointer;
top: -3px;
box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.1);
}
}
&--unchecked {
div {
justify-content: flex-end;
&:after {
left: 15px;
}
}
}
&--disabled {
div {
opacity: .3;
}
input {
cursor: not-allowed;
}
}
&--bold {
div {
top: -8px;
height: 26px;
width: 51px;
&:after {
margin-left: -24px;
top: 3px;
}
}
&--unchecked {
div {
&:after {
left: 28px;
}
}
}
.vue-switcher__label {
span {
padding-bottom: 7px;
display: inline-block;
}
}
}
&-theme--default {
@each $colorName, $color in $theme-default-colors {
&.vue-switcher-color--#{$colorName} {
div {
@if $colorName == 'default' {
background-color: lighten($color, 5%);
} @else {
background-color: lighten($color, 10%);
}
&:after {
@if $colorName == 'default' {
background-color: darken($color, 5%);
} @else {
background-color: $color
}
}
}
&.vue-switcher--unchecked {
div {
@if $colorName == 'default' {
background-color: $color;
} @else {
background-color: lighten($color, 30%);
}
&:after {
background-color: lighten($color, 10%);
}
}
}
}
}
}
&-theme--bulma {
@each $colorName, $color in $theme-bulma-colors {
&.vue-switcher-color--#{$colorName} {
div {
@if $colorName == 'default' {
background-color: darken($color, 10%);
} @else {
background-color: lighten($color, 10%);
}
&:after {
background-color: $color;
}
}
&.vue-switcher--unchecked {
div {
@if $colorName == 'default' or $colorName == 'yellow' {
background-color: darken($color, 5%);
} @else {
background-color: lighten($color, 30%);
}
&:after {
@if $colorName == 'default' {
background-color: $color;
} @else {
background-color: lighten($color, 10%);
}
}
}
}
}
}
}
&-theme--bootstrap {
@each $colorName, $color in $theme-bootstrap-colors {
&.vue-switcher-color--#{$colorName} {
div {
@if $colorName == 'default' {
background-color: darken($color, 10%);
} @else {
background-color: lighten($color, 10%);
}
&:after {
@if $colorName == 'default' {
background-color: darken($color, 6%);
} @else {
background-color: $color;
}
}
}
&.vue-switcher--unchecked {
div {
@if $colorName == 'default' {
background-color: darken($color, 4%);
} @else {
background-color: lighten($color, 30%);
}
&:after {
@if $colorName == 'default' {
background-color: darken($color, 6%);
} @else {
background-color: lighten($color, 10%);
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,94 @@
<template>
<label :class="classObject">
<span class="vue-switcher__label" v-if="shouldShowLabel">
<span v-if="label" v-text="label"></span>
<span v-if="!label && modelValue" v-text="textEnabled"></span>
<span v-if="!label && !modelValue" v-text="textDisabled"></span>
</span>
<input type="checkbox" :disabled="disabled" @change="trigger" :checked="modelValue">
<div></div>
</label>
</template>
<script>
export default {
name: 'switches',
props: {
typeBold: {
default: false
},
modelValue: {
default: false
},
disabled: {
default: false
},
label: {
default: ''
},
textEnabled: {
default: ''
},
textDisabled: {
default: ''
},
color: {
default: 'default'
},
theme: {
default: 'default'
},
emitOnMount: {
default: true
}
},
mounted() {
if (this.emitOnMount) {
this.$emit('update:modelValue', this.modelValue)
}
},
methods: {
trigger(e) {
this.$emit('update:modelValue', e.target.checked)
}
},
computed: {
classObject() {
const {color, modelValue, theme, typeBold, disabled} = this;
return {
'vue-switcher': true,
['vue-switcher--unchecked']: !modelValue,
['vue-switcher--disabled']: disabled,
['vue-switcher--bold']: typeBold,
['vue-switcher--bold--unchecked']: typeBold && !modelValue,
[`vue-switcher-theme--${theme}`]: color,
[`vue-switcher-color--${color}`]: color,
};
},
shouldShowLabel() {
return this.label !== '' || this.textEnabled !== '' || this.textDisabled !== '';
}
}
}
</script>
<style src="./switches.scss" lang="scss"></style>

View File

@ -1,14 +1,16 @@
<template>
<div class="People">
<img src="../../../assets/img/icon/msg-icon1.png" alt="" class="head-image pull-left">
<img v-if="people.type === 6" src="../../../assets/img/icon/add.png" alt="" class="add">
<img v-else src="../../../assets/img/icon/msg-icon1.png" alt="" class="head-image pull-left">
<div class="content">
<div class="left">
<div class="name">A</div>
<div class="detail">
<div class="name">{{ people.name }}</div>
<div class="detail" v-if="people.type !== 6">
该用户关注了你
</div>
</div>
<!-- -->
<!-- 已关注 -->
<div class="right" v-if="people.type === 1">
<div class="button">已关注</div>
</div>
@ -17,23 +19,23 @@
<div class="button red">回关</div>
<img src="../../../assets/img/icon/close.svg" alt="">
</div>
<!-- 互相关注 -->
<!-- 朋友推荐 -->
<div class="right" v-if="people.type === 3">
<div class="button red">回关</div>
<div class="button ">移除</div>
</div>
<!-- 互相关注 -->
<div class="right" v-if="people.type === 4">
<div class="button">发私信</div>
<img src="../../../assets/img/icon/close.svg" alt="" @click="showPopover = !showPopover">
</div>
<!-- 粉丝 -->
<div class="right" v-if="people.type === 4">
<div class="button red">回关</div>
<div class="button">移除</div>
</div>
<!-- 通讯录 -->
<div class="right" v-if="people.type === 5">
<div class="button red address-list">回关</div>
</div>
</div>
<transition name="scale">
<div class="popover" v-if="people.type === 3 && showPopover">
<div class="popover" v-if="people.type === 4 && showPopover">
<div class="arrow"></div>
<div class="item">
<img src="../../../assets/img/icon/close.svg" alt="">
@ -83,6 +85,7 @@ export default {
}
.People {
height: 7rem;
display: flex;
align-items: center;
position: relative;
@ -93,9 +96,18 @@ export default {
.head-image {
margin-right: 15px;
width: 48px;
height: 48px;
margin-right: 1.5rem;
width: 4.8rem;
height: 4.8rem;
border-radius: 50%;
}
.add {
background: $second-btn-color-tran;
margin-right: 1.5rem;
padding: 1.5rem;
width: 1.8rem;
height: 1.8rem;
border-radius: 50%;
}
@ -107,7 +119,7 @@ export default {
.left {
.name {
font-size: 1.8rem;
font-size: 1.6rem;
color: white;
}

View File

@ -28,6 +28,7 @@ import AddressList from "../pages/people/AddressList";
import Scan from "../pages/people/Scan";
import FaceToFace from "../pages/people/FaceToFace";
import Chat from "../pages/message/Chat";
import ChatDetail from "../pages/message/ChatDetail";
const routes = [
// {path: '', component: Music},
@ -60,6 +61,7 @@ const routes = [
{path: '/scan', component: Scan},
{path: '/face-to-face', component: FaceToFace},
{path: '/chat', component: Chat},
{path: '/chat-detail', component: ChatDetail},
]
export default VueRouter.createRouter({

View File

@ -8,6 +8,7 @@ const store = Vuex.createStore({
maskDialog: false,
maskDialogMode: 'dark',
userinfo: {
id: 1,
name: '',
account: '',
desc: '123',