123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- <template>
- <div id="index">
- <el-row>
- <el-col :span="24" class="main animate__animated animate__backInRight" v-loading="loading">
- <div class="w_1200">
- <el-container>
- <el-aside width="400px" class="aside">
- <el-col :span="24" class="one">
- <el-col :span="24" class="one_1">
- <el-col :span="4" class="left">
- <el-image class="image" :src="user.icon || kf" fit="fill" />
- </el-col>
- <el-col :span="20" class="right">
- {{ user.nick_name || '游客' }}
- </el-col>
- </el-col>
- <el-col :span="24" class="one_2">
- <a-input placeholder="查找联系人">
- <template #prefix><SearchOutlined /></template>
- </a-input>
- </el-col>
- </el-col>
- <el-col :span="24" class="two">
- <el-col
- :span="24"
- class="list"
- v-for="(item, index) in chatList"
- :key="index"
- @click="toView(item)"
- >
- <div class="line" v-if="item.user == id"></div>
- <el-col :span="4" class="left">
- <el-image
- class="image"
- v-if="item.icon && item.icon.length > 0"
- :src="item.icon[0].url"
- fit="fill"
- />
- <el-image class="image" v-else :src="kf" fit="fill" />
- </el-col>
- <el-col :span="20" class="right">
- <el-col :span="24" class="right_1">
- <div class="name">{{ item.nick_name }}</div>
- <div class="time">{{ item.send_time }}</div>
- </el-col>
- <el-col :span="24" class="right_2">{{ item.content }}</el-col>
- </el-col>
- </el-col>
- </el-col>
- </el-aside>
- <el-main style="background: #eee; padding: 0">
- <Chat></Chat>
- </el-main>
- </el-container>
- </div>
- </el-col>
- </el-row>
- <el-dialog v-model="dialog" title="智能客服">
- <el-col :span="24" class="dialog">
- <div class="content">
- <div class="title">智能客服为您服务</div>
- <div class="list">
- <el-image class="image" :src="kf" fit="fill" />
- <div class="message">Hi,遇到问题随时找智能客服哟~ 有什么需要我帮忙的吗?</div>
- </div>
- </div>
- <div class="foot">
- <textarea class="input" placeholder="输入消息"></textarea>
- <div class="button">
- <div class="send">发 送</div>
- </div>
- </div>
- </el-col>
- </el-dialog>
- </div>
- </template>
- <script setup>
- // 基础
- import moment from 'moment'
- import Chat from './parts/chat.vue'
- import { SearchOutlined } from '@ant-design/icons-vue'
- // 接口
- import { ChatStore } from '@/store/api/platform/chat'
- import { UsersStore } from '@/store/api/user/user'
- const store = UsersStore()
- const chatstore = ChatStore()
- import { UserStore } from '@/store/user'
- const userStore = UserStore()
- const user = computed(() => userStore.user)
- // 图片引入
- import kf from '@/assets/kf.png'
- // 路由
- const route = useRoute()
- // 加载中
- const loading = ref(false)
- // 是否弹框客服
- const dialog = ref(false)
- // 聊天者id
- const id = ref(route.query.id || '')
- // 聊天室
- const chatList = ref([])
- // 列表
- const list = ref([])
- let skip = 0
- let limit = inject('limit')
- // 搜索
- const searchForm = ref({})
- // 个人信息
- const info = ref({})
- // 消息
- const textarea = ref('')
- // 请求
- onMounted(async () => {
- loading.value = true
- await search()
- loading.value = false
- })
- const search = async () => {
- searchList({ skip, limit })
- if (id.value) {
- let res = await store.fetch(id.value)
- if (res.errcode == '0') info.value = res.data
- searchChat({ skip, limit })
- }
- }
- const searchList = async (query = { skip: 0, limit }) => {
- const info = {
- skip: query.skip,
- limit: query.limit,
- ...searchForm.value
- }
- const res = await chatstore.chat(info)
- if (res.errcode == '0') chatList.value = res.data
- }
- const toView = async (item) => {
- id.value = item._id
- info.value = item
- await searchChat({ skip, limit })
- }
- const searchChat = async (query = { skip: 0, limit }) => {
- const data = {
- skip: query.skip,
- limit: query.limit,
- user: info.value._id,
- ...searchForm.value
- }
- const res = await chatstore.query(data)
- if (res.errcode == '0') list.value = res.data
- }
- const toSend = async () => {
- const data = {
- sender_id: user.value._id,
- receiver_id: id.value,
- type: '0',
- content: textarea.value,
- send_time: moment().format('YYYY-MM-DD HH:mm:ss')
- }
- const res = await chatstore.create(data)
- if (res.errcode === 0) {
- textarea.value = ''
- searchChat()
- }
- }
- // provide
- provide('id', id)
- provide('list', list)
- provide('info', info)
- provide('textarea', textarea)
- provide('toSend', toSend)
- </script>
- <style scoped lang="scss">
- .main {
- margin: 10px 0;
- .aside {
- border-radius: 5px 0 0 5px;
- background: #33353a;
- .one {
- .one_1 {
- display: flex;
- align-items: center;
- background: #292b2e;
- padding: 10px;
- .left {
- image {
- width: 50px;
- height: 50px;
- border-radius: 50%;
- }
- }
- .right {
- margin: 0 0 0 10px;
- white-space: nowrap;
- color: #fff;
- font-size: 16px;
- }
- }
- .one_2 {
- padding: 10px;
- border-bottom: 1px solid #404247;
- background-color: hsla(0, 0%, 75.3%, 0.2);
- :deep(.ant-input-affix-wrapper) {
- color: #ccc !important;
- background-color: transparent;
- }
- :deep(.ant-input) {
- height: 32px;
- position: relative;
- caret-color: #fff;
- font-size: 14px;
- padding: 6px 0;
- border: none;
- outline: none;
- background-color: transparent;
- &::placeholder {
- color: #ccc !important;
- }
- }
- }
- }
- .two {
- overflow-y: auto;
- height: 57vh;
- .list {
- position: relative;
- display: flex;
- padding: 10px;
- border-bottom: 1px solid #2c2e31;
- .right {
- margin: 5px 0 0 10px;
- .right_1 {
- display: flex;
- justify-content: space-between;
- padding: 0 10px 10px 0;
- .name {
- color: #fff;
- font-size: 14px;
- }
- .time {
- color: #51555e;
- vertical-align: top;
- word-break: keep-all;
- font-size: 10px;
- }
- }
- .right_2 {
- color: #999;
- }
- }
- .line {
- position: absolute;
- background: #ff8f2c;
- width: 2px;
- left: 0;
- top: 0;
- height: 85px;
- }
- }
- .list:hover {
- background-color: #3a3c42;
- }
- }
- .two::-webkit-scrollbar {
- display: none;
- }
- }
- }
- :deep(.el-dialog__body) {
- padding: 0 !important;
- }
- .dialog {
- .content {
- padding: 20px;
- .title {
- padding-top: 23px;
- padding-bottom: 26px;
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: center;
- font-size: 14px;
- color: rgba(0, 0, 0, 0.45);
- }
- .list {
- display: flex;
- align-items: center;
- .image {
- width: 60px;
- height: 60px;
- border-radius: 60px;
- margin: 0 10px 0 0;
- }
- .message {
- position: relative;
- max-width: 330px;
- border-radius: 4px;
- font-size: 14px;
- line-height: 22px;
- box-sizing: border-box;
- color: rgba(0, 0, 0, 0.65);
- padding: 16px 11px 16px 16px;
- background: #ffffff;
- box-shadow: 0px 2px 10px 1px rgba(0, 0, 0, 0.12);
- }
- }
- }
- .foot {
- height: 140px;
- background: rgba(0, 0, 0, 0.04);
- border: 1px solid rgba(0, 0, 0, 0.15);
- padding: 13px 20px;
- display: flex;
- flex-direction: column;
- align-items: stretch;
- .input {
- flex: 1;
- height: 0;
- resize: none;
- border: none;
- outline: none;
- background: transparent;
- font-size: 14px;
- line-height: 22px;
- }
- .button {
- margin-top: 8px;
- flex-shrink: 0;
- display: flex;
- flex-direction: row;
- justify-content: flex-end;
- .send {
- cursor: pointer;
- color: #fff;
- background: #2f54eb;
- border-radius: 4px;
- width: 64px;
- height: 32px;
- font-size: 14px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- }
- }
- }
- </style>
|