@peng_kai/kit 0.3.0-beta.2 → 0.3.0-beta.21

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 (56) hide show
  1. package/.vscode/settings.json +2 -2
  2. package/admin/components/currency/src/CurrencyIcon.vue +37 -33
  3. package/admin/components/date/PeriodPicker.vue +122 -0
  4. package/admin/components/date/TimeFieldSelectForLabel.vue +24 -0
  5. package/admin/components/date/TtaTimeZone.vue +516 -0
  6. package/admin/components/date/TtaTimeZoneSimple.vue +104 -0
  7. package/admin/components/date/helpers.ts +250 -0
  8. package/admin/components/date/index.ts +6 -0
  9. package/admin/components/date/presetProps.ts +19 -0
  10. package/admin/components/filter/src/FilterReset.vue +53 -8
  11. package/admin/components/filter/src/more/TableSetting.vue +95 -0
  12. package/admin/components/provider/Admin.vue +17 -0
  13. package/admin/components/provider/admin-permission.ts +48 -0
  14. package/admin/components/provider/admin-router.ts +361 -0
  15. package/admin/components/provider/index.ts +3 -0
  16. package/admin/components/rich-text/src/RichText.new.vue +17 -4
  17. package/admin/components/rich-text/src/editorConfig.ts +10 -1
  18. package/admin/components/text/index.ts +2 -0
  19. package/admin/components/text/src/Amount.v2.vue +131 -0
  20. package/admin/components/text/src/Datetime.vue +17 -12
  21. package/admin/components/text/src/Num.vue +192 -0
  22. package/admin/layout/large/Breadcrumb.vue +10 -23
  23. package/admin/layout/large/Content.vue +14 -6
  24. package/admin/layout/large/Layout.vue +129 -0
  25. package/admin/layout/large/Menu.vue +24 -17
  26. package/admin/layout/large/Notice.vue +138 -0
  27. package/admin/layout/large/Tabs.vue +183 -0
  28. package/admin/layout/large/index.ts +61 -1
  29. package/admin/layout/large/y682.mp3 +0 -0
  30. package/admin/permission/routerGuard.ts +15 -8
  31. package/admin/permission/vuePlugin.ts +5 -10
  32. package/admin/route-guards/index.ts +0 -1
  33. package/admin/stores/index.ts +1 -0
  34. package/admin/styles/classCover.scss +1 -1
  35. package/admin/styles/index.scss +2 -2
  36. package/antd/hooks/useAntdModal.ts +29 -15
  37. package/antd/hooks/useAntdTable.ts +10 -7
  38. package/antd/hooks/useAntdTheme.ts +7 -0
  39. package/antd/hooks/useTableColumns.ts +83 -0
  40. package/antd/index.ts +1 -1
  41. package/libs/bignumber.ts +1 -1
  42. package/libs/dayjs.ts +15 -1
  43. package/libs/fingerprintjs.ts +1 -0
  44. package/package.json +59 -60
  45. package/request/interceptors/getDeviceInfo.ts +14 -0
  46. package/utils/LocaleManager.ts +1 -1
  47. package/utils/locale/LocaleManager.ts +2 -1
  48. package/utils/locale/helpers.ts +9 -0
  49. package/utils/number.ts +0 -1
  50. package/utils/storage.ts +31 -0
  51. package/utils/upload/AwsS3.ts +11 -4
  52. package/admin/layout/large/PageTab.vue +0 -70
  53. package/admin/route-guards/collapseMenu.ts +0 -11
  54. package/libs/a-calc.ts +0 -1
  55. package/vue/components/test/KitTest.vue +0 -9
  56. package/vue/components/test/testStore.ts +0 -11
@@ -0,0 +1,183 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue';
3
+ import { autoResetRef, useEventListener, useMutationObserver, useResizeObserver } from '@vueuse/core';
4
+ import type { TTab } from '../../components/provider/admin-router';
5
+
6
+ const props = defineProps<{
7
+ current: TTab
8
+ tabs: TTab[]
9
+ }>();
10
+ const emits = defineEmits<{
11
+ close: [TTab]
12
+ open: [TTab]
13
+ refresh: [TTab]
14
+ }>();
15
+
16
+ const $tabList = ref<HTMLElement>();
17
+ const refreshAnim = autoResetRef(false, 300);
18
+ const isLeftmost = ref(false);
19
+ const isRightmost = ref(false);
20
+
21
+ useEventListener($tabList, 'wheel', (e) => {
22
+ if ($tabList.value) {
23
+ const deltaY = e.deltaY < 0 ? -100 : 100;
24
+ $tabList.value.scrollLeft += deltaY;
25
+ }
26
+ });
27
+
28
+ function isScrollEnd() {
29
+ if ($tabList.value) {
30
+ const { scrollLeft, scrollWidth, clientWidth } = $tabList.value;
31
+ isRightmost.value = scrollLeft === 0;
32
+ isLeftmost.value = Math.ceil(Math.abs(scrollLeft) + clientWidth) >= scrollWidth;
33
+ }
34
+ }
35
+
36
+ function open(tab: TTab) {
37
+ if (props.current.path !== tab.path) {
38
+ emits('open', tab);
39
+ }
40
+ }
41
+
42
+ function refresh(tab: TTab) {
43
+ if (!refreshAnim.value) {
44
+ refreshAnim.value = true;
45
+ emits('refresh', tab);
46
+ }
47
+ }
48
+
49
+ useEventListener($tabList, 'scroll', isScrollEnd);
50
+ useResizeObserver($tabList, isScrollEnd);
51
+ useMutationObserver($tabList, isScrollEnd, { childList: true });
52
+ </script>
53
+
54
+ <template>
55
+ <div class="flex items-center">
56
+ <div ref="$tabList" class="tab-list relative ml-auto" :class="{ 'is-leftmost': isLeftmost, 'is-rightmost': isRightmost }">
57
+ <div
58
+ v-for="tab of props.tabs" :key="tab.path"
59
+ class="tab" :class="{ opened: tab.path === props.current?.path }"
60
+ @click="open(tab)"
61
+ >
62
+ <span class="title">{{ tab.title }}</span>
63
+ <span v-if="tab.type !== 2 && props.tabs.length > 1" class="close-btn" @click.stop="emits('close', tab)">
64
+ <i class="i-material-symbols:close-rounded" />
65
+ </span>
66
+ </div>
67
+ </div>
68
+ <div v-memo="[refreshAnim]" class="operation-btn ml-2" @click="refresh(props.current)">
69
+ <i class="i-material-symbols:refresh-rounded" :class="{ 'refresh-spin': refreshAnim }" />
70
+ </div>
71
+ </div>
72
+ </template>
73
+
74
+ <style lang="scss" scoped>
75
+ .tab-list {
76
+ display: flex;
77
+ gap: 4px;
78
+ align-items: center;
79
+ overflow-x: auto;
80
+ scrollbar-width: none;
81
+ flex: 1;
82
+ mask-image: linear-gradient(to left, #0000 0%, #000 5%, #000 95%, #0000 100%);
83
+ direction: rtl;
84
+
85
+ &.is-leftmost:not(.is-rightmost) {
86
+ mask-image: linear-gradient(to right, #000 0%, #000 5%, #000 95%, #0000 100%);
87
+ }
88
+
89
+ &.is-rightmost:not(.is-leftmost) {
90
+ mask-image: linear-gradient(to left, #000 0%, #000 5%, #000 95%, #0000 100%);
91
+ }
92
+
93
+ &.is-leftmost.is-rightmost {
94
+ mask-image: none;
95
+ }
96
+ }
97
+
98
+ .tab {
99
+ position: relative;
100
+ display: flex;
101
+ align-items: center;
102
+ padding: 0 8px;
103
+ height: 28px;
104
+ font-size: 14px;
105
+ cursor: pointer;
106
+ border-radius: 4px;
107
+ transition: all 0.2s;
108
+ white-space: nowrap;
109
+ line-height: 1em;
110
+ color: var(--antd-colorTextTertiary);
111
+
112
+ &:not(.opened):hover {
113
+ color: var(--antd-colorText);
114
+ }
115
+
116
+ &.opened {
117
+ color: var(--antd-colorPrimary);
118
+ }
119
+
120
+ .title {
121
+ transition: transform 0.1s;
122
+ }
123
+
124
+ &:has(.close-btn):hover .title {
125
+ transform: translateX(-4px);
126
+ }
127
+
128
+ .close-btn {
129
+ position: absolute;
130
+ right: -6px;
131
+ display: flex;
132
+ align-items: center;
133
+ width: 1em;
134
+ height: 1em;
135
+ padding: 2px;
136
+ opacity: 0;
137
+ margin-bottom: -1px;
138
+ transition: all 0.2s;
139
+ transform: scale(0);
140
+ color: var(--antd-colorTextTertiary);
141
+
142
+ &:hover {
143
+ color: var(--antd-colorText);
144
+ }
145
+ }
146
+
147
+ &:hover .close-btn {
148
+ margin-left: 0;
149
+ opacity: 1;
150
+ transform: scale(1);
151
+ }
152
+ }
153
+
154
+ .operation-btn {
155
+ flex: none;
156
+ display: flex;
157
+ align-items: center;
158
+ justify-content: center;
159
+ height: 28px;
160
+ font-size: 18px;
161
+ cursor: pointer;
162
+ border-radius: 4px;
163
+
164
+ &:hover {
165
+ color: var(--antd-colorPrimaryText);
166
+ }
167
+ }
168
+
169
+ .refresh-spin {
170
+ & {
171
+ animation: spin 200ms linear;
172
+ }
173
+
174
+ @keyframes spin {
175
+ 0% {
176
+ transform: rotate(0deg);
177
+ }
178
+ 100% {
179
+ transform: rotate(360deg);
180
+ }
181
+ }
182
+ }
183
+ </style>
@@ -1,4 +1,64 @@
1
1
  export { default as Breadcrumb } from './Breadcrumb.vue';
2
2
  export { default as Content } from './Content.vue';
3
3
  export { default as Menu } from './Menu.vue';
4
- export { default as PageTab } from './PageTab.vue';
4
+ export { default as Tabs } from './Tabs.vue';
5
+ export { default as Layout } from './Layout.vue';
6
+ export { default as Notice } from './Notice.vue';
7
+
8
+ /* #B 优化表格水平滚动(测试版方案) */
9
+ const antTableHeaders = new WeakSet<HTMLElement>();
10
+ setInterval(() => {
11
+ const $header = document.querySelector('.antd-cover__table-sticky-pagination .ant-table-header') as HTMLElement;
12
+
13
+ if (!$header || antTableHeaders.has($header))
14
+ return;
15
+
16
+ antTableHeaders.add($header);
17
+ $header.addEventListener('wheel', (ev) => {
18
+ ev.preventDefault();
19
+ const $body = (ev.currentTarget as HTMLElement).parentElement?.querySelector(':scope > .ant-table-body');
20
+
21
+ if ($body) {
22
+ const left = $body.scrollLeft + (ev.deltaY < 0 ? -100 : 100);
23
+ $body.scroll({ left });
24
+ }
25
+ })
26
+
27
+ const tipsKey = 'HIDDEN_TABLE_HORIZONTAL_SCROLLING_PROMPT';
28
+ const hiddenTips = localStorage.getItem(tipsKey);
29
+
30
+ if (!hiddenTips) {
31
+ const tips = `将鼠标指针移至表头,通过滚动鼠标滚轮来水平滑动表格(点击可关闭此提示)`;
32
+ const ctn = document.createElement('div');
33
+ ctn.className = `pos-sticky left-0 top-0 w-full h-full bg-$antd-colorPrimary text-center lh-relaxed text-white`;
34
+ ctn.innerHTML = tips;
35
+ ctn.onclick = () => {
36
+ ctn.remove();
37
+ localStorage.setItem(tipsKey, '1');
38
+ };
39
+ $header.appendChild(ctn);
40
+ }
41
+ }, 1000);
42
+ /* #E */
43
+
44
+ /* #B 高亮最后一次操作的表格行 */
45
+ window.addEventListener('click', (ev) => {
46
+ const targetEle = ev.target as HTMLElement;
47
+ const closestRow = targetEle.closest('.ant-table-row');
48
+
49
+ if (closestRow) {
50
+ document.querySelectorAll('.last-clicked-cell').forEach((cell) => {
51
+ cell.classList.remove('ant-table-cell-row-hover');
52
+ cell.classList.remove('last-clicked-cell');
53
+ });
54
+
55
+ const cells = closestRow.querySelectorAll(':scope > .ant-table-cell');
56
+ setTimeout(() => {
57
+ cells.forEach((cell) => {
58
+ cell.classList.add('ant-table-cell-row-hover');
59
+ cell.classList.add('last-clicked-cell');
60
+ });
61
+ }, 300);
62
+ }
63
+ });
64
+ /* #E */
Binary file
@@ -1,24 +1,31 @@
1
1
  import type { Router } from 'vue-router';
2
- import { adminPlugin } from '../adminPlugin';
3
2
  import { hasToken } from '../../utils';
4
3
 
5
- export function setupPermissionRouterGuard(router: Router, rouneNames: { index: string, login: string, 403: string }) {
4
+ interface TPermission {
5
+ refresh: () => Promise<any>
6
+ has: (code: string) => boolean
7
+ }
8
+
9
+ export function setupPermissionRouterGuard(
10
+ router: Router,
11
+ permission: TPermission | undefined,
12
+ rouneNames: { index: string, login: string, 403: string },
13
+ ) {
6
14
  router.beforeEach(async (to, _, next) => {
7
- const permissionStore = adminPlugin.deps.usePermissionStore();
8
15
  const isLogin = hasToken();
9
16
  const needLogin = Boolean(to.meta?.requireAuth);
10
17
  let hasPermission = false;
11
18
 
12
19
  if (isLogin) {
13
- await permissionStore.refreshPermission();
20
+ await permission?.refresh();
14
21
 
15
22
  const permissionCode = to.meta?.permissionCode;
16
- hasPermission = permissionCode ? permissionStore.hasPermission(permissionCode) : true;
23
+ hasPermission = permissionCode ? (permission?.has(permissionCode) ?? true) : true;
17
24
  }
18
25
 
19
26
  // 已登录状态跳转登录页,跳转至首页
20
27
  if (isLogin && to.name === rouneNames.login)
21
- return next({ name: rouneNames.index, replace: true });
28
+ return next({ name: rouneNames.index, replace: true, state: { tabType: 2 } });
22
29
 
23
30
  // 不需要登录权限的页面直接通行
24
31
  else if (!needLogin)
@@ -26,7 +33,7 @@ export function setupPermissionRouterGuard(router: Router, rouneNames: { index:
26
33
 
27
34
  // 未登录状态进入需要登录权限的页面
28
35
  else if (!isLogin && needLogin)
29
- return next({ name: rouneNames.login, replace: true, query: { redirect: to.fullPath } });
36
+ return next({ name: rouneNames.login, query: { redirect: to.fullPath }, state: { tabType: 0 }, replace: true });
30
37
 
31
38
  // 登录状态进入需要登录权限的页面,有权限直接通行
32
39
  else if (isLogin && needLogin && hasPermission)
@@ -34,7 +41,7 @@ export function setupPermissionRouterGuard(router: Router, rouneNames: { index:
34
41
 
35
42
  // 登录状态进入需要登录权限的页面,无权限,重定向到无权限页面
36
43
  else if (isLogin && needLogin && !hasPermission)
37
- return next({ name: rouneNames[403], replace: true });
44
+ return next({ name: rouneNames[403], state: { tabType: 0 }, replace: true });
38
45
 
39
46
  return next(false);
40
47
  });
@@ -1,25 +1,22 @@
1
1
  import type { App } from 'vue';
2
2
  import { watch } from 'vue';
3
- import { adminPlugin } from '../adminPlugin';
3
+ import type { createAdminPermission } from '../components/provider/admin-permission';
4
4
 
5
5
  type TCodes = string | string[];
6
6
 
7
7
  const PLUGIN_NAME = 'has-permission';
8
8
  const UNWATCH_NAME = `v-${PLUGIN_NAME}@unwatch`;
9
9
 
10
- export function setupPermissionPlugin(app: App) {
10
+ export function setupPermissionPlugin(app: App, permission: ReturnType<typeof createAdminPermission>) {
11
11
  app.directive<HTMLElement, TCodes>(PLUGIN_NAME, {
12
12
  mounted(el, binding) {
13
- console.log('🤡 / el:', el);
14
- const permissionStore = adminPlugin.deps.usePermissionStore();
15
-
16
13
  function updateVisibility() {
17
14
  const codes = binding.value;
18
- el.style.display = permissionStore.hasPermission(codes) ? '' : 'none';
15
+ el.style.display = permission.has(codes) ? '' : 'none';
19
16
  }
20
17
 
21
18
  (el as any)[UNWATCH_NAME] = watch(
22
- () => permissionStore.permissionCodesStr,
19
+ () => permission.codes,
23
20
  updateVisibility,
24
21
  { immediate: true },
25
22
  );
@@ -32,9 +29,7 @@ export function setupPermissionPlugin(app: App) {
32
29
  app.use({
33
30
  install(app) {
34
31
  app.config.globalProperties.$hasPermission = (codes: TCodes) => {
35
- const permissionStore = adminPlugin.deps.usePermissionStore();
36
-
37
- return permissionStore.hasPermission(codes);
32
+ return permission.has(codes);
38
33
  };
39
34
  },
40
35
  });
@@ -1,3 +1,2 @@
1
1
  export { setupPageProgress } from './pageProgress';
2
2
  export { setupPageTitle } from './pageTitle';
3
- export { setupCollapseMenu } from './collapseMenu';
@@ -1,3 +1,4 @@
1
+ /* 已弃用 */
1
2
  export { createUsePageStore } from './createUsePageStore';
2
3
  export { createUseMenuStore } from './createUseMenuStore';
3
4
  export { createUsePageTabStore } from './createUsePageTabStore';
@@ -103,7 +103,7 @@
103
103
  z-index: 2;
104
104
  margin-bottom: -9px !important;
105
105
  background-color: var(--pagination-bg-color);
106
- box-shadow: 0 0 0 16px var(--pagination-bg-color);
106
+ box-shadow: 0 0 0 14.5px var(--pagination-bg-color);
107
107
  }
108
108
  }
109
109
 
@@ -1,5 +1,5 @@
1
- @import './classCover.scss';
2
- @import './globalCover.scss';
1
+ @use './classCover';
2
+ @use './globalCover';
3
3
 
4
4
  @media (pointer: fine) {
5
5
 
@@ -1,6 +1,6 @@
1
1
  import { Modal as AntModal } from 'ant-design-vue';
2
- import { tryOnUnmounted } from '@vueuse/core';
3
- import { createVNode, defineComponent, isProxy, onMounted, reactive, toRef, toRefs } from 'vue';
2
+ import { tryOnBeforeUnmount } from '@vueuse/core';
3
+ import { createVNode, defineComponent, isProxy, onDeactivated, onMounted, reactive, toRef, toRefs } from 'vue';
4
4
  import type { Component } from 'vue';
5
5
  import type { ModalProps } from 'ant-design-vue';
6
6
  import type { ComponentEmit, ComponentProps } from 'vue-component-type-helpers';
@@ -53,8 +53,10 @@ export function useAntdModal<Comp extends Component>(
53
53
  ...defaultModalProps,
54
54
  ...isProxy(modalProps) ? toRefs(modalProps) : modalProps,
55
55
  confirmLoading: toRef(() => (refs.comp as any)?.loading),
56
- onOk: (e: MouseEvent) => {
57
- (refs.comp as any)?.confirm?.(e);
56
+ onOk: async (e: MouseEvent) => {
57
+ const comp = refs.comp as any;
58
+ const isClosed = await comp?.confirm?.(e, (data: any) => promiseResolvers?.resolve(data));
59
+ // isClosed === true && (_modalProps.open = false);
58
60
  modalProps.onOk?.(e);
59
61
  },
60
62
  });
@@ -72,34 +74,45 @@ export function useAntdModal<Comp extends Component>(
72
74
  newCompProps?: typeof compProps,
73
75
  newAntdModalProps?: Omit<Partial<ModalProps>, 'open'>,
74
76
  ) => {
75
- Object.assign(_modalProps, newAntdModalProps);
76
- Object.assign(compProps, newCompProps);
77
-
78
77
  if (_modalProps.open) {
79
78
  // return Promise.reject(new Error('Modal is already open'));
80
79
  return;
81
80
  }
82
81
 
82
+ Object.assign(_modalProps, newAntdModalProps);
83
+
84
+ // eslint-disable-next-line no-lone-blocks
85
+ {
86
+ if (!(comp as any).props) {
87
+ Object.keys(compProps).forEach((key) => {
88
+ if (!['ref', 'onClose', 'onConfirm'].includes(key))
89
+ delete compProps[key];
90
+ });
91
+ }
92
+
93
+ Object.assign(compProps, newCompProps);
94
+ }
95
+
83
96
  promiseResolvers = Promise.withResolvers();
84
97
  _modalProps.open = true;
85
98
 
86
99
  return promiseResolvers.promise;
87
100
  };
88
- const confirm = (ev: any) => {
101
+ const onConfirm = (ev: any) => {
89
102
  _modalProps.open = false;
90
103
  promiseResolvers.resolve(ev);
91
104
  };
92
- const close = (reason?: any) => {
105
+ const onClose = (reason?: any) => {
93
106
  _modalProps.open = false;
94
107
  _onClose(reason);
95
108
  };
96
109
 
97
110
  const PresetComponent = defineComponent({
98
- setup() {
111
+ setup(props) {
99
112
  onMounted(() => _modalProps.opener?.(open as any));
100
113
 
101
114
  return () => {
102
- return createVNode(AntModal, _modalProps, {
115
+ return createVNode(AntModal, { ..._modalProps, ...props }, {
103
116
  [modalSlotName]: () => createVNode(_comp.is, compProps as any),
104
117
  });
105
118
  };
@@ -111,10 +124,11 @@ export function useAntdModal<Comp extends Component>(
111
124
  !visiable && _onClose();
112
125
  };
113
126
  (compProps as any).ref = setRefs.comp;
114
- (compProps as any).onClose = close;
115
- (compProps as any).onConfirm = confirm;
127
+ (compProps as any).onClose = onClose;
128
+ (compProps as any).onConfirm = onConfirm;
116
129
 
117
- tryOnUnmounted(_onClose);
130
+ tryOnBeforeUnmount(_onClose);
131
+ onDeactivated(_onClose);
118
132
 
119
133
  return {
120
134
  PresetComponent,
@@ -124,6 +138,6 @@ export function useAntdModal<Comp extends Component>(
124
138
  return refs.comp;
125
139
  },
126
140
  open,
127
- close,
141
+ close: onClose,
128
142
  };
129
143
  }
@@ -1,5 +1,6 @@
1
1
  import { computed, reactive, ref } from 'vue';
2
2
  import { pick } from 'lodash-es';
3
+ import { useTableColumns } from './useTableColumns'
3
4
  import type { UseQueryReturnType } from '@tanstack/vue-query';
4
5
  import type { Table, TableProps } from 'ant-design-vue';
5
6
  import type { ColumnType, FilterValue } from 'ant-design-vue/es/table/interface';
@@ -10,6 +11,12 @@ interface ISorter {
10
11
  order?: string
11
12
  }
12
13
 
14
+ type GetRecordType<T> = T extends UseQueryReturnType<infer D, any>
15
+ ? D extends Api.PageData
16
+ ? NonNullable<D['list']>[0]
17
+ : never
18
+ : never;
19
+
13
20
  const defaultPageSizeOptions = ['10', '20', '50', '100'];
14
21
 
15
22
  export function useAntdTable<
@@ -54,7 +61,7 @@ export function useAntdTable<
54
61
  }
55
62
  }
56
63
  };
57
- const defineColumns = (columnsGetter: () => LocalColumnsType) => computed(columnsGetter);
64
+ const { defineColumns } = useTableColumns<LocalColumnsType>();
58
65
  const defineRowSelection = (rowSelectionGetter: () => LocalTableRowSelection = () => ({})) => {
59
66
  const rowSelection = reactive(rowSelectionGetter());
60
67
 
@@ -78,6 +85,7 @@ export function useAntdTable<
78
85
  showSizeChanger: true,
79
86
  showQuickJumper: true,
80
87
  pageSizeOptions,
88
+ size: 'default',
81
89
  showTotal: total => `共 ${total} 条`,
82
90
  },
83
91
  loading: isLoading.value,
@@ -112,7 +120,7 @@ export function useAntdTable<
112
120
  dataIndexs,
113
121
  /** 【类型辅助】bodyCell 插槽数据的精确类型描述 */
114
122
  bodyCellType,
115
- /** 【类型辅助】用于定义出类型精确的 columns */
123
+ /** 用于定义 columns */
116
124
  defineColumns,
117
125
  /** 【类型辅助】用于定义出类型精确的 rowSelection */
118
126
  defineRowSelection,
@@ -121,8 +129,3 @@ export function useAntdTable<
121
129
  };
122
130
  }
123
131
 
124
- type GetRecordType<T> = T extends UseQueryReturnType<infer D, any>
125
- ? D extends Api.PageData
126
- ? NonNullable<D['list']>[0]
127
- : never
128
- : never;
@@ -20,6 +20,13 @@ export function useAntdTheme(mode: Ref<string>, config: Ref<Record<string, any>>
20
20
  const token: ThemeConfig['token'] = {
21
21
  ...algorithm({ ...theme.defaultSeed, colorPrimary: _config.colors.primary.DEFAULT, colorInfo: '#17B2FF' }),
22
22
  borderRadius: 4,
23
+
24
+ motionUnit: 0.05,
25
+ motionBase: 0,
26
+ motionDurationFast: "0.05s",
27
+ motionDurationMid: "0.1s",
28
+ motionDurationSlow: "0.15s",
29
+
23
30
  screenXS,
24
31
  screenXSMin: screenXS,
25
32
  screenXSMax: screenSM - 0.1,
@@ -0,0 +1,83 @@
1
+ import { computed, reactive, ref, toValue, watch, provide } from 'vue';
2
+ import { extendRef } from '@vueuse/core';
3
+
4
+ export type ColumnConfig = { dataIndex: string, visible: boolean, compact: boolean }
5
+ interface TableConfig {
6
+ columns: ColumnConfig[];
7
+ }
8
+
9
+ const tableConfigStore = {
10
+ key: 'TABLE_CONFIG',
11
+ genTableId(columns: { dataIndex: string }[], extra = '') {
12
+ const indexsStr = columns.map((col: any) => col.dataIndex).join(',') + extra;
13
+ let hash = 0;
14
+ let len = 12;
15
+
16
+ for (let i = 0; i < indexsStr.length; i++) {
17
+ const ch = indexsStr.charCodeAt(i);
18
+ hash = (hash << 5) - hash + ch;
19
+ hash |= 0;
20
+ }
21
+
22
+ const base36 = (hash >>> 0).toString(36);
23
+ return base36.length >= len ? base36.slice(0, len) : base36.padStart(len, '0');
24
+ },
25
+ getTableConfig(tableId: string): TableConfig | null {
26
+ const configStr = localStorage.getItem(this.key);
27
+ const allConfig = configStr ? JSON.parse(configStr) : {};
28
+ return allConfig[tableId] || null;
29
+ },
30
+ setTableConfig(tableId: string, config: Partial<TableConfig>) {
31
+ const configStr = localStorage.getItem(this.key);
32
+ const allConfig = configStr ? JSON.parse(configStr) : {};
33
+ allConfig[tableId] = Object.assign({}, allConfig[tableId] || {}, config);
34
+ localStorage.setItem(this.key, JSON.stringify(allConfig));
35
+ }
36
+ }
37
+
38
+ export function useTableColumns<LCT extends any[]>() {
39
+ const columnsConfig = ref<Array<ColumnConfig> | null | undefined>();
40
+ let originalColumns: LCT | null = null;
41
+ let tableId = ''
42
+
43
+ const defineColumns = (columnsGetter: () => LCT) => {
44
+ originalColumns = toValue(columnsGetter) || [] as unknown as LCT;
45
+ tableId = tableConfigStore.genTableId(originalColumns);
46
+ columnsConfig.value = tableConfigStore.getTableConfig(tableId)?.columns;
47
+
48
+ provide('tableColumns', originalColumns);
49
+ provide('tableColumnsConfig', columnsConfig);
50
+
51
+ const columns = computed(() => {
52
+ const config = columnsConfig.value;
53
+ let columns = columnsGetter();
54
+
55
+ if (config?.length) {
56
+ columns = columns.filter((col: any) => config.find(c => c.dataIndex === col.dataIndex)?.visible !== false) as LCT;
57
+ columns = columns.map((col: any) => {
58
+ const cf = config.find(c => c.dataIndex === col.dataIndex);
59
+ return { ...col, compact: cf?.compact };
60
+ }) as LCT;
61
+ const dataIndexOrderMap = new Map(config.map((c, i) => [c.dataIndex, i]));
62
+ columns.sort((a, b) => {
63
+ const indexA = dataIndexOrderMap.get(a.dataIndex);
64
+ const indexB = dataIndexOrderMap.get(b.dataIndex);
65
+ return (indexA ?? Infinity) - (indexB ?? Infinity);
66
+ });
67
+ }
68
+
69
+ return columns;
70
+ })
71
+
72
+ return extendRef(columns, {});
73
+ };
74
+
75
+ watch(columnsConfig, (newConfig) => {
76
+ newConfig && tableConfigStore.setTableConfig(tableId, { columns: newConfig });
77
+ });
78
+
79
+ return {
80
+ defineColumns,
81
+ tableColumnsConfig: columnsConfig,
82
+ };
83
+ }
package/antd/index.ts CHANGED
@@ -5,4 +5,4 @@ export { useAntdForm } from './hooks/useAntdForm';
5
5
  export { useAntdTheme } from './hooks/useAntdTheme';
6
6
  export { createAntdModal } from './hooks/createAntdModal';
7
7
  export { default as InputNumberRange } from './components/InputNumberRange.vue';
8
- export type { SchemaConfig, ItemSchema } from './hooks/useAntdForm.ts';
8
+ export type { SchemaConfig, ItemSchema } from './hooks/useAntdForm.ts';
package/libs/bignumber.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { default } from 'bignumber.js';
2
- export { BigNumber } from 'bignumber.js';
2
+ export { type BigNumber } from 'bignumber.js';
package/libs/dayjs.ts CHANGED
@@ -5,15 +5,29 @@ import weekday from 'dayjs/esm/plugin/weekday';
5
5
  import localeData from 'dayjs/esm/plugin/localeData';
6
6
  import utc from 'dayjs/esm/plugin/utc';
7
7
  import tz from 'dayjs/esm/plugin/timezone';
8
+ import weekOfYear from 'dayjs/esm/plugin/weekOfYear';
9
+ import weekYear from 'dayjs/esm/plugin/weekYear';
10
+ import quarterOfYear from 'dayjs/esm/plugin/quarterOfYear';
11
+ import advancedFormat from 'dayjs/esm/plugin/advancedFormat';
12
+ import customParseFormat from 'dayjs/esm/plugin/customParseFormat';
13
+ import updateLocale from 'dayjs/esm/plugin/updateLocale';
14
+ import isBetween from 'dayjs/esm/plugin/isBetween';
8
15
  import 'dayjs/esm/locale/zh';
9
16
  import 'dayjs/esm/locale/en';
10
17
 
11
18
  export type { Dayjs, PluginFunc, UnitType, UnitTypeLong, UnitTypeLongPlural, UnitTypeShort, QUnitType, ConfigType, ConfigTypeMap, OpUnitType, OptionType, ManipulateType } from 'dayjs';
12
19
  export default dayjs;
13
20
 
14
- dayjs.locale('zh');
15
21
  dayjs.extend(relativeTime);
16
22
  dayjs.extend(weekday);
17
23
  dayjs.extend(localeData);
18
24
  dayjs.extend(utc);
19
25
  dayjs.extend(tz);
26
+ dayjs.extend(weekOfYear);
27
+ dayjs.extend(weekYear);
28
+ dayjs.extend(quarterOfYear);
29
+ dayjs.extend(advancedFormat);
30
+ dayjs.extend(customParseFormat);
31
+ dayjs.extend(updateLocale);
32
+ dayjs.extend(isBetween);
33
+ dayjs.locale('zh');
@@ -0,0 +1 @@
1
+ export * from '@fingerprintjs/fingerprintjs';