best-unit 0.0.1 → 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.
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Best Unit SDK Example</title>
6
+ <style>
7
+ body { background: #f6f8fa; }
8
+ #best-unit-root { margin: 40px auto; max-width: 600px; min-height: 400px; background: #fff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
9
+ </style>
10
+ </head>
11
+ <body>
12
+ <div id="best-unit-root"></div>
13
+ <script type="module">
14
+ import { initBestUnit } from '../dist/best-unit.es.js';
15
+ // 初始化 SDK
16
+ initBestUnit({
17
+ token: 'your-user-token',
18
+ userId: 'your-user-id',
19
+ theme: 'dark',
20
+ container: '#best-unit-root'
21
+ });
22
+ </script>
23
+ </body>
24
+ </html>
package/index.html ADDED
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Vite App</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/src/main.jsx"></script>
12
+ </body>
13
+ </html>
package/package.json CHANGED
@@ -1,20 +1,18 @@
1
1
  {
2
2
  "name": "best-unit",
3
- "private": false,
4
- "version": "0.0.1",
3
+ "version": "1.0.0",
5
4
  "type": "module",
6
- "main": "dist/output.js",
7
- "types": "dist/output.d.ts",
8
- "files": [
9
- "dist"
10
- ],
11
5
  "scripts": {
12
6
  "dev": "vite",
13
- "build": "tsc",
7
+ "build": "vite build",
14
8
  "preview": "vite preview"
15
9
  },
16
10
  "devDependencies": {
17
- "typescript": "~5.8.3",
18
11
  "vite": "^7.0.4"
12
+ },
13
+ "dependencies": {
14
+ "@preact/preset-vite": "^2.10.2",
15
+ "axios": "^1.10.0",
16
+ "preact": "^10.26.9"
19
17
  }
20
18
  }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
package/src/App.jsx ADDED
@@ -0,0 +1,6 @@
1
+
2
+ import Home from './views/Home.jsx';
3
+
4
+ export default function App() {
5
+ return <Home />;
6
+ }
@@ -0,0 +1,49 @@
1
+ import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
2
+
3
+ // 创建 axios 实例
4
+ const instance: AxiosInstance = axios.create({
5
+ baseURL: '/api',
6
+ timeout: 10000,
7
+ });
8
+
9
+ // 请求拦截器,自动加上 authorization
10
+ instance.interceptors.request.use(
11
+ (config) => {
12
+ // 允许外部传入 headers.authorization
13
+ if (config.headers && config.headers['authorization']) {
14
+ config.headers['Authorization'] = config.headers['authorization'];
15
+ delete config.headers['authorization'];
16
+ }
17
+ return config;
18
+ },
19
+ (error) => Promise.reject(error)
20
+ );
21
+
22
+ // 响应拦截器,统一处理 code=0 的返回
23
+ instance.interceptors.response.use(
24
+ (response: AxiosResponse) => {
25
+ if (response.data && response.data.code === 0) {
26
+ return response.data;
27
+ }
28
+ // 其他情况抛出错误
29
+ return Promise.reject(response.data || response);
30
+ },
31
+ (error) => Promise.reject(error)
32
+ );
33
+
34
+ // 通用请求方法
35
+ export function request<T = any>(
36
+ url: string,
37
+ method: 'get' | 'post' | 'put' | 'delete' | 'patch' = 'get',
38
+ data?: any,
39
+ config: AxiosRequestConfig = {}
40
+ ): Promise<T> {
41
+ return instance({
42
+ url,
43
+ method,
44
+ ...(method === 'get' ? { params: data } : { data }),
45
+ ...config,
46
+ });
47
+ }
48
+
49
+ export default instance;
@@ -0,0 +1,19 @@
1
+ import { request } from './define-api';
2
+
3
+ // 示例:获取用户信息
4
+ export function getUserInfo(token: string) {
5
+ return request('/user/info', 'get', undefined, {
6
+ headers: {
7
+ authorization: token,
8
+ },
9
+ });
10
+ }
11
+
12
+ // 表单提交接口
13
+ export function submitAccountForm(data: { agent: string; requestId: string; remark: string }, token: string) {
14
+ return request('/account/submit', 'post', data, {
15
+ headers: {
16
+ authorization: token,
17
+ },
18
+ });
19
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="#F7DF1E" d="M0 0h256v256H0V0Z"></path><path d="m67.312 213.932l19.59-11.856c3.78 6.701 7.218 12.371 15.465 12.371c7.905 0 12.89-3.092 12.89-15.12v-81.798h24.057v82.138c0 24.917-14.606 36.259-35.916 36.259c-19.245 0-30.416-9.967-36.087-21.996m85.07-2.576l19.588-11.341c5.157 8.421 11.859 14.607 23.715 14.607c9.969 0 16.325-4.984 16.325-11.858c0-8.248-6.53-11.17-17.528-15.98l-6.013-2.58c-17.357-7.387-28.87-16.667-28.87-36.257c0-18.044 13.747-31.792 35.228-31.792c15.294 0 26.292 5.328 34.196 19.247l-18.732 12.03c-4.125-7.389-8.591-10.31-15.465-10.31c-7.046 0-11.514 4.468-11.514 10.31c0 7.217 4.468 10.14 14.778 14.608l6.014 2.577c20.45 8.765 31.963 17.7 31.963 37.804c0 21.654-17.012 33.51-39.867 33.51c-22.339 0-36.774-10.654-43.819-24.574"></path></svg>
package/src/main.jsx ADDED
@@ -0,0 +1,4 @@
1
+ import { render } from 'preact';
2
+ import App from './App.jsx';
3
+
4
+ render(<App />, document.getElementById('app'));
@@ -0,0 +1,33 @@
1
+ import { render } from 'preact';
2
+ import Home from '../views/Home.jsx';
3
+
4
+ export interface BestUnitOptions {
5
+ token: string;
6
+ userId: string;
7
+ theme?: 'light' | 'dark' | string;
8
+ container?: HTMLElement | string;
9
+ }
10
+
11
+ declare global {
12
+ interface Window {
13
+ BEST_UNIT_TOKEN?: string;
14
+ BEST_UNIT_USERID?: string;
15
+ BEST_UNIT_THEME?: string;
16
+ }
17
+ }
18
+
19
+ export function initBestUnit(options: BestUnitOptions) {
20
+ window.BEST_UNIT_TOKEN = options.token;
21
+ window.BEST_UNIT_USERID = options.userId;
22
+ window.BEST_UNIT_THEME = options.theme || 'light';
23
+
24
+ let container: HTMLElement | null;
25
+ if (typeof options.container === 'string') {
26
+ container = document.querySelector(options.container);
27
+ } else if (options.container instanceof HTMLElement) {
28
+ container = options.container;
29
+ } else {
30
+ container = document.body;
31
+ }
32
+ render(Home, container!);
33
+ }
package/src/style.css ADDED
@@ -0,0 +1,96 @@
1
+ :root {
2
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+ line-height: 1.5;
4
+ font-weight: 400;
5
+
6
+ color-scheme: light dark;
7
+ color: rgba(255, 255, 255, 0.87);
8
+ background-color: #242424;
9
+
10
+ font-synthesis: none;
11
+ text-rendering: optimizeLegibility;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ }
15
+
16
+ a {
17
+ font-weight: 500;
18
+ color: #646cff;
19
+ text-decoration: inherit;
20
+ }
21
+ a:hover {
22
+ color: #535bf2;
23
+ }
24
+
25
+ body {
26
+ margin: 0;
27
+ display: flex;
28
+ place-items: center;
29
+ min-width: 320px;
30
+ min-height: 100vh;
31
+ }
32
+
33
+ h1 {
34
+ font-size: 3.2em;
35
+ line-height: 1.1;
36
+ }
37
+
38
+ #app {
39
+ max-width: 1280px;
40
+ margin: 0 auto;
41
+ padding: 2rem;
42
+ text-align: center;
43
+ }
44
+
45
+ .logo {
46
+ height: 6em;
47
+ padding: 1.5em;
48
+ will-change: filter;
49
+ transition: filter 300ms;
50
+ }
51
+ .logo:hover {
52
+ filter: drop-shadow(0 0 2em #646cffaa);
53
+ }
54
+ .logo.vanilla:hover {
55
+ filter: drop-shadow(0 0 2em #f7df1eaa);
56
+ }
57
+
58
+ .card {
59
+ padding: 2em;
60
+ }
61
+
62
+ .read-the-docs {
63
+ color: #888;
64
+ }
65
+
66
+ button {
67
+ border-radius: 8px;
68
+ border: 1px solid transparent;
69
+ padding: 0.6em 1.2em;
70
+ font-size: 1em;
71
+ font-weight: 500;
72
+ font-family: inherit;
73
+ background-color: #1a1a1a;
74
+ cursor: pointer;
75
+ transition: border-color 0.25s;
76
+ }
77
+ button:hover {
78
+ border-color: #646cff;
79
+ }
80
+ button:focus,
81
+ button:focus-visible {
82
+ outline: 4px auto -webkit-focus-ring-color;
83
+ }
84
+
85
+ @media (prefers-color-scheme: light) {
86
+ :root {
87
+ color: #213547;
88
+ background-color: #ffffff;
89
+ }
90
+ a:hover {
91
+ color: #747bff;
92
+ }
93
+ button {
94
+ background-color: #f9f9f9;
95
+ }
96
+ }
@@ -0,0 +1,125 @@
1
+ import { useState } from 'preact/hooks';
2
+ import { submitAccountForm } from '../api';
3
+
4
+ const initialFormData = {
5
+ agent: '',
6
+ requestId: '',
7
+ remark: '',
8
+ };
9
+
10
+ export default function AccountModal({ visible, onClose, onSubmit }) {
11
+ const [formData, setFormData] = useState(initialFormData);
12
+ const [loading, setLoading] = useState(false);
13
+
14
+ // 每次弹窗打开时重置表单
15
+ if (!visible) {
16
+ if (formData.agent !== '' || formData.requestId !== '' || formData.remark !== '') {
17
+ setFormData(initialFormData);
18
+ }
19
+ return null;
20
+ }
21
+
22
+ const handleChange = (e) => {
23
+ setFormData({ ...formData, [e.target.name]: e.target.value });
24
+ };
25
+
26
+ const handleSubmit = async (e) => {
27
+ e.preventDefault();
28
+ setLoading(true);
29
+ try {
30
+ const token = window.localStorage.getItem('token') || '';
31
+ await submitAccountForm(formData, token);
32
+ onSubmit(formData);
33
+ onClose();
34
+ } catch (err) {
35
+ alert('提交失败');
36
+ } finally {
37
+ setLoading(false);
38
+ }
39
+ };
40
+
41
+ // 点击遮罩关闭弹窗
42
+ const handleMaskClick = (e) => {
43
+ if (e.target === e.currentTarget) {
44
+ onClose();
45
+ }
46
+ };
47
+
48
+ return (
49
+ <div
50
+ style={{
51
+ position: 'fixed', top: 0, left: 0, width: '100vw', height: '100vh',
52
+ background: 'rgba(0,0,0,0.15)', display: 'flex', alignItems: 'center', justifyContent: 'center',
53
+ zIndex: 1000
54
+ }}
55
+ onClick={handleMaskClick}
56
+ >
57
+ <div
58
+ style={{ background: '#fff', padding: '32px 32px 16px 32px', borderRadius: '8px', minWidth: '480px', boxShadow: '0 2px 8px rgba(0,0,0,0.15)', position: 'relative' }}
59
+ onClick={e => e.stopPropagation()}
60
+ >
61
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24 }}>
62
+ <span style={{ fontSize: 20, fontWeight: 600 }}>新建开户申请</span>
63
+ <button onClick={onClose} style={{ background: 'none', border: 'none', fontSize: 22, cursor: 'pointer', color: '#999' }} title="关闭">×</button>
64
+ </div>
65
+ <form onSubmit={handleSubmit} style={{ fontSize: 15, color: '#222', textAlign: 'left' }}>
66
+ <div style={{ border: '1px solid #f0f0f0', borderRadius: 6, padding: '20px 24px 8px 24px', marginBottom: 24 }}>
67
+ <div style={{ marginBottom: 20 }}>
68
+ <label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>
69
+ <span style={{ color: '#f5222d', marginRight: 4 }}>*</span>代理商
70
+ </label>
71
+ <select
72
+ name="agent"
73
+ value={formData.agent}
74
+ onChange={handleChange}
75
+ disabled
76
+ style={{ width: '100%', padding: '8px', borderRadius: 4, border: '1px solid #d9d9d9', background: '#f5f5f5', color: '#aaa' }}
77
+ >
78
+ <option value="">请选择代理商</option>
79
+ </select>
80
+ </div>
81
+ <div style={{ marginBottom: 20 }}>
82
+ <label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>
83
+ <span style={{ color: '#f5222d', marginRight: 4 }}>*</span>Request ID
84
+ </label>
85
+ <input
86
+ type="text"
87
+ name="requestId"
88
+ value={formData.requestId}
89
+ onChange={handleChange}
90
+ placeholder="请输入Request ID"
91
+ required
92
+ style={{ width: '100%', padding: '8px', borderRadius: 4, border: '1px solid #d9d9d9' }}
93
+ />
94
+ </div>
95
+ <div style={{ marginBottom: 20 }}>
96
+ <label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>备注信息</label>
97
+ <textarea
98
+ name="remark"
99
+ value={formData.remark}
100
+ onChange={handleChange}
101
+ placeholder="请输入备注信息"
102
+ rows={4}
103
+ style={{ width: '100%', padding: '8px', borderRadius: 4, border: '1px solid #1677ff', resize: 'none' }}
104
+ maxLength={200}
105
+ />
106
+ <div style={{ textAlign: 'right', color: '#aaa', fontSize: 12 }}>{formData.remark.length}/200</div>
107
+ </div>
108
+ </div>
109
+ <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 12, marginTop: 8 }}>
110
+ <button type="button" onClick={onClose} style={{ padding: '6px 18px', border: 'none', background: '#f5f5f5', color: '#222', borderRadius: 4, fontSize: 15, cursor: 'pointer' }} disabled={loading}>
111
+ 取消
112
+ </button>
113
+ <button type="submit" style={{ padding: '6px 18px', border: 'none', background: '#1677ff', color: '#fff', borderRadius: 8, fontSize: 16, fontWeight: 600, cursor: 'pointer', boxShadow: '0 1px 2px rgba(22,119,255,0.08)', transition: 'background 0.2s' }}
114
+ onMouseOver={e => (e.currentTarget.style.background = '#145dde')}
115
+ onMouseOut={e => (e.currentTarget.style.background = '#1677ff')}
116
+ disabled={loading}
117
+ >
118
+ {loading ? '提交中...' : '确认'}
119
+ </button>
120
+ </div>
121
+ </form>
122
+ </div>
123
+ </div>
124
+ );
125
+ }
@@ -0,0 +1,53 @@
1
+ import { useState } from 'preact/hooks';
2
+ import AccountModal from './AccountModal.jsx';
3
+
4
+ export default function Home() {
5
+ const [showModal, setShowModal] = useState(false);
6
+ const [submitted, setSubmitted] = useState(false);
7
+ const [lastRequestId, setLastRequestId] = useState('');
8
+
9
+ const handleOpen = () => {
10
+ setShowModal(true);
11
+ setSubmitted(false);
12
+ };
13
+
14
+ const handleClose = () => {
15
+ setShowModal(false);
16
+ };
17
+
18
+ const handleSubmit = (formData) => {
19
+ setSubmitted(true);
20
+ setLastRequestId(formData.requestId);
21
+ };
22
+
23
+ return (
24
+ <div style={{ textAlign: 'center', marginTop: '2rem', fontFamily: 'Arial, sans-serif' }}>
25
+ <button onClick={handleOpen} style={{
26
+ padding: '6px 18px',
27
+ border: 'none',
28
+ background: '#1677ff',
29
+ color: '#fff',
30
+ borderRadius: 8,
31
+ fontSize: 16,
32
+ fontWeight: 600,
33
+ cursor: 'pointer',
34
+ boxShadow: '0 1px 2px rgba(22,119,255,0.08)',
35
+ transition: 'background 0.2s',
36
+ }}
37
+ onMouseOver={e => (e.currentTarget.style.background = '#145dde')}
38
+ onMouseOut={e => (e.currentTarget.style.background = '#1677ff')}>
39
+ 新建开户申请
40
+ </button>
41
+ <AccountModal
42
+ visible={showModal}
43
+ onClose={handleClose}
44
+ onSubmit={handleSubmit}
45
+ />
46
+ {submitted && (
47
+ <div style={{ marginTop: '1rem', color: 'green' }}>
48
+ 提交成功!Request ID:{lastRequestId}
49
+ </div>
50
+ )}
51
+ </div>
52
+ );
53
+ }
package/vite.config.js ADDED
@@ -0,0 +1,31 @@
1
+ import { defineConfig } from 'vite';
2
+ import preact from '@preact/preset-vite';
3
+
4
+ export default defineConfig({
5
+ plugins: [preact()],
6
+ server: {
7
+ proxy: {
8
+ '/api': {
9
+ target: 'http://localhost:3000',
10
+ changeOrigin: true,
11
+ },
12
+ },
13
+ },
14
+ build: {
15
+ lib: {
16
+ entry: 'src/sdk/index.ts', // 你的 SDK 入口
17
+ name: 'BestUnit',
18
+ fileName: 'best-unit',
19
+ formats: ['umd', 'es'],
20
+ },
21
+ rollupOptions: {
22
+ // 保证 preact 作为外部依赖或内联
23
+ external: ['preact'],
24
+ output: {
25
+ globals: {
26
+ preact: 'preact'
27
+ }
28
+ }
29
+ }
30
+ }
31
+ });