| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553 |
- <template>
- <div class="record-wrap">
- <custom-header :name="t('recordDetails.workName')"></custom-header>
- <div class="record-content">
- <div class="record-header">
- <span>{{ t(recordHeaderInfo.titleKey) }}</span>
- <div class="question">{{ recordHeaderInfo.question }}</div>
- </div>
- <div class="record-body">
- <div class="card-wrap">
- <div class="card-item">
- <span class="item-label">{{ t(`recordDetails.${leadDetailField.labelKey}`) }}</span>
- <text-ellipsis class="item-value" v-bind="textEllipsisBind"
- :content="leadDetailField.content" />
- </div>
- <div v-if="recordType === 'pest'" class="phenology-tip-banner">
- <div class="banner__left">
- <div class="banner__title">
- <span>{{ t('recordDetails.abnormalBanner') }}</span>
- </div>
- <span class="banner__desc">{{ t('recordDetails.abnormalBannerDesc') }}</span>
- </div>
- <div class="banner__btn" @click="handleAbnormalRecord">
- <el-icon size="17">
- <CameraFilled />
- </el-icon>
- <span>{{ t('recordDetails.abnormalRecord') }}</span>
- </div>
- </div>
- <div v-if="recordType === 'growth'" class="phenology-tip-banner">
- <div class="banner__left">
- <div class="banner__title">
- <span>{{ t('recordDetails.growthAbnormalBanner') }}</span>
- </div>
- <span class="banner__desc">{{ t('recordDetails.growthAbnormalBannerDesc') }}</span>
- </div>
- <div class="banner__btn" @click="handleAbnormalRecord">
- <span>{{ t('recordDetails.growthAbnormalRecord') }}</span>
- </div>
- </div>
- <template v-if="recordType === 'phenology'">
- <div v-for="field in trailDetailFields" :key="field.labelKey" class="card-item">
- <span class="item-label">{{ t(`recordDetails.${field.labelKey}`) }}</span>
- <text-ellipsis class="item-value" v-bind="textEllipsisBind" :content="field.content" />
- </div>
- <div class="phenology-tip-banner blue">
- <div class="banner__left">
- <div class="banner__title">{{ t('recordDetails.partitionBannerTitle') }}</div>
- <span class="banner__desc">{{ t('recordDetails.partitionBannerDesc') }}</span>
- </div>
- <div class="banner__btn" @click="goPartitionManage">
- {{ t('recordDetails.partitionManage') }}
- </div>
- </div>
- </template>
- </div>
- <template v-if="recordType === 'growth' && !showMap">
- <div class="growth-anomaly-tabs">
- <div v-for="(item, index) in growthAnomalyTabs" :key="index" class="item-tab"
- :class="{ 'item-tab--active': activeGrowthAnomalyIndex === index }"
- @click="handleGrowthAnomalyTabClick(index)">
- <span v-if="getGrowthTabDisplayLevel(index) > 0" class="item-tab__badge" :class="[
- `item-tab__badge--${getGrowthTabDisplayLevel(index)}`,
- { 'item-tab__badge--on-active': activeGrowthAnomalyIndex === index },
- ]">
- {{ t('agriRecord.riskLevel', { level: getGrowthTabDisplayLevel(index) }) }}
- </span>
- <span class="item-tab__text">{{ item.label }}</span>
- </div>
- </div>
- <div class="card-wrap growth-detail-card">
- <div v-for="field in trailDetailFields" :key="field.labelKey" class="card-item"
- :class="{ 'card-item--phenotype': field.image }">
- <div v-if="field.image" class="card-item__phenotype">
- <div class="card-item__phenotype-content">
- <span class="item-label">{{ t(`recordDetails.${field.labelKey}`) }}</span>
- <text-ellipsis class="item-value" v-bind="textEllipsisBind"
- :content="field.content" />
- </div>
- <!-- <img class="card-item__phenotype-image" :src="field.image" alt=""
- @click="handlePhenotypeImagePreview(field.image)" /> -->
- </div>
- <template v-else>
- <span class="item-label">{{ t(`recordDetails.${field.labelKey}`) }}</span>
- <text-ellipsis class="item-value" v-bind="textEllipsisBind" :content="field.content" />
- </template>
- </div>
- </div>
- </template>
- <div v-if="recordType === 'pest'" class="pest-classify-picker">
- <div class="pest-classify-picker__tabs" ref="pestTabsRef">
- <div v-for="(label, i) in pestTopLabels" :key="'top-' + i" :ref="(el) => setPestTabRef(el, i)"
- class="pest-classify-picker__tab"
- :class="{ 'pest-classify-picker__tab--active': pestTopIndex === i }"
- @click="handlePestTopClick(i)">{{ label }}</div>
- </div>
- <div class="pest-classify-picker__panel" ref="pestPanelRef"
- :style="{ '--arrow-left': `${pestPanelArrowLeft}px` }">
- <div class="pest-classify-picker__categories">
- <div v-for="(item, i) in pestCategoryLabels" :key="'cat-' + item.value"
- class="pest-classify-picker__category"
- :class="{ 'pest-classify-picker__category--active': pestCategoryIndex === i }"
- @click="handlePestCategoryClick(i)">{{ item.label }}</div>
- </div>
- <div class="pest-classify-picker__details">
- <div v-for="(item, i) in pestDetailLabels" :key="'det-' + item.value"
- class="pest-classify-picker__detail"
- :class="{ 'pest-classify-picker__detail--active': pestDetailIndex === i }"
- @click="handlePestDetailClick(i)">
- <span v-if="item.risk_level > 0" class="pest-classify-picker__badge"
- :class="`pest-classify-picker__badge--${item.risk_level}`">
- {{ t('agriRecord.riskLevel', { level: item.risk_level }) }}
- </span>
- <span class="pest-classify-picker__detail-text">{{ item.label }}</span>
- </div>
- </div>
- </div>
- </div>
- <div v-if="recordType === 'pest'" class="card-wrap pest-detail-card">
- <div v-for="field in trailDetailFields" :key="field.labelKey" class="card-item"
- :class="{ 'card-item--phenotype': field.image }">
- <div v-if="field.image" class="card-item__phenotype">
- <div class="card-item__phenotype-content">
- <span class="item-label">{{ t(`recordDetails.${field.labelKey}`) }}</span>
- <text-ellipsis class="item-value" v-bind="textEllipsisBind" :content="field.content" />
- </div>
- <!-- <img class="card-item__phenotype-image" :src="field.image" alt=""
- @click="handlePhenotypeImagePreview(field.image)" /> -->
- </div>
- <template v-else>
- <span class="item-label">{{ t(`recordDetails.${field.labelKey}`) }}</span>
- <text-ellipsis class="item-value" v-bind="textEllipsisBind" :content="field.content" />
- </template>
- </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="recordMapContainer">
- <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">{{ workDetail.interaction_issue }}</span>
- <div class="current-status">{{ currentStatusText }}</div>
- </div>
- <div class="time-line">
- <GrowthStageTimeline :key="phenologyTimelineKey" v-model="growthStageIndex"
- :stages="growthStages" @scroll-settled="onStageScrollSettled"
- @locale-change="getFindPhenologyInfo" />
- </div>
- <div class="confirm-btn-wrap">
- <div class="confirm-btn" @click="hanldeSubmit">{{ t('recordDetails.confirmInfo') }}</div>
- </div>
- </div>
- <div class="phenology-track-section">
- <PhenologyTrackTimelineItem ref="phenologyTrackTimelineRef"
- :abnormalType="recordTypeObj[recordType]" />
- </div>
- </div>
- </div>
- </div>
- <!-- <div class="custom-bottom-fixed-btns">
- <div class="bottom-btn secondary-btn">{{ $t('转发记录') }}</div>
- </div> -->
- <RiskAssessPopup v-model:show="showRiskAssessPopup" />
- <!-- 表型特征图片大图预览 -->
- <ImagePreviewPopup v-model:show="showPhenotypeImagePreview" :images="phenotypePreviewImages" />
- <!-- 物候 上传弹窗 -->
- <UploadProgressPopup ref="phenologyUploadPopupRef" v-model:show="showPhenologyUploadPopup"
- :enable-identify="false" @cancel="handleCancelPhenologyUploadPopup" :upload-required="false"
- @confirm="handleConfirmPhenologyUpload">
- <template #header>
- <div class="upload-progress-title">
- <!-- <span class="label">{{ $t('当前现状:') }}</span> -->
- <span class="value">{{ currentStatusText }}</span>
- </div>
- </template>
- </UploadProgressPopup>
- <!-- 病虫害 上传弹窗 -->
- <UploadProgressPopup ref="pestUploadPopupRef" v-model:show="showPestUploadPopup" :enable-identify="true"
- :init-img-arr="pestInitImgArr" :confirm-text="t('recordDetails.confirmUpload')" :upload-required="false"
- @reset="handlePestUploadPopupReset" @cancel="handleCancelPestUploadPopup"
- @confirm="handleConfirmPestUpload">
- <template #header>
- <div class="upload-form">
- <div v-if="recordType === 'growth'" class="form-item growth-abnormal-type-field">
- <div class="growth-abnormal-type-field__label">
- {{ t('recordDetails.growthAbnormalTypeLabel') }}
- </div>
- <el-select v-model="formData.abnormalType" class="growth-abnormal-type-select"
- placeholder="请选择异常类型" size="large">
- <el-option v-for="item in growthAbnormalTypeOptions" :key="item.value" :label="item.label"
- :value="item.value" />
- </el-select>
- </div>
- <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>
- {{ t('recordDetails.treeUnit') }}
- </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 ImagePreviewPopup from "@/components/popup/ImagePreviewPopup.vue";
- import RiskAssessPopup from "./components/RiskAssessPopup.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 { TextEllipsis } from "vant";
- import { ElMessage } from "element-plus";
- import IndexMap from "./map/index.js";
- import pestPhenotypePlaceholder from '@/assets/img/common/sd-1.jpg';
- import pestPreviewImg2 from '@/assets/img/common/sd-2.jpg';
- import pestPreviewImg3 from '@/assets/img/common/sd-3.jpg';
- /** 表型大图预览 mock 多图(联调后可删) */
- const PHENOTYPE_PREVIEW_MOCK_IMAGES = [
- pestPhenotypePlaceholder,
- pestPreviewImg2,
- pestPreviewImg3,
- ];
- 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'),
- }));
- const textEllipsisBind = computed(() => ({
- expandText: expandCollapse.value.expand,
- collapseText: expandCollapse.value.collapse,
- rows: 3,
- }));
- function goPartitionManage() {
- router.push({ name: 'MapManage' });
- }
- const showMap = ref(false);
- const showRiskAssessPopup = ref(false);
- /** 表型特征图片预览弹窗 */
- const showPhenotypeImagePreview = ref(false);
- const phenotypePreviewImages = ref([]);
- /** 点击表型缩略图,打开大图预览 */
- function handlePhenotypeImagePreview(image) {
- if (!image) return;
- phenotypePreviewImages.value = PHENOTYPE_PREVIEW_MOCK_IMAGES;
- showPhenotypeImagePreview.value = true;
- }
- /** 病虫害态势监控:分类选择(接入接口后可绑定 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 PEST_MOCK_RISK_LEVELS = [3, 2, 1, 0];
- const pestDetailLabels = computed(() =>
- pestData.value.map((item, index) => ({
- label: item.label ?? item.name ?? '',
- value: item.value ?? item.fourth_type ?? item.id,
- risk_level: PEST_MOCK_RISK_LEVELS[index % PEST_MOCK_RISK_LEVELS.length],
- }))
- );
- 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();
- syncPestPanelArrow();
- };
- const handlePestCategoryClick = async (index) => {
- pestCategoryIndex.value = index;
- pestDetailIndex.value = 0;
- await getAbnormalPlan();
- };
- const handlePestDetailClick = (index) => {
- pestDetailIndex.value = index;
- };
- const pestTabsRef = ref(null);
- const pestTabRefs = ref([]);
- const pestPanelRef = ref(null);
- const pestPanelArrowLeft = ref(0);
- const setPestTabRef = (el, index) => {
- if (el) {
- pestTabRefs.value[index] = el;
- }
- };
- const syncPestPanelArrow = () => {
- nextTick(() => {
- requestAnimationFrame(() => {
- const activeTab = pestTabRefs.value[pestTopIndex.value];
- const panel = pestPanelRef.value;
- if (!activeTab || !panel) return;
- const panelRect = panel.getBoundingClientRect();
- const tabRect = activeTab.getBoundingClientRect();
- pestPanelArrowLeft.value = tabRect.left + tabRect.width / 2 - panelRect.left;
- });
- });
- };
- watch(pestTopIndex, syncPestPanelArrow);
- watch(recordType, (type) => {
- if (type === 'pest') {
- syncPestPanelArrow();
- }
- });
- const indexMap = new IndexMap();
- const recordMapContainer = 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 },
- ]);
- /** UI 占位:接口未返回等级时,前三个 Tab 依次展示三级 / 二级 / 一级 */
- const GROWTH_TAB_MOCK_LEVELS = [3, 2, 1];
- function getGrowthTabDisplayLevel(index) {
- return GROWTH_TAB_MOCK_LEVELS[index] ?? 0;
- }
- function resolveGrowthAnomalyOptionValue(item, index) {
- const raw = item?.value ?? item?.id ?? item?.code ?? index;
- if (raw === undefined || raw === null || raw === '') return '';
- return String(raw);
- }
- const activeGrowthAnomalyIndex = ref(0);
- const growthAnomalyData = ref([]);
- const growthAnomalyTabs = computed(() =>
- growthAnomalyData.value.map((item, index) => ({
- label: item.name ?? '',
- value: resolveGrowthAnomalyOptionValue(item, 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)";
- function getFarmMapLocation() {
- try {
- const farmData = JSON.parse(localStorage.getItem('selectedFarmData') || '{}');
- return farmData.geom_wkt || farmData.wkt || DEFAULT_MAP_LOCATION;
- } catch {
- return DEFAULT_MAP_LOCATION;
- }
- }
- const mapConfirmPayload = computed(() => store.state.recordDetails.mapConfirmPayload);
- const hasMapConfirmGeometry = computed(() => store.getters['recordDetails/mapConfirmCoordinatesArray'].length > 0);
- const formData = ref({
- ratio: '',
- regionName: '',
- abnormalType: '',
- });
- const growthAbnormalTypeOptions = computed(() => {
- if (growthAnomalyData.value.length) {
- return growthAnomalyData.value.map((item, index) => ({
- label: item.name ?? '',
- value: resolveGrowthAnomalyOptionValue(item, index),
- }));
- }
- return [
- { label: t('recordDetails.tabFruitBlocked'), value: 'fruit_blocked' },
- { label: t('recordDetails.tabSlowGrowth'), value: 'slow_growth' },
- { label: t('recordDetails.tabVigorous'), value: 'vigorous' },
- { label: t('recordDetails.tabWilting'), value: 'wilting' },
- ];
- });
- function syncGrowthAbnormalTypeSelection() {
- const idx = activeGrowthAnomalyIndex.value;
- const fromApi = resolveGrowthAnomalyOptionValue(growthAnomalyData.value[idx], idx);
- const options = growthAbnormalTypeOptions.value;
- if (fromApi && options.some((o) => o.value === fromApi)) {
- formData.value.abnormalType = fromApi;
- return;
- }
- formData.value.abnormalType = options[idx]?.value ?? options[0]?.value ?? '';
- }
- function destroyMap() {
- if (indexMap.kmap?.destroy) {
- indexMap.kmap.destroy();
- }
- indexMap.kmap = null;
- }
- function ensureRecordMap() {
- if (!recordMapContainer.value) return false;
- location.value = getFarmMapLocation();
- destroyMap();
- indexMap.initMap(location.value, recordMapContainer.value, { editable: false, movable: false });
- return Boolean(indexMap.kmap);
- }
- function syncRecordMapView() {
- nextTick(() => {
- setTimeout(() => {
- if (showMap.value) return;
- if (!ensureRecordMap()) return;
- resizeMap();
- }, 250);
- });
- }
- function ensureUploadMap() {
- if (!uploadMapContainer.value) return false;
- location.value = getFarmMapLocation();
- destroyMap();
- indexMap.initMap(location.value, uploadMapContainer.value, { editable: false, movable: false });
- return Boolean(indexMap.kmap);
- }
- function resizeMap() {
- indexMap.kmap?.map?.updateSize?.();
- }
- /** 弹窗动画结束后再初始化,避免容器尺寸为 0 导致白屏 */
- function syncUploadMapView() {
- const geometryArr = store.getters['recordDetails/mapConfirmCoordinatesArray'];
- nextTick(() => {
- setTimeout(() => {
- if (!ensureUploadMap()) return;
- resizeMap();
- if (geometryArr.length) {
- indexMap.setAreaGeometry(geometryArr);
- resizeMap();
- }
- }, 250);
- });
- }
- function syncFormFromMapConfirmPayload() {
- if (!showPestUploadPopup.value) return;
- syncUploadMapView();
- }
- const handleAbnormalRecord = () => {
- formData.value.ratio = '';
- formData.value.regionName = '';
- if (recordType.value === 'growth') {
- syncGrowthAbnormalTypeSelection();
- } else {
- formData.value.abnormalType = '';
- }
- showPestUploadPopup.value = true;
- };
- const handleMapClick = () => {
- router.push({
- name: 'MapManage', query: {
- type: recordType.value,
- }
- });
- }
- const showPhenologyUploadPopup = ref(false);
- const showPestUploadPopup = ref(false);
- watch(showPestUploadPopup, (show) => {
- if (show) {
- if (recordType.value === 'growth') {
- nextTick(() => syncGrowthAbnormalTypeSelection());
- }
- syncUploadMapView();
- } else {
- destroyMap();
- if (!showMap.value) {
- syncRecordMapView();
- }
- }
- });
- const pestInitImgArr = ref([]);
- const phenologyUploadPopupRef = ref(null);
- const pestUploadPopupRef = ref(null);
- const phenologyTrackTimelineRef = ref(null);
- const handlePestUploadPopupReset = () => {
- pestInitImgArr.value = [];
- };
- const curStage = ref({});
- const curCode = ref('')
- function onStageScrollSettled(index, stage, code) {
- shouldApplyPhenologyDefault.value = false;
- 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 = () => {
- showPhenologyUploadPopup.value = true;
- };
- const handleCancelPhenologyUploadPopup = () => {
- showPhenologyUploadPopup.value = false;
- };
- const handleConfirmPhenologyUpload = () => {
- 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) {
- showPhenologyUploadPopup.value = false;
- phenologyTrackTimelineRef.value?.refresh?.();
- ElMessage.success(t('recordDetails.confirmSuccess'));
- }
- });
- };
- const handleCancelPestUploadPopup = () => {
- showPestUploadPopup.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;
- }
- function validateGrowthAbnormalType() {
- if (String(formData.value.abnormalType ?? '').trim() === '') {
- ElMessage.warning(t('recordDetails.growthAbnormalTypeRequired'));
- return false;
- }
- return true;
- }
- const handleConfirmPestUpload = (imgArr) => {
- if (!validateAbnormalRatio()) return;
- if (recordType.value === 'growth' && !validateGrowthAbnormalType()) return;
- if (recordType.value === 'pest') {
- const hasDrawnMap = store.getters['recordDetails/mapConfirmCoordinatesArray'].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,
- "recog_type": "20",
- image_urls: imgArr,
- ...formData.value,
- }
- VE_API.record.writeFarmRecord(params).then(res => {
- if (res.code === 200) {
- showPestUploadPopup.value = false;
- store.commit('recordDetails/CLEAR_MAP_CONFIRM_PAYLOAD');
- phenologyTrackTimelineRef.value?.refresh?.();
- ElMessage.success(t('recordDetails.confirmSuccess'));
- } else {
- ElMessage.error(res.msg);
- }
- });
- };
- const activeTab = ref(0);
- const handleTabClick = (index) => {
- activeTab.value = index;
- };
- const workDetail = ref({});
- const recordHeaderInfo = computed(() => {
- if (recordType.value === 'growth') {
- return {
- titleKey: 'agriRecord.growthWorkName',
- question: currentGrowthAnomalyDetail.value?.interact_question,
- };
- }
- if (recordType.value === 'pest') {
- return {
- titleKey: 'agriRecord.pestWorkName',
- question: currentPestDetail.value?.interaction_reason,
- };
- }
- return {
- titleKey: 'agriRecord.phenologyWorkName',
- question: workDetail.value.work_reason_short,
- };
- });
- const leadDetailField = computed(() => {
- if (recordType.value === 'growth') {
- return {
- labelKey: 'scienceLabel',
- content: currentGrowthAnomalyDetail.value?.agri_judgment,
- };
- }
- if (recordType.value === 'pest') {
- return {
- labelKey: 'scienceLabel',
- content: currentPestDetail.value?.kepu,
- };
- }
- return {
- labelKey: 'farmAnalysisLabel',
- content: workDetail.value.work_reason_detail,
- };
- });
- const trailDetailFields = computed(() => {
- if (recordType.value === 'growth') {
- const detail = currentGrowthAnomalyDetail.value;
- const phenotypeImage = detail?.phenotype_image ?? detail?.phenotype_img ?? detail?.image;
- const mockLevel = getGrowthTabDisplayLevel(activeGrowthAnomalyIndex.value);
- const anomalyLevelContent =
- detail?.abnormal_level ??
- detail?.anomaly_level ??
- detail?.level_desc ??
- detail?.harm_level ??
- detail?.level;
- return [
- {
- labelKey: 'anomalyLevelLabel',
- content:
- anomalyLevelContent ||
- (mockLevel ? t('agriRecord.riskLevel', { level: mockLevel }) : ''),
- },
- {
- labelKey: 'phenotypeLabel',
- content: detail?.phenotype,
- image: phenotypeImage || pestPhenotypePlaceholder,
- },
- { labelKey: 'highRiskLabel', content: detail?.patrol_points },
- ];
- }
- if (recordType.value === 'pest') {
- const detail = currentPestDetail.value;
- return [
- {
- labelKey: 'harmLevelLabel',
- content: detail?.harm ?? detail?.harm_desc ?? detail?.harm_level ?? detail?.damage,
- },
- {
- labelKey: 'phenotypeLabel',
- content: detail?.phenotype,
- image: pestPhenotypePlaceholder,
- },
- { labelKey: 'highRiskLabel', content: detail?.patrol_points },
- ];
- }
- return [
- { labelKey: 'patrolLabel', content: workDetail.value.inspection_keypoints },
- { labelKey: 'phenotypeLabel', content: workDetail.value.phenotypic_characteristics },
- ];
- });
- const growthStageIndex = ref();
- const growthStages = ref([]);
- const phenologyRawList = ref([]);
- const phenologyTimelineKey = ref(0);
- /** 数据加载后按物候编码定位,用户拖动后不再自动覆盖 */
- const shouldApplyPhenologyDefault = ref(true);
- function normalizePhenologyCode(code) {
- if (code == null || code === '') return '';
- return String(code).trim();
- }
- function findStageIndexByPhenophaseCode(stages, code) {
- const target = normalizePhenologyCode(code);
- if (!Array.isArray(stages) || !stages.length || !target) return -1;
- return stages.findIndex(
- (s) => normalizePhenologyCode(s.phenophase_code) === target
- );
- }
- function resolveStageIndexByPhenophaseCode(stages, rawItems, code) {
- const target = normalizePhenologyCode(code);
- if (!target || !Array.isArray(stages) || !stages.length) return -1;
- const byFlat = findStageIndexByPhenophaseCode(stages, target);
- if (byFlat >= 0) return byFlat;
- const period = (rawItems || []).find(
- (item) =>
- normalizePhenologyCode(item.phenophase_code ?? item.phenophaseCode) ===
- target
- );
- if (!period) return -1;
- const milestones = normalizeTimeDescribe(period.time_discribe);
- const firstLabel = milestones[0]?.label;
- if (!firstLabel) return -1;
- return stages.findIndex(
- (s) =>
- s.label === firstLabel &&
- normalizePhenologyCode(s.phenophase_code) === target
- );
- }
- function buildGrowthStagesFlat(rawItems) {
- const flat = [];
- for (const item of rawItems) {
- const periodCode =
- item.phenophase_code ?? item.phenophaseCode ?? '';
- 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:
- t.phenophase_code ??
- t.phenophaseCode ??
- t.code ??
- periodCode,
- });
- }
- }
- return flat;
- }
- /** 时间轴初始定位:优先 workDetail.phenelogy_code,否则路由 curCode */
- function resolveTimelineDefaultCode() {
- const fromWork =
- workDetail.value?.phenelogy_code ?? workDetail.value?.phenology_code;
- return normalizePhenologyCode(fromWork ?? route.query.curCode);
- }
- function applyTimelineDefaultIndex(flat, rawItems) {
- const targetCode = resolveTimelineDefaultCode();
- const defaultIndex = resolveStageIndexByPhenophaseCode(
- flat,
- rawItems,
- targetCode
- );
- if (defaultIndex < 0) return -1;
- shouldApplyPhenologyDefault.value = true;
- growthStageIndex.value = defaultIndex;
- growthStages.value = flat;
- phenologyTimelineKey.value += 1;
- return defaultIndex;
- }
- function syncCurStageFromModel() {
- const stages = growthStages.value;
- if (!Array.isArray(stages) || !stages.length) return;
- const n = stages.length;
- let i;
- if (shouldApplyPhenologyDefault.value) {
- const targetCode = resolveTimelineDefaultCode();
- const byCode = resolveStageIndexByPhenophaseCode(
- stages,
- phenologyRawList.value,
- targetCode
- );
- i = byCode >= 0 ? byCode : Math.max(0, Math.floor((n - 1) / 2));
- if (byCode >= 0) {
- growthStageIndex.value = i;
- shouldApplyPhenologyDefault.value = false;
- }
- } else {
- 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(async () => {
- sessionStorage.removeItem('mapManageConfirmPayload');
- syncFormFromMapConfirmPayload();
- if (route.query.workId) {
- await getWorkDetail();
- }
- if (route.query.type === 'pest') {
- await getAbnormalPlan();
- syncPestPanelArrow();
- }
- if (route.query.type === 'growth') {
- getGrowthAnomalyInfo();
- }
- getFindPhenologyInfo();
- if (!showMap.value) {
- syncRecordMapView();
- }
- });
- 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];
- console.log(workDetail.value);
- }
- }
- // 获取生长异常列表
- 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) {
- phenologyRawList.value = res.data;
- const flat = buildGrowthStagesFlat(res.data);
- if (!flat.length) return;
- const defaultIndex = applyTimelineDefaultIndex(flat, res.data);
- await nextTick();
- if (defaultIndex >= 0) {
- growthStageIndex.value = defaultIndex;
- shouldApplyPhenologyDefault.value = false;
- syncCurStageFromModel();
- }
- }
- };
- </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;
- }
- .card-wrap+.phenology-tip-banner,
- .phenology-tip-banner+.growth-anomaly-tabs,
- .growth-anomaly-tabs+.card-wrap {
- margin-top: 10px;
- }
- .growth-anomaly-tabs {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- padding-top: 12px;
- .item-tab {
- position: relative;
- flex: 1 1 calc(33.333% - 6px);
- min-width: 0;
- box-sizing: border-box;
- padding: 8px 0;
- border: 1px solid transparent;
- border-radius: 6px;
- background: #fff;
- color: #666;
- text-align: center;
- font-size: 14px;
- font-weight: 600;
- }
- .item-tab--active {
- border-color: #2199f8;
- background: rgba(33, 153, 248, 0.08);
- color: #2199f8;
- }
- .item-tab__text {
- display: block;
- width: 100%;
- }
- .item-tab__badge {
- position: absolute;
- top: -9px;
- right: -8px;
- padding: 0 6px;
- border-radius: 2px;
- font-size: 10px;
- font-weight: 400;
- line-height: 16px;
- color: #fff;
- z-index: 1;
- &--1 {
- background: rgba(33, 153, 248, 0.4);
- }
- &--2 {
- background: rgba(33, 153, 248, 0.7);
- }
- &--3 {
- background: #2199f8;
- }
- &--on-active {
- background: #2199f8;
- }
- }
- }
- .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;
- }
- &__phenotype {
- display: flex;
- align-items: flex-start;
- gap: 6px;
- &-content {
- flex: 1;
- min-width: 0;
- }
- }
- &__phenotype-image {
- width: 70px;
- height: 63px;
- object-fit: cover;
- border-radius: 5px;
- cursor: pointer;
- }
- }
- .card-item+.card-item {
- margin-top: 8px;
- }
- .border-wrap {
- border-radius: 8px;
- border: 0.5px solid #2199F8;
- padding: 10px;
- .question-box {
- border: 1px solid #2199F8;
- background: rgba(33, 153, 248, 0.1);
- 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;
- }
- }
- }
- .card-wrap+.card-wrap {
- margin-top: 10px;
- }
- .pest-classify-picker {
- padding: 10px;
- background: #f2f3f5;
- border-radius: 8px;
- display: flex;
- flex-direction: column;
- align-items: center;
- &__tabs {
- position: relative;
- z-index: 2;
- display: inline-flex;
- width: fit-content;
- padding: 3px;
- border-radius: 4px;
- background: #fff;
- box-sizing: border-box;
- }
- &__tab {
- position: relative;
- padding: 6px 28px;
- border-radius: 4px;
- font-size: 13px;
- line-height: 18px;
- color: #767676;
- text-align: center;
- box-sizing: border-box;
- &--active {
- background: #2199f8;
- color: #fff;
- }
- }
- &__panel {
- position: relative;
- align-self: stretch;
- width: 100%;
- margin-top: 10px;
- padding: 12px 10px 10px;
- border-radius: 4px;
- background: #fff;
- box-sizing: border-box;
- &::before {
- content: '';
- position: absolute;
- top: -7px;
- left: var(--arrow-left, 50%);
- transform: translateX(-50%);
- width: 0;
- height: 0;
- border-left: 8px solid transparent;
- border-right: 8px solid transparent;
- border-bottom: 8px solid #fff;
- z-index: 3;
- }
- }
- &__categories {
- display: flex;
- justify-content: space-between;
- gap: 2px;
- margin-bottom: 10px;
- overflow-x: auto;
- -webkit-overflow-scrolling: touch;
- }
- &__category {
- position: relative;
- flex: 1;
- padding-bottom: 5px;
- font-size: 13px;
- color: #909090;
- text-align: center;
- &--active {
- color: #333;
- border-bottom: 2px solid #3A3A3A;
- }
- }
- &__details {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 10px;
- padding-top: 6px;
- }
- &__detail {
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 5px 6px;
- border: 1px solid transparent;
- border-radius: 4px;
- background: #f5f5f5;
- color: #909090;
- font-size: 12px;
- text-align: center;
- box-sizing: border-box;
- &--active {
- border-color: #2199f8;
- background: rgba(33, 153, 248, 0.1);
- color: #2199f8;
- }
- }
- &__detail-text {
- display: block;
- width: 100%;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- &__badge {
- position: absolute;
- top: -9px;
- right: -8px;
- padding: 0 5px;
- border-radius: 2px;
- font-size: 10px;
- color: #fff;
- z-index: 1;
- &--1 {
- background: rgba(33, 153, 248, 0.35);
- }
- &--2 {
- background: rgba(33, 153, 248, 0.65);
- }
- &--3 {
- background: #2199f8;
- }
- }
- }
- .phenology-track-section {
- margin-top: 15px;
- display: flex;
- flex-direction: column;
- gap: 16px;
- }
- }
- }
- .phenology-tip-banner {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 8px;
- padding: 10px;
- border-radius: 8px;
- background: rgba(255, 149, 61, 0.1);
- border: 1px solid rgba(255, 149, 61, 0.5);
- margin-top: 10px;
- .banner__left {
- flex: 1;
- .banner__title {
- font-size: 16px;
- font-weight: 600;
- color: #FF953D;
- }
- .banner__desc {
- font-size: 12px;
- color: rgba(0, 0, 0, 0.4);
- }
- }
- .banner__btn {
- display: flex;
- align-items: center;
- gap: 4px;
- padding: 7px 16px;
- border-radius: 25px;
- color: #ffffff;
- background: linear-gradient(180deg, #FFB273 0%, #FF953D 100%);
- }
- &.blue {
- background: #F1F8FE;
- border: 1px solid rgba(33, 153, 248, 0.5);
- .banner__title {
- color: #2199F8;
- }
- .banner__btn {
- background: linear-gradient(180deg, #8ACBFF 0%, #2199F8 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);
- }
- }
- }
- .growth-abnormal-type-field {
- margin-bottom: 12px;
- &__label {
- font-size: 16px;
- color: #000;
- margin-bottom: 10px;
- }
- }
- .growth-abnormal-type-select {
- width: 100%;
- :deep(.el-select__wrapper) {
- min-height: 44px;
- padding: 4px 12px;
- border-radius: 6px;
- border: 1px solid #d6d6d6;
- box-shadow: none;
- background: #fff;
- }
- }
- .special-input {
- padding: 8px;
- background: rgba(33, 153, 248, 0.1);
- border-radius: 5px;
- border: 1px solid #2199F8;
- }
- }
- </style>
|