关于video的一些思路心得

前言:在写聊天页面时,由于除了文字内容以外,还可以发送图片、视频之类。

而作为用户端聊天页面,有时候不是只有app在使用,浏览器也会打开它。

video标签虽然是作为播放使用,但不同浏览器或多或少对video标签有所改动或影响。

vivo 自带浏览器:只能听声音而无视频,但是有时候又能正常播放(必发)

谷歌浏览器:播放遵从video 自带属性,当video全局播放关闭后,层级将高于body ,导致向下滚动时容器浮动在底部输入框之上。 z-index层级是由video内置决定,此外其他浏览器也各自存在类似此问题。

火狐浏览器:在播放时容易引起黑屏。

UC浏览器:播放完成后,需要两次物理返回才能退出黑屏到正式页,为了避免体验不好,推荐播放时直接新开页面: window.open('视频地址', '_blank') 。(推荐此做法,后面浏览器开播放我干脆就改成用这种在,但是微信/app  这两者打开浏览地址,如果video播放  又会造成一次新开页面,返回时引起 WebSocket重新链接,由于不太清楚里面页面重开的机制,所以此处我使用我原来的逻辑: 列表渲染的  视频图片和播放按钮由后端提供海报,前端提供播放 ,在列表for循环容器外,再造一个video容器 ,此容器只有在点击事件触发时才会展示 (video 自带的层级很高),一般隐藏。再借用 video自带的方法播放,传入点击地址,使容器点击后播放,监听容器变化后  再关闭)

百度浏览器:自带预览图片功能,和vant 组件的预览会产生冲突,故做判断:

var sUserAgent = navigator.userAgent.toLowerCase();

  if (sUserAgent.indexOf('baidu') > 0) { //百度自带预览引擎  容易与vant 的插件冲突

    state.ImgisShow = false;

  }

如果 不是百度浏览器 那么执行

const imgSize = (img: any) => { //预览图片
  if (!state.ImgisShow) { // 当百度浏览器开启时,自带预览 这时禁用 vant 插件
    console.log('当前为百度浏览器,故关闭vant 预览',)
    return
  }
  state.img_url = img;
  document.getElementById("content")!.style.opacity = '0';
  document.getElementById("hhh")!.style.opacity = '0';
  let newImg = [img];
  ImagePreview({
    images: newImg,
    onClose() {//部分手机app打开页面时(小米) 发现预览图片打开后,内容依旧会展示出来,但是在华为和vivo上没有这种问题  故做隐藏处理【此处 z-index设定不生效!!!】
      console.log('预览图片关闭了');
      document.getElementById("hhh")!.style.opacity = '1'; 
      document.getElementById("content")!.style.opacity = '1';
    },
  });
};

完整代码 

<script lang="ts" setup>
import {
  ref,
  reactive,
  nextTick,
  getCurrentInstance,
  Ref,
  onBeforeMount,
  onMounted,
  watch,
  computed,
} from "vue";
import Cookies from "js-cookie";
import { Sticky, Toast, ImagePreview } from "vant"; //注册弹窗
import "vant/es/toast/style"; //vant 使用时  ts文件需要样式引入
import showImagePreview from 'vant';
import { newsChatTime } from "./config/time"; //引入时间戳 方法
import service from "./config/request"; //图片上传 接口配置
import { setParams } from "./config/oss"; //上传时的文件名配置
import emoji from "./config/emoji"; //表情包
import { chatsApi } from "./api/index"; //常用接口
import { indexOf, stubString, toNumber } from "lodash";
import { da } from "element-plus/lib/locale";

import { getParamsNew, onScroll } from './config/appBuse';

const wsurl: string = "wss://alphatur-kefu-ws.fangpian-h5.com:8900"; //wss地址
let channelId: any = 1001; //渠道id
let token: any = "";
let userId: any = 0;
let langs: any = "";
let sendmsg: string | undefined = ""; //输入的内容

let content: any = ""; //您好~
let content2: any = ""; //正在为您连接人工客服,请你耐心等待
let content3: any = ""; //人工客服为您服务,请问有什么可以帮您呢?

let _this: any = []; //获取vue实例
const { proxy }: any = getCurrentInstance();
_this = proxy;

let imgList = [
  "img1.png",
  "img2.png",
  "img3.png",
  "img4.png",
  "img5.png",
  "img6.png",
];
let Auatar: string = imgList[Math.round(Math.random() * 6)]; //用户头像 (随机抽取)
Auatar = Auatar != undefined ? Auatar : imgList[0];
console.log('Auatar', Auatar)
let Auatar2: string = '';
const chatApi = chatsApi();

let kefuId: number | string = ""; //客服id
let kefuAuatar: string = "kefu.png"; //客服头像

let heightTemp: Ref<string> = ref(""); //浏览器容器高度
let heightTemp2: Ref<string> = ref(""); //聊天消息列表高度  视口高度- 底部输入框60px

let widthTemp: Ref<string> = ref("");//屏幕宽度

const emojiList = emoji; //客服
const ws = new WebSocket(wsurl); //连接
let touchTop: number = 0;
let startTop: number = 0;

let king = ref('0');//声明一个下拉状态  0为待下拉(下拉查看历史记录消息)  1为加载中  2为加载完成(没有更多了)

var startx, starty;

let inWX = /micromessenger\s*\//i.test(navigator.userAgent);
console.log('inWx', inWX);
let url: any = window.location.href;
let inAPP = url.indexOf('Agen101') == '-1' ? false : true; //存在说明 是app开启的  不存在说明不是app打开的
console.log('inAPP', inAPP)
//定义接口
interface StateInter {
  list: any[];
  isSend: boolean;
  sendLimit: boolean;
  showEmoji: boolean;
  isShow: boolean;
  isFlag: boolean;
  isPc: boolean;
  defaultPhoneHeight: number; //屏幕默认高度
  nowPhoneHeight: number; //屏幕现在的高度
  isImg: string;
  Lis: boolean;
  linshi: any;

  video_url: any;
  img_url: any;
  ImgisShow: boolean,
  Isgo: boolean,
}
//全局变量
const state = reactive<StateInter>({
  list: [], //内容列表
  isSend: false, //是否显示发送按钮
  sendLimit: false, //发送状态
  showEmoji: false,
  isShow: true,
  isFlag: false, //用于判断视频是否打开
  isPc: false, //用于判断  当前设备打开的是否是Pc端
  defaultPhoneHeight: 0, //屏幕默认高度
  nowPhoneHeight: 0, //屏幕现在的高度
  isImg: "",
  Lis: false,//初次进入页面让他下拉  后面以滚动形式来实现
  linshi: '',
  video_url: '', //视频
  img_url: '',//图片
  ImgisShow: true, //预览插件是否生效  默认生效
  Isgo: true,//由于没做异步,所以 当滚动到顶部  可能造成重复的时间戳传入接口 (网络延迟情况) 
});
const checkFull = () => { //判定是开启全屏了  还是关闭全屏了
  let isFull = document.fullscreenElement ? true : false;
  if (isFull === undefined || isFull === null) isFull = false;
  return isFull;
};

//获得角度
function getAngle(angx, angy) {
  return Math.atan2(angy, angx) * 180 / Math.PI;
};

//根据起点终点返回方向 1向上 2向下 3向左 4向右 0未滑动
function getDirection(startx, starty, endx, endy) {
  var angx = endx - startx;
  var angy = endy - starty;
  var result = 0;

  //如果滑动距离太短
  if (Math.abs(angx) < 2 && Math.abs(angy) < 2) {
    return result;
  }

  var angle = getAngle(angx, angy);
  if (angle >= -135 && angle <= -45) {
    result = 1;
  } else if (angle > 45 && angle < 135) {
    result = 2;
  } else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
    result = 3;
  } else if (angle >= -45 && angle <= 45) {
    result = 4;
  }

  return result;
}
//手指接触屏幕
document.addEventListener("touchstart", function (e) {
  startx = e.touches[0].pageX;
  starty = e.touches[0].pageY;
}, false);

const getScroll = (event: any) => {
  // console.log('event', event)
  let scrollBottom = event.target.scrollHeight - event.target.scrollTop - event.target.clientHeight;
  console.log(scrollBottom); // 滚动到底部的距离
  if (scrollBottom <= 4) {
    // document.getElementById("msg")!.innerText += '到底了';
    // 判断滚动到底部时
    getHistoryMessage(state.list);
  }
};

//手指离开屏幕
document.addEventListener("touchend", function (e) {
  var endx, endy;
  endx = e.changedTouches[0].pageX;
  endy = e.changedTouches[0].pageY;
  var direction = getDirection(startx, starty, endx, endy);
  switch (direction) {
    case 0:
      // alert("未滑动!");
      break;
    case 1:
      // alert("向上!")
      break;
    case 2:
      // alert("向下!")
      console.log('向下滑动')
      // getDistance()
      // 可视区窗口高度 + 文档滚动高度 - 当前元素与页面顶部距离 - 当前元素高度
      // const height = window.innerHeight;
      if (state.Lis == false) {
        return
      }
      getHistoryMessage(state.list);
      break;
    case 3:
      // alert("向左!")
      break;
    case 4:
      // alert("向右!")
      break;
    default:
  }
}, false);

// 声明 存在一个设备
const equipment = () => {
  console.log("看下当前浏览器设备");
  let u = navigator.userAgent;
  if (
    u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/) ||
    u.indexOf("Android") > -1 ||
    u.indexOf("Linux") > -1
  ) {
    console.log("手机浏览器打开");
    (sendmsg = document.getElementById("msg")!.innerText += "手机浏览器打开")
  } else if (
    navigator.userAgent.match(
      /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
    )
  ) {
    console.log("移动端");
    (sendmsg = document.getElementById("msg")!.innerText += "移动端"),
      (state.isPc = false);
  } else {
    console.log("pc端");
    (sendmsg = document.getElementById("msg")!.innerText += "pc端"),
      (state.isPc = true);
  }
};
const setLanguage = (val: string) => {
  let params = {
    token: token,
    language: val,
  }
  chatApi.setLanguage(params).then((res) => {
    console.log('选择语言', res)
  })
}
onBeforeMount(() => { //挂载完成前
  channelId = getParamsNew("channelId");
  token = getParamsNew("token");
  userId = getParamsNew("userId");
  langs = getParamsNew("lang");
  console.log('token', token)
  if (langs && langs != undefined) {
    window.sessionStorage.setItem("lang", langs);
    _this.$i18n.locale = langs;
  }
  onScroll();//微信禁用 页面下滑
  let newLang = sessionStorage.getItem("lang");
  if (
    newLang === "zh-CN" ||
    newLang === "zh-cn" ||
    newLang === "zh" ||
    newLang === "ZH"
  ) {
    console.log("====zh===");
    document.title = "在线客服";
    content = "您好~";
    content2 = "正在为您连接人工客服,请您耐心等待";
    content3 = "人工客服为您服务,请问有什么可以帮您呢?";
    setLanguage('ZH')
  } else if (newLang === "en-CN" || newLang === "en-us" || newLang === "en") {
    console.log("====en===");
    document.title = "Online Service";
    content = "Hello~";
    content2 = "We are connecting to manual customer service for you. Please be patient and wait";
    content3 = "Human customer service is at your service. May I help you";
  } else {
    console.log("====tr====,newLang", newLang);
    document.title = "Çevrimiçi Hizmeti";
    content = "Merhaba~";
    content2 = "Sizin için müşteri hizmetine el bağlantı yapıyoruz. Lütfen sabırlı olun ve bekleyin.";
    content3 = "İnsan müşteri hizmeti sizin hizmetinizde. Size yardım edebilir miyim?";
    setLanguage('TUR')
  }

  initWebSocket(); // 初始化websocket
});

// 初始化websocket
const initWebSocket = () => {
  ws.onopen = (_status) => {
    console.log("socket 连接成功!!");
    // 连接建立之后执行send方法发送数据
    const actions = {
      target: "login",
      token: token,
      isKefu: "0",
    };
    console.log("actions ===", actions);
    sendMessage(actions);
  };
  ws.onclose = (status) => {
    console.log(status, "用户已掉线,当前对话已关闭");
    clearInterval(hearbearTime);
    alert(_this.$t('tips12'));
    console.log('关闭会话后,点击确定时,刷新页面');
    window.location.reload();
  };
  ws.onmessage = (message) => {
    // console.log('收到消息message', message)
    onMessage(message); //收到消息
  };
  ws.onerror = (err) => {
    console.log("失败", err);
  };
};
//收消息类型
interface OnmessageType {
  data: string;
  path?: string[];
  source: any;
  target: any;
  timeStamp: number;
  type: string;
}

//心跳
let hearbearTime: any;
const hearbeat = () => {
  hearbearTime = setInterval(() => {
    let HeartbeatData = {
      target: "heartbeat",
      token: token,
    };
    sendMessage(HeartbeatData);
  }, 3000);
};

//收到消息
const onMessage = (message: OnmessageType) => {
  // console.log("收到内容", message);
  let data = JSON.parse(message.data);
  if (data.code === 0 && data.target == "login") {
    hearbeat(); //心跳
    let ccc = typeof data.data == "string" ? JSON.parse(data.data) : data.data;
    let time = new Date().getTime().toString().slice(0, 10);
    if (ccc.avatar) {
      Auatar2 = ccc.avatar;
    }
    connectList({
      content, //您好~
      content2,//正在为您连接人工客服,请您耐心等候。
      contentType: 1,
      type: 2,
      time,
      // msg: _this.$t("text2"),
    });
  } else if (data.route && data.route == "onChat") {
    //接收消息
    let ccc = typeof data.data == "string" ? JSON.parse(data.data) : data.data;
    if (ccc.contentType == "2" || ccc.contentType == "3") {
      // 解密操作
      let param2 = {
        content: ccc.message, //内容
        token: token,
        contentType: ccc.contentType, //2图片 3 视频
      };
      console.log("看下操作解密", param2);
      chatApi.Signoss(param2).then((res: any) => {
        // 传参成功后,将获取到一个临时 解密的url字段路径,用于临时访问
        if (res.code == 0) {
          let sendData2 = {
            time: new Date().getTime().toString().slice(0, 10),
            contentType: ccc.contentType,
            message: res.data.url,
            type: 2, //用于插入操作 判断当前数据为客服 发出
            coverUrl: res.data.coverUrl,
          };
          console.log("解密成功了没", sendData2);
          connectList(sendData2); //本地插入操作
        }
      });
    } else {
      connectList(ccc);
    }
  } else if (data.route && data.route == "makeNewSession") {
    let index = '';
    state.list.forEach((item, indet) => {
      console.log('item', item)
      if (item.message && item.message == _this.$t('text2')) {
        console.log('里头有通知')
        index = '0';
      }
      if (indet == state.list.length - 1 && index != '0') {
        let sendData2 = {
          time: new Date().getTime().toString().slice(0, 10),
          contentType: 1,//文字
          message: _this.$t('text2'),
          type: 2, //用于插入操作 判断当前数据为客服 发出
        };
        state.list.unshift(sendData2);
        document.getElementById("lists")!.scrollTop = 0;
      }
    })
  }
};
const renderResize = () => {
  console.log('尺寸发生了变化');
  heightTemp.value = window.innerHeight + "px";
  widthTemp.value = window.innerWidth - 138 + 'px';

  let shuru: any = document.getElementById("shuru")?.offsetHeight; //输入框
  document.getElementById("app")!.style.height = heightTemp.value;
  document.getElementById("KefuBox")!.style.height = heightTemp.value;
  document.getElementById("lists")!.style.height = window.innerHeight - shuru + "px"; //消息记录 组件容器高度
  // document.getElementById("zhezhao3")!.style.height = heightTemp.value;//遮罩容器
};
onMounted(() => {
  renderResize()
  window.addEventListener("resize", renderResize, false); //监听页面尺寸 当页面发生翻转后重新重新赋予 容器高度
  var sUserAgent = navigator.userAgent.toLowerCase();
  if (sUserAgent.indexOf('baidu') > 0) { //百度自带预览引擎  容易与vant 的插件冲突
    state.ImgisShow = false;
  }
  //监听输入的内容,修改div的值和监听动态显示按钮
  document.getElementById("msg")?.addEventListener("input", () => {
    sendmsg = document.getElementById("msg")?.innerText;
    let reg = /^\s*$/g; //正则校验,是否输入为空
    let shuru: any = document.getElementById("shuru")?.offsetHeight; //内容高度
    console.log('监听输入后,底部容器的高度变化', shuru)
    document.getElementById("lists")!.style.height = window.innerHeight - shuru + 'px'; //消息容器

    if (
      String(sendmsg).length > 0 &&
      sendmsg != "" &&
      !reg.test(String(sendmsg))
    ) {
      //输入大于0 就不展示上传图片
      state.isSend = true;
    } else {
      state.isSend = false;
    }
  });

  // 挂载完成后  从html 身上获取pause 播放关闭
  document.addEventListener("fullscreenchange", () => {
    // 监听到屏幕变化,在回调中判断是否已退出全屏
    if (!checkFull()) {
      state.linshi.pause();//关闭播放
      state.linshi.style.display = 'none' //隐藏
      document.getElementById("video_T")!.style.opacity = '0';
      state.linshi = [];
      state.video_url = '';
      state.isFlag = false;
    } else {
      console.log("触发了开启全屏");
      state.linshi.play();
      document.getElementById("video_T")!.style.opacity = '1';
      state.isFlag = true;
    }
  });
});

// 发送消息内容
interface SendType {
  target?: string;
  token?: string; // token
  message?: string;
  channelId?: number | string; //渠道id
  userId?: string; //用户id
}

//获取历史消息
// id 根据id 去获取历史内容
const getHistoryMessage = (last: any = "") => {
  // 在网络延迟情况下  上一条请求的时间戳与现在时间戳 为同一条(即,接口数据还没返回  而用户进行了 多次重复下拉操作)
  // 这时就需要禁止对接口的调用
  if (!state.Isgo) {
    return
  }
  king.value = '1'; //加载中
  state.Isgo = false;
  let time = last
    ? last[last.length - 1].createTime
    : new Date().getTime().toString().slice(0, 10);
  let id = last ? last[last.length - 1].id : "";
  console.log("time", time);
  let param = {
    userId: userId,
    token: token,
    lastTime: time,
    lastId: id,
  };
  chatApi.getMessageRecord(param).then((res: any) => {
    if (res.code == 0) {
      console.log("历史消息res", res);
      let list = res.data.list;
      king.value = list.length > 0 ? '0' : '2' // 0下拉查看历史记录  2没有更多了
      // let list = [res.data.list[0]];
      console.log("list", list);
      connectList(list.reverse()); //reverse  顺序变倒叙
    }
    state.Isgo = true;
  });
};
const sendMessage = (message: SendType) => {
  message.token = token;
  ws.send(JSON.stringify(message));

  ws.onmessage = (message) => {
    // console.log("发送成功后,接收 target", JSON.stringify(message.target));
    onMessage(message); //收到消息
  };
};
//视频、图片上传
const afterRead = (file) => {
  let type = ""; //存储上传类型
  let type2 = "";
  if (
    file.file.type == "video/mp4" || //.mp4文件
    file.file.type == "video/webm" || //.webm文件
    file.file.type == "flv" || //.flv文件
    file.file.type == "video/x-matroska" || //.mkv文件
    file.file.type == "video/quicktime" || //.mov文件
    file.file.type == "application/x-shockwave-flash" //.swf文件
  ) {
    type = "3"; //发送视频
    type2 = "3"; //oss 传参
  } else {
    type = "2"; //发送图片
    type2 = "2"; //oss 传参
  }
  // 此时可以自行将文件上传至服务器
  service({
    // url: "/Extend/Oss/JsConfig?type=1",
    url: `/getOSSJsConfig?type=${type2}&token=${token}`,
    method: "post",
  }).then((res) => {
    console.log("图片上传res", res);
    if (res.data.code === 0) {
      Toast.loading({
        duration: 0,
        forbidClick: true,
        message: _this.$t("text3"), // 视频、图片上传中
        className: "transform180",
      });
      let { formData, fileName } = setParams(file, res.data.data);

      service({
        url: res.data.data.domain,
        method: "post",
        data: formData,
        headers: {
          "Content-Type": "multipart/form-data",
        },
      })
        .then((response) => {
          console.log("response", response);
          console.log("contentType", type);
          let sendData = {
            time: new Date().getTime().toString().slice(0, 10),

            target: "Play/chat",
            userId: userId,
            token: token,
            // contentType: "2",
            contentType: type,
            channelId: channelId,
            message: `${res.data.data.domain}/${fileName}`,
            type: 1, //用于插入操作 判断当前数据为用户 发出
          };

          sendMessage(sendData); //wss发送操作

          // 解密操作
          let param2 = {
            content: `${res.data.data.domain}/${fileName}`, //内容
            token: token,
            contentType: type2, //2图片 3 视频
          };
          chatApi.Signoss(param2).then((res: any) => {
            // 传参成功后,将获取到一个临时 解密的url字段路径,用于临时访问
            if (res.code == 0) {
              let sendData2 = {
                time: new Date().getTime().toString().slice(0, 10),
                userId: userId,
                token: token,
                contentType: type,
                channelId: channelId,
                message: res.data.url,
                coverUrl: res.data.coverUrl,
                type: 1, //用于插入操作 判断当前数据为用户 发出
              };
              connectList(sendData2); //本地插入操作
            }
          });

          Toast.clear();
        })
        .catch((err) => {
          Toast.clear();
        });
    }
  });
};
watch(state.list, (newL, oldL) => {
  // let Fheight = window.innerHeight - 78; //可视区
  setTimeout(() => {
    let newHeight: any = document.getElementById("list_content")?.offsetHeight; //仅内容(历史记录)
    let hhh: any = document.getElementById('hhh')?.offsetHeight;//下拉提示
    console.log('hhh 下拉', hhh)
    let shuru: any = document.getElementById("shuru")?.offsetHeight; //底部(输入框)
    console.log('shuru===========', shuru)
    let newHeight2 = window.innerHeight - shuru; //历史记录box父 容器(历史记录)
    console.log('newHeight===== 内容高度', newHeight)
    console.log('newHeight2=====', newHeight2)
    if (newHeight2 <= newHeight + hhh) {
      console.log('容器小于内容高度')
      state.Lis = false;
      console.log('state.Lis', state.Lis)
    } else {
      console.log('容器大于内容高度')
      state.Lis = true;
      console.log('state.Lis', state.Lis)
    }
  }, 500)

})

//清除消息
const clearMsg = () => {
  sendmsg = "";
  state.isSend = false;
  document.getElementById("msg")!.innerText = "";
};
// 拼接list列表
const connectList = (params: any, reverse: boolean = false): void => {
  console.log("看下插入params", params);
  // state.isSend = false; //展示上传图标

  if (Array.isArray(params)) {
    params.forEach((e, i) => {
      if (params[i - 1] && e.createTime - params[i - 1].createTime > 300) {
        e.showTime = true;
      }
    });
    if (state.list.length === 0) {
      //第一次获取数据
      state.list = params;
    } else {
      //获取历史数据
      if (params.length === 0) {
        return;
      }
      state.list.push(...params);
    }
  } else {
    if (reverse) {
      state.list.push(params);
      console.log("插入头部", state.list);
    } else {
      state.list.unshift(params);
      console.log("插入底部", state.list);
      document.getElementById("lists")!.scrollTop = 0;
    }
  }
};

// 开启全屏视口播放
const video_play = (item: any) => {
  if (!inWX && !inAPP) {//只要不是微信和app打开页面 新开video
    window.open(item + '', "_blank");
  } else {
    state.video_url = item;
    let videoId = document.getElementById('video_T');
    state.linshi = videoId;
    if (videoId?.requestFullscreen) {
      console.log('开全屏播放')
      videoId.requestFullscreen();
      videoId.style.display = 'block';
    }
  }
};

//发送消息(用于文本类型)
const sendMsg = () => {
  if (sendmsg == "") {
    Toast(_this.$t("text4")); //请勿发送空内容
    return;
  }
  let sendData = {
    time: Number(new Date().getTime().toString().slice(0, 10)),

    target: "Play/chat",
    channelId: channelId,
    userId: userId,
    message: sendmsg, //wss 通过此字段发送图片或内容
    type: 1, //生成用户端插入时的判断参数
    content: sendmsg, //内容
    contentType: "1", //内容类型 1为文本  2图片
  };

  sendMessage(sendData); //发送内容
  connectList(sendData); //列表追加内容
  clearMsg(); //清除信息  //清除输入框内容
  // }
};

//打开表情选项
const openEmoji = () => {
  state.showEmoji = !state.showEmoji;
};
//添加表情
const addEmoji = (item, index) => {
  state.isSend = true;
  openEmoji(); //关闭表情选项
  document.getElementById("msg")!.innerText += replace_message(`[em_${index}]`);
  sendmsg = document.getElementById("msg")?.innerText;
};

//替换表情
const replace_message = (str) => {
  // console.log("看下内容str", str);
  str = str.replace(/\[em_([0-9]*)\]/g, (e, i) => {
    return emojiList[i];
  });
  return str;
};
const touchstartFun = (e) => {
  console.log("touchstartFun === 点击了屏幕", e);
  startTop = e.changedTouches[0].clientY;
};
const touchmoveFun = (e) => {
  console.log("touchmoveFun === 触摸了屏幕", e)
  // console.log(e.changedTouches[0].clientY)
  console.log('state.Lis', state.Lis)
  if (state.Lis == false) {
    return
  }
  let top = startTop - e.changedTouches[0].clientY;
  let scrollTop = document.getElementById("lists")!.scrollTop;

  touchTop = top < -60 ? -60 : top;
  if (scrollTop < window.innerHeight - 80) {
    nextTick(() => {
      document.getElementById("chatBox")!.style.top = touchTop + "px";
    });
  }
};
const touchendFun = (e) => { //松开后
  console.log("touchendFun === ", e);
  nextTick(() => {
    touchTop = 0;
    document.getElementById("chatBox")!.style.top = "0px";
  });
};

const imgSize = (img: any) => { //预览图片
  if (!state.ImgisShow) { // 当百度浏览器开启时,自带预览 这时禁用 vant 插件
    console.log('当前为百度浏览器,故关闭vant 预览',)
    return
  }
  state.img_url = img;
  document.getElementById("content")!.style.opacity = '0';
  document.getElementById("hhh")!.style.opacity = '0';
  let newImg = [img];
  ImagePreview({
    images: newImg,
    onClose() {
      console.log('预览图片关闭了');
      document.getElementById("hhh")!.style.opacity = '1';
      document.getElementById("content")!.style.opacity = '1';
    },
  });
};

</script>

<template>
  <div class="KefuBox" id="KefuBox">
    <div class="chilun">
      <!-- 历史记录(内容容器) -->
      <div class="centent_box" id="centent_box">
        <div id="chatBox" class="chat" @touchstart="touchstartFun" @touchmove="touchmoveFun" @touchend="touchendFun">
          <div class="chat_content" ref="classifyLayout" id="lists" @click="state.showEmoji = false"
            @scroll.passive="getScroll($event)">
            <!-- id= content 仅使用于隐藏展示内容-->
            <div id="content" class="chat_content_item">
              <!-- id="list_content" 仅判断于 内容(不含加载中)容器的高度 是否足够支持滚动  不支持提供触屏下滑效果 -->
              <div class="list_content" id="list_content">
                <div class="contents" v-for="(item, index) in state.list" :key="index">
                  <div class="time">
                    {{
                      newsChatTime(item.createTime) || newsChatTime(item.time)
                    }}
                  </div>
                  <div class="jus_center" v-if="item.method_name && item.method_name == 'tips'">
                    <div class="msg_tips">
                      <img class="msg_tips_image" :src="'.' + kefuAuatar" alt="" v-if="item.type == 2" />
                      <span class="msg_tips_name">{{ item.msg }}</span>
                    </div>
                  </div>

                  <!-- 我的消息 -->
                  <div class="message" v-if="item.type === 1">
                    <img class="message_image" :style="{ 'max-width': widthTemp }" :src="item.content || item.message"
                      v-if="item.contentType == '2'" @click.stop="imgSize(item.content || item.message)" alt="" />
                    <div class="videoBox" v-else-if="item.contentType == '3'">
                      <div class="zz" :style="{ 'width': widthTemp }"></div>
                      <img :src="item.coverUrl" alt="" class="message_image_other" :style="{ 'max-width': widthTemp }" />
                      <p class="iconfont icon-bofang" @click="video_play(item.content || item.message)"></p>
                    </div>
                    <p class="message_content" v-else>
                      {{ replace_message(item.content || item.message) }}
                    </p>
                    <img class="author" :src="Auatar || '/dist/src' + Auatar" v-real-img="Auatar2" />
                  </div>
                  <!-- 客服的消息 -->
                  <div class="message_other" v-if="!item.method_name && item.type === 2">
                    <img class="author_other" :src="kefuAuatar || '/dist/src' + kefuAuatar" alt="" srcset="" />
                    <img class="message_image_other2" :style="{ 'max-width': widthTemp }"
                      :src="item.content || item.message" v-if="item.contentType == '2'"
                      @click.stop="imgSize(item.content || item.message)" alt="" />
                    <div class="videoBox" v-else-if="item.contentType == '3'">
                      <div class="zz" :style="{ 'width': widthTemp }"></div>
                      <img :src="item.coverUrl" alt="" class="message_image_other" :style="{ 'max-width': widthTemp }" />
                      <p class="iconfont icon-bofang" @click="video_play(item.content || item.message)"></p>
                    </div>

                    <div class="flex_column message_content_other" v-else-if="item.content2">
                      <span>
                        {{ replace_message(item.content || item.message) }}
                      </span>
                      <span>{{ item.content2 }}</span>
                    </div>
                    <p class="message_content_other" v-else>
                      {{ replace_message(item.content || item.message) }}
                    </p>
                  </div>
                </div>
              </div>
            </div>
            <!-- 0 下拉查看历史消息  1加载中  2没有更多了-->
            <div class="loading-tips contents" id="hhh">
              {{ king == '0' ? _this.$t('text8') : king == '1' ? _this.$t('text10') : _this.$t('text9') }}
            </div>
          </div>
        </div>
        <!-- 加载中... -->
        <div class="ceshi">{{ _this.$t('text10') }}</div>
      </div>
      <van-popup v-model:show="state.showEmoji" round :overlay="false" position="bottom" :style="{ height: '40%' }"
        style="z-index: 5;">
        <div class="emoji_list">
          <template v-for="(item, index) in emojiList" :key="index">
            <span class="emoji_item" @click="addEmoji(item, index)">{{
              item
            }}</span>
          </template>
        </div>
      </van-popup>
    </div>

    <!-- bottom(输入框容器) -->
    <div class="bottom_box">
      <div class="shuru" id="shuru">
        <img class="emoji" src="./assets/emoji.png" alt="" @click="openEmoji" />
        <div class="input" contenteditable="true" :placeholder="_this.$t('text5')" id="msg"></div>
        <van-uploader :after-read="afterRead" multiple :max-count="1" v-if="!state.isSend">
          <img class="add" src="./assets/add.png" alt="" />
        </van-uploader>
        <!-- 发送 -->
        <div class="send" v-else @click="sendMsg">{{ _this.$t("send") }}</div>
      </div>
    </div>

    <!-- 播放容器 -->
    <video id="video_T" :src="state.video_url" controls class="topBanner" webkit-playsinline playsinline x5-playsinline
      x-webkit-airplay='allow' x5-video-player-type='h5' x5-video-orientation='portraint' x5-video-player-fullscreen=''
      style="display: none;">
      <!-- <p>当前浏览器与视频类型不兼容,如需播放请移至goole或其他浏览器</p> -->
    </video>
    <!-- preload="auto" /*这个属性规定页面加载完成后载入视频*/ -->
  </div>
</template>

<style scoped>
@import url("./fonts/iconfont.css");

#app {
  height: 100vh;
}

/deep/.van-popup--bottom {
  bottom: 4rem !important;
}

.van-list__loading {
  color: #fff;
}

.van-loading {
  transform: rotate(180deg) !important;
  text-align: center;
}

.emoji_list {
  /* margin-bottom: 4.625rem; */
}

/* 日期 */
.time {
  color: #999999;
  text-align: center;
  font-size: .8125rem;
}

* {
  box-sizing: border-box !important;
}

/* 上传中弹窗样式 */
.transform180 {
  transform-origin: 25% 50%;
  text-align: center;
}

.list_content {
  background-color: #f2f2f2;
}

.contents {
  transform: rotate(180deg);
}

.flex_column {
  flex: 1;
  display: flex;
  flex-direction: column;
}

/* 总容器盒子 */
.KefuBox {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  position: relative;
}

.chilun {
  flex: 1;
  display: flex;
  overflow: hidden;
  z-index: 4 !important;
  position: relative;
  /* 超出容器的内容将被隐藏 */
}

/* 历史消息容器 */
.centent_box {
  flex: 1;
  background: #f1f1f1;
  transform: rotate(180deg) !important;
  overflow-y: auto;
}

.centent_box::-webkit-scrollbar {
  width: 0;
}

.message {
  display: flex;
  justify-content: flex-end;
}

.message_other {
  display: flex;
}

.ceshi {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  text-align: center;
  height: 4.3125rem;
  line-height: 4.3125rem;
  transform: rotate(180deg);
  z-index: -1 !important;
  color: #969799;
  font-size: 0.9rem;
}

.message_image {
  max-height: 200px;
  max-width: 200px;
  margin: 0.9375rem 0;
  border-radius: 0.3125rem;
}

.message_image_other2,
.message_image_other {
  max-height: 12.5rem;
  border-radius: 0.3125rem;
}

.message_image_other2 {
  margin: 0.9375rem 0rem 0.9375rem 0.625rem;
}

.author_other {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
}

.message_content_other {
  background-color: #ffffff;
  border-radius: 0.125rem 0.6875rem 0.6875rem 0.6875rem;
  padding: 0.75rem 1.3125rem;
  font-size: 0.9375rem;
  color: #333333;
  margin: 0.9375rem 0rem 0.9375rem 0.625rem;
  word-break: break-all;
  /*  使中文和英文为一体,一起换行 */
  word-wrap: break-word;
  margin-right: 3.375rem;
  /* 使中文和英文分开换行 */
}

.message_content {
  background-color: #c1e6ff;
  border-radius: 0.6875rem 0.125rem 0.6875rem 0.6875rem;
  padding: 0.75rem 1.3125rem;
  font-size: 0.9375rem;
  color: #333333;
  /* font-weight: bold; */
  /* margin-right: 0.625rem; */

  word-break: break-all;
  /*  使中文和英文为一体,一起换行 */
  word-wrap: break-word;
  /* 使中文和英文分开换行 */
  margin-left: 3.375rem;
}

.author {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  margin-left: .625rem;
}

.jus_center {
  display: flex;
  justify-content: center;
}

.msg_tips {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #ffffff;
  width: 16.875rem;
  padding: 1.0625rem;
  border-radius: 0.8125rem;
  margin: 1.25rem 0;
}

.msg_tips_image {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
}

.msg_tips_name {
  font-size: 0.9375rem;
  color: #999999;
  margin-left: 0.75rem;
}

.time {
  color: #999999;
  text-align: center;
}

.chat {
  flex: 1;
  position: relative;
  display: flex;
  flex-direction: column;
  background-color: #f2f2f2;
}

.videoBox {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0.9375rem 0rem 0.9375rem 0.625rem;
  overflow: hidden;
  border-radius: 0.3125rem;
}

.videoBox p {
  position: absolute;
  font-size: 2.3rem;
  color: white;
  z-index: 2
}

.videoBox .zz {
  min-width: 6.25rem;
  position: absolute;
  background: #000;
  opacity: 0.5;
  height: 100%;
  border-radius: 0.375rem;
}

* {
  box-sizing: border-box !important;
}

.loading-tips {
  color: #888;
  height: 3.125rem;
  line-height: 3.125rem;
  font-size: .8125rem;
  text-align: center;
}

.chat_content {
  overflow-y: scroll;
  padding: 0 0.9375rem;
  display: flex;
  flex-direction: column;
}

.chat_content_item {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

/* bottom 底部容器 */
.bottom_box {
  width: 100vw;
  display: flex;
  flex-direction: column;
  position: relative;
  z-index: 6 !important;
}

.shuru {
  padding: 0.9375rem;
  box-sizing: border-box;
  display: flex;
  flex-direction: row;
  background: #ffffff;
  align-items: center;
  z-index: 2;
  margin-top: .1rem;
}

.emoji {
  width: 2.0625rem;
  height: 2.0625rem;
  margin-right: 0.9375rem;
}

.add {
  width: 2.0625rem;
  height: 2.0625rem;
  margin-left: 0.9375rem;
  margin-top: 0.25rem;
}

.input {
  flex: 1;
  outline: none;
  padding: 0.1875rem;
  font-size: 0.75rem;
  line-height: 1.5rem;
  padding: 0.125rem;
  word-wrap: break-word;
  overflow-x: hidden;
  overflow-y: auto;
  border-radius: 1.375rem;
  background-color: #f2f2f2;
  font-size: 0.9375rem;
  color: #999999;
  padding: 0.6875rem 0.8125rem;
  max-height: 6.25rem;
}

.input:empty::before {
  content: attr(placeholder);
}


.send {
  width: 4.375rem;
  border-radius: 0.25rem;
  height: 1.875rem;
  text-align: center;
  line-height: 1.875rem;
  color: #ffffff;
  background-color: #1191e7;
  margin-left: 0.9375rem;
}

.emoji_item {
  text-align: center;
  display: inline-block;
  width: 12.5%;
  height: 3.125rem;
  line-height: 3.125rem;
  font-size: 1.25rem;
}
</style>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值