| New file |
| | |
| | | <template> |
| | | <div class="service-box flex flex-col pb-40"> |
| | | <iframe height="100%" src="https://cdn.chat20gm.cfd/chat_online/index?channelId=1958101585091772416"></iframe> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { |
| | | Uploader, |
| | | showImagePreview |
| | | } from 'vant' |
| | | import { |
| | | _getMsg, |
| | | _getUnreadMsg, |
| | | _sendMsg |
| | | } from '@/service/im.api' |
| | | import { |
| | | _uploadImage |
| | | } from '@/service/upload.api' |
| | | import { |
| | | ref, |
| | | onMounted, |
| | | onUnmounted, |
| | | onBeforeMount |
| | | } from "vue"; |
| | | import { |
| | | useI18n |
| | | } from "vue-i18n"; |
| | | import { |
| | | throttle |
| | | } from '@/utils/index' |
| | | import { |
| | | closeToast, |
| | | showToast, |
| | | showLoadingToast |
| | | } from "vant"; |
| | | import { |
| | | useRoute, |
| | | useRouter |
| | | } from 'vue-router'; |
| | | import { |
| | | getc2cOrder |
| | | } from "@/service/recharge.api.js"; |
| | | import { |
| | | useHomesStore |
| | | } from "@/store/homes.store"; |
| | | import { |
| | | SET_KEFU |
| | | } from "@/store/types.store"; |
| | | import { |
| | | useUserStore |
| | | } from '@/store/user'; |
| | | import dayjs from 'dayjs'; |
| | | import duration from 'dayjs/plugin/duration'; |
| | | dayjs.extend(duration); |
| | | |
| | | const userStore = useUserStore() |
| | | const { |
| | | t |
| | | } = useI18n() |
| | | const homesStore = useHomesStore(); |
| | | const router = useRouter() |
| | | const route = useRoute() |
| | | const list = ref([]) |
| | | const message = ref('') |
| | | const lastMsgId = ref('') |
| | | const interval = ref(null) |
| | | const unread = ref(0) |
| | | const finished = ref(false) |
| | | const isScrollBottom = ref(true) |
| | | const currentScrollTop = ref(0) |
| | | const androidAttrs = ref(null) |
| | | const navEl = ref(null); |
| | | const boxScrollEl = ref(null); |
| | | const navHeight = ref(0); |
| | | const payInfo = ref({}) |
| | | const remainingTime = ref(0) |
| | | |
| | | let state = ref(null) |
| | | let orderNo = "" |
| | | let partyId = "" |
| | | onMounted(() => { |
| | | // 美洽客服 |
| | | // const _ll="&id="+_id.value+"&name="+_name.value |
| | | // const _ll="" |
| | | // console.log(_ll) |
| | | // const _uull='https://cdn.chat20gm.cfd/chat_online/index?channelId=1958101585091772416'+_ll; |
| | | // window.location.href=_uull |
| | | |
| | | }) |
| | | // onMounted_bak(() => { |
| | | // orderNo = "" |
| | | // partyId = "" |
| | | // navHeight.value = navEl.value.$el.getBoundingClientRect().height |
| | | // if (route.query.order_no) { |
| | | // getc2cOrderDetails(route.query.order_no, (data) => { |
| | | // console.log("getc2cOrderDetails = " + JSON.stringify(data)) |
| | | // orderNo = data.orderNo; |
| | | // partyId = data.partyId; |
| | | // fetchList() |
| | | // }) |
| | | // } else { |
| | | // if (!homesStore.kefu_url) { |
| | | // fetchList() |
| | | // } |
| | | // } |
| | | // setInterval(() => { |
| | | // getCountdown() |
| | | // }, 1000) |
| | | // const model = navigator.userAgent; |
| | | // // 判断是否是安卓手机,是则是true |
| | | // androidAttrs.value = model.indexOf('Android') > -1 || model.indexOf('Linux') > -1 |
| | | // window.addEventListener('scroll', handleScroll, true) |
| | | // }) |
| | | |
| | | onBeforeMount(() => { |
| | | homesStore[SET_KEFU]() |
| | | }) |
| | | |
| | | //获取订单详情 |
| | | const getc2cOrderDetails = (orderNo, call) => { |
| | | getc2cOrder({ |
| | | order_no: orderNo |
| | | }).then((res) => { |
| | | payInfo.value = res |
| | | state.value = payInfo.value.state |
| | | remainingTime.value = res.autoCancelTimeRemain |
| | | if (call) { |
| | | call(res) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | //第三方客服带用户id |
| | | const generateExtranetLink = () => { |
| | | let extranetLink = '' |
| | | if (userStore.userInfo && userStore.userInfo.usercode) { |
| | | const userData = encodeURIComponent(JSON.stringify({ |
| | | name: userStore.userInfo.usercode, |
| | | comment: '' |
| | | })) |
| | | let params = `&clientid=${userStore.userInfo.usercode}&metadata=${userData}`; |
| | | extranetLink = homesStore.kefu_url + params; |
| | | } else { |
| | | extranetLink = homesStore.kefu_url |
| | | } |
| | | // extranetLink = homesStore.kefu_url + params; |
| | | console.log('generateExtranetLink', extranetLink) |
| | | // console.log('extranetLink',extranetLink) |
| | | return extranetLink; |
| | | } |
| | | |
| | | const throttleSend = throttle((message) => { |
| | | send('text', message) |
| | | }, 500) |
| | | |
| | | const onOversize = (file) => { |
| | | showToast(t('fileMaxLimit')); |
| | | } |
| | | const onPreview = (url) => { // 预览 |
| | | showImagePreview([url]) |
| | | } |
| | | const showTime = (index) => { // 时间显示 |
| | | if (index === 0) { |
| | | return true |
| | | } |
| | | if (index === list.value.length - 1) { |
| | | return false |
| | | } |
| | | if (list.value[index].createtime.split(' ')[0] === list.value[index + 1].createtime.split(' ')[1]) { |
| | | return false |
| | | } |
| | | } |
| | | const afterRead = (file) => { // 文件上传 |
| | | showLoadingToast({ |
| | | duration: 0 |
| | | }) |
| | | _uploadImage(file, (percent) => { |
| | | console.log(percent) |
| | | }).then(data => { |
| | | closeToast() |
| | | send('img', data) |
| | | }).catch(() => { |
| | | showToast(t('失败')) |
| | | }) |
| | | } |
| | | const fetchList = async (message_id = '') => { // 获取消息列表 |
| | | console.log("orderNo = " + orderNo); |
| | | console.log("partyId = " + partyId); |
| | | _getMsg({ |
| | | message_id |
| | | }, orderNo, partyId).then(data => { // |
| | | if (!lastMsgId.value) { |
| | | lastMsgId.value = data.length && data[data.length - 1]['id'] |
| | | } |
| | | if (data.length) { |
| | | if (message_id) { // 加载更多 |
| | | lastMsgId.value = data[data.length - 1]['id'] |
| | | list.value = [...data.reverse(), ...list.value] |
| | | } else { |
| | | list.value = [...list.value, ...data.reverse()] |
| | | let hash = {}; |
| | | list.value = list.value.reduce(function(preVal, curVal) { |
| | | hash[curVal.id] ? ' ' : hash[curVal.id] = true && preVal.push(curVal); |
| | | return preVal |
| | | }, []); |
| | | list.value.forEach((item, index) => { |
| | | data.forEach((item2, index2) => { |
| | | if (item.id === item2.id) { |
| | | item.delete_status = item2.delete_status |
| | | } |
| | | }) |
| | | }) |
| | | |
| | | } |
| | | |
| | | if (isScrollBottom.value) { |
| | | boxScrollEl.value.scrollTop = boxScrollEl.value.scrollHeight - boxScrollEl.value |
| | | .offsetHeight |
| | | } |
| | | currentScrollTop.value = boxScrollEl.value.scrollTop; |
| | | if (data.length < 10) { |
| | | finished.value = true |
| | | } |
| | | } |
| | | if (!message_id) { |
| | | clearIntervalTimer() |
| | | interval.value = setInterval(() => { |
| | | fetchList() |
| | | }, 1000) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const handleScroll = () => { |
| | | if (boxScrollEl.value) { |
| | | if (boxScrollEl.value.scrollTop === currentScrollTop.value) { |
| | | isScrollBottom.value = true |
| | | } else { |
| | | isScrollBottom.value = false |
| | | } |
| | | } |
| | | } |
| | | |
| | | const onMore = () => { // 加载更多 |
| | | fetchList(lastMsgId.value) |
| | | } |
| | | const clearIntervalTimer = () => { |
| | | if (interval.value) { |
| | | clearInterval(interval.value) |
| | | interval.value = null |
| | | } |
| | | } |
| | | const fetchUnread = () => { // 获取未读 |
| | | _getUnreadMsg(orderNo, partyId).then(data => { |
| | | unread.value = data |
| | | }) |
| | | } |
| | | const onClickLeft = () => { // 返回 |
| | | router.go(-1); |
| | | } |
| | | const send = (type = 'text', content = '') => { // 发送消息, img 也当消息text |
| | | if (!content) { |
| | | showToast(t('entryMessageContent')) |
| | | return |
| | | } |
| | | _sendMsg(type, content, orderNo, partyId).then(async data => { |
| | | message.value = '' |
| | | isScrollBottom.value = true |
| | | // document.getElementById('bottom').click() |
| | | // await fetchList() |
| | | }) |
| | | } |
| | | const getCountdown = () => { |
| | | if (remainingTime.value > 0) { |
| | | remainingTime.value = remainingTime.value - 1 |
| | | } else { |
| | | remainingTime.value = 0 |
| | | } |
| | | } |
| | | |
| | | onUnmounted(() => { |
| | | clearIntervalTimer() |
| | | }) |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | .service-box { |
| | | font-size: 14px; |
| | | width: 100%; |
| | | height: 100vh; |
| | | box-sizing: border-box; |
| | | background: $mainBgColor; |
| | | overflow: hidden; |
| | | |
| | | :deep(.van-hairline--bottom::after) { |
| | | border-color: $mainBgColor; |
| | | } |
| | | } |
| | | |
| | | .break-word { |
| | | word-wrap: break-word; |
| | | white-space: pre-wrap; |
| | | } |
| | | |
| | | .max-w-230 { |
| | | max-width: 115px; |
| | | } |
| | | |
| | | .responser { |
| | | position: relative; |
| | | |
| | | &::after { |
| | | content: ''; |
| | | width: 0; |
| | | height: 0; |
| | | border-top: 5px solid transparent; |
| | | border-bottom: 5px solid transparent; |
| | | border-right: 10px solid $input_background; |
| | | position: absolute; |
| | | left: -10px; |
| | | top: 10px; |
| | | } |
| | | } |
| | | |
| | | .borderTop { |
| | | border-top: 1px solid $border_color; |
| | | } |
| | | |
| | | .bottomBox { |
| | | height: 65px; |
| | | } |
| | | |
| | | .white { |
| | | color: $text_color; |
| | | } |
| | | |
| | | .chatBg { |
| | | background: $input_background; |
| | | height: 36px; |
| | | padding: 8px 18px; |
| | | border-radius: 18px; |
| | | color: $text_color; |
| | | font-size: 14px; |
| | | border: 1px solid $chat-border; |
| | | } |
| | | |
| | | .right-chatBg { |
| | | position: relative; |
| | | background: $color_main; |
| | | color: $text_color; |
| | | |
| | | &::after { |
| | | content: ''; |
| | | width: 0; |
| | | height: 0; |
| | | border-top: 5px solid transparent; |
| | | border-bottom: 5px solid transparent; |
| | | border-left: 10px solid $color_main; |
| | | position: absolute; |
| | | right: -8px; |
| | | top: 14px; |
| | | } |
| | | } |
| | | |
| | | .left-chatBg { |
| | | background: $input_background; |
| | | } |
| | | |
| | | .localKefu { |
| | | overflow: auto; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .van-nav-bar--fixed { |
| | | position: relative !important; |
| | | } |
| | | </style> |