| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- <template>
- <!-- <div class="add-btn">{{ t('点击新建管理分区') }}</div> -->
- <floating-panel class="file-float-panel" :class="{ 'custom-panel': height === anchors[0] }" v-model:height="height"
- :anchors="anchors">
- <div class="file-float-content">
- <div class="float-tabs">
- <div class="tab-active-bg" :style="primaryActiveBgStyle"></div>
- <div v-for="(item, index) in floatTabLabels" :key="item.value" class="tab-item"
- @click="changePrimaryTab(index)" :class="{ 'tab-item-active': activeTab === index }">
- {{ item.title }}
- </div>
- </div>
- <div class="tab-content-group" v-show="height !== anchors[0]">
- <template v-if="isAgriRecordTab">
- <div class="float-sub-tabs">
- <div v-for="(item, index) in agriSubTabLabels" :key="item.value" class="sub-tab-item"
- :class="{ 'sub-tab-item-active': activeSubTab === index }" @click="changeSubTab(index)">
- {{ item.title }}
- </div>
- </div>
- <div class="tab-loading" v-if="loading">{{ t('agriFile.loading') }}</div>
- <div class="tab-empty" v-else-if="displayList.length === 0">{{ t('agriFile.noData') }}</div>
- <div
- v-else
- v-for="item in displayList"
- :key="`${activeSubTabValue}-${item.id}`"
- class="tab-content-item"
- >
- <div class="time-tag">{{ item.time }}</div>
- <div class="item-info">
- {{ item.recordText }}
- <span class="blue-text">{{ item.ratio }}{{ item.showRatio ? '%' : '' }}</span>
- </div>
- </div>
- </template>
- <div v-else-if="isRemoteSensingTab" class="remote-sensing-chart">
- <div v-if="!isBaseMapTool" class="remote-sensing-chart__legend">
- <div
- v-for="item in remoteSensingLegendItems"
- :key="item.key"
- class="remote-sensing-chart__legend-item"
- >
- <span
- v-if="item.iconType === 'bar'"
- class="remote-sensing-chart__legend-bar"
- :style="{ background: item.color }"
- ></span>
- <span
- v-else-if="item.iconType === 'dashed'"
- class="remote-sensing-chart__legend-line remote-sensing-chart__legend-line--dashed"
- :style="{ '--legend-color': item.color }"
- ></span>
- <span
- v-else
- class="remote-sensing-chart__legend-line"
- :style="{ background: item.color }"
- ></span>
- <span class="remote-sensing-chart__legend-text">{{ item.label }}</span>
- </div>
- </div>
- <div class="tab-loading" v-if="loading">{{ t('agriFile.loading') }}</div>
- <div class="tab-empty" v-else-if="isBaseMapTool">
- {{ t('agriFile.remoteSensingSelectHint') }}
- </div>
- <remote-sensing-chart v-else />
- </div>
- </div>
- </div>
- </floating-panel>
- </template>
- <script setup>
- import { useI18n } from "@/i18n";
- import { RECORD_KEY_MAP } from "@/i18n/recordTextMap";
- import { FloatingPanel } from 'vant';
- import { computed, ref } from 'vue';
- import remoteSensingChart from './remoteSensingChart.vue';
- const { t } = useI18n();
- const props = defineProps({
- farmRecordData: {
- type: Object,
- default: () => ({}),
- },
- activeTab: {
- type: Number,
- default: 0,
- },
- activeSubTab: {
- type: Number,
- default: 0,
- },
- loading: {
- type: Boolean,
- default: false,
- },
- mapTool: {
- type: Object,
- default: null,
- },
- cropVariety: {
- type: String,
- default: "",
- },
- });
- const emit = defineEmits(["update:activeTab", "update:activeSubTab"]);
- const anchors = [
- 130,
- Math.round(0.4 * window.innerHeight),
- Math.round(0.8 * window.innerHeight),
- ];
- const height = ref(anchors[0]);
- const AGRI_SUB_TAB_KEYS = ["phenology", "farming", "abnormal"];
- const resolveRecordI18nKey = (record) => {
- if (record == null || record === "") return "";
- const text = String(record).trim();
- return RECORD_KEY_MAP[text] || text;
- };
- const floatTabLabels = computed(() => [
- { title: t("agriFile.tabAgriRecord"), value: "agriRecord" },
- { title: t("agriFile.tabRemoteSensing"), value: "remoteSensing" },
- ]);
- const agriSubTabLabels = computed(() => [
- { title: t("agriFile.tabPhenology"), value: "phenology" },
- { title: t("agriFile.tabFarming"), value: "farming" },
- { title: t("agriFile.tabAbnormal"), value: "abnormal" },
- ]);
- const isAgriRecordTab = computed(() => floatTabLabels.value[props.activeTab]?.value === "agriRecord");
- const isRemoteSensingTab = computed(() => floatTabLabels.value[props.activeTab]?.value === "remoteSensing");
- const isBaseMapTool = computed(() => (props.mapTool?.index ?? 0) === 0);
- const REMOTE_SENSING_LEGEND_ITEMS = [
- { key: "ndwi", labelKey: "agriFile.remoteSensingLegendNdwi", color: "#6277FB", iconType: "line" },
- { key: "ndvi", labelKey: "agriFile.remoteSensingLegendNdvi", color: "#1CC277", iconType: "line" },
- { key: "precipitation", labelKey: "agriFile.remoteSensingLegendPrecipitation", color: "#E2F1FD", iconType: "bar" },
- { key: "avgPrecipitation", labelKey: "agriFile.remoteSensingLegendAvgPrecipitation", color: "#66BBFF", iconType: "dashed" },
- ];
- const remoteSensingLegendItems = computed(() =>
- REMOTE_SENSING_LEGEND_ITEMS.map(({ key, labelKey, color, iconType }) => ({
- key,
- label: t(labelKey),
- color,
- iconType,
- }))
- );
- const activeSubTabValue = computed(
- () => agriSubTabLabels.value[props.activeSubTab]?.value || AGRI_SUB_TAB_KEYS[0]
- );
- const displayList = computed(() => {
- if (!isAgriRecordTab.value) return [];
- const list = props.farmRecordData?.[activeSubTabValue.value] || [];
- return list
- .map((item) => {
- const i18nKey = resolveRecordI18nKey(item?.record);
- return {
- ...item,
- recordText: i18nKey ? t(i18nKey) : "",
- showRatio:
- activeSubTabValue.value !== "farming" &&
- String(item.ratio ?? "").length > 0,
- };
- })
- .filter((item) => item.recordText.length > 0);
- });
- const changePrimaryTab = (index) => {
- emit("update:activeTab", index);
- };
- const changeSubTab = (index) => {
- emit("update:activeSubTab", index);
- };
- const primaryActiveBgStyle = computed(() => ({
- transform: `translateX(${props.activeTab * 100}%)`,
- }));
- </script>
- <style lang="scss" scoped>
- .add-btn {
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- color: #fff;
- border-radius: 20px;
- padding: 0 20px;
- background: #2199f8;
- height: 40px;
- line-height: 40px;
- cursor: pointer;
- }
- .file-float-panel {
- left: 12px;
- width: calc(100% - 24px);
- &.custom-panel {
- background: transparent;
- ::v-deep {
- .van-floating-panel__header {
- background: #fff;
- border-radius: 10px 10px 0 0;
- }
- .van-floating-panel__content {
- background: transparent;
- margin-top: -1px;
- }
- }
- }
- }
- .file-float-content {
- padding: 0 10px 10px;
- background: #fff;
- border-radius: 0 0 10px 10px;
- .float-tabs {
- position: relative;
- border-radius: 4px;
- padding: 3px;
- background: #E9E9E9;
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- align-items: center;
- overflow: hidden;
- .tab-active-bg {
- position: absolute;
- top: 3px;
- left: 3px;
- width: calc((100% - 6px) / 2);
- height: 26px;
- border-radius: 4px;
- background: #fff;
- transition: transform 0.25s ease;
- }
- .tab-item {
- position: relative;
- z-index: 1;
- flex: 1;
- height: 26px;
- line-height: 26px;
- text-align: center;
- color: #767676;
- border-radius: 4px;
- transition: color 0.2s ease;
- &.tab-item-active {
- color: #0D0D0D;
- }
- }
- }
- .float-sub-tabs {
- display: flex;
- align-items: center;
- gap: 8px;
- margin-bottom: 10px;
- .sub-tab-item {
- height: 26px;
- line-height: 24px;
- padding: 0 10px;
- font-size: 12px;
- color: #767676;
- background: #e9e9e9;
- border-radius: 4px;
- border: 1px solid transparent;
- box-sizing: border-box;
- cursor: pointer;
- transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
- &.sub-tab-item-active {
- color: #2199f8;
- background: #fff;
- border-color: #2199f8;
- }
- }
- }
- .tab-content-group {
- padding-top: 12px;
- .tab-loading,
- .tab-empty {
- text-align: center;
- color: #9a9a9a;
- font-size: 13px;
- padding: 14px 0;
- }
- .tab-content-item+.tab-content-item {
- margin-top: 10px;
- }
- .tab-content-item {
- display: flex;
- align-items: center;
- gap: 10px;
- .time-tag {
- color: #2199F8;
- background: rgba(33, 153, 248, 0.1);
- font-size: 12px;
- height: 21px;
- line-height: 21px;
- padding: 0 6px;
- min-width: fit-content;
- box-sizing: border-box;
- }
- .item-info {
- color: rgba(60, 60, 60, 0.5);
- line-height: 21px;
- }
- .blue-text {
- color: #2199f8;
- }
- }
- .remote-sensing-chart {
- &__legend {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 10px;
- margin-bottom: 8px;
- }
- &__legend-item {
- display: flex;
- align-items: center;
- gap: 4px;
- }
- &__legend-line {
- width: 15px;
- height: 3px;
- border-radius: 2px;
- &--dashed {
- background: repeating-linear-gradient(
- to right,
- var(--legend-color) 0,
- var(--legend-color) 5px,
- transparent 5px,
- transparent 7px
- ) !important;
- }
- }
- &__legend-bar {
- width: 8px;
- height: 12px;
- border-radius: 1px;
- flex-shrink: 0;
- }
- &__legend-text {
- font-size: 12px;
- color: #666666;
- }
- }
- }
- }
- </style>
|