|
|
@@ -1,5 +1,25 @@
|
|
|
<template>
|
|
|
<div class="patrol-panel">
|
|
|
+ <!-- 品种切换 -->
|
|
|
+ <div class="variety-bar">
|
|
|
+ <div class="variety-bar__tags">
|
|
|
+ <div
|
|
|
+ v-for="item in zoneList"
|
|
|
+ :key="item.zone_id"
|
|
|
+ class="variety-bar__tag"
|
|
|
+ :class="{ active: item.zone_id === selectedZoneId }"
|
|
|
+ @click="handleSelectZone(item)"
|
|
|
+ >
|
|
|
+ {{ item.zone_name }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="variety-bar__add" @click="goAddVariety">
|
|
|
+ <el-icon>
|
|
|
+ <Plus />
|
|
|
+ </el-icon>
|
|
|
+ <span>新增品种</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
<div
|
|
|
v-for="item in patrolList"
|
|
|
:key="item.id"
|
|
|
@@ -63,6 +83,7 @@ import { onActivated, onMounted, ref } from "vue";
|
|
|
import { useRouter } from "vue-router";
|
|
|
import { Icon } from "vant";
|
|
|
import { ElMessage } from "element-plus";
|
|
|
+import { Plus } from "@element-plus/icons-vue";
|
|
|
import { useI18n } from "@/i18n";
|
|
|
import PhenologyTrackTimelineItem from "@/components/pageComponents/PhenologyTrackTimelineItem.vue";
|
|
|
import albumUploadPopup from "./albumUploadPopup.vue";
|
|
|
@@ -75,6 +96,9 @@ const router = useRouter();
|
|
|
const PATROL_ICON = require("@/assets/img/report/wh-icon.png");
|
|
|
|
|
|
// ---------- 页面状态 ----------
|
|
|
+const zoneList = ref([]);
|
|
|
+const selectedZoneId = ref(null);
|
|
|
+const selectedZoneName = ref("");
|
|
|
const patrolList = ref([
|
|
|
{
|
|
|
id: 1,
|
|
|
@@ -145,9 +169,14 @@ const getSelectedFarm = () => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-const getSelectedZoneId = () => {
|
|
|
+const getSelectedFarmId = () => {
|
|
|
+ const stored = localStorage.getItem("selectedFarmId");
|
|
|
+ if (stored != null && stored !== "") {
|
|
|
+ return Number(stored);
|
|
|
+ }
|
|
|
const farm = getSelectedFarm();
|
|
|
- return farm.zone_id ?? farm.zoneId ?? null;
|
|
|
+ const id = farm.farm_id ?? farm.id;
|
|
|
+ return id != null && id !== "" ? Number(id) : null;
|
|
|
};
|
|
|
|
|
|
/** 清空第一条上的感知任务残留,回退为 mock 文案 */
|
|
|
@@ -159,7 +188,7 @@ const clearFirstPatrolInteract = () => {
|
|
|
id: 1,
|
|
|
level: t("agriFile.riskLevel2"),
|
|
|
title: t("agriFile.patrolGrowthTitle"),
|
|
|
- zone: t("agriFile.zoneOne"),
|
|
|
+ zone: selectedZoneName.value || t("agriFile.zoneOne"),
|
|
|
subject: t("agriFile.patrolSubject"),
|
|
|
issue: t("agriFile.patrolIssue"),
|
|
|
desc: "",
|
|
|
@@ -170,8 +199,34 @@ const clearFirstPatrolInteract = () => {
|
|
|
};
|
|
|
|
|
|
// ---------- 业务逻辑:加载 ----------
|
|
|
+const applySelectedZone = (zone) => {
|
|
|
+ selectedZoneId.value = zone?.zone_id ?? null;
|
|
|
+ selectedZoneName.value = zone?.zone_name || "";
|
|
|
+};
|
|
|
+
|
|
|
+const fetchZones = async () => {
|
|
|
+ const farmId = getSelectedFarmId();
|
|
|
+ if (farmId == null) {
|
|
|
+ zoneList.value = [];
|
|
|
+ applySelectedZone(null);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ const res = await VE_API.questionnaire.getZones({ farm_id: farmId });
|
|
|
+ const zones = Array.isArray(res?.data?.zones) ? res.data.zones : [];
|
|
|
+ zoneList.value = zones;
|
|
|
+ // 优先沿用当前选中;否则默认第一个
|
|
|
+ const matched = zones.find((z) => z.zone_id === selectedZoneId.value);
|
|
|
+ applySelectedZone(matched || zones[0] || null);
|
|
|
+ } catch (e) {
|
|
|
+ console.warn("[patrolPanel] getZones failed", e);
|
|
|
+ zoneList.value = [];
|
|
|
+ applySelectedZone(null);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
const fetchPatrolInteractTask = async () => {
|
|
|
- const zoneId = getSelectedZoneId();
|
|
|
+ const zoneId = selectedZoneId.value;
|
|
|
if (zoneId == null || zoneId === "") {
|
|
|
clearFirstPatrolInteract();
|
|
|
return;
|
|
|
@@ -200,16 +255,16 @@ const fetchPatrolInteractTask = async () => {
|
|
|
|
|
|
const fetchCropArchive = async () => {
|
|
|
const farm = getSelectedFarm();
|
|
|
- const params = {
|
|
|
- farm_id: farm.farm_id,
|
|
|
- zone_id: farm.zone_id,
|
|
|
- crop_code: farm.category_code,
|
|
|
- };
|
|
|
- if (!params) {
|
|
|
+ const zoneId = selectedZoneId.value;
|
|
|
+ if (zoneId == null || zoneId === "") {
|
|
|
cropArchiveList.value = [];
|
|
|
return;
|
|
|
}
|
|
|
- const { zone_name: zoneName, ...query } = params;
|
|
|
+ const query = {
|
|
|
+ farm_id: farm.farm_id ?? farm.id,
|
|
|
+ zone_id: zoneId,
|
|
|
+ crop_code: farm.category_code,
|
|
|
+ };
|
|
|
try {
|
|
|
const res = await VE_API.entry.getPhenoRecord(query);
|
|
|
if (res?.code !== 200) {
|
|
|
@@ -217,7 +272,7 @@ const fetchCropArchive = async () => {
|
|
|
return;
|
|
|
}
|
|
|
const list = Array.isArray(res.data) ? res.data : [];
|
|
|
- const fallbackZone = zoneName || t("agriFile.zoneOne");
|
|
|
+ const fallbackZone = selectedZoneName.value || t("agriFile.zoneOne");
|
|
|
cropArchiveList.value = list
|
|
|
.map((item, index) => mapPhenoRecordToArchive(item, index, fallbackZone))
|
|
|
.sort((a, b) => String(b.date).localeCompare(String(a.date)));
|
|
|
@@ -228,7 +283,7 @@ const fetchCropArchive = async () => {
|
|
|
};
|
|
|
|
|
|
const fillMockLabels = () => {
|
|
|
- const albumName = t("agriFile.zoneOne");
|
|
|
+ const albumName = selectedZoneName.value || t("agriFile.zoneOne");
|
|
|
patrolList.value = patrolList.value.map((item, index) => {
|
|
|
// 第一条由外部感知任务接口填充,不覆盖
|
|
|
if (index === 0 && item.interactCode) return item;
|
|
|
@@ -243,15 +298,37 @@ const fillMockLabels = () => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
-const loadPatrolData = () => {
|
|
|
+/** 按当前选中品种刷新巡园卡片与作物档案 */
|
|
|
+const reloadBySelectedZone = () => {
|
|
|
+ clearFirstPatrolInteract();
|
|
|
+ fillMockLabels();
|
|
|
+ fetchPatrolInteractTask();
|
|
|
+ fetchCropArchive();
|
|
|
+};
|
|
|
+
|
|
|
+const loadPatrolData = async () => {
|
|
|
// 先清掉上一农场感知任务,避免 fillMockLabels 因 interactCode 保留旧数据
|
|
|
clearFirstPatrolInteract();
|
|
|
+ await fetchZones();
|
|
|
fillMockLabels();
|
|
|
fetchPatrolInteractTask();
|
|
|
fetchCropArchive();
|
|
|
};
|
|
|
|
|
|
// ---------- 事件处理 ----------
|
|
|
+const handleSelectZone = (zone) => {
|
|
|
+ if (!zone || zone.zone_id === selectedZoneId.value) return;
|
|
|
+ applySelectedZone(zone);
|
|
|
+ reloadBySelectedZone();
|
|
|
+};
|
|
|
+
|
|
|
+const goAddVariety = () => {
|
|
|
+ router.push({
|
|
|
+ path: "/add_zone",
|
|
|
+ query: { type: "region", isSuccessPopup: "0" },
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
const goGrowthTrack = (item) => {
|
|
|
router.push({
|
|
|
path: "/growth_track",
|
|
|
@@ -284,7 +361,7 @@ const handleShare = (item) => {
|
|
|
};
|
|
|
|
|
|
const handleUploadClick = (item) => {
|
|
|
- uploadZoneId.value = item?.zoneId ?? item?.id ?? getSelectedZoneId() ?? "";
|
|
|
+ uploadZoneId.value = item?.zoneId ?? item?.id ?? selectedZoneId.value ?? "";
|
|
|
showUploadPopup.value = true;
|
|
|
};
|
|
|
|
|
|
@@ -299,9 +376,9 @@ const handleUploadConfirm = async ({ images, date } = {}) => {
|
|
|
const res = await VE_API.questionnaire.uploadImages({
|
|
|
cloud_url,
|
|
|
farm_id: farm.farm_id ?? farm.id ?? "",
|
|
|
- zone_id: uploadZoneId.value || getSelectedZoneId() || "",
|
|
|
+ zone_id: uploadZoneId.value || selectedZoneId.value || "",
|
|
|
category_code: farm.category_code || "",
|
|
|
- zone_name: farm.variety_name || "",
|
|
|
+ zone_name: selectedZoneName.value || farm.variety_name || "",
|
|
|
create_time: date,
|
|
|
});
|
|
|
if (res?.code === 200) {
|
|
|
@@ -324,11 +401,56 @@ onActivated(loadPatrolData);
|
|
|
|
|
|
defineExpose({
|
|
|
fetchPatrolInteractTask,
|
|
|
+ fetchCropArchive,
|
|
|
+ loadPatrolData,
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.patrol-panel {
|
|
|
+ .variety-bar {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+
|
|
|
+ &__tags {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 6px;
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__tag {
|
|
|
+ padding: 4px 16px;
|
|
|
+ border-radius: 4px;
|
|
|
+ background: #fff;
|
|
|
+ color: #4e5969;
|
|
|
+ font-size: 14px;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background: #2199f8;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &__add {
|
|
|
+ flex: none;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 2px;
|
|
|
+ padding: 5px 10px;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #2199f8;
|
|
|
+ background: rgba(33, 153, 248, 0.1);
|
|
|
+ color: #2199f8;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
.tracking-item {
|
|
|
width: 100%;
|
|
|
margin-bottom: 16px;
|