web-component-gallery 2.3.12 → 2.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,97 +1,273 @@
1
1
  <template>
2
- <div class="weather">
3
- <slot name="weatherIcon" :weatherInfo="weatherInfo">
4
- <!-- <IconFont scriptUrl="weatherGallery" :type="weatherType" /> -->
5
- </slot>
6
- <span class="weather__Info">{{ weatherInfo.weather || '--' }}</span>
7
- <span class="weather__Temp">温度{{ weatherInfo.temperature || '--' }}°</span>
2
+ <div class="weather" :class="layoutMode">
3
+ <!-- 紧凑模式 -->
4
+ <template v-if="mode === 'compact'">
5
+ <span class="weather__icon">
6
+ <WeatherIcon
7
+ :iconCode="weatherIconCode"
8
+ :slotData="{ weatherType: weatherIconCode }"
9
+ >
10
+ <template #default="{ iconCode }">
11
+ <IconFont scriptUrl="weatherGallery" :type="`W${iconCode}`" />
12
+ </template>
13
+ </WeatherIcon>
14
+ </span>
15
+ <span class="weather__text">{{ condition.condition }}</span>
16
+ <span class="weather__temp">温度 {{ condition.temp }} °</span>
17
+ </template>
18
+
19
+ <!-- 卡片模式 -->
20
+ <template v-else>
21
+ <!-- 城市信息和日夜标识 -->
22
+ <WeatherHeader
23
+ :city="city.name"
24
+ :dayOrNight="dayOrNight"
25
+ />
26
+
27
+ <div class="weather__container">
28
+ <!-- 温度和天气状况 -->
29
+ <WeatherMain
30
+ :temp="condition.temp"
31
+ :conditionText="condition.condition"
32
+ :iconCode="weatherIconCode"
33
+ />
34
+ <!-- 详情数据网格 -->
35
+ <WeatherDetailGrid
36
+ :items="detailItems"
37
+ :windData="windData"
38
+ />
39
+ </div>
40
+ </template>
8
41
  </div>
9
42
  </template>
10
43
 
11
44
  <script>
12
- const WEATHER_TYPES = {
13
- '小雨,毛毛雨/细雨,雨,小雨-中雨': 'lightRain',
14
- '中雨,大雨,中雨-大雨,大雨-暴雨,暴雨,大暴雨,特大暴雨,极端降雨,暴雨-大暴雨,大暴雨-特大暴雨': 'heavyRain',
15
- '阵雨,强阵雨': 'rainShower',
16
- '冻雨': 'iceRain',
17
- '雷阵雨,强雷阵雨': 'thunderShower',
18
- '少云,多云,阴,冷': 'overcast',
19
- '雪,小雪,中雪,大雪,小雪-大雪,中雪-大雪,暴雪,大雪-暴雪': 'lightSnow',
20
- '阵雪': 'showerySnow',
21
- '晴,热': 'clear',
22
- '雨雪天气,雨夹雪,阵雨夹雪,雷阵雨并伴有冰雹': 'sleet',
23
- '霾,中度霾,雾,轻雾': 'lightFog',
24
- '重度霾,严重霾,浓雾,强浓雾,大雾,特强浓雾,未知': 'heavyFog',
25
- '晴间多云': 'cloudyToSunny',
26
- '有风,平静,微风,和风,清风,强风/劲风,疾风,大风,烈风,风暴,狂爆风,飓风,热带风暴,龙卷风,浮尘,扬沙,沙尘暴,强沙尘暴': 'gale'
27
- }
28
-
29
45
  import IconFont from '../icon-font'
30
46
 
47
+ // 详情项配置常量(防止意外修改)
48
+ const DETAIL_ITEMS_CONFIG = Object.freeze([
49
+ { name: '风向', icon: '💨', code: 'windDegrees', unit: '°' },
50
+ { name: '风速', icon: '🌪️', code: 'windSpeed', unit: 'm/s' },
51
+ { name: '降雨', icon: '🌧️', code: 'precipitation', unit: 'mm/h' },
52
+ { name: '湿度', icon: '💧', code: 'humidity', unit: '%' }
53
+ ])
54
+
55
+ // 默认天气数据结构
56
+ const DEFAULT_WEATHER_DATA = Object.freeze({
57
+ condition: {
58
+ condition: '--',
59
+ temp: '--',
60
+ windDir: '',
61
+ windLevel: '',
62
+ windDegrees: 0,
63
+ windSpeed: 0,
64
+ precipitation: 0,
65
+ humidity: 0,
66
+ icon: 0
67
+ },
68
+ city: { name: '--' }
69
+ })
70
+
31
71
  export default {
32
72
  name: 'Weather',
73
+ components: {
74
+ IconFont,
75
+ // 功能型子组件:天气图标容器
76
+ WeatherIcon: {
77
+ functional: true,
78
+ props: ['iconCode', 'slotData'],
79
+ render(h, ctx) {
80
+ const slotContent = ctx.parent.$scopedSlots.weatherIcon?.(ctx.props.slotData)
81
+ return h('div', {}, [
82
+ slotContent || ctx.slots().default
83
+ ])
84
+ }
85
+ },
86
+ // 功能型子组件:城市头部
87
+ WeatherHeader: {
88
+ functional: true,
89
+ props: ['city', 'dayOrNight'],
90
+ render(h, ctx) {
91
+ return h('div', { class: 'weather__header' }, [
92
+ h('span', { class: 'weather__city-name' }, ctx.props.city),
93
+ h('span', { class: 'weather__day-night' }, ctx.props.dayOrNight)
94
+ ])
95
+ }
96
+ },
97
+ // 功能型子组件:主区域
98
+ WeatherMain: {
99
+ functional: true,
100
+ props: ['temp', 'conditionText', 'iconCode'],
101
+ render(h, ctx) {
102
+ return h('div', { class: 'weather__main' }, [
103
+ h('div', { class: 'weather__temp-box' }, [
104
+ h('span', { class: 'weather__temp-value' }, ctx.props.temp),
105
+ h('span', { class: 'weather__temp-unit' }, ' °C')
106
+ ]),
107
+ h('div', { class: 'weather__condition-box' }, [
108
+ h('span', { class: 'weather__condition-text' }, ctx.props.conditionText),
109
+ h(IconFont, {
110
+ props: { scriptUrl: 'weatherGallery', type: `W${ctx.props.iconCode}` },
111
+ class: { 'weather__icon-font': true }
112
+ })
113
+ ])
114
+ ])
115
+ }
116
+ },
117
+ // 功能型子组件:详情网格
118
+ WeatherDetailGrid: {
119
+ functional: true,
120
+ props: ['items', 'windData'],
121
+ render(h, ctx) {
122
+ return h('div', { class: 'weather__detail-grid' },
123
+ ctx.props.items.map((item, index) =>
124
+ h('div', { key: index, class: 'weather__detail-item' }, [
125
+ h('span', { class: 'weather__detail-icon' }, item.icon),
126
+ h('div', { class: 'weather__detail-info' }, [
127
+ h('span', { class: 'weather__detail-label' }, item.name),
128
+ h('span', { class: 'weather__detail-value' },
129
+ index === 0
130
+ ? [h('span', { class: 'weather__wind-value' }, `${ctx.props.windData.degrees}°`), ` ${ctx.props.windData.direction} ${ctx.props.windData.level}级`]
131
+ : `${item.value} ${item.unit}`
132
+ )
133
+ ])
134
+ ])
135
+ )
136
+ )
137
+ }
138
+ }
139
+ },
33
140
  props: {
34
- /** 当前城市高德code编码 */
35
- adCode: {
141
+ // 显示模式:'compact'(紧凑) | 'card'(卡片)
142
+ mode: {
36
143
  type: String,
37
- default: '330802'
144
+ default: 'card',
145
+ validator: (val) => ['compact', 'card'].includes(val)
38
146
  },
39
- AMapServiceKey: {
147
+ // 城市 ID(用于查询天气数据)
148
+ cityId: {
40
149
  type: String,
41
- default: '76aa516e74e87a1b9d169e796f2bbdc3'
150
+ default: '1262'
42
151
  }
43
152
  },
44
- components: {
45
- IconFont
46
- },
47
153
  data() {
48
154
  return {
49
- weatherInfo: {}
155
+ weatherData: DEFAULT_WEATHER_DATA,
156
+ detailItems: [],
157
+ loading: false // 加载状态控制
50
158
  }
51
159
  },
52
160
  computed: {
53
- weatherType() {
54
- if (!this.weatherInfo.weather) return 'clear'
55
-
56
- const matchedKey = Object.keys(WEATHER_TYPES).find(key =>
57
- key.includes(this.weatherInfo.weather)
58
- )
59
- return WEATHER_TYPES[matchedKey] || 'clear'
161
+ // 提取 condition 对象,提供默认值保护
162
+ condition() {
163
+ return this.weatherData.condition || DEFAULT_WEATHER_DATA.condition
164
+ },
165
+
166
+ // 提取 city 对象,提供默认值保护
167
+ city() {
168
+ return this.weatherData.city || DEFAULT_WEATHER_DATA.city
169
+ },
170
+
171
+ // 根据当前小时判断白天/夜间(6-18 点为白天)
172
+ dayOrNight() {
173
+ const hour = new Date().getHours()
174
+ return hour >= 6 && hour < 18 ? '白天' : '夜间'
175
+ },
176
+
177
+ // 天气图标编码(来自 API 返回)
178
+ weatherIconCode() {
179
+ return this.condition.icon || 0
180
+ },
181
+
182
+ // 布局模式类名(用于 CSS 样式切换)
183
+ layoutMode() {
184
+ return `weather--${this.mode}`
185
+ },
186
+
187
+ // 风力数据聚合(角度、方向、等级)
188
+ windData() {
189
+ return {
190
+ degrees: this.condition.windDegrees || 0,
191
+ direction: this.condition.windDir || '无持续风向',
192
+ level: this.condition.windLevel || 0
193
+ }
60
194
  }
61
195
  },
196
+ created() {
197
+ // 初始化详情项数据
198
+ this.detailItems = DETAIL_ITEMS_CONFIG.map(item => ({
199
+ ...item,
200
+ value: '0'
201
+ }))
202
+ },
62
203
  mounted() {
63
204
  this.fetchWeatherData()
64
205
  },
65
206
  watch: {
66
- adCode() {
67
- this.fetchWeatherData()
207
+ // 监听城市变化,自动重新获取数据
208
+ cityId: {
209
+ handler() {
210
+ this.fetchWeatherData()
211
+ },
212
+ immediate: false
68
213
  }
69
214
  },
70
215
  methods: {
216
+ // 获取天气数据(并发请求)
71
217
  async fetchWeatherData() {
72
- if (!this.adCode || !this.AMapServiceKey) return
218
+ if (!this.cityId) return
219
+
220
+ this.loading = true
73
221
 
74
222
  try {
75
- const response = await fetch(
76
- `https://restapi.amap.com/v3/weather/weatherInfo?key=${this.AMapServiceKey}&city=${this.adCode}`,
77
- {
78
- method: 'GET',
79
- headers: {
80
- 'Content-Type': 'application/json'
81
- }
82
- }
83
- )
223
+ await Promise.all([
224
+ this.fetchConditionData(), // 实时天气
225
+ this.fetchForecastData() // 24 小时预报
226
+ ])
84
227
 
85
- const { infocode, lives } = await response.json()
86
-
87
- if (infocode === '10000') {
88
- this.weatherInfo = { ...lives[0] }
89
- return
90
- }
91
- console.error('天气数据解析失败:', err)
228
+ this.$emit('update', this.weatherData)
92
229
  } catch (err) {
93
230
  console.error('获取天气数据失败:', err)
231
+ this.$emit('error', err)
232
+ } finally {
233
+ this.loading = false
234
+ }
235
+ },
236
+
237
+ // 获取实时天气数据
238
+ async fetchConditionData() {
239
+ const res = await this.$request.get('/weather-service-center/weatherService/weather/conditionV2', {
240
+ params: { cityId: this.cityId }
241
+ })
242
+
243
+ if (res.data) {
244
+ this.weatherData = res.data
245
+ this.updateDetailItems()
246
+ }
247
+ },
248
+
249
+ // 获取 24 小时预报(用于降雨量数据)
250
+ async fetchForecastData() {
251
+ const res = await this.$request.get('/weather-service-center/weatherService/weather/forecast24hoursV2', {
252
+ params: { cityId: this.cityId }
253
+ })
254
+
255
+ const precipitation = res?.data?.hourly?.[0]?.qpf
256
+ if (precipitation !== undefined) {
257
+ this.detailItems[2].value = precipitation
94
258
  }
259
+ },
260
+
261
+ // 更新详情项数值(从 API 数据同步)
262
+ updateDetailItems() {
263
+ this.detailItems.forEach(item => {
264
+ item.value = this.condition[item.code] || 0
265
+ })
266
+ },
267
+
268
+ // 手动刷新天气数据
269
+ refresh() {
270
+ this.fetchWeatherData()
95
271
  }
96
272
  }
97
273
  }
@@ -3,9 +3,212 @@
3
3
  @import '../../style/mixins.less';
4
4
 
5
5
  .weather {
6
- font-size: @font-size-lg;
7
- align-items: center;
8
- .flex-layout(@flexGap: 0 @padding-xs);
6
+ &--compact {
7
+ display: flex;
8
+ align-items: center;
9
+ gap: 0px 8px;
10
+ }
11
+
12
+ &--card {
13
+ padding: @padding-md;
14
+ box-sizing: border-box;
15
+ .square(100%);
16
+ .flex-layout(column, 24px 0);
17
+ }
9
18
 
10
- i { font-size: @avatar-size-sm; }
11
- }
19
+ // ====== 紧凑模式 ======
20
+ &__text {
21
+ color: @text-color;
22
+ font-size: @font-size-base;
23
+ }
24
+
25
+ &__temp {
26
+ color: @primary-color;
27
+ font-size: @font-size-lg;
28
+ }
29
+
30
+ // ====== 卡片模式 ======
31
+
32
+ &__header {
33
+ align-items: center;
34
+ .flex-layout(@flexGap: 0 12px);
35
+ }
36
+
37
+ &__city-name {
38
+ font-size: @avatar-size-sm;
39
+ }
40
+
41
+ &__container {
42
+ flex: 1;
43
+ justify-content: space-between;
44
+ .flex-layout();
45
+ }
46
+
47
+ &__main {
48
+ flex: 1;
49
+ justify-content: center;
50
+ .flex-layout(column, 16px 0);
51
+ }
52
+
53
+ &__temp-box {
54
+ font-size: @avatar-size-base;
55
+ }
56
+
57
+ &__detail-grid {
58
+ width: 280px;
59
+ flex-wrap: wrap;
60
+ .flex-layout(@flexGap: 12px);
61
+ }
62
+
63
+ &__detail-item {
64
+ width: calc(50% - 6px);
65
+ align-items: center;
66
+ .flex-layout(@flexGap: 0 12px);
67
+ }
68
+
69
+ &__detail-icon {
70
+ font-size: @font-size-lg;
71
+ }
72
+
73
+ &__detail-info {
74
+ .flex-layout(column, 8px 0px);
75
+ }
76
+
77
+ &__condition-box {
78
+ align-items: center;
79
+ font-size: @font-size-lg;
80
+ padding-left: 4px; // 与温度保持左对齐
81
+ box-sizing: border-box;
82
+ .flex-layout(@flexGap: 0 12px);
83
+ }
84
+
85
+ &__icon-font {
86
+ font-size: @avatar-size-base;
87
+ }
88
+ }
89
+
90
+ // .weather-card {
91
+ // .square(100%);
92
+ // .flex-layout(column);
93
+
94
+ // &__content {
95
+ // flex: 1;
96
+ // padding: @padding-md;
97
+ // box-sizing: border-box;
98
+ // .flex-layout(@flexGap: @padding-md);
99
+ // }
100
+
101
+ // &__left {
102
+ // width: 30%;
103
+ // min-width: 120px;
104
+ // .flex-mixins(column, @gap: @margin-xs);
105
+ // }
106
+
107
+ // &__header {
108
+ // align-items: center;
109
+ // .flex-layout(@flexGap: @margin-sm);
110
+
111
+ // .city-name {
112
+ // color: @heading-color;
113
+ // font-size: @font-size-lg;
114
+ // font-weight: @typography-title-font-weight;
115
+ // }
116
+
117
+ // .day-night {
118
+ // font-size: @font-size-sm;
119
+ // color: @text-color-secondary;
120
+ // background: fade(@text-color, 8%);
121
+ // padding: 2px @padding-xs;
122
+ // border-radius: @border-radius-lg;
123
+ // }
124
+ // }
125
+
126
+ // &__main {
127
+ // display: flex;
128
+ // flex-direction: column;
129
+ // align-items: center;
130
+ // gap: @margin-sm;
131
+
132
+ // .temp-box {
133
+ // display: flex;
134
+ // align-items: baseline;
135
+ // gap: 2px;
136
+
137
+ // .temp-value {
138
+ // font-size: 36px;
139
+ // font-weight: 300;
140
+ // color: @heading-color;
141
+ // line-height: @line-height-base;
142
+ // }
143
+
144
+ // .temp-unit {
145
+ // font-size: @font-size-lg;
146
+ // color: @text-color-secondary;
147
+ // }
148
+ // }
149
+
150
+ // .condition-box {
151
+ // display: flex;
152
+ // align-items: center;
153
+ // gap: 6px;
154
+
155
+ // .weather-condition {
156
+ // font-size: @font-size-base;
157
+ // color: @text-color;
158
+ // }
159
+
160
+ // .weather-icon {
161
+ // font-size: 48px;
162
+ // line-height: @line-height-base;
163
+ // }
164
+ // }
165
+ // }
166
+
167
+ // &__detail-grid {
168
+ // flex: 1;
169
+ // display: flex;
170
+ // flex-wrap: wrap;
171
+ // gap: @padding-md @margin-sm;
172
+ // align-content: center;
173
+ // }
174
+
175
+ // &__detail-item {
176
+ // display: flex;
177
+ // align-items: center;
178
+ // gap: @margin-sm;
179
+ // white-space: nowrap;
180
+
181
+ // &:nth-child(odd) {
182
+ // width: calc(65% - 6px);
183
+ // }
184
+
185
+ // &:nth-child(even) {
186
+ // width: calc(35% - 6px);
187
+ // }
188
+
189
+ // .icon-svg {
190
+ // font-size: 20px;
191
+ // line-height: @line-height-base;
192
+ // }
193
+
194
+ // .detail-info {
195
+ // .flex-layout(column, 4px);
196
+
197
+ // .detail-label {
198
+ // font-size: @font-size-sm;
199
+ // color: @text-color-secondary;
200
+ // }
201
+
202
+ // .detail-value {
203
+ // font-size: @font-size-base;
204
+ // font-weight: @btn-font-weight;
205
+ // color: @heading-color;
206
+
207
+ // .wind-value {
208
+ // display: inline-block;
209
+ // margin-right: 4px;
210
+ // }
211
+ // }
212
+ // }
213
+ // }
214
+ // }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-component-gallery",
3
- "version": "2.3.12",
3
+ "version": "2.3.14",
4
4
  "description": "基础vue、antdvue、less实现的私有组件库",
5
5
  "main": "dist/index.umd.js",
6
6
  "files": [