weatherInfo.vue 18 KB

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