@owocow/saber-ui 0.0.4 → 0.0.6

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.
@@ -27,7 +27,7 @@ export interface TableConfig<A extends ApiFn, T = any> {
27
27
  apiParams?: Partial<ApiParams<A>>;
28
28
  /** 转换已经附带 _index 的行数据。 */
29
29
  transformer?: (rows: TableDataWithIndex<T>[]) => any[];
30
- /** 自定义响应归一化;默认兼容顶层与 data 内的 rows/total */
30
+ /** 自定义响应归一化;默认解析 `data.rows / data.total`。 */
31
31
  responseAdapter?: (response: Awaited<ReturnType<A>>) => NormalizedTableResponse<T>;
32
32
  /** 获取并转换数据后的回调。 */
33
33
  onFetched?: (data: any[]) => MaybePromise<void>;
@@ -1,78 +1,78 @@
1
- import { reactive as y, ref as N, watch as z, computed as R } from "vue";
2
- import { klona as h } from "klona/json";
1
+ import { reactive as z, ref as N, watch as h, computed as R } from "vue";
2
+ import { klona as b } from "klona/json";
3
3
  import { useBoolean as C } from "./use-boolean.mjs";
4
4
  import { useLoading as D } from "./use-loading.mjs";
5
- function I(a) {
6
- const r = Array.isArray(a == null ? void 0 : a.rows) ? a : a == null ? void 0 : a.data;
5
+ function I(n) {
6
+ const e = n == null ? void 0 : n.data;
7
7
  return {
8
- rows: Array.isArray(r == null ? void 0 : r.rows) ? r.rows : [],
9
- total: Number((r == null ? void 0 : r.total) ?? 0)
8
+ rows: Array.isArray(e == null ? void 0 : e.rows) ? e.rows : [],
9
+ total: Number((e == null ? void 0 : e.total) ?? 0)
10
10
  };
11
11
  }
12
- function H(a) {
13
- const { loading: r, startLoading: d, endLoading: g } = D(), { bool: b, setBool: w } = C(), { apiFn: A, apiParams: f, transformer: p, responseAdapter: P, immediate: j = !0, paginConfig: O } = a, i = y(h({ ...f })), S = N([]), { pageSizes: k, pageSize: v, disabled: x = !1 } = O || {}, t = y({
14
- pageSizes: k || [10, 20, 50, 100],
15
- pageSize: v || 10,
12
+ function H(n) {
13
+ const { loading: e, startLoading: d, endLoading: g } = D(), { bool: y, setBool: w } = C(), { apiFn: j, apiParams: f, transformer: p, responseAdapter: P, immediate: O = !0, paginConfig: k } = n, s = z(b({ ...f })), S = N([]), { pageSizes: v, pageSize: x, disabled: A = !1 } = k || {}, t = z({
14
+ pageSizes: v || [10, 20, 50, 100],
15
+ pageSize: x || 10,
16
16
  currentPage: 1,
17
17
  total: 0
18
18
  });
19
19
  let u = !1;
20
- z(
20
+ h(
21
21
  () => t.currentPage,
22
22
  () => {
23
- u || s();
23
+ u || i();
24
24
  },
25
25
  { flush: "sync" }
26
- ), z(
26
+ ), h(
27
27
  () => t.pageSize,
28
28
  () => {
29
- u = !0, t.currentPage = 1, u = !1, s();
29
+ u = !0, t.currentPage = 1, u = !1, i();
30
30
  },
31
31
  { flush: "sync" }
32
32
  );
33
- function E(e) {
33
+ function E(a) {
34
34
  const o = t.currentPage || 1, c = t.pageSize > 0 ? t.pageSize : 10;
35
- return e.map((m, n) => ({
35
+ return a.map((m, r) => ({
36
36
  ...m,
37
- _index: (o - 1) * c + n + 1
37
+ _index: (o - 1) * c + r + 1
38
38
  }));
39
39
  }
40
- async function s() {
41
- var e;
40
+ async function i() {
41
+ var a;
42
42
  d();
43
43
  try {
44
- const o = T(i), c = await A(o), m = P ? P(c) : I(c), n = E(m.rows), l = p ? p(n) : n;
45
- return t.total = m.total, S.value = l, w(l.length === 0), await ((e = a.onFetched) == null ? void 0 : e.call(a, l)), l;
44
+ const o = T(s), c = await j(o), m = P ? P(c) : I(c), r = E(m.rows), l = p ? p(r) : r;
45
+ return t.total = m.total, S.value = l, w(l.length === 0), await ((a = n.onFetched) == null ? void 0 : a.call(n, l)), l;
46
46
  } finally {
47
47
  g();
48
48
  }
49
49
  }
50
50
  function L() {
51
- return x ? {} : {
51
+ return A ? {} : {
52
52
  pageNum: t.currentPage,
53
53
  pageSize: t.pageSize
54
54
  };
55
55
  }
56
- function T(e) {
57
- const o = {}, c = { ...L(), ...e };
58
- return Object.entries(c).forEach(([m, n]) => {
59
- n != null && n !== "" && (o[m] = n);
56
+ function T(a) {
57
+ const o = {}, c = { ...L(), ...a };
58
+ return Object.entries(c).forEach(([m, r]) => {
59
+ r != null && r !== "" && (o[m] = r);
60
60
  }), o;
61
61
  }
62
- function B(e) {
63
- return Object.assign(i, e), s();
62
+ function B(a) {
63
+ return Object.assign(s, a), i();
64
64
  }
65
65
  function F() {
66
66
  u = !0, t.currentPage = 1, u = !1;
67
- const e = h({ ...f });
68
- return Object.keys(i).forEach((o) => delete i[o]), Object.assign(i, e), s();
67
+ const a = b({ ...f });
68
+ return Object.keys(s).forEach((o) => delete s[o]), Object.assign(s, a), i();
69
69
  }
70
- return j && s(), {
71
- loading: r,
72
- empty: b,
70
+ return O && i(), {
71
+ loading: e,
72
+ empty: y,
73
73
  data: S,
74
- getData: s,
75
- params: i,
74
+ getData: i,
75
+ params: s,
76
76
  updateSearchParams: B,
77
77
  resetSearchParams: F,
78
78
  pagination: t,
@@ -4,13 +4,6 @@ export interface ApiResponse<T = unknown> {
4
4
  msg: string;
5
5
  data: T;
6
6
  }
7
- /** rows、total 位于响应顶层的列表响应。 */
8
- export interface ListResponse<T = unknown> {
9
- code: number;
10
- msg: string;
11
- rows: T[];
12
- total: number;
13
- }
14
7
  /** 通用分页请求参数。 */
15
8
  export interface PageParams {
16
9
  pageNum?: number;
@@ -1,3 +1,3 @@
1
1
  export type { EnableStatus, Id, Option, PartialNullable, VisibleStatus, YesNo, } from './common';
2
- export type { ApiResponse, CommonSearchParams, ListResponse, PageData, PageParams, SortOrder, } from './api';
2
+ export type { ApiResponse, CommonSearchParams, PageData, PageParams, SortOrder, } from './api';
3
3
  export type { AuthUserInfo, CaptchaCode, LoginForm, LoginTenant, LoginTenantItem, LoginToken, PwdLoginForm, RegisterForm, SocialLoginForm, } from './auth';
@@ -1,7 +1,12 @@
1
- import { ApiResponse, ListResponse } from '../types/api';
2
- export type { ApiResponse, ListResponse } from '../types/api';
3
- /** @deprecated 请使用 ListResponse。 */
4
- export type ListResult<T = unknown> = ListResponse<T>;
1
+ import { ApiResponse, PageData } from '../types/api';
2
+ export type { ApiResponse, PageData } from '../types/api';
3
+ /**
4
+ * 分页列表响应。
5
+ *
6
+ * 后端分页接口统一返回 `{ code, msg, data: { rows, total } }`,
7
+ * rows/total 位于 `ApiResponse.data` 内。
8
+ */
9
+ export type ListResult<T = unknown> = ApiResponse<PageData<T>>;
5
10
  export interface HttpConfig {
6
11
  /** API 基础路径,如 `/api` */
7
12
  baseURL: string;
@@ -18,6 +23,29 @@ export interface HttpConfig {
18
23
  /** 加密响应头的 key,默认 `'encrypt-key'` */
19
24
  encryptHeaderKey?: string;
20
25
  }
26
+ /** 二进制请求选项,适用于下载、导出以及文件预览。 */
27
+ export interface BlobRequestOptions {
28
+ /** 请求方法,默认 GET。 */
29
+ method?: HttpMethod;
30
+ /** 非 GET 请求体;普通对象会由 ofetch 序列化为 JSON。 */
31
+ data?: BodyInit | Record<string, unknown>;
32
+ /** URL 查询参数。 */
33
+ params?: Record<string, unknown>;
34
+ /** 是否携带 Bearer Token,默认 true。 */
35
+ withToken?: boolean;
36
+ /** 附加或覆盖请求头;显式 Authorization 优先于自动注入的 Token。 */
37
+ headers?: HeadersInit;
38
+ /** 用于主动取消下载或预览请求。 */
39
+ signal?: AbortSignal;
40
+ }
41
+ /** 二进制响应,以及服务端提供的可选文件元数据。 */
42
+ export interface BlobResponse {
43
+ blob: Blob;
44
+ /** 从 Content-Disposition 解析并去除路径片段后的文件名。 */
45
+ fileName?: string;
46
+ /** 响应 Content-Type;响应头缺失时回退到 Blob.type。 */
47
+ contentType?: string;
48
+ }
21
49
  type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
22
50
  interface RequestOptions {
23
51
  method?: HttpMethod;
@@ -34,12 +62,21 @@ interface UploadOptions {
34
62
  }
35
63
  export declare function setupHttp(config: HttpConfig): void;
36
64
  export declare function request<T = unknown>(url: string, options?: RequestOptions): Promise<T>;
65
+ /**
66
+ * 请求二进制内容,但不触发浏览器下载。
67
+ *
68
+ * PDF/图片预览可直接消费返回的 blob;需要保存到本地时再调用 saveBlob()。
69
+ */
70
+ export declare function requestBlob(url: string, options?: BlobRequestOptions): Promise<BlobResponse>;
37
71
  export declare const http: {
38
72
  get<T = unknown, P extends object = object>(url: string, params?: P, withToken?: boolean): Promise<ApiResponse<T>>;
39
- list<T = unknown, P extends object = object>(url: string, params?: P, withToken?: boolean): Promise<ListResponse<T>>;
73
+ list<T = unknown, P extends object = object>(url: string, params?: P, withToken?: boolean): Promise<ListResult<T>>;
40
74
  post<T = unknown, D extends object = object>(url: string, data?: D, withToken?: boolean, encrypt?: boolean): Promise<ApiResponse<T>>;
41
75
  put<T = unknown, D extends object = object>(url: string, data?: D, withToken?: boolean, encrypt?: boolean): Promise<ApiResponse<T>>;
42
76
  patch<T = unknown, D extends object = object>(url: string, data?: D, withToken?: boolean, encrypt?: boolean): Promise<ApiResponse<T>>;
43
77
  delete<T = unknown, D extends object = object>(url: string, data?: D, withToken?: boolean): Promise<ApiResponse<T>>;
78
+ blob(url: string, options?: BlobRequestOptions): Promise<BlobResponse>;
44
79
  upload<T = unknown>(url: string, file: File | File[], options?: UploadOptions): Promise<T>;
45
80
  };
81
+ /** 使用临时 Object URL 将 Blob 保存到本地。 */
82
+ export declare function saveBlob(blob: Blob, fileName: string): void;
@@ -1,147 +1,231 @@
1
1
  import { ofetch as H } from "ofetch";
2
- import { decryptBase64 as L, decryptWithAes as O, generateAesKey as R, encryptBase64 as I, encryptWithAes as K } from "./crypto.mjs";
3
- import { rsaDecrypt as B, rsaEncrypt as U } from "./rsa.mjs";
4
- import { storage as k } from "./storage.mjs";
5
- import { getToast as T } from "./toast.mjs";
6
- let $ = "", S = "", _ = "token", y = null, b = !1, P = "encrypt-key", A = null;
7
- function v() {
8
- if (!A)
2
+ import { decryptBase64 as O, decryptWithAes as P, generateAesKey as N, encryptBase64 as _, encryptWithAes as S } from "./crypto.mjs";
3
+ import { rsaDecrypt as z, rsaEncrypt as G } from "./rsa.mjs";
4
+ import { storage as U } from "./storage.mjs";
5
+ import { getToast as b } from "./toast.mjs";
6
+ let T = "", A = "", v = "token", m = null, R = !1, L = "encrypt-key", B = null, k = null;
7
+ function K() {
8
+ if (!B)
9
9
  throw new Error("[http] 尚未初始化,请先调用 setupHttp({ baseURL, clientId, onAuthError })");
10
- return A;
10
+ return B;
11
11
  }
12
- function C() {
13
- return new Headers({ Clientid: S });
12
+ function q() {
13
+ if (!k)
14
+ throw new Error("[http] 尚未初始化,请先调用 setupHttp({ baseURL, clientId, onAuthError })");
15
+ return k;
16
+ }
17
+ function I(r) {
18
+ const e = new Headers({ Clientid: A });
19
+ return new Headers(r).forEach((t, o) => e.set(o, t)), e;
14
20
  }
15
- function N(r) {
16
- $ = r.baseURL, S = r.clientId, y = r.onAuthError, _ = r.tokenKey ?? "token", b = r.encryptEnabled ?? !1, P = r.encryptHeaderKey ?? "encrypt-key", A = H.create({
17
- baseURL: $,
21
+ function V(r) {
22
+ T = r.baseURL, A = r.clientId, m = r.onAuthError, v = r.tokenKey ?? "token", R = r.encryptEnabled ?? !1, L = r.encryptHeaderKey ?? "encrypt-key", B = H.create({
23
+ baseURL: T,
18
24
  timeout: r.timeout ?? 1e4,
19
- async onResponse({ response: t }) {
20
- const e = b ? t.headers.get(P) : null;
21
- if (e) {
22
- const c = L(String(B(e)));
23
- t._data = JSON.parse(O(String(t._data), c));
24
- } else if (typeof t._data == "string")
25
+ async onResponse({ response: e }) {
26
+ const t = R ? e.headers.get(L) : null;
27
+ if (t) {
28
+ const c = O(String(z(t)));
29
+ e._data = JSON.parse(P(String(e._data), c));
30
+ } else if (typeof e._data == "string")
25
31
  try {
26
- t._data = JSON.parse(t._data);
32
+ e._data = JSON.parse(e._data);
27
33
  } catch {
28
34
  }
29
- const o = t._data;
35
+ const o = e._data;
30
36
  if (!o) return;
31
- const { code: n, msg: i } = o;
37
+ const { code: n, msg: a } = o;
32
38
  if (n === 200) return;
33
- const p = T();
39
+ const l = b();
34
40
  if (n === 401) {
35
- p.add({ title: i, color: "error" }), y == null || y();
41
+ l.add({ title: a, color: "error" }), m == null || m();
36
42
  return;
37
43
  }
38
44
  if (n === 402 || n === 403) {
39
- p.add({ title: i ?? "无权限访问该资源", color: "error" });
45
+ l.add({ title: a ?? "无权限访问该资源", color: "error" });
40
46
  return;
41
47
  }
42
48
  if (n === 404) {
43
- p.add({ title: "后端 API 不存在", color: "error" });
49
+ l.add({ title: "后端 API 不存在", color: "error" });
44
50
  return;
45
51
  }
46
- throw p.add({ title: i ?? `请求失败,错误码:${n}`, color: "error" }), new Error(i ?? `请求失败,错误码:${n}`);
52
+ throw l.add({ title: a ?? `请求失败,错误码:${n}`, color: "error" }), new Error(a ?? `请求失败,错误码:${n}`);
47
53
  },
48
- onResponseError({ response: t }) {
54
+ onResponseError({ response: e }) {
49
55
  var n;
50
- const e = t.status, o = (n = t._data) == null ? void 0 : n.msg;
51
- T().add({
52
- title: `请求错误 ${e}${o ? `:${o}` : ""}`,
56
+ const t = e.status, o = (n = e._data) == null ? void 0 : n.msg;
57
+ b().add({
58
+ title: `请求错误 ${t}${o ? `:${o}` : ""}`,
53
59
  color: "error"
54
60
  });
55
61
  }
62
+ }), k = H.create({
63
+ baseURL: T,
64
+ timeout: r.timeout ?? 1e4
56
65
  });
57
66
  }
58
- function m(r, t = {}) {
59
- const { method: e = "GET", data: o, params: n, withToken: i = !0, encrypt: p = !1 } = t, c = C();
60
- if (i) {
61
- const a = k.get(_);
62
- a && c.set("Authorization", `Bearer ${a}`);
67
+ function E(r, e = {}) {
68
+ const { method: t = "GET", data: o, params: n, withToken: a = !0, encrypt: l = !1 } = e, c = I();
69
+ if (a) {
70
+ const s = U.get(v);
71
+ s && c.set("Authorization", `Bearer ${s}`);
63
72
  }
64
- let f = e !== "GET" ? o : void 0, E;
65
- if (b && p && (e === "POST" || e === "PUT" || e === "PATCH") && f) {
66
- const a = R();
67
- c.set(P, String(U(I(a)))), f = K(typeof f == "object" ? JSON.stringify(f) : String(f), a), E = "text";
73
+ let u = t !== "GET" ? o : void 0, f;
74
+ if (R && l && (t === "POST" || t === "PUT" || t === "PATCH") && u) {
75
+ const s = N();
76
+ c.set(L, String(G(_(s)))), u = S(typeof u == "object" ? JSON.stringify(u) : String(u), s), f = "text";
68
77
  }
69
- return v()(r, {
70
- method: e,
78
+ return K()(r, {
79
+ method: t,
71
80
  headers: c,
72
- body: f,
73
- query: e === "GET" ? { ...n, _t: Date.now() } : void 0,
74
- responseType: E
81
+ body: u,
82
+ query: t === "GET" ? { ...n, _t: Date.now() } : void 0,
83
+ responseType: f
75
84
  });
76
85
  }
77
- const z = {
78
- get(r, t, e = !0) {
79
- return m(r, { method: "GET", params: t, withToken: e });
86
+ function $(r) {
87
+ var o;
88
+ return ((o = r.trim().replace(/^['"]|['"]$/g, "").replace(/[\r\n]/g, "").split(/[\\/]/).pop()) == null ? void 0 : o.trim()) || void 0;
89
+ }
90
+ function C(r) {
91
+ if (!r) return;
92
+ const e = r.match(/filename\*\s*=\s*(?:[^']*'[^']*')?([^;]+)/i);
93
+ if (e != null && e[1]) {
94
+ const o = e[1].trim().replace(/^['"]|['"]$/g, "");
95
+ try {
96
+ return $(decodeURIComponent(o));
97
+ } catch {
98
+ return $(o);
99
+ }
100
+ }
101
+ const t = r.match(/filename\s*=\s*(?:"([^"]*)"|([^;]*))/i);
102
+ return $((t == null ? void 0 : t[1]) ?? (t == null ? void 0 : t[2]) ?? "");
103
+ }
104
+ async function x(r, e) {
105
+ if (!e || !/(?:^|\/)json(?:;|$)|\+json(?:;|$)/i.test(e)) return null;
106
+ try {
107
+ const t = JSON.parse(await r.text());
108
+ if (!t || typeof t != "object") return null;
109
+ const o = t, n = typeof o.code == "number" ? o.code : void 0, a = typeof o.msg == "string" ? o.msg : void 0;
110
+ return n !== void 0 || a !== void 0 ? { code: n, msg: a } : null;
111
+ } catch {
112
+ return null;
113
+ }
114
+ }
115
+ function J(r, e) {
116
+ const t = (e == null ? void 0 : e.code) !== void 0 && e.code !== 200 ? e.code : r;
117
+ let o = e == null ? void 0 : e.msg;
118
+ throw t === 401 ? (o ?? (o = "登录状态已失效"), b().add({ title: o, color: "error" }), m == null || m(), new Error(o)) : (t === 402 || t === 403 ? o ?? (o = "无权限访问该资源") : t === 404 ? o ?? (o = "后端 API 不存在") : o ?? (o = r >= 400 ? `请求错误 ${r}` : `请求失败,错误码:${t}`), b().add({ title: o, color: "error" }), new Error(o));
119
+ }
120
+ async function D(r, e = {}) {
121
+ const { method: t = "GET", data: o, params: n, withToken: a = !0, headers: l, signal: c } = e, u = I(l);
122
+ if (a) {
123
+ const i = U.get(v);
124
+ i && !u.has("Authorization") && u.set("Authorization", `Bearer ${i}`);
125
+ }
126
+ let f;
127
+ try {
128
+ f = await q().raw(r, {
129
+ method: t,
130
+ headers: u,
131
+ body: t === "GET" ? void 0 : o,
132
+ query: t === "GET" ? { ...n, _t: Date.now() } : n,
133
+ responseType: "blob",
134
+ ignoreResponseError: !0,
135
+ signal: c
136
+ });
137
+ } catch (i) {
138
+ throw c != null && c.aborted || b().add({ title: "文件请求失败", color: "error" }), i;
139
+ }
140
+ const h = f._data ?? new Blob(), s = f.headers.get("content-type") || h.type || void 0, d = await x(h, s);
141
+ return (!f.ok || (d == null ? void 0 : d.code) !== void 0 && d.code !== 200) && J(f.status, d), {
142
+ blob: h,
143
+ fileName: C(f.headers.get("content-disposition")),
144
+ contentType: s
145
+ };
146
+ }
147
+ const Y = {
148
+ get(r, e, t = !0) {
149
+ return E(r, { method: "GET", params: e, withToken: t });
150
+ },
151
+ list(r, e, t = !0) {
152
+ return E(r, { method: "GET", params: e, withToken: t });
80
153
  },
81
- list(r, t, e = !0) {
82
- return m(r, { method: "GET", params: t, withToken: e });
154
+ post(r, e, t = !0, o = !1) {
155
+ return E(r, { method: "POST", data: e, withToken: t, encrypt: o });
83
156
  },
84
- post(r, t, e = !0, o = !1) {
85
- return m(r, { method: "POST", data: t, withToken: e, encrypt: o });
157
+ put(r, e, t = !0, o = !1) {
158
+ return E(r, { method: "PUT", data: e, withToken: t, encrypt: o });
86
159
  },
87
- put(r, t, e = !0, o = !1) {
88
- return m(r, { method: "PUT", data: t, withToken: e, encrypt: o });
160
+ patch(r, e, t = !0, o = !1) {
161
+ return E(r, { method: "PATCH", data: e, withToken: t, encrypt: o });
89
162
  },
90
- patch(r, t, e = !0, o = !1) {
91
- return m(r, { method: "PATCH", data: t, withToken: e, encrypt: o });
163
+ delete(r, e, t = !0) {
164
+ return E(r, { method: "DELETE", data: e, withToken: t });
92
165
  },
93
- delete(r, t, e = !0) {
94
- return m(r, { method: "DELETE", data: t, withToken: e });
166
+ blob(r, e = {}) {
167
+ return D(r, e);
95
168
  },
96
- upload(r, t, e = {}) {
97
- const { data: o, field: n = "file", onProgress: i, withToken: p = !0 } = e, c = new FormData();
98
- (Array.isArray(t) ? t : [t]).forEach((a) => c.append(n, a)), o && Object.entries(o).forEach(([a, l]) => c.append(a, l));
99
- const E = $, g = { Clientid: S };
100
- if (p) {
101
- const a = k.get(_);
102
- a && (g.Authorization = `Bearer ${a}`);
169
+ upload(r, e, t = {}) {
170
+ const { data: o, field: n = "file", onProgress: a, withToken: l = !0 } = t, c = new FormData();
171
+ (Array.isArray(e) ? e : [e]).forEach((s) => c.append(n, s)), o && Object.entries(o).forEach(([s, d]) => c.append(s, d));
172
+ const f = T, h = { Clientid: A };
173
+ if (l) {
174
+ const s = U.get(v);
175
+ s && (h.Authorization = `Bearer ${s}`);
103
176
  }
104
- return new Promise((a, l) => {
105
- const s = new XMLHttpRequest();
106
- s.open("POST", `${E}${r}`), Object.entries(g).forEach(([u, d]) => s.setRequestHeader(u, d)), i && s.upload.addEventListener("progress", (u) => {
107
- u.lengthComputable && i(Math.round(u.loaded / u.total * 100));
108
- }), s.addEventListener("load", () => {
109
- if (s.status < 200 || s.status >= 300) {
110
- T().add({ title: `请求错误 ${s.status}`, color: "error" }), l(new Error(`请求错误 ${s.status}`));
177
+ return new Promise((s, d) => {
178
+ const i = new XMLHttpRequest();
179
+ i.open("POST", `${f}${r}`), Object.entries(h).forEach(([y, p]) => i.setRequestHeader(y, p)), a && i.upload.addEventListener("progress", (y) => {
180
+ y.lengthComputable && a(Math.round(y.loaded / y.total * 100));
181
+ }), i.addEventListener("load", () => {
182
+ if (i.status < 200 || i.status >= 300) {
183
+ b().add({ title: `请求错误 ${i.status}`, color: "error" }), d(new Error(`请求错误 ${i.status}`));
111
184
  return;
112
185
  }
113
- let u;
186
+ let y;
114
187
  try {
115
- u = JSON.parse(s.responseText);
188
+ y = JSON.parse(i.responseText);
116
189
  } catch {
117
- l(new Error("响应解析失败"));
190
+ d(new Error("响应解析失败"));
118
191
  return;
119
192
  }
120
- const { code: d, msg: h } = u;
121
- if (d === 200) {
122
- a(u.data);
193
+ const { code: p, msg: w } = y;
194
+ if (p === 200) {
195
+ s(y.data);
123
196
  return;
124
197
  }
125
- const w = T();
126
- if (d === 401) {
127
- w.add({ title: h, color: "error" }), y == null || y(), l(new Error(h ?? `鉴权失败,错误码:${d}`));
198
+ const g = b();
199
+ if (p === 401) {
200
+ g.add({ title: w, color: "error" }), m == null || m(), d(new Error(w ?? `鉴权失败,错误码:${p}`));
128
201
  return;
129
202
  }
130
- if (d === 402 || d === 403) {
131
- w.add({ title: h ?? "无权限访问该资源", color: "error" }), l(new Error(h ?? `无权限,错误码:${d}`));
203
+ if (p === 402 || p === 403) {
204
+ g.add({ title: w ?? "无权限访问该资源", color: "error" }), d(new Error(w ?? `无权限,错误码:${p}`));
132
205
  return;
133
206
  }
134
- if (d === 404) {
135
- w.add({ title: "后端 API 不存在", color: "error" }), l(new Error("后端 API 不存在"));
207
+ if (p === 404) {
208
+ g.add({ title: "后端 API 不存在", color: "error" }), d(new Error("后端 API 不存在"));
136
209
  return;
137
210
  }
138
- w.add({ title: h ?? `上传失败,错误码:${d}`, color: "error" }), l(new Error(h ?? `上传失败,错误码:${d}`));
139
- }), s.addEventListener("error", () => l(new Error("上传请求失败"))), s.send(c);
211
+ g.add({ title: w ?? `上传失败,错误码:${p}`, color: "error" }), d(new Error(w ?? `上传失败,错误码:${p}`));
212
+ }), i.addEventListener("error", () => d(new Error("上传请求失败"))), i.send(c);
140
213
  });
141
214
  }
142
215
  };
216
+ function Z(r, e) {
217
+ var a, l;
218
+ if (typeof document > "u" || typeof ((a = globalThis.URL) == null ? void 0 : a.createObjectURL) != "function")
219
+ throw new Error("[saveBlob] 仅支持浏览器环境");
220
+ const t = $(e);
221
+ if (!t) throw new Error("[saveBlob] fileName 不能为空");
222
+ const o = globalThis.URL.createObjectURL(r), n = document.createElement("a");
223
+ n.href = o, n.download = t, n.rel = "noopener", n.style.display = "none", (l = document.body) == null || l.append(n), n.click(), n.remove(), setTimeout(() => globalThis.URL.revokeObjectURL(o), 4e4);
224
+ }
143
225
  export {
144
- z as http,
145
- m as request,
146
- N as setupHttp
226
+ Y as http,
227
+ E as request,
228
+ D as requestBlob,
229
+ Z as saveBlob,
230
+ V as setupHttp
147
231
  };
@@ -1,41 +1,43 @@
1
- import { canAccess as t, hasPermission as o, hasRole as s, setupPermission as p } from "./permission.mjs";
1
+ import { canAccess as t, hasPermission as o, hasRole as s, setupPermission as a } from "./permission.mjs";
2
2
  import { formatCurrency as m } from "./format.mjs";
3
3
  import { createFilterOptions as c } from "./filter.mjs";
4
4
  import { createTreeFromList as x } from "./tree.mjs";
5
- import { decryptBase64 as y, decryptWithAes as d, encryptBase64 as u, encryptWithAes as h, generateAesKey as A } from "./crypto.mjs";
6
- import { getToast as l, setToast as P, tryGetToast as T } from "./toast.mjs";
7
- import { setupStorage as C, storage as F } from "./storage.mjs";
8
- import { rsaDecrypt as W, rsaEncrypt as j, setupRsa as k } from "./rsa.mjs";
9
- import { http as D, request as E, setupHttp as G } from "./http.mjs";
5
+ import { decryptBase64 as y, decryptWithAes as d, encryptBase64 as u, encryptWithAes as l, generateAesKey as h } from "./crypto.mjs";
6
+ import { getToast as g, setToast as B, tryGetToast as P } from "./toast.mjs";
7
+ import { setupStorage as b, storage as q } from "./storage.mjs";
8
+ import { rsaDecrypt as F, rsaEncrypt as R, setupRsa as W } from "./rsa.mjs";
9
+ import { http as k, request as v, requestBlob as D, saveBlob as E, setupHttp as G } from "./http.mjs";
10
10
  import { klona as K } from "klona/json";
11
11
  import { nanoid as O } from "nanoid";
12
- import { addressParse as b, createAddressParser as v } from "./addressParse.mjs";
12
+ import { addressParse as w, createAddressParser as z } from "./addressParse.mjs";
13
13
  export {
14
- b as addressParse,
14
+ w as addressParse,
15
15
  t as canAccess,
16
- v as createAddressParser,
16
+ z as createAddressParser,
17
17
  c as createFilterOptions,
18
18
  x as createTreeFromList,
19
19
  y as decryptBase64,
20
20
  d as decryptWithAes,
21
21
  u as encryptBase64,
22
- h as encryptWithAes,
22
+ l as encryptWithAes,
23
23
  m as formatCurrency,
24
- A as generateAesKey,
25
- l as getToast,
24
+ h as generateAesKey,
25
+ g as getToast,
26
26
  o as hasPermission,
27
27
  s as hasRole,
28
- D as http,
28
+ k as http,
29
29
  K as jsonClone,
30
30
  O as nanoid,
31
- E as request,
32
- W as rsaDecrypt,
33
- j as rsaEncrypt,
34
- P as setToast,
31
+ v as request,
32
+ D as requestBlob,
33
+ F as rsaDecrypt,
34
+ R as rsaEncrypt,
35
+ E as saveBlob,
36
+ B as setToast,
35
37
  G as setupHttp,
36
- p as setupPermission,
37
- k as setupRsa,
38
- C as setupStorage,
39
- F as storage,
40
- T as tryGetToast
38
+ a as setupPermission,
39
+ W as setupRsa,
40
+ b as setupStorage,
41
+ q as storage,
42
+ P as tryGetToast
41
43
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owocow/saber-ui",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Saber application foundation for Vue 3 projects built on Nuxt UI",
5
5
  "type": "module",
6
6
  "sideEffects": false,