123
dcc
2024-06-28 25b2ba1cf86bc3439e7ad2acf2cd4a9ea7e4b0ed
src/page/customerService/index.vue
@@ -1,284 +1,362 @@
<template>
    <div class="service-box pb-150 flex flex-col">
        <van-nav-bar :title="$t('在线客服')" left-arrow @click-left="onClickLeft" fixed />
        <div class="flex-1" v-if="kefu_url">
            <iframe :src="kefu_url" width="100%" height="100%" frameborder="0"></iframe>
        </div>
        <div v-else class="localKefu flex-1 flex">
            <div class="flex flex-col px-32 box-border flex-1" ref="boxScroll" style="overflow:auto;">
                <div class="w-full py-10 text-grey text-center pt-10" @click="onMore"
                    :style="{ 'visibility': finished ? 'hidden' : 'visiable' }">
                    {{ $t('历史消息') }}</div>
                <ul class="flex flex-col pt-20">
                    <li v-for="(item, index) in list" :key="item.id" class="flex flex-col mt-20"
                        v-if="item.delete_status == 0">
                        <p class="font-26 text-center py-20 text-grey">
                            {{ item.createtime }}</p>
                        <div class="flex" :class="item.send_receive === 'send' ? 'justify-end' : ''">
                            <template v-if="item.send_receive === 'receive'">
                                <img src="@/assets/image/assets-center/logo.png" class="w-81 h-81 mr-40" />
                                <div class="responser px-50 py-35 font-30 rounded-lg font-26 chatBg">
                                    <p class="break-word textColor" style="max-width: 230px;" v-if="item.type === 'text'">
                                        {{ item.content }}</p>
                                    <img v-else :src="item.content" class="w-200 h-200" @click="onPreview(item.content)" />
                                </div>
                            </template>
                            <div class="py-25 px-50 responser-two rounded-lg flex flex-col chatBg" v-else>
                                <img :src="`${item.content}`" class="w-200 h-200" v-if="item.type === 'img'"
                                    @click="onPreview(item.content)" />
                                <p class="break-word textColor" v-else style="max-width: 230px;">{{ item.content }}
                                </p>
                            </div>
                        </div>
                    </li>
                </ul>
            </div>
            <div
                class="bottom flex justify-between h-130 items-center w-full fixed bottom-0 borderTop px-32 box-border bgBottom">
                <van-uploader :max-size="10000 * 1024" @oversize="onOversize" :after-read="afterRead">
                    <img src="@/assets/image/service/photo.png" class="w-72 h-72" />
                </van-uploader>
                <input type="text" v-model="value" :placeholder="$t('请输入您的消息...')"
                    class="flex-1 mx-20 h-full border-none bgBottom textColor" />
                <img src="@/assets/image/service/send.png" class="w-72 h-72" @click="send('text', value)" />
            </div>
        </div>
  <div class="service-box pb-150 flex flex-col">
    <van-nav-bar
      :title="$t('在线客服')"
      left-arrow
      @click-left="onClickLeft"
      fixed
    />
    <div class="flex-1" v-if="kefu_url">
      <iframe
        :src="kefu_url"
        width="100%"
        height="100%"
        frameborder="0"
      ></iframe>
    </div>
    <div v-else class="localKefu flex-1 flex">
      <div
        class="flex flex-col px-32 box-border flex-1"
        ref="boxScroll"
        style="overflow: auto"
      >
        <div
          class="w-full py-10 text-grey text-center pt-10"
          @click="onMore"
          :style="{ visibility: finished ? 'hidden' : 'visiable' }"
        >
          {{ $t("历史消息") }}
        </div>
        <ul class="flex flex-col pt-20">
          <li
            v-for="(item, index) in list"
            :key="item.id"
            class="flex flex-col mt-20"
            v-if="item.delete_status == 0"
          >
            <p class="font-26 text-center py-20 text-grey">
              {{ item.createtime }}
            </p>
            <div
              class="flex"
              :class="item.send_receive === 'send' ? 'justify-end' : ''"
            >
              <template v-if="item.send_receive === 'receive'">
                <img
                  src="@/assets/image/assets-center/logo.png"
                  class="w-81 h-81 mr-40"
                />
                <div
                  class="responser px-50 py-35 font-30 rounded-lg font-26 chatBg"
                >
                  <p
                    class="break-word textColor"
                    style="max-width: 230px"
                    v-if="item.type === 'text'"
                  >
                    {{ item.content }}
                  </p>
                  <img
                    v-else
                    :src="item.content"
                    class="w-200 h-200"
                    @click="onPreview(item.content)"
                  />
                </div>
              </template>
              <div
                class="py-25 px-50 responser-two rounded-lg flex flex-col chatBg"
                v-else
              >
                <img
                  :src="`${item.content}`"
                  class="w-200 h-200"
                  v-if="item.type === 'img'"
                  @click="onPreview(item.content)"
                />
                <p class="break-word textColor" v-else style="max-width: 230px">
                  {{ item.content }}
                </p>
              </div>
            </div>
          </li>
        </ul>
      </div>
      <div
        class="bottom flex justify-between h-130 items-center w-full fixed bottom-0 borderTop px-32 box-border bgBottom"
      >
        <van-uploader
          :max-size="10000 * 1024"
          @oversize="onOversize"
          :after-read="afterRead"
        >
          <img src="@/assets/image/service/photo.png" class="w-72 h-72" />
        </van-uploader>
        <input
          type="text"
          v-model="value"
          :placeholder="$t('请输入您的消息...')"
          class="flex-1 mx-20 h-full border-none bgBottom textColor"
        />
        <span @click="send('text', value)">
          <img src="@/assets/image/service/send.png" class="w-72 h-72" />
        </span>
      </div>
    </div>
  </div>
</template>
<script>
import { mapGetters } from "vuex";
import { _getMsg, _getUnreadMsg, _sendMsg } from '@/API/im.api'
import { _uploadImage } from '@/API/fund.api'
import { Uploader, ImagePreview } from 'vant'
import { _getMsg, _getUnreadMsg, _sendMsg } from "@/API/im.api";
import { _uploadImage } from "@/API/fund.api";
import { Uploader, ImagePreview } from "vant";
export default {
    name: 'CustomerService',
    components: {
        [Uploader.name]: Uploader
  name: "CustomerService",
  components: {
    [Uploader.name]: Uploader,
  },
  data() {
    return {
      list: [],
      value: "",
      lastMsgId: "",
      interval: null,
      unread: 0,
      finished: false, // 没有历史消息
      currentScrollTop: 0,
      isScrollBottom: false,
    };
  },
  computed: {
    ...mapGetters("home", ["kefu_url"]),
  },
  created() {
    this.fetchList();
  },
  mounted() {
    window.addEventListener("scroll", this.handleScroll, true);
  },
  methods: {
    onOversize(file) {
      console.log(file);
      this.$toast(this.$t("文件大小不能超过10m"));
    },
    data() {
        return {
            list: [],
            value: '',
            lastMsgId: '',
            interval: null,
            unread: 0,
            finished: false, // 没有历史消息
            currentScrollTop: 0,
            isScrollBottom: false
    onPreview(url) {
      // 预览
      ImagePreview([url]);
    },
    showTime(index) {
      // 时间显示
      if (index === 0) {
        return true;
      }
      if (index === this.list.length - 1) {
        return false;
      }
      if (
        this.list[index].createtime.split(" ")[0] ===
        this.list[index + 1].createtime.split(" ")[1]
      ) {
        return false;
      }
    },
    afterRead(file) {
      // 文件上传
      this.$toast.loading({ duration: 0 });
      _uploadImage(file, (percent) => {
        console.log(percent);
      })
        .then((data) => {
          this.$toast.clear();
          this.send("img", data);
        })
        .catch(() => {
          this.$toast.clear();
          file.status = "failed";
          file.message = this.$t("图片上传失败");
        });
    },
    fetchList(message_id = "") {
      // 获取消息列表
      _getMsg({ message_id }).then((data) => {
        // this.lastMsgId
        if (!this.lastMsgId) {
          this.lastMsgId = data.length && data[data.length - 1]["id"];
        }
    },
    computed: {
        ...mapGetters('home', ['kefu_url'])
    },
    created() {
        this.fetchList()
    },
    mounted() {
        window.addEventListener('scroll', this.handleScroll, true)
    },
    methods: {
        onOversize(file) {
            console.log(file);
            this.$toast(this.$t('文件大小不能超过10m'));
        },
        onPreview(url) { // 预览
            ImagePreview([url])
        },
        showTime(index) { // 时间显示
            if (index === 0) {
                return true
            }
            if (index === this.list.length - 1) {
                return false
            }
            if (this.list[index].createtime.split(' ')[0] === this.list[index + 1].createtime.split(' ')[1]) {
                return false
            }
        },
        afterRead(file) { // 文件上传
            this.$toast.loading({ duration: 0 })
            _uploadImage(file, (percent) => {
                console.log(percent)
            }).then(data => {
                this.$toast.clear()
                this.send('img', data)
            }).catch(() => {
                this.$toast.clear();
                file.status = 'failed';
                file.message = this.$t('图片上传失败');
            })
        },
        fetchList(message_id = '') { // 获取消息列表
            _getMsg({ message_id }).then(data => { // this.lastMsgId
                if (!this.lastMsgId) {
                    this.lastMsgId = data.length && data[data.length - 1]['id']
                }
                if (data.length) {
                    if (message_id) { // 加载更多
                        this.lastMsgId = data[data.length - 1]['id']
                        this.list = [...data.reverse(), ...this.list]
                    } else {
                        let list = [...this.list, ...data.reverse()]
                        let hash = {};
                        list = list.reverse().reduce(function (preVal, curVal) {
                            hash[curVal.id] ? ' ' : hash[curVal.id] = true && preVal.push(curVal);
                            return preVal
                        }, []);
                        this.list = list.reverse()
                    }
                    if (this.isScrollBottom) {
                        this.$refs.boxScroll.scrollTop = this.$refs.boxScroll.scrollHeight - this.$refs.boxScroll.offsetHeight
                    }
                    this.currentScrollTop = this.$refs.boxScroll.scrollTop;
                    if (data.length < 10) {
                        this.finished = true
                    }
                }
                // else  {
                //     this.list = [{ id: '1', send_receive: 'receive', content: this.$t('你好,欢迎来到我们的数字货币平台!'), type: 'text'}]
                // }
                if (!message_id) {
                    this.clearInterval()
                    this.interval = setInterval(() => {
                        this.fetchList()
                    }, 1000)
                }
            })
        },
        handleScroll() {
            if (this.$refs.boxScroll.scrollTop == this.currentScrollTop) {
                this.isScrollBottom = true
            } else {
                this.isScrollBottom = false
            }
        },
        onMore() { // 加载更多
            this.fetchList(this.lastMsgId)
        },
        clearInterval() {
            if (this.interval) {
                clearInterval(this.interval)
                this.interval = null
            }
        },
        fetchUnread() { // 获取未读
            _getUnreadMsg().then(data => {
                this.unread = data
            })
        },
        onClickLeft() { // 返回
            this.$router.go(-1);
        },
        send(type = 'text', content = '') { // 发送消息, img 也当消息text
            if (!content) {
                this.$toast(this.$t('请输入消息内容'))
                return
            }
            _sendMsg(type, content).then(data => {
                console.log(data)
                this.isScrollBottom = true
                this.value = ''
                // document.getElementById('bottom').click()
            })
        if (data.length) {
          if (message_id) {
            // 加载更多
            this.lastMsgId = data[data.length - 1]["id"];
            this.list = [...data.reverse(), ...this.list];
          } else {
            let list = [...this.list, ...data.reverse()];
            let hash = {};
            list = list.reverse().reduce(function (preVal, curVal) {
              hash[curVal.id]
                ? " "
                : (hash[curVal.id] = true && preVal.push(curVal));
              return preVal;
            }, []);
            this.list = list.reverse();
          }
          if (this.isScrollBottom) {
            this.$refs.boxScroll.scrollTop =
              this.$refs.boxScroll.scrollHeight -
              this.$refs.boxScroll.offsetHeight;
          }
          this.currentScrollTop = this.$refs.boxScroll.scrollTop;
          if (data.length < 10) {
            this.finished = true;
          }
        }
        // else  {
        //     this.list = [{ id: '1', send_receive: 'receive', content: this.$t('你好,欢迎来到我们的数字货币平台!'), type: 'text'}]
        // }
        if (!message_id) {
          this.clearInterval();
          this.interval = setInterval(() => {
            this.fetchList();
          }, 1000);
        }
      });
    },
    beforeDestroy() {
        this.clearInterval()
    handleScroll() {
      if (this.$refs.boxScroll.scrollTop == this.currentScrollTop) {
        this.isScrollBottom = true;
      } else {
        this.isScrollBottom = false;
      }
    },
    activated() {
        window.addEventListener('scroll', this.handleScroll, true)
    onMore() {
      // 加载更多
      this.fetchList(this.lastMsgId);
    },
    deactivated() {
        this.clearInterval()
        window.removeEventListener('scroll', this.handleScroll)
    clearInterval() {
      if (this.interval) {
        clearInterval(this.interval);
        this.interval = null;
      }
    },
    destroyed() {
        window.removeEventListener('scroll', this.handleScroll)
    }
}
    fetchUnread() {
      // 获取未读
      _getUnreadMsg().then((data) => {
        this.unread = data;
      });
    },
    onClickLeft() {
      // 返回
      this.$router.go(-1);
    },
    send(type = "text", content = "") {
      // 发送消息, img 也当消息text
      if (!content) {
        this.$toast(this.$t("请输入消息内容"));
        return;
      }
      _sendMsg(type, content).then((data) => {
        console.log(data);
        this.isScrollBottom = true;
        this.value = "";
        // document.getElementById('bottom').click()
      });
    },
  },
  beforeDestroy() {
    this.clearInterval();
  },
  activated() {
    window.addEventListener("scroll", this.handleScroll, true);
  },
  deactivated() {
    this.clearInterval();
    window.removeEventListener("scroll", this.handleScroll);
  },
  destroyed() {
    window.removeEventListener("scroll", this.handleScroll);
  },
};
</script>
<style lang="scss" scoped>
.service-box {
    width: 100%;
    height: 100%;
    box-sizing: border-box;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
    ::v-deep .van-hairline--bottom::after {
        @include themify() {
            border-color: themed("border_color");
        }
  ::v-deep .van-hairline--bottom::after {
    @include themify() {
      border-color: themed("border_color");
    }
  }
}
.break-word {
    word-wrap: break-word;
    white-space: pre-wrap;
  word-wrap: break-word;
  white-space: pre-wrap;
}
.max-w-230 {
    max-width: 230px;
  max-width: 230px;
}
.responser {
    position: relative;
  position: relative;
    &::after {
        content: '';
        width: 0;
        height: 0;
        border-top: 10px solid transparent;
        border-bottom: 10px solid transparent;
  &::after {
    content: "";
    width: 0;
    height: 0;
    border-top: 10px solid transparent;
    border-bottom: 10px solid transparent;
        @include themify() {
            border-right: 2px solid themed("chat_bg");
        }
        position: absolute;
        left: -20px;
        top: 20px;
    @include themify() {
      border-right: 2px solid themed("chat_bg");
    }
    position: absolute;
    left: -20px;
    top: 20px;
  }
}
.responser-two {
    position: relative;
  position: relative;
    &::after {
        content: '';
        width: 0;
        height: 0;
        border-bottom: 10px solid transparent;
        border-top: 10px solid transparent;
  &::after {
    content: "";
    width: 0;
    height: 0;
    border-bottom: 10px solid transparent;
    border-top: 10px solid transparent;
        @include themify() {
            border-left: 20px solid themed("chat_bg");
        }
        position: absolute;
        right: -19px;
        top: 20px;
    @include themify() {
      border-left: 20px solid themed("chat_bg");
    }
    position: absolute;
    right: -19px;
    top: 20px;
  }
}
.borderTop {
    @include themify() {
        border-top: 1px solid themed("border_color");
    }
  @include themify() {
    border-top: 1px solid themed("border_color");
  }
}
.localKefu {
    overflow: auto;
    padding-bottom: 40px;
  overflow: auto;
  padding-bottom: 40px;
}
.van-nav-bar--fixed {
    position: relative !important;
  position: relative !important;
}
.bottom {
    margin-bottom: constant(safe-area-inset-bottom);
    margin-bottom: env(safe-area-inset-bottom);
  margin-bottom: constant(safe-area-inset-bottom);
  margin-bottom: env(safe-area-inset-bottom);
}
</style>