123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- <template>
- <div class="chat-box-message">
- <div
- class="chat-box-message__item"
- v-for="item in flist"
- :key="item.id || item.uid"
- :class="[item.type == 0 ? `is-right` : `is-left`, `is-${item.mode}`]"
- >
- <div class="date" v-if="item._date">
- <span>{{ item._date }}</span>
- </div>
- <div class="main">
- <div class="avatar" @tap="toUserDetail(item)">
- <el-image :src="item.avatarUrl"></el-image>
- </div>
- <div class="det">
- <span class="name">{{ item.nickName }}</span>
- <div
- class="content"
- v-loading="item.loading"
- :element-loading-text="item.progress"
- @click="tapItem(item)"
- >
- <!-- 文本 -->
- <template v-if="item.mode === 'text'">{{ item.content.text }}</template>
- <!-- 图片 -->
- <template v-else-if="item.mode === 'image'">
- <el-image
- :key="item.uid"
- :src="item.content.imageUrl"
- :preview-src-list="[item.content.imageUrl]"
- ></el-image>
- </template>
- <!-- 表情 -->
- <template v-else-if="item.mode === 'emoji'">
- <img :src="item.content.imageUrl" />
- </template>
- <!-- 语音 -->
- <template v-else-if="item.mode === 'voice'">
- <icon-voice :play="item.isPlay"></icon-voice>
- <span class="duration">{{ item.content.duration | duration }}"</span>
- </template>
- <!-- 视频 -->
- <template v-else-if="item.mode === 'video'">
- <div class="item">
- <video
- :poster="item.content.videoUrl | video_poster"
- :src="item.content.videoUrl"
- controls
- ></video>
- </div>
- </template>
- <!-- 未知 -->
- <template v-else>
- <span>待扩展消息类型</span>
- <i class="el-icon-warning-outline"></i>
- </template>
- </div>
- </div>
- </div>
- </div>
- <!-- voice -->
- <div class="voice">
- <audio style="display: none" ref="voice" :src="voice.url" controls></audio>
- </div>
- </div>
- </template>
- <script>
- import dayjs from "dayjs";
- import IconVoice from "./icon-voice";
- export default {
- components: {
- IconVoice
- },
- props: {
- list: Array
- },
- data() {
- return {
- player: {},
- voice: {
- url: "",
- timer: null
- }
- };
- },
- filters: {
- duration(val) {
- return Math.ceil((val || 1) / 1000);
- }
- },
- destroyed() {
- clearTimeout(this.voice.timer);
- this.list.map((e) => {
- e.isPlay = false;
- });
- },
- computed: {
- flist() {
- let date = "";
- return this.list.map((e) => {
- e._date = date
- ? dayjs(e.createTime).isBefore(dayjs(date).add(1, "minute"))
- ? ""
- : e.createTime
- : e.createTime;
- date = e.createTime;
- return e;
- });
- }
- },
- methods: {
- tapItem(item) {
- if (item.mode == "voice") {
- this.list.map((e) => {
- this.$set(e, "isPlay", e.id == item.id ? e.isPlay : false);
- });
- item.isPlay = !item.isPlay;
- if (item.isPlay) {
- this.voice.url = item.content.voiceUrl;
- this.$nextTick(() => {
- this.$refs["voice"].play();
- });
- } else {
- this.$refs["voice"].pause();
- item.isPlay = false;
- }
- clearTimeout(this.voice.timer);
- this.voice.timer = setTimeout(() => {
- item.isPlay = false;
- }, item.content.duration);
- }
- }
- }
- };
- </script>
- <style lang="scss" scoped>
- .chat-box-message {
- &__item {
- margin-bottom: 20px;
- .date {
- text-align: center;
- margin: 10px 0;
- span {
- background-color: #dadada;
- font-size: 12px;
- color: #fff;
- border-radius: 3px;
- padding: 2px 5px;
- letter-spacing: 1px;
- }
- }
- .main {
- display: flex;
- .avatar {
- flex-shrink: 0;
- height: 40px;
- width: 40px;
- .el-image {
- border-radius: 3px;
- }
- }
- .det {
- display: flex;
- flex-direction: column;
- max-width: 60%;
- .name {
- margin-bottom: 5px;
- }
- .content {
- display: inline-block;
- border-radius: 8px;
- box-sizing: border-box;
- font-size: 12px;
- }
- }
- }
- &.is-left {
- .main {
- .det {
- margin-left: 10px;
- align-items: flex-start;
- .content {
- border-top-left-radius: 0;
- background-color: #fff;
- }
- }
- }
- &.is-voice {
- .content {
- justify-content: flex-start;
- }
- }
- }
- &.is-right {
- .main {
- flex-direction: row-reverse;
- .det {
- margin-right: 10px;
- align-items: flex-end;
- .content {
- border-top-right-radius: 0;
- background-color: $color-main;
- color: #fff;
- }
- }
- }
- &.is-voice {
- .content {
- justify-content: flex-end;
- }
- }
- }
- &.is-text {
- .content {
- max-width: 100%;
- min-width: 40px;
- word-wrap: break-word;
- }
- }
- &.is-text,
- &.is-voice {
- .content {
- padding: 10px;
- line-height: 20px;
- letter-spacing: 1px;
- }
- }
- &.is-emoji {
- .content {
- padding: 10px;
- img {
- height: 20px;
- width: 20px;
- }
- }
- }
- &.is-voice {
- .content {
- display: flex;
- align-items: center;
- width: 65px;
- cursor: pointer;
- &:hover {
- opacity: 0.8;
- }
- }
- }
- &.is-video {
- .item {
- video {
- max-width: 300px;
- max-height: 300px;
- }
- }
- }
- &.is-image {
- .main {
- .det {
- .content {
- background-color: #fff;
- /deep/.el-image {
- display: block;
- border-radius: 6px;
- max-width: 200px;
- min-width: 80px;
- }
- }
- }
- }
- }
- &.is-undefined {
- .main {
- .det {
- .content {
- display: flex;
- align-items: center;
- padding: 10px;
- letter-spacing: 1px;
- background-color: #f56c6c;
- color: #fff;
- .el-icon-warning-outline {
- font-size: 15px;
- margin-left: 4px;
- }
- }
- }
- }
- }
- }
- }
- </style>
|