|
@@ -76,7 +76,6 @@
|
|
|
import { computed, nextTick, onActivated, onMounted, ref } from "vue";
|
|
import { computed, nextTick, onActivated, onMounted, ref } from "vue";
|
|
|
import { useStore } from "vuex";
|
|
import { useStore } from "vuex";
|
|
|
import { useRouter } from "vue-router";
|
|
import { useRouter } from "vue-router";
|
|
|
-import { ElMessage } from "element-plus";
|
|
|
|
|
import IndexMap from "./index";
|
|
import IndexMap from "./index";
|
|
|
import customCalendar from "./components/calendar.vue";
|
|
import customCalendar from "./components/calendar.vue";
|
|
|
import farmHeader from "@/components/farmHeader.vue";
|
|
import farmHeader from "@/components/farmHeader.vue";
|
|
@@ -85,6 +84,14 @@ import surveyAskPopup from "@/components/popup/surveyAskPopup.vue";
|
|
|
import { useI18n } from "@/i18n";
|
|
import { useI18n } from "@/i18n";
|
|
|
import wx from "weixin-js-sdk";
|
|
import wx from "weixin-js-sdk";
|
|
|
|
|
|
|
|
|
|
+// ---------- 常量 ----------
|
|
|
|
|
+/** zoneId / date 临时固定,后续改为当前分区 / 当天 */
|
|
|
|
|
+const QUERY = {
|
|
|
|
|
+ zoneId: 41,
|
|
|
|
|
+ date: "2025-09-11",
|
|
|
|
|
+};
|
|
|
|
|
+const COMPLETED_STATUS_RE = /已完成|已执行|已认证|本人完成/;
|
|
|
|
|
+
|
|
|
const { t } = useI18n();
|
|
const { t } = useI18n();
|
|
|
const store = useStore();
|
|
const store = useStore();
|
|
|
const router = useRouter();
|
|
const router = useRouter();
|
|
@@ -96,138 +103,140 @@ const showFarmCalendarPopup = ref(false);
|
|
|
const showSurveyAskPopup = ref(false);
|
|
const showSurveyAskPopup = ref(false);
|
|
|
const tabBarHeight = computed(() => store.state.home.tabBarHeight);
|
|
const tabBarHeight = computed(() => store.state.home.tabBarHeight);
|
|
|
|
|
|
|
|
-const formatDateYMD = (date = new Date()) =>
|
|
|
|
|
- `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
|
|
|
-
|
|
|
|
|
-const filterDate = ref(formatDateYMD());
|
|
|
|
|
-
|
|
|
|
|
-/** 假数据:后续可替换为接口;status: pending 待执行 / completed 已完成 */
|
|
|
|
|
-const taskList = ref([
|
|
|
|
|
- {
|
|
|
|
|
- id: 1,
|
|
|
|
|
- status: "pending",
|
|
|
|
|
- title: "喷施杀菌剂",
|
|
|
|
|
- isHighRisk: false,
|
|
|
|
|
- isNormal: true,
|
|
|
|
|
- isAbnormal: false,
|
|
|
|
|
- isWeather: false,
|
|
|
|
|
- cornerTip: "待执行",
|
|
|
|
|
- analysisSummary: "近期高温高湿,蒂蛀虫风险上升",
|
|
|
|
|
- areaCode: "A-01 / 分区一",
|
|
|
|
|
- farmName: "喷施杀菌剂",
|
|
|
|
|
- executeDate: "08/06",
|
|
|
|
|
- wkt: "POINT(113.612409 23.587036)",
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 2,
|
|
|
|
|
- status: "pending",
|
|
|
|
|
- title: "叶面追肥",
|
|
|
|
|
- isHighRisk: true,
|
|
|
|
|
- isNormal: false,
|
|
|
|
|
- isAbnormal: true,
|
|
|
|
|
- isWeather: false,
|
|
|
|
|
- cornerTip: "发现异常后机动执行",
|
|
|
|
|
- analysisSummary: "转色期养分需求增加,需补充钾肥",
|
|
|
|
|
- areaCode: "B-03 / 分区二",
|
|
|
|
|
- farmName: "叶面追肥",
|
|
|
|
|
- executeDate: "08/06",
|
|
|
|
|
- wkt: "POINT(113.615809 23.586636)",
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 3,
|
|
|
|
|
- status: "pending",
|
|
|
|
|
- title: "灌溉补水",
|
|
|
|
|
- isHighRisk: true,
|
|
|
|
|
- isNormal: false,
|
|
|
|
|
- isAbnormal: false,
|
|
|
|
|
- isWeather: true,
|
|
|
|
|
- cornerTip: "建议执行",
|
|
|
|
|
- analysisSummary: "土壤墒情偏低,局部出现轻度干旱胁迫",
|
|
|
|
|
- areaCode: "C-02 / 分区三",
|
|
|
|
|
- farmName: "灌溉补水",
|
|
|
|
|
- executeDate: "08/06",
|
|
|
|
|
- wkt: "POINT(113.613609 23.584436)",
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 4,
|
|
|
|
|
- status: "completed",
|
|
|
|
|
- title: "病虫害巡查",
|
|
|
|
|
- isHighRisk: false,
|
|
|
|
|
- isNormal: true,
|
|
|
|
|
- isAbnormal: false,
|
|
|
|
|
- isWeather: false,
|
|
|
|
|
- isByMyself: true,
|
|
|
|
|
- cornerTip: "本人完成",
|
|
|
|
|
- analysisSummary: "新梢与果实表面未见明显异常斑点",
|
|
|
|
|
- areaCode: "D-05 / 全园",
|
|
|
|
|
- farmName: "病虫害巡查",
|
|
|
|
|
- executeDate: "08/05",
|
|
|
|
|
- wkt: "POINT(113.614209 23.585836)",
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 5,
|
|
|
|
|
- status: "completed",
|
|
|
|
|
- title: "修剪整枝",
|
|
|
|
|
- isHighRisk: false,
|
|
|
|
|
- isNormal: true,
|
|
|
|
|
- isAbnormal: false,
|
|
|
|
|
- isWeather: false,
|
|
|
|
|
- cornerTip: "已完成",
|
|
|
|
|
- analysisSummary: "已清理过密枝条,改善通风透光",
|
|
|
|
|
- areaCode: "A-02 / 分区一",
|
|
|
|
|
- farmName: "修剪整枝",
|
|
|
|
|
- executeDate: "08/04",
|
|
|
|
|
- wkt: "POINT(113.611809 23.586236)",
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 6,
|
|
|
|
|
- status: "completed",
|
|
|
|
|
- title: "除草松土",
|
|
|
|
|
- isHighRisk: false,
|
|
|
|
|
- isNormal: false,
|
|
|
|
|
- isAbnormal: true,
|
|
|
|
|
- isWeather: false,
|
|
|
|
|
- cornerTip: "已完成",
|
|
|
|
|
- analysisSummary: "行间杂草已清除,土壤表层已疏松",
|
|
|
|
|
- areaCode: "B-01 / 分区二",
|
|
|
|
|
- farmName: "除草松土",
|
|
|
|
|
- executeDate: "08/03",
|
|
|
|
|
- wkt: "POINT(113.616209 23.584936)",
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 7,
|
|
|
|
|
- status: "completed",
|
|
|
|
|
- title: "防风加固",
|
|
|
|
|
- isHighRisk: false,
|
|
|
|
|
- isNormal: false,
|
|
|
|
|
- isAbnormal: false,
|
|
|
|
|
- isWeather: true,
|
|
|
|
|
- cornerTip: "已完成",
|
|
|
|
|
- analysisSummary: "台风过境前已完成支架加固与果袋检查",
|
|
|
|
|
- areaCode: "C-01 / 分区三",
|
|
|
|
|
- farmName: "防风加固",
|
|
|
|
|
- executeDate: "08/02",
|
|
|
|
|
- wkt: "POINT(113.613109 23.587436)",
|
|
|
|
|
- },
|
|
|
|
|
-]);
|
|
|
|
|
-
|
|
|
|
|
|
|
+// ---------- 页面状态 ----------
|
|
|
|
|
+const filterDate = ref(QUERY.date); // 当前选中日期
|
|
|
|
|
+const taskList = ref([]); // 已映射的农事卡片(归属接口 date)
|
|
|
|
|
+const mapCenterPoint = ref(null); // 接口返回的分区中心点 WKT
|
|
|
const activeTab = ref("pending");
|
|
const activeTab = ref("pending");
|
|
|
-const pendingCount = computed(() => taskList.value.filter((item) => item.status === "pending").length);
|
|
|
|
|
-const completedCount = computed(() => taskList.value.filter((item) => item.status === "completed").length);
|
|
|
|
|
-const displayTaskList = computed(() => taskList.value.filter((item) => item.status === activeTab.value));
|
|
|
|
|
|
|
|
|
|
|
|
+// ---------- 计算属性 ----------
|
|
|
|
|
+const dayTaskList = computed(() =>
|
|
|
|
|
+ taskList.value.filter((item) => item.executeDateFull === filterDate.value)
|
|
|
|
|
+);
|
|
|
|
|
+const pendingCount = computed(() => dayTaskList.value.filter((item) => item.status === "pending").length);
|
|
|
|
|
+const completedCount = computed(() => dayTaskList.value.filter((item) => item.status === "completed").length);
|
|
|
|
|
+const displayTaskList = computed(() => dayTaskList.value.filter((item) => item.status === activeTab.value));
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 工具函数 ----------
|
|
|
|
|
+const isCompletedStatus = (status) => COMPLETED_STATUS_RE.test(String(status || ""));
|
|
|
|
|
+
|
|
|
|
|
+const formatExecuteDate = (dateStr) => {
|
|
|
|
|
+ if (!dateStr) return "";
|
|
|
|
|
+ return String(dateStr).replace(/^\d{4}-(\d{2})-(\d{2})$/, "$1/$2");
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const buildPointWkt = (lng, lat, index = 0) => {
|
|
|
|
|
+ if (lng == null || lat == null || Number.isNaN(Number(lng)) || Number.isNaN(Number(lat))) return "";
|
|
|
|
|
+ const offset = (index % 6) * 0.00025;
|
|
|
|
|
+ return `POINT(${Number(lng) + offset} ${Number(lat) + offset * 0.6})`;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+/** 将扁平 fw_tasks 映射为卡片;日期优先用任务自身 date,否则用接口 data.date */
|
|
|
|
|
+const mapFwTasks = (fwTasks, dateStr, lat, lng, zoneId) => {
|
|
|
|
|
+ const list = Array.isArray(fwTasks) ? fwTasks : [];
|
|
|
|
|
+ return list
|
|
|
|
|
+ .filter((task) => task)
|
|
|
|
|
+ .map((task, index) => {
|
|
|
|
|
+ const executeDateFull = task.date || dateStr;
|
|
|
|
|
+ const completed = isCompletedStatus(task.fw_status);
|
|
|
|
|
+ return {
|
|
|
|
|
+ id: `${task.manual_id || task.mechanical_id || "fw"}_${index}_${executeDateFull}`,
|
|
|
|
|
+ status: completed ? "completed" : "pending",
|
|
|
|
|
+ title: task.fw_name || "",
|
|
|
|
|
+ isHighRisk: false,
|
|
|
|
|
+ isNormal: true,
|
|
|
|
|
+ isAbnormal: false,
|
|
|
|
|
+ isWeather: false,
|
|
|
|
|
+ cornerTip: task.fw_status || (completed ? "已完成" : "待执行"),
|
|
|
|
|
+ analysisSummary: task.fw_explain || "",
|
|
|
|
|
+ areaCode: zoneId ? `分区 ${zoneId}` : "",
|
|
|
|
|
+ farmName: task.fw_name || "",
|
|
|
|
|
+ executeDate: formatExecuteDate(executeDateFull),
|
|
|
|
|
+ executeDateFull,
|
|
|
|
|
+ zoneId,
|
|
|
|
|
+ manualId: task.manual_id,
|
|
|
|
|
+ mechanicalId: task.mechanical_id,
|
|
|
|
|
+ latitude: lat,
|
|
|
|
|
+ longitude: lng,
|
|
|
|
|
+ wkt: buildPointWkt(lng, lat, index),
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const resolveRangeStart = (data) => {
|
|
|
|
|
+ if (data?.start_date) return data.start_date;
|
|
|
|
|
+ if (data?.date) return data.date;
|
|
|
|
|
+ return QUERY.date;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 业务逻辑 ----------
|
|
|
|
|
+const refreshMapMarkers = () => {
|
|
|
|
|
+ if (!indexMap.kmap) return;
|
|
|
|
|
+ indexMap.initData(getMapMarkerList(), "farmName", "wkt");
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+/** 地图展示全部日期的待执行农事 */
|
|
|
|
|
+const getMapMarkerList = () =>
|
|
|
|
|
+ taskList.value.filter((item) => item.status === "pending" && item.wkt);
|
|
|
|
|
+
|
|
|
|
|
+const syncCalendar = (data) => {
|
|
|
|
|
+ const start = resolveRangeStart(data);
|
|
|
|
|
+ calendarRef.value?.setStartDate?.(start);
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ const preferDate =
|
|
|
|
|
+ (taskList.value.some((item) => item.executeDateFull === filterDate.value) && filterDate.value) ||
|
|
|
|
|
+ start;
|
|
|
|
|
+ calendarRef.value?.selectDateByValue?.(preferDate);
|
|
|
|
|
+ calendarRef.value?.setSolarTerm?.(taskList.value);
|
|
|
|
|
+ });
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const fetchFwTasks = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await VE_API.questionnaire.getFwTasks({
|
|
|
|
|
+ zone_id: QUERY.zoneId,
|
|
|
|
|
+ date: QUERY.date,
|
|
|
|
|
+ });
|
|
|
|
|
+ const data = res?.data;
|
|
|
|
|
+ if (!data) {
|
|
|
|
|
+ taskList.value = [];
|
|
|
|
|
+ mapCenterPoint.value = null;
|
|
|
|
|
+ calendarRef.value?.setStartDate?.(QUERY.date);
|
|
|
|
|
+ calendarRef.value?.selectDateByValue?.(QUERY.date);
|
|
|
|
|
+ calendarRef.value?.setSolarTerm?.([]);
|
|
|
|
|
+ refreshMapMarkers();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const lat = data.latitude ?? 22.970229;
|
|
|
|
|
+ const lng = data.longitude ?? 113.746646;
|
|
|
|
|
+ const dateStr = resolveRangeStart(data);
|
|
|
|
|
+ mapCenterPoint.value = `POINT(${lng} ${lat})`;
|
|
|
|
|
+
|
|
|
|
|
+ taskList.value = mapFwTasks(
|
|
|
|
|
+ data.fw_tasks,
|
|
|
|
|
+ dateStr,
|
|
|
|
|
+ lat,
|
|
|
|
|
+ lng,
|
|
|
|
|
+ data.zone_id ?? QUERY.zoneId
|
|
|
|
|
+ );
|
|
|
|
|
+ filterDate.value = dateStr;
|
|
|
|
|
+ syncCalendar(data);
|
|
|
|
|
+ nextTick(() => refreshMapMarkers());
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error("获取农事任务失败", e);
|
|
|
|
|
+ taskList.value = [];
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 事件处理 ----------
|
|
|
const handleTabChange = (key) => {
|
|
const handleTabChange = (key) => {
|
|
|
if (activeTab.value === key) return;
|
|
if (activeTab.value === key) return;
|
|
|
activeTab.value = key;
|
|
activeTab.value = key;
|
|
|
- nextTick(() => {
|
|
|
|
|
- if (indexMap.kmap) {
|
|
|
|
|
- indexMap.initData(getMapMarkerList(), "farmName", "wkt");
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleDateSelect = (date) => {
|
|
const handleDateSelect = (date) => {
|
|
|
- filterDate.value = date ?? formatDateYMD();
|
|
|
|
|
|
|
+ filterDate.value = date ?? QUERY.date;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleForward = (item) => {
|
|
const handleForward = (item) => {
|
|
@@ -237,7 +246,7 @@ const handleForward = (item) => {
|
|
|
shareText: `${workName} 已触发,请在最佳农时内及时执行`,
|
|
shareText: `${workName} 已触发,请在最佳农时内及时执行`,
|
|
|
targetUrl: "work_detail",
|
|
targetUrl: "work_detail",
|
|
|
paramsPage: JSON.stringify({
|
|
paramsPage: JSON.stringify({
|
|
|
- id: item?.id,
|
|
|
|
|
|
|
+ ...buildFwTaskQuery(item),
|
|
|
fromShare: 1,
|
|
fromShare: 1,
|
|
|
}),
|
|
}),
|
|
|
imageUrl: "https://birdseye-img.sysuimars.com/birdseye-look-mini/work_img.png",
|
|
imageUrl: "https://birdseye-img.sysuimars.com/birdseye-look-mini/work_img.png",
|
|
@@ -247,13 +256,22 @@ const handleForward = (item) => {
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+const buildFwTaskQuery = (item) => ({
|
|
|
|
|
+ zone_id: item?.zoneId ?? QUERY.zoneId,
|
|
|
|
|
+ date: item?.executeDateFull || QUERY.date,
|
|
|
|
|
+ manual_id: item?.manualId,
|
|
|
|
|
+ mechanical_id: item?.mechanicalId,
|
|
|
|
|
+ statusVal: item?.status,
|
|
|
|
|
+ lat: item?.latitude,
|
|
|
|
|
+ lng: item?.longitude,
|
|
|
|
|
+ fw_name: item?.title || item?.farmName || "",
|
|
|
|
|
+ isAbnormal: item?.isAbnormal ? 1 : 0,
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
const handleViewDetail = (item) => {
|
|
const handleViewDetail = (item) => {
|
|
|
router.push({
|
|
router.push({
|
|
|
path: "/work_detail",
|
|
path: "/work_detail",
|
|
|
- query: {
|
|
|
|
|
- id: item.id,
|
|
|
|
|
- statusVal: item.status,
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ query: buildFwTaskQuery(item),
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -266,28 +284,27 @@ const handleSurveyConfirm = () => {
|
|
|
router.push("/survey_entry");
|
|
router.push("/survey_entry");
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const mapPoint = ref(null);
|
|
|
|
|
-
|
|
|
|
|
-const getMapMarkerList = () => displayTaskList.value.filter((item) => item.wkt);
|
|
|
|
|
|
|
+const initMap = () => {
|
|
|
|
|
+ if (!mapContainer.value) return;
|
|
|
|
|
+ const fallback = store.state.home.miniUserLocationPoint || "POINT(113.746646 22.970229)";
|
|
|
|
|
+ const point = mapCenterPoint.value || fallback;
|
|
|
|
|
+ if (!indexMap.kmap) {
|
|
|
|
|
+ indexMap.initMap(point, mapContainer.value, true);
|
|
|
|
|
+ }
|
|
|
|
|
+ refreshMapMarkers();
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
- mapPoint.value = store.state.home.miniUserLocationPoint || "POINT(113.614209 23.585836)";
|
|
|
|
|
- nextTick(() => {
|
|
|
|
|
- if (mapContainer.value) {
|
|
|
|
|
- indexMap.initMap(mapPoint.value, mapContainer.value, true);
|
|
|
|
|
- indexMap.initData(getMapMarkerList(), "farmName", "wkt");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ nextTick(async () => {
|
|
|
|
|
+ await fetchFwTasks();
|
|
|
|
|
+ initMap();
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
onActivated(() => {
|
|
onActivated(() => {
|
|
|
nextTick(() => {
|
|
nextTick(() => {
|
|
|
if (!indexMap.kmap) {
|
|
if (!indexMap.kmap) {
|
|
|
- if (mapContainer.value) {
|
|
|
|
|
- mapPoint.value = store.state.home.miniUserLocationPoint || "POINT(113.614209 23.585836)";
|
|
|
|
|
- indexMap.initMap(mapPoint.value, mapContainer.value, true);
|
|
|
|
|
- indexMap.initData(getMapMarkerList(), "farmName", "wkt");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ initMap();
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
if (mapContainer.value && indexMap.kmap.map) {
|
|
if (mapContainer.value && indexMap.kmap.map) {
|
|
@@ -297,6 +314,7 @@ onActivated(() => {
|
|
|
const rect = container.getBoundingClientRect();
|
|
const rect = container.getBoundingClientRect();
|
|
|
if (rect.width > 0 && rect.height > 0) {
|
|
if (rect.width > 0 && rect.height > 0) {
|
|
|
indexMap.kmap.map.updateSize();
|
|
indexMap.kmap.map.updateSize();
|
|
|
|
|
+ refreshMapMarkers();
|
|
|
} else {
|
|
} else {
|
|
|
setTimeout(checkAndUpdateSize, 100);
|
|
setTimeout(checkAndUpdateSize, 100);
|
|
|
}
|
|
}
|