feat(web): add timezone setup for each bot

This commit is contained in:
Acbox
2026-04-11 19:39:17 +08:00
parent bb6c3c826e
commit 3307b27a80
3 changed files with 72 additions and 11 deletions
+2 -1
View File
@@ -39,7 +39,8 @@
"no": "No",
"details": "Details",
"pin": "Pin",
"unpin": "Unpin"
"unpin": "Unpin",
"searchTimezone": "Search timezone"
},
"auth": {
"welcome": "Welcome Back",
+2 -1
View File
@@ -39,7 +39,8 @@
"no": "否",
"details": "详情",
"pin": "置顶",
"unpin": "取消置顶"
"unpin": "取消置顶",
"searchTimezone": "搜索时区"
},
"auth": {
"welcome": "欢迎回来",
@@ -212,6 +212,19 @@
/>
</div>
<div class="space-y-2">
<Label>{{ $t('bots.timezone') }}</Label>
<TimezoneSelect
v-model="form.timezone"
allow-empty
:placeholder="defaultTimezone"
:empty-label="timezoneEmptyLabel"
/>
<p class="text-xs text-muted-foreground">
{{ $t('bots.timezoneInheritedHint') }}
</p>
</div>
<Separator />
<!-- Language -->
@@ -262,10 +275,10 @@
<!-- Save -->
<div class="flex justify-end">
<Button
:disabled="!hasChanges || isLoading"
:disabled="!hasChanges || saveLoading"
@click="handleSave"
>
<Spinner v-if="isLoading" />
<Spinner v-if="saveLoading" />
{{ $t('bots.settings.save') }}
</Button>
</div>
@@ -322,6 +335,7 @@ import { useRouter } from 'vue-router'
import { toast } from 'vue-sonner'
import { useI18n } from 'vue-i18n'
import ConfirmPopover from '@/components/confirm-popover/index.vue'
import TimezoneSelect from '@/components/timezone-select/index.vue'
import ModelSelect from './model-select.vue'
import ReasoningEffortSelect from './reasoning-effort-select.vue'
import { EFFORT_LABELS, EFFORT_OPACITY } from './reasoning-effort'
@@ -330,10 +344,11 @@ import MemoryProviderSelect from './memory-provider-select.vue'
import TtsModelSelect from './tts-model-select.vue'
import BrowserContextSelect from './browser-context-select.vue'
import { useQuery, useMutation, useQueryCache } from '@pinia/colada'
import { getBotsByBotIdSettings, putBotsByBotIdSettings, deleteBotsById, getModels, getProviders, getSearchProviders, getMemoryProviders, getSpeechProviders, getSpeechModels, getBrowserContexts, getBotsByBotIdMemoryStatus, postBotsByBotIdMemoryRebuild } from '@memohai/sdk'
import { getBotsById, putBotsById, getBotsByBotIdSettings, putBotsByBotIdSettings, deleteBotsById, getModels, getProviders, getSearchProviders, getMemoryProviders, getSpeechProviders, getSpeechModels, getBrowserContexts, getBotsByBotIdMemoryStatus, postBotsByBotIdMemoryRebuild } from '@memohai/sdk'
import type { SettingsSettings } from '@memohai/sdk'
import type { Ref } from 'vue'
import { resolveApiErrorMessage } from '@/utils/api-error'
import { useUserStore } from '@/store/user'
const props = defineProps<{
botId: string
@@ -341,8 +356,13 @@ const props = defineProps<{
const { t } = useI18n()
const router = useRouter()
const userStore = useUserStore()
const botIdRef = computed(() => props.botId) as Ref<string>
const defaultTimezone = computed(() => userStore.userInfo.timezone || 'UTC')
const timezoneEmptyLabel = computed(() =>
`${t('bots.timezoneInherited')} (${defaultTimezone.value})`,
)
// ---- Data ----
const queryCache = useQueryCache()
@@ -356,6 +376,15 @@ const { data: settings } = useQuery({
enabled: () => !!botIdRef.value,
})
const { data: bot } = useQuery({
key: () => ['bot', botIdRef.value],
query: async () => {
const { data } = await getBotsById({ path: { id: botIdRef.value }, throwOnError: true })
return data
},
enabled: () => !!botIdRef.value,
})
const { data: modelData } = useQuery({
key: ['all-models'],
query: async () => {
@@ -424,6 +453,21 @@ const { mutateAsync: updateSettings, isLoading } = useMutation({
onSettled: () => queryCache.invalidateQueries({ key: ['bot-settings', botIdRef.value] }),
})
const { mutateAsync: updateBot, isLoading: isUpdatingBot } = useMutation({
mutation: async (timezone: string) => {
const { data } = await putBotsById({
path: { id: botIdRef.value },
body: { timezone },
throwOnError: true,
})
return data
},
onSettled: () => {
queryCache.invalidateQueries({ key: ['bot', botIdRef.value] })
queryCache.invalidateQueries({ key: ['bots'] })
},
})
const { mutateAsync: deleteBot, isLoading: deleteLoading } = useMutation({
mutation: async () => {
await deleteBotsById({ path: { id: botIdRef.value }, throwOnError: true })
@@ -455,6 +499,7 @@ const form = reactive({
memory_provider_id: '',
tts_model_id: '',
browser_context_id: '',
timezone: '',
language: '',
reasoning_enabled: false,
reasoning_effort: 'medium',
@@ -599,10 +644,14 @@ watch(settings, (val) => {
}
}, { immediate: true })
const hasChanges = computed(() => {
watch(bot, (val) => {
form.timezone = val?.timezone ?? ''
}, { immediate: true })
const hasSettingsChanges = computed(() => {
if (!settings.value) return true
const s = settings.value
let changed =
return (
form.chat_model_id !== (s.chat_model_id ?? '')
|| form.title_model_id !== (s.title_model_id ?? '')
|| form.image_model_id !== (s.image_model_id ?? '')
@@ -613,15 +662,25 @@ const hasChanges = computed(() => {
|| form.language !== (s.language ?? '')
|| form.reasoning_enabled !== (s.reasoning_enabled ?? false)
|| form.reasoning_effort !== (s.reasoning_effort || 'medium')
return changed
)
})
const hasTimezoneChanges = computed(() => form.timezone !== (bot.value?.timezone ?? ''))
const hasChanges = computed(() => hasSettingsChanges.value || hasTimezoneChanges.value)
const saveLoading = computed(() => isLoading.value || isUpdatingBot.value)
async function handleSave() {
try {
await updateSettings({ ...form })
if (hasSettingsChanges.value) {
const { timezone: _timezone, ...settingsPayload } = form
await updateSettings(settingsPayload)
}
if (hasTimezoneChanges.value) {
await updateBot(form.timezone)
}
toast.success(t('bots.settings.saveSuccess'))
} catch {
return
} catch (error) {
toast.error(resolveApiErrorMessage(error, t('common.saveFailed')))
}
}