mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-16 14:12:51 +00:00
perf: 优化消息组件
This commit is contained in:
parent
e956a03098
commit
ae587950b9
@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="`content-file ${msg.type}`">
|
||||||
|
<div class="dialog-file">
|
||||||
|
<img v-if="msg.type === 'img'" class="file-img" :style="imageStyle(msg)" :src="msg.thumb" @click="viewFile"/>
|
||||||
|
<div v-else-if="isVideoFile(msg)" class="file-video" :style="imageStyle(msg)" @click="viewFile">
|
||||||
|
<img v-if="msg.thumb" :src="msg.thumb">
|
||||||
|
<video v-else :width="imageStyle(msg, 'width')" :height="imageStyle(msg, 'height')">
|
||||||
|
<source :src="msg.path" type="video/mp4">
|
||||||
|
</video>
|
||||||
|
<div class="file-play">
|
||||||
|
<div class="play-icon">
|
||||||
|
<i class="taskfont"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="file-box" @click="downFile">
|
||||||
|
<img class="file-thumb" :src="msg.thumb"/>
|
||||||
|
<div class="file-info">
|
||||||
|
<div class="file-name">{{ msg.name }}</div>
|
||||||
|
<div class="file-size">{{ $A.bytesToSize(msg.size) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="msg.percentage" class="file-percentage">
|
||||||
|
<span :style="fileStyle(msg.percentage)"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
msg: Object,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
viewFile() {
|
||||||
|
this.$emit('viewFile');
|
||||||
|
},
|
||||||
|
downFile() {
|
||||||
|
this.$emit('downFile');
|
||||||
|
},
|
||||||
|
|
||||||
|
fileStyle(percentage) {
|
||||||
|
if (percentage) {
|
||||||
|
return {
|
||||||
|
width: `${percentage}%`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
|
||||||
|
imageStyle(info, type = 'style') {
|
||||||
|
const {width, height} = info;
|
||||||
|
if (width && height) {
|
||||||
|
let maxW = 220,
|
||||||
|
maxH = 220,
|
||||||
|
tempW = width,
|
||||||
|
tempH = height;
|
||||||
|
if (width > maxW || height > maxH) {
|
||||||
|
if (width > height) {
|
||||||
|
tempW = maxW;
|
||||||
|
tempH = height * (maxW / width);
|
||||||
|
} else {
|
||||||
|
tempW = width * (maxH / height);
|
||||||
|
tempH = maxH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type === 'width') {
|
||||||
|
return tempW
|
||||||
|
}
|
||||||
|
if (type === 'height') {
|
||||||
|
return tempH
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
width: tempW + 'px',
|
||||||
|
height: tempH + 'px',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (type === 'width' || type === 'height') {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
|
||||||
|
isVideoFile(msg) {
|
||||||
|
return msg.type === 'file'
|
||||||
|
&& msg.ext === 'mp4'
|
||||||
|
&& msg.width > 0
|
||||||
|
&& msg.height > 0;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -26,148 +26,21 @@
|
|||||||
<!--详情-->
|
<!--详情-->
|
||||||
<div ref="content" class="dialog-content" :class="contentClass">
|
<div ref="content" class="dialog-content" :class="contentClass">
|
||||||
<!--文本-->
|
<!--文本-->
|
||||||
<div v-if="msgData.type === 'text'" class="content-text no-dark-content">
|
<TextMsg v-if="msgData.type === 'text'" :msg="msgData.msg" @click="viewText"/>
|
||||||
<DialogMarkdown v-if="msgData.msg.type === 'md'" @click="viewText" :text="msgData.msg.text"/>
|
|
||||||
<pre v-else @click="viewText" v-html="$A.formatTextMsg(msgData.msg.text, userId)"></pre>
|
|
||||||
</div>
|
|
||||||
<!--文件-->
|
<!--文件-->
|
||||||
<div v-else-if="msgData.type === 'file'" :class="`content-file ${msgData.msg.type}`">
|
<FileMsg v-else-if="msgData.type === 'file'" :msg="msgData.msg" @viewFile="viewFile" @downFile="downFile"/>
|
||||||
<div class="dialog-file">
|
|
||||||
<img v-if="msgData.msg.type === 'img'" class="file-img" :style="imageStyle(msgData.msg)" :src="msgData.msg.thumb" @click="viewFile"/>
|
|
||||||
<div v-else-if="isVideoFile(msgData.msg)" class="file-video" :style="imageStyle(msgData.msg)" @click="viewFile">
|
|
||||||
<img v-if="msgData.msg.thumb" :src="msgData.msg.thumb">
|
|
||||||
<video v-else :width="imageStyle(msgData.msg, 'width')" :height="imageStyle(msgData.msg, 'height')">
|
|
||||||
<source :src="msgData.msg.path" type="video/mp4">
|
|
||||||
</video>
|
|
||||||
<div class="file-play">
|
|
||||||
<div class="play-icon">
|
|
||||||
<i class="taskfont"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else class="file-box" @click="downFile">
|
|
||||||
<img class="file-thumb" :src="msgData.msg.thumb"/>
|
|
||||||
<div class="file-info">
|
|
||||||
<div class="file-name">{{msgData.msg.name}}</div>
|
|
||||||
<div class="file-size">{{$A.bytesToSize(msgData.msg.size)}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="msgData.msg.percentage" class="file-percentage">
|
|
||||||
<span :style="fileStyle(msgData.msg.percentage)"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--录音-->
|
<!--录音-->
|
||||||
<div v-else-if="msgData.type === 'record'" class="content-record no-dark-content">
|
<RecordMsg v-else-if="msgData.type === 'record'" :msg="msgData.msg" @playRecord="playRecord"/>
|
||||||
<div class="dialog-record" :class="{playing: audioPlaying === msgData.msg.path}" :style="recordStyle(msgData.msg)" @click="playRecord">
|
|
||||||
<div class="record-time">{{recordDuration(msgData.msg.duration)}}</div>
|
|
||||||
<div class="record-icon taskfont"></div>
|
|
||||||
</div>
|
|
||||||
<div v-if="msgData.msg.text" class="dialog-record-text">
|
|
||||||
{{msgData.msg.text}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--会议-->
|
<!--会议-->
|
||||||
<div v-else-if="msgData.type === 'meeting'" class="content-meeting no-dark-content">
|
<MeetingMsg v-else-if="msgData.type === 'meeting'" :msg="msgData.msg" @openMeeting="openMeeting"/>
|
||||||
<ul class="dialog-meeting" :class="{'meeting-end':!!msgData.msg.end_at}">
|
|
||||||
<li>
|
|
||||||
<em>{{$L('会议主题')}}</em>
|
|
||||||
{{msgData.msg.name}}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<em>{{$L('会议创建人')}}</em>
|
|
||||||
<UserAvatar :userid="msgData.msg.userid" :show-icon="false" :show-name="true"/>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<em>{{$L('频道ID')}}</em>
|
|
||||||
{{msgData.msg.meetingid.replace(/^(.{3})(.{3})(.*)$/, '$1 $2 $3')}}
|
|
||||||
</li>
|
|
||||||
<li v-if="msgData.msg.end_at" class="meeting-operation">
|
|
||||||
{{$L('会议已结束')}}
|
|
||||||
</li>
|
|
||||||
<li v-else class="meeting-operation" @click="openMeeting">
|
|
||||||
{{$L('点击加入会议')}}
|
|
||||||
<i class="taskfont"></i>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<!--接龙-->
|
<!--接龙-->
|
||||||
<div v-else-if="msgData.type === 'word-chain'" class="content-text content-word-chain no-dark-content">
|
<WordChainMsg v-else-if="msgData.type === 'word-chain'" :msg="msgData.msg" :msgId="msgData.id" :unfoldWordChainData="unfoldWordChainData" @unfoldWordChain="unfoldWordChain(msgData)" @onWordChain="onWordChain"/>
|
||||||
<pre v-html="$A.formatTextMsg(msgData.msg.text, userId)"></pre>
|
|
||||||
<ul :class="{'expand': unfoldWordChainData.indexOf(msgData.id) !== -1 }">
|
|
||||||
<li v-for="(item) in (msgData.msg.list || []).filter(h=>h.type == 'case')">
|
|
||||||
{{ $L('例') }} {{ item.text }}
|
|
||||||
</li>
|
|
||||||
<li v-for="(item,index) in (msgData.msg.list || []).filter(h=>h.type != 'case' && h.text)">
|
|
||||||
<span class="expand" v-if="index == 2 && msgData.msg.list.length > 4" @click="unfoldWordChain(msgData)">
|
|
||||||
...{{$L('展开')}}...
|
|
||||||
</span>
|
|
||||||
<span :class="{'shrink': index >= 2 && msgData.msg.list.length > 4 } ">
|
|
||||||
{{index + 1}}. {{item.text}}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li @click="onWordChain" class="participate">
|
|
||||||
{{ $L('参与接龙') }}
|
|
||||||
<i class="taskfont"></i>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<!--投票-->
|
<!--投票-->
|
||||||
<div v-else-if="msgData.type === 'vote'" class="content-text content-word-vote no-dark-content">
|
<VoteMsg v-else-if="msgData.type === 'vote'" :msg="msgData.msg" :voteData="voteData" @onVote="onVote($event, msgData)"/>
|
||||||
<div class="vote-msg-head">
|
|
||||||
<i class="taskfont"></i>
|
|
||||||
<em>{{ $L('投票') }}</em>
|
|
||||||
<span>{{ msgData.msg.multiple == 1 ? $L('多选') : $L('单选')}}</span>
|
|
||||||
<span>{{ msgData.msg.anonymous == 1 ? $L('匿名') : $L('实名')}}</span>
|
|
||||||
</div>
|
|
||||||
<pre v-html="$A.formatTextMsg(msgData.msg.text, userId)"></pre>
|
|
||||||
<template v-if="(msgData.msg.votes || []).filter(h=>h.userid == userId).length == 0">
|
|
||||||
<RadioGroup v-if="msgData.msg.multiple == 0" v-model="voteData[msgData.msg.uuid]" vertical>
|
|
||||||
<Radio v-for="(item,index) in (msgData.msg.list || [])" :label="item.id" :key="index">
|
|
||||||
{{item.text}}
|
|
||||||
</Radio>
|
|
||||||
</RadioGroup>
|
|
||||||
<CheckboxGroup v-else v-model="voteData[msgData.msg.uuid]">
|
|
||||||
<Checkbox v-for="(item,index) in (msgData.msg.list || [])" :label="item.id" :key="index">
|
|
||||||
{{item.text}}
|
|
||||||
</Checkbox>
|
|
||||||
</CheckboxGroup>
|
|
||||||
<div class="btn-row">
|
|
||||||
<Button v-if="(voteData[msgData.msg.uuid] || []).length == 0" class="ivu-btn-grey" disabled>{{$L("请选择后投票")}}</Button>
|
|
||||||
<Button v-else type="warning" :loading="msgData.msg._loadIng > 0" class="no-dark-content" @click="onVote('vote',msgData)">{{$L("立即投票")}}</Button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div class="vote-result-body">
|
|
||||||
<ul>
|
|
||||||
<li v-for="item in (msgData.msg.list || [])">
|
|
||||||
<div class="vote-option-title">{{ item.text }}</div>
|
|
||||||
<div class="ticket-num">
|
|
||||||
<span>{{ getVoteProgress(msgData.msg,item.id).num }}{{$L('票')}}</span>
|
|
||||||
<span>{{ getVoteProgress(msgData.msg,item.id).progress + '%' }}</span>
|
|
||||||
</div>
|
|
||||||
<Progress :percent="Number(getVoteProgress(msgData.msg,item.id).progress)" :stroke-width="5" hide-info/>
|
|
||||||
<div v-if="msgData.msg.anonymous == 0" class="avatar-row">
|
|
||||||
<template v-for="votes in (msgData.msg.votes || []).filter(h=>h.votes.indexOf(item.id) != -1)">
|
|
||||||
<UserAvatar :userid="votes.userid" :size="18" />
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="btn-row" v-if="msgData.msg.state == 1 && msgData.msg.userid == userId">
|
|
||||||
<Button type="warning" :loading="msgData.msg._loadIng > 0" @click="onVote('again',msgData)">{{$L("再次发送")}}</Button>
|
|
||||||
<Button type="warning" :loading="msgData.msg._loadIng > 0" @click="onVote('finish',msgData)">{{$L("结束投票")}}</Button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<!--等待-->
|
<!--等待-->
|
||||||
<div v-else-if="msgData.type === 'loading'" class="content-loading">
|
<LoadMsg v-else-if="isLoading" :error="msgData.error"/>
|
||||||
<Icon v-if="msgData.error === true" type="ios-alert-outline" />
|
|
||||||
<Loading v-else/>
|
|
||||||
</div>
|
|
||||||
<!--未知-->
|
<!--未知-->
|
||||||
<div v-else class="content-unknown">{{$L("未知的消息类型")}}</div>
|
<UnknownMsg v-else/>
|
||||||
</div>
|
</div>
|
||||||
<!--emoji-->
|
<!--emoji-->
|
||||||
<ul v-if="$A.arrayLength(msgData.emoji) > 0" class="dialog-emoji">
|
<ul v-if="$A.arrayLength(msgData.emoji) > 0" class="dialog-emoji">
|
||||||
@ -295,15 +168,33 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import WCircle from "../../../components/WCircle";
|
import WCircle from "../../../../components/WCircle";
|
||||||
import {mapGetters, mapState} from "vuex";
|
import {mapGetters, mapState} from "vuex";
|
||||||
import {Store} from "le5le-store";
|
import {Store} from "le5le-store";
|
||||||
import longpress from "../../../directives/longpress";
|
import longpress from "../../../../directives/longpress";
|
||||||
import DialogMarkdown from "./DialogMarkdown.vue";
|
|
||||||
|
import TextMsg from "./text.vue";
|
||||||
|
import FileMsg from "./file.vue";
|
||||||
|
import RecordMsg from "./record.vue";
|
||||||
|
import MeetingMsg from "./meet.vue";
|
||||||
|
import WordChainMsg from "./word-chain.vue";
|
||||||
|
import VoteMsg from "./vote.vue";
|
||||||
|
import LoadMsg from "./load.vue";
|
||||||
|
import UnknownMsg from "./unknown.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "DialogView",
|
name: "DialogView",
|
||||||
components: {DialogMarkdown, WCircle},
|
components: {
|
||||||
|
UnknownMsg,
|
||||||
|
LoadMsg,
|
||||||
|
VoteMsg,
|
||||||
|
WordChainMsg,
|
||||||
|
MeetingMsg,
|
||||||
|
RecordMsg,
|
||||||
|
TextMsg,
|
||||||
|
FileMsg,
|
||||||
|
WCircle
|
||||||
|
},
|
||||||
directives: {longpress},
|
directives: {longpress},
|
||||||
props: {
|
props: {
|
||||||
msgData: {
|
msgData: {
|
||||||
@ -382,7 +273,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['loads', 'audioPlaying']),
|
...mapState(['loads']),
|
||||||
...mapGetters(['isLoad']),
|
...mapGetters(['isLoad']),
|
||||||
|
|
||||||
isLoading() {
|
isLoading() {
|
||||||
@ -557,72 +448,6 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
recordStyle(info) {
|
|
||||||
const {duration} = info;
|
|
||||||
const width = 50 + Math.min(180, Math.floor(duration / 200));
|
|
||||||
return {
|
|
||||||
width: width + 'px',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
recordDuration(duration) {
|
|
||||||
const minute = Math.floor(duration / 60000),
|
|
||||||
seconds = Math.floor(duration / 1000) % 60;
|
|
||||||
if (minute > 0) {
|
|
||||||
return `${minute}:${seconds}″`
|
|
||||||
}
|
|
||||||
return `${Math.max(1, seconds)}″`
|
|
||||||
},
|
|
||||||
|
|
||||||
fileStyle(percentage) {
|
|
||||||
if (percentage) {
|
|
||||||
return {
|
|
||||||
width: `${percentage}%`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
|
|
||||||
imageStyle(info, type = 'style') {
|
|
||||||
const {width, height} = info;
|
|
||||||
if (width && height) {
|
|
||||||
let maxW = 220,
|
|
||||||
maxH = 220,
|
|
||||||
tempW = width,
|
|
||||||
tempH = height;
|
|
||||||
if (width > maxW || height > maxH) {
|
|
||||||
if (width > height) {
|
|
||||||
tempW = maxW;
|
|
||||||
tempH = height * (maxW / width);
|
|
||||||
} else {
|
|
||||||
tempW = width * (maxH / height);
|
|
||||||
tempH = maxH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (type === 'width') {
|
|
||||||
return tempW
|
|
||||||
}
|
|
||||||
if (type === 'height') {
|
|
||||||
return tempH
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
width: tempW + 'px',
|
|
||||||
height: tempH + 'px',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (type === 'width' || type === 'height') {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
|
|
||||||
isVideoFile(msg) {
|
|
||||||
return msg.type === 'file'
|
|
||||||
&& msg.ext === 'mp4'
|
|
||||||
&& msg.width > 0
|
|
||||||
&& msg.height > 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
playRecord() {
|
playRecord() {
|
||||||
if (this.operateVisible) {
|
if (this.operateVisible) {
|
||||||
return
|
return
|
||||||
@ -737,15 +562,15 @@ export default {
|
|||||||
cancelText: '取消',
|
cancelText: '取消',
|
||||||
okText: '确定',
|
okText: '确定',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
this.vote(type, msgData);
|
this.submitVote(type, msgData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.vote(type, msgData);
|
this.submitVote(type, msgData);
|
||||||
},
|
},
|
||||||
|
|
||||||
vote(type, msgData) {
|
submitVote(type, msgData) {
|
||||||
this.$set(msgData.msg, '_loadIng', 1)
|
this.$set(msgData.msg, '_loadIng', 1)
|
||||||
this.$store.dispatch("call", {
|
this.$store.dispatch("call", {
|
||||||
url: 'dialog/msg/vote',
|
url: 'dialog/msg/vote',
|
||||||
@ -769,12 +594,6 @@ export default {
|
|||||||
this.$set(msgData.msg, '_loadIng', 0)
|
this.$set(msgData.msg, '_loadIng', 0)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getVoteProgress(msgData, id) {
|
|
||||||
const num = msgData.votes.filter(h => (h.votes || '').indexOf(id) != -1).length
|
|
||||||
const progress = !num ? '0.00' : (num / msgData.votes.length * 100).toFixed(2);
|
|
||||||
return { num, progress };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<div class="content-loading">
|
||||||
|
<Icon v-if="error === true" type="ios-alert-outline" />
|
||||||
|
<Loading v-else/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
error: Boolean,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<div class="content-meeting no-dark-content">
|
||||||
|
<ul class="dialog-meeting" :class="{'meeting-end':!!msg.end_at}">
|
||||||
|
<li>
|
||||||
|
<em>{{ $L('会议主题') }}</em>
|
||||||
|
{{ msg.name }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<em>{{ $L('会议创建人') }}</em>
|
||||||
|
<UserAvatar :userid="msg.userid" :show-icon="false" :show-name="true"/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<em>{{ $L('频道ID') }}</em>
|
||||||
|
{{ channelID(msg.meetingid) }}
|
||||||
|
</li>
|
||||||
|
<li v-if="msg.end_at" class="meeting-operation">
|
||||||
|
{{ $L('会议已结束') }}
|
||||||
|
</li>
|
||||||
|
<li v-else class="meeting-operation" @click="openMeeting">
|
||||||
|
{{ $L('点击加入会议') }}
|
||||||
|
<i class="taskfont"></i>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
msg: Object,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openMeeting() {
|
||||||
|
this.$emit('openMeeting');
|
||||||
|
},
|
||||||
|
channelID(meetingid: string) {
|
||||||
|
return meetingid.replace(/^(.{3})(.{3})(.*)$/, '$1 $2 $3');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<div class="content-record no-dark-content">
|
||||||
|
<div class="dialog-record" :class="{playing: audioPlaying === msg.path}" :style="recordStyle(msg)" @click="playRecord">
|
||||||
|
<div class="record-time">{{recordDuration(msg.duration)}}</div>
|
||||||
|
<div class="record-icon taskfont"></div>
|
||||||
|
</div>
|
||||||
|
<div v-if="msg.text" class="dialog-record-text">
|
||||||
|
{{msg.text}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {mapState} from "vuex";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
msg: Object,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(['audioPlaying']),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
playRecord() {
|
||||||
|
this.$emit('playRecord');
|
||||||
|
},
|
||||||
|
recordStyle(info) {
|
||||||
|
const {duration} = info;
|
||||||
|
const width = 50 + Math.min(180, Math.floor(duration / 200));
|
||||||
|
return {
|
||||||
|
width: width + 'px',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
recordDuration(duration) {
|
||||||
|
const minute = Math.floor(duration / 60000),
|
||||||
|
seconds = Math.floor(duration / 1000) % 60;
|
||||||
|
if (minute > 0) {
|
||||||
|
return `${minute}:${seconds}″`
|
||||||
|
}
|
||||||
|
return `${Math.max(1, seconds)}″`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<div class="content-text no-dark-content">
|
||||||
|
<DialogMarkdown v-if="msg.type === 'md'" @click="viewText" :text="msg.text"/>
|
||||||
|
<pre v-else @click="viewText" v-html="$A.formatTextMsg(msg.text, userId)"></pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import DialogMarkdown from "../DialogMarkdown.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {DialogMarkdown},
|
||||||
|
props: {
|
||||||
|
msg: Object,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
viewText() {
|
||||||
|
this.$emit('viewText');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<div class="content-unknown">{{$L("未知的消息类型")}}</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<div class="content-text content-word-vote no-dark-content">
|
||||||
|
<div class="vote-msg-head">
|
||||||
|
<i class="taskfont"></i>
|
||||||
|
<em>{{ $L('投票') }}</em>
|
||||||
|
<span>{{ msg.multiple == 1 ? $L('多选') : $L('单选') }}</span>
|
||||||
|
<span>{{ msg.anonymous == 1 ? $L('匿名') : $L('实名') }}</span>
|
||||||
|
</div>
|
||||||
|
<pre v-html="$A.formatTextMsg(msg.text, userId)"></pre>
|
||||||
|
<template v-if="(msg.votes || []).filter(h=>h.userid == userId).length == 0">
|
||||||
|
<RadioGroup v-if="msg.multiple == 0" v-model="voteData[msg.uuid]" vertical>
|
||||||
|
<Radio v-for="(item,index) in (msg.list || [])" :label="item.id" :key="index">
|
||||||
|
{{ item.text }}
|
||||||
|
</Radio>
|
||||||
|
</RadioGroup>
|
||||||
|
<CheckboxGroup v-else v-model="voteData[msg.uuid]">
|
||||||
|
<Checkbox v-for="(item,index) in (msg.list || [])" :label="item.id" :key="index">
|
||||||
|
{{ item.text }}
|
||||||
|
</Checkbox>
|
||||||
|
</CheckboxGroup>
|
||||||
|
<div class="btn-row">
|
||||||
|
<Button v-if="(voteData[msg.uuid] || []).length == 0" class="ivu-btn-grey" disabled>{{ $L("请选择后投票") }}</Button>
|
||||||
|
<Button v-else type="warning" :loading="msg._loadIng > 0" class="no-dark-content" @click="onVote('vote')">{{ $L("立即投票") }}</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="vote-result-body">
|
||||||
|
<ul>
|
||||||
|
<li v-for="item in (msg.list || [])">
|
||||||
|
<div class="vote-option-title">{{ item.text }}</div>
|
||||||
|
<div class="ticket-num">
|
||||||
|
<span>{{ getVoteProgress(msg, item.id).num }}{{ $L('票') }}</span>
|
||||||
|
<span>{{ getVoteProgress(msg, item.id).progress + '%' }}</span>
|
||||||
|
</div>
|
||||||
|
<Progress :percent="Number(getVoteProgress(msg,item.id).progress)" :stroke-width="5" hide-info/>
|
||||||
|
<div v-if="msg.anonymous == 0" class="avatar-row">
|
||||||
|
<template v-for="votes in (msg.votes || []).filter(h=>h.votes.indexOf(item.id) != -1)">
|
||||||
|
<UserAvatar :userid="votes.userid" :size="18"/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="btn-row" v-if="msg.state == 1 && msg.userid == userId">
|
||||||
|
<Button type="warning" :loading="msg._loadIng > 0" @click="onVote('again')">{{ $L("再次发送") }}</Button>
|
||||||
|
<Button type="warning" :loading="msg._loadIng > 0" @click="onVote('finish')">{{ $L("结束投票") }}</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
msg: Object,
|
||||||
|
voteData: Object,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getVoteProgress(data, id) {
|
||||||
|
const num = data.votes.filter(h => (h.votes || '').indexOf(id) != -1).length
|
||||||
|
const progress = !num ? '0.00' : (num / data.votes.length * 100).toFixed(2);
|
||||||
|
return {num, progress};
|
||||||
|
},
|
||||||
|
onVote(type) {
|
||||||
|
this.$emit('onVote', type);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<div class="content-text content-word-chain no-dark-content">
|
||||||
|
<pre v-html="$A.formatTextMsg(msg.text, userId)"></pre>
|
||||||
|
<ul :class="{'expand': unfoldWordChainData.indexOf(msgId) !== -1 }">
|
||||||
|
<li v-for="(item) in (msg.list || []).filter(h=>h.type == 'case')">
|
||||||
|
{{ $L('例') }} {{ item.text }}
|
||||||
|
</li>
|
||||||
|
<li v-for="(item, index) in (msg.list || []).filter(h=>h.type != 'case' && h.text)">
|
||||||
|
<span class="expand" v-if="index == 2 && msg.list.length > 4" @click="unfoldWordChain">
|
||||||
|
...{{ $L('展开') }}...
|
||||||
|
</span>
|
||||||
|
<span :class="{'shrink': index >= 2 && msg.list.length > 4 } ">
|
||||||
|
{{ index + 1 }}. {{ item.text }}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li @click="onWordChain" class="participate">
|
||||||
|
{{ $L('参与接龙') }}
|
||||||
|
<i class="taskfont"></i>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
msg: Object,
|
||||||
|
msgId: Number,
|
||||||
|
unfoldWordChainData: Array,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
unfoldWordChain() {
|
||||||
|
this.$emit('unfoldWordChain');
|
||||||
|
},
|
||||||
|
onWordChain() {
|
||||||
|
this.$emit('onWordChain');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -655,7 +655,8 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&.text,
|
&.text,
|
||||||
&.record {
|
&.record,
|
||||||
|
&.word-chain {
|
||||||
max-width: 70%;
|
max-width: 70%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1317,7 +1318,7 @@
|
|||||||
|
|
||||||
.content-word-vote {
|
.content-word-vote {
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
max-width: 260px;
|
max-width: 300px;
|
||||||
|
|
||||||
.vote-msg-head {
|
.vote-msg-head {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user