py-test-components 1.0.1 → 1.0.2

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,220 +1,60 @@
1
- import React, { forwardRef, useEffect, useRef, useState, createElement } 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
- // 存储 initStore 函数
10
- let storeInitFn = null;
11
-
12
- /**
13
- * 注入 ElementUI CSS
14
- */
15
- function injectElementUICSS() {
16
- if (cssInjected || typeof document === 'undefined') return;
17
-
18
- // 检查是否已存在
19
- const existing = document.querySelector('link[href="' + ELEMENT_UI_CSS + '"]');
20
- if (existing) {
21
- cssInjected = true;
22
- return;
23
- }
24
-
25
- const link = document.createElement('link');
26
- link.rel = 'stylesheet';
27
- link.href = ELEMENT_UI_CSS;
28
- document.head.appendChild(link);
29
- cssInjected = true;
30
- }
31
-
32
- /**
33
- * 加载主组件库
34
- */
35
- function loadMainLibrary() {
36
- return new Promise(function(resolve, reject) {
37
- // 如果已经加载过
38
- if (typeof window !== 'undefined' && window.PyComponent) {
39
- storeInitFn = window.PyComponent.initStore;
40
- resolve(window.PyComponent);
41
- return;
42
- }
43
-
44
- // 动态加载脚本
45
- if (typeof document === 'undefined') {
46
- reject(new Error('只能在浏览器环境中使用'));
47
- return;
48
- }
49
-
50
- const script = document.createElement('script');
51
- // 使用 CDN 或相对路径加载构建产物
52
- script.src = '/node_modules/py-test-components/dist/py-component.js';
53
- script.onload = function() {
54
- if (window.PyComponent) {
55
- storeInitFn = window.PyComponent.initStore;
56
- resolve(window.PyComponent);
57
- } else {
58
- reject(new Error('组件库加载失败'));
59
- }
60
- };
61
- script.onerror = function() {
62
- // 尝试备用路径
63
- const script2 = document.createElement('script');
64
- script2.src = './dist/py-component.js';
65
- script2.onload = function() {
66
- if (window.PyComponent) {
67
- storeInitFn = window.PyComponent.initStore;
68
- resolve(window.PyComponent);
69
- } else {
70
- reject(new Error('组件库加载失败'));
71
- }
72
- };
73
- script2.onerror = function() {
74
- reject(new Error('无法加载组件库'));
75
- };
76
- document.head.appendChild(script2);
77
- };
78
- document.head.appendChild(script);
79
- });
80
- }
81
-
82
- /**
83
- * 包装 Vue Web Component 为 React 组件
84
- * @param {string} tagName - 自定义元素标签名
85
- * @param {string} dataProp - 数据属性名
86
- * @returns {React.Component} React 组件
87
- */
88
- function wrapVueComponent(tagName, dataProp) {
89
- return forwardRef(function WrappedComponent(props, ref) {
90
- var propData = props.propData,
91
- loading = props.loading,
92
- onChange = props.onChange,
93
- onLoad = props.onLoad,
94
- onError = props.onError,
95
- onRefresh = props.onRefresh,
96
- onSelectionChange = props.onSelectionChange,
97
- onEdit = props.onEdit,
98
- onDelete = props.onDelete,
99
- onSizeChange = props.onSizeChange,
100
- onPageChange = props.onPageChange,
101
- restProps = {};
102
-
103
- // 提取其他 props
104
- for (var key in props) {
105
- if (props.hasOwnProperty(key) &&
106
- !['propData', 'loading', 'onChange', 'onLoad', 'onError',
107
- 'onRefresh', 'onSelectionChange', 'onEdit', 'onDelete',
108
- 'onSizeChange', 'onPageChange'].includes(key)) {
109
- restProps[key] = props[key];
110
- }
111
- }
112
-
113
- var innerRef = useRef(null);
114
- var _useState = useState(false);
115
- var isReady = _useState[0];
116
- var setIsReady = _useState[1];
117
-
118
- // 合并 ref
119
- useEffect(function() {
120
- if (typeof ref === 'function') {
121
- ref(innerRef.current);
122
- } else if (ref) {
123
- ref.current = innerRef.current;
124
- }
125
- }, [ref]);
126
-
127
- // 加载组件库和 CSS
128
- useEffect(function() {
129
- injectElementUICSS();
130
-
131
- loadMainLibrary()
132
- .then(function() {
133
- setIsReady(true);
134
- })
135
- .catch(function(err) {
136
- console.error('[PyComponent] 加载失败:', err);
137
- });
138
- }, []);
139
-
140
- // 设置 propData 到 DOM property
141
- useEffect(function() {
142
- if (innerRef.current && isReady) {
143
- innerRef.current[dataProp] = propData;
144
- }
145
- }, [propData, isReady, dataProp]);
146
-
147
- // 绑定事件
148
- useEffect(function() {
149
- var el = innerRef.current;
150
- if (!el || !isReady) return;
151
-
152
- var events = {
153
- 'change': onChange,
154
- 'load': onLoad,
155
- 'error': onError,
156
- 'refresh': onRefresh,
157
- 'selection-change': onSelectionChange,
158
- 'edit': onEdit,
159
- 'delete': onDelete,
160
- 'size-change': onSizeChange,
161
- 'page-change': onPageChange
162
- };
163
-
164
- Object.keys(events).forEach(function(eventName) {
165
- var handler = events[eventName];
166
- if (handler) {
167
- el.addEventListener(eventName, handler);
168
- }
169
- });
170
-
171
- return function() {
172
- Object.keys(events).forEach(function(eventName) {
173
- var handler = events[eventName];
174
- if (handler) {
175
- el.removeEventListener(eventName, handler);
176
- }
177
- });
178
- };
179
- }, [isReady, onChange, onLoad, onError, onRefresh, onSelectionChange, onEdit, onDelete, onSizeChange, onPageChange]);
180
-
181
- // 显示 loading 状态
182
- if (!isReady) {
183
- return loading || createElement('div', {
184
- style: { padding: 20, textAlign: 'center' }
185
- }, '加载中...');
186
- }
187
-
188
- return createElement(tagName, {
189
- ref: innerRef,
190
- ...restProps
191
- });
192
- });
193
- }
194
-
195
- // 创建组件
196
- export var PyTable = wrapVueComponent('py-table', 'propData');
197
- export var PyWeather = wrapVueComponent('py-weather', 'propData');
198
-
199
- /**
200
- * 初始化 Store
201
- * @param {object} config - 配置对象
202
- * @returns {Promise<void>}
203
- */
204
- export function initStore(config) {
205
- return loadMainLibrary().then(function(module) {
206
- if (module.initStore) {
207
- module.initStore(config);
208
- } else if (storeInitFn) {
209
- storeInitFn(config);
210
- } else {
211
- throw new Error('initStore 不存在');
212
- }
213
- });
214
- }
215
-
216
- export default {
217
- PyTable: PyTable,
218
- PyWeather: PyWeather,
219
- initStore: initStore
220
- };
1
+ import { forwardRef, useEffect, useState } from 'react';
2
+
3
+ const injected = { css: false };
4
+
5
+ function ensureCSS() {
6
+ if (injected.css) return;
7
+ const link = document.createElement('link');
8
+ link.rel = 'stylesheet';
9
+ link.href = 'https://unpkg.com/element-ui@2.15.14/lib/theme-chalk/index.css';
10
+ document.head.appendChild(link);
11
+ injected.css = true;
12
+ }
13
+
14
+ function wrapVueComponent(tagName) {
15
+ const Component = forwardRef(function WrappedComponent(
16
+ { propData, loading, ...props },
17
+ ref
18
+ ) {
19
+ const [mounted, setMounted] = useState(false);
20
+
21
+ useEffect(() => {
22
+ ensureCSS();
23
+ setMounted(true);
24
+ }, []);
25
+
26
+ if (!mounted) {
27
+ return loading || null;
28
+ }
29
+
30
+ const tag = document.createElement(tagName);
31
+ if (propData) {
32
+ tag.propData = propData;
33
+ }
34
+ Object.keys(props).forEach((key) => {
35
+ if (key.startsWith('on') && typeof props[key] === 'function') {
36
+ const eventName = key.slice(2).toLowerCase();
37
+ tag.addEventListener(eventName, props[key]);
38
+ }
39
+ });
40
+
41
+ if (ref && typeof ref === 'object' && ref.current !== undefined) {
42
+ ref.current = tag;
43
+ }
44
+
45
+ return tag;
46
+ });
47
+
48
+ Component.displayName = tagName;
49
+ return Component;
50
+ }
51
+
52
+ export const PyTable = wrapVueComponent('py-table');
53
+ export const PyWeather = wrapVueComponent('py-weather');
54
+
55
+ export async function initStore(config) {
56
+ if (typeof window !== 'undefined' && window.PyComponent?.initStore) {
57
+ window.PyComponent.initStore(config);
58
+ }
59
+ return Promise.resolve();
60
+ }
@@ -1,66 +1,17 @@
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;
1
+ import Vue from 'vue';
2
+
3
+ const state = Vue.observable({
4
+ apiKey: '',
5
+ baseUrl: '',
6
+ });
7
+
8
+ const store = {
9
+ get(key) {
10
+ return state[key];
11
+ },
12
+ set(key, value) {
13
+ state[key] = value;
14
+ },
15
+ };
16
+
17
+ export default store;
package/src/utils/api.js CHANGED
@@ -1,101 +1,13 @@
1
- /**
2
- * API 请求封装
3
- */
4
- import { get, post } from './request';
5
- import store from '../store';
6
-
7
- // 获取基础 URL
8
- function getBaseUrl() {
9
- return store.get('baseUrl') || '';
10
- }
11
-
12
- // 获取 API Key
13
- function getApiKey() {
14
- return store.get('apiKey') || '';
15
- }
16
-
17
- // 构建带认证的请求头
18
- function getAuthHeaders() {
19
- const apiKey = getApiKey();
20
- return apiKey ? { 'X-API-Key': apiKey } : {};
21
- }
22
-
23
- /**
24
- * 天气相关 API
25
- */
26
- export const weatherApi = {
27
- /**
28
- * 获取天气信息
29
- * @param {string} city - 城市名
30
- * @returns {Promise} 天气数据
31
- */
32
- getWeather(city) {
33
- return get('/api/weather', { city }, {
34
- baseURL: getBaseUrl(),
35
- headers: getAuthHeaders()
36
- });
37
- },
38
-
39
- /**
40
- * 获取forecast
41
- * @param {string} city - 城市名
42
- * @returns {Promise} forecast数据
43
- */
44
- getForecast(city) {
45
- return get('/api/weather/forecast', { city }, {
46
- baseURL: getBaseUrl(),
47
- headers: getAuthHeaders()
48
- });
49
- }
50
- };
51
-
52
- /**
53
- * 数据表格相关 API
54
- */
55
- export const tableApi = {
56
- /**
57
- * 获取表格数据
58
- * @param {object} params - 查询参数
59
- * @returns {Promise} 表格数据
60
- */
61
- getData(params = {}) {
62
- return get('/api/table/data', params, {
63
- baseURL: getBaseUrl(),
64
- headers: getAuthHeaders()
65
- });
66
- },
67
-
68
- /**
69
- * 提交表格数据
70
- * @param {object} data - 提交的数据
71
- * @returns {Promise} 提交结果
72
- */
73
- submitData(data) {
74
- return post('/api/table/data', data, {
75
- baseURL: getBaseUrl(),
76
- headers: getAuthHeaders()
77
- });
78
- }
79
- };
80
-
81
- /**
82
- * 用户相关 API
83
- */
84
- export const userApi = {
85
- /**
86
- * 获取用户信息
87
- * @returns {Promise} 用户数据
88
- */
89
- getUserInfo() {
90
- return get('/api/user/info', {}, {
91
- baseURL: getBaseUrl(),
92
- headers: getAuthHeaders()
93
- });
94
- }
95
- };
96
-
97
- export default {
98
- weather: weatherApi,
99
- table: tableApi,
100
- user: userApi
101
- };
1
+ import { post } from './request';
2
+ import store from '../store';
3
+
4
+ const DEFAULT_BASE_URL = 'https://api.example.com';
5
+
6
+ export function queryWeather(city) {
7
+ const baseUrl = store.get('baseUrl') || DEFAULT_BASE_URL;
8
+ const apiKey = store.get('apiKey');
9
+ return post(`${baseUrl}/weather/query`, {
10
+ city,
11
+ apiKey,
12
+ });
13
+ }
@@ -1,113 +1,10 @@
1
- /**
2
- * 封装的 fetch 请求工具
3
- */
4
-
5
- // 默认配置
6
- const defaultConfig = {
7
- baseURL: '',
8
- timeout: 10000,
9
- headers: {
10
- 'Content-Type': 'application/json'
11
- }
12
- };
13
-
14
- /**
15
- * 发起 HTTP 请求
16
- * @param {string} url - 请求地址
17
- * @param {object} options - 请求选项
18
- * @returns {Promise} 响应结果
19
- */
20
- export function request(url, options = {}) {
21
- const config = {
22
- ...defaultConfig,
23
- ...options,
24
- headers: {
25
- ...defaultConfig.headers,
26
- ...options.headers
27
- }
28
- };
29
-
30
- // 处理 URL
31
- const fullUrl = config.baseURL ? `${config.baseURL}${url}` : url;
32
-
33
- // 创建 AbortController 用于超时控制
34
- const controller = new AbortController();
35
- const timeoutId = setTimeout(() => controller.abort(), config.timeout);
36
-
37
- return fetch(fullUrl, {
38
- ...config,
39
- signal: controller.signal
40
- })
41
- .then(response => {
42
- clearTimeout(timeoutId);
43
-
44
- if (!response.ok) {
45
- throw new Error(`HTTP error! status: ${response.status}`);
46
- }
47
-
48
- // 根据返回类型解析数据
49
- const contentType = response.headers.get('content-type');
50
- if (contentType && contentType.includes('application/json')) {
51
- return response.json();
52
- }
53
- return response.text();
54
- })
55
- .catch(error => {
56
- clearTimeout(timeoutId);
57
-
58
- if (error.name === 'AbortError') {
59
- throw new Error('Request timeout');
60
- }
61
- throw error;
62
- });
63
- }
64
-
65
- /**
66
- * GET 请求
67
- * @param {string} url - 请求地址
68
- * @param {object} params - URL 参数
69
- * @param {object} options - 其他选项
70
- * @returns {Promise} 响应结果
71
- */
72
- export function get(url, params = {}, options = {}) {
73
- const queryString = Object.keys(params)
74
- .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
75
- .join('&');
76
-
77
- const fullUrl = queryString ? `${url}?${queryString}` : url;
78
-
79
- return request(fullUrl, {
80
- method: 'GET',
81
- ...options
82
- });
83
- }
84
-
85
- /**
86
- * POST 请求
87
- * @param {string} url - 请求地址
88
- * @param {object} data - 请求数据
89
- * @param {object} options - 其他选项
90
- * @returns {Promise} 响应结果
91
- */
92
- export function post(url, data = {}, options = {}) {
93
- return request(url, {
94
- method: 'POST',
95
- body: JSON.stringify(data),
96
- ...options
97
- });
98
- }
99
-
100
- /**
101
- * 设置默认配置
102
- * @param {object} config - 配置对象
103
- */
104
- export function setDefaultConfig(config) {
105
- Object.assign(defaultConfig, config);
106
- }
107
-
108
- export default {
109
- request,
110
- get,
111
- post,
112
- setDefaultConfig
113
- };
1
+ export function post(url, data, headers = {}) {
2
+ return fetch(url, {
3
+ method: 'POST',
4
+ headers: {
5
+ 'Content-Type': 'application/json',
6
+ ...headers,
7
+ },
8
+ body: JSON.stringify(data),
9
+ }).then((res) => res.json());
10
+ }
package/src/vue/index.js CHANGED
@@ -1,32 +1,25 @@
1
- // Vue 入口 - 直接导出 Vue 组件
2
-
3
- import PyTable from '../components/PyTable.vue';
4
- import PyWeather from '../components/PyWeather.vue';
5
- import store from '../store';
6
-
7
- /**
8
- * 初始化 Store
9
- * @param {object} config - 配置对象
10
- */
11
- function initStore(config) {
12
- if (!config || typeof config !== 'object') {
13
- console.warn('[PyComponent] initStore 需要传入配置对象');
14
- return;
15
- }
16
-
17
- Object.keys(config).forEach(key => {
18
- store.set(key, config[key]);
19
- });
20
-
21
- console.log('[PyComponent] Store 已初始化');
22
- }
23
-
24
- // 导出组件和函数
25
- export { PyTable, PyWeather, initStore };
26
-
27
- // 默认导出
28
- export default {
29
- PyTable,
30
- PyWeather,
31
- initStore
32
- };
1
+ import Vue from 'vue';
2
+ import ElementUI from 'element-ui';
3
+ import 'element-ui/lib/theme-chalk/index.css';
4
+ import store from '../store';
5
+
6
+ import PyTable from '../components/PyTable.vue';
7
+ import PyWeather from '../components/PyWeather.vue';
8
+
9
+ Vue.use(ElementUI);
10
+
11
+ function initStore(config) {
12
+ if (config) {
13
+ Object.keys(config).forEach((key) => {
14
+ store.set(key, config[key]);
15
+ });
16
+ }
17
+ }
18
+
19
+ export { PyTable, PyWeather, initStore };
20
+
21
+ export default {
22
+ PyTable,
23
+ PyWeather,
24
+ initStore,
25
+ };