py-test-component 2.0.2 → 2.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "py-test-component",
3
- "version": "2.0.2",
3
+ "version": "2.0.5",
4
4
  "description": "Vue2 + ElementUI 组件库,支持 React 使用",
5
5
  "main": "dist/py-component.js",
6
6
  "module": "dist/py-component.esm.js",
@@ -21,7 +21,13 @@
21
21
  "files": [
22
22
  "dist",
23
23
  "src/react",
24
- "README.md","USAGE.md","DEVELOPER.md","src/vue"
24
+ "src/components",
25
+ "src/store",
26
+ "src/utils",
27
+ "README.md",
28
+ "USAGE.md",
29
+ "DEVELOPER.md",
30
+ "src/vue"
25
31
  ],
26
32
  "scripts": {
27
33
  "build": "webpack --mode production",
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <el-table
3
+ :data="tableData"
4
+ style="width: 100%">
5
+ <el-table-column
6
+ prop="date"
7
+ label="日期"
8
+ width="180">
9
+ </el-table-column>
10
+ <el-table-column
11
+ prop="name"
12
+ label="姓名"
13
+ width="180">
14
+ </el-table-column>
15
+ <el-table-column
16
+ prop="address"
17
+ label="地址">
18
+ </el-table-column>
19
+ </el-table>
20
+ </template>
21
+
22
+ <script>
23
+ export default {
24
+ name: 'PyTable',
25
+ props: {
26
+ propData: {
27
+ default: null
28
+ }
29
+ },
30
+ computed: {
31
+ tableData() {
32
+ // el-table 需要数组,如果不是数组则返回空数组
33
+ return Array.isArray(this.propData) ? this.propData : []
34
+ }
35
+ }
36
+ };
37
+ </script>
38
+
39
+ <style scoped>
40
+ .py-table-wrapper {
41
+ padding: 10px;
42
+ }
43
+ </style>
@@ -0,0 +1,109 @@
1
+ <template>
2
+ <div class="py-weather">
3
+ <div class="search-box">
4
+ <el-input
5
+ v-model="city"
6
+ placeholder="请输入城市名称"
7
+ @keyup.enter.native="handleSearch"
8
+ clearable
9
+ />
10
+ <el-button type="primary" @click="handleSearch" :loading="loading">
11
+ 查询
12
+ </el-button>
13
+ </div>
14
+
15
+ <div v-if="weatherData" class="weather-info">
16
+ <h3>{{ weatherData.location.name }}</h3>
17
+ <div class="weather-detail">
18
+ <span class="temperature">{{ weatherData.now.temperature }}°C</span>
19
+ <span class="weather-text">{{ weatherData.now.text }}</span>
20
+ </div>
21
+ </div>
22
+
23
+ <div v-if="error" class="error-message">
24
+ {{ error }}
25
+ </div>
26
+ </div>
27
+ </template>
28
+
29
+ <script>
30
+ import { queryWeather } from '../utils/api';
31
+
32
+ export default {
33
+ name: 'PyWeather',
34
+ data() {
35
+ return {
36
+ city: '',
37
+ loading: false,
38
+ weatherData: null,
39
+ error: null
40
+ };
41
+ },
42
+ methods: {
43
+ async handleSearch() {
44
+ if (!this.city.trim()) {
45
+ this.$message?.warning?.('请输入城市名称') || alert('请输入城市名称');
46
+ return;
47
+ }
48
+
49
+ this.loading = true;
50
+ this.error = null;
51
+
52
+ try {
53
+ const response = await queryWeather(this.city.trim());
54
+ if (response.results && response.results[0]) {
55
+ this.weatherData = response.results[0];
56
+ } else {
57
+ this.error = '未找到该城市的天气信息';
58
+ this.weatherData = null;
59
+ }
60
+ } catch (err) {
61
+ this.error = '查询失败,请稍后重试';
62
+ this.weatherData = null;
63
+ } finally {
64
+ this.loading = false;
65
+ }
66
+ }
67
+ }
68
+ };
69
+ </script>
70
+
71
+ <style scoped>
72
+ .py-weather {
73
+ padding: 20px;
74
+ max-width: 400px;
75
+ }
76
+ .search-box {
77
+ display: flex;
78
+ gap: 10px;
79
+ }
80
+ .weather-info {
81
+ margin-top: 20px;
82
+ padding: 20px;
83
+ background: #f5f7fa;
84
+ border-radius: 8px;
85
+ }
86
+ .weather-info h3 {
87
+ margin: 0 0 15px 0;
88
+ color: #303133;
89
+ }
90
+ .weather-detail {
91
+ display: flex;
92
+ align-items: center;
93
+ gap: 15px;
94
+ }
95
+ .temperature {
96
+ font-size: 36px;
97
+ font-weight: bold;
98
+ color: #409EFF;
99
+ }
100
+ .weather-text {
101
+ font-size: 18px;
102
+ color: #606266;
103
+ }
104
+ .error-message {
105
+ margin-top: 15px;
106
+ color: #f56c6c;
107
+ font-size: 14px;
108
+ }
109
+ </style>
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef, useState, forwardRef } from 'react';
1
+ import React, { useEffect, useRef, useState, forwardRef } from 'react';
2
2
 
3
3
  // 注入 ElementUI CSS(只执行一次)
4
4
  let cssInjected = false;
@@ -16,7 +16,7 @@ function usePyComponent() {
16
16
  const [loaded, setLoaded] = useState(false);
17
17
  useEffect(() => {
18
18
  injectElementUICSS();
19
- import('../../dist/py-component.esm.js').then(() => setLoaded(true));
19
+ import('py-test-component').then(() => setLoaded(true));
20
20
  }, []);
21
21
  return loaded;
22
22
  }
@@ -37,7 +37,8 @@ function wrapVueComponent(tagName, dataProp = null) {
37
37
  return <div style={{ padding: '20px', textAlign: 'center' }}>{loading}</div>;
38
38
  }
39
39
 
40
- return <tagName ref={ref || elRef} {...props} />;
40
+ // 使用 createElement 避免 JSX 标签名大小写警告
41
+ return React.createElement(tagName, { ref: ref || elRef, ...props });
41
42
  });
42
43
  }
43
44
 
@@ -47,7 +48,7 @@ export const PyWeather = wrapVueComponent('py-weather', 'propData');
47
48
 
48
49
  // 初始化 store(仅设置,不暴露 store 给外部)
49
50
  export function initStore(config) {
50
- return import('../../dist/py-component.esm.js').then((m) => {
51
+ return import('py-test-component').then((m) => {
51
52
  const PyComponent = m.default || m;
52
53
  PyComponent?.initStore?.(config);
53
54
  });
@@ -0,0 +1,75 @@
1
+ import Vue from 'vue';
2
+
3
+ // 使用 Vue.observable 创建响应式全局状态
4
+ const state = Vue.observable({
5
+ config: {}
6
+ });
7
+
8
+ // 订阅者列表(用于非 Vue 环境,如 React)
9
+ const subscribers = new Set();
10
+
11
+ function notifySubscribers(key, value) {
12
+ subscribers.forEach(fn => fn(key, value, state.config));
13
+ }
14
+
15
+ const store = {
16
+ /**
17
+ * 获取 store 中的值
18
+ * @param {string} key - 键名,支持点号路径如 'user.name'
19
+ * @returns {any} 值
20
+ */
21
+ get(key) {
22
+ if (!key) return state.config;
23
+ const keys = key.split('.');
24
+ let value = state.config;
25
+ for (const k of keys) {
26
+ if (value === undefined || value === null) return undefined;
27
+ value = value[k];
28
+ }
29
+ return value;
30
+ },
31
+
32
+ /**
33
+ * 设置 store 中的值
34
+ * @param {string} key - 键名
35
+ * @param {any} value - 值
36
+ */
37
+ set(key, value) {
38
+ if (typeof key === 'object') {
39
+ Object.assign(state.config, key);
40
+ notifySubscribers(null, state.config);
41
+ } else {
42
+ const keys = key.split('.');
43
+ let target = state.config;
44
+ for (let i = 0; i < keys.length - 1; i++) {
45
+ const k = keys[i];
46
+ if (!(k in target) || typeof target[k] !== 'object') {
47
+ target[k] = {};
48
+ }
49
+ target = target[k];
50
+ }
51
+ target[keys[keys.length - 1]] = value;
52
+ notifySubscribers(key, value);
53
+ }
54
+ },
55
+
56
+ /**
57
+ * 订阅 store 变化(供 React 等使用)
58
+ * @param {function} callback - (key, value, config) => void
59
+ * @returns {function} 取消订阅函数
60
+ */
61
+ subscribe(callback) {
62
+ subscribers.add(callback);
63
+ return () => subscribers.delete(callback);
64
+ },
65
+
66
+ /**
67
+ * 获取整个状态对象(供内部组件使用)
68
+ * @returns {object}
69
+ */
70
+ getState() {
71
+ return state;
72
+ }
73
+ };
74
+
75
+ export default store;
@@ -0,0 +1,35 @@
1
+ import { get, post } from './request';
2
+
3
+ /**
4
+ * 查询天气
5
+ * @param {string} city - 城市名称
6
+ * @returns {Promise<object>}
7
+ */
8
+ export function queryWeather(city) {
9
+ // 使用免费的天气 API(心知天气或 OpenWeatherMap 等)
10
+ // 这里使用一个示例 API,实际使用时可以替换为真实的天气 API
11
+ const apiKey = 'demo'; // 实际使用时替换为真实的 API Key
12
+ const url = `https://api.seniverse.com/v3/weather/now.json`;
13
+
14
+ // 如果没有真实 API,返回模拟数据
15
+ if (apiKey === 'demo') {
16
+ return new Promise((resolve) => {
17
+ setTimeout(() => {
18
+ resolve({
19
+ results: [{
20
+ location: { name: city },
21
+ now: {
22
+ temperature: Math.floor(Math.random() * 30) + 5,
23
+ text: ['晴', '多云', '阴', '小雨', '大雨'][Math.floor(Math.random() * 5)]
24
+ }
25
+ }]
26
+ });
27
+ }, 500);
28
+ });
29
+ }
30
+
31
+ return get(url, {
32
+ key: apiKey,
33
+ location: city
34
+ });
35
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * 封装 fetch POST 请求
3
+ * @param {string} url - 请求地址
4
+ * @param {object} data - 请求数据
5
+ * @param {object} options - 其他配置
6
+ * @returns {Promise}
7
+ */
8
+ export function post(url, data = {}, options = {}) {
9
+ const defaultOptions = {
10
+ method: 'POST',
11
+ headers: {
12
+ 'Content-Type': 'application/json'
13
+ },
14
+ ...options
15
+ };
16
+
17
+ // 如果 data 是 FormData,不设置 Content-Type
18
+ if (data instanceof FormData) {
19
+ delete defaultOptions.headers['Content-Type'];
20
+ } else {
21
+ defaultOptions.body = JSON.stringify(data);
22
+ }
23
+
24
+ return fetch(url, defaultOptions)
25
+ .then(response => {
26
+ if (!response.ok) {
27
+ throw new Error(`HTTP error! status: ${response.status}`);
28
+ }
29
+ return response.json();
30
+ })
31
+ .catch(error => {
32
+ console.error('Request failed:', error);
33
+ throw error;
34
+ });
35
+ }
36
+
37
+ /**
38
+ * 封装 fetch GET 请求
39
+ * @param {string} url - 请求地址
40
+ * @param {object} params - URL 参数
41
+ * @param {object} options - 其他配置
42
+ * @returns {Promise}
43
+ */
44
+ export function get(url, params = {}, options = {}) {
45
+ const queryString = Object.keys(params)
46
+ .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
47
+ .join('&');
48
+
49
+ const fullUrl = queryString ? `${url}?${queryString}` : url;
50
+
51
+ return fetch(fullUrl, {
52
+ method: 'GET',
53
+ headers: {
54
+ 'Content-Type': 'application/json'
55
+ },
56
+ ...options
57
+ })
58
+ .then(response => {
59
+ if (!response.ok) {
60
+ throw new Error(`HTTP error! status: ${response.status}`);
61
+ }
62
+ return response.json();
63
+ })
64
+ .catch(error => {
65
+ console.error('Request failed:', error);
66
+ throw error;
67
+ });
68
+ }