123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- <template>
- <div class="chatting d-flex flex-column">
- <v-overlay
- :model-value="overlay"
- contained
- class="align-center justify-center"
- >
- <v-progress-circular
- color="primary"
- size="64"
- indeterminate
- ></v-progress-circular>
- </v-overlay>
- <div class="top-info">
- <div class="user-info d-flex align-center">
- <p class="d-flex align-center float-left">
- <span class="name">{{ info.name }}</span>
- <template v-if="info.enterpriseId">
- <span>{{ info.postNameCn }}</span>
- <span class="septal-line"></span>
- <span>{{ info.enterpriseName }}</span>
- </template>
- </p>
- </div>
- <!-- <div class="position-content" v-if="!isEnterprise">
- <div class="d-flex mb-2">
- <div class="font-weight-black">{{ info.name }}</div>
- <div class="position-content-emolument">{{ info.payFrom }} - {{ info.payFrom }}</div>
- </div>
- <div class="text-subtitle-2">{{ info?.enterprise?.name }}</div>
- <div class="pt-3">
- <v-chip
- v-for="(item, index) in info?.enterprise?.welfareList"
- :key="item + index"
- color="green"
- label
- class="mr-3 mb-3"
- >
- {{ item }}
- </v-chip>
- </div>
- </div> -->
- </div>
- <v-divider></v-divider>
- <div class="my-3 message-box" @scroll="handleScroll" ref="chatRef">
- <div>
- <div class="d-flex justify-center" v-if="hasMore">
- <v-btn :loading="loading" variant="text" color="primary" size="small" @click="handleMore">查看更多</v-btn>
- </div>
- <div v-for="(val, i) in items" :key="i" :id="val.id">
- <div class="time-box">{{ timesTampChange(+(val.timestamp.padEnd(13, '0'))) }}</div>
-
- <!-- <template v-if="val.payload.type === 102 && val.from_uid !== IM.uid"> -->
- <template v-if="val.payload.type === 102">
- <v-card
- color="teal"
- variant="tonal"
- class="mx-auto"
- width="400"
- min-height="150"
- :elevation="3"
- >
- <div class="pa-3">
- <div class="text-h6"> {{ val.payload.content.positionInfo.name }}</div>
- <div class="text-subtitle-2">薪酬待遇: {{ val.payload.content.positionInfo.payFrom }} - {{ val.payload.content.positionInfo.payTo }}</div>
- <div>
- <v-chip
- color="secondary"
- v-for="(v, i) in val.payload.content.positionInfo.enterprise.welfareList"
- :key="val.message_id + v + i"
- x-small
- class="mt-1 mr-1"
- >
- {{ v }}
- </v-chip>
- </div>
- <v-divider class="my-3"></v-divider>
- <div class="text-subtitle-2 text-right">
- <v-avatar size="24">
- <v-img :src="val.payload.content.positionInfo.contact.avatar"></v-img>
- </v-avatar>
- {{ val.payload.content.positionInfo.contact.name }}
- {{ val.payload.content.positionInfo.contact.postNameCn }}
- {{ val.payload.content.positionInfo.enterprise.name }}
- </div>
- <div class="text-subtitle-2 text-right">
- 地址:{{ val.payload.content.positionInfo.address }}
- </div>
-
-
- </div>
- </v-card>
- </template>
- <div :class="['message-view_item', val.from_uid === IM.uid ? 'is-self' : 'is-other']">
- <div style="width: 40px; height: 40px;">
- <v-avatar>
- <v-img
- :src="val.from_uid === IM.uid ? mAvatar : info.avatar"
- :width="40"
- height="40"
- rounded
- ></v-img>
- </v-avatar>
- </div>
- <!-- 显示沟通职位 -->
- <template v-if="val.payload.type === 102">
- <div class="message-text" :class="{ active: val.from_uid === IM.uid}">
- {{ val.payload.content.text }}
- </div>
- </template>
- <!-- 发起职位 -->
- <div v-else-if="val.payload.type === 101">
- <v-chip
- class="ma-2"
- color="teal"
- label
- >
- 发起了面试邀请
- <v-icon icon="mdi-email-newsletter" end></v-icon>
- </v-chip>
- </div>
- <div v-else class="message-text" :class="{ active: val.from_uid === IM.uid}">
- {{ val.payload.content }}
- </div>
- </div>
- <!-- 插入面试职位邀请 -->
- <div v-if="val.payload.type === 101" class="d-flex justify-center">
- <v-card
- color="teal"
- variant="tonal"
- class="mx-auto"
- min-width="400"
- min-height="150"
- :elevation="3"
- >
- <v-card-item>
- <div>
- <!-- {{val.payload.content}} -->
- <div class="text-overline mb-1">
- 面试邀请
- </div>
- <div class=" d-flex justify-space-between">
- <div class="text-h6 mb-1">
- {{ val.payload.content.positionInfo.data.name }}
- </div>
- <div>
- {{ val.payload.content.positionInfo.data.payFrom }} -
- {{ val.payload.content.positionInfo.data.payTo }}
- </div>
- </div>
- <!-- <div class="text-caption"></div> -->
- <div class="text-caption">面试时间: {{ timesTampChange(val.payload.content?.time) }}</div>
- <div class="text-caption">面试地点: {{ val.payload.content.address }}</div>
- <div class="text-caption">联系电话: {{ val.payload.content.invitePhone }}</div>
- </div>
- </v-card-item>
- <v-card-actions class="justify-end" v-if="val.from_uid !== IM.uid">
- <v-btn @click="handleAgree(val.payload.content)">
- 接受邀请
- </v-btn>
- </v-card-actions>
- </v-card>
- </div>
- </div>
- </div>
- </div>
- <!-- <v-divider></v-divider> -->
- <div class="tools pa-3" v-if="Object.keys(info).length > 0">
- <slot name="tools"></slot>
- <!-- <v-btn
- v-for="tool in tools"
- :key="tool.name"
- size="small"
- :prepend-icon="tool.icon"
- class="mr-3"
- @click="tool.handle"
- >
- {{ tool.name }}
- </v-btn> -->
- </div>
- <div class="bottom-info">
- <v-divider></v-divider>
- <div class="pa-3">
- <v-textarea
- v-model="inputVal"
- label="请输入消息"
- placeholder="请输入消息 按Ctrl+Enter换行"
- hide-details
- no-resize
- bg-color="white"
- variant="plain"
- :disabled="Object.keys(info).length === 0"
- @keydown="handleKeyDown"
- >
- <!-- @keydown.stop.prevent="handleKeyDown" -->
- <template #append-inner>
- <v-btn color="primary" :disabled="!inputVal" style="align-self: center;" @click="handleSend">发送</v-btn>
- </template>
- </v-textarea>
- <!-- <v-text-field v-model="inputVal" label="请输入消息" color="primary" density="comfortable" variant="plain" hide-details></v-text-field> -->
- <!-- <div class="d-flex align-center justify-end bottom-send">
- <div class="color-ccc font-size-14 mr-5">按Enter键发送</div>
- <v-btn color="primary" size="small" :disabled="!inputVal" @click="handleSend">发送</v-btn>
- </div> -->
- </div>
- </div>
- </div>
- </template>
- <script setup>
- defineOptions({ name: 'message-chatting'})
- import { ref, nextTick, onMounted } from 'vue'
- import { timesTampChange } from '@/utils/date'
- import { useIMStore } from '@/store/im'
- import { useUserStore } from '@/store/user'
- // const isEnterprise = inject('isEnterprise')
- const emits = defineEmits(['handleMore', 'handleSend'])
- defineProps({
- items: {
- type: Array,
- default: () => []
- },
- info: {
- type: Object,
- default: () => ({})
- },
- // uid: {
- // type: String,
- // default: ''
- // },
- hasMore: {
- type: Boolean,
- default: false
- }
- })
- const overlay = ref(false)
- const IM = useIMStore()
- const userStore = useUserStore()
- const loading = ref(false)
- const mAvatar = userStore.baseInfo?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'
- const chatRef = ref()
- const inputVal = ref('')
- const pullDowning = ref(false) // 下拉中
- const pulldownFinished = ref(false) // 下拉完成
- // const enterpriseTools = [
- // { name: '查看面试', icon: 'mdi-email-newsletter', handle: handleInquire },
- // { name: '面试邀约', icon: 'mdi-email', handle: handleInvite }
- // ]
- // const userTools = [
- // { name: '查看面试', icon: 'mdi-email-newsletter', handle: handleInquire }
- // ]
- // const tools = isEnterprise ? enterpriseTools : userTools
- // 查看
- // function handleInquire () {
- // console.log(props.info)
- // emits('handleInquire', props.info.userId, props.info.enterpriseId || undefined)
- // }
- // // 邀请
- // function handleInvite () {
- // emits('handleInvite', props.info.userId, props.info.enterpriseId || undefined)
- // }
- // 滚动到底部
- const scrollBottom = () => {
- const chat = chatRef.value
- if (chat) {
- nextTick(function () {
- chat.scrollTop = chat.scrollHeight
- })
- }
- }
- onMounted(() => {
- nextTick(() => {
- scrollBottom()
- })
- })
- const handleMore = () => {
- emits('handleMore')
- }
- const handleSend = () => {
- emits('handleSend', inputVal)
- }
- // const pullDown = async () => {
- // if (messages.value.length == 0) {
- // return
- // }
- // const firstMsg = messages.value[0]
- // if (firstMsg.messageSeq == 1) {
- // pulldownFinished.value = true
- // return
- // }
- // const limit = 15
- // const msgs = await WKSDK.shared().chatManager.syncMessages(to.value, {
- // limit: limit,
- // startMessageSeq: firstMsg.messageSeq - 1,
- // endMessageSeq: 0,
- // pullMode: PullMode.Down,
- // })
- // if (msgs.length < limit) {
- // pulldownFinished.value = true;
- // }
- // if (msgs && msgs.length > 0) {
- // msgs.reverse().forEach((m) => {
- // messages.value.unshift(m)
- // })
- // }
- // nextTick(function () {
- // const chat = chatRef.value
- // const firstMsgEl = document.getElementById(firstMsg.clientMsgNo)
- // if (firstMsgEl) {
- // chat.scrollTop = firstMsgEl.offsetTop
- // }
- // })
- // }
- const handleScroll = (e) => {
- const targetScrollTop = e.target.scrollTop;
- if (targetScrollTop <= 250) {
- // 下拉
- if (pullDowning.value || pulldownFinished.value) {
- return
- }
- console.log('下拉')
- pullDowning.value = true
- // pullDown().then(() => {
- // pullDowning.value = false
- // }).catch(() => {
- // pullDowning.value = false
- // })
- }
- }
- const changeOverlay = (val) => {
- overlay.value = val
- }
- const changeLoading = (val) => {
- loading.value = val
- }
- const handleKeyDown = (event) => {
- if (event.keyCode === 13) {
- event.preventDefault()
- if(event.ctrlKey) {
- // 换行
- inputVal.value += '\n'
- return
- }
- // 发送
- emits('handleSend', inputVal)
- }
- }
- const reset = () => {
- inputVal.value = ''
- }
- const handleAgree = (val) => {
- // const { positionInfo, ...obj } = val
- // console.log(positionInfo)
- emits('handleAgree', val)
- }
- defineExpose({
- reset,
- changeLoading,
- scrollBottom,
- changeOverlay
- })
- </script>
- <style scoped lang="scss">
- .chatting {
- position: relative;
- height: 100%;
- }
- .top-info {
- // height: 104px;
- padding: 0 30px;
- .user-info {
- position: relative;
- height: 48px;
- justify-content: space-between;
- // padding: 0 30px;
- span {
- font-size: 14px;
- font-weight: 400;
- // color: var(--color-666);
- line-height: 22px;
- }
- .name {
- line-height: 25px;
- margin-right: 20px;
- }
- }
- .position-content {
- position: relative;
- padding: 16px;
- border-radius: 12px;
- // margin: auto;
- width: 60%;
- max-width: 800px;
- // width: 760px;
- background: linear-gradient(90deg, #d5ffff, #f0fff4);
- .salary {
- font-size: 20px;
- font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
- color: var(--v-error-base);
- line-height: 24px;
- }
- &-emolument {
- color: #f30;
- font-weight: 600;
- }
- }
- }
- .message-box {
- flex: 1;
- padding: 0 30px 30px;
- overflow-y: auto;
- .time-box {
- user-select: none;
- position: relative;
- top: 8px;
- margin: 20px 0;
- max-height: 20px;
- text-align: center;
- font-weight: 400;
- font-size: 12px;
- color: var(--color-time-divider);
- }
- .message-view_item {
- display: flex;
- flex-direction: row;
- align-items: flex-start;
- margin: 8px 0;
- position: relative;
- .message-text {
- overflow-wrap: break-word;
- background-color: #f0f2f5;
- border-radius: 6px;
- max-width: 85%;
- padding: 10px;
- &.active {
- background: #d5e6e8;
- }
- }
- }
- .is-self {
- flex-direction: row-reverse;
- display: flex;
- .message-text {
- margin-right: 10px;
- }
- }
- .is-other {
- .message-text {
- margin-left: 10px;
- }
- }
- }
- .bottom-info {
- width: 100%;
- height: 160px;
- background-color: #fff;
- }
- input {
- outline: none;
- width: 748px;
- height: 60px;
- max-width: 748px;
- overflow: auto;
- white-space: pre-wrap;
- &:focus {
- border: none;
- }
- }
- /* 滚动条样式 */
- ::-webkit-scrollbar {
- -webkit-appearance: none;
- width: 10px;
- height: 0px;
- }
- /* 滚动条内的轨道 */
- ::-webkit-scrollbar-track {
- background: rgba(0, 0, 0, 0.1);
- border-radius: 0;
- }
- /* 滚动条内的滑块 */
- ::-webkit-scrollbar-thumb {
- cursor: pointer;
- border-radius: 5px;
- background: rgba(0, 0, 0, 0.15);
- transition: color 0.2s ease;
- }
- </style>
|