@usethink/shared-frontend 0.1.3 → 0.1.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.
Files changed (42) hide show
  1. package/dist/src/components/basic/excel/index.js +1 -1
  2. package/dist/src/components/core/Modal/src/BasicModal2.vue.js +18 -17
  3. package/dist/src/components/core/Modal/src/BasicModal2.vue.js.map +1 -1
  4. package/dist/src/components/core/schema-form/src/hooks/useFormMethods.js +48 -47
  5. package/dist/src/components/core/schema-form/src/hooks/useFormMethods.js.map +1 -1
  6. package/dist/src/index.js +663 -651
  7. package/dist/src/index.js.map +1 -1
  8. package/dist/src/node_modules/.pnpm/call-bind-apply-helpers@1.0.2/node_modules/call-bind-apply-helpers/actualApply.js +5 -5
  9. package/dist/src/node_modules/.pnpm/call-bind-apply-helpers@1.0.2/node_modules/call-bind-apply-helpers/actualApply.js.map +1 -1
  10. package/dist/src/node_modules/.pnpm/call-bind-apply-helpers@1.0.2/node_modules/call-bind-apply-helpers/functionCall.js +5 -2
  11. package/dist/src/node_modules/.pnpm/call-bind-apply-helpers@1.0.2/node_modules/call-bind-apply-helpers/functionCall.js.map +1 -1
  12. package/dist/src/node_modules/.pnpm/call-bind-apply-helpers@1.0.2/node_modules/call-bind-apply-helpers/index.js +7 -7
  13. package/dist/src/node_modules/.pnpm/call-bind-apply-helpers@1.0.2/node_modules/call-bind-apply-helpers/index.js.map +1 -1
  14. package/dist/src/node_modules/.pnpm/function-bind@1.1.2/node_modules/function-bind/implementation.js +45 -38
  15. package/dist/src/node_modules/.pnpm/function-bind@1.1.2/node_modules/function-bind/implementation.js.map +1 -1
  16. package/dist/src/node_modules/.pnpm/function-bind@1.1.2/node_modules/function-bind/index.js +10 -3
  17. package/dist/src/node_modules/.pnpm/function-bind@1.1.2/node_modules/function-bind/index.js.map +1 -1
  18. package/dist/src/node_modules/.pnpm/get-intrinsic@1.3.0/node_modules/get-intrinsic/index.js +62 -62
  19. package/dist/src/node_modules/.pnpm/get-intrinsic@1.3.0/node_modules/get-intrinsic/index.js.map +1 -1
  20. package/dist/src/node_modules/.pnpm/hasown@2.0.4/node_modules/hasown/index.js +8 -8
  21. package/dist/src/node_modules/.pnpm/hasown@2.0.4/node_modules/hasown/index.js.map +1 -1
  22. package/dist/src/style.css +1 -1
  23. package/dist/src/styles/antdv.override.less +154 -0
  24. package/dist/src/styles/common.less +23 -0
  25. package/dist/src/styles/index.less +8 -0
  26. package/dist/src/styles/loading.less +64 -0
  27. package/dist/src/styles/login.css +81 -0
  28. package/dist/src/styles/reset.css +12 -0
  29. package/dist/src/styles/theme.less +50 -0
  30. package/dist/src/styles/transition.less +60 -0
  31. package/dist/src/styles/variables.less +51 -0
  32. package/dist/src/utils/index.js +64 -63
  33. package/dist/src/utils/index.js.map +1 -1
  34. package/dist/src/utils/tableConfig.js +17 -0
  35. package/dist/src/utils/tableConfig.js.map +1 -0
  36. package/dist/src/utils/useBufferStatus.js +35 -0
  37. package/dist/src/utils/useBufferStatus.js.map +1 -0
  38. package/dist/src/utils/useDownloadBlob.js +28 -0
  39. package/dist/src/utils/useDownloadBlob.js.map +1 -0
  40. package/dist/src/utils/useExeSubscription.js +66 -0
  41. package/dist/src/utils/useExeSubscription.js.map +1 -0
  42. package/package.json +8 -4
@@ -0,0 +1,50 @@
1
+ // @import 'ant-design-vue/lib/style/themes/default.less';
2
+ // @import 'ant-design-vue/dist/antd.dark.less';
3
+ // @root-entry-name: variable;
4
+
5
+ @white: #fff;
6
+ @black: #000;
7
+
8
+ html:not(.dark) {
9
+ @component-background: @white;
10
+ }
11
+
12
+ // html.dark {
13
+ // }
14
+
15
+ .setStyle(@className, @propName) {
16
+ html {
17
+ &.dark {
18
+ .@{className} {
19
+ @{propName}: @black;
20
+ }
21
+ }
22
+
23
+ &:not(.dark) {
24
+ .@{className} {
25
+ @{propName}: @white;
26
+ }
27
+ }
28
+ }
29
+ }
30
+
31
+ .themeColor(@classNames, @i: 1) when(@i =< length(@classNames)) {
32
+ @className: extract(@classNames, @i);
33
+
34
+ .setStyle(@className, background-color);
35
+ .setStyle(@className, color);
36
+
37
+ .themeColor(@classNames, (@i + 1));
38
+ }
39
+
40
+ .themeTextColor(@classNames, @i: 1) when(@i =< length(@classNames)) {
41
+ @className: extract(@classNames, @i);
42
+ .setStyle(@className, color);
43
+ .themeColor(@classNames, (@i + 1));
44
+ }
45
+
46
+ .themeBgColor(@classNames, @i: 1) when(@i =< length(@classNames)) {
47
+ @className: extract(@classNames, @i);
48
+ .setStyle(@className, background-color);
49
+ .themeColor(@classNames, (@i + 1));
50
+ }
@@ -0,0 +1,60 @@
1
+ /* breadcrumb transition */
2
+ .breadcrumb-enter-active,
3
+ .breadcrumb-leave-active {
4
+ transition: all 0.5s;
5
+ }
6
+
7
+ .breadcrumb-enter-from,
8
+ .breadcrumb-leave-active {
9
+ transform: translateX(20px);
10
+ opacity: 0;
11
+ }
12
+
13
+ .breadcrumb-leave-active {
14
+ position: absolute;
15
+ }
16
+
17
+ /* fade */
18
+ .fade-enter-active,
19
+ .fade-leave-active {
20
+ transition: opacity 0.28s;
21
+ }
22
+
23
+ .fade-enter,
24
+ .fade-leave-active {
25
+ opacity: 0;
26
+ }
27
+
28
+ /* fade-slide */
29
+ .fade-slide-leave-active,
30
+ .fade-slide-enter-active {
31
+ transition: all 0.5s;
32
+ }
33
+
34
+ .fade-slide-enter-from {
35
+ transform: translateX(-30px);
36
+ opacity: 0;
37
+ }
38
+
39
+ .fade-slide-leave-to {
40
+ transform: translateX(30px);
41
+ opacity: 0;
42
+ }
43
+
44
+ /* router view transition */
45
+ .zoom-fade-enter-active,
46
+ .zoom-fade-leave-active {
47
+ transition:
48
+ transform 0.35s,
49
+ opacity 0.28s ease-in-out;
50
+ }
51
+
52
+ .zoom-fade-enter-from {
53
+ transform: scale(0.97);
54
+ opacity: 0;
55
+ }
56
+
57
+ .zoom-fade-leave-to {
58
+ transform: scale(1.03);
59
+ opacity: 0;
60
+ }
@@ -0,0 +1,51 @@
1
+ // @import 'ant-design-vue/lib/style/themes/default.less';
2
+
3
+ // @header-height: 64px;
4
+ // @footer-height: 70px;
5
+ // @primary-color: #00b96b;
6
+
7
+ :root {
8
+ --app-header-height: 64px;
9
+ --app-footer-height: 0px;
10
+ --app-primary-color: #00b96b;
11
+ }
12
+
13
+ // 紧凑布局的样式
14
+ .compact {
15
+ .tabs-view-content {
16
+ padding: 10px 10px 0 !important;
17
+ }
18
+
19
+ .schemaForm {
20
+ margin-bottom: 10px !important;
21
+ padding-top: 12px !important;
22
+ }
23
+
24
+ .ant-col-24.form-action {
25
+ margin-top: -10px !important;
26
+ margin-bottom: -10px !important;
27
+ }
28
+
29
+ #mom-dynamic-toolbar {
30
+ padding: 10px 10px 0 !important;
31
+ }
32
+
33
+ .ant-table-tbody > tr > td {
34
+ padding: 4px !important;
35
+ }
36
+
37
+ .ant-table-measure-row {
38
+ display: none !important;
39
+ }
40
+ }
41
+
42
+ .monaco-editor {
43
+ .monaco-scrollable-element {
44
+ left: 43px !important;
45
+ width: 100% !important;
46
+ }
47
+
48
+ .line-numbers {
49
+ width: 30px !important;
50
+ }
51
+ }
@@ -1,73 +1,74 @@
1
1
  import "dayjs";
2
- import { dateUtil as n, formatToCustomWeek as f, formatToDate as u, formatToDateTime as x, formatToWeek as d, renderCustomWeekToDayjs as y } from "./dateUtil.js";
2
+ import { dateUtil as f, formatToCustomWeek as u, formatToDate as x, formatToDateTime as d, formatToWeek as y, renderCustomWeekToDayjs as b } from "./dateUtil.js";
3
3
  import "./eventBus.js";
4
4
  import "../node_modules/.pnpm/file-saver@2.0.5/node_modules/file-saver/dist/FileSaver.min.js";
5
- import { isArguments as c, isArray as k, isArrayBuffer as A, isArrayLike as S, isArrayLikeObject as T, isBoolean as U, isBuffer as g, isDate as W, isElement as h, isEmpty as D, isEqual as E, isEqualWith as M, isError as j, isFinite as C, isFunction as I, isLength as L, isMap as N, isMatch as q, isMatchWith as B, isNative as F, isNil as O, isNull as R, isNumber as _, isObjectLike as v, isPlainObject as H, isRegExp as P, isSafeInteger as w, isSet as z, isString as G, isSymbol as J, isTypedArray as K, isUndefined as Q, isWeakMap as V, isWeakSet as X } from "lodash-es";
6
- import { i18nRender as Z } from "./i18n.js";
5
+ import { isArguments as k, isArray as A, isArrayBuffer as S, isArrayLike as T, isArrayLikeObject as U, isBoolean as g, isBuffer as W, isDate as h, isElement as D, isEmpty as E, isEqual as M, isEqualWith as j, isError as C, isFinite as I, isFunction as L, isLength as N, isMap as q, isMatch as B, isMatchWith as F, isNative as O, isNil as R, isNull as _, isNumber as v, isObjectLike as H, isPlainObject as P, isRegExp as w, isSafeInteger as z, isSet as G, isString as J, isSymbol as K, isTypedArray as Q, isUndefined as V, isWeakMap as X, isWeakSet as Y } from "lodash-es";
6
+ import { i18nRender as $ } from "./i18n.js";
7
7
  import "vue";
8
8
  /* empty css */
9
- import { close as ee, start as ie } from "./nprogress.js";
9
+ import { close as ie, start as re } from "./nprogress.js";
10
10
  import "./performanceMonitor.js";
11
11
  import "./SignalRConnectionManager.js";
12
- import { propTypes as te } from "./propTypes.js";
13
- import { aotClientHttp as se, baseAotClientUrl as ae, baseApiUrl as me, request as pe, uploadFile as le } from "./request.js";
14
- import { Storage as fe, createStorage as ue } from "./Storage.js";
15
- import { buildShortUUID as de, buildUUID as ye } from "./uuid.js";
16
- const m = !0;
12
+ import { propTypes as oe } from "./propTypes.js";
13
+ import { aotClientHttp as ae, baseAotClientUrl as me, baseApiUrl as pe, request as le, uploadFile as ne } from "./request.js";
14
+ import { Storage as ue, createStorage as xe } from "./Storage.js";
15
+ import { buildShortUUID as ye, buildUUID as be } from "./uuid.js";
16
+ import "ant-design-vue";
17
+ const p = !0;
17
18
  export {
18
- fe as Storage,
19
- m as __utilsIndexMarker,
20
- se as aotClientHttp,
21
- ae as baseAotClientUrl,
22
- me as baseApiUrl,
23
- de as buildShortUUID,
24
- ye as buildUUID,
25
- ee as close,
26
- ue as createStorage,
27
- n as dateUtil,
28
- f as formatToCustomWeek,
29
- u as formatToDate,
30
- x as formatToDateTime,
31
- d as formatToWeek,
32
- Z as i18nRender,
33
- c as isArguments,
34
- k as isArray,
35
- A as isArrayBuffer,
36
- S as isArrayLike,
37
- T as isArrayLikeObject,
38
- U as isBoolean,
39
- g as isBuffer,
40
- W as isDate,
41
- h as isElement,
42
- D as isEmpty,
43
- E as isEqual,
44
- M as isEqualWith,
45
- j as isError,
46
- C as isFinite,
47
- I as isFunction,
48
- L as isLength,
49
- N as isMap,
50
- q as isMatch,
51
- B as isMatchWith,
52
- F as isNative,
53
- O as isNil,
54
- R as isNull,
55
- _ as isNumber,
56
- v as isObjectLike,
57
- H as isPlainObject,
58
- P as isRegExp,
59
- w as isSafeInteger,
60
- z as isSet,
61
- G as isString,
62
- J as isSymbol,
63
- K as isTypedArray,
64
- Q as isUndefined,
65
- V as isWeakMap,
66
- X as isWeakSet,
67
- te as propTypes,
68
- y as renderCustomWeekToDayjs,
69
- pe as request,
70
- ie as start,
71
- le as uploadFile
19
+ ue as Storage,
20
+ p as __utilsIndexMarker,
21
+ ae as aotClientHttp,
22
+ me as baseAotClientUrl,
23
+ pe as baseApiUrl,
24
+ ye as buildShortUUID,
25
+ be as buildUUID,
26
+ ie as close,
27
+ xe as createStorage,
28
+ f as dateUtil,
29
+ u as formatToCustomWeek,
30
+ x as formatToDate,
31
+ d as formatToDateTime,
32
+ y as formatToWeek,
33
+ $ as i18nRender,
34
+ k as isArguments,
35
+ A as isArray,
36
+ S as isArrayBuffer,
37
+ T as isArrayLike,
38
+ U as isArrayLikeObject,
39
+ g as isBoolean,
40
+ W as isBuffer,
41
+ h as isDate,
42
+ D as isElement,
43
+ E as isEmpty,
44
+ M as isEqual,
45
+ j as isEqualWith,
46
+ C as isError,
47
+ I as isFinite,
48
+ L as isFunction,
49
+ N as isLength,
50
+ q as isMap,
51
+ B as isMatch,
52
+ F as isMatchWith,
53
+ O as isNative,
54
+ R as isNil,
55
+ _ as isNull,
56
+ v as isNumber,
57
+ H as isObjectLike,
58
+ P as isPlainObject,
59
+ w as isRegExp,
60
+ z as isSafeInteger,
61
+ G as isSet,
62
+ J as isString,
63
+ K as isSymbol,
64
+ Q as isTypedArray,
65
+ V as isUndefined,
66
+ X as isWeakMap,
67
+ Y as isWeakSet,
68
+ oe as propTypes,
69
+ b as renderCustomWeekToDayjs,
70
+ le as request,
71
+ re as start,
72
+ ne as uploadFile
72
73
  };
73
74
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/utils/index.ts"],"sourcesContent":["/**\n * 工具函数模块入口\n * 导出所有共享工具函数\n */\n\nexport * from \"./awaitTo\";\nexport { default as BrowserType } from \"./browser-type\";\nexport { default } from \"./browser-type\";\nexport * from \"./common\";\nexport * from \"./component\";\nexport * from \"./dateUtil\";\nexport * from \"./digit\";\nexport * from \"./downloadFile\";\nexport * from \"./eventBus\";\nexport * from \"./Export2Excel\";\nexport * from \"./global\";\nexport * from \"./helper\";\nexport * from \"./i18n\";\nexport * from \"./importExport\";\nexport * from \"./is\";\nexport * from \"./loading\";\nexport * from \"./log\";\nexport * from \"./nprogress\";\nexport * from \"./performanceMonitor\";\nexport * from \"./plcSignalR\";\nexport * from \"./propTypes\";\nexport * from \"./request\";\nexport * from \"./Storage\";\nexport * from \"./toolsValidate\";\nexport * from \"./tree\";\nexport * from \"./urlUtils\";\nexport * from \"./uuid\";\nexport * from \"./validate\";\nexport * from \"./format\";\n\n// 兼容 preserveModules 目录导入:确保该模块作为目录时有 index.js 落盘\nexport const __utilsIndexMarker = true\n"],"names":["__utilsIndexMarker"],"mappings":";;;;;;;;;;;;;;;AAoCO,MAAMA,IAAqB;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/utils/index.ts"],"sourcesContent":["/**\n * 工具函数模块入口\n * 导出所有共享工具函数\n */\n\nexport * from \"./awaitTo\";\nexport { default as BrowserType } from \"./browser-type\";\nexport { default } from \"./browser-type\";\nexport * from \"./common\";\nexport * from \"./component\";\nexport * from \"./dateUtil\";\nexport * from \"./digit\";\nexport * from \"./downloadFile\";\nexport * from \"./eventBus\";\nexport * from \"./Export2Excel\";\nexport * from \"./global\";\nexport * from \"./helper\";\nexport * from \"./i18n\";\nexport * from \"./importExport\";\nexport * from \"./is\";\nexport * from \"./loading\";\nexport * from \"./log\";\nexport * from \"./nprogress\";\nexport * from \"./performanceMonitor\";\nexport * from \"./plcSignalR\";\nexport * from \"./propTypes\";\nexport * from \"./request\";\nexport * from \"./Storage\";\nexport * from \"./toolsValidate\";\nexport * from \"./tree\";\nexport * from \"./urlUtils\";\nexport * from \"./uuid\";\nexport * from \"./validate\";\nexport * from \"./format\";\nexport * from \"./useBufferStatus\";\nexport * from \"./useDownloadBlob\";\nexport * from \"./useExeSubscription\";\nexport * from \"./tableConfig\";\n\n// 兼容 preserveModules 目录导入:确保该模块作为目录时有 index.js 落盘\nexport const __utilsIndexMarker = true\n"],"names":["__utilsIndexMarker"],"mappings":";;;;;;;;;;;;;;;;AAwCO,MAAMA,IAAqB;"}
@@ -0,0 +1,17 @@
1
+ const o = {
2
+ pageSize: 20,
3
+ showSizeChanger: !0,
4
+ pageSizeOptions: ["10", "20", "50", "100"],
5
+ showTotal: (e) => `共 ${e} 条`
6
+ }, a = "small", s = "calc(100vh - 340px)", c = "calc(100vh - 480px)";
7
+ function i(e, n = 20, t) {
8
+ return e <= n ? !1 : { ...o, ...t };
9
+ }
10
+ export {
11
+ o as DEFAULT_PAGINATION,
12
+ s as PAGE_SCROLL_Y,
13
+ a as TABLE_SIZE,
14
+ c as TAB_SCROLL_Y,
15
+ i as usePagination
16
+ };
17
+ //# sourceMappingURL=tableConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tableConfig.js","sources":["../../../src/utils/tableConfig.ts"],"sourcesContent":["/**\n * 表格统一分页/样式/滚动配置\n * 所有主数据表格统一使用此配置,避免各页面各自为政\n *\n * 设计原则:\n * 1. 统一 size=\"small\" — 行高紧凑,单屏信息密度高\n * 2. 统一 scroll.y — 固定表头,数据区滚动,页面不跟着滚\n * 3. 统一分页配置 — 页码、每页条数、总数显示一致\n */\n\n/** 默认分页配置 */\nexport const DEFAULT_PAGINATION = {\n pageSize: 20,\n showSizeChanger: true,\n pageSizeOptions: ['10', '20', '50', '100'],\n showTotal: (total: number) => `共 ${total} 条`,\n}\n\n/** 表格统一尺寸 */\nexport const TABLE_SIZE = 'small' as const\n\n/**\n * 主数据表格默认 scroll.y — 固定表头,数据区滚动\n * 独立页面用 PAGE_SCROLL_Y,Tab/Drawer 内用 TAB_SCROLL_Y\n */\nexport const PAGE_SCROLL_Y = 'calc(100vh - 340px)'\nexport const TAB_SCROLL_Y = 'calc(100vh - 480px)'\n\n/**\n * 生成分页配置\n * @param total 数据总数,<= threshold 时禁用分页\n * @param threshold 启用分页的最小数据量,默认 20\n * @param overrides 覆盖默认值\n */\nexport function usePagination(\n total: number,\n threshold = 20,\n overrides?: Partial<typeof DEFAULT_PAGINATION>,\n) {\n if (total <= threshold) return false\n return { ...DEFAULT_PAGINATION, ...overrides }\n}\n"],"names":["DEFAULT_PAGINATION","total","TABLE_SIZE","PAGE_SCROLL_Y","TAB_SCROLL_Y","usePagination","threshold","overrides"],"mappings":"AAWO,MAAMA,IAAqB;AAAA,EAChC,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,iBAAiB,CAAC,MAAM,MAAM,MAAM,KAAK;AAAA,EACzC,WAAW,CAACC,MAAkB,KAAKA,CAAK;AAC1C,GAGaC,IAAa,SAMbC,IAAgB,uBAChBC,IAAe;AAQrB,SAASC,EACdJ,GACAK,IAAY,IACZC,GACA;AACA,SAAIN,KAASK,IAAkB,KACxB,EAAE,GAAGN,GAAoB,GAAGO,EAAA;AACrC;"}
@@ -0,0 +1,35 @@
1
+ import { ref as o, onMounted as i, onUnmounted as c } from "vue";
2
+ import { getTagHistoryBufferStatus as m } from "../api/device/tagHistory.js";
3
+ function p(s = {}) {
4
+ const { autoRefreshMs: a = 0, onError: f } = s, u = o(0), t = o(!1), l = o("");
5
+ let e = null;
6
+ const n = async () => {
7
+ t.value = !0;
8
+ try {
9
+ const r = await m();
10
+ u.value = Number(r?.pending_count ?? 0), l.value = (/* @__PURE__ */ new Date()).toLocaleTimeString();
11
+ } catch (r) {
12
+ f?.(r);
13
+ } finally {
14
+ t.value = !1;
15
+ }
16
+ };
17
+ return i(() => {
18
+ n(), a > 0 && (e = setInterval(n, a));
19
+ }), c(() => {
20
+ e && (clearInterval(e), e = null);
21
+ }), {
22
+ /** 待写入条数 */
23
+ pending: u,
24
+ /** 是否正在查询 */
25
+ loading: t,
26
+ /** 上次查询成功时间(本地化字符串) */
27
+ updatedAt: l,
28
+ /** 手动触发一次查询 */
29
+ refresh: n
30
+ };
31
+ }
32
+ export {
33
+ p as useBufferStatus
34
+ };
35
+ //# sourceMappingURL=useBufferStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBufferStatus.js","sources":["../../../src/utils/useBufferStatus.ts"],"sourcesContent":["import { ref, onMounted, onUnmounted } from 'vue'\nimport { getTagHistoryBufferStatus } from '@/api/device/tagHistory'\n\nexport interface UseBufferStatusOptions {\n /**\n * 自动刷新间隔(毫秒)。默认 0 表示不自动刷新。\n * plcMonitor 用 30000;tagHistory / systemMaintenance 用 0(手动刷新)。\n */\n autoRefreshMs?: number\n /** 查询失败时调用。默认空(静默)。 */\n onError?: (err: unknown) => void\n}\n\n/**\n * 标签历史入库缓冲区状态 composable(/api/TagHistory/BufferStatus)。\n *\n * 消除 3 个页面里重复的 pending_count 拉取 + 定时刷新逻辑:\n * - plcMonitor.vue autoRefreshMs=30000\n * - tagHistory.vue 手动刷新\n * - systemMaintenance.vue 手动刷新\n *\n * 用法:\n * ```ts\n * const { pending, loading, updatedAt, refresh } = useBufferStatus({ autoRefreshMs: 30000 })\n * ```\n */\nexport function useBufferStatus(options: UseBufferStatusOptions = {}) {\n const { autoRefreshMs = 0, onError } = options\n\n const pending = ref(0)\n const loading = ref(false)\n const updatedAt = ref('')\n\n let timer: ReturnType<typeof setInterval> | null = null\n\n const refresh = async () => {\n loading.value = true\n try {\n const res = await getTagHistoryBufferStatus()\n pending.value = Number(res?.pending_count ?? 0)\n updatedAt.value = new Date().toLocaleTimeString()\n } catch (err) {\n onError?.(err)\n } finally {\n loading.value = false\n }\n }\n\n onMounted(() => {\n refresh()\n if (autoRefreshMs > 0) {\n timer = setInterval(refresh, autoRefreshMs)\n }\n })\n\n onUnmounted(() => {\n if (timer) {\n clearInterval(timer)\n timer = null\n }\n })\n\n return {\n /** 待写入条数 */\n pending,\n /** 是否正在查询 */\n loading,\n /** 上次查询成功时间(本地化字符串) */\n updatedAt,\n /** 手动触发一次查询 */\n refresh,\n }\n}\n"],"names":["useBufferStatus","options","autoRefreshMs","onError","pending","ref","loading","updatedAt","timer","refresh","res","getTagHistoryBufferStatus","err","onMounted","onUnmounted"],"mappings":";;AA0BO,SAASA,EAAgBC,IAAkC,IAAI;AACpE,QAAM,EAAE,eAAAC,IAAgB,GAAG,SAAAC,EAAA,IAAYF,GAEjCG,IAAUC,EAAI,CAAC,GACfC,IAAUD,EAAI,EAAK,GACnBE,IAAYF,EAAI,EAAE;AAExB,MAAIG,IAA+C;AAEnD,QAAMC,IAAU,YAAY;AAC1B,IAAAH,EAAQ,QAAQ;AAChB,QAAI;AACF,YAAMI,IAAM,MAAMC,EAAA;AAClB,MAAAP,EAAQ,QAAQ,OAAOM,GAAK,iBAAiB,CAAC,GAC9CH,EAAU,SAAQ,oBAAI,KAAA,GAAO,mBAAA;AAAA,IAC/B,SAASK,GAAK;AACZ,MAAAT,IAAUS,CAAG;AAAA,IACf,UAAA;AACE,MAAAN,EAAQ,QAAQ;AAAA,IAClB;AAAA,EACF;AAEA,SAAAO,EAAU,MAAM;AACd,IAAAJ,EAAA,GACIP,IAAgB,MAClBM,IAAQ,YAAYC,GAASP,CAAa;AAAA,EAE9C,CAAC,GAEDY,EAAY,MAAM;AAChB,IAAIN,MACF,cAAcA,CAAK,GACnBA,IAAQ;AAAA,EAEZ,CAAC,GAEM;AAAA;AAAA,IAEL,SAAAJ;AAAA;AAAA,IAEA,SAAAE;AAAA;AAAA,IAEA,WAAAC;AAAA;AAAA,IAEA,SAAAE;AAAA,EAAA;AAEJ;"}
@@ -0,0 +1,28 @@
1
+ import { ref as u } from "vue";
2
+ import { message as n } from "ant-design-vue";
3
+ function p() {
4
+ const r = u(!1);
5
+ async function l(o, d, a = {}) {
6
+ r.value = !0;
7
+ try {
8
+ const t = typeof o == "function" ? await o() : await o;
9
+ if (t?.type?.includes("application/json"))
10
+ try {
11
+ const i = await t.text(), c = JSON.parse(i);
12
+ return n.error(c?.msg || c?.detail || a.errorMsg || "导出失败"), !1;
13
+ } catch {
14
+ }
15
+ const s = URL.createObjectURL(t), e = document.createElement("a");
16
+ return e.href = s, e.download = d, e.style.display = "none", document.body.appendChild(e), e.click(), document.body.removeChild(e), URL.revokeObjectURL(s), n.success(a.successMsg ?? "开始下载"), !0;
17
+ } catch (t) {
18
+ return n.error(a.errorMsg || t?.message || "导出失败"), !1;
19
+ } finally {
20
+ r.value = !1;
21
+ }
22
+ }
23
+ return { downloading: r, downloadBlob: l };
24
+ }
25
+ export {
26
+ p as useDownloadBlob
27
+ };
28
+ //# sourceMappingURL=useDownloadBlob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDownloadBlob.js","sources":["../../../src/utils/useDownloadBlob.ts"],"sourcesContent":["import { ref } from 'vue'\nimport { message } from 'ant-design-vue'\n\n/**\n * Blob 文件下载 composable(统一所有\"调 API 拿 Blob → 触发浏览器下载\"场景)。\n *\n * 设计目标:\n * - 消除 4 个页面(tagHistory / DeviceWorkbench / auditLog / DataQueryTool)里\n * 重复的 URL.createObjectURL + createElement('a') + click + revokeObjectURL 流程。\n * - 内置 loading 状态与错误提示,调用方只需一行代码。\n *\n * 用法:\n * ```ts\n * const { downloading, downloadBlob } = useDownloadBlob()\n * await downloadBlob(() => exportDeviceBundle(id), `device-${id}.zip`)\n * await downloadBlob(Promise.resolve(myCsvBlob), 'audit.csv', { successMsg: '已导出' })\n * ```\n */\nexport function useDownloadBlob() {\n const downloading = ref(false)\n\n /**\n * 触发浏览器下载\n * @param source Blob 实例 或 返回 Blob 的 Promise / 工厂函数\n * @param filename 下载文件名(建议含时间戳,便于归档)\n * @param options 可选的成功/失败提示\n */\n async function downloadBlob(\n source: Blob | Promise<Blob> | (() => Promise<Blob>),\n filename: string,\n options: { successMsg?: string; errorMsg?: string } = {},\n ): Promise<boolean> {\n downloading.value = true\n try {\n const blob = typeof source === 'function'\n ? await source()\n : await source\n\n // 兜底:如果后端返回的是 JSON 错误包成 Blob,尝试解析提示\n if (blob?.type?.includes('application/json')) {\n try {\n const text = await blob.text()\n const parsed = JSON.parse(text)\n message.error(parsed?.msg || parsed?.detail || options.errorMsg || '导出失败')\n return false\n } catch {\n // 不是 JSON,按普通 blob 处理\n }\n }\n\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = filename\n link.style.display = 'none'\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n URL.revokeObjectURL(url)\n\n message.success(options.successMsg ?? '开始下载')\n return true\n } catch (err: any) {\n message.error(options.errorMsg || err?.message || '导出失败')\n return false\n } finally {\n downloading.value = false\n }\n }\n\n return { downloading, downloadBlob }\n}\n"],"names":["useDownloadBlob","downloading","ref","downloadBlob","source","filename","options","blob","text","parsed","message","url","link","err"],"mappings":";;AAkBO,SAASA,IAAkB;AAChC,QAAMC,IAAcC,EAAI,EAAK;AAQ7B,iBAAeC,EACbC,GACAC,GACAC,IAAsD,CAAA,GACpC;AAClB,IAAAL,EAAY,QAAQ;AACpB,QAAI;AACF,YAAMM,IAAO,OAAOH,KAAW,aAC3B,MAAMA,EAAA,IACN,MAAMA;AAGV,UAAIG,GAAM,MAAM,SAAS,kBAAkB;AACzC,YAAI;AACF,gBAAMC,IAAO,MAAMD,EAAK,KAAA,GAClBE,IAAS,KAAK,MAAMD,CAAI;AAC9B,iBAAAE,EAAQ,MAAMD,GAAQ,OAAOA,GAAQ,UAAUH,EAAQ,YAAY,MAAM,GAClE;AAAA,QACT,QAAQ;AAAA,QAER;AAGF,YAAMK,IAAM,IAAI,gBAAgBJ,CAAI,GAC9BK,IAAO,SAAS,cAAc,GAAG;AACvC,aAAAA,EAAK,OAAOD,GACZC,EAAK,WAAWP,GAChBO,EAAK,MAAM,UAAU,QACrB,SAAS,KAAK,YAAYA,CAAI,GAC9BA,EAAK,MAAA,GACL,SAAS,KAAK,YAAYA,CAAI,GAC9B,IAAI,gBAAgBD,CAAG,GAEvBD,EAAQ,QAAQJ,EAAQ,cAAc,MAAM,GACrC;AAAA,IACT,SAASO,GAAU;AACjB,aAAAH,EAAQ,MAAMJ,EAAQ,YAAYO,GAAK,WAAW,MAAM,GACjD;AAAA,IACT,UAAA;AACE,MAAAZ,EAAY,QAAQ;AAAA,IACtB;AAAA,EACF;AAEA,SAAO,EAAE,aAAAA,GAAa,cAAAE,EAAA;AACxB;"}
@@ -0,0 +1,66 @@
1
+ import { unref as s, onMounted as l, onUnmounted as x, watch as E } from "vue";
2
+ import { notification as p, message as d } from "ant-design-vue";
3
+ import { usePlcSignalR as S } from "./plcSignalR.js";
4
+ function z(n, i = {}) {
5
+ const { onChanged: u, notify: m = !0 } = i, {
6
+ onExeExecutionCompleted: a,
7
+ subscribeExeExecution: b,
8
+ unsubscribeExeExecution: g,
9
+ connect: f
10
+ } = S();
11
+ a((t) => {
12
+ if (!t)
13
+ return;
14
+ const r = s(n);
15
+ if (r == null || String(t.deviceId) !== String(r))
16
+ return;
17
+ const e = {
18
+ deviceId: t.deviceId,
19
+ tagName: t.tagName,
20
+ bizCode: t.bizCode,
21
+ success: !!t.success,
22
+ result: t.result,
23
+ error: t.error,
24
+ traceId: t.traceId,
25
+ timestamp: t.timestamp
26
+ };
27
+ m && p[e.success ? "success" : "error"]({
28
+ message: e.success ? "Exe 执行成功" : "Exe 执行失败",
29
+ description: `${e.bizCode || e.tagName || "-"} · ${e.result ?? ""}${e.error ? " · " + e.error : ""}`,
30
+ duration: 3,
31
+ placement: "bottomRight"
32
+ }), u?.(e);
33
+ });
34
+ const c = async (t) => {
35
+ if (t != null) {
36
+ try {
37
+ await f();
38
+ } catch {
39
+ }
40
+ try {
41
+ await b(String(t));
42
+ } catch {
43
+ d.warning("订阅 Exe 执行完成事件失败,日志将不会自动刷新");
44
+ }
45
+ }
46
+ }, o = (t) => {
47
+ t != null && g(String(t)).catch(() => {
48
+ });
49
+ };
50
+ return l(() => c(s(n))), x(() => o(s(n))), E(
51
+ () => s(n),
52
+ (t, r) => {
53
+ String(t) !== String(r) && (o(r), c(t));
54
+ }
55
+ ), {
56
+ /** 让调用方能在特殊场景下手动重订阅 */
57
+ resubscribe: () => {
58
+ const t = s(n);
59
+ o(t), c(t);
60
+ }
61
+ };
62
+ }
63
+ export {
64
+ z as useExeSubscription
65
+ };
66
+ //# sourceMappingURL=useExeSubscription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useExeSubscription.js","sources":["../../../src/utils/useExeSubscription.ts"],"sourcesContent":["import { onMounted, onUnmounted, unref, watch, type MaybeRef } from 'vue'\nimport { message, notification } from 'ant-design-vue'\nimport { usePlcSignalR } from '@/utils/plcSignalR'\n\nexport interface ExeExecutionInfo {\n deviceId: number | string\n tagName?: string\n bizCode?: string\n success: boolean\n result?: string | number | null\n error?: string | null\n traceId?: string\n timestamp?: string\n}\n\nexport interface UseExeSubscriptionOptions {\n /**\n * 命中当前设备的执行完成事件时调用。组件用它触发列表刷新。\n * 不传则只弹通知,不做任何副作用。\n */\n onChanged?: (info: ExeExecutionInfo) => void\n /** 是否弹右下角通知(默认 true) */\n notify?: boolean\n}\n\n/**\n * 订阅 PlcHub 推送的 OnExeExecutionCompleted 事件(按 deviceId 分组)。\n *\n * 设计目标:\n * - 把 ExeRulesTab.vue 里那段\"acquire + subscribe + 回调过滤 + 通知 + 清理\"\n * 抽成可复用 composable,未来 exeExecLog.vue / plcMonitor.vue 都能一行接入。\n * - 重连自动恢复订阅(SignalRConnectionManager 已支持 subscribedExeDevices)。\n *\n * 用法:\n * ```ts\n * useExeSubscription(\n * () => props.deviceId,\n * { onChanged: () => { loadExes(); loadLogs() } },\n * )\n * ```\n */\nexport function useExeSubscription(\n deviceId: MaybeRef<number | string | undefined>,\n options: UseExeSubscriptionOptions = {},\n) {\n const { onChanged, notify = true } = options\n\n const {\n onExeExecutionCompleted,\n subscribeExeExecution,\n unsubscribeExeExecution,\n connect,\n } = usePlcSignalR()\n\n onExeExecutionCompleted((raw: any) => {\n if (!raw) return\n const current = unref(deviceId)\n if (current == null || String(raw.deviceId) !== String(current)) return\n\n const info: ExeExecutionInfo = {\n deviceId: raw.deviceId,\n tagName: raw.tagName,\n bizCode: raw.bizCode,\n success: !!raw.success,\n result: raw.result,\n error: raw.error,\n traceId: raw.traceId,\n timestamp: raw.timestamp,\n }\n\n if (notify) {\n notification[info.success ? 'success' : 'error']({\n message: info.success ? 'Exe 执行成功' : 'Exe 执行失败',\n description:\n `${info.bizCode || info.tagName || '-'} · ${info.result ?? ''}${info.error ? ' · ' + info.error : ''}`,\n duration: 3,\n placement: 'bottomRight',\n })\n }\n\n onChanged?.(info)\n })\n\n // 订阅当前 deviceId;deviceId 变化时平滑切换\n const subscribe = async (id: number | string | undefined) => {\n if (id == null) return\n try {\n await connect() // acquire 引用计数,已连接时 noop\n } catch { /* 父组件可能已 connect,忽略 */ }\n try {\n await subscribeExeExecution(String(id))\n } catch {\n message.warning('订阅 Exe 执行完成事件失败,日志将不会自动刷新')\n }\n }\n\n const unsubscribe = (id: number | string | undefined) => {\n if (id == null) return\n unsubscribeExeExecution(String(id)).catch(() => { /* 忽略 */ })\n }\n\n onMounted(() => subscribe(unref(deviceId)))\n onUnmounted(() => unsubscribe(unref(deviceId)))\n\n // 父组件动态切换 deviceId 时,平滑切换订阅\n watch(\n () => unref(deviceId),\n (next, prev) => {\n if (String(next) === String(prev)) return\n unsubscribe(prev)\n subscribe(next)\n },\n )\n\n return {\n /** 让调用方能在特殊场景下手动重订阅 */\n resubscribe: () => {\n const id = unref(deviceId)\n unsubscribe(id)\n subscribe(id)\n },\n }\n}\n"],"names":["useExeSubscription","deviceId","options","onChanged","notify","onExeExecutionCompleted","subscribeExeExecution","unsubscribeExeExecution","connect","usePlcSignalR","raw","current","unref","info","notification","subscribe","id","message","unsubscribe","onMounted","onUnmounted","watch","next","prev"],"mappings":";;;AAyCO,SAASA,EACdC,GACAC,IAAqC,IACrC;AACA,QAAM,EAAE,WAAAC,GAAW,QAAAC,IAAS,GAAA,IAASF,GAE/B;AAAA,IACJ,yBAAAG;AAAA,IACA,uBAAAC;AAAA,IACA,yBAAAC;AAAA,IACA,SAAAC;AAAA,EAAA,IACEC,EAAA;AAEJ,EAAAJ,EAAwB,CAACK,MAAa;AACpC,QAAI,CAACA;AAAK;AACV,UAAMC,IAAUC,EAAMX,CAAQ;AAC9B,QAAIU,KAAW,QAAQ,OAAOD,EAAI,QAAQ,MAAM,OAAOC,CAAO;AAAG;AAEjE,UAAME,IAAyB;AAAA,MAC7B,UAAUH,EAAI;AAAA,MACd,SAASA,EAAI;AAAA,MACb,SAASA,EAAI;AAAA,MACb,SAAS,CAAC,CAACA,EAAI;AAAA,MACf,QAAQA,EAAI;AAAA,MACZ,OAAOA,EAAI;AAAA,MACX,SAASA,EAAI;AAAA,MACb,WAAWA,EAAI;AAAA,IAAA;AAGjB,IAAIN,KACFU,EAAaD,EAAK,UAAU,YAAY,OAAO,EAAE;AAAA,MAC/C,SAASA,EAAK,UAAU,aAAa;AAAA,MACrC,aACE,GAAGA,EAAK,WAAWA,EAAK,WAAW,GAAG,MAAMA,EAAK,UAAU,EAAE,GAAGA,EAAK,QAAQ,QAAQA,EAAK,QAAQ,EAAE;AAAA,MACtG,UAAU;AAAA,MACV,WAAW;AAAA,IAAA,CACZ,GAGHV,IAAYU,CAAI;AAAA,EAClB,CAAC;AAGD,QAAME,IAAY,OAAOC,MAAoC;AAC3D,QAAIA,KAAM,MACV;AAAA,UAAI;AACF,cAAMR,EAAA;AAAA,MACR,QAAQ;AAAA,MAA0B;AAClC,UAAI;AACF,cAAMF,EAAsB,OAAOU,CAAE,CAAC;AAAA,MACxC,QAAQ;AACN,QAAAC,EAAQ,QAAQ,2BAA2B;AAAA,MAC7C;AAAA;AAAA,EACF,GAEMC,IAAc,CAACF,MAAoC;AACvD,IAAIA,KAAM,QACVT,EAAwB,OAAOS,CAAE,CAAC,EAAE,MAAM,MAAM;AAAA,IAAW,CAAC;AAAA,EAC9D;AAEA,SAAAG,EAAU,MAAMJ,EAAUH,EAAMX,CAAQ,CAAC,CAAC,GAC1CmB,EAAY,MAAMF,EAAYN,EAAMX,CAAQ,CAAC,CAAC,GAG9CoB;AAAA,IACE,MAAMT,EAAMX,CAAQ;AAAA,IACpB,CAACqB,GAAMC,MAAS;AACd,MAAI,OAAOD,CAAI,MAAM,OAAOC,CAAI,MAChCL,EAAYK,CAAI,GAChBR,EAAUO,CAAI;AAAA,IAChB;AAAA,EAAA,GAGK;AAAA;AAAA,IAEL,aAAa,MAAM;AACjB,YAAMN,IAAKJ,EAAMX,CAAQ;AACzB,MAAAiB,EAAYF,CAAE,GACdD,EAAUC,CAAE;AAAA,IACd;AAAA,EAAA;AAEJ;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usethink/shared-frontend",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "@usethink 前端共享库 — Vue 3 工具函数、组件、Hooks、类型与业务能力",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,7 +27,7 @@
27
27
  "dist"
28
28
  ],
29
29
  "scripts": {
30
- "build": "vite build",
30
+ "build": "vite build && mkdir -p dist/src/styles && cp src/styles/*.less src/styles/*.css dist/src/styles/",
31
31
  "prepack": "pnpm run build",
32
32
  "dev": "vite build --watch",
33
33
  "typecheck": "tsc --noEmit",
@@ -44,7 +44,11 @@
44
44
  ],
45
45
  "license": "MIT",
46
46
  "pnpm": {
47
- "onlyBuiltDependencies": ["core-js", "esbuild", "vue-demi"]
47
+ "onlyBuiltDependencies": [
48
+ "core-js",
49
+ "esbuild",
50
+ "vue-demi"
51
+ ]
48
52
  },
49
53
  "publishConfig": {
50
54
  "access": "public",
@@ -109,4 +113,4 @@
109
113
  "vue": "~3.5.34",
110
114
  "vue-router": "~4.3.3"
111
115
  }
112
- }
116
+ }