@peng_kai/kit 0.3.0-beta.13 → 0.3.0-beta.15
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/admin/components/date/helpers.ts +70 -98
- package/admin/components/date/index.ts +2 -1
- package/admin/components/date/presetProps.ts +23 -0
- package/admin/components/filter/src/FilterReset.vue +7 -1
- package/admin/components/filter/src/more/TableSetting.vue +14 -0
- package/admin/components/provider/admin-router.ts +1 -1
- package/admin/components/text/src/Datetime.vue +2 -1
- package/admin/layout/large/Content.vue +6 -1
- package/admin/layout/large/Notice.vue +2 -2
- package/antd/hooks/useAntdModal.ts +9 -7
- package/antd/hooks/useAntdTable.ts +18 -14
- package/package.json +1 -1
- package/utils/locale/helpers.ts +9 -0
- package/vue/components/test/KitTest.vue +0 -9
- package/vue/components/test/testStore.ts +0 -11
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import dayjs, { type Dayjs } from '../../../libs/dayjs';
|
|
2
2
|
import { tryOnScopeDispose } from '@vueuse/core';
|
|
3
|
-
import { computed, reactive, watch,
|
|
3
|
+
import { computed, reactive, watch, customRef, type Ref } from 'vue';
|
|
4
4
|
import type { Simplify } from 'type-fest';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -11,7 +11,27 @@ export function getTtaTimeZone() {
|
|
|
11
11
|
const win = window as any;
|
|
12
12
|
const key = '__APP_TZ__';
|
|
13
13
|
|
|
14
|
-
|
|
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>;
|
|
15
35
|
}
|
|
16
36
|
|
|
17
37
|
/**
|
|
@@ -41,6 +61,15 @@ export const onTtaTimeZone = (() => {
|
|
|
41
61
|
}
|
|
42
62
|
})();
|
|
43
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
|
+
|
|
44
73
|
type OfDayType = dayjs.Dayjs | undefined | null;
|
|
45
74
|
type UnitType = dayjs.QUnitType | dayjs.OpUnitType;
|
|
46
75
|
export const datetime = {
|
|
@@ -48,82 +77,90 @@ export const datetime = {
|
|
|
48
77
|
* 将日期范围转换为 API 参数格式的时间戳范围
|
|
49
78
|
* @param days 时间范围元组,包含开始和结束时间
|
|
50
79
|
* @param unit 时间单位,默认为 'day'
|
|
51
|
-
* @param
|
|
80
|
+
* @param tz 时区字符串,默认为 'APP'(应用时区)。特殊值:UTC、LOCAL、APP
|
|
52
81
|
* @param keys 可选的键名元组,默认为 ['start_time', 'end_time']
|
|
53
82
|
* @returns 返回一个对象,包含开始和结束时间的时间戳
|
|
54
83
|
*/
|
|
55
|
-
|
|
84
|
+
toRangeForApiParams<KS extends readonly [string, string] = ['start_time', 'end_time']>(
|
|
56
85
|
days: Array<OfDayType | undefined | null> | undefined | null,
|
|
57
|
-
unit: UnitType = 'day',
|
|
58
|
-
|
|
86
|
+
unit: UnitType = 'day',
|
|
87
|
+
tz: string = 'APP',
|
|
59
88
|
keys?: KS
|
|
60
89
|
) {
|
|
61
90
|
if (!days || !days[0] || !days[1])
|
|
62
91
|
return undefined;
|
|
63
92
|
|
|
64
|
-
const
|
|
93
|
+
const _tz = standardizeTzParam(tz);
|
|
65
94
|
const _keys = (Array.isArray(keys) && keys?.length > 1) ? keys : ['start_time', 'end_time'];
|
|
66
95
|
|
|
67
96
|
type RetType = KS extends readonly [string, string]
|
|
68
|
-
? Simplify<{ [K in KS[0]]: number } & { [K in KS[1]]: number }>
|
|
97
|
+
? Simplify<{ [K in KS[0]]: number } & { [K in KS[1]]: number }>
|
|
69
98
|
: { start_time: number; end_time: number };
|
|
70
99
|
|
|
71
100
|
return {
|
|
72
|
-
[_keys[0]]: dayjs(days[0].valueOf()).tz(
|
|
73
|
-
[_keys[1]]: dayjs(days[1].valueOf()).tz(
|
|
101
|
+
[_keys[0]]: dayjs(days[0].valueOf()).tz(_tz).startOf(unit).valueOf(),
|
|
102
|
+
[_keys[1]]: dayjs(days[1].valueOf()).tz(_tz).endOf(unit).valueOf(),
|
|
74
103
|
} as RetType;
|
|
75
104
|
},
|
|
76
105
|
/**
|
|
77
106
|
* 将日期范围转换为表单使用的时间范围
|
|
78
107
|
* @param days 时间范围元组,包含开始和结束时间
|
|
79
|
-
* @param
|
|
80
|
-
* @
|
|
108
|
+
* @param defaultRangeUnit 默认的时间范围单位,如果未提供,则返回 undefined
|
|
109
|
+
* @return 返回一个包含开始和结束时间的元组,格式为 dayjs 对象
|
|
81
110
|
*/
|
|
82
111
|
toRangeForForm(
|
|
83
|
-
days: Array<OfDayType | string | undefined | null>,
|
|
84
|
-
|
|
112
|
+
days: Array<OfDayType | string | number | undefined | null>,
|
|
113
|
+
defaultRangeUnit?: UnitType,
|
|
85
114
|
) {
|
|
115
|
+
const getDefaultRange = () => {
|
|
116
|
+
const now = dayjs();
|
|
117
|
+
return defaultRangeUnit
|
|
118
|
+
? [now.startOf(defaultRangeUnit), now.endOf(defaultRangeUnit)] as [dayjs.Dayjs, dayjs.Dayjs]
|
|
119
|
+
: undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
86
122
|
if (!days || !days[0] || !days[1])
|
|
87
|
-
return
|
|
123
|
+
return getDefaultRange();
|
|
88
124
|
|
|
89
|
-
const sDay = dayjs(typeof
|
|
90
|
-
const eDay = dayjs(typeof days[1] === 'string' ? Number(days[1]) : days[1].valueOf());
|
|
125
|
+
const [sDay, eDay] = days.map(d => dayjs(typeof d === 'string' ? Number(d) : d?.valueOf()));
|
|
91
126
|
|
|
92
127
|
if (!sDay.isValid() || !eDay.isValid())
|
|
93
|
-
return
|
|
128
|
+
return getDefaultRange();
|
|
94
129
|
|
|
95
|
-
return [sDay
|
|
130
|
+
return [sDay, eDay] as [dayjs.Dayjs, dayjs.Dayjs];
|
|
96
131
|
},
|
|
97
132
|
/**
|
|
98
133
|
* 获取指定日期的开始时间
|
|
99
134
|
* @param unit 时间单位
|
|
100
135
|
* @param day 日期对象
|
|
136
|
+
* @param tz 时区字符串,默认为 'APP'(应用时区)。特殊值:UTC、LOCAL、APP
|
|
101
137
|
* @returns 返回指定日期的开始时间,格式为 dayjs 对象
|
|
102
138
|
*/
|
|
103
|
-
ofStart(unit: UnitType, day: OfDayType) {
|
|
139
|
+
ofStart(unit: UnitType, day: OfDayType, tz: string = 'APP') {
|
|
104
140
|
if (!day)
|
|
105
141
|
return undefined;
|
|
106
142
|
|
|
107
|
-
const
|
|
108
|
-
return dayjs(day.valueOf()).tz(
|
|
143
|
+
const _tz = standardizeTzParam(tz);
|
|
144
|
+
return dayjs(day.valueOf()).tz(_tz).startOf(unit);
|
|
109
145
|
},
|
|
110
146
|
/**
|
|
111
147
|
* 获取指定日期的结束时间
|
|
112
148
|
* @param unit 时间单位
|
|
113
149
|
* @param day 日期对象
|
|
150
|
+
* @param tz 时区字符串,默认为 'APP'(应用时区)。特殊值:UTC、LOCAL、APP
|
|
114
151
|
* @returns 返回指定日期的结束时间,格式为 dayjs 对象
|
|
115
152
|
*/
|
|
116
|
-
ofEnd(unit: UnitType, day: OfDayType) {
|
|
153
|
+
ofEnd(unit: UnitType, day: OfDayType, tz: string = 'APP') {
|
|
117
154
|
if (!day)
|
|
118
155
|
return undefined;
|
|
119
156
|
|
|
120
|
-
const
|
|
121
|
-
return dayjs(day.valueOf()).tz(
|
|
157
|
+
const _tz = standardizeTzParam(tz);
|
|
158
|
+
return dayjs(day.valueOf()).tz(_tz).endOf(unit);
|
|
122
159
|
},
|
|
123
160
|
}
|
|
124
161
|
|
|
125
162
|
/** 为 DatePicker 组件提供响应式时区支持,当时区更新时,自动调整时间戳 */
|
|
126
|
-
|
|
163
|
+
function useDatePickerPropsForTz(
|
|
127
164
|
value: Ref<Dayjs | undefined>,
|
|
128
165
|
props: {
|
|
129
166
|
createPresets?: (day: Dayjs) => { label: string; value: Dayjs }[];
|
|
@@ -157,7 +194,7 @@ export function useDatePickerPropsForTz(
|
|
|
157
194
|
});
|
|
158
195
|
}
|
|
159
196
|
|
|
160
|
-
|
|
197
|
+
function useRangePickerPropsForTz(
|
|
161
198
|
value: Ref<[string, string] | [Dayjs, Dayjs] | undefined>,
|
|
162
199
|
props: {
|
|
163
200
|
createPresets?: (day: Dayjs) => { label: string; value: Dayjs[] }[];
|
|
@@ -182,87 +219,22 @@ export function useRangePickerPropsForTz(
|
|
|
182
219
|
{ label: '近30天', value: [day.subtract(29, 'day').startOf('day'), day.endOf('day')] },
|
|
183
220
|
];
|
|
184
221
|
};
|
|
185
|
-
const nowDay = computed(() => dayjs().tz(_tz.value));
|
|
186
222
|
const _value = computed(() => {
|
|
187
223
|
const v = value.value;
|
|
188
224
|
if (v && v[0] && v[1]) {
|
|
189
225
|
return [
|
|
190
|
-
dayjs(v[0].valueOf())
|
|
191
|
-
dayjs(v[1].valueOf())
|
|
226
|
+
dayjs(v[0].valueOf()),
|
|
227
|
+
dayjs(v[1].valueOf()),
|
|
192
228
|
] as [Dayjs, Dayjs];
|
|
193
229
|
}
|
|
194
230
|
return undefined;
|
|
195
231
|
});
|
|
196
232
|
|
|
197
|
-
watch(_tz, (newV, oldV) => {
|
|
198
|
-
const v = value.value;
|
|
199
|
-
if (!v || !v[0] || !v[1]) return;
|
|
200
|
-
|
|
201
|
-
const newTzOffset = dayjs().tz(newV).utcOffset();
|
|
202
|
-
const oldTzOffset = dayjs().tz(oldV).utcOffset();
|
|
203
|
-
value.value = [
|
|
204
|
-
dayjs(v[0]).add((oldTzOffset - newTzOffset) * 60 * 1000),
|
|
205
|
-
dayjs(v[1]).add((oldTzOffset - newTzOffset) * 60 * 1000),
|
|
206
|
-
]
|
|
207
|
-
console.log('🤡 / value.value:', value.value[0].valueOf())
|
|
208
|
-
});
|
|
209
|
-
|
|
210
233
|
return reactive({
|
|
211
234
|
'value': _value,
|
|
212
|
-
'onUpdate:value': (
|
|
213
|
-
'presets': computed(() => props.createPresets!(
|
|
235
|
+
'onUpdate:value': (v: any) => value.value = v,
|
|
236
|
+
'presets': computed(() => props.createPresets!(dayjs())),
|
|
237
|
+
// showTime: computed(() => ({ defaultValue: [dayjs().startOf('day'), dayjs().endOf('day')] })),
|
|
238
|
+
format: 'YY-MM-DD HH:mm:ss'
|
|
214
239
|
});
|
|
215
240
|
};
|
|
216
|
-
|
|
217
|
-
/** 为 RangePicker 组件提供响应式时区支持,当时区更新时,自动调整时间戳 */
|
|
218
|
-
export function useRangePickerPropsForTz_old(
|
|
219
|
-
ts: [Ref<number | undefined>, Ref<number | undefined>],
|
|
220
|
-
props: {
|
|
221
|
-
createPresets?: (day: Dayjs) => { label: string; value: Dayjs[] }[];
|
|
222
|
-
} = {},
|
|
223
|
-
tz?: Ref<string>,
|
|
224
|
-
) {
|
|
225
|
-
// 默认值
|
|
226
|
-
props.createPresets ??= (day: Dayjs) => {
|
|
227
|
-
return [
|
|
228
|
-
{ label: '今天', value: [day.startOf('day'), day.endOf('day')] },
|
|
229
|
-
{ label: '昨天', value: [day.subtract(1, 'day').startOf('day'), day.subtract(1, 'day').endOf('day')] },
|
|
230
|
-
{ label: '前一天', value: [day.subtract(1, 'day').startOf('day'), day.subtract(1, 'day').endOf('day')] },
|
|
231
|
-
{ label: '后一天', value: [day.add(1, 'day').startOf('day'), day.add(1, 'day').endOf('day')] },
|
|
232
|
-
{ label: '本周', value: [day.startOf('week'), day.endOf('week')] },
|
|
233
|
-
{ label: '上周', value: [day.subtract(1, 'week').startOf('week'), day.subtract(1, 'week').endOf('week')] },
|
|
234
|
-
{ label: '本月', value: [day.startOf('month'), day.endOf('month')] },
|
|
235
|
-
{ label: '上个月', value: [day.subtract(1, 'month').startOf('month'), day.subtract(1, 'month').endOf('month')] },
|
|
236
|
-
{ label: '上上月', value: [day.subtract(2, 'month').startOf('month'), day.subtract(2, 'month').endOf('month')] },
|
|
237
|
-
{ label: '近7天', value: [day.subtract(6, 'day').startOf('day'), day.endOf('day')] },
|
|
238
|
-
{ label: '近30天', value: [day.subtract(29, 'day').startOf('day'), day.endOf('day')] },
|
|
239
|
-
];
|
|
240
|
-
};
|
|
241
|
-
const _tz = tz || getTtaTimeZone();
|
|
242
|
-
const nowDay = computed(() => dayjs().tz(_tz.value));
|
|
243
|
-
const inputDay = computed(() => {
|
|
244
|
-
console.log('🤡 / ts:', ts);
|
|
245
|
-
if (ts[0].value && ts[1].value)
|
|
246
|
-
return [dayjs(ts[0].value).tz(_tz.value), dayjs(ts[1].value).tz(_tz.value)] as [Dayjs, Dayjs];
|
|
247
|
-
return undefined;
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
watch(_tz, (newV, oldV) => {
|
|
251
|
-
if (!ts[0].value || !ts[1].value) return;
|
|
252
|
-
|
|
253
|
-
const newTzOffset = dayjs().tz(newV).utcOffset();
|
|
254
|
-
const oldTzOffset = dayjs().tz(oldV).utcOffset();
|
|
255
|
-
ts[0].value = ts[0].value + (oldTzOffset - newTzOffset) * 60 * 1000;
|
|
256
|
-
ts[1].value = ts[1].value + (oldTzOffset - newTzOffset) * 60 * 1000;
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
return reactive({
|
|
260
|
-
'value': inputDay,
|
|
261
|
-
'onUpdate:value': (value: any) => {
|
|
262
|
-
console.log('🤡 / value:', value);
|
|
263
|
-
ts[0].value = dayjs(value[0]).valueOf();
|
|
264
|
-
ts[1].value = dayjs(value[1]).valueOf();
|
|
265
|
-
},
|
|
266
|
-
'presets': computed(() => props.createPresets!(nowDay.value)),
|
|
267
|
-
});
|
|
268
|
-
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as TimeFieldSelectForLabel } from './TimeFieldSelectForLabel.vue';
|
|
2
2
|
export { default as TtaTimeZone } from './TtaTimeZone.vue';
|
|
3
3
|
export { default as PeriodPicker } from './PeriodPicker.vue';
|
|
4
|
-
export {
|
|
4
|
+
export { getTtaTimeZone, onTtaTimeZone, datetime } from './helpers';
|
|
5
|
+
export { antdRangePickerPresets, antdRangePickerShowTimeProps } from './presetProps';
|
|
@@ -0,0 +1,23 @@
|
|
|
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: '今天', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
|
|
7
|
+
{ label: '昨天', value: [dayjs().subtract(1, 'day').startOf('day'), dayjs().subtract(1, 'day').endOf('day')] },
|
|
8
|
+
{ label: '前一天', value: [dayjs().subtract(1, 'day').startOf('day'), dayjs().subtract(1, 'day').endOf('day')] },
|
|
9
|
+
{ label: '后一天', value: [dayjs().add(1, 'day').startOf('day'), dayjs().add(1, 'day').endOf('day')] },
|
|
10
|
+
{ label: '本周', value: [dayjs().startOf('week'), dayjs().endOf('week')] },
|
|
11
|
+
{ label: '上周', value: [dayjs().subtract(1, 'week').startOf('week'), dayjs().subtract(1, 'week').endOf('week')] },
|
|
12
|
+
{ label: '本月', value: [dayjs().startOf('month'), dayjs().endOf('month')] },
|
|
13
|
+
{ label: '上个月', value: [dayjs().subtract(1, 'month').startOf('month'), dayjs().subtract(1, 'month').endOf('month')] },
|
|
14
|
+
{ label: '上上月', value: [dayjs().subtract(2, 'month').startOf('month'), dayjs().subtract(2, 'month').endOf('month')] },
|
|
15
|
+
{ label: '近7天', value: [dayjs().subtract(6, 'day').startOf('day'), dayjs().endOf('day')] },
|
|
16
|
+
{ label: '近30天', value: [dayjs().subtract(29, 'day').startOf('day'), dayjs().endOf('day')] },
|
|
17
|
+
] as { label: string; value: [Dayjs, Dayjs] }[]
|
|
18
|
+
|
|
19
|
+
export const antdRangePickerShowTimeProps: ComponentProps<typeof RangePicker> = {
|
|
20
|
+
presets: antdRangePickerPresets,
|
|
21
|
+
showTime: { defaultValue: [dayjs().startOf('day'), dayjs().endOf('day')] },
|
|
22
|
+
format: 'YY-MM-DD HH:mm:ss'
|
|
23
|
+
}
|
|
@@ -16,6 +16,7 @@ const props = withDefaults(defineProps<{
|
|
|
16
16
|
const emits = defineEmits<{
|
|
17
17
|
(e: 'filter'): void
|
|
18
18
|
(e: 'reset'): void
|
|
19
|
+
(e: 'setColumnsConfig', columnsConfig: any): void
|
|
19
20
|
}>();
|
|
20
21
|
|
|
21
22
|
const loading = computed(() => {
|
|
@@ -48,7 +49,12 @@ function openTableSettingModal() {
|
|
|
48
49
|
title: typeof x.title === 'string' ? x.title : x.dataIndex || '',
|
|
49
50
|
})) || [];
|
|
50
51
|
|
|
51
|
-
TableSettingModal.open({ tableColumns })
|
|
52
|
+
TableSettingModal.open({ tableColumns })?.then((res) => {
|
|
53
|
+
TableSettingModal.close();
|
|
54
|
+
if (res) {
|
|
55
|
+
emits('setColumnsConfig', res);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
onTtaTimeZone(() => {
|
|
@@ -3,6 +3,7 @@ import { RadioGroup,type TableColumnsType } from 'ant-design-vue';
|
|
|
3
3
|
import { reactive, defineAsyncComponent, ref } from 'vue';
|
|
4
4
|
import { useDragAndDrop } from "fluid-dnd/vue";
|
|
5
5
|
import { useAntdModal } from '../../../../../antd/hooks/useAntdModal';
|
|
6
|
+
import { type ColumnConfig } from '../../../../../antd/hooks/useAntdTable';
|
|
6
7
|
|
|
7
8
|
export const TableSettingModal = useAntdModal(
|
|
8
9
|
// eslint-disable-next-line import/no-self-import
|
|
@@ -24,6 +25,7 @@ const columnsConfig = ref(props.tableColumns.map((col: any) => ({
|
|
|
24
25
|
title: col.title as string,
|
|
25
26
|
dataIndex: col.dataIndex as string,
|
|
26
27
|
visible: col.visible !== false,
|
|
28
|
+
compact: col.compact === true,
|
|
27
29
|
})));
|
|
28
30
|
const columnsShowOptions = reactive(Object.fromEntries(props.tableColumns.map((col: any) => {
|
|
29
31
|
const options = [{ label: '显示', value: 'show' }, { label: '隐藏', value: 'hidden' }];
|
|
@@ -40,6 +42,18 @@ const [$ctn] = useDragAndDrop(columnsConfig, {
|
|
|
40
42
|
draggingClass: 'op-50',
|
|
41
43
|
direction: 'vertical',
|
|
42
44
|
});
|
|
45
|
+
|
|
46
|
+
defineExpose({
|
|
47
|
+
confirm(_: any, resolve: (data: any) => void) {
|
|
48
|
+
const newColumnsConfig = columnsConfig.value.map((col) => ({
|
|
49
|
+
...col,
|
|
50
|
+
visible: columnsShowValue[col.dataIndex] !== 'hidden',
|
|
51
|
+
compact: columnsShowValue[col.dataIndex] === 'compact',
|
|
52
|
+
})) as ColumnConfig[];
|
|
53
|
+
resolve(newColumnsConfig);
|
|
54
|
+
return true;
|
|
55
|
+
},
|
|
56
|
+
})
|
|
43
57
|
</script>
|
|
44
58
|
|
|
45
59
|
<template>
|
|
@@ -39,7 +39,7 @@ const PageRefresh = defineComponent({
|
|
|
39
39
|
mounted() {
|
|
40
40
|
const { router } = this.$props;
|
|
41
41
|
const fullPath = router.currentRoute.value.fullPath;
|
|
42
|
-
|
|
42
|
+
router.replace({ ...router.resolve(fullPath), force: true });
|
|
43
43
|
},
|
|
44
44
|
setup() {
|
|
45
45
|
return () => null;
|
|
@@ -20,6 +20,7 @@ const props = withDefaults(
|
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
const tz = getTtaTimeZone();
|
|
23
|
+
const localTz = dayjs.tz.guess();
|
|
23
24
|
const timestamp = computed(() => {
|
|
24
25
|
let tsStr = String(props.ts || props.timestamp);
|
|
25
26
|
|
|
@@ -49,7 +50,7 @@ const text = computed(() => {
|
|
|
49
50
|
<span class="op-50">指定</span>
|
|
50
51
|
</div>
|
|
51
52
|
<div>
|
|
52
|
-
{{ dayjs(timestamp).tz(
|
|
53
|
+
{{ dayjs(timestamp).tz(localTz).format('YYYY-MM-DD HH:mm:ss') }}
|
|
53
54
|
<span class="op-50">本地</span>
|
|
54
55
|
</div>
|
|
55
56
|
<div v-if="dayjs().utc">
|
|
@@ -5,13 +5,18 @@ import { KeepAlive, type KeepAliveProps } from 'vue';
|
|
|
5
5
|
const props = defineProps<{
|
|
6
6
|
cached: KeepAliveProps['include']
|
|
7
7
|
}>();
|
|
8
|
+
|
|
9
|
+
function side(comp: any) {
|
|
10
|
+
console.log('🤡 / side / comp:', comp)
|
|
11
|
+
return comp;
|
|
12
|
+
}
|
|
8
13
|
</script>
|
|
9
14
|
|
|
10
15
|
<template>
|
|
11
16
|
<RouterView #default="{ Component }">
|
|
12
17
|
<KeepAlive :include="props.cached">
|
|
13
18
|
<Suspense>
|
|
14
|
-
<component :is="Component" />
|
|
19
|
+
<component :is="Component" :key="$route.fullPath" />
|
|
15
20
|
<template #fallback>
|
|
16
21
|
<div class="flex justify-center items-center h-full">
|
|
17
22
|
<div class="flex items-center">
|
|
@@ -5,7 +5,7 @@ import { type UseQueryReturnType } from '@tanstack/vue-query'
|
|
|
5
5
|
import { watch, ref, computed } from 'vue'
|
|
6
6
|
import dayjs from '../../../libs/dayjs';
|
|
7
7
|
import { useRouter } from 'vue-router';
|
|
8
|
-
import audioURL from './y682.mp3';
|
|
8
|
+
import audioURL from './y682.mp3?url';
|
|
9
9
|
|
|
10
10
|
const noticeAudio = new Audio(audioURL);
|
|
11
11
|
noticeAudio.src = audioURL;
|
|
@@ -88,7 +88,7 @@ useIntervalFn(() => {
|
|
|
88
88
|
<i class="i-ri:notification-4-line block text-5" :class="{ shake: !!noticeNum }" />
|
|
89
89
|
</div>
|
|
90
90
|
<template #overlay>
|
|
91
|
-
<Menu v-if="noticeNum" @click="visible = false">
|
|
91
|
+
<Menu v-if="noticeNum" @click="visible = false" class="max-h-100 overflow-y-auto">
|
|
92
92
|
<MenuItem v-for="(item, i) of noticesQry.data.value" :key="i" @click="operate(item)">
|
|
93
93
|
<div class="min-w-50">
|
|
94
94
|
<div>{{ item.title }}</div>
|
|
@@ -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
|
-
|
|
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
|
});
|
|
@@ -96,11 +98,11 @@ export function useAntdModal<Comp extends Component>(
|
|
|
96
98
|
|
|
97
99
|
return promiseResolvers.promise;
|
|
98
100
|
};
|
|
99
|
-
const
|
|
101
|
+
const onConfirm = (ev: any) => {
|
|
100
102
|
_modalProps.open = false;
|
|
101
103
|
promiseResolvers.resolve(ev);
|
|
102
104
|
};
|
|
103
|
-
const
|
|
105
|
+
const onClose = (reason?: any) => {
|
|
104
106
|
_modalProps.open = false;
|
|
105
107
|
_onClose(reason);
|
|
106
108
|
};
|
|
@@ -122,8 +124,8 @@ export function useAntdModal<Comp extends Component>(
|
|
|
122
124
|
!visiable && _onClose();
|
|
123
125
|
};
|
|
124
126
|
(compProps as any).ref = setRefs.comp;
|
|
125
|
-
(compProps as any).onClose =
|
|
126
|
-
(compProps as any).onConfirm =
|
|
127
|
+
(compProps as any).onClose = onClose;
|
|
128
|
+
(compProps as any).onConfirm = onConfirm;
|
|
127
129
|
|
|
128
130
|
tryOnBeforeUnmount(_onClose);
|
|
129
131
|
onDeactivated(_onClose);
|
|
@@ -136,6 +138,6 @@ export function useAntdModal<Comp extends Component>(
|
|
|
136
138
|
return refs.comp;
|
|
137
139
|
},
|
|
138
140
|
open,
|
|
139
|
-
close,
|
|
141
|
+
close: onClose,
|
|
140
142
|
};
|
|
141
143
|
}
|
|
@@ -54,7 +54,7 @@ export function useAntdTable<
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
|
-
const { defineColumns } = useTableColumns<LocalColumnsType>();
|
|
57
|
+
const { defineColumns, setColumnsConfig } = useTableColumns<LocalColumnsType>();
|
|
58
58
|
const defineRowSelection = (rowSelectionGetter: () => LocalTableRowSelection = () => ({})) => {
|
|
59
59
|
const rowSelection = reactive(rowSelectionGetter());
|
|
60
60
|
|
|
@@ -114,8 +114,10 @@ export function useAntdTable<
|
|
|
114
114
|
dataIndexs,
|
|
115
115
|
/** 【类型辅助】bodyCell 插槽数据的精确类型描述 */
|
|
116
116
|
bodyCellType,
|
|
117
|
-
/**
|
|
117
|
+
/** 用于定义 columns */
|
|
118
118
|
defineColumns,
|
|
119
|
+
/** 用于设置 columns 的配置 */
|
|
120
|
+
setColumnsConfig,
|
|
119
121
|
/** 【类型辅助】用于定义出类型精确的 rowSelection */
|
|
120
122
|
defineRowSelection,
|
|
121
123
|
/** 内置的 ATable onChange 事件,默认在 `tableProps` 中 */
|
|
@@ -129,8 +131,9 @@ type GetRecordType<T> = T extends UseQueryReturnType<infer D, any>
|
|
|
129
131
|
: never
|
|
130
132
|
: never;
|
|
131
133
|
|
|
134
|
+
export type ColumnConfig = { title: string, dataIndex: string, visible: boolean, compact: boolean }
|
|
135
|
+
|
|
132
136
|
function useTableColumns<LCT extends any[]>() {
|
|
133
|
-
type ColumnConfig = {title: string, dataIndex: string, visible: boolean}
|
|
134
137
|
|
|
135
138
|
const columnsConfig = ref<Array<ColumnConfig> | null>(null);
|
|
136
139
|
let originalColumns: LCT | null = null;
|
|
@@ -143,11 +146,18 @@ function useTableColumns<LCT extends any[]>() {
|
|
|
143
146
|
let columns = columnsGetter();
|
|
144
147
|
|
|
145
148
|
if (config?.length) {
|
|
146
|
-
|
|
147
|
-
columns = columns.filter((col: any) => {
|
|
149
|
+
columns = columns.map((col: any) => {
|
|
148
150
|
const x = config.find(c => c.dataIndex === col.dataIndex);
|
|
149
|
-
|
|
151
|
+
if (x) {
|
|
152
|
+
return { ...col, visible: x.visible, compact: x.compact,};
|
|
153
|
+
}
|
|
154
|
+
return col;
|
|
150
155
|
}) as LCT;
|
|
156
|
+
// 过滤掉不可见的列
|
|
157
|
+
// columns = columns.filter((col: any) => {
|
|
158
|
+
// const x = config.find(c => c.dataIndex === col.dataIndex);
|
|
159
|
+
// return x ? x.visible : true;
|
|
160
|
+
// }) as LCT;
|
|
151
161
|
// 基于 config 的顺序排序
|
|
152
162
|
columns = config.map(x => {
|
|
153
163
|
const col = columns.find(c => c.dataIndex === x.dataIndex);
|
|
@@ -161,18 +171,12 @@ function useTableColumns<LCT extends any[]>() {
|
|
|
161
171
|
|
|
162
172
|
const setColumnsConfig = (columns: Array<ColumnConfig>) => {
|
|
163
173
|
columnsConfig.value = columns;
|
|
164
|
-
|
|
165
|
-
// if (!columnsConfig.value) {
|
|
166
|
-
// columnsConfig.value = originalColumns?.map(col => ({
|
|
167
|
-
// title: col.title,
|
|
168
|
-
// dataIndex: col.dataIndex as string,
|
|
169
|
-
// visible: true,
|
|
170
|
-
// })) || [];
|
|
171
|
-
// }
|
|
174
|
+
console.log('🤡 / columns:', columns);
|
|
172
175
|
};
|
|
173
176
|
|
|
174
177
|
|
|
175
178
|
return {
|
|
176
179
|
defineColumns,
|
|
180
|
+
setColumnsConfig
|
|
177
181
|
};
|
|
178
182
|
}
|
package/package.json
CHANGED
package/utils/locale/helpers.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { I18nOptions } from 'vue-i18n';
|
|
1
2
|
import { omitBy } from '../../libs/lodash-es';
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -32,6 +33,14 @@ export function omitLocale(modules: Record<string, any>, excludes: string[] = []
|
|
|
32
33
|
return omitBy(modules, (_, path) => excludes.some(locale => path.includes(locale)));
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
/** 添加语言格式 */
|
|
37
|
+
export function addDatetimeFormat<T extends string>(name: T, config: NonNullable<I18nOptions['datetimeFormats']>[string][string]) {
|
|
38
|
+
return {
|
|
39
|
+
[name]: config,
|
|
40
|
+
[`${name}-utc`]: { ...config, timeZone: 'UTC' },
|
|
41
|
+
} as Record<T | `${T}-utc`, typeof config>;
|
|
42
|
+
}
|
|
43
|
+
|
|
35
44
|
/**
|
|
36
45
|
* 加密 JSON 消息
|
|
37
46
|
*/
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { useQueryClient } from '@tanstack/vue-query';
|
|
2
|
-
import { defineStore } from 'pinia';
|
|
3
|
-
import { useRouter } from 'vue-router';
|
|
4
|
-
|
|
5
|
-
export const useTestStore = defineStore('test-store', () => {
|
|
6
|
-
const router = useRouter();
|
|
7
|
-
const queryClient = useQueryClient();
|
|
8
|
-
console.log('🤡 / router:', router);
|
|
9
|
-
console.log('🤡 / queryClient:', queryClient);
|
|
10
|
-
return {};
|
|
11
|
-
});
|