weatherInfo.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. <template>
  2. <div class="weather-info is-garden" :class="{ expanded: isExpanded}">
  3. <div class="header flex-center">
  4. <div class="header-left">
  5. <div class="address-select flex-center" v-if="hasFarm">
  6. <el-dropdown class="select-garden" trigger="click" popper-class="select-garden-popper">
  7. <div class="el-dropdown-link flex-center">
  8. <span class="ellipsis-l1">{{ farmName }}</span>
  9. <div class="default-text" v-show="isDefaultFarm">默认</div>
  10. <el-icon class="el-icon--right"><arrow-down /></el-icon>
  11. </div>
  12. <template #dropdown>
  13. <el-dropdown-menu>
  14. <el-dropdown-item
  15. @click="handleCommand(item)"
  16. v-for="item in farmList"
  17. :key="item.id"
  18. :class="{ 'selected-active-garden': farmId === item.id }"
  19. >
  20. <span>{{ item.name }}</span>
  21. <span v-if="item.defaultOption" class="dropdown-default-text">默认</span>
  22. </el-dropdown-item>
  23. </el-dropdown-menu>
  24. </template>
  25. </el-dropdown>
  26. <div class="btn-wrap">
  27. <div class="add-garden" @click="handleFarmInfo">农场信息</div>
  28. <div class="add-garden gray-btn" @click="handleAddFarm">
  29. <el-icon><Plus /></el-icon>
  30. <span>新建农场</span>
  31. </div>
  32. </div>
  33. </div>
  34. <div class="address-select flex-center farm-name" v-else>
  35. 示范农场
  36. </div>
  37. <div class="temperature flex-center">
  38. <div class="temperature-number">{{ currentWeather.temp || '--' }}</div>
  39. <div class="temperature-text">
  40. <span>{{ locationName || '--' }}</span>
  41. <div class="temperature-text-time">
  42. <span>{{ currentWeather.text }}-</span>
  43. <span>{{ currentDateText }}</span>
  44. <span v-show="!isExpanded" class="temperature-text-more" @click="toggleExpand">
  45. 展开更多天气
  46. </span>
  47. </div>
  48. </div>
  49. </div>
  50. </div>
  51. <div class="weather-icon" v-if="currentWeather.iconDay">
  52. <i :class="'qi-'+currentWeather.iconDay + '-fill'"></i>
  53. </div>
  54. <!-- <div class="weather-icon" v-else>
  55. <img :src="`https://birdseye-img.sysuimars.com/weather/${currentWeather.iconDay}.svg`" alt="" />
  56. </div> -->
  57. </div>
  58. <div class="weather-chart-container">
  59. <div class="weather-chart-title">
  60. <span>未来七天天气</span>
  61. <div class="weather-chart-title-more" @click="toggleExpand">收起</div>
  62. </div>
  63. <weather-chart class="weather-chart" :weather-data="weatherData"></weather-chart>
  64. </div>
  65. <!-- 农场信息 -->
  66. <farm-info-popup
  67. ref="myFarmInfoRef"
  68. :showEditBtn="false"
  69. :showBtn="true"
  70. :farmId="farmId"
  71. ></farm-info-popup>
  72. </div>
  73. </template>
  74. <script setup>
  75. import { ref, onActivated, computed, watch, onMounted } from "vue";
  76. import weatherChart from "./weatherChart.vue";
  77. import { useRouter } from "vue-router";
  78. import { useStore } from "vuex";
  79. import farmInfoPopup from "@/views/old_mini/home/components/farmInfoPopup.vue";
  80. import { convertPointToArray } from "@/utils/index";
  81. const store = useStore();
  82. const props = defineProps({
  83. gardenId: {
  84. type: [Number, String],
  85. default: null
  86. }
  87. });
  88. // 定义emit事件
  89. const emit = defineEmits(['weatherExpanded','changeGarden']);
  90. const router = useRouter();
  91. const handleCommand = ({id, name}) => {
  92. farmName.value = name;
  93. farmId.value = id;
  94. // 更新默认农场标识
  95. const selectedFarm = farmList.value.find(farm => farm.id === id);
  96. isDefaultFarm.value = selectedFarm ? selectedFarm.defaultOption || false : false;
  97. // 保存用户选择的农场到 localStorage
  98. localStorage.setItem('selectedFarmId', id);
  99. localStorage.setItem('selectedFarmName', name);
  100. localStorage.setItem('selectedFarmPoint', selectedFarm.wkt);
  101. getLocationName();
  102. getWeatherData();
  103. emit('changeGarden',{id, name});
  104. };
  105. const isExpanded = ref(false);
  106. const toggleExpand = () => {
  107. isExpanded.value = !isExpanded.value;
  108. emit('weatherExpanded',isExpanded.value);
  109. };
  110. const farmId = ref(null);
  111. const farmName = ref("");
  112. const farmList = ref([]);
  113. const hasFarm = ref(true)
  114. const isDefaultFarm = ref(false); // 添加默认农场标识
  115. // 根据传入的gardenId设置农场(先刷新列表再设置)
  116. async function setFarmByGardenId(gardenIdValue) {
  117. if (!gardenIdValue) {
  118. return false;
  119. }
  120. // 先刷新农场列表,确保数据是最新的
  121. return new Promise((resolve) => {
  122. VE_API.farm.listByUserId().then(({data}) => {
  123. // const fullData = data.filter(item => item.userType === 2);
  124. const fullData = data;
  125. farmList.value = fullData || [];
  126. if (fullData && fullData.length > 0) {
  127. hasFarm.value = true;
  128. const targetFarm = fullData.find(farm => farm.id == gardenIdValue);
  129. if (targetFarm) {
  130. farmName.value = targetFarm.name;
  131. farmId.value = Number(gardenIdValue);
  132. isDefaultFarm.value = targetFarm.defaultOption || false;
  133. // 保存到 localStorage
  134. localStorage.setItem('selectedFarmId', farmId.value);
  135. localStorage.setItem('selectedFarmName', farmName.value);
  136. localStorage.setItem('selectedFarmPoint', targetFarm.wkt);
  137. getLocationName();
  138. getWeatherData();
  139. emit('changeGarden', { id: farmId.value, name: farmName.value });
  140. resolve(true);
  141. } else {
  142. resolve(false);
  143. }
  144. } else {
  145. farmList.value = [];
  146. hasFarm.value = false;
  147. getLocationName();
  148. getWeatherData();
  149. resolve(false);
  150. }
  151. }).catch(() => {
  152. resolve(false);
  153. });
  154. });
  155. }
  156. // 获取农场列表
  157. function getFarmList() {
  158. // 如果传入了 gardenId,优先使用 setFarmByGardenId(它会刷新列表并设置)
  159. if (props.gardenId) {
  160. setFarmByGardenId(props.gardenId).then((setSuccess) => {
  161. // 如果设置失败,使用已获取的列表数据执行默认逻辑(避免重复请求)
  162. if (!setSuccess && farmList.value && farmList.value.length > 0) {
  163. selectFarmFromList(farmList.value);
  164. } else if (!setSuccess) {
  165. // 如果列表为空,再次获取列表
  166. getFarmListWithoutGardenId();
  167. }
  168. });
  169. return;
  170. }
  171. // 如果没有传入 gardenId,执行正常逻辑
  172. getFarmListWithoutGardenId();
  173. }
  174. // 从列表中选择农场(使用已有列表数据)
  175. function selectFarmFromList(data) {
  176. // 使用 localStorage 中保存的农场选择
  177. const savedFarmId = localStorage.getItem('selectedFarmId');
  178. const savedFarmName = localStorage.getItem('selectedFarmName');
  179. if (savedFarmId && savedFarmName) {
  180. // 检查保存的农场是否还在当前列表中
  181. const savedFarm = data.find(farm => farm.id == savedFarmId);
  182. if (savedFarm) {
  183. farmName.value = savedFarmName;
  184. farmId.value = Number(savedFarmId);
  185. isDefaultFarm.value = savedFarm.defaultOption || false;
  186. localStorage.setItem('selectedFarmPoint', savedFarm.wkt);
  187. } else {
  188. // 如果保存的农场不在列表中,按优先级选择
  189. selectDefaultFarm(data);
  190. }
  191. } else {
  192. // 如果没有保存的选择,按优先级选择
  193. selectDefaultFarm(data);
  194. }
  195. getLocationName();
  196. getWeatherData();
  197. emit('changeGarden',{id: farmId.value, name: farmName.value});
  198. }
  199. // 获取农场列表(不处理传入的gardenId)
  200. function getFarmListWithoutGardenId() {
  201. VE_API.farm.listByUserId().then(({data}) => {
  202. // const fullData = data.filter(item => item.userType === 2);
  203. const fullData = data;
  204. farmList.value = fullData || [];
  205. if (fullData && fullData.length > 0) {
  206. hasFarm.value = true;
  207. selectFarmFromList(fullData);
  208. } else {
  209. farmList.value = [];
  210. hasFarm.value = false;
  211. getLocationName();
  212. getWeatherData();
  213. }
  214. })
  215. }
  216. // 监听父组件传入的gardenId变化
  217. watch(() => props.gardenId, (newGardenId) => {
  218. if (newGardenId) {
  219. // 直接调用 setFarmByGardenId,它会刷新列表并设置
  220. setFarmByGardenId(newGardenId);
  221. }
  222. }, { immediate: false });
  223. // 选择默认农场的逻辑
  224. function selectDefaultFarm(data) {
  225. // 首先查找 defaultOption 为 true 的农场
  226. const defaultFarm = data.find(farm => farm.defaultOption === true);
  227. if (defaultFarm) {
  228. // 如果有默认农场,选择它
  229. farmName.value = defaultFarm.name;
  230. farmId.value = defaultFarm.id;
  231. isDefaultFarm.value = true;
  232. localStorage.setItem('selectedFarmPoint', defaultFarm.wkt);
  233. } else {
  234. // 如果没有默认农场,选择第一个
  235. farmName.value = data[0].name;
  236. farmId.value = data[0].id;
  237. isDefaultFarm.value = data[0].defaultOption || false;
  238. localStorage.setItem('selectedFarmPoint', data[0].wkt);
  239. }
  240. // 保存到 localStorage
  241. localStorage.setItem('selectedFarmId', farmId.value);
  242. localStorage.setItem('selectedFarmName', farmName.value);
  243. localStorage.setItem('selectedFarmPoint', data[0].wkt);
  244. getLocationName();
  245. getWeatherData();
  246. }
  247. onActivated(() => {
  248. getFarmList();
  249. });
  250. // 暴露刷新方法供父组件调用
  251. defineExpose({
  252. refreshFarmList: getFarmList,
  253. toggleExpand
  254. });
  255. const locationName = ref("");
  256. const weatherData = ref(null);
  257. const currentWeather = ref({ temp: "--", text: "--", iconDay: "" });
  258. const MAP_KEY = "CZLBZ-LJICQ-R4A5J-BN62X-YXCRJ-GNBUT";
  259. function getLocationName() {
  260. const locationPoint = localStorage.getItem('selectedFarmPoint') || store.state.home.miniUserLocationPoint;
  261. const farmLocation = convertPointToArray(locationPoint);
  262. let formattedLocation = `${farmLocation[1]},${farmLocation[0]}`;
  263. const params = {
  264. key: MAP_KEY,
  265. location: formattedLocation,
  266. };
  267. VE_API.old_mini_map.location(params).then(({ result }) => {
  268. // locationVal.value = result.formatted_addresses.recommend;
  269. locationName.value = result?.address_component
  270. ? result.address_component.city + result.address_component.district
  271. : result?.address + "";
  272. });
  273. }
  274. const myFarmInfoRef = ref(null);
  275. const handleFarmInfo = () => {
  276. // myFarmInfoRef.value.handleShow();
  277. router.push(`/farm_info?subjectId=${farmId.value}`);
  278. }
  279. const handleAddFarm = () => {
  280. router.push(`/create_farm?type=farmer`);
  281. }
  282. // 获取天气数据
  283. function getWeatherData() {
  284. const point = localStorage.getItem('selectedFarmPoint') || store.state.home.miniUserLocationPoint;
  285. if (!point) {
  286. return;
  287. }
  288. VE_API.old_mini_map.get7d({ point }).then(({ data }) => {
  289. if (data && data.daily && data.daily.length > 0) {
  290. weatherData.value = data;
  291. // 设置当前天气(第一天的数据)
  292. const today = data.daily[0];
  293. currentWeather.value = {
  294. temp: today.tempMax || today.tempMin || 26,
  295. text: today.textDay || "晴天",
  296. iconDay: today.iconDay
  297. };
  298. }
  299. }).catch(() => {
  300. // 获取天气数据失败,使用默认值
  301. });
  302. }
  303. // 获取当前日期和星期
  304. const currentDateText = computed(() => {
  305. const now = new Date();
  306. const month = String(now.getMonth() + 1).padStart(2, '0');
  307. const day = String(now.getDate()).padStart(2, '0');
  308. return `${month}/${day}`;
  309. });
  310. </script>
  311. <style lang="scss" scoped>
  312. .weather-info {
  313. width: 100%;
  314. // height: 58px;
  315. height: 70px;
  316. border-radius: 14px;
  317. background-color: #ffffff;
  318. padding: 10px 12px;
  319. box-sizing: border-box;
  320. transition: height 0.3s ease-in-out;
  321. overflow: hidden;
  322. &.is-garden {
  323. height: 85px;
  324. box-shadow: 0px 1px 5.5px 0px #00000005;
  325. border-radius: 8px;
  326. padding: 10px 12px;
  327. }
  328. &.expanded {
  329. height: 312px;
  330. background-image: linear-gradient(90deg, #e2f1fe 0%, #ffffff 80%);
  331. }
  332. &.bg-white{
  333. border-radius: 14px;
  334. background-image: linear-gradient(90deg, #e2f1fe 0%, #ffffff 80%);
  335. }
  336. .flex-center {
  337. display: flex;
  338. align-items: center;
  339. }
  340. .header {
  341. display: flex;
  342. align-items: flex-end;
  343. justify-content: space-between;
  344. .header-left {
  345. .address-select {
  346. .select-garden {
  347. width: fit-content;
  348. max-width: 170px;
  349. margin-right: 8px;
  350. margin-bottom: 10px;
  351. .el-dropdown-link {
  352. font-size: 15px;
  353. font-weight: 500;
  354. color: #000000;
  355. span {
  356. width: fit-content;
  357. max-width: 95%;
  358. margin-right: 4px;
  359. }
  360. .default-text {
  361. font-size: 12px;
  362. color: #fff;
  363. padding: 2px 4px;
  364. width: 44px;
  365. text-align: center;
  366. box-sizing: border-box;
  367. font-weight: 400;
  368. background-color: #2199f8;
  369. border-radius: 25px;
  370. }
  371. }
  372. }
  373. .btn-wrap{
  374. position: absolute;
  375. right: 10px;
  376. top: 8px;
  377. display: flex;
  378. gap: 8px;
  379. }
  380. .add-garden {
  381. font-size: 12px;
  382. color: #2199f8;
  383. padding: 3px 10px;
  384. border: 1px solid rgba(33, 153, 248, 0.5);
  385. border-radius: 25px;
  386. display: flex;
  387. align-items: center;
  388. }
  389. .gray-btn {
  390. color: #919191;
  391. border: 1px solid rgba(145, 145, 145, 0.5);
  392. }
  393. }
  394. .farm-name {
  395. font-size: 16px;
  396. color: #1D2129;
  397. }
  398. .temperature {
  399. .temperature-number {
  400. font-size: 40px;
  401. position: relative;
  402. margin-right: 14px;
  403. &::after {
  404. content: "°";
  405. font-size: 18px;
  406. position: absolute;
  407. right: -6px;
  408. top: 2px;
  409. }
  410. }
  411. .temperature-text {
  412. font-weight: 500;
  413. .temperature-text-time {
  414. font-size: 12px;
  415. font-weight: 400;
  416. }
  417. .temperature-text-more {
  418. font-size: 12px;
  419. color: #2199f8;
  420. margin-left: 10px;
  421. font-weight: 500;
  422. cursor: pointer;
  423. }
  424. }
  425. }
  426. }
  427. .header-right {
  428. width: 84px;
  429. height: 73px;
  430. }
  431. .weather-icon {
  432. i {
  433. font-size: 40px;
  434. color: #a7cffb;
  435. }
  436. }
  437. }
  438. .weather-chart-container {
  439. width: 100%;
  440. height: 100%;
  441. overflow: hidden;
  442. margin-top: 8px;
  443. .weather-chart-title {
  444. display: flex;
  445. justify-content: space-between;
  446. align-items: center;
  447. font-size: 12px;
  448. font-weight: 500;
  449. .weather-chart-title-more {
  450. color: #828282;
  451. cursor: pointer;
  452. padding: 3px 10px;
  453. border-radius: 25px;
  454. font-weight: 400;
  455. border: 1px solid rgba(130, 130, 130, 0.5);
  456. }
  457. }
  458. .weather-chart {
  459. margin-top: 5px;
  460. width: 100%;
  461. height: 180px;
  462. }
  463. }
  464. }
  465. </style>
  466. <style lang="scss">
  467. .select-garden-popper {
  468. max-height: calc(100vh - 200px);
  469. overflow-y: auto;
  470. &.el-dropdown__popper {
  471. .el-dropdown__list {
  472. max-width: 250px;
  473. }
  474. .el-dropdown-menu__item{
  475. background-color: transparent !important;
  476. color: #606266 !important;
  477. }
  478. .selected-active-garden {
  479. color: #2199f8 !important;
  480. background-color: rgba(33, 153, 248, 0.1) !important;
  481. font-weight: 500;
  482. }
  483. }
  484. .dropdown-default-text {
  485. font-size: 11px;
  486. color: #2199f8;
  487. margin-left: 8px;
  488. padding: 0 5px;
  489. background-color: rgba(33, 153, 248, 0.1);
  490. border-radius: 10px;
  491. font-weight: 400;
  492. }
  493. }
  494. </style>