Bläddra i källkod

feat: 增加识别结果页面

lxf 1 vecka sedan
förälder
incheckning
6e9c4960ed

BIN
src/assets/img/home/good-fill.png


BIN
src/assets/img/home/link-icon.png


+ 5 - 11
src/components/detailDialog.vue

@@ -65,15 +65,9 @@
             </div>
 
             <div class="bottom-btn">
-                <div class="btn-item secondary-btn">咨询专家</div>
+                <div class="btn-item secondary-btn" @click="toPage">咨询专家</div>
                 <div class="btn-item primary-btn" @click="triggerClick">触发农事</div>
             </div>
-            <div class="close-btn" v-show="dialogData.isBtn" @click.stop="winDialogVisible=false">
-                    <!-- <el-icon size="32" color="#fff"><CircleCloseFilled /></el-icon> -->
-                    <el-button v-show="dialogData.typeStr === 'export'" type="primary" class="one-btn" @click.stop="toPage"> 邀请专家诊断 </el-button>
-                    <el-button v-show="dialogData.typeStr === 'activate'" type="primary" class="one-btn" @click.stop="handleAct"> 立即激活专家农事 </el-button>
-                    <!-- <el-button type="primary" class="one-btn" @click.stop="toShare"> 立即分享 </el-button> -->
-            </div>
         </div>
         <template #footer>
             <div class="dialog-footer">
@@ -304,10 +298,6 @@ const showDialog = (pageParams) => {
 
 defineExpose({showDialog})
 
-//邀请专家诊断
-const toPage = () =>{
-    router.push('/prescription')
-}
 
 //立即激活
 const handleAct = () =>{
@@ -330,6 +320,10 @@ function closeDialog() {
     winDialogVisible.value = false
 }
 
+function toPage() {
+    router.push("/expert_list")
+}
+
 </script>
 
 <style lang="scss" scoped>

+ 7 - 0
src/router/globalRoutes.js

@@ -63,4 +63,11 @@ export default [
         name: "ExpertList",
         component: () => import("@/views/old_mini/home/subPages/expertList.vue"),
     },
+    // 识别结果
+    {
+        path: "/recognize",
+        name: "Recognize",
+        component: () => import("@/views/old_mini/home/subPages/recognize.vue"),
+    },
+
 ];

+ 1 - 1
src/views/old_mini/home/components/problemReminder.vue

@@ -45,7 +45,7 @@ import detailDialog from "@/components/detailDialog.vue"
 import { useRouter } from "vue-router";
 const router = useRouter();
 
-const show = ref(false);
+const show = ref(true);
 const noShow = ref(false);
 const noClick = () => {
     show.value = false;

+ 147 - 6
src/views/old_mini/home/subPages/expertList.vue

@@ -1,9 +1,61 @@
 <template>
     <div class="prescription-list">
         <custom-header name="创建农场"></custom-header>
+        <div class="prescription-filter">
+            <div class="filter-search">
+                <el-input
+                    v-model="keyword"
+                    style="width: 100%"
+                    placeholder="搜索位置"
+                    :prefix-icon="Search"
+                    />
+            </div>
+            <div class="select-box">
+                <div class="select-item width-30">
+                    <el-select v-model="typeVal" placeholder="品种">
+                            <el-option
+                                v-for="item in typeOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            />
+                    </el-select>
+                </div>
+                <div class="select-item width-20">
+                    <el-select v-model="regionVal" placeholder="城市">
+                            <el-option
+                                v-for="item in regionOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            />
+                    </el-select>
+                </div>
+                <div class="select-item width-30">
+                    <el-select v-model="levelVal" placeholder="专家擅长">
+                            <el-option
+                                v-for="item in levelOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            />
+                    </el-select>
+                </div>
+                <div class="select-item width-20">
+                    <el-select v-model="levelVal" placeholder="筛选">
+                            <el-option
+                                v-for="item in levelOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            />
+                    </el-select>
+                </div>
+            </div>
+        </div>
         <div class="prescription-content">
             <div class="list">
-                <div class="list-item" v-for="(item, index) in list" :key="index" @click.stop="handleDetails(item)">
+                <div class="list-item" v-for="(item, index) in list" :key="index" @click.stop="toPage(item)">
                     <div class="list-top">
                         <div class="user">
                             <img :src="item.icon" alt="" />
@@ -57,6 +109,7 @@ import { onActivated, ref } from "vue";
 import { useRouter,useRoute } from "vue-router";
 import { useStore } from "vuex";
 import { ElMessage } from "element-plus";
+import { Search } from '@element-plus/icons-vue'
 const store = useStore();
 const router = useRouter();
 const route = useRoute();
@@ -65,10 +118,6 @@ const handleCheck = (item) =>{
     item.isDefault = !item.isDefault
 }
 
-const handleDetails = ({userId}) => {
-    router.push(`/prescription_detail?userId=${userId}`)
-};
-
 const handleChat = ({userId,name}) =>{
     router.push(`/consult?userId=${userId}&name=${name}`)
 }
@@ -124,6 +173,45 @@ const getList = () => {
 onActivated(() => {
     // getList();
 });
+
+function toPage() {
+    // userId=${id}&name=${name}
+    router.push(`/chat_frame?chat_frame?userId=91754&name=咨询专家`)
+}
+
+// 搜索
+const keyword = ref(null)
+const typeVal = ref(null)
+const regionVal = ref(null)
+const levelVal = ref(null)
+const typeOptions = ref([
+    { label: "全部", value: "all" },
+    { label: "施肥", value: "1" },
+    { label: "用药", value: "2" },
+    { label: "修剪", value: "3" },
+    { label: "其他", value: "4" },
+])
+
+const regionOptions = ref([{
+    label: "全部",
+    value: "all"
+}, {
+    label: "区域1",
+    value: "1"
+}, {
+    label: "区域2",
+    value: "2"
+}, {
+    label: "区域3",
+    value: "3"
+}])
+
+const levelOptions = ref([
+    { label: "全部", value: "all" },
+    { label: "荔枝", value: "1" },
+    { label: "龙眼", value: "2" },
+    { label: "枇杷", value: "3" },
+])
 </script>
 
 <style lang="scss" scoped>
@@ -132,10 +220,63 @@ onActivated(() => {
     width: 100%;
     height: 100vh;
     background-color: #f5f7fb;
+    .prescription-filter {
+        padding: 12px 12px 0 12px;
+        .filter-search {
+            padding-bottom: 6px;
+            ::v-deep {
+                .el-input__wrapper {
+                    box-shadow: 0 0 0 1px #fff inset;
+                    border-radius: 20px;
+                }
+            }
+        }
+        .select-box {
+            width: 100%;
+            display: flex;
+            justify-content: space-around;
+            .select-item {
+                width: fit-content;
+                ::v-deep {
+                    .el-input__wrapper {
+                        background: none;
+                        box-shadow: none;
+                    }
+                    .el-input__inner {
+                        font-size: 14px;
+                        color: rgba(0, 0, 0, 0.5);
+                    }
+                    .el-select__wrapper {
+                        background: none;
+                        box-shadow: none;
+                        gap: 2px;
+                        padding: 4px 2px;
+                        justify-content: center;
+                    }
+                    .el-select__selection {
+                        flex: none;
+                        width: fit-content;
+                    }
+                    .el-select__placeholder {
+                        color: rgba(0, 0, 0, 0.5);
+                        position: static;
+                        transform: none;
+                        width: fit-content;
+                    }
+                }
+            }
+            .width-30 {
+                width: 30%;
+            }
+            .width-20 {
+                width: 20%;
+            }
+        }
+    }
     .prescription-content {
         overflow-y: auto;
         height: calc(100% - 40px);
-        padding: 12px;
+        padding: 6px 12px 12px;
         box-sizing: border-box;
         width: 100%;
         .list {

+ 398 - 0
src/views/old_mini/home/subPages/recognize.vue

@@ -0,0 +1,398 @@
+<template>
+    <div class="diseases-dictionary-detail">
+        <div class="page-title" @click="goBack">
+            <el-icon class="title-icon" color="rgba(0, 0, 0, 0.8)" size="16"><ArrowLeftBold /></el-icon>
+            识别结果
+        </div>
+        <div class="detail-content">
+            <div class="detail-img">
+                <div class="card-item">
+
+                    <div class="ing-wrap" v-if="isRecognize">
+                        <div>
+                            <el-icon size="20" class="is-loading">
+                                <Loading />
+                            </el-icon>
+                        </div>
+                        正在识别,请稍后...
+                    </div>
+                    <!-- <img class="card-bg" src="@/assets/img/diseases/1.jpg" /> -->
+                    <img class="card-bg" :src="base_img_url2 + imgKey + resize" />
+                    <div class="card-content" v-if="!isRecognize && !noData">
+                        <div class="title-ques">
+                            <div class="ques-text">病虫名称:{{recoginizeResult?.name}}</div>
+                        </div>
+                        <div class="dialog-famous">管理方法:</div>
+                        <div class="dialog-answer" v-html="recoginizeResult?.cure">
+
+                        </div>
+                        <div class="info">基本信息:</div>
+                        <div class="desc">
+                            {{recoginizeResult?.info}}
+                        </div>
+                        <div class="famous-info">
+                            <img src="@/assets/img/home/link-icon.png" />
+                            <span class="type-name">{{recoginizeResult?.name}}</span>
+                            <span>合作专家:韦帮稳</span>
+                        </div>
+                    </div>
+                    <div class="card-content no-data" v-if="!isRecognize && noData">
+                        <img src="@/assets/img/home/good-fill.png" />
+                        长势良好,并未发现病虫害
+                    </div>
+                </div>
+            </div>
+            <div class="btn-wrap" v-if="!isRecognize">
+                <div class="btn share" @click="toPage">咨询专家</div>
+                <div class="btn primary" @click="triggerRecord">触发农事</div>
+            </div>
+            <!-- <div class="recognizing-box" v-if="isRecognize">正在识别,请稍等~</div> -->
+        </div>
+    </div>
+    
+    
+    <Popup v-model:show="tipsShow" class="tips-popup">
+        <div class="right-icon">
+            <img class="right-img" src="@/assets/img/home/right.png" alt="">
+        </div>
+        <div class="no-popup-title">
+            <span>农事已触发成功</span>
+            <div class="no-popup-title-sub">您可以在 农场监测-农事任务 看到农事状态</div>
+        </div>
+        <div class="no-popup-btn" @click="tipsShow = false">我知道了</div>
+    </Popup>
+</template>
+
+<script setup>
+import { onMounted, ref } from "vue";
+import { useRouter, useRoute } from "vue-router";
+import { base_img_url2 } from "@/api/config.js";
+import { Popup } from "vant";
+import wx from "weixin-js-sdk";
+
+import MqttClient from "@/plugins/MqttClient";
+
+
+let resize = '?x-oss-process=image/resize,w_1300'
+const route = useRoute();
+// const json = JSON.parse(route.query.json);
+const json = {
+"imgKey":"16926861-1e20-4cbd-8bf2-90208db5a2d0/806080da-1a30-4b5b-b64b-b22e722c6cb6/DJI_202509010800_001_806080da-1a30-4b5b-b64b-b22e722c6cb6/DJI_20250901080536_0045_V_code-ws0fsmge97gh.jpeg",
+"imgId":431600}
+const imgKey = json.imgKey;
+const imgId = json.imgId;
+
+const isRecognize = ref(false);
+const recoginizeResult = ref({
+    name: "蒂蛀虫",
+    cure: " (1) 化学农药喷洒,如:乙醇草酸酯乳油‌。 <br/>(2) 最佳喷药时间:荔枝成熟期要防治蒂蛀虫,一般建议在荔枝成熟前5-8天进行喷药。 <br/>(3) 使用剂量‌:喷药时应按照说明书上的使用剂量进行使用,不要过量使用。",
+    info: "又称蛀蒂虫,属于鳞翅目细蛾科,主要危害荔枝和龙眼的果实、嫩茎、嫩叶和花穗。",
+});
+const noData = ref(false);
+onMounted(() => {
+    console.log("img", imgId, imgKey);
+
+    // 使用示例
+    const topics = ["phone/image/update/" + imgId]; // 订阅的主题数组
+    const mqttClient = new MqttClient(topics, (topic, message) => {
+          let res = message.toString();
+          console.log("接收推送信息:", res);
+          let status = JSON.parse(res).status;
+          console.log("status", status);
+          if (status === "recog_ing") {
+            console.log("recog_ing");
+          }
+          if (status === "recog_finished") {
+            let resData =  JSON.parse(message)
+            console.log("recog_finished",resData.data);
+            isRecognize.value = false;
+            if (resData.data && resData.data.length) {
+              noData.value = false
+              const formData = new FormData();
+              formData.append('code', resData.data[0]);
+              VE_API.disease.fetchDiseaseDetail(formData).then(({data}) => {
+                recoginizeResult.value = data;
+              })
+            } else {
+              noData.value = true
+            }
+          }
+    });
+    mqttClient.connect();
+});
+
+const router = useRouter();
+const goBack = () => {
+    // router.go(-1);
+    router.push('/diseases_dictionary')
+};
+
+const toDetail = () => {
+    router.push("/diseases_dictionary_detail");
+};
+
+const toRecognize = () => {
+    // router.push("/diseases_recognize")
+    const gardenData = {
+        organId: 25862,
+    };
+    wx.miniProgram.navigateTo({
+        url: `/pages/subPages/recognize_carmera/index?gardenData=${JSON.stringify(gardenData)}`,
+    });
+};
+
+const tipsShow = ref(false)
+function triggerRecord() {
+    tipsShow.value = true
+}
+
+function toPage() {
+    router.push("/expert_list")
+}
+</script>
+
+<style lang="scss" scoped>
+.diseases-dictionary-detail {
+    position: relative;
+    width: 100%;
+    height: 100vh;
+    overflow: hidden;
+    background: #fff;
+
+    .page-title {
+        height: 44px;
+        line-height: 44px;
+        text-align: center;
+        font-size: 17px;
+        font-weight: bold;
+        // border-bottom: 1px solid #ededed;
+        .title-icon {
+            cursor: pointer;
+            position: absolute;
+            left: 16px;
+            top: 13px;
+        }
+    }
+    .detail-content {
+        height: calc(100% - 44px - 20px);
+        overflow: auto;
+        .detail-img {
+            height: calc(100% - 100px);
+            position: relative;
+            padding: 4px 16px 30px 16px;
+            .card-item {
+                width: 100%;
+                height: 100%;
+                position: relative;
+                .ing-wrap {
+                    color: #fff;
+                    position: absolute;
+                    left: 0;
+                    right: 0;
+                    top: 50%;
+                    transform: translateY(-50%);
+                    margin: 0 auto;
+                    background: rgba(0, 0, 0, 0.6);
+                    border-radius: 12px;
+                    width: 164px;
+                    height: 95px;
+                    display: flex;
+                    flex-direction: column;
+                    align-items: center;
+                    justify-content: center;
+                }
+                img {
+                    border-radius: 24px 0 36px 4px;
+                    width: 100%;
+                    height: 100%;
+                    object-fit: cover;
+                }
+                .no-data {
+                    color: #fff;
+                    text-align: center;
+                    padding-top: 26px;
+                     img {
+                        width: 26px;
+                     }
+                }
+                .card-content {
+                    position: absolute;
+                    bottom: 0;
+                    left: 0;
+                    padding: 12px 12px 26px 12px;
+                    border-radius: 24px 0 36px 4px;
+                    color: #ffffff;
+                    background: rgba(0, 0, 0, 0.6);
+                    backdrop-filter: blur(4px);
+                    max-height: calc(100% - 38px);
+                    overflow: auto;
+                    width: 100%;
+                    box-sizing: border-box;
+                    .ques-text {
+                        color: #ffd786;
+                        font-size: 18px;
+                        position: relative;
+                        padding-left: 12px;
+                    }
+                    .ques-text:after {
+                        content: "";
+                        position: absolute;
+                        width: 4px;
+                        height: 15px;
+                        top: 5px;
+                        left: 0;
+                        border-radius: 2px;
+                        background: #ffd186;
+                    }
+                    .dialog-famous {
+                        padding: 8px 0 4px 0;
+                        color: #ffd786;
+                        font-size: 16px;
+                    }
+                    .dialog-answer {
+                        line-height: 24px;
+                    }
+                    .info {
+                        padding: 10px 10px 6px 0;
+                        font-size: 15px;
+                    }
+                    .desc {
+                        font-size: 14px;
+                        line-height: 24px;
+                    }
+                    .famous-info {
+                        display: flex;
+                        align-items: center;
+                        color: #ffd786;
+                        font-size: 16px;
+                        padding: 8px 0 0px 0;
+                        .type-name {
+                            padding: 0 4px 0 8px;
+                        }
+                        img {
+                            width: 16px;
+                        }
+                    }
+                }
+            }
+            .recognize-img {
+                width: 100%;
+            }
+        }
+        .btn-wrap {
+            width: 100%;
+            height: 56px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            margin: 0 auto;
+            padding: 0 16px;
+            box-sizing: border-box;
+            .btn {
+                height: 40px;
+                line-height: 40px;
+                font-size: 16px;
+                text-align: center;
+                color: #000;
+                border-radius: 24px;
+                &.primary {
+                    flex: 1;
+                    color: #fff;
+                    background: #2199f8;
+                }
+                &.share {
+                    background: #fff;
+                    min-width: 80px;
+                    flex: 1;
+                    text-align: center;
+                    border: 1px solid #8E8E8E;
+                }
+            }
+            .btn + .btn {
+                margin-left: 15px;
+            }
+        }
+        .recognizing-box {
+            padding-top: 30px;
+            text-align: center;
+            font-size: 16px;
+        }
+        .box-title {
+            font-size: 16px;
+            font-weight: bold;
+            color: #000;
+            padding: 16px 2px 16px 0;
+        }
+        .padding-t {
+            padding-top: 26px;
+        }
+        .expert-box {
+            .expert-item {
+                display: flex;
+                justify-content: space-between;
+            }
+            .expert-l {
+                display: inline-flex;
+                align-items: center;
+                .expert-name {
+                    padding-left: 10px;
+                }
+                .expert-tag {
+                    background: #f7ecc7;
+                    padding: 2px 6px;
+                    color: #ae7d22;
+                    width: fit-content;
+                    border-radius: 4px;
+                    font-size: 12px;
+                    display: flex;
+                    align-items: center;
+                    margin-left: 10px;
+                }
+                img {
+                    width: 40px;
+                    height: 40px;
+                    border-radius: 50%;
+                }
+            }
+            .line {
+                margin: 16px;
+                width: calc(100% - 32px);
+                height: 1px;
+                background: #f1f1f1;
+            }
+        }
+    }
+}
+
+.tips-popup {
+    width: 300px;
+    border-radius: 14px;
+    padding: 28px 15px 20px;
+    box-sizing: border-box;
+    .right-icon {
+        text-align: center;
+        padding-bottom: 12px;
+        .right-img {
+            width: 68px;
+        }
+    }
+    .no-popup-title {
+        font-size: 24px;
+        font-weight: 500;
+        text-align: center;
+        .no-popup-title-sub{
+            font-size: 14px;
+            margin-top: 8px;
+        }
+    }
+    .no-popup-btn {
+        background-color: #2199F8;
+        padding: 8px;
+        border-radius: 20px;
+        color: #fff;
+        font-size: 16px;
+        margin-top: 32px;
+        text-align: center;
+    }
+}
+</style>

+ 3 - 8
yarn.lock

@@ -1363,10 +1363,10 @@
     read-package-json-fast "^3.0.0"
     which "^3.0.0"
 
-"@parcel/watcher-darwin-arm64@2.5.1":
+"@parcel/watcher-win32-x64@2.5.1":
   version "2.5.1"
-  resolved "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz"
-  integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==
+  resolved "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz"
+  integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==
 
 "@parcel/watcher@^2.4.1":
   version "2.5.1"
@@ -6203,11 +6203,6 @@ fs.realpath@^1.0.0:
   resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
   integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
 
-fsevents@~2.3.2:
-  version "2.3.3"
-  resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz"
-  integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
-
 function-bind@^1.1.1, function-bind@^1.1.2:
   version "1.1.2"
   resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"