32 changed files with 577 additions and 9 deletions
@ -0,0 +1,7 @@ |
|||||
|
import { eventHandler } from 'h3'; |
||||
|
import { TIME_ZONE_OPTIONS } from '~/utils/mock-data'; |
||||
|
import { useResponseSuccess } from '~/utils/response'; |
||||
|
|
||||
|
export default eventHandler(() => { |
||||
|
return useResponseSuccess(TIME_ZONE_OPTIONS); |
||||
|
}); |
||||
@ -0,0 +1,14 @@ |
|||||
|
import { eventHandler, readBody } from 'h3'; |
||||
|
import { verifyAccessToken } from '~/utils/jwt-utils'; |
||||
|
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response'; |
||||
|
import { setTimezone } from '~/utils/timezone-utils'; |
||||
|
|
||||
|
export default eventHandler(async (event) => { |
||||
|
const userinfo = verifyAccessToken(event); |
||||
|
if (!userinfo) { |
||||
|
return unAuthorizedResponse(event); |
||||
|
} |
||||
|
const { timezone } = await readBody(event); |
||||
|
setTimezone(timezone); |
||||
|
return useResponseSuccess(); |
||||
|
}); |
||||
@ -0,0 +1,12 @@ |
|||||
|
import { eventHandler } from 'h3'; |
||||
|
import { verifyAccessToken } from '~/utils/jwt-utils'; |
||||
|
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response'; |
||||
|
import { getTimezone } from '~/utils/timezone-utils'; |
||||
|
|
||||
|
export default eventHandler((event) => { |
||||
|
const userinfo = verifyAccessToken(event); |
||||
|
if (!userinfo) { |
||||
|
return unAuthorizedResponse(event); |
||||
|
} |
||||
|
return useResponseSuccess(getTimezone()); |
||||
|
}); |
||||
@ -0,0 +1,10 @@ |
|||||
|
let mockTimeZone: null | string = null; |
||||
|
|
||||
|
export const setTimezone = (timeZone: string) => { |
||||
|
mockTimeZone = timeZone; |
||||
|
}; |
||||
|
|
||||
|
export const getTimezone = () => { |
||||
|
console.log('mockTimeZone', mockTimeZone); |
||||
|
return mockTimeZone; |
||||
|
}; |
||||
@ -1,3 +1,4 @@ |
|||||
export * from './auth'; |
export * from './auth'; |
||||
export * from './menu'; |
export * from './menu'; |
||||
export * from './user'; |
export * from './user'; |
||||
|
export * from './user-profile'; |
||||
|
|||||
@ -0,0 +1,23 @@ |
|||||
|
import type { TimezoneOption } from '@vben/types'; |
||||
|
|
||||
|
import { requestClient } from '#/api/request'; |
||||
|
|
||||
|
/** |
||||
|
* 获取系统支持的时区列表 |
||||
|
*/ |
||||
|
export async function getTimezoneOptionsApi() { |
||||
|
return requestClient.get<TimezoneOption[]>('/profile/timezone'); |
||||
|
} |
||||
|
/** |
||||
|
* 获取用户时区 |
||||
|
*/ |
||||
|
export async function getUserTimezoneApi(): Promise<null | string | undefined> { |
||||
|
return requestClient.get<null | string | undefined>('/user/timezone'); |
||||
|
} |
||||
|
/** |
||||
|
* 设置用户时区 |
||||
|
* @param timezone 时区 |
||||
|
*/ |
||||
|
export async function setUserTimezoneApi(timezone: string) { |
||||
|
return requestClient.post('/user/setTimezone', { timezone }); |
||||
|
} |
||||
@ -1 +1,2 @@ |
|||||
export * from './auth'; |
export * from './auth'; |
||||
|
export * from './user-profile'; |
||||
|
|||||
@ -0,0 +1,56 @@ |
|||||
|
import { ref } from 'vue'; |
||||
|
|
||||
|
import { getTimezone, setDefaultTimezone } from '@vben/utils'; |
||||
|
|
||||
|
import { acceptHMRUpdate, defineStore } from 'pinia'; |
||||
|
|
||||
|
import { getUserTimezoneApi, setUserTimezoneApi } from '#/api'; |
||||
|
|
||||
|
const useUserProfileStore = defineStore('user-profile', () => { |
||||
|
const timezoneRef = ref( |
||||
|
getTimezone() || new Intl.DateTimeFormat().resolvedOptions().timeZone, |
||||
|
); |
||||
|
|
||||
|
/** |
||||
|
* 设置用户时区 |
||||
|
* Set the user's timezone |
||||
|
* @param timezone 时区字符串 |
||||
|
*/ |
||||
|
async function setTimezone(timezone: string) { |
||||
|
timezoneRef.value = timezone; |
||||
|
// 设置dayjs默认时区
|
||||
|
setDefaultTimezone(timezone); |
||||
|
// 保存用户的时区设置
|
||||
|
await setUserTimezoneApi(timezone); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 初始化用户时区 |
||||
|
* Initialize the user's timezone |
||||
|
*/ |
||||
|
async function initTimezone() { |
||||
|
// 从服务器获取用户时区
|
||||
|
const timezone = await getUserTimezoneApi(); |
||||
|
if (timezone) { |
||||
|
timezoneRef.value = timezone; |
||||
|
// 设置dayjs默认时区
|
||||
|
setDefaultTimezone(timezone); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
initTimezone(); |
||||
|
|
||||
|
return { |
||||
|
timezone: timezoneRef, |
||||
|
setTimezone, |
||||
|
initTimezone, |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
export { useUserProfileStore }; |
||||
|
|
||||
|
// 解决热更新问题
|
||||
|
const hot = import.meta.hot; |
||||
|
if (hot) { |
||||
|
hot.accept(acceptHMRUpdate(useUserProfileStore, hot)); |
||||
|
} |
||||
@ -1,3 +1,4 @@ |
|||||
export * from './auth'; |
export * from './auth'; |
||||
export * from './menu'; |
export * from './menu'; |
||||
export * from './user'; |
export * from './user'; |
||||
|
export * from './user-profile'; |
||||
|
|||||
@ -0,0 +1,23 @@ |
|||||
|
import type { TimezoneOption } from '@vben/types'; |
||||
|
|
||||
|
import { requestClient } from '#/api/request'; |
||||
|
|
||||
|
/** |
||||
|
* 获取系统支持的时区列表 |
||||
|
*/ |
||||
|
export async function getTimezoneOptionsApi() { |
||||
|
return requestClient.get<TimezoneOption[]>('/profile/timezone'); |
||||
|
} |
||||
|
/** |
||||
|
* 获取用户时区 |
||||
|
*/ |
||||
|
export async function getUserTimezoneApi(): Promise<null | string | undefined> { |
||||
|
return requestClient.get<null | string | undefined>('/user/timezone'); |
||||
|
} |
||||
|
/** |
||||
|
* 设置用户时区 |
||||
|
* @param timezone 时区 |
||||
|
*/ |
||||
|
export async function setUserTimezoneApi(timezone: string) { |
||||
|
return requestClient.post('/user/setTimezone', { timezone }); |
||||
|
} |
||||
@ -1 +1,2 @@ |
|||||
export * from './auth'; |
export * from './auth'; |
||||
|
export * from './user-profile'; |
||||
|
|||||
@ -0,0 +1,56 @@ |
|||||
|
import { ref } from 'vue'; |
||||
|
|
||||
|
import { getTimezone, setDefaultTimezone } from '@vben/utils'; |
||||
|
|
||||
|
import { acceptHMRUpdate, defineStore } from 'pinia'; |
||||
|
|
||||
|
import { getUserTimezoneApi, setUserTimezoneApi } from '#/api'; |
||||
|
|
||||
|
const useUserProfileStore = defineStore('user-profile', () => { |
||||
|
const timezoneRef = ref( |
||||
|
getTimezone() || new Intl.DateTimeFormat().resolvedOptions().timeZone, |
||||
|
); |
||||
|
|
||||
|
/** |
||||
|
* 设置用户时区 |
||||
|
* Set the user's timezone |
||||
|
* @param timezone 时区字符串 |
||||
|
*/ |
||||
|
async function setTimezone(timezone: string) { |
||||
|
timezoneRef.value = timezone; |
||||
|
// 设置dayjs默认时区
|
||||
|
setDefaultTimezone(timezone); |
||||
|
// 保存用户的时区设置
|
||||
|
await setUserTimezoneApi(timezone); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 初始化用户时区 |
||||
|
* Initialize the user's timezone |
||||
|
*/ |
||||
|
async function initTimezone() { |
||||
|
// 从服务器获取用户时区
|
||||
|
const timezone = await getUserTimezoneApi(); |
||||
|
if (timezone) { |
||||
|
timezoneRef.value = timezone; |
||||
|
// 设置dayjs默认时区
|
||||
|
setDefaultTimezone(timezone); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
initTimezone(); |
||||
|
|
||||
|
return { |
||||
|
timezone: timezoneRef, |
||||
|
setTimezone, |
||||
|
initTimezone, |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
export { useUserProfileStore }; |
||||
|
|
||||
|
// 解决热更新问题
|
||||
|
const hot = import.meta.hot; |
||||
|
if (hot) { |
||||
|
hot.accept(acceptHMRUpdate(useUserProfileStore, hot)); |
||||
|
} |
||||
@ -1,3 +1,4 @@ |
|||||
export * from './auth'; |
export * from './auth'; |
||||
export * from './menu'; |
export * from './menu'; |
||||
export * from './user'; |
export * from './user'; |
||||
|
export * from './user-profile'; |
||||
|
|||||
@ -0,0 +1,23 @@ |
|||||
|
import type { TimezoneOption } from '@vben/types'; |
||||
|
|
||||
|
import { requestClient } from '#/api/request'; |
||||
|
|
||||
|
/** |
||||
|
* 获取系统支持的时区列表 |
||||
|
*/ |
||||
|
export async function getTimezoneOptionsApi() { |
||||
|
return requestClient.get<TimezoneOption[]>('/profile/timezone'); |
||||
|
} |
||||
|
/** |
||||
|
* 获取用户时区 |
||||
|
*/ |
||||
|
export async function getUserTimezoneApi(): Promise<null | string | undefined> { |
||||
|
return requestClient.get<null | string | undefined>('/user/timezone'); |
||||
|
} |
||||
|
/** |
||||
|
* 设置用户时区 |
||||
|
* @param timezone 时区 |
||||
|
*/ |
||||
|
export async function setUserTimezoneApi(timezone: string) { |
||||
|
return requestClient.post('/user/setTimezone', { timezone }); |
||||
|
} |
||||
@ -1 +1,2 @@ |
|||||
export * from './auth'; |
export * from './auth'; |
||||
|
export * from './user-profile'; |
||||
|
|||||
@ -0,0 +1,56 @@ |
|||||
|
import { ref } from 'vue'; |
||||
|
|
||||
|
import { getTimezone, setDefaultTimezone } from '@vben/utils'; |
||||
|
|
||||
|
import { acceptHMRUpdate, defineStore } from 'pinia'; |
||||
|
|
||||
|
import { getUserTimezoneApi, setUserTimezoneApi } from '#/api'; |
||||
|
|
||||
|
const useUserProfileStore = defineStore('user-profile', () => { |
||||
|
const timezoneRef = ref( |
||||
|
getTimezone() || new Intl.DateTimeFormat().resolvedOptions().timeZone, |
||||
|
); |
||||
|
|
||||
|
/** |
||||
|
* 设置用户时区 |
||||
|
* Set the user's timezone |
||||
|
* @param timezone 时区字符串 |
||||
|
*/ |
||||
|
async function setTimezone(timezone: string) { |
||||
|
timezoneRef.value = timezone; |
||||
|
// 设置dayjs默认时区
|
||||
|
setDefaultTimezone(timezone); |
||||
|
// 保存用户的时区设置
|
||||
|
await setUserTimezoneApi(timezone); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 初始化用户时区 |
||||
|
* Initialize the user's timezone |
||||
|
*/ |
||||
|
async function initTimezone() { |
||||
|
// 从服务器获取用户时区
|
||||
|
const timezone = await getUserTimezoneApi(); |
||||
|
if (timezone) { |
||||
|
timezoneRef.value = timezone; |
||||
|
// 设置dayjs默认时区
|
||||
|
setDefaultTimezone(timezone); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
initTimezone(); |
||||
|
|
||||
|
return { |
||||
|
timezone: timezoneRef, |
||||
|
setTimezone, |
||||
|
initTimezone, |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
export { useUserProfileStore }; |
||||
|
|
||||
|
// 解决热更新问题
|
||||
|
const hot = import.meta.hot; |
||||
|
if (hot) { |
||||
|
hot.accept(acceptHMRUpdate(useUserProfileStore, hot)); |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
/** |
||||
|
* 时区选项 |
||||
|
*/ |
||||
|
interface TimezoneOption { |
||||
|
offset: number; |
||||
|
timezone: string; |
||||
|
} |
||||
|
|
||||
|
export type { TimezoneOption }; |
||||
@ -0,0 +1 @@ |
|||||
|
export { default as TimezoneButton } from './timezone-button.vue'; |
||||
@ -0,0 +1,89 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { ExtendedModalApi } from '@vben-core/popup-ui'; |
||||
|
|
||||
|
import { ref, unref, watch } from 'vue'; |
||||
|
|
||||
|
import { createIconifyIcon } from '@vben/icons'; |
||||
|
import { $t } from '@vben/locales'; |
||||
|
|
||||
|
import { useVbenModal } from '@vben-core/popup-ui'; |
||||
|
import { |
||||
|
RadioGroup, |
||||
|
RadioGroupItem, |
||||
|
VbenIconButton, |
||||
|
} from '@vben-core/shadcn-ui'; |
||||
|
|
||||
|
interface Props { |
||||
|
timezoneOptions: string[]; |
||||
|
okHandler?: ( |
||||
|
timezone: string, |
||||
|
modalApi: ExtendedModalApi, |
||||
|
) => Promise<void> | void; |
||||
|
timezone?: string; |
||||
|
} |
||||
|
|
||||
|
interface Listener { |
||||
|
change: (timezone: string) => void; |
||||
|
} |
||||
|
|
||||
|
const props = defineProps<Props>(); |
||||
|
const emit = defineEmits<Listener>(); |
||||
|
|
||||
|
const TimezoneIcon = createIconifyIcon('fluent-mdl2:world-clock'); |
||||
|
|
||||
|
const [Modal, modalApi] = useVbenModal({ |
||||
|
fullscreenButton: false, |
||||
|
onConfirm: () => { |
||||
|
props.okHandler?.(unref(timezoneValue), modalApi); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const handleClick = () => { |
||||
|
modalApi.open(); |
||||
|
}; |
||||
|
|
||||
|
const timezoneValue = ref(props.timezone); |
||||
|
watch( |
||||
|
() => props.timezone, |
||||
|
(newTimezone) => { |
||||
|
timezoneValue.value = newTimezone; |
||||
|
}, |
||||
|
); |
||||
|
const handleClickItem = (timezone: string) => { |
||||
|
timezoneValue.value = timezone; |
||||
|
emit('change', timezone); |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div> |
||||
|
<VbenIconButton |
||||
|
:tooltip="$t('ui.widgets.timezone.setTimezone')" |
||||
|
class="hover:animate-[shrink_0.3s_ease-in-out]" |
||||
|
@click="handleClick" |
||||
|
> |
||||
|
<TimezoneIcon class="text-foreground size-4" /> |
||||
|
</VbenIconButton> |
||||
|
<Modal :title="$t('ui.widgets.timezone.setTimezone')"> |
||||
|
<div class="timezone-container"> |
||||
|
<RadioGroup v-model="timezoneValue" class="flex flex-col gap-2"> |
||||
|
<div |
||||
|
class="flex cursor-pointer items-center gap-2" |
||||
|
v-for="item in props.timezoneOptions" |
||||
|
:key="`container${item}`" |
||||
|
@click="handleClickItem(item)" |
||||
|
> |
||||
|
<RadioGroupItem :id="item" :value="item" /> |
||||
|
<label class="cursor-pointer">{{ item }}</label> |
||||
|
</div> |
||||
|
</RadioGroup> |
||||
|
</div> |
||||
|
</Modal> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped> |
||||
|
.container { |
||||
|
padding-left: 20px; |
||||
|
} |
||||
|
</style> |
||||
Loading…
Reference in new issue