|
|
@@ -27,17 +27,23 @@
|
|
|
<span>{{ t('recordDetails.abnormalRecord') }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div v-if="recordType === 'growth' && !showMap" class="tabs-list">
|
|
|
- <div v-for="(item, index) in growthAnomalyTabs" :key="index" class="item-tab"
|
|
|
- :class="{ 'item-tab--active': activeGrowthAnomalyIndex === index }"
|
|
|
- @click="handleGrowthAnomalyTabClick(index)">{{ item.label }}</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 !== 'pest'">
|
|
|
+ <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 v-if="recordType === 'phenology'" class="phenology-tip-banner blue">
|
|
|
+ <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>
|
|
|
@@ -48,6 +54,39 @@
|
|
|
</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)"
|
|
|
@@ -151,8 +190,9 @@
|
|
|
<ImagePreviewPopup v-model:show="showPhenotypeImagePreview" :images="phenotypePreviewImages" />
|
|
|
|
|
|
<!-- 物候 上传弹窗 -->
|
|
|
- <UploadProgressPopup ref="phenologyUploadPopupRef" v-model:show="showPhenologyUploadPopup" :enable-identify="false"
|
|
|
- @cancel="handleCancelPhenologyUploadPopup" :upload-required="false" @confirm="handleConfirmPhenologyUpload">
|
|
|
+ <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> -->
|
|
|
@@ -163,12 +203,21 @@
|
|
|
|
|
|
<!-- 病虫害 上传弹窗 -->
|
|
|
<UploadProgressPopup ref="pestUploadPopupRef" v-model:show="showPestUploadPopup" :enable-identify="true"
|
|
|
- :init-img-arr="pestInitImgArr"
|
|
|
- :confirm-text="t('recordDetails.confirmUpload')" :upload-required="false"
|
|
|
+ :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>
|
|
|
@@ -395,12 +444,25 @@ const tabsList = ref([
|
|
|
{ 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: item.value ?? item.id ?? index,
|
|
|
+ value: resolveGrowthAnomalyOptionValue(item, index),
|
|
|
}))
|
|
|
);
|
|
|
|
|
|
@@ -434,8 +496,35 @@ const hasMapConfirmGeometry = computed(() => store.getters['recordDetails/mapCon
|
|
|
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();
|
|
|
@@ -496,13 +585,18 @@ function syncFormFromMapConfirmPayload() {
|
|
|
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: 'pest',
|
|
|
+ type: recordType.value,
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
@@ -512,6 +606,9 @@ const showPestUploadPopup = ref(false);
|
|
|
|
|
|
watch(showPestUploadPopup, (show) => {
|
|
|
if (show) {
|
|
|
+ if (recordType.value === 'growth') {
|
|
|
+ nextTick(() => syncGrowthAbnormalTypeSelection());
|
|
|
+ }
|
|
|
syncUploadMapView();
|
|
|
} else {
|
|
|
destroyMap();
|
|
|
@@ -591,8 +688,17 @@ function validateAbnormalRatio() {
|
|
|
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;
|
|
|
@@ -675,9 +781,28 @@ const leadDetailField = computed(() => {
|
|
|
|
|
|
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: 'phenotypeLabel', content: currentGrowthAnomalyDetail.value?.phenotype },
|
|
|
- { labelKey: 'highRiskLabel', content: currentGrowthAnomalyDetail.value?.patrol_points },
|
|
|
+ {
|
|
|
+ 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') {
|
|
|
@@ -939,28 +1064,73 @@ const getFindPhenologyInfo = async () => {
|
|
|
.tabs-list {
|
|
|
margin: 10px 0;
|
|
|
flex-shrink: 0;
|
|
|
- display: grid;
|
|
|
- grid-template-columns: repeat(auto-fill, minmax(8em, 1fr));
|
|
|
+ }
|
|
|
+
|
|
|
+ .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: 6px 4px;
|
|
|
- border-radius: 2px;
|
|
|
- color: #767676;
|
|
|
+ padding: 8px 0;
|
|
|
+ border: 1px solid transparent;
|
|
|
+ border-radius: 6px;
|
|
|
background: #fff;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
+ color: #666;
|
|
|
text-align: center;
|
|
|
- font-size: 13px;
|
|
|
- white-space: nowrap;
|
|
|
- word-break: keep-all;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
}
|
|
|
|
|
|
.item-tab--active {
|
|
|
- background: #2199F8;
|
|
|
- color: #ffffff;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1349,6 +1519,29 @@ const getFindPhenologyInfo = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ .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);
|