@peng_kai/kit 0.3.0-beta.1 → 0.3.0-beta.11

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 (53) hide show
  1. package/.vscode/settings.json +2 -2
  2. package/admin/components/currency/src/CurrencyIcon.vue +35 -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/helpers.ts +223 -0
  7. package/admin/components/date/index.ts +4 -0
  8. package/admin/components/filter/src/FilterReset.vue +39 -7
  9. package/admin/components/filter/src/more/TableSetting.vue +63 -0
  10. package/admin/components/provider/Admin.vue +17 -0
  11. package/admin/components/provider/admin-permission.ts +48 -0
  12. package/admin/components/provider/admin-router.ts +358 -0
  13. package/admin/components/provider/index.ts +3 -0
  14. package/admin/components/text/index.ts +2 -0
  15. package/admin/components/text/src/Amount.v2.vue +127 -0
  16. package/admin/components/text/src/Datetime.vue +15 -11
  17. package/admin/components/text/src/Num.vue +192 -0
  18. package/admin/components/upload/src/customRequests.ts +1 -1
  19. package/admin/components/upload/src/helpers.ts +1 -0
  20. package/admin/layout/large/Breadcrumb.vue +10 -23
  21. package/admin/layout/large/Content.vue +9 -6
  22. package/admin/layout/large/Layout.vue +129 -0
  23. package/admin/layout/large/Menu.vue +24 -17
  24. package/admin/layout/large/Notice.vue +140 -0
  25. package/admin/layout/large/Tabs.vue +177 -0
  26. package/admin/layout/large/index.ts +61 -1
  27. package/admin/layout/large/y682.mp3 +0 -0
  28. package/admin/permission/routerGuard.ts +15 -8
  29. package/admin/permission/vuePlugin.ts +5 -10
  30. package/admin/route-guards/index.ts +0 -1
  31. package/admin/stores/index.ts +1 -0
  32. package/admin/styles/classCover.scss +1 -1
  33. package/admin/styles/globalCover.scss +4 -0
  34. package/admin/styles/index.scss +2 -2
  35. package/antd/hooks/useAntdForm.helpers.ts +10 -1
  36. package/antd/hooks/useAntdModal.ts +20 -8
  37. package/antd/hooks/useAntdTable.ts +52 -2
  38. package/antd/hooks/useAntdTheme.ts +7 -0
  39. package/antd/index.ts +1 -1
  40. package/libs/bignumber.ts +1 -1
  41. package/libs/dayjs.ts +11 -1
  42. package/libs/fingerprintjs.ts +1 -0
  43. package/package.json +95 -95
  44. package/request/interceptors/getDeviceInfo.ts +9 -0
  45. package/utils/LocaleManager.ts +1 -1
  46. package/utils/locale/LocaleManager.ts +2 -1
  47. package/utils/number.ts +1 -2
  48. package/utils/storage.ts +31 -0
  49. package/utils/string.ts +14 -0
  50. package/utils/upload/AwsS3.ts +10 -3
  51. package/admin/layout/large/PageTab.vue +0 -70
  52. package/admin/route-guards/collapseMenu.ts +0 -11
  53. package/libs/a-calc.ts +0 -1
@@ -0,0 +1,223 @@
1
+ import dayjs, { type Dayjs } from 'dayjs/esm';
2
+ import { tryOnScopeDispose } from '@vueuse/core';
3
+ import { computed, reactive, watch, ref, type Ref } from 'vue';
4
+ import type { Simplify } from 'type-fest';
5
+
6
+ export function getTtaTimeZone() {
7
+ const win = window as any;
8
+ const key = '__APP_TZ__';
9
+
10
+ return (win[key] || (win[key] = ref(dayjs.tz.guess()))) as Ref<string>;
11
+ }
12
+
13
+ export const onTtaTimeZone = (() => {
14
+ const ttaTz = getTtaTimeZone();
15
+ const cbList: any[] = [];
16
+
17
+ watch(ttaTz, (newTz, oldTz) => {
18
+ newTz !== oldTz && cbList.forEach((cb) => cb(newTz));
19
+ }, { flush: 'post' });
20
+
21
+ return (cb: (tz: string) => void) => {
22
+ cbList.push(cb);
23
+
24
+ const unbind = () => {
25
+ const index = cbList.indexOf(cb);
26
+ if (index !== -1) {
27
+ cbList.splice(index, 1);
28
+ }
29
+ }
30
+
31
+ tryOnScopeDispose(unbind);
32
+
33
+ return unbind;
34
+ }
35
+ })();
36
+
37
+ type OfDayType = Dayjs | undefined | null;
38
+ type UnitType = dayjs.QUnitType | dayjs.OpUnitType;
39
+ export const dayOf = {
40
+ rangeTs<KS extends readonly [string, string] = ['start_time', 'end_time']>(
41
+ unit: UnitType,
42
+ day: Array<OfDayType> | undefined | null,
43
+ keys?: KS
44
+ ): KS extends readonly [string, string]
45
+ ? Simplify<{ [K in KS[0]]: number } & { [K in KS[1]]: number }>
46
+ : { start_time: number; end_time: number } {
47
+ if (!day || !day[0] || !day[1])
48
+ return undefined as any;
49
+
50
+ const tz = getTtaTimeZone().value;
51
+ const _keys = (Array.isArray(keys) && keys?.length > 1) ? keys : ['start_time', 'end_time'];
52
+
53
+ return {
54
+ [_keys[0]]: dayjs(day[0].valueOf()).tz(tz).startOf(unit).valueOf(),
55
+ [_keys[1]]: dayjs(day[1].valueOf()).tz(tz).endOf(unit).valueOf(),
56
+ } as any;
57
+ },
58
+ start(unit: UnitType, day: OfDayType) {
59
+ if (!day)
60
+ return undefined;
61
+
62
+ const tz = getTtaTimeZone().value;
63
+ return dayjs(day.valueOf()).tz(tz).startOf(unit);
64
+ },
65
+ startTs(unit: UnitType, day: OfDayType) {
66
+ this.start(unit, day);
67
+ },
68
+ end(unit: UnitType, day: OfDayType) {
69
+ if (!day)
70
+ return undefined;
71
+
72
+ const tz = getTtaTimeZone().value;
73
+ return dayjs(day.valueOf()).tz(tz).endOf(unit);
74
+ },
75
+ endTs(unit: UnitType, day: OfDayType) {
76
+ this.end(unit, day);
77
+ },
78
+ }
79
+
80
+ /** 为 DatePicker 组件提供响应式时区支持,当时区更新时,自动调整时间戳 */
81
+ export function useDatePickerPropsForTz(
82
+ value: Ref<Dayjs | undefined>,
83
+ props: {
84
+ createPresets?: (day: Dayjs) => { label: string; value: Dayjs }[];
85
+ } = {},
86
+ tz?: Ref<string>,
87
+ ) {
88
+ // 默认值
89
+ props.createPresets ??= () => {
90
+ return [
91
+ // { label: '昨天', value: day.subtract(1, 'day').startOf('day') },
92
+ // { label: '上周', value: day.subtract(1, 'week').startOf('week') },
93
+ // { label: '上个月', value: day.subtract(1, 'month').startOf('month') },
94
+ ];
95
+ };
96
+ const _tz = tz || getTtaTimeZone();
97
+ const nowDay = computed(() => dayjs().tz(_tz.value));
98
+ const _value = computed(() => value.value ? dayjs(value.value.valueOf()).tz(_tz.value) : undefined);
99
+
100
+ watch(_tz, (newV, oldV) => {
101
+ if (!value.value) return;
102
+
103
+ const newTzOffset = dayjs().tz(newV).utcOffset();
104
+ const oldTzOffset = dayjs().tz(oldV).utcOffset();
105
+ value.value = dayjs(value.value.valueOf() + (oldTzOffset - newTzOffset) * 60 * 1000);
106
+ });
107
+
108
+ return reactive({
109
+ 'value': _value,
110
+ 'onUpdate:value': (value: any) => value.value = dayjs(value).valueOf(),
111
+ 'presets': computed(() => props.createPresets!(nowDay.value)),
112
+ });
113
+ }
114
+
115
+ export function useRangePickerPropsForTz(
116
+ value: Ref<[string, string] | [Dayjs, Dayjs] | undefined>,
117
+ props: {
118
+ createPresets?: (day: Dayjs) => { label: string; value: Dayjs[] }[];
119
+ } = {},
120
+ tz?: Ref<string>,
121
+ ) {
122
+ const _tz = tz || getTtaTimeZone();
123
+
124
+ // 默认值
125
+ props.createPresets ??= (day: Dayjs) => {
126
+ return [
127
+ { label: '今天', value: [day.startOf('day'), day.endOf('day')] },
128
+ { label: '昨天', value: [day.subtract(1, 'day').startOf('day'), day.subtract(1, 'day').endOf('day')] },
129
+ { label: '前一天', value: [day.subtract(1, 'day').startOf('day'), day.subtract(1, 'day').endOf('day')] },
130
+ { label: '后一天', value: [day.add(1, 'day').startOf('day'), day.add(1, 'day').endOf('day')] },
131
+ { label: '本周', value: [day.startOf('week'), day.endOf('week')] },
132
+ { label: '上周', value: [day.subtract(1, 'week').startOf('week'), day.subtract(1, 'week').endOf('week')] },
133
+ { label: '本月', value: [day.startOf('month'), day.endOf('month')] },
134
+ { label: '上个月', value: [day.subtract(1, 'month').startOf('month'), day.subtract(1, 'month').endOf('month')] },
135
+ { label: '上上月', value: [day.subtract(2, 'month').startOf('month'), day.subtract(2, 'month').endOf('month')] },
136
+ { label: '近7天', value: [day.subtract(6, 'day').startOf('day'), day.endOf('day')] },
137
+ { label: '近30天', value: [day.subtract(29, 'day').startOf('day'), day.endOf('day')] },
138
+ ];
139
+ };
140
+ const nowDay = computed(() => dayjs().tz(_tz.value));
141
+ const _value = computed(() => {
142
+ const v = value.value;
143
+ if (v && v[0] && v[1]) {
144
+ return [
145
+ dayjs(v[0].valueOf()).tz(_tz.value),
146
+ dayjs(v[1].valueOf()).tz(_tz.value),
147
+ ] as [Dayjs, Dayjs];
148
+ }
149
+ return undefined;
150
+ });
151
+
152
+ watch(_tz, (newV, oldV) => {
153
+ const v = value.value;
154
+ if (!v || !v[0] || !v[1]) return;
155
+
156
+ const newTzOffset = dayjs().tz(newV).utcOffset();
157
+ const oldTzOffset = dayjs().tz(oldV).utcOffset();
158
+ value.value = [
159
+ dayjs(v[0]).add((oldTzOffset - newTzOffset) * 60 * 1000),
160
+ dayjs(v[1]).add((oldTzOffset - newTzOffset) * 60 * 1000),
161
+ ]
162
+ console.log('🤡 / value.value:', value.value[0].valueOf())
163
+ });
164
+
165
+ return reactive({
166
+ 'value': _value,
167
+ 'onUpdate:value': (newVal: any) => value.value = newVal,
168
+ 'presets': computed(() => props.createPresets!(nowDay.value)),
169
+ });
170
+ };
171
+
172
+ /** 为 RangePicker 组件提供响应式时区支持,当时区更新时,自动调整时间戳 */
173
+ export function useRangePickerPropsForTz_old(
174
+ ts: [Ref<number | undefined>, Ref<number | undefined>],
175
+ props: {
176
+ createPresets?: (day: Dayjs) => { label: string; value: Dayjs[] }[];
177
+ } = {},
178
+ tz?: Ref<string>,
179
+ ) {
180
+ // 默认值
181
+ props.createPresets ??= (day: Dayjs) => {
182
+ return [
183
+ { label: '今天', value: [day.startOf('day'), day.endOf('day')] },
184
+ { label: '昨天', value: [day.subtract(1, 'day').startOf('day'), day.subtract(1, 'day').endOf('day')] },
185
+ { label: '前一天', value: [day.subtract(1, 'day').startOf('day'), day.subtract(1, 'day').endOf('day')] },
186
+ { label: '后一天', value: [day.add(1, 'day').startOf('day'), day.add(1, 'day').endOf('day')] },
187
+ { label: '本周', value: [day.startOf('week'), day.endOf('week')] },
188
+ { label: '上周', value: [day.subtract(1, 'week').startOf('week'), day.subtract(1, 'week').endOf('week')] },
189
+ { label: '本月', value: [day.startOf('month'), day.endOf('month')] },
190
+ { label: '上个月', value: [day.subtract(1, 'month').startOf('month'), day.subtract(1, 'month').endOf('month')] },
191
+ { label: '上上月', value: [day.subtract(2, 'month').startOf('month'), day.subtract(2, 'month').endOf('month')] },
192
+ { label: '近7天', value: [day.subtract(6, 'day').startOf('day'), day.endOf('day')] },
193
+ { label: '近30天', value: [day.subtract(29, 'day').startOf('day'), day.endOf('day')] },
194
+ ];
195
+ };
196
+ const _tz = tz || getTtaTimeZone();
197
+ const nowDay = computed(() => dayjs().tz(_tz.value));
198
+ const inputDay = computed(() => {
199
+ console.log('🤡 / ts:', ts);
200
+ if (ts[0].value && ts[1].value)
201
+ return [dayjs(ts[0].value).tz(_tz.value), dayjs(ts[1].value).tz(_tz.value)] as [Dayjs, Dayjs];
202
+ return undefined;
203
+ });
204
+
205
+ watch(_tz, (newV, oldV) => {
206
+ if (!ts[0].value || !ts[1].value) return;
207
+
208
+ const newTzOffset = dayjs().tz(newV).utcOffset();
209
+ const oldTzOffset = dayjs().tz(oldV).utcOffset();
210
+ ts[0].value = ts[0].value + (oldTzOffset - newTzOffset) * 60 * 1000;
211
+ ts[1].value = ts[1].value + (oldTzOffset - newTzOffset) * 60 * 1000;
212
+ })
213
+
214
+ return reactive({
215
+ 'value': inputDay,
216
+ 'onUpdate:value': (value: any) => {
217
+ console.log('🤡 / value:', value);
218
+ ts[0].value = dayjs(value[0]).valueOf();
219
+ ts[1].value = dayjs(value[1]).valueOf();
220
+ },
221
+ 'presets': computed(() => props.createPresets!(nowDay.value)),
222
+ });
223
+ };
@@ -0,0 +1,4 @@
1
+ export { default as TimeFieldSelectForLabel } from './TimeFieldSelectForLabel.vue';
2
+ export { default as TtaTimeZone } from './TtaTimeZone.vue';
3
+ export { default as PeriodPicker } from './PeriodPicker.vue';
4
+ export { useDatePickerPropsForTz, useRangePickerPropsForTz, getTtaTimeZone, onTtaTimeZone, dayOf } from './helpers';
@@ -1,12 +1,15 @@
1
1
  <script setup lang="ts">
2
- import { Button as AButton } from 'ant-design-vue';
2
+ import { Button, Menu, MenuItem, Dropdown, type TableColumnsType } from 'ant-design-vue';
3
3
  import { computed } from 'vue';
4
+ import { onTtaTimeZone } from '../../date';
5
+ import { TableSettingModal } from './more/TableSetting.vue'
4
6
 
5
7
  const props = withDefaults(defineProps<{
6
8
  loading?: boolean
7
9
  filterQuery?: any
8
10
  filterParams?: any
9
11
  filterForm?: any
12
+ tableColumns?: TableColumnsType<any>
10
13
  }>(), {
11
14
  loading: undefined,
12
15
  });
@@ -38,18 +41,47 @@ function reset() {
38
41
  props.filterParams?.update?.(true);
39
42
  emits('reset');
40
43
  }
44
+
45
+ function openTableSettingModal() {
46
+ const tableColumns = props.tableColumns?.map((x: any) => ({
47
+ ...x,
48
+ title: typeof x.title === 'string' ? x.title : x.dataIndex || '',
49
+ })) || [];
50
+
51
+ TableSettingModal.open({ tableColumns });
52
+ }
53
+
54
+ onTtaTimeZone(() => {
55
+ setTimeout(() => {
56
+ props.filterParams?.update?.(true);
57
+ }, 100)
58
+ });
41
59
  </script>
42
60
 
43
61
  <template>
44
62
  <div class="btns flex-none flex w-min ml-auto gap-2">
45
63
  <slot :loading="loading" :filter="filter" :reset="reset">
46
- <AButton class="filter-btn" type="primary" htmlType="submit" :loading="loading" @click="filter()">
64
+ <Button class="filter-btn" type="primary" htmlType="submit" :loading="loading" @click="filter()">
47
65
  查询
48
- </AButton>
49
- <AButton :disabled="loading" @click="reset()">
66
+ </Button>
67
+ <Button :disabled="loading" @click="reset()">
50
68
  重置
51
- </AButton>
69
+ </Button>
70
+ <Dropdown trigger="click" placement="bottomRight">
71
+ <Button class="px-1 w-8">
72
+ <i class="i-si:more-square-horiz-fill scale-130 transform-origin-bottom-center" />
73
+ </Button>
74
+ <template #overlay>
75
+ <Menu>
76
+ <MenuItem v-if="props.tableColumns" @click="openTableSettingModal">
77
+ 设置表格
78
+ </MenuItem>
79
+ </Menu>
80
+ </template>
81
+ </Dropdown>
52
82
  </slot>
83
+
84
+ <TableSettingModal.PresetComponent />
53
85
  </div>
54
86
  </template>
55
87
 
@@ -58,7 +90,7 @@ function reset() {
58
90
  :slotted(.filter-btn) {
59
91
  position: relative;
60
92
 
61
- .ant-btn-loading-icon{
93
+ .ant-btn-loading-icon {
62
94
  position: absolute;
63
95
  left: 50%;
64
96
  transform: translateX(-50%);
@@ -68,7 +100,7 @@ function reset() {
68
100
  margin-inline-end: 0 !important;
69
101
  }
70
102
 
71
- + span {
103
+ +span {
72
104
  visibility: hidden;
73
105
  }
74
106
  }
@@ -0,0 +1,63 @@
1
+ <script lang="ts">
2
+ import { Button, Switch, Divider, Select, type TableColumnsType } from 'ant-design-vue';
3
+ import { reactive, defineAsyncComponent, ref } from 'vue';
4
+ import { useDragAndDrop } from "fluid-dnd/vue";
5
+ import { useAntdModal } from '../../../../../antd/hooks/useAntdModal';
6
+
7
+ export const TableSettingModal = useAntdModal(
8
+ // eslint-disable-next-line import/no-self-import
9
+ defineAsyncComponent(() => import('./TableSetting.vue')),
10
+ {
11
+ title: '表格设置',
12
+ width: 500,
13
+ // footer: null,
14
+ centered: true,
15
+ maskClosable: false,
16
+ }
17
+ );
18
+
19
+ const showTypeOptions = [
20
+ { label: '显示', value: 'show' },
21
+ { label: '简略', value: 'simple' },
22
+ { label: '隐藏', value: 'hidden' },
23
+ ];
24
+ </script>
25
+
26
+ <script lang="ts" setup>
27
+ const props = defineProps<{
28
+ tableColumns: TableColumnsType<any>
29
+ }>();
30
+ const columnsConfig = ref(props.tableColumns.map((col: any) => ({
31
+ title: col.title as string,
32
+ dataIndex: col.dataIndex as string,
33
+ visible: col.visible !== false,
34
+ })));
35
+
36
+ const [$ctn] = useDragAndDrop(columnsConfig, {
37
+ handlerSelector: '.drag-handler',
38
+ draggingClass: 'op-50',
39
+ direction: 'vertical',
40
+ });
41
+ </script>
42
+
43
+ <template>
44
+ <div>
45
+ <div>
46
+ <Divider>列设置</Divider>
47
+ <div ref="$ctn">
48
+ <div v-for="(col, i) in columnsConfig" :key="col.dataIndex" :index="i" class="flex items-center gap-2 p-2 select-none">
49
+ <div class="drag-handler flex items-center justify-center cursor-move p-1 -m-2">
50
+ <i class="i-material-symbols:drag-indicator" />
51
+ </div>
52
+ <span class="mr-auto">{{ col.title }}</span>
53
+ <Select value="show" :options="showTypeOptions" />
54
+ <!-- <Switch checked-children="简略" un-checked-children="简略" />
55
+ <Switch checked-children="显示" un-checked-children="显示" />
56
+ <Button>上</Button>
57
+ <Button>下</Button> -->
58
+ </div>
59
+ </div>
60
+
61
+ </div>
62
+ </div>
63
+ </template>
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import type { Router } from 'vue-router';
3
+
4
+ export const admin = {};
5
+ </script>
6
+
7
+ <script setup lang="ts">
8
+ const props = defineProps<{
9
+ router: Router
10
+ }>();
11
+ </script>
12
+
13
+ <template>
14
+ <div>
15
+ <slot />
16
+ </div>
17
+ </template>
@@ -0,0 +1,48 @@
1
+ import { reactive, readonly, ref, watch } from 'vue';
2
+ import type { AsyncReturnType } from 'type-fest';
3
+
4
+ type TRole = () => Promise<Record<string, boolean> | null | undefined>;
5
+ interface TRoles {
6
+ main: TRole
7
+ [key: string]: TRole
8
+ }
9
+
10
+ export function createAdminPermission(roles: TRoles) {
11
+ const role = ref('main');
12
+ const permissionCodes = ref<AsyncReturnType<TRole>>();
13
+
14
+ /**
15
+ * 刷新权限
16
+ */
17
+ const refreshPermission = async () => {
18
+ permissionCodes.value = await roles?.[role.value]?.();
19
+
20
+ return permissionCodes.value;
21
+ };
22
+
23
+ /**
24
+ * 是否满足权限要求
25
+ * @param codes 权限 code
26
+ */
27
+ const hasPermission = (codes: string | string[]) => {
28
+ const _codes = Array.isArray(codes) ? codes : [codes];
29
+ const _permissionCodes = permissionCodes.value;
30
+
31
+ if (_permissionCodes === null)
32
+ return false;
33
+ else if (_permissionCodes === undefined)
34
+ // undefined 则表示没有权限要求,返回 true
35
+ return true;
36
+ else
37
+ return _codes.every(code => !!_permissionCodes[code]);
38
+ };
39
+
40
+ watch(role, refreshPermission, { immediate: true });
41
+
42
+ return reactive({
43
+ codes: readonly(permissionCodes),
44
+ role,
45
+ has: hasPermission,
46
+ refresh: refreshPermission,
47
+ });
48
+ }