|
|
@@ -19,96 +19,116 @@
|
|
|
<span class="term-name">{{ t.displayName }}</span>
|
|
|
<span class="term-date">{{ formatDate(t.createDate) }}</span>
|
|
|
</div>
|
|
|
- <div v-for="(p, idx) in phenologyList" :key="`phenology-${uniqueTimestamp}-${idx}`" class="phenology-bar">
|
|
|
<div
|
|
|
- class="phenology-title"
|
|
|
- :class="{ 'phenology-red': !shouldShowBlue(p), 'phenology-blue': shouldShowBlue(p) }"
|
|
|
- v-if="p.reproductiveList[0]?.phenologyName === getNextPhenologyName(idx, 0)"
|
|
|
+ v-for="(p, idx) in phenologyList"
|
|
|
+ :key="`phenology-${uniqueTimestamp}-${idx}`"
|
|
|
+ class="phenology-bar"
|
|
|
>
|
|
|
- {{ p.reproductiveList[0]?.phenologyName }}
|
|
|
- </div>
|
|
|
- <div
|
|
|
- v-for="(r, rIdx) in Array.isArray(p.reproductiveList) ? p.reproductiveList : []"
|
|
|
- :key="`reproductive-${uniqueTimestamp}-${idx}-${rIdx}`"
|
|
|
- class="reproductive-item"
|
|
|
- >
|
|
|
- <div class="arranges">
|
|
|
- <div
|
|
|
- v-for="(fw, aIdx) in Array.isArray(r.farmWorkArrangeList) ? r.farmWorkArrangeList : []"
|
|
|
- :key="`arrange-${uniqueTimestamp}-${idx}-${rIdx}-${aIdx}`"
|
|
|
- class="arrange-card"
|
|
|
- :class="[
|
|
|
- getArrangeStatusClass(fw),
|
|
|
- {
|
|
|
- 'last-card':
|
|
|
- aIdx === r.farmWorkArrangeList.length - 1 &&
|
|
|
- rIdx !== r.farmWorkArrangeList.length - 1,
|
|
|
- },
|
|
|
- // 右侧农事卡片跟随物候期颜色:未来节气对应的农事卡片置灰
|
|
|
- { 'future-card': !shouldShowBlue(p) },
|
|
|
- ]"
|
|
|
- @click="handleRowClick(fw)"
|
|
|
- >
|
|
|
- <div class="card-content">
|
|
|
- <div class="card-left">
|
|
|
- <div class="left-info">
|
|
|
- <div class="left-date">{{ formatDate(fw.createTime) }}</div>
|
|
|
- <div class="text van-ellipsis" v-if="fw?.sourceType === 6">上传者:{{ fw.sourceDataJson.userName }}</div>
|
|
|
- <div class="text green van-ellipsis" v-if="fw?.sourceType === 7">执行者:{{ fw.sourceDataJson.executorName }}</div>
|
|
|
+ <div
|
|
|
+ class="phenology-title"
|
|
|
+ :class="{ 'phenology-red': !shouldShowBlue(p), 'phenology-blue': shouldShowBlue(p) }"
|
|
|
+ v-if="p.reproductiveList[0]?.phenologyName === getNextPhenologyName(idx, 0)"
|
|
|
+ >
|
|
|
+ {{ p.reproductiveList[0]?.phenologyName }}
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-for="(r, rIdx) in Array.isArray(p.reproductiveList) ? p.reproductiveList : []"
|
|
|
+ :key="`reproductive-${uniqueTimestamp}-${idx}-${rIdx}`"
|
|
|
+ class="reproductive-item"
|
|
|
+ >
|
|
|
+ <div class="arranges">
|
|
|
+ <div
|
|
|
+ v-for="(fw, aIdx) in Array.isArray(r.farmWorkArrangeList) ? r.farmWorkArrangeList : []"
|
|
|
+ :key="`arrange-${uniqueTimestamp}-${idx}-${rIdx}-${aIdx}`"
|
|
|
+ class="arrange-card"
|
|
|
+ :class="[
|
|
|
+ getArrangeStatusClass(fw),
|
|
|
+ {
|
|
|
+ 'last-card':
|
|
|
+ aIdx === r.farmWorkArrangeList.length - 1 &&
|
|
|
+ rIdx !== r.farmWorkArrangeList.length - 1,
|
|
|
+ },
|
|
|
+ // 右侧农事卡片跟随物候期颜色:未来节气对应的农事卡片置灰
|
|
|
+ { 'future-card': !shouldShowBlue(p) },
|
|
|
+ ]"
|
|
|
+ @click="handleRowClick(fw)"
|
|
|
+ >
|
|
|
+ <div class="card-content">
|
|
|
+ <div class="card-left">
|
|
|
+ <div class="left-info">
|
|
|
+ <div class="left-date">{{ formatDate(fw.createTime) }}</div>
|
|
|
+ <div class="text van-ellipsis" v-if="fw?.sourceType === 6">
|
|
|
+ 上传者:{{ fw.sourceDataJson.userName }}
|
|
|
+ </div>
|
|
|
+ <div class="text green van-ellipsis" v-if="fw?.sourceType === 7">
|
|
|
+ 执行者:{{ fw.sourceDataJson.executorName }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="title-text van-ellipsis">{{ fw.title }}</div>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="card-right"
|
|
|
+ v-if="fw.sourceDataJson && fw.sourceDataJson.resFilename"
|
|
|
+ @click="handleImageClick(fw)"
|
|
|
+ >
|
|
|
+ <img :src="base_img_url2 + fw.sourceDataJson?.resFilename?.[0]" alt="" />
|
|
|
+ <div class="num" v-if="fw?.sourceDataJson?.imageIds">
|
|
|
+ {{ fw?.sourceDataJson?.imageIds.length || 0 }}
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="title-text van-ellipsis">{{ fw.title }}</div>
|
|
|
- </div>
|
|
|
- <div class="card-right" v-if="fw.sourceDataJson" @click="handleImageClick(fw)">
|
|
|
- <img src="@/assets/img/home/farm.png" alt="" />
|
|
|
- <div class="num" v-if="fw?.sourceDataJson?.imageIds">{{ fw?.sourceDataJson?.imageIds.length || 0 }}</div>
|
|
|
- <!-- <div class="num" v-if="fw?.sourceDataJson?.imageIds">{{ fw?.sourceDataJson?.imageIds.length || 0 }}</div> -->
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <template v-if="r.name === r.phenologyName">
|
|
|
- <div
|
|
|
- class="phenology-name single"
|
|
|
- :class="{ 'phenology-red': !shouldShowBlue(p), 'phenology-blue': shouldShowBlue(p) }"
|
|
|
- :style="r.phenologyName === getNextPhenologyName(idx, rIdx) ? 'padding: 6px 0;' : ''"
|
|
|
- >
|
|
|
- {{ r.name }}
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template v-else>
|
|
|
- <template v-if="r.phenologyName === getNextPhenologyName(idx, rIdx)">
|
|
|
+ <template v-if="r.name === r.phenologyName">
|
|
|
<div
|
|
|
- class="phenology-name"
|
|
|
- :class="{ 'text-red': !shouldShowBlue(p), 'text-blue': shouldShowBlue(p) }"
|
|
|
+ class="phenology-name single"
|
|
|
+ :class="{ 'phenology-red': !shouldShowBlue(p), 'phenology-blue': shouldShowBlue(p) }"
|
|
|
+ :style="r.phenologyName === getNextPhenologyName(idx, rIdx) ? 'padding: 6px 0;' : ''"
|
|
|
>
|
|
|
{{ r.name }}
|
|
|
</div>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
- <div
|
|
|
- class="phenology-name"
|
|
|
- :class="{ 'text-red': !shouldShowBlue(p), 'text-blue': shouldShowBlue(p) }"
|
|
|
- >
|
|
|
- {{ r.name }}
|
|
|
- </div>
|
|
|
- <div
|
|
|
- class="phenology-name mr"
|
|
|
- :class="{ 'phenology-red': !shouldShowBlue(p), 'phenology-blue': shouldShowBlue(p) }"
|
|
|
- >
|
|
|
- {{ r.phenologyName }}
|
|
|
- </div>
|
|
|
+ <template v-if="r.phenologyName === getNextPhenologyName(idx, rIdx)">
|
|
|
+ <div
|
|
|
+ class="phenology-name"
|
|
|
+ :class="{ 'text-red': !shouldShowBlue(p), 'text-blue': shouldShowBlue(p) }"
|
|
|
+ >
|
|
|
+ {{ r.name }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <div
|
|
|
+ class="phenology-name"
|
|
|
+ :class="{ 'text-red': !shouldShowBlue(p), 'text-blue': shouldShowBlue(p) }"
|
|
|
+ >
|
|
|
+ {{ r.name }}
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="phenology-name mr"
|
|
|
+ :class="{
|
|
|
+ 'phenology-red': !shouldShowBlue(p),
|
|
|
+ 'phenology-blue': shouldShowBlue(p),
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ {{ r.phenologyName }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
</template>
|
|
|
- </template>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
</template>
|
|
|
</div>
|
|
|
</div>
|
|
|
<!-- 图片弹窗 -->
|
|
|
- <popup v-model:show="showImagePopup" closeable round class="image-popup" z-index="9999" teleport="body">
|
|
|
- <div class="popup-content">
|
|
|
- <p>图片详情</p>
|
|
|
- </div>
|
|
|
+ <popup v-model:show="showImagePopup" round class="image-popup" z-index="9999" teleport="body">
|
|
|
+ <album-carousel
|
|
|
+ class="popup-content"
|
|
|
+ :key="currentImageData?.sourceDataJson?.resFilename?.length"
|
|
|
+ labelText=""
|
|
|
+ :imgData="currentImageData"
|
|
|
+ :images="currentImageData?.sourceDataJson?.resFilename || []"
|
|
|
+ ></album-carousel>
|
|
|
</popup>
|
|
|
</template>
|
|
|
|
|
|
@@ -116,6 +136,8 @@
|
|
|
import { ref, nextTick, watch, onMounted, onUnmounted } from "vue";
|
|
|
import { ElMessage } from "element-plus";
|
|
|
import { Empty, Popup } from "vant";
|
|
|
+import { base_img_url2 } from "@/api/config";
|
|
|
+import AlbumCarousel from "@/components/album_compoents/albumCarousel";
|
|
|
|
|
|
const props = defineProps({
|
|
|
// 农场 ID,用于请求农事规划数据
|
|
|
@@ -238,11 +260,39 @@ const batchValidatePesticideFertilizerQuotes = (ids, items) => {
|
|
|
.catch(() => {});
|
|
|
};
|
|
|
|
|
|
+// 获取图片 URL 列表
|
|
|
+const fetchImageUrls = async (params) => {
|
|
|
+ try {
|
|
|
+ const res = await VE_API.ali.getTreeImageList(params);
|
|
|
+ if (res.code === 0 && Array.isArray(res.data)) {
|
|
|
+ // 将 resFilename 加上 base_img_url2 前缀后返回
|
|
|
+ return res.data.map((item) => {
|
|
|
+ if (item.filename) {
|
|
|
+ return item.filename;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return [];
|
|
|
+ } catch (error) {
|
|
|
+ console.error("获取图片列表失败:", error);
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
// 点击图片
|
|
|
+const currentImageData = ref({});
|
|
|
const handleImageClick = (fw) => {
|
|
|
- console.log(fw,'fw');
|
|
|
+ console.log(fw, "fw");
|
|
|
+ currentImageData.value = {
|
|
|
+ ...fw,
|
|
|
+ executeName: fw.sourceDataJson.userName,
|
|
|
+ executeDate: formatDate(fw.updateTime),
|
|
|
+ farmName: fw.sourceDataJson.farmName,
|
|
|
+ farmWorkLibName: fw.sourceDataJson.farmWorkLibName,
|
|
|
+ };
|
|
|
showImagePopup.value = true;
|
|
|
-}
|
|
|
+};
|
|
|
|
|
|
// 获取下一个reproductive-item的phenologyName
|
|
|
const getNextPhenologyName = (currentPhenologyIdx, currentReproductiveIdx) => {
|
|
|
@@ -448,7 +498,7 @@ const getFarmWorkPlan = () => {
|
|
|
|
|
|
VE_API.monitor
|
|
|
.getArchivesList({ farmId: props.farmId })
|
|
|
- .then(({ data, code }) => {
|
|
|
+ .then(async ({ data, code }) => {
|
|
|
if (code === 0) {
|
|
|
const list = Array.isArray(data?.solarTermsList) ? data.solarTermsList : [];
|
|
|
const filtered = list
|
|
|
@@ -465,35 +515,62 @@ const getFarmWorkPlan = () => {
|
|
|
}));
|
|
|
solarTerms.value = filtered;
|
|
|
// 物候期数据
|
|
|
- phenologyList.value = Array.isArray(data?.phenologyList)
|
|
|
- ? data.phenologyList.map((it) => {
|
|
|
- const reproductiveList = Array.isArray(it.reproductiveList)
|
|
|
- ? it.reproductiveList.map((r) => {
|
|
|
- const farmWorkArrangeList = Array.isArray(r.broadcastList)
|
|
|
- ? r.broadcastList.map((fw) => ({
|
|
|
- ...fw,
|
|
|
- sourceDataJson: fw.sourceData && JSON.parse(fw.sourceData),
|
|
|
- containerSpaceTimeId: it.containerSpaceTimeId,
|
|
|
- }))
|
|
|
- : [];
|
|
|
- return {
|
|
|
- ...r,
|
|
|
- farmWorkArrangeList,
|
|
|
- };
|
|
|
- })
|
|
|
- : [];
|
|
|
- return {
|
|
|
- id: it.id ?? it.phenologyId ?? it.name ?? `${it.progress}-${it.progress2}`,
|
|
|
- progress: Number(it.progress) || 0, // 起点 %
|
|
|
- progress2: Number(it.progress2) || 0, // 终点 %
|
|
|
- startDate: it.startDate,
|
|
|
- startTimeMs: safeParseDate(
|
|
|
- it.startDate || it.beginDate || it.startTime || it.start || it.start_at
|
|
|
- ),
|
|
|
- reproductiveList,
|
|
|
- };
|
|
|
- })
|
|
|
+ const processedPhenologyList = Array.isArray(data?.phenologyList)
|
|
|
+ ? await Promise.all(
|
|
|
+ data.phenologyList.map(async (it) => {
|
|
|
+ const reproductiveList = Array.isArray(it.reproductiveList)
|
|
|
+ ? await Promise.all(
|
|
|
+ it.reproductiveList.map(async (r) => {
|
|
|
+ const farmWorkArrangeList = Array.isArray(r.broadcastList)
|
|
|
+ ? await Promise.all(
|
|
|
+ r.broadcastList.map(async (fw) => {
|
|
|
+ let sourceDataJson =
|
|
|
+ fw.sourceData && JSON.parse(fw.sourceData);
|
|
|
+ // 如果有 imageIds,获取图片 URL
|
|
|
+ if (
|
|
|
+ sourceDataJson &&
|
|
|
+ sourceDataJson.imageIds &&
|
|
|
+ Array.isArray(sourceDataJson.imageIds) &&
|
|
|
+ sourceDataJson.imageIds.length > 0
|
|
|
+ ) {
|
|
|
+ const resFilenameList = await fetchImageUrls({
|
|
|
+ imageIds: sourceDataJson.imageIds,
|
|
|
+ page: 1,
|
|
|
+ limit: 100,
|
|
|
+ });
|
|
|
+ // 将获取到的 resFilename(已加上 base_img_url2 前缀)添加到 sourceDataJson
|
|
|
+ console.log(resFilenameList, "resFilenameList");
|
|
|
+ sourceDataJson.resFilename = resFilenameList;
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ ...fw,
|
|
|
+ sourceDataJson,
|
|
|
+ containerSpaceTimeId: it.containerSpaceTimeId,
|
|
|
+ };
|
|
|
+ })
|
|
|
+ )
|
|
|
+ : [];
|
|
|
+ return {
|
|
|
+ ...r,
|
|
|
+ farmWorkArrangeList,
|
|
|
+ };
|
|
|
+ })
|
|
|
+ )
|
|
|
+ : [];
|
|
|
+ return {
|
|
|
+ id: it.id ?? it.phenologyId ?? it.name ?? `${it.progress}-${it.progress2}`,
|
|
|
+ progress: Number(it.progress) || 0, // 起点 %
|
|
|
+ progress2: Number(it.progress2) || 0, // 终点 %
|
|
|
+ startDate: it.startDate,
|
|
|
+ startTimeMs: safeParseDate(
|
|
|
+ it.startDate || it.beginDate || it.startTime || it.start || it.start_at
|
|
|
+ ),
|
|
|
+ reproductiveList,
|
|
|
+ };
|
|
|
+ })
|
|
|
+ )
|
|
|
: [];
|
|
|
+ phenologyList.value = processedPhenologyList;
|
|
|
// 使用多次 nextTick 和 requestAnimationFrame 确保DOM完全渲染
|
|
|
nextTick(() => {
|
|
|
requestAnimationFrame(() => {
|
|
|
@@ -576,7 +653,7 @@ const getFarmWorkPlan = () => {
|
|
|
if (farmWorkIds.length > 0) {
|
|
|
batchValidatePesticideFertilizerQuotes(farmWorkIds, farmWorks);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 判断是否为空数据:没有节气或没有物候期数据
|
|
|
if (solarTerms.value.length === 0 && phenologyList.value.length === 0) {
|
|
|
isEmpty.value = true;
|
|
|
@@ -608,22 +685,14 @@ const updateFarmWorkPlan = () => {
|
|
|
};
|
|
|
|
|
|
watch(
|
|
|
- () => props.farmId || props.containerId,
|
|
|
+ () => props.farmId,
|
|
|
(val) => {
|
|
|
if (val) {
|
|
|
isInitialLoad.value = true;
|
|
|
updateFarmWorkPlan();
|
|
|
}
|
|
|
},
|
|
|
- { immediate: true }
|
|
|
-);
|
|
|
-watch(
|
|
|
- () => props.schemeId,
|
|
|
- (val) => {
|
|
|
- // if (val) {
|
|
|
- updateFarmWorkPlan();
|
|
|
- // }
|
|
|
- }
|
|
|
+ // { immediate: true }
|
|
|
);
|
|
|
|
|
|
// 格式化日期为 MM-DD 格式
|
|
|
@@ -910,7 +979,7 @@ watch(
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 5px;
|
|
|
- .left-date {
|
|
|
+ .left-date {
|
|
|
color: #fff;
|
|
|
background: #2199f8;
|
|
|
padding: 1px 5px;
|
|
|
@@ -940,6 +1009,8 @@ watch(
|
|
|
img {
|
|
|
width: 45px;
|
|
|
height: 45px;
|
|
|
+ border-radius: 4px;
|
|
|
+ object-fit: cover;
|
|
|
}
|
|
|
.num {
|
|
|
position: absolute;
|
|
|
@@ -1076,13 +1147,9 @@ watch(
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.image-popup {
|
|
|
- width: 100%;
|
|
|
+ width: 327px;
|
|
|
.popup-content {
|
|
|
- p {
|
|
|
- margin: 0;
|
|
|
- font-size: 16px;
|
|
|
- color: #333;
|
|
|
- }
|
|
|
+ width: 100%;
|
|
|
}
|
|
|
}
|
|
|
</style>
|