Jelajahi Sumber

0820-1206-h01

Zory 1 bulan lalu
induk
melakukan
470a883d8a

+ 28 - 0
src/api/model/serChat.js

@@ -37,4 +37,32 @@ export default {
             return await http.get(this.url, data);
         },
     },
+    line: {
+        url: `${config.API_URL}/chat/line`,
+        name: "-",
+        get: async function (data = {}) {
+            return await http.get(this.url, data);
+        },
+    },
+    lineSet: {
+        url: `${config.API_URL}/chat/line/set`,
+        name: "-",
+        post: async function (data = {}) {
+            return await http.post(this.url, data);
+        },
+    },
+    search: {
+        url: `${config.API_URL}/chat/search`,
+        name: "-",
+        get: async function (data = {}) {
+            return await http.get(this.url, data);
+        },
+    },
+    check: {
+        url: `${config.API_URL}/chat/check`,
+        name: "-",
+        post: async function (data = {}) {
+            return await http.post(this.url, data);
+        },
+    },
 }

+ 2 - 2
src/components/imFloat/imFloat.vue

@@ -496,8 +496,8 @@ export default {
 	}),
     mounted() {
         // console.log("pushObj")
-        this.initEmoji()
-        this.initPush();
+        // this.initEmoji()
+        // this.initPush();
         // this.simulateNewMessage()
     },
     beforeDestroy() {

+ 5 - 5
src/layout/index.vue

@@ -281,7 +281,7 @@ export default {
 			this.requestPermission()
 		}
 		if (this.userInfo.type == 3) {
-			this.startHeartbeat()
+			// this.startHeartbeat()
 		}
 	},
 	watch: {
@@ -300,10 +300,10 @@ export default {
 	},
 	methods: {
 		startHeartbeat() {
-			this.sendHeartbeat() // 立即发送一次
-			this.heartbeatTimer = setInterval(() => {
-				this.sendHeartbeat()
-			}, 45 * 1000)
+			// this.sendHeartbeat() // 立即发送一次
+			// this.heartbeatTimer = setInterval(() => {
+			// 	this.sendHeartbeat()
+			// }, 45 * 1000)
 		},
 		stopHeartbeat() {
 			if (this.heartbeatTimer) {

+ 51 - 9
src/views/service/chat/components/aside.vue

@@ -6,19 +6,23 @@
                 <div class="name">{{ userInfo.truename }}</div>
                 <div class="status">
                     <el-dropdown trigger="click" @command="handleUser">
-                        <div class="user-state">
+                        <div class="user-state" v-if="isLine==1">
                             <div class="names"><text class="success"></text>在线</div>
                             <el-icon class="el-icon--right"><el-icon-arrow-down /></el-icon>
                         </div>
+                        <div class="user-state" v-else>
+                            <div class="names"><text class="info"></text>离线</div>
+                            <el-icon class="el-icon--right"><el-icon-arrow-down /></el-icon>
+                        </div>
                         <template #dropdown>
                             <el-dropdown-menu>
-                                <el-dropdown-item command="clearmenu">
+                                <el-dropdown-item @click="setUserLine(1)">
                                     <div class="state-item">
                                         <div class="name"><text class="success"></text>在线</div>
                                         <div class="desc">可接待所有客户咨询</div>
                                     </div>
                                 </el-dropdown-item>
-                                <el-dropdown-item divided command="clearmenu">
+                                <el-dropdown-item divided @click="setUserLine(0)">
                                     <div class="state-item">
                                         <div class="name"><text class="info"></text>离线</div>
                                         <div class="desc">无法接收和发送消息</div>
@@ -31,7 +35,7 @@
             </div>
         </div>
         <div class="sub-fitter">
-            <div class="search">
+            <div class="search" @click="searchData">
                 <div class="icon"><el-icon><el-icon-search /></el-icon></div>
                 <div class="info">用户昵称/聊天记录/订单号</div>
             </div>
@@ -87,10 +91,15 @@
             </div>
         </div>
     </div>
+    <searchMain ref="searchMain" @success="searchUser"></searchMain>
 </template>
 
 <script>
+import searchMain from './search'
 export default {
+    components: {
+        searchMain
+    },
     data () {
         return {
             userLoading:true,
@@ -103,17 +112,48 @@ export default {
                 end:true
             },
             chatType:1, // 1当前(今天)2历史
-            checkChat:0
+            checkChat:0,
+            isLine:0
         }
     },
     props: {
         userInfo: { type: Object, default: () => {} }
     },
     created(){
-        this.loadMoreChat()
+        this.getUserLine();
+        this.loadMoreChat();
     },
     methods: {
-        updateItem(data){
+        searchUser(data){
+            var respData = {};
+            respData.item = data;
+            this.updateItem(respData);
+        },
+        searchData(){
+            this.$nextTick(() => {
+                this.$refs.searchMain.open()
+            })
+        },
+        async setUserLine(status){
+            if (status == this.isLine) {
+                return this.$message.error("当前操作无需操作");
+            }
+            var resp = await this.$API.serChat.lineSet.post({'status':status});
+            if (resp.code !== 1) {
+                return this.$message.error(resp.msg)
+            }
+            this.isLine = status;
+            this.$emit("line",this.isLine)
+        },
+        async getUserLine(){
+            var resp = await this.$API.serChat.line.get();
+            if (resp.code !== 1) {
+                return this.$message.error(resp.msg)
+            }
+            this.isLine = resp.data.line;
+            this.$emit("line",this.isLine)
+        },
+        updateItem(data,type = 'list'){
             if (this.chatType == 2) {
                 this.chatUserList.list = []
             }
@@ -127,7 +167,9 @@ export default {
                     item.last.content = this.truncateByBytes(data.content,12);
                     item.last.create_at = data.create_at
                     item.last.last_at = data.last_at
-                    item.last.num ++;
+                    if(type == 'list') {
+                        item.last.num ++;
+                    }
                 }
             }
         },
@@ -321,7 +363,7 @@ export default {
         align-items: center;
         cursor: pointer;
         .names {
-            font-size: 14px;
+            font-size: 12px;
             display: flex;
             align-items: center;
             gap: 5px;

+ 25 - 10
src/views/service/chat/components/asideRight.vue

@@ -6,7 +6,7 @@
                 <el-container v-loading="orderData.loading">
                     <el-header>
                         <div class="order-search-input">
-                            <el-input v-model="orderData.order" placeholder="搜索订单编号..." style="width: 100%;" />
+                            <el-input v-model="orderData.order" @keyup.enter="searchOrder()" clearable placeholder="搜索订单编号..." style="width: 100%;" />
                             <el-button @click="searchOrder">搜索</el-button>
                         </div>
                     </el-header>
@@ -36,12 +36,12 @@
                                     </div>
                                 </div>
                                 <div class="order-btn-group">
-                                    <div class="status primary cus" @click="sendAddress(item)" v-if="item.status=='1' && user.trade == '2'">发起预约</div>
-                                    <div class="status info cus" @click="editAddress(item)" v-if="item.status=='1' && user.trade == '2'">代客预约</div>
+                                    <div class="status primary cus" @click="sendAddress(item)" v-if="userInfo.trade == '2'">发起预约</div>
+                                    <div class="status info cus" @click="editAddress(item)" v-if="item.status=='1' && userInfo.trade == '2'">代客预约</div>
                                     <!-- <div class="status warn cus" v-if="item.product.product_type==11 && item.custom_price > 0 && item.custom_status ==0" @click="submitPrice(item)">补差价</div>
                                     <div class="status danger cus" v-if="item.product.product_type==11 && item.custom_price > 0 && item.custom_status ==0" @click="submitPrice(item)">改差价</div> -->
                                     <div class="status danger cus" @click="sendOrder(item)">发送订单</div>
-                                    <div class="status primary cus" @click="doneOrder(item)" v-if="item.status=='1' && user.trade == '1'">手动核销</div>
+                                    <div class="status primary cus" @click="doneOrder(item)" v-if="item.status=='1' && userInfo.trade == '1'">手动核销</div>
                                 </div>
                             </div>
                         </div>
@@ -103,12 +103,12 @@
                             </div>
                         </div>
                         <div class="order-btn-group">
-                            <div class="status primary cus" @click="sendAddress(msgOrder)" v-if="msgOrder.status=='1' && user.trade == '2'">发起预约</div>
-                            <div class="status info cus" @click="editAddress(msgOrder)" v-if="msgOrder.status=='1' && user.trade == '2'">代客预约</div>
+                            <div class="status primary cus" @click="sendAddress(msgOrder)" v-if="msgOrder.status=='1' && userInfo.trade == '2'">发起预约</div>
+                            <div class="status info cus" @click="editAddress(msgOrder)" v-if="msgOrder.status=='1' && userInfo.trade == '2'">代客预约</div>
                             <!-- <div class="status warn cus" v-if="msgOrder.product.product_type==11 && msgOrder.custom_price > 0 && msgOrder.custom_status ==0" @click="submitPrice(msgOrder)">补差价</div> -->
                             <!-- <div class="status danger cus" v-if="msgOrder.product.product_type==11 && msgOrder.custom_price > 0 && msgOrder.custom_status ==0" @click="submitPrice(msgOrder)">改差价</div> -->
                             <div class="status danger cus" @click="sendOrder(msgOrder)">发送订单</div>
-                            <div class="status primary cus" @click="doneOrder(msgOrder)" v-if="msgOrder.status=='1' && user.trade == '1'">手动核销</div>
+                            <div class="status primary cus" @click="doneOrder(msgOrder)" v-if="msgOrder.status=='1' && userInfo.trade == '1'">手动核销</div>
                         </div>
                     </div>
                 </div>
@@ -129,10 +129,15 @@
             </div> -->
         </div>
     </div>
+    <orderDetail ref="orderDetail" @success="orderSuccess"></orderDetail>
 </template>
 
 <script>
+import orderDetail from '@/components/imFloat/order';
 export default {
+    components:{
+        orderDetail
+    },
     data(){
         return {
             rightType:1,
@@ -163,6 +168,9 @@ export default {
         }
     },
     methods: {
+        searchOrder(){
+            this.getOrderList();
+        },
         async getMsgOrder(){
             if (!this.chatData) return ;
             this.msgOrderLoad = true;
@@ -187,11 +195,18 @@ export default {
             this.orderData.list = resp.data.rows;
             this.orderData.total = resp.data.total;
         },
-        viewOrder(data){},
-        sendAddress(data){},
+        viewOrder(data){
+            data.order = data.out_order_no;
+            this.$refs.orderDetail.open("add").setData(data)
+        },
+        sendAddress(data){
+            var orderData = {"img":data.img,"name":"完善会员信息","order":data.out_order_no,"status":0};
+            this.$emit("sendData",{'type':"address",data:JSON.stringify(orderData)})
+        },
         editAddress(data){},
         sendOrder(data){
-
+            var orderData = {"img":data.img,"name":data.product.product_name,"goods_id":data.life_goods_id,"order":data.out_order_no,"price":this.$TOOL.disMoney(data.pay_money),"status":data.status};
+            this.$emit("sendData",{'type':"order",data:JSON.stringify(orderData)})
         },
     }
 }

+ 22 - 2
src/views/service/chat/components/chatBody.vue

@@ -190,9 +190,10 @@ export default {
             uploadImg:"",
 			pushData: {
 				obj:null,
-				auth:"http://192.168.2.142:9881/plugin/webman/push/auth",
-				url: 'ws://192.168.2.142:3131',
+				auth:"http://127.0.0.1:9881/plugin/webman/push/auth",
+				url: 'ws://127.0.0.1:3131',
 			},
+            isLineState:0
         }
     },
 	computed: mapState({
@@ -234,6 +235,9 @@ export default {
         this.$refs.messageListRef.removeEventListener('scroll', this.handleScroll)
     },
     methods: {
+        setUserLine(state){
+            this.isLineState = state;
+        },
         initPush(){
             var _this = this;
             if (!this.pushData.obj) {
@@ -340,6 +344,9 @@ export default {
             }
         },
         async confirmUpload() {
+            if (this.isLineState == 0) {
+                return this.$message.error("当前状态无法进行该操作")
+            }
             if (!this.pendingFile) {
                 this.$message.warning('没有待上传的文件');
                 return;
@@ -382,6 +389,16 @@ export default {
             } else {
                 return ;
             }
+        },
+        async sendToRight(data){
+            if (!data) return;
+            var resp = await this.$API.serChat.text.post({"type":data.type,"content": JSON.parse(data.data),"groupId": this.chatData.poi_id,"openid":this.chatData.openid})
+            if (resp.code !== 1){
+                this.$message.error(resp.msg)
+                return false;
+            }
+            this.$emit("success",{'type':data.type,'content':data.data,'time':resp.data.time})
+            this.sendMessageData(JSON.parse(data.data),data.type);
         },
 		sendMessageData(content,type){
 			const message = {
@@ -400,6 +417,9 @@ export default {
 		},
         async sendMessage(){
             if (!this.inputMessage) return ;
+            if (this.isLineState == 0) {
+                return this.$message.error("当前状态无法进行该操作")
+            }
             var resp = await this.sendMessageApi("text",this.inputMessage);
             if (!resp) {
                 this.inputMessage = "";

+ 201 - 0
src/views/service/chat/components/search.vue

@@ -0,0 +1,201 @@
+<template>
+    <el-dialog :title="titleMap[mode]" v-model="visible" :width="800" :close-on-click-modal="false" append-to-body destroy-on-close>
+        <div v-loading="loading">
+            <div class="search-dialog-top">
+                <el-input clearable v-model="searchKey" placeholder="搜索" @keyup.enter="searchForm()">
+                    <template #append>
+                        <el-button icon="el-icon-search" @click="searchForm"></el-button>
+                    </template>
+                </el-input>
+            </div>
+            <div class="search-dialog-tab">
+                <el-tabs v-model="activeName" class="demo-tabs" @tab-change="handleClick">
+                    <el-tab-pane label="联系人" name="user"></el-tab-pane>
+                    <el-tab-pane label="聊天记录" name="msg"></el-tab-pane>
+                </el-tabs>
+            </div>
+            <div class="search-dialog-content">
+                <div class="empty-data" v-if="dataList.length == 0">
+                    <img src="https://lf-govern-cdn-tos.bytedcdn.com/obj/life-governance-cdn/life-im-pc/static/image/empty.252a622e.png" />
+                    暂无你想要的内容,请尝试换个内容试试
+                </div>
+                <div class="search-data-body" v-else>
+                    <template v-if="activeName == 'user'">
+                        <div class="search-user-list">
+                            <div class="user-item" v-for="(item,indx) in dataList" :key="indx" @click="checkUser(item)">
+                                <div class="icon"><el-image style="width: 40px;height: 40px;" :src="item.user.avatar"></el-image></div>
+                                <div class="name">
+                                    <span v-for="(part, idx) in splitHighlight(item.nickname)" :style="part.highlight ? { color: highlightColor, fontWeight: 'bold' } : {}">
+                                        {{ part.text }}
+                                    </span>
+                                </div>
+                            </div>
+                        </div>
+                    </template>
+                    <template v-else>
+                        <div class="search-user-list">
+                            <div class="user-item" v-for="(item,indx) in dataList" :key="indx" @click="checkUser(item)">
+                                <div class="icon"><el-image style="width: 40px;height: 40px;" :src="item.user.avatar"></el-image></div>
+                                <div class="name">
+                                    <div class="infos">{{ item.user.nickname }}</div>
+                                    <div class="text">
+                                        <span v-for="(part, idx) in splitHighlight(item.content)" :style="part.highlight ? { color: highlightColor, fontWeight: 'bold' } : {}">
+                                            {{ part.text }}
+                                        </span>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </template>
+                </div>
+                <div class="search-page">
+                    <el-pagination background small layout="prev, pager, next" hide-on-single-page="false" @current-change="pageChange" :total="searchData.total" />
+                </div>
+            </div>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+export default {
+    data(){
+        return {
+            loading: false,
+            isSaveing: false,
+            mode:"add",
+            titleMap:{
+                add:"搜索会话"
+            },
+            visible:false,
+            activeName:"user",
+            searchKey:"",
+            searchData:{
+                page:1,
+                size:10,
+                total:0
+            },
+            dataList:[],
+            highlightColor:"#1c5cfb"
+        }
+    },
+    methods: {
+        open(mode = 'add'){
+            this.mode = mode;
+            this.visible = true;
+            return this
+        },
+        pageChange(event){
+            this.searchData.page = event;
+            this.searchForm();
+        },
+        handleClick(event){
+            this.activeName = event;
+            this.dataList = [];
+            this.searchForm();
+        },
+        async checkUser(data){
+            var search = {};
+            search.openid = data.openid;
+            search.service_id = data.service_id;
+            search.poi_id = data.poi_id;
+            var resp = await this.$API.serChat.check.post(search);
+            if (resp.code !== 1) return ;
+            this.dataList = [];
+            this.searchData = {
+                page:1,
+                size:10,
+                total:0
+            }
+            this.visible = false;
+            this.activeName = "user";
+            this.$emit("success",resp.data[0])
+        },
+        async searchForm(){
+            if (!this.searchKey) return ;
+            this.loading = true;
+            var resp = await this.$API.serChat.search.get({"key":this.searchKey,'type':this.activeName,'page':this.searchData.page,'size':this.searchData.size});
+            this.loading = false;
+            if (resp.code !== 1) return ;
+            const respData = resp.data.data;
+            this.dataList = respData;
+            this.searchData.total = resp.data.total;
+        },
+        splitHighlight(name) {
+            if (!this.searchKey.trim()) return [{ text: name, highlight: false }];
+            const escaped = this.searchKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+            const regex = new RegExp(escaped, 'gi');
+            const parts = [];
+            let lastIndex = 0;
+            let match;
+            while ((match = regex.exec(name)) !== null) {
+                // 匹配前的普通文本
+                if (match.index > lastIndex) {
+                parts.push({
+                    text: name.slice(lastIndex, match.index),
+                    highlight: false,
+                });
+                }
+                // 匹配的关键词(高亮)
+                parts.push({
+                    text: match[0],
+                    highlight: true,
+                });
+                lastIndex = regex.lastIndex;
+            }
+            // 剩余普通文本
+            if (lastIndex < name.length) {
+                parts.push({
+                    text: name.slice(lastIndex),
+                    highlight: false,
+                });
+            }
+            return parts;
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.search-dialog-tab{
+    margin-top: 10px;
+}
+.search-user-list {
+    .user-item{
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        font-size: 14px;
+        cursor: pointer;
+        margin-top: 10px;
+        padding: 5px;
+        border-radius: 5px;
+        &:hover{
+            background-color: #f5fdff;
+        }
+        .text{
+            font-size: 12px;
+            margin-top: 5px;
+            color: #999;
+        }
+    }
+}
+.search-dialog-content{
+    .search-data-body{
+        height: 410px;
+        overflow: auto;
+    }
+    .search-page{
+        margin-top: 10px;
+    }
+}
+.empty-data{
+    display: flex;width: 100%;text-align: center;justify-content: center;font-size: 14px;align-items: center;
+    height: 410px;
+    color: #95989d;
+    flex-direction: column;
+    img {
+        width: 240px;
+        height: 240px;
+    }
+}
+</style>

+ 15 - 4
src/views/service/chat/web/index.vue

@@ -12,13 +12,13 @@
         <el-main class="nopadding">
             <el-container>
                 <el-aside width="300px">
-                    <asideMain ref="asideMain" :userInfo="userInfo" @chatItem="chatItem"></asideMain>
+                    <asideMain ref="asideMain" :userInfo="userInfo" @chatItem="chatItem" @line="isLine"></asideMain>
                 </el-aside>
                 <el-container>
                     <chatMain ref="chatMain" :userInfo="userInfo" :chatData="chatItemData" :msgData="msgData" @success="sendSuccess" @pushNew="newChatPush"></chatMain>
                 </el-container>
                 <el-aside width="400px">
-                    <rightMain ref="rightMain" :userInfo="userInfo" :chatData="chatItemData"></rightMain>
+                    <rightMain ref="rightMain" :userInfo="userInfo" :chatData="chatItemData" @sendData="sendData"></rightMain>
                 </el-aside>
             </el-container>
         </el-main>
@@ -39,7 +39,8 @@ export default {
             chatItemData:null,
             chatItemIndex:null,
             msgData:null,
-            newChat:null
+            newChat:null,
+            isLineState:0
         }
     },
     beforeDestroy() {
@@ -59,11 +60,21 @@ export default {
             this.chatItemData = data;
             this.chatItemIndex = index;
         },
+        isLine(state){ // 在线状态
+            this.isLineState = state;
+            this.$refs.chatMain.setUserLine(state)
+        },
         newChatPush(data){
-            this.$refs.asideMain.updateItem(data,this.chatItemIndex);
+            this.$refs.asideMain.updateItem(data,'msg');
         },
         sendSuccess(data) {
             this.$refs.asideMain.sendSuccess(data,this.chatItemIndex);
+        },
+        sendData(data){
+            if (this.isLineState == 0) {
+                return this.$message.error("当前状态无法进行该操作")
+            }
+            this.$refs.chatMain.sendToRight(data)
         }
     }
 }