| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094 |
- <template>
- <div class="record-wrap">
- <custom-header :name="t('recordDetails.workName')"></custom-header>
- <div class="record-content">
- <div class="record-header" v-if="recordType === 'growth'">
- <span>{{ t('agriRecord.growthWorkName') }}</span>
- <div class="question">{{ currentGrowthAnomalyDetail?.interact_question }}</div>
- </div>
- <div class="record-header" v-else-if="recordType === 'pest'">
- <span>{{ t('agriRecord.pestWorkName') }}</span>
- <div class="question">{{ t('recordDetails.pestQuestion') }}</div>
- </div>
- <div class="record-header" v-else>
- <span>{{ t('agriRecord.phenologyWorkName') }}</span>
- <div class="question">{{ t('recordDetails.phenologyQuestion') }}</div>
- </div>
- <div class="record-body">
- <div class="card-wrap" v-if="recordType === 'growth'">
- <div class="card-item">
- <span class="item-label">{{ t('recordDetails.scienceLabel') }}</span>
- <text-ellipsis class="item-value" :expand-text="expandCollapse.expand"
- :collapse-text="expandCollapse.collapse" rows="3"
- :content="currentGrowthAnomalyDetail?.agri_judgment" />
- </div>
- <div class="tabs-list" v-if="!showMap">
- <div class="item-tab" :class="{ 'item-tab--active': activeGrowthAnomalyIndex === index }"
- v-for="(item, index) in growthAnomalyTabs" :key="index"
- @click="handleGrowthAnomalyTabClick(index)">{{ item.label }}
- </div>
- </div>
- <div class="card-item">
- <span class="item-label">{{ t('recordDetails.phenotypeLabel') }}</span>
- <text-ellipsis class="item-value" :expand-text="expandCollapse.expand"
- :collapse-text="expandCollapse.collapse" rows="3"
- :content="currentGrowthAnomalyDetail?.phenotype" />
- </div>
- <div class="card-item">
- <span class="item-label">{{ t('recordDetails.highRiskLabel') }}</span>
- <text-ellipsis class="item-value" :expand-text="expandCollapse.expand"
- :collapse-text="expandCollapse.collapse" rows="3"
- :content="currentGrowthAnomalyDetail?.patrol_points" />
- </div>
- </div>
- <div class="card-wrap" v-else-if="recordType === 'pest'">
- <div class="card-item">
- <span class="item-label">{{ t('recordDetails.scienceLabel') }}</span>
- <text-ellipsis class="item-value" :expand-text="expandCollapse.expand"
- :collapse-text="expandCollapse.collapse" rows="3"
- :content="t('recordDetails.pestScience')" />
- </div>
- <div class="pest-classify-picker">
- <div class="pest-classify-picker__row pest-classify-picker__row--top">
- <div v-for="(label, i) in pestTopLabels" :key="'top-' + i"
- class="pest-classify-picker__top-btn"
- :class="{ 'pest-classify-picker__top-btn--active': pestTopIndex === i }"
- @click="handlePestTopClick(i)">{{ label }}</div>
- </div>
- <div class="pest-classify-picker__row pest-classify-picker__row--grid4">
- <div v-for="(item, i) in pestCategoryLabels" :key="'cat-' + item.value"
- class="pest-classify-picker__chip pest-classify-picker__chip--solid"
- :class="{ 'pest-classify-picker__chip--solid-active': pestCategoryIndex === i }"
- @click="handlePestCategoryClick(i)">{{ item.label }}</div>
- </div>
- <div class="pest-classify-picker__row pest-classify-picker__row--grid4">
- <div v-for="(item, i) in pestDetailLabels" :key="'det-' + item.value"
- class="pest-classify-picker__chip pest-classify-picker__chip--soft"
- :class="{ 'pest-classify-picker__chip--soft-active': pestDetailIndex === i }"
- @click="handlePestDetailClick(i)">{{ item.label }}</div>
- </div>
- </div>
- <div class="card-item">
- <span class="item-label">{{ t('recordDetails.phenotypeLabel') }}</span>
- <text-ellipsis class="item-value" :expand-text="expandCollapse.expand"
- :collapse-text="expandCollapse.collapse" rows="3" :content="currentPestDetail?.phenotype" />
- </div>
- <div class="card-item">
- <span class="item-label">{{ t('recordDetails.highRiskLabel') }}</span>
- <text-ellipsis class="item-value" :expand-text="expandCollapse.expand"
- :collapse-text="expandCollapse.collapse" rows="3"
- :content="currentPestDetail?.patrol_points" />
- </div>
- </div>
- <div class="card-wrap" v-else>
- <div class="card-item">
- <span class="item-label">{{ t('recordDetails.farmAnalysisLabel') }}</span>
- <text-ellipsis class="item-value" :expand-text="expandCollapse.expand"
- :collapse-text="expandCollapse.collapse" rows="3"
- :content="t('recordDetails.phenologyAnalysis')" />
- </div>
- <div class="card-item">
- <span class="item-label">{{ t('recordDetails.patrolLabel') }}</span>
- <text-ellipsis class="item-value" :expand-text="expandCollapse.expand"
- :collapse-text="expandCollapse.collapse" rows="3"
- :content="workDetail.inspection_keypoints" />
- </div>
- <div class="card-item">
- <span class="item-label">{{ t('recordDetails.phenotypeLabel') }}</span>
- <text-ellipsis class="item-value" :expand-text="expandCollapse.expand"
- :collapse-text="expandCollapse.collapse" rows="3"
- :content="t('recordDetails.phenologyPhenotype')" />
- </div>
- </div>
- <!-- <div class="tabs-list" v-if="!showMap">
- <div class="item-tab" :class="{ 'item-tab--active': activeTab === index }"
- v-for="(item, index) in tabsList" :key="index" @click="handleTabClick(index)">{{ item.label }}
- </div>
- </div> -->
- <div class="card-wrap">
- <div class="border-wrap" v-if="showMap">
- <div class="question-box">
- <span>{{ workDetail.interaction_issue }}</span>
- <el-input class="input" v-model="input" size="large" type="number">
- <template #suffix>
- <span>%</span>
- </template>
- </el-input>
- </div>
- <div class="my-map">
- <div class="map-container" ref="mapContainer">
- <div class="tip">{{ t('recordDetails.drawTip') }}</div>
- </div>
- <div class="confirm-btn-wrap">
- <div class="cancel-btn">{{ t('recordDetails.notReached') }}</div>
- <div class="confirm-btn">{{ t('recordDetails.confirmUpload') }}</div>
- </div>
- </div>
- </div>
- <div v-if="!showMap && recordType === 'phenology'" class="border-wrap no-map-wrap">
- <div class="question-info">
- <span class="title">{{ t('recordDetails.keyAssessment') }}</span>
- <span class="content">{{ t('recordDetails.phenologyIssue') }}</span>
- <div class="current-status">{{ currentStatusText }}</div>
- </div>
- <div class="time-line">
- <GrowthStageTimeline v-model="growthStageIndex" :stages="growthStages"
- @scroll-settled="onStageScrollSettled" @locale-change="getFindPhenologyInfo" />
- <!-- <GrowthStageTimeline v-model="growthStageIndex" :stages="growthStages" /> -->
- </div>
- <div class="confirm-btn-wrap">
- <uploader @click="handleUploadClick" :before-read="beforeReadUpload" class="upload-wrap"
- multiple :max-count="10" :after-read="afterReadUpload">
- <div class="upload-btn">
- <el-icon>
- <Plus />
- </el-icon>
- <span>{{ t('recordDetails.uploadPhoto') }}</span>
- </div>
- </uploader>
- <div class="confirm-btn" @click="hanldeSubmit">{{ t('recordDetails.confirmInfo') }}</div>
- </div>
- </div>
- <div class="phenology-track-section">
- <PhenologyTrackTimelineItem :abnormalType="recordTypeObj[recordType]" />
- </div>
- </div>
- </div>
- </div>
- <!-- <div class="custom-bottom-fixed-btns">
- <div class="bottom-btn secondary-btn">{{ $t('转发记录') }}</div>
- </div> -->
- <!-- <div class="phenology-tip-banner">
- <div class="banner__left">
- <div class="banner__title">{{ $t('物候不整齐?') }}</div>
- <span class="banner__desc">
- 如果区域长势不同,会降低病虫害防治功效,
- 建议根据长势拆分区域,进行分区精细管理,
- 达到减药减肥的目的
- </span>
- </div>
- <div class="banner__btn" @click="goPartitionManage">
- 分区管理
- </div>
- </div> -->
- <div class="phenology-tip-banner" v-if="recordType !== 'phenology'">
- <div class="banner__left">
- <div class="banner__title">
- <el-icon size="17">
- <WarningFilled />
- </el-icon>
- <span>{{ t('recordDetails.abnormalBanner') }}</span>
- </div>
- <span class="banner__desc">
- {{ t('recordDetails.abnormalBannerDesc') }}
- </span>
- </div>
- <!-- <div class="banner__btn" @click="goPartitionManage">
- 分区管理
- </div> -->
- <div class="banner__btn" @click="handleAbnormalRecord">
- {{ t('recordDetails.abnormalRecord') }}
- </div>
- </div>
- <!-- <UploadProgressPopup ref="uploadProgressPopupRef" v-model:show="showUploadProgressPopup"
- :popup-image-upload-loading="popupImageUploadLoading" :init-img-arr="initImgArr"
- @cancel="handleCancelUploadPopup" @confirm="handleConfirmUpload" @handleUpload="handleUploadSuccess">
- <template #header>
- <div class="upload-progress-title">
- <span class="label">{{ $t('当前现状:') }}</span>
- <span class="value">{{ $t('60% 进入红黄叶进程') }}</span>
- </div>
- </template>
- </UploadProgressPopup> -->
- <UploadProgressPopup ref="uploadProgressPopupRef" v-model:show="showUploadProgressPopup"
- :popup-image-upload-loading="popupImageUploadLoading" :init-img-arr="initImgArr"
- :confirm-text="t('recordDetails.confirmUpload')" :upload-required="false"
- @reset="handleUploadPopupReset" @cancel="handleCancelUploadPopup" @confirm="handleConfirmUpload">
- <template #header>
- <div class="upload-form">
- <div class="form-item special-input">
- <div class="item-label" style="font-size: 14px;color: #5A5A5A;">
- <span>{{ t('recordDetails.abnormalCount') }}</span>
- </div>
- <el-input v-model="formData.ratio" type="number" size="large"
- :placeholder="t('recordDetails.inputPlaceholder')">
- <template #suffix>
- %
- </template>
- </el-input>
- </div>
- </div>
- </template>
- <template #footer>
- <div class="upload-form">
- <div class="form-item">
- <div class="item-label">{{ t('recordDetails.patrolDesc') }}</div>
- <el-input v-model="formData.regionName" size="large"
- :placeholder="t('recordDetails.inputDescPlaceholder')" />
- </div>
- <div class="map-container" ref="uploadMapContainer" @click="handleMapClick">
- <div class="tip" v-if="!hasMapConfirmGeometry">{{ t('recordDetails.drawTip') }}</div>
- </div>
- </div>
- </template>
- </UploadProgressPopup>
- </div>
- </template>
- <script setup>
- import customHeader from "@/components/customHeader.vue";
- import PhenologyTrackTimelineItem from "@/components/pageComponents/PhenologyTrackTimelineItem.vue";
- import GrowthStageTimeline from "@/components/pageComponents/GrowthStageTimeline.vue";
- import UploadProgressPopup from "@/components/popup/UploadProgressPopup.vue";
- import { ref, onActivated, watch, nextTick, computed } from 'vue';
- import { useRouter, useRoute } from 'vue-router';
- import { useStore } from 'vuex';
- import { useI18n } from '@/i18n';
- import { RECORD_KEY_MAP } from '@/i18n/recordTextMap';
- import { Uploader, TextEllipsis } from "vant";
- import { ElMessage } from "element-plus";
- import UploadFile from "@/utils/upliadFile";
- import { getFileExt } from "@/utils/util";
- import IndexMap from "./map/index.js";
- import { Plus } from "@element-plus/icons-vue";
- const router = useRouter();
- const route = useRoute();
- const store = useStore();
- const { t } = useI18n();
- const TYPE_LEGACY_MAP = {
- 长势异常态势跟踪: 'growth',
- 病虫害态势监控: 'pest',
- 物候跟踪记录: 'phenology',
- };
- const recordType = computed(() => {
- const type = route.query.type;
- return TYPE_LEGACY_MAP[type] || type || 'phenology';
- });
- const expandCollapse = computed(() => ({
- expand: t('recordDetails.expand'),
- collapse: t('recordDetails.collapse'),
- }));
- function goPartitionManage() {
- router.push({ name: 'MapManage' });
- }
- const showMap = ref(false);
- /** 病虫害态势监控:分类选择(接入接口后可绑定 workDetail) */
- const pestTopIndex = ref(0);
- const pestCategoryIndex = ref(0);
- const pestDetailIndex = ref(0);
- const pestData = ref([]);
- const pestTopLabels = computed(() => [t('recordDetails.pestDisease'), t('recordDetails.pestInsect')]);
- const PEST_CATEGORY_DISEASE = [
- { key: 'catDiseaseFungal', value: 1 },
- { key: 'catDiseaseBacterial', value: 2 },
- { key: 'catDiseaseSoilBorne', value: 3 },
- { key: 'catDiseaseInsectBorne', value: 4 },
- { key: 'catDiseaseViral', value: 5 },
- ];
- const PEST_CATEGORY_INSECT = [
- { key: 'catInsectPiercing', value: 1 },
- { key: 'catInsectChewing', value: 2 },
- { key: 'catInsectBoring', value: 3 },
- { key: 'catInsectUnderground', value: 4 },
- ];
- const pestCategoryLabels = computed(() => {
- const list = pestTopIndex.value === 0 ? PEST_CATEGORY_DISEASE : PEST_CATEGORY_INSECT;
- return list.map(({ key, value }) => ({
- label: t(`recordDetails.${key}`),
- value,
- }));
- });
- const pestDetailLabels = computed(() =>
- pestData.value.map((item) => ({
- label: item.label ?? item.name ?? '',
- value: item.value ?? item.fourth_type ?? item.id,
- }))
- );
- const currentPestDetail = computed(() => pestData.value[pestDetailIndex.value]);
- /** 病虫害列表查询 */
- const getAbnormalPlan = async () => {
- const category = pestCategoryLabels.value[pestCategoryIndex.value];
- const params = {
- crop_type: JSON.parse(localStorage.getItem('selectedFarmData')).farm_variety,
- phenology_code: route.query.curCode,
- second_type: pestTopIndex.value === 0 ? 2 : 1,
- third_type: category?.value ?? 1,
- };
- const res = await VE_API.record.abnormalPlan(params);
- if (res.code === 200 && res.data?.length) {
- pestData.value = res.data;
- if (pestDetailIndex.value >= res.data.length) {
- pestDetailIndex.value = 0;
- }
- } else {
- pestData.value = [];
- pestDetailIndex.value = 0;
- }
- };
- /** 病虫害分类选择 */
- const handlePestTopClick = async (index) => {
- pestTopIndex.value = index;
- pestCategoryIndex.value = 0;
- pestDetailIndex.value = 0;
- await getAbnormalPlan();
- };
- const handlePestCategoryClick = async (index) => {
- pestCategoryIndex.value = index;
- pestDetailIndex.value = 0;
- await getAbnormalPlan();
- };
- const handlePestDetailClick = (index) => {
- pestDetailIndex.value = index;
- };
- const indexMap = new IndexMap();
- const mapContainer = ref(null);
- const uploadMapContainer = ref(null);
- const location = ref(null);
- const input = ref('');
- const tabsList = ref([
- { label: '分区一', value: 0 },
- { label: '分区二', value: 1 },
- { label: '分区三', value: 2 },
- { label: '分区四', value: 3 },
- ]);
- const activeGrowthAnomalyIndex = ref(0);
- const growthAnomalyData = ref([]);
- const growthAnomalyTabs = computed(() =>
- growthAnomalyData.value.map((item, index) => ({
- label: item.name ?? '',
- value: item.value ?? item.id ?? index,
- }))
- );
- const currentGrowthAnomalyDetail = computed(() => growthAnomalyData.value[activeGrowthAnomalyIndex.value]);
- const handleGrowthAnomalyTabClick = (index) => {
- activeGrowthAnomalyIndex.value = index;
- };
- const currentStatusText = computed(() =>
- t('recordDetails.currentStatus', {
- label: curStage.value?.label || '',
- period: curStage.value?.periodTitle || '',
- })
- );
- const DEFAULT_MAP_LOCATION = "POINT(113.6142086995688 23.585836479509055)";
- const mapConfirmPayload = computed(() => store.state.recordDetails.mapConfirmPayload);
- const hasMapConfirmGeometry = computed(() => {
- const coordinates = mapConfirmPayload.value?.coordinates;
- return Array.isArray(coordinates) && coordinates.length > 0;
- });
- const formData = ref({
- ratio: '',
- regionName: '',
- });
- function destroyUploadMap() {
- if (indexMap.kmap?.destroy) {
- indexMap.kmap.destroy();
- }
- indexMap.kmap = null;
- }
- function ensureUploadMap() {
- if (!uploadMapContainer.value) return false;
- location.value = location.value || DEFAULT_MAP_LOCATION;
- destroyUploadMap();
- indexMap.initMap(location.value, uploadMapContainer.value, { editable: false, movable: false });
- return Boolean(indexMap.kmap);
- }
- function resizeUploadMap() {
- indexMap.kmap?.map?.updateSize?.();
- }
- /** 弹窗动画结束后再初始化,避免容器尺寸为 0 导致白屏 */
- function syncUploadMapView() {
- const { coordinates } = mapConfirmPayload.value;
- nextTick(() => {
- setTimeout(() => {
- if (!ensureUploadMap()) return;
- resizeUploadMap();
- if (Array.isArray(coordinates) && coordinates.length) {
- indexMap.setAreaGeometry(coordinates);
- resizeUploadMap();
- }
- }, 250);
- });
- }
- function syncFormFromMapConfirmPayload() {
- if (!showUploadProgressPopup.value) return;
- syncUploadMapView();
- }
- const handleAbnormalRecord = () => {
- formData.value.ratio = '';
- formData.value.regionName = '';
- showUploadProgressPopup.value = true;
- };
- const handleMapClick = () => {
- router.push({
- name: 'MapManage', query: {
- type: 'pest',
- }
- });
- }
- const showUploadProgressPopup = ref(false);
- watch(showUploadProgressPopup, (show) => {
- if (show) {
- syncUploadMapView();
- } else {
- destroyUploadMap();
- }
- });
- const initImgArr = ref([]);
- const uploadProgressPopupRef = ref(null);
- const popupImageUploadLoading = ref(false);
- const uploadFileObj = new UploadFile();
- const miniUserId = localStorage.getItem("MINI_USER_ID");
- const handleUploadPopupReset = () => {
- initImgArr.value = [];
- popupImageUploadLoading.value = false;
- };
- const beforeReadUpload = (file) => {
- showUploadProgressPopup.value = true;
- return true;
- };
- const curStage = ref({});
- const curCode = ref('')
- function onStageScrollSettled(index, stage, code) {
- if (stage) curStage.value = stage;
- curCode.value = code ?? stage?.phenophase_code ?? "";
- }
- /** 从节点文案如「60%展开」中取出百分数,转为 0~1,如 60% -> 0.6 */
- function labelPercentToProgress(label) {
- if (typeof label !== "string" || !label.trim()) return 0;
- const m = label.match(/(\d+(?:\.\d+)?)\s*%/);
- if (!m) return 0;
- return Number(m[1]) / 100;
- }
- const hanldeSubmit = () => {
- const farmData = JSON.parse(localStorage.getItem('selectedFarmData'))
- const time = new Date().toISOString()
- const params = {
- farm_id: farmData.farm_id,
- farm_variety: farmData.farm_variety,
- current_code: curCode.value,
- new_start_date: time.slice(0, 10),
- progress: labelPercentToProgress(curStage.value.label),
- }
- VE_API.monitor.farmPhenologyAdjust(params).then(res => {
- if (res.code === 200) {
- ElMessage.success(t('recordDetails.confirmSuccess'))
- }
- })
- }
- const afterReadUpload = async (data) => {
- initImgArr.value = [];
- if (!Array.isArray(data)) {
- data = [data];
- }
- popupImageUploadLoading.value = true;
- try {
- for (const file of data) {
- const fileVal = file.file;
- file.status = "uploading";
- file.message = "上传中...";
- const ext = getFileExt(fileVal.name);
- const key = `birdseye-look-mini/${miniUserId}/${new Date().getTime()}.${ext}`;
- const resFilename = await uploadFileObj.put(key, fileVal);
- if (resFilename) {
- file.status = "done";
- file.message = "";
- initImgArr.value.push(resFilename);
- } else {
- file.status = "failed";
- file.message = "上传失败";
- ElMessage.error("图片上传失败,请稍后再试!");
- }
- }
- } finally {
- popupImageUploadLoading.value = false;
- }
- };
- const handleCancelUploadPopup = () => {
- showUploadProgressPopup.value = false;
- store.commit('recordDetails/CLEAR_MAP_CONFIRM_PAYLOAD');
- };
- const recordTypeObj = {
- phenology: 0,
- pest: 1,
- growth: 2,
- };
- function validateAbnormalRatio() {
- const ratio = String(formData.value.ratio ?? '').trim();
- if (ratio === '' || Number.isNaN(Number(ratio))) {
- ElMessage.warning(t('recordDetails.ratioRequired'));
- return false;
- }
- return true;
- }
- const handleConfirmUpload = (imgArr) => {
- if (!validateAbnormalRatio()) return;
- if (recordType.value === 'pest') {
- const coordinates = mapConfirmPayload.value?.coordinates;
- const hasDrawnMap = Array.isArray(coordinates) && coordinates.length > 0;
- if (!hasDrawnMap) {
- handleMapClick();
- return;
- }
- }
- const farmData = JSON.parse(localStorage.getItem('selectedFarmData'));
- const params = {
- farm_id: farmData.farm_id,
- variety_code: farmData.farm_variety,
- category_code: farmData.farm_category,
- type: recordTypeObj[recordType.value],
- time: new Date().toISOString().slice(0, 10),
- zone_name: mapConfirmPayload.value.zone_name,
- coordinates: mapConfirmPayload.value.coordinates[0],
- "recog_type": "20",
- image_urls: imgArr,
- ...formData.value,
- }
- VE_API.record.writeFarmRecord(params).then(res => {
- if (res.code === 200) {
- showUploadProgressPopup.value = false;
- store.commit('recordDetails/CLEAR_MAP_CONFIRM_PAYLOAD');
- ElMessage.success(t('recordDetails.confirmSuccess'));
- } else {
- ElMessage.error(res.msg);
- }
- });
- };
- const handleUploadClick = () => { };
- const activeTab = ref(0);
- const handleTabClick = (index) => {
- activeTab.value = index;
- };
- /** 生育期进程时间轴:接入接口后可替换为接口数据 */
- /** 不设初值时由 GrowthStageTimeline 默认选中间一档 */
- const growthStageIndex = ref();
- const growthStages = ref([]);
- /** 与 GrowthStageTimeline 默认档一致:未绑 v-model 时用中间索引 */
- function syncCurStageFromModel() {
- const stages = growthStages.value;
- if (!Array.isArray(stages) || !stages.length) return;
- const n = stages.length;
- let i = growthStageIndex.value;
- if (i === undefined || i === null) {
- i = Math.max(0, Math.floor((n - 1) / 2));
- } else {
- i = Math.min(Math.max(0, i), n - 1);
- }
- const stage = stages[i];
- if (stage) curStage.value = stage;
- curCode.value = stage?.phenophase_code ?? "";
- }
- watch([growthStages, growthStageIndex], syncCurStageFromModel, { immediate: true });
- onActivated(() => {
- sessionStorage.removeItem('mapManageConfirmPayload');
- syncFormFromMapConfirmPayload();
- if (route.query.workId) {
- getWorkDetail();
- }
- if (route.query.type === 'pest') {
- getAbnormalPlan();
- }
- if (route.query.type === 'growth') {
- getGrowthAnomalyInfo();
- }
- getFindPhenologyInfo()
- if (showMap.value) {
- location.value = "POINT(113.6142086995688 23.585836479509055)";
- indexMap.initMap(location.value, mapContainer.value);
- }
- });
- const workDetail = ref({});
- const getWorkDetail = async () => {
- const res = await VE_API.monitor.getWorkDetail({ id: route.query.workId });
- if (res.code === 200 && res.data.length) {
- workDetail.value = res.data[0];
- }
- }
- // 获取生长异常列表
- const getGrowthAnomalyInfo = async () => {
- const farmData = JSON.parse(localStorage.getItem('selectedFarmData'));
- const params = {
- crop_type: farmData.farm_variety,
- phenology_code: route.query.curCode,
- farm_id: farmData.farm_id,
- }
- const res = await VE_API.record.growthAnomalyInfo(params);
- if (res.code === 200 && res.data?.length) {
- growthAnomalyData.value = res.data;
- activeGrowthAnomalyIndex.value = 0;
- } else {
- growthAnomalyData.value = [];
- activeGrowthAnomalyIndex.value = 0;
- }
- }
- function normalizeTimeDescribe(raw) {
- if (Array.isArray(raw)) return raw;
- if (typeof raw === 'string' && raw.trim()) {
- try {
- const parsed = JSON.parse(raw);
- return Array.isArray(parsed) ? parsed : [];
- } catch {
- return [];
- }
- }
- return [];
- }
- const getFindPhenologyInfo = async () => {
- const cropType = JSON.parse(localStorage.getItem('selectedFarmData')).farm_variety;
- const res = await VE_API.monitor.getFindPhenologyInfo({ crop_type: cropType });
- if (res.code === 200 && Array.isArray(res.data) && res.data.length) {
- const flat = [];
- for (const item of res.data) {
- const milestones = normalizeTimeDescribe(item.time_discribe);
- for (const t of milestones) {
- flat.push({
- label: t.label ?? '',
- tags: Array.isArray(t.tags) ? t.tags : [],
- periodTitle: item.phenophase_name ?? '',
- periodSubtitle: item.phenophase_discribe ?? '',
- phenophase_code: item.phenophase_code ?? item.phenophaseCode ?? '',
- });
- }
- }
- if (flat.length) {
- growthStages.value = flat;
- }
- }
- };
- </script>
- <style scoped lang="scss">
- .record-wrap {
- height: 100vh;
- background: #f2f3f5;
- .record-content {
- // height: calc(100% - 120px);
- // overflow: auto;
- // padding-bottom: 120px;
- height: calc(100% - 40px);
- overflow: auto;
- // padding-bottom: 120px;
- .record-header {
- color: #fff;
- background: #2199F8;
- padding: 10px;
- font-size: 22px;
- .question {
- font-size: 14px;
- color: #2199F8;
- margin-top: 10px;
- padding: 4px 5px;
- border-radius: 2px;
- background: #ffffff;
- }
- }
- .record-body {
- padding: 10px;
- .tabs-list {
- margin: 10px 0;
- flex-shrink: 0;
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(8em, 1fr));
- gap: 8px;
- .item-tab {
- box-sizing: border-box;
- padding: 6px 4px;
- border-radius: 2px;
- color: #767676;
- background: #fff;
- display: flex;
- align-items: center;
- justify-content: center;
- text-align: center;
- font-size: 13px;
- white-space: nowrap;
- word-break: keep-all;
- }
- .item-tab--active {
- background: #2199F8;
- color: #ffffff;
- }
- }
- .card-wrap {
- background: #fff;
- border-radius: 8px;
- padding: 10px;
- .card-item {
- padding: 5px 10px;
- border-radius: 5px;
- background: #F6F6F6;
- .item-label {
- color: rgba(60, 60, 60, 0.5);
- }
- .item-value {
- display: inline;
- }
- }
- .card-item+.card-item {
- margin-top: 8px;
- }
- .pest-classify-picker {
- margin: 8px 0;
- padding: 10px;
- border-radius: 8px;
- background: #f2f3f5;
- &__row {
- display: flex;
- justify-content: center;
- gap: 8px;
- &--top {
- .pest-classify-picker__top-btn {
- padding: 6px 30px;
- border-radius: 6px;
- background: #E9E9E9;
- color: #767676;
- }
- .pest-classify-picker__top-btn--active {
- background: #ffffff;
- color: #1a1a1a;
- }
- }
- &--grid4 {
- margin-top: 10px;
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 8px;
- }
- }
- &__chip {
- border: none;
- padding: 6px 4px;
- border-radius: 6px;
- font-size: 13px;
- text-align: center;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- background: #ffffff;
- color: #909090;
- }
- &__chip--solid-active {
- background: #2199f8;
- color: #ffffff;
- }
- &__chip--soft-active {
- background: rgba(33, 153, 248, 0.14);
- color: #2199f8;
- }
- }
- .border-wrap {
- border-radius: 8px;
- border: 0.5px solid #2199F8;
- padding: 10px;
- .question-box {
- border: 1px solid #2199F8;
- border-radius: 5px;
- padding: 8px;
- color: #5A5A5A;
- .input {
- margin-top: 10px;
- width: 100%;
- }
- }
- .confirm-btn-wrap {
- display: flex;
- gap: 13px;
- div {
- flex: 1;
- text-align: center;
- border-radius: 5px;
- font-size: 16px;
- padding: 8px 0;
- }
- .cancel-btn {
- border: 1px solid #D6D6D6;
- color: #7F7F7F;
- }
- .confirm-btn {
- border: 1px solid #2199F8;
- background: #2199F8;
- color: #fff;
- }
- }
- .question-info {
- padding: 8px 10px;
- border-radius: 5px;
- background: rgba(33, 153, 248, 0.1);
- color: #909090;
- font-weight: 500;
- .content {
- font-weight: 400;
- }
- .current-status {
- color: #2199F8;
- }
- }
- .time-line {
- margin: 10px 0;
- }
- }
- .no-map-wrap {
- .confirm-btn-wrap {
- div {
- font-size: 14px;
- flex: none;
- }
- .upload-wrap {
- width: calc(100% - 96px - 13px);
- padding: 0;
- background: rgba(255, 149, 61, 0.1);
- color: #FF953D;
- border-radius: 4px;
- display: flex;
- justify-content: center;
- border: 1px solid #FF953D;
- .upload-btn {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 3px;
- }
- }
- .confirm-btn {
- width: 96px;
- }
- }
- }
- }
- .card-wrap+.card-wrap {
- margin-top: 10px;
- }
- .phenology-track-section {
- margin-top: 15px;
- display: flex;
- flex-direction: column;
- gap: 16px;
- }
- }
- }
- .phenology-tip-banner {
- position: fixed;
- left: 12px;
- right: 12px;
- // bottom: calc(90px + env(safe-area-inset-bottom, 0px));
- bottom: 20px;
- z-index: 99;
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 8px;
- padding: 10px 14px;
- border-radius: 12px;
- // background: linear-gradient(180deg, #CCE8FF 0%, #ffffff 60%);
- background: linear-gradient(180deg, #FFDFC5 -12.59%, #FFFFFF 38.15%);
- box-shadow: 0 2px 8px rgba(33, 153, 248, 0.12);
- .banner__left {
- flex: 1;
- .banner__title {
- display: flex;
- align-items: center;
- gap: 4px;
- font-size: 16px;
- font-weight: 600;
- // color: #1890ff;
- color: #FF953D;
- }
- .banner__desc {
- font-size: 12px;
- color: rgba(0, 0, 0, 0.4);
- }
- }
- .banner__btn {
- padding: 7px 16px;
- border-radius: 25px;
- color: #ffffff;
- background: linear-gradient(180deg, #FFB273 0%, #FF953D 100%);
- // background: linear-gradient(180deg, #5cb8ff 0%, #2e90ff 100%);
- }
- }
- }
- // .my-map {
- // width: 100%;
- // .map-container {
- // width: 100%;
- // height: 170px;
- // margin: 10px 0;
- // clip-path: inset(0px round 5px);
- // position: relative;
- // .tip {
- // position: absolute;
- // top: 0;
- // left: 0;
- // color: #fff;
- // padding: 5px 10px;
- // border-radius: 5px;
- // background: rgba(0, 0, 0, 0.6);
- // z-index: 2;
- // }
- // }
- // }
- .map-container {
- width: 100%;
- height: 170px;
- margin: 10px 0;
- clip-path: inset(0px round 5px);
- position: relative;
- .tip {
- position: absolute;
- top: 0;
- left: 0;
- color: #fff;
- padding: 5px 10px;
- border-radius: 5px;
- background: rgba(0, 0, 0, 0.6);
- z-index: 2;
- }
- }
- .upload-progress-title {
- color: rgba(60, 60, 60, 0.5);
- font-weight: 500;
- background: rgba(33, 153, 248, 0.1);
- border-radius: 5px;
- padding: 5px 10px;
- margin-bottom: 12px;
- .value {
- color: #2199F8;
- }
- }
- .upload-form {
- .form-item {
- margin-bottom: 10px;
- .item-label {
- font-size: 16px;
- margin-bottom: 8px;
- .optional {
- font-size: 14px;
- color: rgba(0, 0, 0, 0.2);
- }
- }
- }
- .special-input {
- padding: 8px;
- background: rgba(33, 153, 248, 0.1);
- border-radius: 5px;
- border: 1px solid #2199F8;
- }
- }
- </style>
|