weatherChart.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. <template>
  2. <div ref="chartDom" class="chart"></div>
  3. </template>
  4. <script setup>
  5. import { onMounted, ref, watch } from "vue";
  6. import * as echarts from "echarts";
  7. const props = defineProps({
  8. weatherData: {
  9. type: Object,
  10. default: null,
  11. },
  12. });
  13. // 将接口数据转换为图表需要的格式
  14. function formatWeatherData(weatherData) {
  15. if (!weatherData || !weatherData.daily || !Array.isArray(weatherData.daily)) {
  16. return [];
  17. }
  18. const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
  19. return weatherData.daily.map((item) => {
  20. // 格式化日期:从 "2025-11-14" 转换为 "11/14"
  21. const date = new Date(item.fxDate);
  22. const month = String(date.getMonth() + 1).padStart(2, '0');
  23. const day = String(date.getDate()).padStart(2, '0');
  24. const rq = `${month}/${day}`;
  25. // 获取星期几
  26. const weekDay = weekDays[date.getDay()];
  27. // 根据 iconDay 和 iconNight 生成图片URL(这里需要根据实际API返回的icon值来映射)
  28. // 暂时使用默认图片,后续可以根据icon值映射到具体图片
  29. const getWeatherImg = (icon) => {
  30. // 可以根据icon值返回对应的图片URL
  31. // 例如:icon "100" -> 晴, "101" -> 多云, "104" -> 阴等
  32. // return `https://d.scggqx.com/forecast/img/${icon}.png`;
  33. return `https://birdseye-img.sysuimars.com/weather/${icon}.svg`;
  34. };
  35. return {
  36. rq: rq,
  37. weatherZgwd: item.tempMax,
  38. weatherZdwd: item.tempMin,
  39. weatherTqDay: item.textDay,
  40. weatherTqNight: item.textNight,
  41. weatherImgDay: getWeatherImg(item.iconDay),
  42. weatherImgNight: getWeatherImg(item.iconNight),
  43. windDirectDay: item.windDirDay,
  44. windDirectNight: item.windDirNight,
  45. windPowerDay: item.windScaleDay,
  46. windPowerNight: item.windScaleNight,
  47. week: weekDay,
  48. };
  49. });
  50. }
  51. // 处理天气数据
  52. function processWeatherData(data) {
  53. let weekday = [];
  54. let datelist = [];
  55. let weatherDaylist = [];
  56. let weatherNightlist = [];
  57. let weatherImgDaylist = [];
  58. let weatherImgNightlist = [];
  59. let windDirectlist = [];
  60. let windPowerlist = [];
  61. let weatherZgwdlist = [];
  62. let weatherZdwdlist = [];
  63. data.forEach((item) => {
  64. weekday.push(item.week);
  65. datelist.push(item.rq);
  66. weatherDaylist.push(item.weatherTqDay);
  67. weatherNightlist.push(item.weatherTqNight);
  68. weatherImgDaylist.push(item.weatherImgDay);
  69. weatherImgNightlist.push(item.weatherImgNight);
  70. windDirectlist.push(item.windDirectDay);
  71. windPowerlist.push(item.windPowerDay);
  72. weatherZgwdlist.push(item.weatherZgwd);
  73. weatherZdwdlist.push(item.weatherZdwd);
  74. });
  75. const maxWD = weatherZgwdlist.length > 0 ? Math.max(...weatherZgwdlist) + 30 : 60;
  76. const minWD = weatherZdwdlist.length > 0 ? Math.min(...weatherZdwdlist) - 30 : 0;
  77. return {
  78. weekday,
  79. datelist,
  80. weatherDaylist,
  81. weatherNightlist,
  82. weatherImgDaylist,
  83. weatherImgNightlist,
  84. windDirectlist,
  85. windPowerlist,
  86. weatherZgwdlist,
  87. weatherZdwdlist,
  88. maxWD,
  89. minWD
  90. };
  91. }
  92. // 生成天气图片样式
  93. function generateWeatherImgRich(weatherImgList) {
  94. const weatherImgListStyle = weatherImgList.map((item) => {
  95. return {
  96. backgroundColor: {
  97. image: item,
  98. },
  99. height: 20,
  100. width: 20,
  101. };
  102. });
  103. const rich = {};
  104. weatherImgListStyle.forEach((i, index) => {
  105. rich[index] = i;
  106. });
  107. return rich;
  108. }
  109. const myChart = ref(null);
  110. const chartDom = ref(null);
  111. // 初始化默认数据(用于首次渲染)
  112. const defaultData = formatWeatherData(null);
  113. const defaultProcessed = processWeatherData(defaultData.length > 0 ? defaultData : []);
  114. const defaultWeatherImgDaylistRich = generateWeatherImgRich(defaultProcessed.weatherImgDaylist);
  115. const defaultWeatherImgNightlistRich = generateWeatherImgRich(defaultProcessed.weatherImgNightlist);
  116. // 生成图表配置
  117. function generateOptions(processedData, weatherImgDaylistRich, weatherImgNightlistRich) {
  118. return {
  119. grid: {
  120. top: "-100px",
  121. right: "20px",
  122. bottom: '-55px',
  123. left: "-20px",
  124. containLabel: true,
  125. },
  126. xAxis: [
  127. {
  128. type: "category",
  129. boundaryGap: false,
  130. position: "top",
  131. offset: 0,
  132. zlevel: 100,
  133. axisLine: {
  134. show: false,
  135. },
  136. axisTick: {
  137. show: false,
  138. },
  139. splitLine: {
  140. show: false,
  141. },
  142. axisLabel: {
  143. interval: 0,
  144. formatter: ["{a|{value}}"].join("\n"),
  145. rich: {
  146. a: {
  147. color: "#000",
  148. },
  149. },
  150. },
  151. data: processedData.weekday,
  152. },
  153. {
  154. type: "category",
  155. position: "top",
  156. offset: -15,
  157. boundaryGap: false,
  158. zlevel: 100,
  159. axisLine: {
  160. show: false,
  161. },
  162. axisTick: {
  163. show: false,
  164. },
  165. axisLabel: {
  166. interval: 0,
  167. formatter: ["{a|{value}}"].join("\n"),
  168. rich: {
  169. a: {
  170. color: "#999999",
  171. fontSize: 10,
  172. },
  173. },
  174. },
  175. data: processedData.datelist,
  176. axisPointer: {
  177. show: false,
  178. },
  179. },
  180. {
  181. type: "category",
  182. position: "top",
  183. offset: -35,
  184. boundaryGap: false,
  185. zlevel: 100,
  186. axisLine: {
  187. show: false,
  188. },
  189. axisTick: {
  190. show: false,
  191. },
  192. axisLabel: {
  193. interval: 0,
  194. formatter: ["{a|{value}}"].join("\n"),
  195. rich: {
  196. a: {
  197. color: "#000",
  198. },
  199. },
  200. },
  201. data: processedData.weatherDaylist,
  202. axisPointer: {
  203. show: false,
  204. },
  205. },
  206. {
  207. type: "category",
  208. boundaryGap: false,
  209. position: "top",
  210. offset: -60,
  211. zlevel: 100,
  212. axisLine: {
  213. show: false,
  214. },
  215. axisTick: {
  216. show: false,
  217. },
  218. axisLabel: {
  219. interval: 0,
  220. formatter: ["{{value}|}"].join("\n"),
  221. rich: weatherImgDaylistRich,
  222. },
  223. data: processedData.weatherImgDaylist.map((_, index) => String(index)),
  224. axisPointer: {
  225. show: false,
  226. },
  227. },
  228. {
  229. type: "category",
  230. position: "top",
  231. offset: -80,
  232. boundaryGap: false,
  233. zlevel: 100,
  234. axisLine: {
  235. show: false,
  236. },
  237. axisTick: {
  238. show: false,
  239. },
  240. axisLabel: {
  241. interval: 0,
  242. formatter: ["{a|{value}°}"].join("\n"),
  243. rich: {
  244. a: {
  245. color: "#000",
  246. },
  247. },
  248. },
  249. data: processedData.weatherZgwdlist,
  250. axisPointer: {
  251. show: false,
  252. },
  253. },
  254. {
  255. type: "category",
  256. position: "top",
  257. offset: -160,
  258. boundaryGap: false,
  259. zlevel: 100,
  260. axisLine: {
  261. show: false,
  262. },
  263. axisTick: {
  264. show: false,
  265. },
  266. axisLabel: {
  267. interval: 0,
  268. formatter: ["{a|{value}°}"].join("\n"),
  269. rich: {
  270. a: {
  271. color: "#000",
  272. },
  273. },
  274. },
  275. data: processedData.weatherZdwdlist,
  276. axisPointer: {
  277. show: false,
  278. },
  279. },
  280. ],
  281. yAxis: {
  282. show: false,
  283. interval: 15,
  284. type: "value",
  285. axisLabel: {
  286. formatter: "{value}" + "℃",
  287. },
  288. max: processedData.maxWD,
  289. min: processedData.minWD,
  290. },
  291. series: [
  292. {
  293. name: "最高温度",
  294. type: "line",
  295. data: processedData.weatherZgwdlist,
  296. symbol: "circle",
  297. symbolSize: 3,
  298. showSymbol: true,
  299. smooth: true,
  300. itemStyle: {
  301. color: "#FA8258", // 设置圆形的填充色
  302. borderColor: "#FA8258", // 设置边框颜色
  303. borderWidth: 3, // 设置边框宽度
  304. },
  305. lineStyle: {
  306. width: 2, // 设置折线的宽度
  307. color: "#FA8258", // 设置折线的颜色
  308. shadowColor: "#FA8258", // 线的阴影颜色
  309. shadowBlur: 5, // 线的阴影模糊级数
  310. shadowOffsetX: 0, // 线的阴影水平偏移
  311. shadowOffsetY: 5, // 线的阴影垂直偏移
  312. },
  313. silent: false,
  314. },
  315. {
  316. name: "最低温度",
  317. type: "line",
  318. data: processedData.weatherZdwdlist,
  319. symbol: "circle",
  320. symbolSize: 3,
  321. showSymbol: true,
  322. smooth: true,
  323. itemStyle: {
  324. color: "#2E9AFE", // 设置圆形的填充色
  325. borderColor: "#2E9AFE", // 设置边框颜色
  326. borderWidth: 3, // 设置边框宽度
  327. },
  328. lineStyle: {
  329. width: 2, // 设置折线的宽度
  330. color: "#2E9AFE", // 设置折线的颜色
  331. shadowColor: "#2E9AFE", // 线的阴影颜色
  332. shadowBlur: 5, // 线的阴影模糊级数
  333. shadowOffsetX: 0, // 线的阴影水平偏移
  334. shadowOffsetY: 5, // 线的阴影垂直偏移
  335. },
  336. silent: false,
  337. },
  338. ],
  339. };
  340. }
  341. const options = ref(generateOptions(defaultProcessed, defaultWeatherImgDaylistRich, defaultWeatherImgNightlistRich));
  342. onMounted(() => {
  343. setTimeout(() => {
  344. initChart();
  345. }, 200);
  346. });
  347. // 监听 weatherData 变化,更新图表
  348. watch(
  349. () => props.weatherData,
  350. (newValue) => {
  351. if (newValue && newValue.daily) {
  352. const formattedData = formatWeatherData(newValue);
  353. const processedData = processWeatherData(formattedData);
  354. const weatherImgDaylistRich = generateWeatherImgRich(processedData.weatherImgDaylist);
  355. const weatherImgNightlistRich = generateWeatherImgRich(processedData.weatherImgNightlist);
  356. options.value = generateOptions(processedData, weatherImgDaylistRich, weatherImgNightlistRich);
  357. if (myChart.value) {
  358. myChart.value.setOption(options.value);
  359. }
  360. }
  361. },
  362. { deep: true }
  363. );
  364. const initChart = () => {
  365. myChart.value = echarts.init(chartDom.value);
  366. myChart.value.setOption(options.value);
  367. };
  368. </script>
  369. <style lang="scss" scoped>
  370. .chart {
  371. width: 100%;
  372. height: 100%;
  373. }
  374. </style>