This commit is contained in:
程
2022-12-28 10:08:51 +08:00
parent 0fa93d545e
commit 3881370b6e
151 changed files with 17044 additions and 0 deletions

View File

@@ -0,0 +1,193 @@
<template>
<div class="audio-message">
<div class="videodisc">
<div class="disc" :class="{ play: isPlay }" @click="toPlay">
<i v-if="loading" class="el-icon-loading" />
<i v-else-if="isPlay" class="el-icon-video-pause" />
<i v-else class="el-icon-video-play" />
<audio
ref="audio"
type="audio/mp3"
:src="src"
@timeupdate="timeupdate"
@ended="ended"
@canplay="canplay"
></audio>
</div>
</div>
<div class="detail">
<div class="text">
<i class="el-icon-service" />
<span>{{ getCurrDuration }} / {{ getTotalDuration }}</span>
</div>
<div class="process">
<el-progress :percentage="progress" :show-text="false" />
</div>
</div>
</div>
</template>
<script>
function formatSeconds(value) {
var theTime = parseInt(value) // 秒
var theTime1 = 0 // 分
var theTime2 = 0 // 小时
if (theTime > 60) {
theTime1 = parseInt(theTime / 60)
theTime = parseInt(theTime % 60)
if (theTime1 > 60) {
theTime2 = parseInt(theTime1 / 60)
theTime1 = parseInt(theTime1 % 60)
}
}
var result = '' + parseInt(theTime) //秒
if (10 > theTime > 0) {
result = '0' + parseInt(theTime) //秒
} else {
result = '' + parseInt(theTime) //秒
}
if (10 > theTime1 > 0) {
result = '0' + parseInt(theTime1) + ':' + result //分,不足两位数,首位补充0,
} else {
result = '' + parseInt(theTime1) + ':' + result //分
}
if (theTime2 > 0) {
result = '' + parseInt(theTime2) + ':' + result //时
}
return result
}
export default {
name: 'AudioMessage',
props: {
src: {
type: String,
default: '',
},
},
data() {
return {
loading: true,
isPlay: false,
duration: 0,
currentTime: 0,
progress: 0,
}
},
computed: {
getTotalDuration() {
return formatSeconds(this.duration)
},
getCurrDuration() {
return formatSeconds(this.currentTime)
},
},
methods: {
toPlay() {
if (this.loading) {
return
}
let audio = this.$refs.audio
if (this.isPlay) {
audio.pause()
} else {
audio.play()
}
this.isPlay = !this.isPlay
},
// 当目前的播放位置已更改时
timeupdate() {
let audio = this.$refs.audio
this.currentTime = audio.currentTime
this.progress = (audio.currentTime / audio.duration) * 100
},
// 当浏览器可以播放音频/视频时
canplay() {
this.duration = this.$refs.audio.duration
this.loading = false
},
// 当目前的播放列表已结束时
ended() {
this.isPlay = false
},
},
}
</script>
<style scoped lang="less">
.audio-message {
width: 200px;
height: 60px;
border-radius: 5px;
background: #ffffff;
display: flex;
align-items: center;
border: 1px solid #03a9f4;
overflow: hidden;
> div {
height: 100%;
}
.videodisc {
flex-basis: 60px;
flex-shrink: 0;
display: flex;
justify-content: center;
align-items: center;
.disc {
width: 42px;
height: 42px;
background: #e9e5e5;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
cursor: pointer;
transition: ease 0.5;
&.play {
background: #ff5722;
box-shadow: 0 0 4px 0px #f76a3e;
}
i {
font-size: 24px;
}
&:active i {
transform: scale(1.1);
}
}
}
.detail {
flex: 1 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-top: 10px;
.text {
width: 90%;
font-size: 12px;
i {
margin-right: 5px;
}
}
.process {
padding-top: 10px;
height: 20px;
width: 90%;
}
}
}
</style>

View File

@@ -0,0 +1,148 @@
<template>
<div
class="code-message"
:class="{
'max-height': lineNumber > 6,
'max-width': maxwidth,
'full-screen': fullscreen,
}"
>
<i
:class="
fullscreen ? 'el-icon-close' : 'iconfont icon-tubiao_chakangongyi'
"
@click="fullscreen = !fullscreen"
/>
<pre class="lum-scrollbar" v-html="formatCode(code, lang)" />
</div>
</template>
<script>
import Prism from 'prismjs'
import 'prismjs/themes/prism-okaidia.css'
export default {
name: 'CodeMessage',
props: {
code: {
type: [String, Number],
default: '',
},
lang: {
type: String,
default: '',
},
maxwidth: {
type: Boolean,
default: false,
},
},
data() {
return {
fullscreen: false,
lineNumber: 0,
}
},
created() {
this.lineNumber = this.code.split(/\n/).length
},
methods: {
formatCode(code, lang) {
try {
return Prism.highlight(code, Prism.languages[lang], lang) + '<br/>'
} catch (error) {
return code
}
},
},
}
</script>
<style lang="less" scoped>
.code-message {
position: relative;
overflow: hidden;
border-radius: 5px;
box-sizing: border-box;
&.max-width {
max-width: 500px;
}
&.max-height {
height: 208px;
}
i {
position: absolute;
right: 0px;
top: 0px;
font-size: 16px;
cursor: pointer;
color: white;
display: inline-block;
opacity: 0;
width: 50px;
height: 30px;
background: #171616;
text-align: center;
line-height: 30px;
border-radius: 0 0 0px 8px;
transition: 1s ease;
}
&:hover {
i {
opacity: 1;
}
}
pre {
box-sizing: border-box;
height: 100%;
width: 100%;
overflow: auto;
padding: 10px;
line-height: 24px;
background: #272822;
color: #d5d4d4;
font-family: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
'Courier New', monospace;
font-size: 85%;
&.lum-scrollbar {
&::-webkit-scrollbar {
background-color: black;
}
}
}
&.full-screen {
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
max-width: unset;
max-height: unset;
border-radius: 0px;
background: #272822;
z-index: 99999999;
i {
position: fixed;
top: 15px;
right: 15px;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
font-size: 24px;
&:active {
box-shadow: 0 0 5px 0px #ccc;
}
}
}
}
</style>

View File

@@ -0,0 +1,145 @@
<template>
<div class="file-message">
<div class="main">
<div class="ext">{{ ext }}</div>
<div class="file-box">
<p class="info">
<span class="name">{{ fileName }}</span>
<span class="size">({{ fileSize }})</span>
</p>
<p class="notice">文件已成功发送, 文件助手永久保存</p>
</div>
</div>
<div class="footer">
<a @click="download(record_id)">下载</a>
<a>在线预览</a>
</div>
</div>
</template>
<script>
import { formatSize, download } from '@/utils/functions'
export default {
name: 'FileMessage',
props: {
file: {
type: Object,
required: true,
},
record_id: {
type: Number,
required: true,
default: 0,
},
},
data() {
return {
file_id: 0,
ext: '',
fileName: '',
fileSize: '',
}
},
created() {
this.file_id = this.file.id
this.ext = this.file.file_suffix.toUpperCase()
this.fileName = this.file.original_name
this.fileSize = formatSize(this.file.file_size)
},
methods: {
download,
},
}
</script>
<style lang="less" scoped>
.file-message {
width: 250px;
height: 85px;
background: white;
box-shadow: 0 0 5px 0px #e8e4e4;
padding: 10px;
border-radius: 3px;
transition: all 0.5s;
&:hover {
box-shadow: 0 0 5px 0px #cac6c6;
}
.main {
height: 45px;
display: flex;
flex-direction: row;
.ext {
display: flex;
justify-content: center;
align-items: center;
width: 45px;
height: 45px;
flex-shrink: 0;
color: #ffffff;
background: #49a4ff;
border-radius: 5px;
font-size: 12px;
}
.file-box {
flex: 1 1;
height: 45px;
margin-left: 10px;
overflow: hidden;
.info {
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
height: 24px;
color: rgb(76, 76, 76);
font-size: 14px;
.name {
flex: 1 auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.size {
font-size: 12px;
color: #cac6c6;
}
}
.notice {
height: 25px;
line-height: 25px;
font-size: 12px;
color: #929191;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.footer {
height: 30px;
line-height: 37px;
color: #409eff;
text-align: right;
font-size: 12px;
border-top: 1px solid #eff7ef;
margin-top: 10px;
a {
margin: 0 3px;
user-select: none;
cursor: pointer;
&:hover {
color: royalblue;
}
}
}
}
</style>

View File

@@ -0,0 +1,116 @@
<template>
<div>
<div class="forward-message" @click="catForwardRecords">
<div class="title">{{ title }}</div>
<div v-for="(record, index) in records" :key="index" class="lists">
<p>
<span>{{ record.nickname }}:</span>
<span>{{ record.text }}</span>
</p>
</div>
<div class="footer">
<span>转发:聊天会话记录 ({{ num }}条)</span>
</div>
</div>
<!-- 会话记录查看器 -->
<talk-forward-record ref="forwardRecordsRef" />
</div>
</template>
<script>
import TalkForwardRecord from '@/components/chat/TalkForwardRecord'
export default {
name: 'ForwardMessage',
components: {
TalkForwardRecord,
},
props: {
forward: {
type: Object,
required: true,
},
record_id: {
type: Number,
required: true,
default: 0,
},
},
data() {
return {
title: '',
records: [],
num: 0,
}
},
methods: {
catForwardRecords() {
this.$refs.forwardRecordsRef.open(this.record_id)
},
getForwardTitle(list) {
let arr = [...new Set(list.map(v => v.nickname))]
return arr.join('、') + '的会话记录'
},
},
created() {
let forward = this.forward
this.num = forward.num
this.records = forward.list
this.title = this.getForwardTitle(this.records)
},
}
</script>
<style lang="less" scoped>
/* 会话记录消息 */
.forward-message {
width: 250px;
min-height: 95px;
max-height: 150px;
border-radius: 3px;
background-color: white;
padding: 3px 10px;
cursor: pointer;
box-shadow: 0 0 5px 0px #e8e4e4;
text-align: left;
user-select: none;
.title {
height: 30px;
line-height: 30px;
font-size: 14px;
color: #565353;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 400;
}
.lists p {
height: 18px;
line-height: 18px;
font-size: 10px;
color: #aaa9a9;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 100;
}
.footer {
height: 32px;
line-height: 35px;
color: #858282;
border-top: 1px solid #f1ebeb;
font-size: 12px;
margin-top: 12px;
font-weight: 300;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&:hover {
box-shadow: 0 0 5px 0px #cac6c6;
}
}
</style>

View File

@@ -0,0 +1,150 @@
<template>
<!-- 好友申请消息 -->
<div class="apply-card">
<div class="card-header">
<img class="avatar" :src="avatar" />
<div class="nickname">No. {{ nickname }}</div>
<div class="datetime">{{ datetime }}</div>
<div class="remarks">
<span>备注信息:{{ remarks }}</span>
</div>
</div>
<div class="card-footer">
<div class="mini-button" @click="handle(1)">同意</div>
<el-divider direction="vertical"></el-divider>
<div class="mini-button" @click="handle(2)">拒绝</div>
</div>
</div>
</template>
<script>
export default {
name: 'FriendApplyMessage',
props: {
data: {
type: Object,
default() {
return {}
},
},
},
data() {
return {
avatar:
'http://im-img.gzydong.club/media/images/avatar/20210602/60b6f03598ed0104301.png',
nickname: '独特态度',
datetime: '05/09 12:13 分',
remarks: '编辑个签,展示我的独特态度 展示我的独特态度。',
apply_id: 0,
}
},
created() {},
methods: {
handle(type) {
alert(type)
},
},
}
</script>
<style lang="less" scoped>
.apply-card {
position: relative;
width: 170px;
min-height: 180px;
border-radius: 15px;
overflow: hidden;
transition: all 0.5s;
box-sizing: border-box;
background-image: linear-gradient(-84deg, #1ab6ff 0, #1ab6ff 0, #82c1f3 100%);
// #028fff
&:hover {
transform: scale(1.02);
}
.card-header {
position: relative;
width: 100%;
height: 135px;
.avatar {
position: absolute;
top: 18px;
left: 8px;
width: 40px;
height: 40px;
border-radius: 50%;
background: white;
}
.nickname {
position: absolute;
top: 15px;
right: 8px;
width: 90px;
height: 25px;
font-size: 10px;
text-align: center;
line-height: 25px;
color: white;
border-bottom: 1px dashed white;
}
.datetime {
position: absolute;
top: 42px;
right: 11.5px;
color: white;
font-size: 10px;
transform: scale(0.9);
}
.remarks {
position: absolute;
bottom: 5px;
color: white;
font-size: 10px;
padding: 3px 5px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
transform: scale(0.95);
}
}
.card-footer {
position: absolute;
bottom: 0;
width: 100%;
height: 40px;
border-top: 1px solid white;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
/deep/.el-divider {
background: white;
}
.mini-button {
display: flex;
width: 50px;
height: 25px;
margin: 0 10px;
text-align: center;
align-items: center;
justify-content: center;
font-size: 13px;
color: white;
cursor: pointer;
&:hover {
font-size: 14px;
}
}
}
}
</style>

View File

@@ -0,0 +1,51 @@
<template>
<div class="image-message no-select">
<el-image
fit="cover"
:src="src"
:lazy="true"
:style="getImgStyle(src)"
:preview-src-list="[src]"
>
<div slot="error" class="image-slot">图片加载失败...</div>
<div slot="placeholder" class="image-slot">图片加载中...</div>
</el-image>
</div>
</template>
<script>
import { imgZoom } from '@/utils/functions'
export default {
name: 'ImageMessage',
props: {
src: {
type: String,
default: '',
},
},
methods: {
getImgStyle(url) {
return imgZoom(url, 200)
},
},
}
</script>
<style lang="less" scoped>
.image-message {
/deep/.el-image {
border-radius: 5px;
cursor: pointer;
background: #f1efef;
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
font-size: 13px;
color: #908686;
background: #efeaea;
}
}
}
</style>

View File

@@ -0,0 +1,74 @@
<template>
<div class="invite-message">
<div v-if="invite.type == 1 || invite.type == 3" class="system-msg">
<a @click="toUser(invite.operate_user.id)">
{{ invite.operate_user.nickname }}
</a>
<span>{{ invite.type == 1 ? '邀请了' : '将' }}</span>
<template v-for="(user, uidx) in invite.users">
<a @click="toUser(user.id)">{{ user.nickname }}</a>
<em v-show="uidx < invite.users.length - 1">、</em>
</template>
<span>{{ invite.type == 1 ? '加入了群聊' : '踢出了群聊' }}</span>
</div>
<div v-else-if="invite.type == 2" class="system-msg">
<a @click="toUser(invite.operate_user.id)">
{{ invite.operate_user.nickname }}
</a>
<span>退出了群聊</span>
</div>
</div>
</template>
<script>
export default {
name: 'InviteMessage',
props: {
invite: {
type: Object,
required: true,
},
},
methods: {
toUser(user_id) {
this.$emit('cat', user_id)
},
},
}
</script>
<style lang="less" scoped>
.invite-message {
display: flex;
justify-content: center;
}
.system-msg {
margin: 10px auto;
background-color: #f5f5f5;
font-size: 11px;
line-height: 30px;
padding: 0 8px;
word-break: break-all;
word-wrap: break-word;
color: #979191;
user-select: none;
font-weight: 300;
display: inline-block;
border-radius: 3px;
span {
margin: 0 5px;
}
a {
color: #939596;
cursor: pointer;
font-size: 12px;
font-weight: 400;
&:hover {
color: black;
}
}
}
</style>

View File

@@ -0,0 +1,101 @@
<template>
<!-- 登录消息 -->
<div class="login-message">
<h4>登录操作通知</h4>
<p>登录时间:{{ datetime }} (CST)</p>
<p>IP地址:{{ ip }}</p>
<p>登录地点:{{ address }}</p>
<p>登录设备:{{ platform }}</p>
<p>异常原因:{{ reason }}</p>
</div>
</template>
<script>
import { parseTime } from '@/utils/functions'
export default {
name: 'LoginMessage',
props: {
detail: {
type: Object,
required: true,
},
},
data() {
return {
datetime: '',
ip: '',
address: '',
platform: '',
reason: '常用设备登录',
}
},
created() {
this.ip = this.detail.ip
this.datetime = parseTime(
this.detail.created_at,
'{y}年{m}月{d}日 {h}:{i}:{s}'
)
this.address = this.detail.address
this.reason = this.detail.reason
this.platform =
this.getExploreName(this.detail.agent) +
' / ' +
this.getExploreOs(this.detail.agent)
},
methods: {
getExploreName(userAgent = '') {
if (userAgent.indexOf('Opera') > -1 || userAgent.indexOf('OPR') > -1) {
return 'Opera'
} else if (
userAgent.indexOf('compatible') > -1 &&
userAgent.indexOf('MSIE') > -1
) {
return 'IE'
} else if (userAgent.indexOf('Edge') > -1) {
return 'Edge'
} else if (userAgent.indexOf('Firefox') > -1) {
return 'Firefox'
} else if (
userAgent.indexOf('Safari') > -1 &&
userAgent.indexOf('Chrome') == -1
) {
return 'Safari'
} else if (
userAgent.indexOf('Chrome') > -1 &&
userAgent.indexOf('Safari') > -1
) {
return 'Chrome'
} else {
return 'Unkonwn'
}
},
getExploreOs(userAgent = '') {
if (userAgent.indexOf('Mac OS') > -1) {
return 'Mac OS'
} else {
return 'Windows'
}
},
},
}
</script>
<style lang="less" scoped>
.login-message {
width: 300px;
min-height: 50px;
background: #f7f7f7;
border-radius: 5px;
padding: 15px;
p {
font-size: 13px;
margin: 10px 0;
&:last-child {
margin-bottom: 0;
}
}
}
</style>

View File

@@ -0,0 +1,26 @@
<template>
<div class="reply-message">这是回复的消息[预留]</div>
</template>
<script>
export default {
name: 'ReplyMessage',
data() {
return {}
},
created() {},
methods: {},
}
</script>
<style lang="less" scoped>
.reply-message {
margin-top: 5px;
min-height: 28px;
background: #f7f1f1;
line-height: 28px;
font-size: 12px;
padding: 0 10px;
border-radius: 3px;
color: #a7a2a2;
}
</style>

View File

@@ -0,0 +1,66 @@
<template>
<div class="revoke-message">
<div class="content">
<span v-if="$store.state.user.id == item.user_id">
你撤回了一条消息 | {{ sendTime(item.created_at) }}
</span>
<span v-else-if="item.talk_type == 1">
对方撤回了一条消息 | {{ sendTime(item.created_at) }}
</span>
<span v-else>
"{{ item.nickname }}" 撤回了一条消息 | {{ sendTime(item.created_at) }}
</span>
</div>
</div>
</template>
<script>
import { formatTime as sendTime } from "@/utils/functions";
export default {
name: "RevokeMessage",
props: {
item: {
type: Object,
},
},
methods: {
sendTime,
},
};
</script>
<style lang="less" scoped>
.revoke-message {
display: flex;
justify-content: center;
.content {
margin: 10px auto;
background-color: #f5f5f5;
font-size: 11px;
line-height: 30px;
padding: 0 8px;
word-break: break-all;
word-wrap: break-word;
color: #979191;
user-select: none;
font-weight: 300;
display: inline-block;
border-radius: 3px;
span {
margin: 0 5px;
}
a {
color: #939596;
cursor: pointer;
font-size: 12px;
font-weight: 400;
&:hover {
color: black;
}
}
}
}
</style>

View File

@@ -0,0 +1,39 @@
<template>
<div class="system-text-message">
<div class="content">{{ content }}</div>
</div>
</template>
<script>
import { formatTime as sendTime } from '@/utils/functions'
export default {
name: 'SystemTextMessage',
props: {
content: String,
},
methods: {
sendTime,
},
}
</script>
<style lang="less" scoped>
.system-text-message {
display: flex;
justify-content: center;
.content {
margin: 10px auto;
background-color: #f5f5f5;
font-size: 11px;
line-height: 30px;
padding: 0 8px;
word-break: break-all;
word-wrap: break-word;
color: #979191;
user-select: none;
font-weight: 300;
display: inline-block;
border-radius: 3px;
}
}
</style>

View File

@@ -0,0 +1,109 @@
<template>
<div
class="text-message"
:class="{
left: float == 'left',
right: float == 'right',
'max-width': !fullWidth,
}"
>
<div v-if="arrow" class="arrow"></div>
<pre v-html="html" />
</div>
</template>
<script>
import { textReplaceLink } from "@/utils/functions";
import { textReplaceEmoji } from "@/utils/emojis";
export default {
name: "TextMessage",
props: {
content: {
type: [String, Number],
default: "",
},
float: {
type: String,
default: "left",
},
fullWidth: {
type: Boolean,
default: true,
},
arrow: {
type: Boolean,
default: false,
},
},
data() {
return {
html: "",
};
},
created() {
const text = textReplaceLink(
this.content,
this.float == "right" ? "#ffffff" : "rgb(9 149 208)"
);
this.html = textReplaceEmoji(text);
},
};
</script>
<style lang="less" scoped>
@bg-left-color: #f5f5f5;
@bg-right-color: #1ebafc;
.text-message {
position: relative;
min-width: 30px;
min-height: 30px;
border-radius: 5px;
padding: 5px;
.arrow {
position: absolute;
width: 0;
height: 0;
font-size: 0;
border: 5px solid;
top: 6px;
left: -10px;
}
&.max-width {
max-width: calc(100% - 50px);
}
&.left {
color: #3a3a3a;
background: @bg-left-color;
.arrow {
border-color: transparent @bg-left-color transparent transparent;
}
}
&.right {
color: #fff;
background: @bg-right-color;
.arrow {
right: -10px;
left: unset;
border-color: transparent transparent transparent @bg-right-color;
}
}
pre {
white-space: pre-wrap;
overflow: hidden;
word-break: break-word;
word-wrap: break-word;
font-size: 15px;
padding: 3px 10px;
font-family: "Microsoft YaHei";
line-height: 25px;
}
}
</style>

View File

@@ -0,0 +1,19 @@
<template>
<!-- 用户卡片消息 - 预留 -->
<div></div>
</template>
<script>
export default {
name: 'UserCardMessage',
components: {},
data() {
return {}
},
computed: {},
watch: {},
methods: {},
created() {},
}
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,18 @@
<template>
<div class="video-message">
视频消息
</div>
</template>
<script>
export default {
name: 'VideoMessage',
components: {},
data() {
return {}
},
methods: {},
created() {},
}
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,134 @@
<template>
<div class="visit-card-message">
<div class="user flex-center">
<div class="avatar flex-center">
<el-avatar :size="28" :src="avatar" />
</div>
<div class="content flex-center">
<p class="ellipsis">{{ nickname }}</p>
</div>
<div class="tools flex-center">
<span class="flex-center pointer">
<i class="el-icon-plus" /> 加好友
</span>
</div>
</div>
<div class="sign"><span>个性签名 : </span>{{ sign }}</div>
<div class="share no-select ellipsis">
<a class="pointer" @click="openVisitCard(friendId)">你是谁?</a>
分享了用户名片,可点击添加好友 ...
</div>
</div>
</template>
<script>
export default {
name: 'VisitCardMessage',
data() {
return {
userId: 0,
friendId: 0,
avatar:
'http://im-serve0.gzydong.club/static/image/sys-head/2019012107542668696.jpg',
sign:
'这个社会,是赢家通吃,输者一无所有,社会,永远都是只以成败论英雄。',
nickname:
'氨基酸纳氨基酸纳氨基酸纳氨基酸纳氨基酸纳氨基酸纳氨基酸纳氨基酸纳',
}
},
created() {},
methods: {
openVisitCard(user_id) {
this.$emit('openVisitCard', user_id)
},
},
}
</script>
<style lang="less" scoped>
.ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.visit-card-message {
min-height: 130px;
min-width: 100px;
max-width: 300px;
border-radius: 5px;
padding: 10px;
box-sizing: border-box;
border: 1px solid #ece5e5;
transition: all 0.5s;
&:hover {
box-shadow: 0 0 8px #e2d3d3;
transform: scale(1.01);
}
.user {
height: 40px;
overflow: hidden;
box-sizing: border-box;
> div {
height: inherit;
}
.avatar {
flex-basis: 30px;
flex-shrink: 0;
}
.content {
flex: 1 1;
margin: 0 10px;
font-size: 14px;
justify-content: flex-start;
overflow: hidden;
}
.tools {
flex-basis: 60px;
flex-shrink: 0;
span {
width: 65px;
height: 30px;
background: #409eff;
color: white;
font-size: 13px;
border-radius: 20px;
padding: 0 8px;
transform: scale(0.8);
user-select: none;
&:active {
background: #83b0f3;
transform: scale(0.83);
}
}
}
}
.sign {
min-height: 22px;
line-height: 22px;
border-radius: 3px;
padding: 5px 8px;
background: #f3f5f7;
color: #7d7d7d;
font-size: 12px;
margin: 10px 0;
span {
font-weight: bold;
}
}
.share {
font-size: 12px;
color: #7d7d7d;
a {
color: #4cabf7;
}
}
}
</style>

View File

@@ -0,0 +1,16 @@
<template>
<div class="voice-message"></div>
</template>
<script>
export default {
name: 'VoiceMessage',
components: {},
data() {
return {}
},
methods: {},
created() {},
}
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,299 @@
<template>
<div>
<div class="vote-message">
<div class="vote-from">
<div class="vheader">
<p>
{{ answer_mode == 1 ? "[多选投票]" : "[单选投票]" }}
<i
v-show="is_vote"
class="pointer"
:class="{
'el-icon-loading': refresh,
'el-icon-refresh': !refresh,
}"
title="刷新投票结果"
@click="loadRefresh"
></i>
</p>
<p>{{ title }}</p>
</div>
<template v-if="is_vote">
<div class="vbody">
<div class="vote-view" v-for="(option, index) in options">
<p class="vote-option">{{ option.value }}. {{ option.text }}</p>
<p class="vote-census">
{{ option.num }} 票 {{ option.progress }}%
</p>
<p class="vote-progress">
<el-progress
:show-text="false"
:percentage="parseInt(option.progress)"
/>
</p>
</div>
</div>
<div class="vfooter vote-view">
<p>应参与人数:{{ answer_num }} 人</p>
<p>实际参与人数:{{ answered_num }} 人</p>
</div>
</template>
<template v-else>
<div class="vbody">
<p class="option" v-for="(option, index) in options">
<el-checkbox
v-model="option.is_checked"
@change="toSelect2(option)"
/>
<span @click="toSelect(option, index)" style="margin-left: 10px">
{{ option.value }} 、{{ option.text }}
</span>
</p>
</div>
<div class="vfooter">
<el-button plain round @click="toVote">
{{ isUserVote ? "立即投票" : "请选择进行投票" }}
</el-button>
</div>
</template>
</div>
</div>
</div>
</template>
<script>
import { ServeConfirmVoteHandle } from "@/api/chat";
export default {
name: "VoteMessage",
props: {
vote: {
type: Object,
required: true,
},
record_id: {
type: Number,
required: true,
},
},
data() {
return {
answer_mode: 0,
title: "啊谁叫你打开你卡沙发那,那就是看、卡收纳是你",
radio_value: "",
options: [],
is_vote: false,
answer_num: 0,
answered_num: 0,
refresh: false,
};
},
computed: {
isUserVote() {
return this.options.some((iten) => {
return iten.is_checked;
});
},
},
created() {
let user_id = this.$store.state.user.id;
let { detail, statistics, vote_users } = this.vote;
this.answer_mode = detail.answer_mode;
this.answer_num = detail.answer_num;
this.answered_num = detail.answered_num;
detail.answer_option.forEach((item) => {
this.options.push({
value: item.key,
text: item.value,
is_checked: false,
num: 0,
progress: "00.0",
});
});
this.is_vote = vote_users.some((value) => {
return value == user_id;
});
this.updateStatistics(statistics);
},
methods: {
loadRefresh() {
this.refresh = true;
setTimeout(() => {
this.refresh = false;
}, 500);
},
updateStatistics(data) {
let count = data.count;
this.options.forEach((option) => {
option.num = data.options[option.value];
if (count > 0) {
option.progress = (data.options[option.value] / count) * 100;
}
});
},
toSelect(option, index) {
if (this.answer_mode == 0) {
this.options.forEach((option) => {
option.is_checked = false;
});
}
this.options[index].is_checked = !option.is_checked;
},
toSelect2(option) {
if (this.answer_mode == 0) {
this.options.forEach((item) => {
if (option.value == item.value) {
item.is_checked = option.is_checked;
} else {
item.is_checked = false;
}
});
}
},
toVote() {
if (this.isUserVote == false) {
return false;
}
let items = [];
this.options.forEach((item) => {
if (item.is_checked) {
items.push(item.value);
}
});
ServeConfirmVoteHandle({
record_id: this.record_id,
options: items.join(","),
}).then((res) => {
if (res.code == 200) {
this.is_vote = true;
this.updateStatistics(res.data);
}
});
},
},
};
</script>
<style lang="less" scoped>
.vote-message {
width: 300px;
min-height: 150px;
border: 1px solid #eceff1;
box-sizing: border-box;
border-radius: 5px;
overflow: hidden;
.vote-from {
width: 100%;
.vheader {
min-height: 50px;
background: #4e83fd;
padding: 8px;
position: relative;
p {
margin: 3px 0;
&:first-child {
color: rgb(245, 237, 237);
font-size: 13px;
margin-bottom: 8px;
}
&:last-child {
color: white;
}
}
&::before {
content: "投票";
position: absolute;
font-size: 60px;
color: white;
opacity: 0.1;
top: -5px;
right: 10px;
}
}
.vbody {
min-height: 80px;
width: 100%;
padding: 5px 15px;
box-sizing: border-box;
.option {
margin: 14px 0px;
font-size: 13px;
span {
cursor: pointer;
user-select: none;
line-height: 22px;
}
.el-radio {
margin-right: 0;
.el-radio__label {
padding-left: 5px;
}
}
}
margin-bottom: 10px;
}
.vfooter {
height: 55px;
text-align: center;
box-sizing: border-box;
.el-button {
width: 80%;
font-weight: 400;
}
&.vote-view {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
padding-left: 15px;
p {
border-left: 2px solid #2196f3;
padding-left: 5px;
}
}
}
}
.vote-view {
width: 100%;
min-height: 30px;
margin: 15px 0;
box-sizing: border-box;
> p {
margin: 6px 0px;
font-size: 13px;
}
.vote-option {
min-height: 20px;
line-height: 20px;
}
.vote-census {
height: 20px;
line-height: 20px;
}
}
}
</style>

View File

@@ -0,0 +1,33 @@
import AudioMessage from './AudioMessage.vue';
import CodeMessage from './CodeMessage.vue';
import ForwardMessage from './ForwardMessage.vue';
import ImageMessage from './ImageMessage.vue';
import TextMessage from './TextMessage.vue';
import VideoMessage from './VideoMessage.vue';
import VoiceMessage from './VoiceMessage.vue';
import SystemTextMessage from './SystemTextMessage.vue';
import FileMessage from './FileMessage.vue';
import InviteMessage from './InviteMessage.vue';
import RevokeMessage from './RevokeMessage.vue';
import VisitCardMessage from './VisitCardMessage.vue';
import ReplyMessage from './ReplyMessage.vue';
import VoteMessage from './VoteMessage.vue';
import LoginMessage from './LoginMessage.vue';
export {
AudioMessage,
CodeMessage,
ForwardMessage,
ImageMessage,
TextMessage,
VideoMessage,
VoiceMessage,
SystemTextMessage,
FileMessage,
InviteMessage,
RevokeMessage,
VisitCardMessage,
ReplyMessage,
VoteMessage,
LoginMessage
}