Parcourir la source

Merge branch 'master' of http://www.sysuimars.cn:3000/feiniao/feiniao-farm-h5

lxf il y a 1 semaine
Parent
commit
c5f5d9a0ae

+ 1 - 6
package-lock.json

@@ -23,6 +23,7 @@
         "echarts": "^5.4.1",
         "element-plus": "^2.2.8",
         "es6-promise": "^4.2.8",
+        "html2canvas": "^1.4.1",
         "jquery": "^3.6.3",
         "js-base64": "^3.7.7",
         "mars3d": "^3.8.8",
@@ -6839,7 +6840,6 @@
       "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
       "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
       "license": "MIT",
-      "optional": true,
       "engines": {
         "node": ">= 0.6.0"
       }
@@ -8350,7 +8350,6 @@
       "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
       "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
       "license": "MIT",
-      "optional": true,
       "dependencies": {
         "utrie": "^1.0.2"
       }
@@ -11258,8 +11257,6 @@
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
       "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
-      "license": "MIT",
-      "optional": true,
       "dependencies": {
         "css-line-break": "^2.1.0",
         "text-segmentation": "^1.0.3"
@@ -19732,7 +19729,6 @@
       "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
       "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
       "license": "MIT",
-      "optional": true,
       "dependencies": {
         "utrie": "^1.0.2"
       }
@@ -20478,7 +20474,6 @@
       "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
       "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
       "license": "MIT",
-      "optional": true,
       "dependencies": {
         "base64-arraybuffer": "^1.0.2"
       }

+ 1 - 0
package.json

@@ -26,6 +26,7 @@
     "echarts": "^5.4.1",
     "element-plus": "^2.2.8",
     "es6-promise": "^4.2.8",
+    "html2canvas": "^1.4.1",
     "jquery": "^3.6.3",
     "js-base64": "^3.7.7",
     "mars3d": "^3.8.8",

+ 178 - 0
src/common/commonFun.js

@@ -0,0 +1,178 @@
+import html2canvas from "html2canvas";
+import { uploadBase64 } from "./uploadImg";
+import wx from "weixin-js-sdk";
+
+function detectRuntimeEnvironment() {
+    // Node.js环境
+    if (typeof process !== "undefined" && process.versions && process.versions.node) {
+        return "nodejs";
+    }
+
+    // 微信小程序环境
+    if (typeof wx !== "undefined" && wx && wx.getSystemInfo) {
+        return "wechat-miniprogram";
+    }
+
+    // WebView环境
+    if (typeof window !== "undefined" && typeof document !== "undefined") {
+        // 检查是否在微信内置浏览器中
+        const ua = navigator.userAgent.toLowerCase();
+        if (ua.match(/micromessenger/i)) {
+            return "wechat-webview";
+        }
+
+        return "browser";
+    }
+
+    // React Native环境
+    if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
+        return "react-native";
+    }
+
+    // 未知环境
+    return "unknown";
+}
+
+// 深拷贝
+function deepClone(obj) {
+    if (typeof obj !== "object" || obj === null) {
+        return obj;
+    }
+    const target = obj.constructor === Array ? [] : {};
+    for (var key in obj) {
+        if (Object.prototype.hasOwnProperty.call(obj, key)) {
+            if (typeof obj[key] === "object") {
+                target[key] = deepClone(obj[key]);
+            } else {
+                target[key] = obj[key];
+            }
+        }
+    }
+    return target;
+}
+
+function extractCoordinates(input) {
+    // 使用正则表达式匹配括号内的内容
+    const match = input.match(/\(([^)]+)\)/);
+
+    if (match) {
+        // 如果找到了匹配项,match[1] 将包含括号内的内容
+        // 然后我们按照空格分割这个字符串来获取坐标
+        const coordinates = match[1].split(" ").map(Number); // 将每个坐标转换为数字
+        return coordinates;
+    }
+
+    // 如果没有找到匹配项,返回null或其他合适的值
+    return null;
+}
+
+// 节流 => 一定时间内只调用一次函数
+function throttle(func, wait) {
+    let timeout = null;
+    let lastRun = 0;
+
+    return function (...args) {
+        const now = new Date().getTime();
+
+        if (!lastRun) {
+            // 如果 lastRun 没有被设置,表示这是第一次调用
+            func.apply(this, args);
+            lastRun = now;
+        } else {
+            clearTimeout(timeout);
+            // 设置一个新的超时,在 wait 时间后再次运行函数
+            timeout = setTimeout(() => {
+                if (now - lastRun >= wait) {
+                    func.apply(this, args);
+                    lastRun = now;
+                }
+            }, wait - (now - lastRun));
+        }
+    };
+}
+
+// 下载图片的函数
+async function downloadImage(downDom) {
+    if (downDom) {
+        const canvas = await html2canvas(downDom, {
+            useCORS: true, // 支持跨域图片
+            // scale: 2, // 提高生成图片分辨率
+        });
+        // 2. 创建最终合成的 Canvas
+        const finalCanvas = document.createElement("canvas");
+        const ctx = finalCanvas.getContext("2d");
+
+        // 设置最终图片大小
+        finalCanvas.width = canvas.width;
+        finalCanvas.height = canvas.height;
+
+        // 绘制 DOM 转换的内容
+        ctx.drawImage(canvas, 0, 0);
+
+        // 3. 加载本地二维码图片
+        const qrImage = new Image();
+        // qrImage.src = require("@/assets/img/weather_index/code.png"); // 本地二维码路径
+
+        // 等待二维码图片加载完成后绘制到 Canvas
+        qrImage.onload = () => {
+            const qrWidth = 48 * 3; // 二维码宽度
+            const qrHeight = 74 * 3; // 二维码高度
+            const padding = 8 * 3; // 边距
+
+            // 绘制二维码到最终 Canvas 的右上角
+            ctx.drawImage(
+                qrImage,
+                canvas.width - qrWidth - padding, // 右边距
+                padding, // 上边距
+                qrWidth,
+                qrHeight
+            );
+
+            const image = finalCanvas.toDataURL("image/png");
+
+            try {
+                uploadBase64(image);
+            } catch (error) {
+                console.error("上传失败:", error);
+            }
+        };
+        // // 创建下载链接
+        // const link = document.createElement("a");
+        // link.href = image;
+        // link.download = "每日三问.png";
+        // link.click();
+    }
+}
+
+function convertImage(imgUrl) {
+    return new Promise((resolve, reject) => {
+        fetch(imgUrl)
+            .then((response) => {
+                if (!response.ok) {
+                    throw new Error(`HTTP 错误:${response.status}`);
+                }
+                return response.blob(); // 获取图片的二进制数据
+            })
+            .then((blob) => {
+                const reader = new FileReader();
+
+                reader.onload = () => {
+                    // 转换成功后返回 Base64 数据
+                    resolve(reader.result); // 通过 resolve 返回 Base64 数据
+                };
+
+                reader.onerror = (error) => {
+                    console.error("文件读取失败:", error);
+                    reject(error); // 出现错误时通过 reject 抛出错误
+                };
+
+                reader.readAsDataURL(blob); // 将 Blob 数据转换为 Base64
+            })
+            .catch((error) => {
+                console.error("图片转换失败:", error);
+                reject(error); // 捕获错误并通过 reject 抛出
+            });
+    });
+}
+
+export { deepClone, extractCoordinates, throttle, downloadImage, convertImage, detectRuntimeEnvironment };

+ 52 - 0
src/common/uploadImg.js

@@ -0,0 +1,52 @@
+
+import UploadFile from "@/utils/upliadFile";
+import store from "@/store";
+import { base_img_url2 } from "@/api/config";
+import wx from "weixin-js-sdk";
+
+const miniUserId = store.state.home.miniUserId;
+
+
+const uploadFileObj = new UploadFile();
+
+function base64ToBlob(base64, mime = 'image/png') {
+    const byteCharacters = window.atob(base64.split(',')[1]); // 去掉 Base64 前缀
+    const byteNumbers = new Array(byteCharacters.length).fill(0).map((_, i) => byteCharacters.charCodeAt(i));
+    const byteArray = new Uint8Array(byteNumbers);
+    return new Blob([byteArray], { type: mime });
+}
+
+
+function base64ToFile(base64, filename = 'image.png') {
+    const blob = base64ToBlob(base64);
+    return new File([blob], filename, { type: blob.type });
+}
+
+
+async function uploadBase64(base64,isToPage = true) {
+    return new Promise((resolve, reject) => {
+        // 转为 File 对象
+        const file = base64ToFile(base64, 'upload.png');
+        let ext = 'png';
+        let key = `birdseye-look-mini/${miniUserId}/${new Date().getTime()}.${ext}`;
+        uploadFileObj.put(key, file).then((resFilename) => {
+            let imgUrl = base_img_url2 + resFilename
+            console.log('resFilename', base_img_url2 + resFilename);
+            if(isToPage){
+                wx.miniProgram.navigateTo({
+                    url: `/pages/subPages/share_img/index?imgUrl=${imgUrl}`,
+                });
+            }else{
+                if(resFilename){
+                    resolve(imgUrl);
+                }else{
+                    reject('失败')
+                }
+            }
+        });
+    })
+}
+
+
+
+export { uploadBase64 };

+ 1 - 1
src/components/chatWindow.vue

@@ -207,7 +207,7 @@ watch(()=>props.userId,(newValue) =>{
 })
 
 onDeactivated(()=>{
-    mqttClient.value.client.end(true);
+    // mqttClient.value.client.end(true);
 })
 
 // mqtt 连接

+ 12 - 2
src/router/globalRoutes.js

@@ -63,11 +63,21 @@ export default [
         name: "ExpertList",
         component: () => import("@/views/old_mini/home/subPages/expertList.vue"),
     },
-    // 识别结果
+    // 我的农场
+    {
+        path: "/my_farm",
+        name: "MyFarm",
+        component: () => import("@/views/old_mini/mine/pages/farm.vue"),
+    },
+    // 消息列表
+    {
+        path: "/message_list",
+        name: "MessageList",
+        component: () => import("@/views/old_mini/mine/pages/messageList.vue"),
+    },// 识别结果
     {
         path: "/recognize",
         name: "Recognize",
         component: () => import("@/views/old_mini/home/subPages/recognize.vue"),
     },
-
 ];

+ 1 - 1
src/views/old_mini/mine/index.vue

@@ -52,7 +52,7 @@ const gridItems = ref([
     {
         title: '我的农场',
         desc: '查看农场列表',
-        path: '/farm/list'
+        path: '/my_farm'
     },
     {
         title: '我的消息',

+ 150 - 0
src/views/old_mini/mine/pages/farm.vue

@@ -0,0 +1,150 @@
+<template>
+    <div class="farm-page">
+        <custom-header name="我的农场"></custom-header>
+        <div class="farm-list">
+            <div class="list-item" v-for="item in 3" :key="item">
+                <div class="item-info">
+                    <div class="item-top">
+                        <div class="left-img"></div>
+                        <div class="left-content">
+                            <div class="content-title">
+                                <span>荔枝博览园</span>
+                                <div class="content-tag" v-if="item === 1">默认</div>
+                                <div class="content-text" v-else>设为默认</div>
+                            </div>
+                            <div class="content-desc">
+                                <div>服务作物:荔枝-妃子笑、桂味</div>
+                                <div>农场位置:广东省广州市从化区</div>
+                            </div>
+                        </div>
+                    </div>
+                    <el-select class="select" v-model="value">
+                        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+                    </el-select>
+                </div>
+                <div class="item-btn">查看详情</div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import customHeader from "@/components/customHeader.vue";
+import { ref } from "vue";
+
+const value = ref("Option1");
+
+const options = [
+    {
+        value: "Option1",
+        label: "选择方案1",
+    },
+    {
+        value: "Option2",
+        label: "Option2",
+    },
+    {
+        value: "Option3",
+        label: "Option3",
+    },
+    {
+        value: "Option4",
+        label: "Option4",
+    },
+    {
+        value: "Option5",
+        label: "Option5",
+    },
+];
+</script>
+
+<style scoped lang="scss">
+.farm-page {
+    width: 100%;
+    height: 100vh;
+    .farm-list {
+        width: 100%;
+        height: 100%;
+        background-color: #f7f7f7;
+        padding: 12px;
+        box-sizing: border-box;
+        .list-item {
+            background-color: #fff;
+            border-radius: 10px;
+            padding: 10px;
+            display: flex;
+            justify-content: space-between;
+            .item-info {
+                .item-top {
+                    display: flex;
+                    align-items: center;
+                    gap: 12px;
+                    .left-img {
+                        width: 68px;
+                        height: 68px;
+                        border-radius: 8px;
+                        background-color: red;
+                    }
+                    .left-content {
+                        .content-title {
+                            display: flex;
+                            align-items: center;
+                            gap: 10px;
+                            margin-bottom: 4px;
+                            font-size: 16px;
+                            font-weight: 500;
+                            .content-tag {
+                                background-color: #2199f8;
+                                color: #fff;
+                                padding: 2px 8px;
+                                border-radius: 15px;
+                                font-size: 12px;
+                                font-weight: 400;
+                            }
+                            .content-text {
+                                font-size: 12px;
+                                color: #2199F8;
+                                font-weight: 400;
+                            }
+                        }
+                        .content-desc {
+                            font-size: 12px;
+                            color: #999999;
+                            line-height: 18px;
+                        }
+                    }
+                }
+                .select {
+                    width: 105px;
+                    margin-left: 80px;
+                    margin-top: 10px;
+                    ::v-deep{
+                        .el-select__wrapper{
+                            border: 1px solid #2199F8;
+                            box-shadow: none;
+                        }
+                        .el-select__placeholder,.el-select__caret{
+                            color: #2199F8;
+                        }
+                        .el-select__selection {
+                            flex: none;
+                            width: fit-content;
+                        }
+                        .el-select__placeholder {
+                            position: static;
+                            transform: none;
+                            width: fit-content;
+                        }
+                    }
+                }
+            }
+            .item-btn {
+                color: #a8a8a8;
+            }
+        }
+        .list-item + .list-item {
+            margin-top: 12px;
+        }
+    }
+}
+</style>

+ 1 - 3
src/views/old_mini/mine/pages/message.vue

@@ -41,9 +41,7 @@ const messageList = ref([
 ]);
 
 const handleItem = ({type,name}) =>{
-    if(type==='message'){
-        router.push("/message_detail");
-    }
+    router.push("/message_list");
 }
 </script>
 

+ 205 - 0
src/views/old_mini/mine/pages/messageList.vue

@@ -0,0 +1,205 @@
+<template>
+    <div class="container">
+        <custom-header name="专家咨询"></custom-header>
+        <div class="message-list">
+            <div class="message-item" v-for="(item, index) in messageList" :key="index" @click="handleItem(item)">
+                <div class="message">
+                    <!-- <img class="img" v-if="item.type!=='message'" :src="require(`@/assets/img/mine/${item.type}.png`)" alt="" /> -->
+                    <img class="img" v-if="item.senderId===userId&&item.type==='message'" :src="item.receiverIcon || 'https://birdseye-img.sysuimars.com/birdseye-look-mini/Group%201321316260.png'" alt="">
+                    <img class="img" v-else :src="item.senderIcon || 'https://birdseye-img.sysuimars.com/birdseye-look-mini/Group%201321316260.png'" alt="">
+                    <div class="info">
+                        <span class="name" v-if="item.senderId===userId">{{item.receiverName}}</span>
+                        <span class="name" v-else>{{item.senderName}}</span>
+                        <div class="van-ellipsis" v-if="item.content.type==='object'">[图片]</div>
+                        <div class="van-ellipsis" v-else>{{item.newContent}}</div>
+                    </div>
+                </div>
+                <div class="date-time">
+                    <div>{{item.time || '03/22 13:08'}}</div>
+                    <badge v-show="item.unreadCount&&item.unreadCount!=0" class="badge" :content="item.unreadCount" max="99"></badge>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { Badge } from "vant";
+import { computed, onActivated, ref,onDeactivated } from "vue";
+import MqttClient from "@/plugins/MqttClient";
+import { useRouter } from "vue-router";
+import { deepClone } from "@/common/commonFun";
+import eventBus from "@/api/eventBus";
+import customHeader from "@/components/customHeader.vue";
+const router = useRouter();
+
+const userId = Number(localStorage.getItem('MINI_USER_ID'))
+
+const defalutList = ref([
+    {
+        senderName: "专家1",
+        type: "report",
+        typeNum:3,
+        newContent: "好的,我看看后天有没有无人机安我看看后天有没有无人机安",
+        time: "",
+        content:{
+            type:'text'
+        },
+        unreadCount: 1,
+    },
+    {
+        senderName: "专家1",
+        type: "system",
+        typeNum:2,
+        newContent: "好的,我看看后天有没有无人机安...",
+        time: "",
+        content:{
+            type:'text'
+        },
+        unreadCount: 5,
+    },
+    {
+        senderName: "专家1",
+        typeNum:1,
+        type: "warn",
+        newContent: "好的,我看看后天有没有无人机安...",
+        time: "",
+        content:{
+            type:'text'
+        },
+        unreadCount: 10,
+    },
+])
+
+const messageList = ref(defalutList.value);
+
+const timeFormat = (dateStr) =>{
+    // 提取月、日、时、分
+    const [, month, day, hours, minutes] = dateStr.match(
+    /(\d{2})-(\d{2})T(\d{2}):(\d{2})/
+    );
+    const formattedDate = `${month}/${day} ${hours}:${minutes}`;
+    return formattedDate
+}
+
+const handleItem = ({type,receiverName,receiverId,senderId,senderName,unreadCount,typeNum}) =>{
+    // let name = senderName
+    // let id = senderId
+    // if(senderId===userId){
+    //     name = receiverName
+    //     id = receiverId
+    // }
+    // if(type==='message'){
+    //     router.push(`/dialogue?userId=${id}&name=${name}`);
+    // }else{
+    //     router.push(`/warn_page?name=${name}&type=${typeNum}`);
+    // }
+    router.push(`/chat_frame?userId=91754&name=专家1`);
+}
+
+function checkJSON(str) {
+  try {
+    const parsed = JSON.parse(str);
+    return {
+      isJSON: true,
+      type: Array.isArray(parsed) ? 'array' : typeof parsed,
+      value: parsed
+    };
+  } catch (e) {
+    return {
+      isJSON: false,
+      value: str
+    };
+  }
+}
+const mqttClient = ref(null)
+const initMqtt = () =>{
+    mqttClient.value = new MqttClient(['chat/message/'+userId], (topic, message) => {
+        getMessageList()
+    });
+
+    mqttClient.value.connect();
+}
+
+const getMessageList = () =>{
+    messageList.value = deepClone(defalutList.value)
+    VE_API.bbs.list().then((res) => {
+        const arr = res.data.map(item =>{
+            return {
+                ...item,
+                content:checkJSON(item.newContent),
+                time:timeFormat(item.updateTime),
+                type:'message'
+            }
+        });
+        messageList.value.push(...arr)
+    });
+}
+
+const getGroupList = () =>{
+    // getMessageList()
+}
+
+onActivated(()=>{
+    initMqtt()
+    getGroupList()
+})
+
+onDeactivated(()=>{
+    mqttClient.value.client.end(true);
+})
+</script>
+
+<style lang="scss" scoped>
+.container {
+    width: 100%;
+    height: 100vh;
+    background: #f7f7f7;
+    .message-list {
+        width: 100%;
+        overflow: hidden;
+        box-sizing: border-box;
+        padding: 0 12px;
+        .message-item {
+            margin-top: 12px;
+            display: flex;
+            justify-content: space-between;
+            background: #fff;
+            padding: 12px;
+            border-radius: 8px;
+
+            .message {
+                width: calc(100% - 64px - 58px - 10px);
+                display: flex;
+                .img {
+                    width: 48px;
+                    height: 48px;
+                    margin-right: 10px;
+                    border-radius: 10px;
+                }
+                .info {
+                    width: 100%;
+                    color: #999999;
+                    line-height: 24px;
+                    .name {
+                        color: #333333;
+                        font-size: 16px;
+                        font-weight: 500;
+                    }
+                }
+            }
+            .date-time{
+                color: #BBBBBB;
+                font-size: 12px;
+                display: flex;
+                flex-direction: column;
+                align-items: flex-end;
+                margin-top: 4px;
+                .badge{
+                    margin: 20px 15px -6px 0;
+                }
+            }
+        }
+    }
+}
+</style>

+ 1 - 1
yarn.lock

@@ -6611,7 +6611,7 @@ html-webpack-plugin@^5.1.0:
     pretty-error "^4.0.0"
     tapable "^2.0.0"
 
-html2canvas@^1.0.0-rc.5:
+html2canvas@^1.0.0-rc.5, html2canvas@^1.4.1:
   version "1.4.1"
   resolved "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz"
   integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==