|
@@ -29,112 +29,13 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="content-panel">
|
|
<div class="content-panel">
|
|
|
- <div v-show="activeNav === 'patrol'">
|
|
|
|
|
- <div v-for="item in patrolList" :key="item.id" class="tracking-item"
|
|
|
|
|
- :class="`tracking-item--${item.theme}`" @click="goGrowthTrack(item)">
|
|
|
|
|
- <div class="tracking-item__header">
|
|
|
|
|
- <div class="tracking-item__header-row">
|
|
|
|
|
- <div class="tracking-item__header-left">
|
|
|
|
|
- <span class="tracking-item__level">
|
|
|
|
|
- <el-icon><BellFilled /></el-icon>
|
|
|
|
|
- {{ item.level }}
|
|
|
|
|
- </span>
|
|
|
|
|
- <span class="tracking-item__title van-ellipsis">
|
|
|
|
|
- {{ item.title }}<span v-if="item.zone" class="tracking-item__zone"> ({{ item.zone }})</span>
|
|
|
|
|
- </span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="tracking-item__share" @click.stop="handleShare(item)">
|
|
|
|
|
- <Icon size="14" color="#2199F8" name="share" />
|
|
|
|
|
- <span>{{ t("agriFile.forward") }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="tracking-item__body">
|
|
|
|
|
- <div class="tracking-item__action">
|
|
|
|
|
- <div class="tracking-item__action-main">
|
|
|
|
|
- <div class="tracking-item__icon">
|
|
|
|
|
- <img :src="item.icon" alt="" />
|
|
|
|
|
- <span class="tracking-item__reason">{{ item.subject }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="tracking-item__issue">{{ item.issue }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="tracking-item__btn" @click.stop="handleUploadClick(item)">
|
|
|
|
|
- <el-icon>
|
|
|
|
|
- <Plus />
|
|
|
|
|
- </el-icon>
|
|
|
|
|
- <span>{{ t("agriFile.uploadPhoto") }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="crop-archive">
|
|
|
|
|
- <div class="crop-archive__title">{{ t("agriFile.cropArchive") }}</div>
|
|
|
|
|
- <PhenologyTrackTimelineItem :list="cropArchiveList" />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div v-show="activeNav === 'album'">
|
|
|
|
|
- <div class="map-card" @click="goAlbumMap">
|
|
|
|
|
- <div ref="mapContainer" class="map-container"></div>
|
|
|
|
|
- <div v-if="noFarmRange" class="map-mask" @click.stop="goAddZone('farm')">
|
|
|
|
|
- <div class="mask-content">
|
|
|
|
|
- <div class="mask-content__title">勾画种植区域,关联作物照片</div>
|
|
|
|
|
- <div class="mask-content__btn">去勾选</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div v-else class="add-zone-btn" @click.stop="goAddZone('region')">
|
|
|
|
|
- <el-icon>
|
|
|
|
|
- <Plus />
|
|
|
|
|
- </el-icon>
|
|
|
|
|
- <span>{{ t("agriFile.addZone") }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div v-for="item in zoneList" :key="item.id" :ref="(el) => setMarkerEl(item.id, el)"
|
|
|
|
|
- class="zone-marker">
|
|
|
|
|
- <div class="zone-marker__label">{{ item.name }}</div>
|
|
|
|
|
- <div class="zone-marker__thumb">
|
|
|
|
|
- <img :src="item.cover" alt="" />
|
|
|
|
|
- <span class="zone-marker__badge">{{ item.count }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="zone-marker__arrow"></div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="album-grid" v-if="!noFarmRange">
|
|
|
|
|
- <div class="album-add" @click.stop="goAddZone('region')">
|
|
|
|
|
- <el-icon class="album-add__icon">
|
|
|
|
|
- <Plus />
|
|
|
|
|
- </el-icon>
|
|
|
|
|
- <span>{{ t("agriFile.addAlbum") }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div v-for="item in albumList" :key="item.id" class="album-card" :class="{ 'is-empty': !item.count }">
|
|
|
|
|
- <div class="album-card__cover" @click.stop="goAlbumDetail(item)">
|
|
|
|
|
- <img v-if="item.count && item.cover" :src="item.cover" alt="" />
|
|
|
|
|
- <img v-else class="album-card__empty-icon" src="@/assets/img/agricultural/photo.png" alt="" />
|
|
|
|
|
- <span class="album-card__count">{{ t("agriFile.photoCount", { count: item.count }) }}</span>
|
|
|
|
|
- <div class="album-card__add" @click.stop="openUploadPopup">
|
|
|
|
|
- <el-icon>
|
|
|
|
|
- <Plus />
|
|
|
|
|
- </el-icon>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="album-card__name">{{ item.name }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div v-show="activeNav === 'report'">
|
|
|
|
|
- <div v-for="item in reportList" :key="item.id" class="report-card">
|
|
|
|
|
- <div class="report-card__cover">
|
|
|
|
|
- <img :src="item.cover" alt="" />
|
|
|
|
|
- <span class="report-card__date">{{ item.date }}</span>
|
|
|
|
|
- <span class="report-card__status">
|
|
|
|
|
- <i class="report-card__dot"></i>
|
|
|
|
|
- {{ t("agriFile.reportUpdate") }}
|
|
|
|
|
- </span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="report-card__body">
|
|
|
|
|
- <div class="report-card__title">{{ item.title }}</div>
|
|
|
|
|
- <div class="report-card__desc">{{ item.desc }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <patrol-panel v-show="activeNav === 'patrol'" @open-upload="openUploadPopup" />
|
|
|
|
|
+ <album-panel
|
|
|
|
|
+ v-show="activeNav === 'album'"
|
|
|
|
|
+ :active="activeNav === 'album'"
|
|
|
|
|
+ @open-upload="openUploadPopup"
|
|
|
|
|
+ />
|
|
|
|
|
+ <report-panel v-show="activeNav === 'report'" />
|
|
|
</div>
|
|
</div>
|
|
|
<album-upload-popup v-model:show="showUploadPopup" />
|
|
<album-upload-popup v-model:show="showUploadPopup" />
|
|
|
<complete-farm-info-popup v-model:show="showCompleteFarmPopup" @confirm="goCompleteFarmInfo" />
|
|
<complete-farm-info-popup v-model:show="showCompleteFarmPopup" @confirm="goCompleteFarmInfo" />
|
|
@@ -168,14 +69,10 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { computed, nextTick, onActivated, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
|
|
|
|
|
+import { computed, onActivated, onBeforeUnmount, onMounted, ref } from "vue";
|
|
|
import { useStore } from "vuex";
|
|
import { useStore } from "vuex";
|
|
|
import { useRouter } from "vue-router";
|
|
import { useRouter } from "vue-router";
|
|
|
-import Overlay from "ol/Overlay";
|
|
|
|
|
import { useI18n } from "@/i18n";
|
|
import { useI18n } from "@/i18n";
|
|
|
-import { Icon } from 'vant';
|
|
|
|
|
-import FileMap from "./fileMap";
|
|
|
|
|
-import * as util from "@/common/ol_common.js";
|
|
|
|
|
import farmHeader from "@/components/farmHeader.vue";
|
|
import farmHeader from "@/components/farmHeader.vue";
|
|
|
import albumUploadPopup from "./components/albumUploadPopup.vue";
|
|
import albumUploadPopup from "./components/albumUploadPopup.vue";
|
|
|
import completeFarmInfoPopup from "@/components/popup/completeFarmInfoPopup.vue";
|
|
import completeFarmInfoPopup from "@/components/popup/completeFarmInfoPopup.vue";
|
|
@@ -183,28 +80,17 @@ import diagnosisReportPopup from "@/components/popup/diagnosisReportPopup.vue";
|
|
|
import restorePlantingPopup from "@/components/popup/restorePlantingPopup.vue";
|
|
import restorePlantingPopup from "@/components/popup/restorePlantingPopup.vue";
|
|
|
import proxyAuthPopup from "@/components/popup/proxyAuthPopup.vue";
|
|
import proxyAuthPopup from "@/components/popup/proxyAuthPopup.vue";
|
|
|
import phenologyUpdatePopup from "@/components/popup/phenologyUpdatePopup.vue";
|
|
import phenologyUpdatePopup from "@/components/popup/phenologyUpdatePopup.vue";
|
|
|
-import PhenologyTrackTimelineItem from "@/components/pageComponents/PhenologyTrackTimelineItem.vue";
|
|
|
|
|
-import wx from "weixin-js-sdk";
|
|
|
|
|
-import { base_img_url2 } from "@/api/config";
|
|
|
|
|
|
|
+import patrolPanel from "./components/patrolPanel.vue";
|
|
|
|
|
+import albumPanel from "./components/albumPanel.vue";
|
|
|
|
|
+import reportPanel from "./components/reportPanel.vue";
|
|
|
|
|
|
|
|
const { t } = useI18n();
|
|
const { t } = useI18n();
|
|
|
const store = useStore();
|
|
const store = useStore();
|
|
|
const router = useRouter();
|
|
const router = useRouter();
|
|
|
const tabBarHeight = computed(() => store.state.home.tabBarHeight);
|
|
const tabBarHeight = computed(() => store.state.home.tabBarHeight);
|
|
|
|
|
|
|
|
|
|
+// ---------- 常量 ----------
|
|
|
const DIAGNOSIS_GUIDE_STORAGE_KEY = "AGRI_FILE_DIAGNOSIS_REPORT_GUIDE";
|
|
const DIAGNOSIS_GUIDE_STORAGE_KEY = "AGRI_FILE_DIAGNOSIS_REPORT_GUIDE";
|
|
|
-const guideVisible = ref(false);
|
|
|
|
|
-
|
|
|
|
|
-const defaultCover = require("@/assets/img/home/banner.png");
|
|
|
|
|
-const DEFAULT_CENTER = [113.6142086995688, 23.585836479509055];
|
|
|
|
|
-
|
|
|
|
|
-const activeNav = ref("patrol");
|
|
|
|
|
-const mapContainer = ref(null);
|
|
|
|
|
-const fileMap = new FileMap();
|
|
|
|
|
-const markerEls = {};
|
|
|
|
|
-const mapOverlays = [];
|
|
|
|
|
-
|
|
|
|
|
-const noFarmRange = ref(false);
|
|
|
|
|
|
|
|
|
|
const navCards = [
|
|
const navCards = [
|
|
|
{
|
|
{
|
|
@@ -224,28 +110,32 @@ const navCards = [
|
|
|
},
|
|
},
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
-const zoneList = ref([]);
|
|
|
|
|
-
|
|
|
|
|
-const albumList = ref([
|
|
|
|
|
- { id: 1, name: "", count: 0, cover: "" },
|
|
|
|
|
- { id: 2, name: "", count: 125, cover: defaultCover },
|
|
|
|
|
- { id: 3, name: "", count: 125, cover: defaultCover },
|
|
|
|
|
-]);
|
|
|
|
|
-
|
|
|
|
|
-const reportCover = require("@/assets/img/common/sd-1.jpg");
|
|
|
|
|
-const reportList = ref([
|
|
|
|
|
- { id: 1, title: "", desc: "", date: "", cover: reportCover },
|
|
|
|
|
- { id: 2, title: "", desc: "", date: "", cover: require("@/assets/img/common/sd-2.jpg") },
|
|
|
|
|
- { id: 3, title: "", desc: "", date: "", cover: require("@/assets/img/common/sd-3.jpg") },
|
|
|
|
|
-]);
|
|
|
|
|
-
|
|
|
|
|
|
|
+// ---------- 页面状态 ----------
|
|
|
|
|
+const activeNav = ref("patrol");
|
|
|
|
|
+const guideVisible = ref(false);
|
|
|
const showUploadPopup = ref(false);
|
|
const showUploadPopup = ref(false);
|
|
|
const showCompleteFarmPopup = ref(false);
|
|
const showCompleteFarmPopup = ref(false);
|
|
|
|
|
|
|
|
|
|
+/** 后续由接口控制是否展示代管授权弹窗 */
|
|
|
|
|
+const showProxyAuthPopup = ref(false);
|
|
|
|
|
+const proxyServiceName = ref("农服名称");
|
|
|
|
|
+
|
|
|
|
|
+/** 后续由接口控制是否展示申请更换物候期弹窗 */
|
|
|
|
|
+const showPhenologyUpdatePopup = ref(false);
|
|
|
|
|
+const phenologyUpdateInfo = ref({
|
|
|
|
|
+ serviceName: "某某农服",
|
|
|
|
|
+ currentStage: "花穗期",
|
|
|
|
|
+ targetStage: "小果期",
|
|
|
|
|
+ varietyName: "荔枝-桂味",
|
|
|
|
|
+ farmLocation: "--",
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 工具函数 ----------
|
|
|
function getMiniUserId() {
|
|
function getMiniUserId() {
|
|
|
return store.state.home.miniUserId || localStorage.getItem("MINI_USER_ID");
|
|
return store.state.home.miniUserId || localStorage.getItem("MINI_USER_ID");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// ---------- 业务逻辑 ----------
|
|
|
/** 无果园信息时展示「完善农场信息」弹窗 */
|
|
/** 无果园信息时展示「完善农场信息」弹窗 */
|
|
|
async function checkCompleteFarmPopup() {
|
|
async function checkCompleteFarmPopup() {
|
|
|
const id = getMiniUserId();
|
|
const id = getMiniUserId();
|
|
@@ -262,368 +152,6 @@ async function checkCompleteFarmPopup() {
|
|
|
showCompleteFarmPopup.value = false;
|
|
showCompleteFarmPopup.value = false;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-// 后续由接口控制是否展示代管授权弹窗
|
|
|
|
|
-const showProxyAuthPopup = ref(false);
|
|
|
|
|
-const proxyServiceName = ref("农服名称");
|
|
|
|
|
-
|
|
|
|
|
-const handleProxyReject = () => {
|
|
|
|
|
- // TODO: 调用拒绝代管接口
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const handleProxyAllow = () => {
|
|
|
|
|
- // TODO: 调用允许代管接口
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-// 后续由接口控制是否展示申请更换物候期弹窗
|
|
|
|
|
-const showPhenologyUpdatePopup = ref(false);
|
|
|
|
|
-const phenologyUpdateInfo = ref({
|
|
|
|
|
- serviceName: "某某农服",
|
|
|
|
|
- currentStage: "花穗期",
|
|
|
|
|
- targetStage: "小果期",
|
|
|
|
|
- varietyName: "荔枝-桂味",
|
|
|
|
|
- farmLocation: "位置位置位置",
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const handlePhenologyUpdateReject = () => {
|
|
|
|
|
- // TODO: 调用暂不更新接口
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const handlePhenologyUpdateConfirm = () => {
|
|
|
|
|
- // TODO: 调用确认更新接口
|
|
|
|
|
-};
|
|
|
|
|
-const PATROL_ICON = require("@/assets/img/report/wh-icon.png");
|
|
|
|
|
-const patrolList = ref([
|
|
|
|
|
- {
|
|
|
|
|
- id: 1,
|
|
|
|
|
- type: "growth",
|
|
|
|
|
- theme: "blue",
|
|
|
|
|
- level: "",
|
|
|
|
|
- title: "",
|
|
|
|
|
- subject: "",
|
|
|
|
|
- issue: "",
|
|
|
|
|
- desc: "",
|
|
|
|
|
- icon: PATROL_ICON,
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 2,
|
|
|
|
|
- type: "abnormal",
|
|
|
|
|
- theme: "blue",
|
|
|
|
|
- level: "",
|
|
|
|
|
- title: "",
|
|
|
|
|
- subject: "",
|
|
|
|
|
- issue: "",
|
|
|
|
|
- icon: PATROL_ICON,
|
|
|
|
|
- },
|
|
|
|
|
-]);
|
|
|
|
|
-
|
|
|
|
|
-/** 外部感知任务 → 巡园卡片(仅取第一条) */
|
|
|
|
|
-const mapInteractTaskToPatrol = (task) => {
|
|
|
|
|
- const content = task?.interact_content || {};
|
|
|
|
|
- return {
|
|
|
|
|
- id: task.zone_id || task.interact_code,
|
|
|
|
|
- type: "growth",
|
|
|
|
|
- theme: "blue",
|
|
|
|
|
- level: content.interact_level || "",
|
|
|
|
|
- title: t("agriFile.patrolGrowthTitle") || "",
|
|
|
|
|
- desc: content.interact_reason || "",
|
|
|
|
|
- issue: content.interact_reason || "",
|
|
|
|
|
- subject: content.interact_title || "",
|
|
|
|
|
- icon: PATROL_ICON,
|
|
|
|
|
- zoneId: task.zone_id,
|
|
|
|
|
- interactCode: task.interact_code,
|
|
|
|
|
- raw: task,
|
|
|
|
|
- };
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const fetchPatrolInteractTask = async () => {
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await VE_API.questionnaire.getExternalInteractTasks({ zone_id: 42 });
|
|
|
|
|
- const tasks = res?.data?.interact_tasks;
|
|
|
|
|
- const first = Array.isArray(tasks) ? tasks[0] : null;
|
|
|
|
|
- if (!first) return;
|
|
|
|
|
- const mapped = mapInteractTaskToPatrol(first);
|
|
|
|
|
- if (patrolList.value.length) {
|
|
|
|
|
- patrolList.value[0] = {
|
|
|
|
|
- ...patrolList.value[0],
|
|
|
|
|
- ...mapped,
|
|
|
|
|
- };
|
|
|
|
|
- } else {
|
|
|
|
|
- patrolList.value = [mapped];
|
|
|
|
|
- }
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- console.warn("[agri_file] getExternalInteractTasks failed", e);
|
|
|
|
|
- }
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const cropArchiveList = ref([]);
|
|
|
|
|
-
|
|
|
|
|
-const mapPhenoRecordToArchive = (item, index, zoneName) => ({
|
|
|
|
|
- id: [item.zone_id, item.enter_date, item.period_code, index].filter((v) => v != null && v !== "").join("_"),
|
|
|
|
|
- date: item.enter_date || "",
|
|
|
|
|
- zone_name: item.zone_name || zoneName || "",
|
|
|
|
|
- content: item.sentence || (item.period_name ? `到达 ${item.period_name}` : ""),
|
|
|
|
|
- period_code: item.period_code,
|
|
|
|
|
- period_name: item.period_name,
|
|
|
|
|
- shoot_type: item.shoot_type,
|
|
|
|
|
- zone_id: item.zone_id,
|
|
|
|
|
- crop_code: item.crop_code,
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const fetchCropArchive = async () => {
|
|
|
|
|
- const farm = JSON.parse(localStorage.getItem("selectedFarmData") || "{}");
|
|
|
|
|
- const params = {
|
|
|
|
|
- farm_id: farm.farm_id,
|
|
|
|
|
- zone_id: 46,
|
|
|
|
|
- crop_code: 'LCH',
|
|
|
|
|
- }
|
|
|
|
|
- if (!params) {
|
|
|
|
|
- cropArchiveList.value = [];
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- const { zone_name: zoneName, ...query } = params;
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await VE_API.entry.getPhenoRecord(query);
|
|
|
|
|
- if (res?.code !== 200) {
|
|
|
|
|
- cropArchiveList.value = [];
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- const list = Array.isArray(res.data) ? res.data : [];
|
|
|
|
|
- const fallbackZone = zoneName || t("agriFile.zoneOne");
|
|
|
|
|
- cropArchiveList.value = list
|
|
|
|
|
- .map((item, index) => mapPhenoRecordToArchive(item, index, fallbackZone))
|
|
|
|
|
- .sort((a, b) => String(b.date).localeCompare(String(a.date)));
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- console.warn("[agri_file] getPhenoRecord failed", e);
|
|
|
|
|
- cropArchiveList.value = [];
|
|
|
|
|
- }
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const setMarkerEl = (id, el) => {
|
|
|
|
|
- if (el) markerEls[id] = el;
|
|
|
|
|
- else delete markerEls[id];
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-function getSelectedFarmId() {
|
|
|
|
|
- const stored = localStorage.getItem("selectedFarmId");
|
|
|
|
|
- if (stored != null && stored !== "") {
|
|
|
|
|
- return Number(stored);
|
|
|
|
|
- }
|
|
|
|
|
- try {
|
|
|
|
|
- const farm = JSON.parse(localStorage.getItem("selectedFarmData") || "{}");
|
|
|
|
|
- const id = farm.farm_id ?? farm.id;
|
|
|
|
|
- return id != null && id !== "" ? Number(id) : null;
|
|
|
|
|
- } catch {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function parsePointCoordinates(pointWkt) {
|
|
|
|
|
- if (!pointWkt || !/^POINT\s*\(/i.test(String(pointWkt).trim())) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- try {
|
|
|
|
|
- const coord = util.wktCastGeom(pointWkt).getFirstCoordinate();
|
|
|
|
|
- if (!coord || coord.length < 2) return null;
|
|
|
|
|
- return { longitude: coord[0], latitude: coord[1] };
|
|
|
|
|
- } catch {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function resolveZoneCoverUrl(path) {
|
|
|
|
|
- if (!path) return defaultCover;
|
|
|
|
|
- const text = String(path).trim();
|
|
|
|
|
- if (!text) return defaultCover;
|
|
|
|
|
- if (/^https?:\/\//i.test(text)) return text;
|
|
|
|
|
- return base_img_url2 + text.replace(/^\//, "");
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function mapZoneItem(zone) {
|
|
|
|
|
- const coords = parsePointCoordinates(zone?.point);
|
|
|
|
|
- return {
|
|
|
|
|
- id: zone.zone_id,
|
|
|
|
|
- zoneId: zone.zone_id,
|
|
|
|
|
- name: zone.zone_name || "",
|
|
|
|
|
- count: zone.image_count ?? 0,
|
|
|
|
|
- cover: resolveZoneCoverUrl(zone.cover_url),
|
|
|
|
|
- longitude: coords?.longitude,
|
|
|
|
|
- latitude: coords?.latitude,
|
|
|
|
|
- point: zone.point,
|
|
|
|
|
- };
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function fetchZoneList() {
|
|
|
|
|
- const farmId = getSelectedFarmId();
|
|
|
|
|
- if (!farmId) {
|
|
|
|
|
- zoneList.value = [];
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await VE_API.questionnaire.getZones({ farm_id: farmId });
|
|
|
|
|
- const zones = res?.data?.zones;
|
|
|
|
|
- zoneList.value = Array.isArray(zones) ? zones.map(mapZoneItem) : [];
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- console.warn("[agri_file] getZones failed", e);
|
|
|
|
|
- zoneList.value = [];
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const getFarmCenter = () => {
|
|
|
|
|
- try {
|
|
|
|
|
- const farm = JSON.parse(localStorage.getItem("selectedFarmData") || "{}");
|
|
|
|
|
- const wkt = farm.wkt || farm.geom_wkt || farm.farm_location;
|
|
|
|
|
- if (typeof wkt === "string" && /^POINT\s*\(/i.test(wkt.trim())) {
|
|
|
|
|
- return util.wktCastGeom(wkt).getFirstCoordinate();
|
|
|
|
|
- }
|
|
|
|
|
- } catch {
|
|
|
|
|
- // ignore
|
|
|
|
|
- }
|
|
|
|
|
- const firstZone = zoneList.value.find(
|
|
|
|
|
- (z) => z.longitude != null && z.latitude != null
|
|
|
|
|
- );
|
|
|
|
|
- if (firstZone) {
|
|
|
|
|
- return [firstZone.longitude, firstZone.latitude];
|
|
|
|
|
- }
|
|
|
|
|
- return DEFAULT_CENTER;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const clearMapOverlays = () => {
|
|
|
|
|
- if (!fileMap.kmap?.map) {
|
|
|
|
|
- mapOverlays.length = 0;
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- mapOverlays.forEach((overlay) => fileMap.kmap.map.removeOverlay(overlay));
|
|
|
|
|
- mapOverlays.length = 0;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const bindZoneOverlays = () => {
|
|
|
|
|
- if (!fileMap.kmap?.map) return;
|
|
|
|
|
- clearMapOverlays();
|
|
|
|
|
- zoneList.value.forEach((zone) => {
|
|
|
|
|
- const el = markerEls[zone.id];
|
|
|
|
|
- if (!el || zone.longitude == null || zone.latitude == null) return;
|
|
|
|
|
- const overlay = new Overlay({
|
|
|
|
|
- element: el,
|
|
|
|
|
- position: [zone.longitude, zone.latitude],
|
|
|
|
|
- positioning: "bottom-center",
|
|
|
|
|
- stopEvent: false,
|
|
|
|
|
- });
|
|
|
|
|
- fileMap.kmap.map.addOverlay(overlay);
|
|
|
|
|
- mapOverlays.push(overlay);
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const initAlbumMap = async () => {
|
|
|
|
|
- await nextTick();
|
|
|
|
|
- if (!mapContainer.value) return;
|
|
|
|
|
- if (noFarmRange.value) {
|
|
|
|
|
- zoneList.value = [];
|
|
|
|
|
- albumList.value = [];
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- await fetchZoneList();
|
|
|
|
|
- const center = getFarmCenter();
|
|
|
|
|
- fileMap.initMap(`POINT(${center[0]} ${center[1]})`, mapContainer.value);
|
|
|
|
|
- await nextTick();
|
|
|
|
|
- bindZoneOverlays();
|
|
|
|
|
- fileMap.kmap?.map?.updateSize?.();
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const fillMockLabels = () => {
|
|
|
|
|
- const albumName = t("agriFile.zoneOne");
|
|
|
|
|
- albumList.value = albumList.value.map((item) => ({ ...item, name: albumName }));
|
|
|
|
|
- reportList.value = reportList.value.map((item) => ({
|
|
|
|
|
- ...item,
|
|
|
|
|
- title: t("agriFile.reportTitle"),
|
|
|
|
|
- desc: t("agriFile.reportDesc"),
|
|
|
|
|
- date: t("agriFile.reportDate"),
|
|
|
|
|
- }));
|
|
|
|
|
- patrolList.value = patrolList.value.map((item, index) => {
|
|
|
|
|
- // 第一条由外部感知任务接口填充,不覆盖
|
|
|
|
|
- if (index === 0 && item.interactCode) return item;
|
|
|
|
|
- return {
|
|
|
|
|
- ...item,
|
|
|
|
|
- level: t("agriFile.riskLevel2"),
|
|
|
|
|
- title: t(index === 0 ? "agriFile.patrolGrowthTitle" : "agriFile.patrolAbnormalTitle"),
|
|
|
|
|
- zone: albumName,
|
|
|
|
|
- subject: t("agriFile.patrolSubject"),
|
|
|
|
|
- issue: t("agriFile.patrolIssue"),
|
|
|
|
|
- };
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const goAddZone = (type) => {
|
|
|
|
|
- router.push(
|
|
|
|
|
- {
|
|
|
|
|
- path: "/add_zone",
|
|
|
|
|
- query: {
|
|
|
|
|
- type,
|
|
|
|
|
- isSuccessPopup: type === 'farm' ? '1' : '0',
|
|
|
|
|
- },
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const goAlbumMap = () => {
|
|
|
|
|
- router.push("/album_map");
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const goGrowthTrack = (item) => {
|
|
|
|
|
- router.push({
|
|
|
|
|
- path: "/growth_track",
|
|
|
|
|
- query: {
|
|
|
|
|
- id: item?.id,
|
|
|
|
|
- type: item?.type || "growth",
|
|
|
|
|
- pageName: item.type === 'growth' ? t("agriFile.patrolGrowthTitle") : t("agriFile.patrolAbnormalTitle"),
|
|
|
|
|
- },
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const handleShare = (item) => {
|
|
|
|
|
- const query = {
|
|
|
|
|
- askInfo: { title: "转发巡园要点", content: "是否分享给好友" },
|
|
|
|
|
- shareText: item?.title || "速长窗口不追肥?后期发育慢人一步,直接掉队!",
|
|
|
|
|
- targetUrl: "growth_track",
|
|
|
|
|
- paramsPage: JSON.stringify({
|
|
|
|
|
- id: item?.id,
|
|
|
|
|
- type: item?.type || "growth",
|
|
|
|
|
- fromShare: 1,
|
|
|
|
|
- }),
|
|
|
|
|
- imageUrl: "https://birdseye-img.sysuimars.com/temp/field.png",
|
|
|
|
|
- };
|
|
|
|
|
- wx.miniProgram.navigateTo({
|
|
|
|
|
- url: `/pages/subPages/share_page/index?pageParams=${JSON.stringify(query)}&type=sharePage`,
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const needDraw = ref(true);
|
|
|
|
|
-
|
|
|
|
|
-const openUploadPopup = () => {
|
|
|
|
|
- showUploadPopup.value = true;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/** 与 growthTrack 上传一致:需勾画 → add_zone;异常 → pest_interact;否则弹上传 */
|
|
|
|
|
-const handleUploadClick = (item) => {
|
|
|
|
|
- const isAbnormal = item?.type === "abnormal";
|
|
|
|
|
- if (needDraw.value) {
|
|
|
|
|
- router.push({
|
|
|
|
|
- path: "/add_zone",
|
|
|
|
|
- query: {
|
|
|
|
|
- type: "farm",
|
|
|
|
|
- targetUrl: isAbnormal ? "/pest_interact" : "/growth_track",
|
|
|
|
|
- ...(isAbnormal ? { isAbnormal: "true" } : {}),
|
|
|
|
|
- ...(item?.id != null ? { trackId: item.id } : {}),
|
|
|
|
|
- ...(item?.type ? { trackType: item.type } : {}),
|
|
|
|
|
- },
|
|
|
|
|
- });
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (isAbnormal) {
|
|
|
|
|
- router.push({ path: "/pest_interact" });
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- showUploadPopup.value = true;
|
|
|
|
|
-};
|
|
|
|
|
|
|
|
|
|
const dismissGuide = () => {
|
|
const dismissGuide = () => {
|
|
|
if (!guideVisible.value) return;
|
|
if (!guideVisible.value) return;
|
|
@@ -636,6 +164,11 @@ const tryShowGuide = () => {
|
|
|
guideVisible.value = true;
|
|
guideVisible.value = true;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+// ---------- 事件处理 ----------
|
|
|
|
|
+const openUploadPopup = () => {
|
|
|
|
|
+ showUploadPopup.value = true;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const handleGuideUpload = () => {
|
|
const handleGuideUpload = () => {
|
|
|
dismissGuide();
|
|
dismissGuide();
|
|
|
openUploadPopup();
|
|
openUploadPopup();
|
|
@@ -662,43 +195,35 @@ const goCompleteFarmInfo = () => {
|
|
|
router.push("/entry_information");
|
|
router.push("/entry_information");
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const goAlbumDetail = (item) => {
|
|
|
|
|
- router.push({
|
|
|
|
|
- path: "/region_albums",
|
|
|
|
|
- query: {
|
|
|
|
|
- id: item?.id,
|
|
|
|
|
- name: item?.name || "",
|
|
|
|
|
- noImage: item?.count ? "false" : "true",
|
|
|
|
|
- },
|
|
|
|
|
- });
|
|
|
|
|
|
|
+const handleProxyReject = () => {
|
|
|
|
|
+ // TODO: 调用拒绝代管接口
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const handleProxyAllow = () => {
|
|
|
|
|
+ // TODO: 调用允许代管接口
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+const handlePhenologyUpdateReject = () => {
|
|
|
|
|
+ // TODO: 调用暂不更新接口
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const handlePhenologyUpdateConfirm = () => {
|
|
|
|
|
+ // TODO: 调用确认更新接口
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// ---------- 生命周期 ----------
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
- fillMockLabels();
|
|
|
|
|
checkCompleteFarmPopup();
|
|
checkCompleteFarmPopup();
|
|
|
- fetchPatrolInteractTask();
|
|
|
|
|
- initAlbumMap();
|
|
|
|
|
tryShowGuide();
|
|
tryShowGuide();
|
|
|
document.addEventListener("click", handleOutsideClick, true);
|
|
document.addEventListener("click", handleOutsideClick, true);
|
|
|
});
|
|
});
|
|
|
onActivated(() => {
|
|
onActivated(() => {
|
|
|
- fillMockLabels();
|
|
|
|
|
checkCompleteFarmPopup();
|
|
checkCompleteFarmPopup();
|
|
|
- fetchPatrolInteractTask();
|
|
|
|
|
- fetchCropArchive();
|
|
|
|
|
- initAlbumMap();
|
|
|
|
|
tryShowGuide();
|
|
tryShowGuide();
|
|
|
});
|
|
});
|
|
|
onBeforeUnmount(() => {
|
|
onBeforeUnmount(() => {
|
|
|
- clearMapOverlays();
|
|
|
|
|
document.removeEventListener("click", handleOutsideClick, true);
|
|
document.removeEventListener("click", handleOutsideClick, true);
|
|
|
});
|
|
});
|
|
|
-
|
|
|
|
|
-watch(activeNav, (key) => {
|
|
|
|
|
- if (key === "album") {
|
|
|
|
|
- initAlbumMap();
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
<style lang="scss" scoped>
|
|
@@ -709,7 +234,7 @@ watch(activeNav, (key) => {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
height: 100%;
|
|
height: 100%;
|
|
|
overflow: hidden;
|
|
overflow: hidden;
|
|
|
- background: linear-gradient(180deg, #D1EBFF 0%, #FFFFFF 100%);
|
|
|
|
|
|
|
+ background: linear-gradient(180deg, #d1ebff 0%, #ffffff 100%);
|
|
|
|
|
|
|
|
.nav-cards {
|
|
.nav-cards {
|
|
|
position: relative;
|
|
position: relative;
|
|
@@ -737,7 +262,7 @@ watch(activeNav, (key) => {
|
|
|
background: #fff;
|
|
background: #fff;
|
|
|
width: 90px;
|
|
width: 90px;
|
|
|
height: 64px;
|
|
height: 64px;
|
|
|
- box-shadow: 0px 4px 4px 0px #0000000D;
|
|
|
|
|
|
|
+ box-shadow: 0px 4px 4px 0px #0000000d;
|
|
|
|
|
|
|
|
.nav-card__icon {
|
|
.nav-card__icon {
|
|
|
width: 26px;
|
|
width: 26px;
|
|
@@ -746,7 +271,7 @@ watch(activeNav, (key) => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
&.is-active {
|
|
&.is-active {
|
|
|
- background: linear-gradient(180deg, #5BB8FF 0%, #2199F8 100%);
|
|
|
|
|
|
|
+ background: linear-gradient(180deg, #5bb8ff 0%, #2199f8 100%);
|
|
|
box-shadow: 0 4px 10px rgba(33, 153, 248, 0.28);
|
|
box-shadow: 0 4px 10px rgba(33, 153, 248, 0.28);
|
|
|
|
|
|
|
|
.nav-card__icon {
|
|
.nav-card__icon {
|
|
@@ -765,7 +290,7 @@ watch(activeNav, (key) => {
|
|
|
transform: translateX(-50%);
|
|
transform: translateX(-50%);
|
|
|
border-left: 6px solid transparent;
|
|
border-left: 6px solid transparent;
|
|
|
border-right: 6px solid transparent;
|
|
border-right: 6px solid transparent;
|
|
|
- border-top: 6px solid #2199F8;
|
|
|
|
|
|
|
+ border-top: 6px solid #2199f8;
|
|
|
z-index: 3;
|
|
z-index: 3;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -816,457 +341,12 @@ watch(activeNav, (key) => {
|
|
|
overflow-y: auto;
|
|
overflow-y: auto;
|
|
|
background: linear-gradient(360deg, rgba(229, 243, 255, 0.55) 0%, rgba(255, 255, 255, 0.55) 100%);
|
|
background: linear-gradient(360deg, rgba(229, 243, 255, 0.55) 0%, rgba(255, 255, 255, 0.55) 100%);
|
|
|
border-radius: 20px 20px 0 0;
|
|
border-radius: 20px 20px 0 0;
|
|
|
- border: 1px solid #FFFFFF;
|
|
|
|
|
|
|
+ border: 1px solid #ffffff;
|
|
|
padding: 18px 10px;
|
|
padding: 18px 10px;
|
|
|
|
|
|
|
|
&::-webkit-scrollbar {
|
|
&::-webkit-scrollbar {
|
|
|
display: none;
|
|
display: none;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- .map-card {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- height: 210px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .map-container {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- clip-path: inset(0px round 8px);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .map-mask {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- top: 0;
|
|
|
|
|
- left: 0;
|
|
|
|
|
- z-index: 5;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- background: rgba(0, 0, 0, 0.5);
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- pointer-events: auto;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
-
|
|
|
|
|
- .mask-content {
|
|
|
|
|
- background: rgba(255, 255, 255, 0.2);
|
|
|
|
|
- backdrop-filter: blur(4px);
|
|
|
|
|
- padding: 8px 10px;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- width: fit-content;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- gap: 12px;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- pointer-events: none;
|
|
|
|
|
- &__title {
|
|
|
|
|
- font-size: 18px;
|
|
|
|
|
- font-family: "PangMenZhengDao";
|
|
|
|
|
- }
|
|
|
|
|
- &__btn {
|
|
|
|
|
- padding: 0 24px;
|
|
|
|
|
- height: 32px;
|
|
|
|
|
- line-height: 32px;
|
|
|
|
|
- border-radius: 24px;
|
|
|
|
|
- background: #2199F8;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .zone-marker {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- pointer-events: none;
|
|
|
|
|
-
|
|
|
|
|
- &__label {
|
|
|
|
|
- padding: 3px 14px;
|
|
|
|
|
- border-radius: 25px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- color: #0A0A0A;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__thumb {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- width: 42px;
|
|
|
|
|
- height: 42px;
|
|
|
|
|
- margin-top: 8px;
|
|
|
|
|
-
|
|
|
|
|
- img {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- object-fit: cover;
|
|
|
|
|
- border: 2px solid #fff;
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__badge {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- top: -6px;
|
|
|
|
|
- right: -6px;
|
|
|
|
|
- min-width: 18px;
|
|
|
|
|
- height: 18px;
|
|
|
|
|
- line-height: 16px;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- color: #0A0A0A;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__arrow {
|
|
|
|
|
- width: 0;
|
|
|
|
|
- height: 0;
|
|
|
|
|
- border-left: 5px solid transparent;
|
|
|
|
|
- border-right: 5px solid transparent;
|
|
|
|
|
- border-top: 6px solid #fff;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .add-zone-btn {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- right: 5px;
|
|
|
|
|
- bottom: 10px;
|
|
|
|
|
- z-index: 5;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 2px;
|
|
|
|
|
- padding: 5px 10px;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- color: #2199F8;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .album-grid {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-template-columns: 1fr 1fr;
|
|
|
|
|
- gap: 16px;
|
|
|
|
|
- margin-top: 16px;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
-
|
|
|
|
|
- .album-add {
|
|
|
|
|
- align-self: start;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- aspect-ratio: 1;
|
|
|
|
|
- border-radius: 5px;
|
|
|
|
|
- background: #FFFFFF;
|
|
|
|
|
- color: #969494;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
-
|
|
|
|
|
- &__icon {
|
|
|
|
|
- margin-bottom: 6px;
|
|
|
|
|
- font-size: 20px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .album-card {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
-
|
|
|
|
|
- &.is-empty {
|
|
|
|
|
- .album-card__cover {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- background: linear-gradient(180deg, #E0F1FF 0%, #C2E4FF 100%);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__cover {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- aspect-ratio: 1;
|
|
|
|
|
-
|
|
|
|
|
- img:not(.album-card__empty-icon) {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- object-fit: cover;
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__empty-icon {
|
|
|
|
|
- width: 50px;
|
|
|
|
|
- height: 50px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__count {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- top: 1px;
|
|
|
|
|
- right: 0;
|
|
|
|
|
- padding: 1px 5px;
|
|
|
|
|
- border-radius: 0 5px 0 5px;
|
|
|
|
|
- background: rgba(0, 0, 0, 0.4);
|
|
|
|
|
- backdrop-filter: blur(4px);
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__add {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- right: 8px;
|
|
|
|
|
- bottom: 8px;
|
|
|
|
|
- z-index: 1;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- width: 44px;
|
|
|
|
|
- height: 44px;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- background: #2199F8;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- font-size: 20px;
|
|
|
|
|
- box-shadow: 0 2px 8px rgba(33, 153, 248, 0.35);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__name {
|
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
- color: #1F1F1F;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .report-card {
|
|
|
|
|
- margin-bottom: 10px;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
-
|
|
|
|
|
- &:last-child {
|
|
|
|
|
- margin-bottom: 0;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__cover {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 158px;
|
|
|
|
|
-
|
|
|
|
|
- img {
|
|
|
|
|
- display: block;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- object-fit: cover;
|
|
|
|
|
- border-radius: 5px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__date {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- top: 0;
|
|
|
|
|
- padding: 5px 12px;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- background: rgba(0, 0, 0, 0.45);
|
|
|
|
|
- backdrop-filter: blur(4px);
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- border-radius: 5px 0 5px 0;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__status {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- top: 10px;
|
|
|
|
|
- right: 0;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 4px;
|
|
|
|
|
- padding: 2px 13px;
|
|
|
|
|
- border-radius: 20px 0 0 20px;
|
|
|
|
|
- background: #FF5454;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__dot {
|
|
|
|
|
- width: 6px;
|
|
|
|
|
- height: 6px;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__body {
|
|
|
|
|
- padding: 10px;
|
|
|
|
|
- margin-top: 4px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__title {
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__desc {
|
|
|
|
|
- margin-top: 4px;
|
|
|
|
|
- color: #9E9B9B;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .tracking-item {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- margin-bottom: 16px;
|
|
|
|
|
- box-shadow: 0px 4px 4px 0px rgba(33, 153, 248, 0.1);
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- &:last-of-type {
|
|
|
|
|
- margin-bottom: 0;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__header {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- padding: 10px 10px 20px;
|
|
|
|
|
- border-radius: 10px 10px 0 0;
|
|
|
|
|
- background: linear-gradient(180deg, #86CAFF 0%, #2199F8 48.5%);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__header-row {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__header-left {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 5px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__level {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 4px;
|
|
|
|
|
- padding: 1px 4px;
|
|
|
|
|
- border-radius: 2px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- color: #FF6A6A;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__title {
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__zone {
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__share {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 4px;
|
|
|
|
|
- padding: 2px 10px;
|
|
|
|
|
- border-radius: 25px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- color: #2199F8;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__body {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- padding: 10px;
|
|
|
|
|
- margin-top: -8px;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
-
|
|
|
|
|
- &::before {
|
|
|
|
|
- content: "";
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- top: -6px;
|
|
|
|
|
- left: 21%;
|
|
|
|
|
- width: 0;
|
|
|
|
|
- height: 0;
|
|
|
|
|
- border-left: 7px solid transparent;
|
|
|
|
|
- border-right: 7px solid transparent;
|
|
|
|
|
- border-bottom: 7px solid #fff;
|
|
|
|
|
- transform: translateX(-50%);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__action {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- border: 1px solid rgba(33, 153, 248, 0.2);
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
- padding: 8px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__icon {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 5px;
|
|
|
|
|
-
|
|
|
|
|
- img {
|
|
|
|
|
- width: 18px;
|
|
|
|
|
- height: 16px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__reason {
|
|
|
|
|
- color: #2199f8;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__issue {
|
|
|
|
|
- margin-top: 2px;
|
|
|
|
|
- color: #2199f8;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__action-main {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- padding-right: 4px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &__btn {
|
|
|
|
|
- flex: none;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 2px;
|
|
|
|
|
- padding: 7px 10px;
|
|
|
|
|
- border-radius: 5px;
|
|
|
|
|
- background: #2199f8;
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .crop-archive {
|
|
|
|
|
- padding: 10px;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
-
|
|
|
|
|
- &__title {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- display: inline-block;
|
|
|
|
|
- z-index: 1;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
- margin-bottom: 5px;
|
|
|
|
|
-
|
|
|
|
|
- &::after {
|
|
|
|
|
- content: "";
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- left: 0;
|
|
|
|
|
- bottom: 2px;
|
|
|
|
|
- z-index: -1;
|
|
|
|
|
- width: 90px;
|
|
|
|
|
- height: 6px;
|
|
|
|
|
- border-radius: 25px;
|
|
|
|
|
- background: linear-gradient(90deg, #2199f8 0%, rgba(255, 255, 255, 0.37) 100%);
|
|
|
|
|
- opacity: 0.4;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|