@peng_kai/kit 0.3.0-beta.3 → 0.3.0-beta.31

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 (61) 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 +52 -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 +19 -6
  17. package/admin/components/rich-text/src/editorConfig.ts +76 -1
  18. package/admin/components/settings/src/SchemaForm.vue +5 -4
  19. package/admin/components/settings/src/Settings.vue +1 -1
  20. package/admin/components/text/index.ts +2 -0
  21. package/admin/components/text/src/Amount.v2.vue +131 -0
  22. package/admin/components/text/src/Datetime.vue +17 -12
  23. package/admin/components/text/src/IP.vue +17 -3
  24. package/admin/components/text/src/Num.vue +192 -0
  25. package/admin/layout/large/Breadcrumb.vue +10 -23
  26. package/admin/layout/large/Content.vue +9 -6
  27. package/admin/layout/large/Layout.vue +129 -0
  28. package/admin/layout/large/Menu.vue +24 -17
  29. package/admin/layout/large/Notice.vue +152 -0
  30. package/admin/layout/large/Tabs.vue +183 -0
  31. package/admin/layout/large/index.ts +61 -1
  32. package/admin/layout/large/y682.mp3 +0 -0
  33. package/admin/permission/routerGuard.ts +24 -11
  34. package/admin/permission/vuePlugin.ts +5 -10
  35. package/admin/route-guards/index.ts +0 -1
  36. package/admin/stores/index.ts +1 -0
  37. package/admin/styles/classCover.scss +1 -1
  38. package/admin/styles/index.scss +2 -2
  39. package/antd/hooks/useAntdModal.ts +27 -13
  40. package/antd/hooks/useAntdTable.ts +10 -7
  41. package/antd/hooks/useAntdTheme.ts +7 -0
  42. package/antd/hooks/useTableColumns.ts +83 -0
  43. package/antd/index.ts +1 -1
  44. package/libs/bignumber.ts +1 -1
  45. package/libs/dayjs.ts +16 -2
  46. package/libs/fingerprintjs.ts +1 -0
  47. package/package.json +64 -95
  48. package/request/interceptors/getDeviceInfo.ts +14 -0
  49. package/utils/LocaleManager.ts +1 -1
  50. package/utils/index.ts +1 -4
  51. package/utils/locale/LocaleManager.ts +2 -1
  52. package/utils/locale/helpers.ts +9 -0
  53. package/utils/number.ts +8 -10
  54. package/utils/storage.ts +31 -0
  55. package/utils/string.ts +1 -2
  56. package/utils/upload/AwsS3.ts +11 -4
  57. package/admin/layout/large/PageTab.vue +0 -70
  58. package/admin/route-guards/collapseMenu.ts +0 -11
  59. package/libs/a-calc.ts +0 -1
  60. package/vue/components/test/KitTest.vue +0 -9
  61. package/vue/components/test/testStore.ts +0 -11
@@ -0,0 +1,250 @@
1
+ import dayjs, { type Dayjs } from '../../../libs/dayjs';
2
+ import { tryOnScopeDispose } from '@vueuse/core';
3
+ import { computed, reactive, watch, customRef, type Ref } from 'vue';
4
+ import type { Simplify } from 'type-fest';
5
+
6
+ /**
7
+ * 获取应用的时区,默认使用浏览器的时区
8
+ * @returns 返回一个响应式的时区字符串
9
+ */
10
+ export function getTtaTimeZone() {
11
+ const win = window as any;
12
+ const key = '__APP_TZ__';
13
+
14
+ if (!win[key]) {
15
+ win[key] = customRef<string>((track, trigger) => {
16
+ return {
17
+ get() {
18
+ let value = localStorage.getItem(key);
19
+ if (!value) {
20
+ value = dayjs.tz.guess();
21
+ localStorage.setItem(key, value);
22
+ }
23
+ track();
24
+ return value;
25
+ },
26
+ set(newValue: string) {
27
+ localStorage.setItem(key, newValue);
28
+ trigger();
29
+ }
30
+ }
31
+ })
32
+ }
33
+
34
+ return win[key] as Ref<string>;
35
+ }
36
+
37
+ /**
38
+ * 监听应用的时区变化的函数
39
+ */
40
+ export const onTtaTimeZone = (() => {
41
+ const ttaTz = getTtaTimeZone();
42
+ const cbList: any[] = [];
43
+
44
+ watch(ttaTz, (newTz, oldTz) => {
45
+ newTz !== oldTz && cbList.forEach((cb) => cb(newTz));
46
+ }, { flush: 'post' });
47
+
48
+ return (cb: (tz: string) => void) => {
49
+ cbList.push(cb);
50
+
51
+ const unbind = () => {
52
+ const index = cbList.indexOf(cb);
53
+ if (index !== -1) {
54
+ cbList.splice(index, 1);
55
+ }
56
+ }
57
+
58
+ tryOnScopeDispose(unbind);
59
+
60
+ return unbind;
61
+ }
62
+ })();
63
+
64
+ export function standardizeTzParam(tz: string) {
65
+ if (tz === 'APP') {
66
+ return getTtaTimeZone().value;
67
+ } else if (tz === 'LOCAL') {
68
+ return dayjs.tz.guess();
69
+ }
70
+ return tz;
71
+ }
72
+
73
+ export function getLocalOffset(tz: string) {
74
+ const offsetTzMinutes = dayjs().tz(tz).utcOffset();
75
+ const offsetLocalMinutes = dayjs().utcOffset();
76
+ return (offsetLocalMinutes - offsetTzMinutes) * 60 * 1000;
77
+ }
78
+
79
+ type OfDayType = dayjs.Dayjs | undefined | null;
80
+ type UnitType = dayjs.QUnitType | dayjs.OpUnitType;
81
+ export const datetime = {
82
+ /**
83
+ * 将日期范围转换为 API 参数格式的时间戳范围
84
+ * @param days 时间范围元组,包含开始和结束时间
85
+ * @param unit 时间单位,默认为 'day'
86
+ * @param tz 时区字符串,默认为 'APP'(应用时区)。特殊值:UTC、LOCAL、APP
87
+ * @param keys 可选的键名元组,默认为 ['start_time', 'end_time']
88
+ * @returns 返回一个对象,包含开始和结束时间的时间戳
89
+ */
90
+ toRangeForApiParams<KS extends readonly [string, string] = ['start_time', 'end_time']>(
91
+ days: Array<OfDayType | undefined | null> | undefined | null,
92
+ unit: UnitType = 'day',
93
+ tz: string = 'APP',
94
+ keys?: KS
95
+ ) {
96
+ if (!days || !days[0] || !days[1])
97
+ return undefined;
98
+
99
+ const _tz = standardizeTzParam(tz);
100
+ const _keys = (Array.isArray(keys) && keys?.length > 1) ? keys : ['start_time', 'end_time'];
101
+ const offsetMS = getLocalOffset(_tz);
102
+
103
+ type RetType = KS extends readonly [string, string]
104
+ ? Simplify<{ [K in KS[0]]: number } & { [K in KS[1]]: number }>
105
+ : { start_time: number; end_time: number };
106
+
107
+ const startTime = dayjs(days[0].valueOf()).startOf(unit).valueOf() + offsetMS;
108
+ const endTime = dayjs(days[1].valueOf()).endOf(unit).valueOf() + offsetMS;
109
+
110
+ return {
111
+ [_keys[0]]: startTime,
112
+ [_keys[1]]: endTime,
113
+ } as RetType;
114
+ },
115
+ /**
116
+ * 将日期范围转换为表单使用的时间范围
117
+ * @param days 时间范围元组,包含开始和结束时间
118
+ * @param defaultRangeUnit 默认的时间范围单位,如果未提供,则返回 undefined
119
+ * @return 返回一个包含开始和结束时间的元组,格式为 dayjs 对象
120
+ */
121
+ toRangeForForm(
122
+ days: Array<OfDayType | string | number | undefined | null>,
123
+ defaultRangeUnit?: UnitType,
124
+ ) {
125
+ const getDefaultRange = () => {
126
+ const now = dayjs();
127
+ return defaultRangeUnit
128
+ ? [now.startOf(defaultRangeUnit), now.endOf(defaultRangeUnit)] as [dayjs.Dayjs, dayjs.Dayjs]
129
+ : undefined;
130
+ }
131
+
132
+ if (!days || !days[0] || !days[1])
133
+ return getDefaultRange();
134
+
135
+ const [sDay, eDay] = days.map(d => dayjs(typeof d === 'string' ? Number(d) : d?.valueOf()));
136
+
137
+ if (!sDay.isValid() || !eDay.isValid())
138
+ return getDefaultRange();
139
+
140
+ return [sDay, eDay] as [dayjs.Dayjs, dayjs.Dayjs];
141
+ },
142
+ /**
143
+ * 获取指定日期的开始时间
144
+ * @param unit 时间单位
145
+ * @param day 日期对象
146
+ * @param tz 时区字符串,默认为 'APP'(应用时区)。特殊值:UTC、LOCAL、APP
147
+ * @returns 返回指定日期的开始时间,格式为 dayjs 对象
148
+ */
149
+ ofStart(unit: UnitType, day: OfDayType, tz: string = 'APP') {
150
+ if (!day)
151
+ return undefined;
152
+
153
+ const _tz = standardizeTzParam(tz);
154
+ return dayjs(day.valueOf()).tz(_tz).startOf(unit);
155
+ },
156
+ /**
157
+ * 获取指定日期的结束时间
158
+ * @param unit 时间单位
159
+ * @param day 日期对象
160
+ * @param tz 时区字符串,默认为 'APP'(应用时区)。特殊值:UTC、LOCAL、APP
161
+ * @returns 返回指定日期的结束时间,格式为 dayjs 对象
162
+ */
163
+ ofEnd(unit: UnitType, day: OfDayType, tz: string = 'APP') {
164
+ if (!day)
165
+ return undefined;
166
+
167
+ const _tz = standardizeTzParam(tz);
168
+ return dayjs(day.valueOf()).tz(_tz).endOf(unit);
169
+ },
170
+ }
171
+
172
+ /** 为 DatePicker 组件提供响应式时区支持,当时区更新时,自动调整时间戳 */
173
+ function useDatePickerPropsForTz(
174
+ value: Ref<Dayjs | undefined>,
175
+ props: {
176
+ createPresets?: (day: Dayjs) => { label: string; value: Dayjs }[];
177
+ } = {},
178
+ tz?: Ref<string>,
179
+ ) {
180
+ // 默认值
181
+ props.createPresets ??= () => {
182
+ return [
183
+ // { label: '昨天', value: day.subtract(1, 'day').startOf('day') },
184
+ // { label: '上周', value: day.subtract(1, 'week').startOf('week') },
185
+ // { label: '上个月', value: day.subtract(1, 'month').startOf('month') },
186
+ ];
187
+ };
188
+ const _tz = tz || getTtaTimeZone();
189
+ const nowDay = computed(() => dayjs().tz(_tz.value));
190
+ const _value = computed(() => value.value ? dayjs(value.value.valueOf()).tz(_tz.value) : undefined);
191
+
192
+ watch(_tz, (newV, oldV) => {
193
+ if (!value.value) return;
194
+
195
+ const newTzOffset = dayjs().tz(newV).utcOffset();
196
+ const oldTzOffset = dayjs().tz(oldV).utcOffset();
197
+ value.value = dayjs(value.value.valueOf() + (oldTzOffset - newTzOffset) * 60 * 1000);
198
+ });
199
+
200
+ return reactive({
201
+ 'value': _value,
202
+ 'onUpdate:value': (value: any) => value.value = dayjs(value).valueOf(),
203
+ 'presets': computed(() => props.createPresets!(nowDay.value)),
204
+ });
205
+ }
206
+
207
+ function useRangePickerPropsForTz(
208
+ value: Ref<[string, string] | [Dayjs, Dayjs] | undefined>,
209
+ props: {
210
+ createPresets?: (day: Dayjs) => { label: string; value: Dayjs[] }[];
211
+ } = {},
212
+ tz?: Ref<string>,
213
+ ) {
214
+ const _tz = tz || getTtaTimeZone();
215
+
216
+ // 默认值
217
+ props.createPresets ??= (day: Dayjs) => {
218
+ return [
219
+ { label: '今天', value: [day.startOf('day'), day.endOf('day')] },
220
+ { label: '昨天', value: [day.subtract(1, 'day').startOf('day'), day.subtract(1, 'day').endOf('day')] },
221
+ { label: '前一天', value: [day.subtract(1, 'day').startOf('day'), day.subtract(1, 'day').endOf('day')] },
222
+ { label: '后一天', value: [day.add(1, 'day').startOf('day'), day.add(1, 'day').endOf('day')] },
223
+ { label: '本周', value: [day.startOf('week'), day.endOf('week')] },
224
+ { label: '上周', value: [day.subtract(1, 'week').startOf('week'), day.subtract(1, 'week').endOf('week')] },
225
+ { label: '本月', value: [day.startOf('month'), day.endOf('month')] },
226
+ { label: '上个月', value: [day.subtract(1, 'month').startOf('month'), day.subtract(1, 'month').endOf('month')] },
227
+ { label: '上上月', value: [day.subtract(2, 'month').startOf('month'), day.subtract(2, 'month').endOf('month')] },
228
+ { label: '近7天', value: [day.subtract(6, 'day').startOf('day'), day.endOf('day')] },
229
+ { label: '近30天', value: [day.subtract(29, 'day').startOf('day'), day.endOf('day')] },
230
+ ];
231
+ };
232
+ const _value = computed(() => {
233
+ const v = value.value;
234
+ if (v && v[0] && v[1]) {
235
+ return [
236
+ dayjs(v[0].valueOf()),
237
+ dayjs(v[1].valueOf()),
238
+ ] as [Dayjs, Dayjs];
239
+ }
240
+ return undefined;
241
+ });
242
+
243
+ return reactive({
244
+ 'value': _value,
245
+ 'onUpdate:value': (v: any) => value.value = v,
246
+ 'presets': computed(() => props.createPresets!(dayjs())),
247
+ // showTime: computed(() => ({ defaultValue: [dayjs().startOf('day'), dayjs().endOf('day')] })),
248
+ format: 'YY-MM-DD HH:mm:ss'
249
+ });
250
+ };
@@ -0,0 +1,6 @@
1
+ export { default as TimeFieldSelectForLabel } from './TimeFieldSelectForLabel.vue';
2
+ export { default as TtaTimeZone } from './TtaTimeZone.vue';
3
+ export { default as TtaTimeZoneSimple } from './TtaTimeZoneSimple.vue';
4
+ export { default as PeriodPicker } from './PeriodPicker.vue';
5
+ export { getTtaTimeZone, onTtaTimeZone, datetime } from './helpers';
6
+ export { antdRangePickerPresets, antdRangePickerShowTimeProps } from './presetProps';
@@ -0,0 +1,19 @@
1
+ import { type RangePicker } from 'ant-design-vue';
2
+ import dayjs, { type Dayjs } from '../../../libs/dayjs';
3
+ import { type ComponentProps } from 'vue-component-type-helpers'
4
+
5
+ export const antdRangePickerPresets = [
6
+ { label: '今天', key: 'today', get value() {return [dayjs().startOf('day'), dayjs().endOf('day')] } },
7
+ { label: '昨天', key: 'yesterday', get value() { return [dayjs().subtract(1, 'day').startOf('day'), dayjs().subtract(1, 'day').endOf('day')] } },
8
+ { label: '本月', key: 'thisMonth', get value() { return [dayjs().startOf('month'), dayjs().endOf('month')] } },
9
+ { label: '上个月', key: 'lastMonth', get value() { return [dayjs().subtract(1, 'month').startOf('month'), dayjs().subtract(1, 'month').endOf('month')] } },
10
+ { label: '近3个月', key: 'last3months', get value() { return [dayjs().subtract(2, 'month').startOf('month'), dayjs().endOf('month')] } },
11
+ { label: '近半年', key: 'last6months', get value() { return [dayjs().subtract(5, 'month').startOf('month'), dayjs().endOf('month')] } },
12
+ { label: '今年', key: 'thisYear', get value() { return [dayjs().startOf('year'), dayjs().endOf('month')] } },
13
+ ] as unknown as { label: string; key: string; value: [Dayjs, Dayjs] }[]
14
+
15
+ export const antdRangePickerShowTimeProps: ComponentProps<typeof RangePicker> = {
16
+ presets: antdRangePickerPresets,
17
+ showTime: { defaultValue: [dayjs().startOf('day'), dayjs().endOf('day')] },
18
+ format: 'YY-MM-DD HH:mm:ss'
19
+ }
@@ -1,18 +1,24 @@
1
1
  <script setup lang="ts">
2
- import { Button as AButton } from 'ant-design-vue';
3
- import { computed } from 'vue';
2
+ import { Button, Menu, MenuItem, Dropdown, type TableColumnsType } from 'ant-design-vue';
3
+ import { type Ref, computed, inject } from 'vue';
4
+ import { onTtaTimeZone } from '../../date';
5
+ import { TableSettingModal } from './more/TableSetting.vue'
6
+ import { type ColumnConfig } from '../../../../antd/hooks/useTableColumns';
4
7
 
5
8
  const props = withDefaults(defineProps<{
6
9
  loading?: boolean
7
10
  filterQuery?: any
8
11
  filterParams?: any
9
12
  filterForm?: any
13
+ tableColumns?: TableColumnsType<any>
14
+ tableColumnsConfig?: ColumnConfig[]
10
15
  }>(), {
11
16
  loading: undefined,
12
17
  });
13
18
  const emits = defineEmits<{
14
19
  (e: 'filter'): void
15
20
  (e: 'reset'): void
21
+ (e: 'setColumnsConfig', columnsConfig: any): void
16
22
  }>();
17
23
 
18
24
  const loading = computed(() => {
@@ -38,18 +44,56 @@ function reset() {
38
44
  props.filterParams?.update?.(true);
39
45
  emits('reset');
40
46
  }
47
+
48
+ const tableColumns = inject('tableColumns', props.tableColumns) as TableColumnsType<any>;
49
+ const tableColumnsConfig = inject('tableColumnsConfig') as Ref<ColumnConfig[]>;
50
+ function openTableSettingModal() {
51
+
52
+ if (!tableColumns) return;
53
+
54
+ TableSettingModal.open({
55
+ tableColumns,
56
+ tableColumnsConfig: tableColumnsConfig.value || []
57
+ } as any)?.then((res) => {
58
+ TableSettingModal.close();
59
+ if (res) {
60
+ tableColumnsConfig.value = res;
61
+ emits('setColumnsConfig', res);
62
+ }
63
+ });
64
+ }
65
+
66
+ onTtaTimeZone(() => {
67
+ setTimeout(() => {
68
+ props.filterParams?.update?.(true);
69
+ }, 100)
70
+ });
41
71
  </script>
42
72
 
43
73
  <template>
44
74
  <div class="btns flex-none flex w-min ml-auto gap-2">
45
75
  <slot :loading="loading" :filter="filter" :reset="reset">
46
- <AButton class="filter-btn" type="primary" htmlType="submit" :loading="loading" @click="filter()">
76
+ <Button class="filter-btn" type="primary" htmlType="submit" :loading="loading" @click="filter()">
47
77
  查询
48
- </AButton>
49
- <AButton :disabled="loading" @click="reset()">
78
+ </Button>
79
+ <Button :disabled="loading" @click="reset()">
50
80
  重置
51
- </AButton>
81
+ </Button>
82
+ <Dropdown trigger="click" placement="bottomRight" >
83
+ <Button class="px-1 w-8">
84
+ <i class="i-si:more-square-horiz-fill scale-130 transform-origin-bottom-center" />
85
+ </Button>
86
+ <template #overlay>
87
+ <Menu>
88
+ <MenuItem v-if="tableColumns" @click="openTableSettingModal">
89
+ 设置表格
90
+ </MenuItem>
91
+ </Menu>
92
+ </template>
93
+ </Dropdown>
52
94
  </slot>
95
+
96
+ <TableSettingModal.PresetComponent />
53
97
  </div>
54
98
  </template>
55
99
 
@@ -58,7 +102,7 @@ function reset() {
58
102
  :slotted(.filter-btn) {
59
103
  position: relative;
60
104
 
61
- .ant-btn-loading-icon{
105
+ .ant-btn-loading-icon {
62
106
  position: absolute;
63
107
  left: 50%;
64
108
  transform: translateX(-50%);
@@ -68,7 +112,7 @@ function reset() {
68
112
  margin-inline-end: 0 !important;
69
113
  }
70
114
 
71
- + span {
115
+ +span {
72
116
  visibility: hidden;
73
117
  }
74
118
  }
@@ -0,0 +1,95 @@
1
+ <script lang="ts">
2
+ import { RadioGroup, 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
+ import { type ColumnConfig } from '../../../../../antd/hooks/useTableColumns';
7
+
8
+ export const TableSettingModal = useAntdModal(
9
+ // eslint-disable-next-line import/no-self-import
10
+ defineAsyncComponent(() => import('./TableSetting.vue')),
11
+ {
12
+ title: '表格设置',
13
+ width: 500,
14
+ centered: true,
15
+ maskClosable: false,
16
+ }
17
+ );
18
+ </script>
19
+
20
+ <script lang="ts" setup>
21
+ const props = defineProps<{
22
+ tableColumns: TableColumnsType<any>
23
+ tableColumnsConfig: ColumnConfig[]
24
+ }>();
25
+
26
+ let columnsCfg = props.tableColumns.map((col: any) => ({
27
+ title: col.title as string,
28
+ dataIndex: col.dataIndex as string,
29
+ visible: props.tableColumnsConfig.find(cf => cf.dataIndex === col.dataIndex)?.visible !== false,
30
+ compact: props.tableColumnsConfig.find(cf => cf.dataIndex === col.dataIndex)?.compact === true,
31
+ fixed: !!col.fixed,
32
+ }));
33
+
34
+ // 排序
35
+ if (props.tableColumnsConfig.length) {
36
+ const dataIndexOrderMap = new Map(props.tableColumnsConfig.map((col, i) => [col.dataIndex, i]));
37
+ columnsCfg.sort((a, b) => {
38
+ const aIndex = dataIndexOrderMap.get(a.dataIndex);
39
+ const bIndex = dataIndexOrderMap.get(b.dataIndex);
40
+ return (aIndex ?? 0) - (bIndex ?? 0);
41
+ })
42
+ }
43
+
44
+ const columnsConfig = ref(columnsCfg);
45
+ const columnsShowOptions = reactive(Object.fromEntries(props.tableColumns.map((col: any) => {
46
+ const options = [{ label: '显示', value: 'show' }, { label: '隐藏', value: 'hidden' }];
47
+ col.compactable === true && options.unshift({ label: '简略', value: 'compact' });
48
+ return [col.dataIndex, options];
49
+ })) as Record<string, { value: string; options: string }[]>);
50
+ const columnsShowValue = reactive(Object.fromEntries(columnsConfig.value.map((cf) => {
51
+ const value = cf.visible === false ? 'hidden' : cf.compact === true ? 'compact' : 'show';
52
+ return [cf.dataIndex, value]
53
+ })) as Record<string, string>);
54
+
55
+ const [$ctn] = useDragAndDrop(columnsConfig, {
56
+ handlerSelector: '.drag-handler',
57
+ draggingClass: 'op-50',
58
+ direction: 'vertical',
59
+ });
60
+
61
+ defineExpose({
62
+ confirm(_: any, resolve: (data: any) => void) {
63
+ const newColumnsConfig = columnsConfig.value.map((col) => ({
64
+ dataIndex: col.dataIndex,
65
+ visible: columnsShowValue[col.dataIndex] !== 'hidden',
66
+ compact: columnsShowValue[col.dataIndex] === 'compact',
67
+ })) as ColumnConfig[];
68
+ resolve(newColumnsConfig);
69
+ return true;
70
+ },
71
+ })
72
+ </script>
73
+
74
+ <template>
75
+ <div>
76
+ <div>
77
+ <!-- <Divider>列设置</Divider> -->
78
+ <div ref="$ctn">
79
+ <div v-for="(col, i) in columnsConfig" :key="col.dataIndex" :index="i"
80
+ class="flex items-center gap-2 p-2 select-none">
81
+ <div
82
+ class="drag-handler flex items-center justify-center p-1 -m-2 "
83
+ :class="{ '!pointer-events-none !cursor-no-drop': col.fixed }"
84
+ >
85
+ <i class="i-material-symbols:drag-indicator" :class="{ 'op-0': col.fixed }" />&nbsp;
86
+ </div>
87
+ <span>{{ col.title }}</span>
88
+ <RadioGroup class="ml-auto" v-model:value="columnsShowValue[col.dataIndex]" :options="columnsShowOptions[col.dataIndex]"
89
+ optionType="button" buttonStyle="solid" size="small" />
90
+ <!-- <Select v-model:value="columnsShowValue[col.dataIndex]" :options="columnsShowOptions[col.dataIndex]" /> -->
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </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);
41
+
42
+ return reactive({
43
+ codes: readonly(permissionCodes),
44
+ role,
45
+ has: hasPermission,
46
+ refresh: refreshPermission,
47
+ });
48
+ }