py-test-components 1.0.0
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.
- package/README.md +214 -0
- package/dist/313f7dacf2076822059d.woff +0 -0
- package/dist/4520188144a17fb24a6a.ttf +0 -0
- package/dist/py-component.esm.js +2 -0
- package/dist/py-component.esm.js.LICENSE.txt +35 -0
- package/dist/py-component.js +2 -0
- package/dist/py-component.js.LICENSE.txt +35 -0
- package/package.json +42 -0
- package/src/components/PyTable.vue +280 -0
- package/src/components/PyWeather.vue +387 -0
- package/src/index.js +51 -0
- package/src/react/index.js +156 -0
- package/src/store/index.js +66 -0
- package/src/utils/api.js +101 -0
- package/src/utils/request.js +113 -0
- package/src/vue/index.js +32 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="py-weather">
|
|
3
|
+
<el-card class="weather-card" :body-style="{ padding: '0px' }">
|
|
4
|
+
<!-- 加载状态 -->
|
|
5
|
+
<div v-if="loading" class="loading-wrapper">
|
|
6
|
+
<i class="el-icon-loading"></i>
|
|
7
|
+
<span>加载天气数据...</span>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<!-- 错误状态 -->
|
|
11
|
+
<div v-else-if="error" class="error-wrapper">
|
|
12
|
+
<i class="el-icon-warning-outline"></i>
|
|
13
|
+
<span>{{ error }}</span>
|
|
14
|
+
<el-button type="text" @click="loadWeather">重试</el-button>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<!-- 天气内容 -->
|
|
18
|
+
<div v-else class="weather-content">
|
|
19
|
+
<!-- 头部:城市和日期 -->
|
|
20
|
+
<div class="weather-header">
|
|
21
|
+
<div class="location">
|
|
22
|
+
<i class="el-icon-location"></i>
|
|
23
|
+
<span class="city">{{ displayCity }}</span>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="date">{{ currentDate }}</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<!-- 主体:温度和天气 -->
|
|
29
|
+
<div class="weather-main">
|
|
30
|
+
<div class="temperature">
|
|
31
|
+
<span class="temp-value">{{ weatherData.temperature }}</span>
|
|
32
|
+
<span class="temp-unit">°C</span>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="weather-desc">
|
|
35
|
+
<i :class="weatherIcon"></i>
|
|
36
|
+
<span>{{ weatherData.description }}</span>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- 详情:湿度、风速等 -->
|
|
41
|
+
<div class="weather-details">
|
|
42
|
+
<div class="detail-item">
|
|
43
|
+
<i class="el-icon-moisture"></i>
|
|
44
|
+
<span class="label">湿度</span>
|
|
45
|
+
<span class="value">{{ weatherData.humidity }}%</span>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="detail-item">
|
|
48
|
+
<i class="el-icon-wind-power"></i>
|
|
49
|
+
<span class="label">风速</span>
|
|
50
|
+
<span class="value">{{ weatherData.windSpeed }} km/h</span>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="detail-item">
|
|
53
|
+
<i class="el-icon-view"></i>
|
|
54
|
+
<span class="label">能见度</span>
|
|
55
|
+
<span class="value">{{ weatherData.visibility }} km</span>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<!-- 预报 -->
|
|
60
|
+
<div v-if="config.showForecast && forecast.length > 0" class="weather-forecast">
|
|
61
|
+
<div class="forecast-title">未来预报</div>
|
|
62
|
+
<div class="forecast-list">
|
|
63
|
+
<div
|
|
64
|
+
v-for="(item, index) in forecast"
|
|
65
|
+
:key="index"
|
|
66
|
+
class="forecast-item"
|
|
67
|
+
>
|
|
68
|
+
<span class="day">{{ item.day }}</span>
|
|
69
|
+
<i :class="getWeatherIcon(item.weather)"></i>
|
|
70
|
+
<span class="temp">{{ item.low }}° - {{ item.high }}°</span>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</el-card>
|
|
76
|
+
</div>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
79
|
+
<script>
|
|
80
|
+
import store from '../store';
|
|
81
|
+
import { weatherApi } from '../utils/api';
|
|
82
|
+
|
|
83
|
+
export default {
|
|
84
|
+
name: 'PyWeather',
|
|
85
|
+
|
|
86
|
+
props: {
|
|
87
|
+
propData: {
|
|
88
|
+
default: null
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
data() {
|
|
93
|
+
return {
|
|
94
|
+
loading: false,
|
|
95
|
+
error: '',
|
|
96
|
+
weatherData: {
|
|
97
|
+
temperature: '--',
|
|
98
|
+
description: '--',
|
|
99
|
+
humidity: '--',
|
|
100
|
+
windSpeed: '--',
|
|
101
|
+
visibility: '--'
|
|
102
|
+
},
|
|
103
|
+
forecast: []
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
computed: {
|
|
108
|
+
config() {
|
|
109
|
+
return this.propData || {};
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
displayCity() {
|
|
113
|
+
return this.config.city || this.weatherData.city || '北京';
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
currentDate() {
|
|
117
|
+
const date = new Date();
|
|
118
|
+
const options = {
|
|
119
|
+
year: 'numeric',
|
|
120
|
+
month: 'long',
|
|
121
|
+
day: 'numeric',
|
|
122
|
+
weekday: 'long'
|
|
123
|
+
};
|
|
124
|
+
return date.toLocaleDateString('zh-CN', options);
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
weatherIcon() {
|
|
128
|
+
const iconMap = {
|
|
129
|
+
'晴': 'el-icon-sunny',
|
|
130
|
+
'多云': 'el-icon-cloudy-and-sunny',
|
|
131
|
+
'阴': 'el-icon-cloudy',
|
|
132
|
+
'雨': 'el-icon-heavy-rain',
|
|
133
|
+
'雪': 'el-icon-light-snow',
|
|
134
|
+
'雾': 'el-icon-foggy',
|
|
135
|
+
'霾': 'el-icon-foggy'
|
|
136
|
+
};
|
|
137
|
+
return iconMap[this.weatherData.description] || 'el-icon-sunny';
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
mounted() {
|
|
142
|
+
this.loadWeather();
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
watch: {
|
|
146
|
+
'config.city': {
|
|
147
|
+
handler() {
|
|
148
|
+
this.loadWeather();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
methods: {
|
|
154
|
+
// 加载天气数据
|
|
155
|
+
async loadWeather() {
|
|
156
|
+
// 如果有传入的模拟数据,直接使用
|
|
157
|
+
if (this.config.mockData) {
|
|
158
|
+
this.weatherData = { ...this.config.mockData };
|
|
159
|
+
if (this.config.forecast) {
|
|
160
|
+
this.forecast = this.config.forecast;
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
this.loading = true;
|
|
167
|
+
this.error = '';
|
|
168
|
+
|
|
169
|
+
const city = this.config.city || '北京';
|
|
170
|
+
|
|
171
|
+
// 获取当前天气
|
|
172
|
+
const currentRes = await weatherApi.getWeather(city);
|
|
173
|
+
this.weatherData = {
|
|
174
|
+
city: city,
|
|
175
|
+
temperature: currentRes.temperature || 25,
|
|
176
|
+
description: currentRes.weather || '晴',
|
|
177
|
+
humidity: currentRes.humidity || 60,
|
|
178
|
+
windSpeed: currentRes.windSpeed || 10,
|
|
179
|
+
visibility: currentRes.visibility || 10,
|
|
180
|
+
...currentRes
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// 获取预报
|
|
184
|
+
if (this.config.showForecast) {
|
|
185
|
+
const forecastRes = await weatherApi.getForecast(city);
|
|
186
|
+
this.forecast = forecastRes.data || this.getDefaultForecast();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// 触发加载完成事件
|
|
190
|
+
this.$emit('load', this.weatherData);
|
|
191
|
+
} catch (err) {
|
|
192
|
+
console.error('[PyWeather] 加载天气失败:', err);
|
|
193
|
+
this.error = '获取天气数据失败';
|
|
194
|
+
this.$emit('error', err);
|
|
195
|
+
} finally {
|
|
196
|
+
this.loading = false;
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
// 获取默认预报数据
|
|
201
|
+
getDefaultForecast() {
|
|
202
|
+
const days = ['明天', '后天', '周三', '周四', '周五'];
|
|
203
|
+
return days.map(day => ({
|
|
204
|
+
day,
|
|
205
|
+
weather: '晴',
|
|
206
|
+
high: 28,
|
|
207
|
+
low: 18
|
|
208
|
+
}));
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
// 获取天气图标
|
|
212
|
+
getWeatherIcon(weather) {
|
|
213
|
+
const iconMap = {
|
|
214
|
+
'晴': 'el-icon-sunny',
|
|
215
|
+
'多云': 'el-icon-cloudy-and-sunny',
|
|
216
|
+
'阴': 'el-icon-cloudy',
|
|
217
|
+
'雨': 'el-icon-heavy-rain',
|
|
218
|
+
'雪': 'el-icon-light-snow'
|
|
219
|
+
};
|
|
220
|
+
return iconMap[weather] || 'el-icon-sunny';
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
</script>
|
|
225
|
+
|
|
226
|
+
<style scoped>
|
|
227
|
+
.py-weather {
|
|
228
|
+
width: 100%;
|
|
229
|
+
max-width: 400px;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.weather-card {
|
|
233
|
+
border-radius: 12px;
|
|
234
|
+
overflow: hidden;
|
|
235
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.loading-wrapper,
|
|
239
|
+
.error-wrapper {
|
|
240
|
+
padding: 40px;
|
|
241
|
+
text-align: center;
|
|
242
|
+
color: #909399;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.error-wrapper i {
|
|
246
|
+
font-size: 48px;
|
|
247
|
+
color: #F56C6C;
|
|
248
|
+
display: block;
|
|
249
|
+
margin-bottom: 16px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.loading-wrapper i {
|
|
253
|
+
font-size: 32px;
|
|
254
|
+
margin-right: 8px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.weather-content {
|
|
258
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
259
|
+
color: white;
|
|
260
|
+
padding: 24px;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.weather-header {
|
|
264
|
+
display: flex;
|
|
265
|
+
justify-content: space-between;
|
|
266
|
+
align-items: center;
|
|
267
|
+
margin-bottom: 24px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.location {
|
|
271
|
+
display: flex;
|
|
272
|
+
align-items: center;
|
|
273
|
+
font-size: 18px;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.location i {
|
|
277
|
+
margin-right: 8px;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.city {
|
|
281
|
+
font-weight: 500;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.date {
|
|
285
|
+
font-size: 14px;
|
|
286
|
+
opacity: 0.9;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.weather-main {
|
|
290
|
+
text-align: center;
|
|
291
|
+
margin-bottom: 24px;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.temperature {
|
|
295
|
+
display: flex;
|
|
296
|
+
align-items: flex-start;
|
|
297
|
+
justify-content: center;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.temp-value {
|
|
301
|
+
font-size: 72px;
|
|
302
|
+
font-weight: 300;
|
|
303
|
+
line-height: 1;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.temp-unit {
|
|
307
|
+
font-size: 24px;
|
|
308
|
+
margin-top: 8px;
|
|
309
|
+
opacity: 0.8;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.weather-desc {
|
|
313
|
+
margin-top: 12px;
|
|
314
|
+
font-size: 18px;
|
|
315
|
+
opacity: 0.9;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.weather-desc i {
|
|
319
|
+
font-size: 24px;
|
|
320
|
+
margin-right: 8px;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.weather-details {
|
|
324
|
+
display: flex;
|
|
325
|
+
justify-content: space-around;
|
|
326
|
+
padding: 16px 0;
|
|
327
|
+
border-top: 1px solid rgba(255, 255, 255, 0.2);
|
|
328
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.detail-item {
|
|
332
|
+
display: flex;
|
|
333
|
+
flex-direction: column;
|
|
334
|
+
align-items: center;
|
|
335
|
+
gap: 4px;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.detail-item i {
|
|
339
|
+
font-size: 20px;
|
|
340
|
+
opacity: 0.8;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.detail-item .label {
|
|
344
|
+
font-size: 12px;
|
|
345
|
+
opacity: 0.7;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.detail-item .value {
|
|
349
|
+
font-size: 14px;
|
|
350
|
+
font-weight: 500;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.weather-forecast {
|
|
354
|
+
margin-top: 16px;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.forecast-title {
|
|
358
|
+
font-size: 14px;
|
|
359
|
+
margin-bottom: 12px;
|
|
360
|
+
opacity: 0.9;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.forecast-list {
|
|
364
|
+
display: flex;
|
|
365
|
+
justify-content: space-between;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.forecast-item {
|
|
369
|
+
display: flex;
|
|
370
|
+
flex-direction: column;
|
|
371
|
+
align-items: center;
|
|
372
|
+
gap: 4px;
|
|
373
|
+
font-size: 12px;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.forecast-item .day {
|
|
377
|
+
opacity: 0.8;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.forecast-item i {
|
|
381
|
+
font-size: 20px;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.forecast-item .temp {
|
|
385
|
+
opacity: 0.9;
|
|
386
|
+
}
|
|
387
|
+
</style>
|
package/src/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import Vue from 'vue';
|
|
2
|
+
import VueCustomElement from 'vue-custom-element';
|
|
3
|
+
import ElementUI from 'element-ui';
|
|
4
|
+
import 'element-ui/lib/theme-chalk/index.css';
|
|
5
|
+
|
|
6
|
+
// 引入组件
|
|
7
|
+
import PyTable from './components/PyTable.vue';
|
|
8
|
+
import PyWeather from './components/PyWeather.vue';
|
|
9
|
+
|
|
10
|
+
// 引入 store
|
|
11
|
+
import store from './store';
|
|
12
|
+
|
|
13
|
+
// 使用插件
|
|
14
|
+
Vue.use(ElementUI);
|
|
15
|
+
Vue.use(VueCustomElement);
|
|
16
|
+
|
|
17
|
+
// 注册 Web Components(必须禁用 Shadow DOM)
|
|
18
|
+
Vue.customElement('py-table', PyTable, {
|
|
19
|
+
shadow: false
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
Vue.customElement('py-weather', PyWeather, {
|
|
23
|
+
shadow: false
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// initStore 函数 - 外部唯一入口
|
|
27
|
+
function initStore(config) {
|
|
28
|
+
if (!config || typeof config !== 'object') {
|
|
29
|
+
console.warn('[PyComponent] initStore 需要传入配置对象');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 将配置设置到 store 中
|
|
34
|
+
Object.keys(config).forEach(key => {
|
|
35
|
+
store.set(key, config[key]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
console.log('[PyComponent] Store 已初始化');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 挂载到 window(浏览器环境)
|
|
42
|
+
if (typeof window !== 'undefined') {
|
|
43
|
+
window.PyComponent = {
|
|
44
|
+
initStore
|
|
45
|
+
// 注意:不暴露 store 实例
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 默认导出
|
|
50
|
+
export { initStore };
|
|
51
|
+
export default { initStore };
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import React, { forwardRef, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
// ElementUI CSS URL
|
|
4
|
+
const ELEMENT_UI_CSS = 'https://unpkg.com/element-ui/lib/theme-chalk/index.css';
|
|
5
|
+
|
|
6
|
+
// 标记 CSS 是否已注入
|
|
7
|
+
let cssInjected = false;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 注入 ElementUI CSS
|
|
11
|
+
*/
|
|
12
|
+
function injectElementUICSS() {
|
|
13
|
+
if (cssInjected || typeof document === 'undefined') return;
|
|
14
|
+
|
|
15
|
+
// 检查是否已存在
|
|
16
|
+
const existing = document.querySelector(`link[href="${ELEMENT_UI_CSS}"]`);
|
|
17
|
+
if (existing) {
|
|
18
|
+
cssInjected = true;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const link = document.createElement('link');
|
|
23
|
+
link.rel = 'stylesheet';
|
|
24
|
+
link.href = ELEMENT_UI_CSS;
|
|
25
|
+
document.head.appendChild(link);
|
|
26
|
+
cssInjected = true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 包装 Vue Web Component 为 React 组件
|
|
31
|
+
* @param {string} tagName - 自定义元素标签名
|
|
32
|
+
* @param {string} dataProp - 数据属性名
|
|
33
|
+
* @returns {React.Component} React 组件
|
|
34
|
+
*/
|
|
35
|
+
function wrapVueComponent(tagName, dataProp = 'propData') {
|
|
36
|
+
return forwardRef(function WrappedComponent({
|
|
37
|
+
propData,
|
|
38
|
+
loading,
|
|
39
|
+
onChange,
|
|
40
|
+
onLoad,
|
|
41
|
+
onError,
|
|
42
|
+
onRefresh,
|
|
43
|
+
onSelectionChange,
|
|
44
|
+
onEdit,
|
|
45
|
+
onDelete,
|
|
46
|
+
onSizeChange,
|
|
47
|
+
onPageChange,
|
|
48
|
+
...props
|
|
49
|
+
}, ref) {
|
|
50
|
+
const innerRef = useRef(null);
|
|
51
|
+
const [isReady, setIsReady] = useState(false);
|
|
52
|
+
|
|
53
|
+
// 合并 ref
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (typeof ref === 'function') {
|
|
56
|
+
ref(innerRef.current);
|
|
57
|
+
} else if (ref) {
|
|
58
|
+
ref.current = innerRef.current;
|
|
59
|
+
}
|
|
60
|
+
}, [ref]);
|
|
61
|
+
|
|
62
|
+
// 加载组件库和 CSS
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
injectElementUICSS();
|
|
65
|
+
|
|
66
|
+
// 动态加载组件库构建产物
|
|
67
|
+
import('../../dist/py-component.esm.js')
|
|
68
|
+
.then(() => {
|
|
69
|
+
setIsReady(true);
|
|
70
|
+
})
|
|
71
|
+
.catch(err => {
|
|
72
|
+
console.error(`[PyComponent] 加载失败:`, err);
|
|
73
|
+
});
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
// 设置 propData 到 DOM property
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (innerRef.current && isReady) {
|
|
79
|
+
innerRef.current[dataProp] = propData;
|
|
80
|
+
}
|
|
81
|
+
}, [propData, isReady, dataProp]);
|
|
82
|
+
|
|
83
|
+
// 绑定事件
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
const el = innerRef.current;
|
|
86
|
+
if (!el || !isReady) return;
|
|
87
|
+
|
|
88
|
+
const events = {
|
|
89
|
+
'change': onChange,
|
|
90
|
+
'load': onLoad,
|
|
91
|
+
'error': onError,
|
|
92
|
+
'refresh': onRefresh,
|
|
93
|
+
'selection-change': onSelectionChange,
|
|
94
|
+
'edit': onEdit,
|
|
95
|
+
'delete': onDelete,
|
|
96
|
+
'size-change': onSizeChange,
|
|
97
|
+
'page-change': onPageChange
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
Object.entries(events).forEach(([eventName, handler]) => {
|
|
101
|
+
if (handler) {
|
|
102
|
+
el.addEventListener(eventName, handler);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return () => {
|
|
107
|
+
Object.entries(events).forEach(([eventName, handler]) => {
|
|
108
|
+
if (handler) {
|
|
109
|
+
el.removeEventListener(eventName, handler);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
}, [isReady, onChange, onLoad, onError, onRefresh, onSelectionChange, onEdit, onDelete, onSizeChange, onPageChange]);
|
|
114
|
+
|
|
115
|
+
// 显示 loading 状态
|
|
116
|
+
if (!isReady) {
|
|
117
|
+
return loading || <div style={{ padding: 20, textAlign: 'center' }}>加载中...</div>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return React.createElement(tagName, {
|
|
121
|
+
ref: innerRef,
|
|
122
|
+
...props
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 创建组件
|
|
128
|
+
export const PyTable = wrapVueComponent('py-table', 'propData');
|
|
129
|
+
export const PyWeather = wrapVueComponent('py-weather', 'propData');
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 初始化 Store
|
|
133
|
+
* @param {object} config - 配置对象
|
|
134
|
+
* @returns {Promise<void>}
|
|
135
|
+
*/
|
|
136
|
+
export function initStore(config) {
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
import('../../dist/py-component.esm.js')
|
|
139
|
+
.then((module) => {
|
|
140
|
+
const { initStore: init } = module;
|
|
141
|
+
if (init) {
|
|
142
|
+
init(config);
|
|
143
|
+
resolve();
|
|
144
|
+
} else {
|
|
145
|
+
reject(new Error('initStore 不存在'));
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
.catch(reject);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export default {
|
|
153
|
+
PyTable,
|
|
154
|
+
PyWeather,
|
|
155
|
+
initStore
|
|
156
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import Vue from 'vue';
|
|
2
|
+
|
|
3
|
+
// 创建响应式 store 对象
|
|
4
|
+
const state = Vue.observable({
|
|
5
|
+
// 默认配置
|
|
6
|
+
apiKey: '',
|
|
7
|
+
baseUrl: '',
|
|
8
|
+
userInfo: null,
|
|
9
|
+
// 可扩展更多全局配置
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
// Store API
|
|
13
|
+
const store = {
|
|
14
|
+
/**
|
|
15
|
+
* 获取 store 中的值
|
|
16
|
+
* @param {string} key - 键名
|
|
17
|
+
* @returns {any} 值
|
|
18
|
+
*/
|
|
19
|
+
get(key) {
|
|
20
|
+
return state[key];
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 设置 store 中的值
|
|
25
|
+
* @param {string} key - 键名
|
|
26
|
+
* @param {any} value - 值
|
|
27
|
+
*/
|
|
28
|
+
set(key, value) {
|
|
29
|
+
state[key] = value;
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 批量设置值
|
|
34
|
+
* @param {object} config - 配置对象
|
|
35
|
+
*/
|
|
36
|
+
setMultiple(config) {
|
|
37
|
+
Object.assign(state, config);
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 订阅 store 变化(仅供内部使用)
|
|
42
|
+
* @param {function} callback - 回调函数
|
|
43
|
+
* @returns {function} 取消订阅函数
|
|
44
|
+
*/
|
|
45
|
+
subscribe(callback) {
|
|
46
|
+
// 使用 Vue watch 实现订阅
|
|
47
|
+
const unwatch = Vue.watch(
|
|
48
|
+
() => state,
|
|
49
|
+
(newVal, oldVal) => {
|
|
50
|
+
callback(newVal, oldVal);
|
|
51
|
+
},
|
|
52
|
+
{ deep: true }
|
|
53
|
+
);
|
|
54
|
+
return unwatch;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 获取整个 state(慎用,主要用于调试)
|
|
59
|
+
* @returns {object} state 对象
|
|
60
|
+
*/
|
|
61
|
+
getState() {
|
|
62
|
+
return state;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default store;
|