@baishuyun/ui-business 2.0.6 → 3.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.
package/README.md CHANGED
@@ -67,4 +67,191 @@ src/
67
67
 
68
68
  ## 许可证
69
69
 
70
- MIT
70
+ MIT
71
+
72
+ ## API Service 配置指南
73
+
74
+ 本组件库采用依赖注入架构,**要求业务项目传入自己的 Axios 实例**。这种设计确保了:
75
+
76
+ - 🔒 **统一认证体系**:组件库请求使用业务项目的认证逻辑
77
+ - 🎯 **完全请求控制**:业务项目可以感知和控制所有请求
78
+ - 🛡️ **统一错误处理**:所有请求使用相同的错误处理逻辑
79
+ - 📊 **统一状态管理**:Loading 状态、请求监控等统一管理
80
+
81
+ ## 基本用法
82
+
83
+ ### 1. 创建业务 Axios 实例
84
+
85
+ ```typescript
86
+ // src/api/request.ts
87
+ import axios from 'axios';
88
+ import { message } from 'antd';
89
+
90
+ const request = axios.create({
91
+ baseURL: process.env.REACT_APP_API_BASE_URL,
92
+ timeout: 10000,
93
+ withCredentials: true,
94
+ });
95
+
96
+ // 请求拦截器
97
+ request.interceptors.request.use(
98
+ (config) => {
99
+ // 添加认证 Token
100
+ const token = localStorage.getItem('access_token');
101
+ if (token) {
102
+ config.headers.Authorization = `Bearer ${token}`;
103
+ }
104
+ return config;
105
+ },
106
+ (error) => Promise.reject(error)
107
+ );
108
+
109
+ // 响应拦截器
110
+ request.interceptors.response.use(
111
+ (response) => response,
112
+ (error) => {
113
+ // 统一错误处理
114
+ if (error.response?.status === 401) {
115
+ message.error('登录已过期,请重新登录');
116
+ // 跳转到登录页
117
+ window.location.href = '/login';
118
+ } else {
119
+ message.error(error.response?.data?.message || '请求失败');
120
+ }
121
+ return Promise.reject(error);
122
+ }
123
+ );
124
+
125
+ export default request;
126
+ ```
127
+
128
+ ### 2. 注入到组件库
129
+
130
+ ```typescript
131
+ // src/main.ts 或 src/App.tsx
132
+ import { setApiServiceProvider } from '@baishuyun/ui-business';
133
+ import request from './api/request';
134
+
135
+ // 注入业务项目的 Axios 实例
136
+ setApiServiceProvider({
137
+ axiosInstance: request,
138
+ });
139
+ ```
140
+
141
+ ## 高级配置
142
+
143
+ ### 多环境配置
144
+
145
+ ```typescript
146
+ // src/api/config.ts
147
+ const API_CONFIG = {
148
+ development: {
149
+ baseURL: 'http://localhost:3000/api',
150
+ timeout: 10000,
151
+ },
152
+ production: {
153
+ baseURL: 'https://api.example.com',
154
+ timeout: 5000,
155
+ },
156
+ };
157
+
158
+ const config = API_CONFIG[process.env.NODE_ENV as keyof typeof API_CONFIG];
159
+
160
+ const request = axios.create(config);
161
+ ```
162
+
163
+ ### 请求重试机制
164
+
165
+ ```typescript
166
+ // 添加请求重试逻辑
167
+ request.interceptors.response.use(
168
+ (response) => response,
169
+ async (error) => {
170
+ const config = error.config;
171
+
172
+ // 重试逻辑
173
+ if (!config._retry && error.response?.status >= 500) {
174
+ config._retry = true;
175
+ config._retryCount = (config._retryCount || 0) + 1;
176
+
177
+ if (config._retryCount <= 3) {
178
+ await new Promise(resolve => setTimeout(resolve, 1000));
179
+ return request(config);
180
+ }
181
+ }
182
+
183
+ return Promise.reject(error);
184
+ }
185
+ );
186
+ ```
187
+
188
+ ### Loading 状态管理
189
+
190
+ ```typescript
191
+ // src/api/loading.ts
192
+ let loadingCount = 0;
193
+
194
+ const showLoading = () => {
195
+ if (loadingCount === 0) {
196
+ // 显示全局 loading
197
+ }
198
+ loadingCount++;
199
+ };
200
+
201
+ const hideLoading = () => {
202
+ loadingCount--;
203
+ if (loadingCount === 0) {
204
+ // 隐藏全局 loading
205
+ }
206
+ };
207
+
208
+ // 在拦截器中使用
209
+ request.interceptors.request.use((config) => {
210
+ showLoading();
211
+ return config;
212
+ });
213
+
214
+ request.interceptors.response.use(
215
+ (response) => {
216
+ hideLoading();
217
+ return response;
218
+ },
219
+ (error) => {
220
+ hideLoading();
221
+ return Promise.reject(error);
222
+ }
223
+ );
224
+ ```
225
+
226
+ ## 动态配置
227
+
228
+ 可以在运行时动态更换 Axios 实例:
229
+
230
+ ```typescript
231
+ // 切换到不同的 API 环境
232
+ const switchToTestEnv = () => {
233
+ const testRequest = axios.create({
234
+ baseURL: 'https://test-api.example.com',
235
+ // 其他配置...
236
+ });
237
+
238
+ setApiServiceProvider({ axiosInstance: testRequest });
239
+ };
240
+ ```
241
+
242
+ ## 注意事项
243
+
244
+ - ⚠️ **必须在使用组件库之前调用** `setApiServiceProvider`
245
+ - 📍 **推荐在应用入口文件**(如 `main.ts` 或 `App.tsx`)中进行配置
246
+ - 🔧 **Axios 实例应包含完整业务逻辑**(拦截器、错误处理、认证等)
247
+ - 🚫 **组件库不再提供内置的 HTTP 配置**,完全依赖外部注入
248
+
249
+ ## 错误处理
250
+
251
+ 如果未设置 API 服务提供者,组件库会抛出明确的错误提示:
252
+
253
+ ```
254
+ Error: API 服务提供者未设置。请先调用 setApiServiceProvider() 设置 Axios 实例。
255
+ ```
256
+
257
+ 确保在使用任何组件库功能之前完成配置。
@@ -10,12 +10,15 @@ import { S } from "../../../../vendors/simplebar-react.js";
10
10
  import { Tree as w, Icon as C, Checkbox as O } from "bsy-react-ui";
11
11
  import { g as J } from "../../../../vendors/services/corp/outsider.service.js";
12
12
  const Q = (t) => {
13
- const { type: r, containerHeight: p, value: o } = N(), { t: a } = k(), f = o?.map((d) => d._id) || [], i = t.data?.map((d) => d._id);
13
+ const { type: r, containerHeight: p, value: o } = N(), { t: a } = k(), f = o?.map((d) => d._id) || [], s = t.data?.map((d) => d._id);
14
14
  return /* @__PURE__ */ g(
15
15
  S,
16
16
  {
17
17
  style: { height: p - 8, padding: "4px" },
18
18
  className: "internal-container",
19
+ classNames: {
20
+ contentEl: "internal-container-content-el"
21
+ },
19
22
  children: [
20
23
  t.data.length > 0 && /* @__PURE__ */ e(
21
24
  w,
@@ -31,7 +34,7 @@ const Q = (t) => {
31
34
  selectedKeys: f,
32
35
  onSelect: t.onSelect,
33
36
  onDeSelect: t.onDeselect,
34
- defaultExpandedKeys: i,
37
+ defaultExpandedKeys: s,
35
38
  icon: () => /* @__PURE__ */ e(C, { name: "dept-one", className: "internal-container__icon" }),
36
39
  switcherIcon: (d) => /* @__PURE__ */ e(
37
40
  C,
@@ -49,7 +52,7 @@ const Q = (t) => {
49
52
  }
50
53
  );
51
54
  }, V = (t) => {
52
- const { type: r, containerHeight: p, value: o } = N(), { t: a } = k(), [f, i] = D(
55
+ const { type: r, containerHeight: p, value: o } = N(), { t: a } = k(), [f, s] = D(
53
56
  "dockcorp"
54
57
  /* DOCK_PERSON_GROUP */
55
58
  ), d = o?.map((n) => n._id) || [], b = [
@@ -63,7 +66,7 @@ const Q = (t) => {
63
66
  value: "dockcorpdept"
64
67
  /* DOCK_DEPARTMENT */
65
68
  }
66
- ], s = (n, u) => {
69
+ ], i = (n, u) => {
67
70
  const m = [];
68
71
  for (const c of n)
69
72
  if (c.type === u)
@@ -73,56 +76,65 @@ const Q = (t) => {
73
76
  children: c.children
74
77
  });
75
78
  else {
76
- const l = c.children ? s(c.children, u) : [];
79
+ const l = c.children ? i(c.children, u) : [];
77
80
  m.push({
78
81
  ...c,
79
82
  children: l.length > 0 ? l : void 0
80
83
  });
81
84
  }
82
85
  else {
83
- const l = c.children ? s(c.children, u) : [];
86
+ const l = c.children ? i(c.children, u) : [];
84
87
  l.length > 0 && m.push({
85
88
  ...c,
86
89
  children: l
87
90
  });
88
91
  }
89
92
  return m;
90
- }, x = s(t.data, f), _ = x?.map((n) => n._id);
93
+ }, x = i(t.data, f), _ = x?.map((n) => n._id);
91
94
  return /* @__PURE__ */ g("div", { className: "external-container", children: [
92
- /* @__PURE__ */ e(L, { tabs: b, value: f, onChange: i }),
93
- /* @__PURE__ */ e("div", { className: "external-container__content", children: /* @__PURE__ */ g(S, { style: { height: p - 42, padding: "4px" }, children: [
94
- x.length > 0 && /* @__PURE__ */ e(
95
- w,
96
- {
97
- parentCheckable: !0,
98
- checkType: r === E.DEPARMENT ? "radio" : "checkbox",
99
- data: x,
100
- fieldNames: {
101
- title: "name",
102
- value: "_id",
103
- children: "children"
104
- },
105
- selectedKeys: d,
106
- onSelect: t.onSelect,
107
- onDeSelect: t.onDeselect,
108
- defaultExpandedKeys: _,
109
- icon: () => /* @__PURE__ */ e(C, { name: "dept-one", className: "text-[#0265ff]", size: 16 }),
110
- switcherIcon: (n) => /* @__PURE__ */ e(
111
- C,
95
+ /* @__PURE__ */ e(L, { tabs: b, value: f, onChange: s }),
96
+ /* @__PURE__ */ e("div", { className: "external-container__content", children: /* @__PURE__ */ g(
97
+ S,
98
+ {
99
+ style: { height: p - 42, padding: "4px" },
100
+ classNames: {
101
+ contentEl: "external-container-content-el"
102
+ },
103
+ children: [
104
+ x.length > 0 && /* @__PURE__ */ e(
105
+ w,
112
106
  {
113
- name: "xiangxiazhankai-11",
114
- className: v(
115
- "transition-transform text-base duration-300 ease-in-out text-[#666]",
107
+ parentCheckable: !0,
108
+ checkType: r === E.DEPARMENT ? "radio" : "checkbox",
109
+ data: x,
110
+ fieldNames: {
111
+ title: "name",
112
+ value: "_id",
113
+ children: "children"
114
+ },
115
+ selectedKeys: d,
116
+ onSelect: t.onSelect,
117
+ onDeSelect: t.onDeselect,
118
+ defaultExpandedKeys: _,
119
+ icon: () => /* @__PURE__ */ e(C, { name: "dept-one", className: "text-[#0265ff]", size: 16 }),
120
+ switcherIcon: (n) => /* @__PURE__ */ e(
121
+ C,
116
122
  {
117
- "rotate-90": n
123
+ name: "xiangxiazhankai-11",
124
+ className: v(
125
+ "transition-transform text-base duration-300 ease-in-out text-[#666]",
126
+ {
127
+ "rotate-90": n
128
+ }
129
+ )
118
130
  }
119
131
  )
120
132
  }
121
- )
122
- }
123
- ),
124
- x.length === 0 && !t.loading && /* @__PURE__ */ e("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ e("span", { className: "text-[#999]", children: a("department.noAvailable") }) })
125
- ] }) })
133
+ ),
134
+ x.length === 0 && !t.loading && /* @__PURE__ */ e("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ e("span", { className: "text-[#999]", children: a("department.noAvailable") }) })
135
+ ]
136
+ }
137
+ ) })
126
138
  ] });
127
139
  }, W = () => {
128
140
  const { type: t, dispatch: r } = N(), { t: p } = k(), [o] = D([
@@ -131,7 +143,7 @@ const Q = (t) => {
131
143
  ]), [a, f] = D(
132
144
  0
133
145
  /* INTERNAL */
134
- ), { data: i, loading: d } = T(
146
+ ), { data: s, loading: d } = T(
135
147
  () => j({
136
148
  departmentLimit: [],
137
149
  hasDeptDock: a === 1
@@ -141,10 +153,10 @@ const Q = (t) => {
141
153
  refreshDeps: [a]
142
154
  }
143
155
  ), b = G(() => {
144
- const l = i?.departments || [];
156
+ const l = s?.departments || [];
145
157
  return l.length > 0 ? B(l) : [];
146
- }, [i]), s = G(() => {
147
- const l = i?.label_corp_list || [];
158
+ }, [s]), i = G(() => {
159
+ const l = s?.label_corp_list || [];
148
160
  if (!l.length)
149
161
  return [];
150
162
  const y = l.filter((h) => h.type === "dockcorp" || h.type === "dockdept" ? !0 : h.parentId === 0 ? !1 : l.some((z) => z.parentId === h.departmentId)), I = y.map((h) => ({
@@ -158,12 +170,12 @@ const Q = (t) => {
158
170
  value: "_id",
159
171
  title: "name"
160
172
  }) : [];
161
- }, [i]), x = {
173
+ }, [s]), x = {
162
174
  0: b,
163
- 1: s
175
+ 1: i
164
176
  }, _ = M(
165
177
  (l) => {
166
- const y = a === 1 ? i?.label_corp_list : i?.departments;
178
+ const y = a === 1 ? s?.label_corp_list : s?.departments;
167
179
  if (!y)
168
180
  return;
169
181
  const I = y.find((h) => h._id === l);
@@ -174,7 +186,7 @@ const Q = (t) => {
174
186
  }
175
187
  });
176
188
  },
177
- [r, i, a]
189
+ [r, s, a]
178
190
  ), n = M(
179
191
  (l, y) => {
180
192
  r({
@@ -194,7 +206,7 @@ const Q = (t) => {
194
206
  type: "deleteOne",
195
207
  payload: y._id
196
208
  });
197
- }, m = E.DEPARMENT === t ? _ : n, c = i?.label_corp_count || 0;
209
+ }, m = E.DEPARMENT === t ? _ : n, c = s?.label_corp_count || 0;
198
210
  return /* @__PURE__ */ g("div", { className: "flex line-height-normal h-full", children: [
199
211
  c > 0 && /* @__PURE__ */ e("aside", { className: "basis-[200px] p-1 shrink-0", children: /* @__PURE__ */ e("ul", { className: "flex flex-col", children: o.filter((l) => l.visible).map((l) => /* @__PURE__ */ e(
200
212
  "li",
@@ -238,7 +250,7 @@ const Q = (t) => {
238
250
  )
239
251
  ] });
240
252
  }, F = (t) => {
241
- const { list: r } = t, { value: p, type: o, dispatch: a } = N(), f = o === E.DEPARMENT, i = o === E.DEPARMENT_GROUP, d = p?.[0]?._id, b = (n) => {
253
+ const { list: r } = t, { value: p, type: o, dispatch: a } = N(), f = o === E.DEPARMENT, s = o === E.DEPARMENT_GROUP, d = p?.[0]?._id, b = (n) => {
242
254
  const u = r?.find((m) => m._id === n.target.value);
243
255
  u && a({
244
256
  type: "insertOnly",
@@ -246,7 +258,7 @@ const Q = (t) => {
246
258
  type: t.type || u.type
247
259
  })
248
260
  });
249
- }, s = (n) => {
261
+ }, i = (n) => {
250
262
  a({
251
263
  type: "deleteOne",
252
264
  payload: n
@@ -276,7 +288,7 @@ const Q = (t) => {
276
288
  value: n._id,
277
289
  className: "px-[10px] py-[6px]",
278
290
  onClick: () => {
279
- s(n._id);
291
+ i(n._id);
280
292
  },
281
293
  children: /* @__PURE__ */ e("div", { className: "flex gap-[6px] items-center", children: /* @__PURE__ */ e("span", { className: "text-sm select-none", children: n.name }) })
282
294
  }
@@ -284,7 +296,7 @@ const Q = (t) => {
284
296
  },
285
297
  n._id
286
298
  )) }),
287
- i && /* @__PURE__ */ e(K, { children: /* @__PURE__ */ e(O.Group, { value: x, onChange: _, children: r.map((n) => /* @__PURE__ */ e(
299
+ s && /* @__PURE__ */ e(K, { children: /* @__PURE__ */ e(O.Group, { value: x, onChange: _, children: r.map((n) => /* @__PURE__ */ e(
288
300
  "li",
289
301
  {
290
302
  className: v("rounded-[4px] cursor-pointer font-0 px-[10px]", {
@@ -309,25 +321,25 @@ const Q = (t) => {
309
321
  const { containerHeight: t } = N(), { t: r } = k(), [p] = D([
310
322
  { key: "default", label: r("externalContact.defaultGroup") },
311
323
  { key: "custom", label: r("externalContact.customGroup") }
312
- ]), [o, a] = D("default"), { data: f, loading: i } = T(() => J()), d = f?.list || [], b = o === "default" ? d.filter((s) => s.type === "1") : d.filter((s) => s.type !== "1");
324
+ ]), [o, a] = D("default"), { data: f, loading: s } = T(() => J()), d = f?.list || [], b = o === "default" ? d.filter((i) => i.type === "1") : d.filter((i) => i.type !== "1");
313
325
  return /* @__PURE__ */ g("div", { className: "external-contact-entity relative flex", children: [
314
- /* @__PURE__ */ e("aside", { className: "basis-[192px] p-1", children: /* @__PURE__ */ e("ul", { className: "flex flex-col", children: p.map((s) => /* @__PURE__ */ e(
326
+ /* @__PURE__ */ e("aside", { className: "basis-[192px] p-1", children: /* @__PURE__ */ e("ul", { className: "flex flex-col", children: p.map((i) => /* @__PURE__ */ e(
315
327
  "li",
316
328
  {
317
329
  onClick: () => {
318
- a(s.key);
330
+ a(i.key);
319
331
  },
320
332
  className: v("px-[10px] py-[7px] rounded cursor-pointer text-sm", {
321
- "bg-[#F0F6FF] text-[#0265ff]": o === s.key,
322
- "text-black hover:bg-[#f3f3f3]": o !== s.key
333
+ "bg-[#F0F6FF] text-[#0265ff]": o === i.key,
334
+ "text-black hover:bg-[#f3f3f3]": o !== i.key
323
335
  }),
324
- children: s.label
336
+ children: i.label
325
337
  },
326
- s.key
338
+ i.key
327
339
  )) }) }),
328
- /* @__PURE__ */ e("div", { className: "flex-1 border-l border-[#E0E0E0]", children: /* @__PURE__ */ e(A, { spinning: i, tip: r("common.loading"), children: /* @__PURE__ */ g(S, { style: { height: t - 8, padding: "4px" }, children: [
340
+ /* @__PURE__ */ e("div", { className: "flex-1 border-l border-[#E0E0E0]", children: /* @__PURE__ */ e(A, { spinning: s, tip: r("common.loading"), children: /* @__PURE__ */ g(S, { style: { height: t - 8, padding: "4px" }, children: [
329
341
  b.length > 0 && /* @__PURE__ */ e(F, { list: b, type: "group" }),
330
- b.length === 0 && !i && /* @__PURE__ */ e("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ e("span", { className: "text-[#999]", children: r("externalContact.noAvailableGroup") }) })
342
+ b.length === 0 && !s && /* @__PURE__ */ e("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ e("span", { className: "text-[#999]", children: r("externalContact.noAvailableGroup") }) })
331
343
  ] }) }) })
332
344
  ] });
333
345
  }, oe = () => {