@litianxiang/portal-core 0.1.11 → 0.1.13

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/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as vue_router from 'vue-router';
2
2
  import { RouteRecordRaw } from 'vue-router';
3
+ import { Dayjs } from 'dayjs';
3
4
 
4
5
  interface AppRouterOptions {
5
6
  staticRoutes: RouteRecordRaw[];
@@ -121,5 +122,100 @@ interface MenuHelper {
121
122
  getBreadcrumb: (path: string) => BreadcrumbItem[];
122
123
  }
123
124
  declare function createMenuHelper(options: MenuHelperOptions): MenuHelper;
125
+ /**
126
+ * 标准化后的菜单节点结构,供各前端站点复用
127
+ * 不依赖具体项目的 IMenu 类型
128
+ */
129
+ interface CoreMenuItem {
130
+ id: string | number;
131
+ parent_id?: string | number | null;
132
+ category?: string;
133
+ name?: string;
134
+ path?: string;
135
+ icon?: string;
136
+ order_num?: number;
137
+ site?: string;
138
+ children?: CoreMenuItem[];
139
+ }
140
+ interface TransformMenuOptions {
141
+ /**
142
+ * 当后端未返回 site 字段时使用的默认站点标识
143
+ * 示例:manage、hr-kq 等
144
+ */
145
+ defaultSite?: string;
146
+ }
147
+ /**
148
+ * 将后端返回的原始菜单数据转换为前端通用的 CoreMenuItem 结构
149
+ * - 主要是结构统一与 children 递归转换
150
+ * - 是否从 snake_case 转 camelCase 由后端约定,这里只做字段透传
151
+ */
152
+ declare function transformMenuTree(menuList: any[], options?: TransformMenuOptions): CoreMenuItem[];
153
+ /**
154
+ * 生成包含完整 children 的菜单树(包括 page 下的按钮等)
155
+ * 一般用于:
156
+ * - 权限判断(页面 + 按钮)
157
+ * - 侧边菜单折叠前的完整数据源
158
+ */
159
+ declare function buildAllMenuTree(menuList: CoreMenuItem[]): CoreMenuItem[];
160
+ /**
161
+ * 从完整菜单树中裁剪出 page + button 列表结构
162
+ * - 去掉纯 module 节点
163
+ * - 常用于“页面+按钮权限”一体的菜单视图
164
+ */
165
+ declare function buildPageMenuList(menuList: CoreMenuItem[]): CoreMenuItem[];
166
+ /**
167
+ * 递归查找菜单树中的第一个 page 页面路径
168
+ * 常用于:登录后或进入某模块时,自动跳转到第一个页面
169
+ */
170
+ declare function findFirstPagePath(menuList: CoreMenuItem[]): string | null;
171
+
172
+ type TimeInput = string | number | Date | Dayjs;
173
+ interface TimeRange<T = Dayjs> {
174
+ start: T;
175
+ end: T;
176
+ }
177
+ /**
178
+ * 标准时间格式化
179
+ */
180
+ declare function formatTime(date: TimeInput, format?: string): string;
181
+ /**
182
+ * 人性化相对时间(如:3分钟前)
183
+ */
184
+ declare function humanizeTime(date: TimeInput): string;
185
+ /**
186
+ * 计算时间差(毫秒)
187
+ */
188
+ declare function timeDiff(start: TimeInput, end?: TimeInput): number;
189
+ /**
190
+ * 将日期转换为月-日格式
191
+ */
192
+ declare function formatToMonthDay(date: TimeInput): string;
193
+ /**
194
+ * 生成日期区间对象(Dayjs)
195
+ */
196
+ declare function createRange(start: TimeInput, end: TimeInput): TimeRange<Dayjs>;
197
+ /**
198
+ * 将日期区间格式化为字符串:2026-02-01 ~ 2026-02-23
199
+ */
200
+ declare function formatRange(start: TimeInput, end: TimeInput, format?: string): string;
201
+ type PresetRangeKey = 'today' | 'yesterday' | 'last7days' | 'last30days' | 'thisMonth' | 'lastMonth';
202
+ /**
203
+ * 获取常用日期区间(用于统计/筛选快捷选项)
204
+ */
205
+ declare function getPresetRange(key: PresetRangeKey, now?: TimeInput): TimeRange<Dayjs>;
206
+ /**
207
+ * 判断某个时间是否在区间内(含边界)
208
+ */
209
+ declare function isInRange(value: TimeInput, range: TimeRange<TimeInput>): boolean;
210
+ declare function useTime(): {
211
+ formatTime: typeof formatTime;
212
+ humanizeTime: typeof humanizeTime;
213
+ timeDiff: typeof timeDiff;
214
+ formatToMonthDay: typeof formatToMonthDay;
215
+ createRange: typeof createRange;
216
+ formatRange: typeof formatRange;
217
+ getPresetRange: typeof getPresetRange;
218
+ isInRange: typeof isInRange;
219
+ };
124
220
 
125
- export { type AppRouterOptions, type BreadcrumbItem, type CreateAuthHttpClientOptions, type InactivityConfig, type InactivityResult, type LogoutToAuthOptions, type MenuHelper, type MenuHelperOptions, type PermissionHelper, type PermissionHelperOptions, calcInactivityAction, createAppRouter, createAuthHttpClient, createLogoutToAuth, createMenuHelper, createPermissionHelper };
221
+ export { type AppRouterOptions, type BreadcrumbItem, type CoreMenuItem, type CreateAuthHttpClientOptions, type InactivityConfig, type InactivityResult, type LogoutToAuthOptions, type MenuHelper, type MenuHelperOptions, type PermissionHelper, type PermissionHelperOptions, type PresetRangeKey, type TimeInput, type TimeRange, type TransformMenuOptions, buildAllMenuTree, buildPageMenuList, calcInactivityAction, createAppRouter, createAuthHttpClient, createLogoutToAuth, createMenuHelper, createPermissionHelper, createRange, findFirstPagePath, formatRange, formatTime, formatToMonthDay, getPresetRange, humanizeTime, isInRange, timeDiff, transformMenuTree, useTime };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // src/router.ts
1
+ // src/router/router.ts
2
2
  import { createRouter, createWebHashHistory } from "vue-router";
3
3
  function createAppRouter(options) {
4
4
  const { staticRoutes, getUserStore, getTabStore, getFirstPage, authLoginUrl } = options;
@@ -154,7 +154,7 @@ function createAppRouter(options) {
154
154
  return router;
155
155
  }
156
156
 
157
- // src/auth.ts
157
+ // src/auth/auth.ts
158
158
  function createLogoutToAuth(options) {
159
159
  const {
160
160
  getUserStore,
@@ -223,7 +223,7 @@ function calcInactivityAction(lastActiveTime, now, config = {}) {
223
223
  };
224
224
  }
225
225
 
226
- // src/http.ts
226
+ // src/http/http.ts
227
227
  function createAuthHttpClient(options) {
228
228
  const {
229
229
  axios,
@@ -371,7 +371,7 @@ function createAuthHttpClient(options) {
371
371
  return { http, logoutToAuth };
372
372
  }
373
373
 
374
- // src/permission.ts
374
+ // src/permission/permission.ts
375
375
  function normalizePath(raw) {
376
376
  if (!raw) return "";
377
377
  const [base] = raw.split("?");
@@ -427,7 +427,7 @@ function createPermissionHelper(options) {
427
427
  };
428
428
  }
429
429
 
430
- // src/menu.ts
430
+ // src/menu/menu.ts
431
431
  function normalizePath2(raw) {
432
432
  if (!raw) return "";
433
433
  const [base] = raw.split("?");
@@ -513,11 +513,204 @@ function createMenuHelper(options) {
513
513
  getBreadcrumb
514
514
  };
515
515
  }
516
+ function transformMenuTree(menuList, options) {
517
+ const defaultSite = options?.defaultSite;
518
+ return (menuList || []).map((item) => {
519
+ const children = Array.isArray(item?.children) ? transformMenuTree(item.children, options) : void 0;
520
+ return {
521
+ id: item?.id,
522
+ parent_id: item?.parent_id ?? null,
523
+ category: item?.category,
524
+ name: item?.name,
525
+ path: item?.path,
526
+ icon: item?.icon,
527
+ order_num: item?.order_num,
528
+ site: item?.site ?? defaultSite,
529
+ children
530
+ };
531
+ });
532
+ }
533
+ function buildAllMenuTree(menuList) {
534
+ const result = [];
535
+ for (const item of menuList || []) {
536
+ result.push({
537
+ id: item.id,
538
+ parent_id: item.parent_id ?? null,
539
+ category: item.category,
540
+ name: item.name,
541
+ path: item.path,
542
+ icon: item.icon,
543
+ order_num: item.order_num,
544
+ site: item.site,
545
+ children: item.children && item.children.length > 0 ? buildAllMenuTree(item.children) : []
546
+ });
547
+ }
548
+ return result;
549
+ }
550
+ function buildPageMenuList(menuList) {
551
+ const pages = [];
552
+ for (const item of menuList || []) {
553
+ if (item.category !== "module") {
554
+ pages.push({
555
+ id: item.id,
556
+ parent_id: item.parent_id ?? null,
557
+ category: item.category,
558
+ name: item.name,
559
+ path: item.path,
560
+ icon: item.icon,
561
+ order_num: item.order_num,
562
+ site: item.site,
563
+ children: item.children ? buildPageMenuList(item.children) : []
564
+ });
565
+ } else if (item.children && item.children.length > 0) {
566
+ const childMenus = buildPageMenuList(item.children);
567
+ if (childMenus.length > 0) {
568
+ for (const child of childMenus) {
569
+ pages.push({
570
+ id: child.id,
571
+ parent_id: child.parent_id ?? null,
572
+ category: child.category,
573
+ name: child.name,
574
+ path: child.path,
575
+ icon: child.icon,
576
+ order_num: child.order_num,
577
+ site: child.site,
578
+ children: child.children
579
+ });
580
+ }
581
+ }
582
+ }
583
+ }
584
+ return pages;
585
+ }
586
+ function findFirstPagePath(menuList) {
587
+ for (const item of menuList || []) {
588
+ if (item.category === "page" && item.path) {
589
+ return item.path;
590
+ }
591
+ if (item.children && item.children.length > 0) {
592
+ const childFirst = findFirstPagePath(item.children);
593
+ if (childFirst) return childFirst;
594
+ }
595
+ }
596
+ return null;
597
+ }
598
+
599
+ // src/time/time.ts
600
+ import dayjs from "dayjs";
601
+ import relativeTime from "dayjs/plugin/relativeTime";
602
+ import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
603
+ import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
604
+ import "dayjs/locale/zh-cn";
605
+ dayjs.extend(relativeTime);
606
+ dayjs.extend(isSameOrAfter);
607
+ dayjs.extend(isSameOrBefore);
608
+ dayjs.locale("zh-cn");
609
+ function formatTime(date, format = "YYYY-MM-DD HH:mm:ss") {
610
+ const d = dayjs(date);
611
+ if (!d.isValid()) return "";
612
+ return d.format(format);
613
+ }
614
+ function humanizeTime(date) {
615
+ return dayjs(date).fromNow();
616
+ }
617
+ function timeDiff(start, end = Date.now()) {
618
+ return dayjs(end).diff(dayjs(start));
619
+ }
620
+ function formatToMonthDay(date) {
621
+ const d = dayjs(date);
622
+ if (!d.isValid()) return "";
623
+ return d.format("MM-DD");
624
+ }
625
+ function createRange(start, end) {
626
+ const s = dayjs(start);
627
+ const e = dayjs(end);
628
+ return s.isBefore(e) ? { start: s, end: e } : { start: e, end: s };
629
+ }
630
+ function formatRange(start, end, format = "YYYY-MM-DD") {
631
+ const { start: s, end: e } = createRange(start, end);
632
+ if (!s.isValid() || !e.isValid()) return "";
633
+ return `${s.format(format)} ~ ${e.format(format)}`;
634
+ }
635
+ function getPresetRange(key, now = dayjs()) {
636
+ const base = dayjs(now);
637
+ switch (key) {
638
+ case "today": {
639
+ const start = base.startOf("day");
640
+ const end = base.endOf("day");
641
+ return { start, end };
642
+ }
643
+ case "yesterday": {
644
+ const start = base.subtract(1, "day").startOf("day");
645
+ const end = base.subtract(1, "day").endOf("day");
646
+ return { start, end };
647
+ }
648
+ case "last7days": {
649
+ const start = base.subtract(6, "day").startOf("day");
650
+ const end = base.endOf("day");
651
+ return { start, end };
652
+ }
653
+ case "last30days": {
654
+ const start = base.subtract(29, "day").startOf("day");
655
+ const end = base.endOf("day");
656
+ return { start, end };
657
+ }
658
+ case "thisMonth": {
659
+ const start = base.startOf("month");
660
+ const end = base.endOf("month");
661
+ return { start, end };
662
+ }
663
+ case "lastMonth": {
664
+ const prev = base.subtract(1, "month");
665
+ const start = prev.startOf("month");
666
+ const end = prev.endOf("month");
667
+ return { start, end };
668
+ }
669
+ default: {
670
+ const start = base.startOf("day");
671
+ const end = base.endOf("day");
672
+ return { start, end };
673
+ }
674
+ }
675
+ }
676
+ function isInRange(value, range) {
677
+ const v = dayjs(value);
678
+ const s = dayjs(range.start);
679
+ const e = dayjs(range.end);
680
+ if (!v.isValid() || !s.isValid() || !e.isValid()) return false;
681
+ const { start, end } = createRange(s, e);
682
+ return v.isSameOrAfter(start) && v.isSameOrBefore(end);
683
+ }
684
+ function useTime() {
685
+ return {
686
+ formatTime,
687
+ humanizeTime,
688
+ timeDiff,
689
+ formatToMonthDay,
690
+ createRange,
691
+ formatRange,
692
+ getPresetRange,
693
+ isInRange
694
+ };
695
+ }
516
696
  export {
697
+ buildAllMenuTree,
698
+ buildPageMenuList,
517
699
  calcInactivityAction,
518
700
  createAppRouter,
519
701
  createAuthHttpClient,
520
702
  createLogoutToAuth,
521
703
  createMenuHelper,
522
- createPermissionHelper
704
+ createPermissionHelper,
705
+ createRange,
706
+ findFirstPagePath,
707
+ formatRange,
708
+ formatTime,
709
+ formatToMonthDay,
710
+ getPresetRange,
711
+ humanizeTime,
712
+ isInRange,
713
+ timeDiff,
714
+ transformMenuTree,
715
+ useTime
523
716
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litianxiang/portal-core",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -14,11 +14,13 @@
14
14
  },
15
15
  "peerDependencies": {
16
16
  "vue": "^3.5.0",
17
- "vue-router": "^4.5.0"
17
+ "vue-router": "^4.5.0",
18
+ "dayjs": "^1.11.0"
18
19
  },
19
20
  "devDependencies": {
20
21
  "tsup": "^8.3.0",
21
- "typescript": "^5.6.3"
22
+ "typescript": "^5.6.3",
23
+ "dayjs": "^1.11.13"
22
24
  },
23
25
  "license": "UNLICENSED"
24
26
  }