mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
feat(web): add timezone setup for each bot
This commit is contained in:
@@ -39,7 +39,8 @@
|
|||||||
"no": "No",
|
"no": "No",
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
"pin": "Pin",
|
"pin": "Pin",
|
||||||
"unpin": "Unpin"
|
"unpin": "Unpin",
|
||||||
|
"searchTimezone": "Search timezone"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"welcome": "Welcome Back",
|
"welcome": "Welcome Back",
|
||||||
|
|||||||
@@ -39,7 +39,8 @@
|
|||||||
"no": "否",
|
"no": "否",
|
||||||
"details": "详情",
|
"details": "详情",
|
||||||
"pin": "置顶",
|
"pin": "置顶",
|
||||||
"unpin": "取消置顶"
|
"unpin": "取消置顶",
|
||||||
|
"searchTimezone": "搜索时区"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"welcome": "欢迎回来",
|
"welcome": "欢迎回来",
|
||||||
|
|||||||
@@ -212,6 +212,19 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</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 />
|
<Separator />
|
||||||
|
|
||||||
<!-- Language -->
|
<!-- Language -->
|
||||||
@@ -262,10 +275,10 @@
|
|||||||
<!-- Save -->
|
<!-- Save -->
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
:disabled="!hasChanges || isLoading"
|
:disabled="!hasChanges || saveLoading"
|
||||||
@click="handleSave"
|
@click="handleSave"
|
||||||
>
|
>
|
||||||
<Spinner v-if="isLoading" />
|
<Spinner v-if="saveLoading" />
|
||||||
{{ $t('bots.settings.save') }}
|
{{ $t('bots.settings.save') }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -322,6 +335,7 @@ import { useRouter } from 'vue-router'
|
|||||||
import { toast } from 'vue-sonner'
|
import { toast } from 'vue-sonner'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import ConfirmPopover from '@/components/confirm-popover/index.vue'
|
import ConfirmPopover from '@/components/confirm-popover/index.vue'
|
||||||
|
import TimezoneSelect from '@/components/timezone-select/index.vue'
|
||||||
import ModelSelect from './model-select.vue'
|
import ModelSelect from './model-select.vue'
|
||||||
import ReasoningEffortSelect from './reasoning-effort-select.vue'
|
import ReasoningEffortSelect from './reasoning-effort-select.vue'
|
||||||
import { EFFORT_LABELS, EFFORT_OPACITY } from './reasoning-effort'
|
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 TtsModelSelect from './tts-model-select.vue'
|
||||||
import BrowserContextSelect from './browser-context-select.vue'
|
import BrowserContextSelect from './browser-context-select.vue'
|
||||||
import { useQuery, useMutation, useQueryCache } from '@pinia/colada'
|
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 { SettingsSettings } from '@memohai/sdk'
|
||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
import { resolveApiErrorMessage } from '@/utils/api-error'
|
import { resolveApiErrorMessage } from '@/utils/api-error'
|
||||||
|
import { useUserStore } from '@/store/user'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
botId: string
|
botId: string
|
||||||
@@ -341,8 +356,13 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const botIdRef = computed(() => props.botId) as Ref<string>
|
const botIdRef = computed(() => props.botId) as Ref<string>
|
||||||
|
const defaultTimezone = computed(() => userStore.userInfo.timezone || 'UTC')
|
||||||
|
const timezoneEmptyLabel = computed(() =>
|
||||||
|
`${t('bots.timezoneInherited')} (${defaultTimezone.value})`,
|
||||||
|
)
|
||||||
|
|
||||||
// ---- Data ----
|
// ---- Data ----
|
||||||
const queryCache = useQueryCache()
|
const queryCache = useQueryCache()
|
||||||
@@ -356,6 +376,15 @@ const { data: settings } = useQuery({
|
|||||||
enabled: () => !!botIdRef.value,
|
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({
|
const { data: modelData } = useQuery({
|
||||||
key: ['all-models'],
|
key: ['all-models'],
|
||||||
query: async () => {
|
query: async () => {
|
||||||
@@ -424,6 +453,21 @@ const { mutateAsync: updateSettings, isLoading } = useMutation({
|
|||||||
onSettled: () => queryCache.invalidateQueries({ key: ['bot-settings', botIdRef.value] }),
|
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({
|
const { mutateAsync: deleteBot, isLoading: deleteLoading } = useMutation({
|
||||||
mutation: async () => {
|
mutation: async () => {
|
||||||
await deleteBotsById({ path: { id: botIdRef.value }, throwOnError: true })
|
await deleteBotsById({ path: { id: botIdRef.value }, throwOnError: true })
|
||||||
@@ -455,6 +499,7 @@ const form = reactive({
|
|||||||
memory_provider_id: '',
|
memory_provider_id: '',
|
||||||
tts_model_id: '',
|
tts_model_id: '',
|
||||||
browser_context_id: '',
|
browser_context_id: '',
|
||||||
|
timezone: '',
|
||||||
language: '',
|
language: '',
|
||||||
reasoning_enabled: false,
|
reasoning_enabled: false,
|
||||||
reasoning_effort: 'medium',
|
reasoning_effort: 'medium',
|
||||||
@@ -599,10 +644,14 @@ watch(settings, (val) => {
|
|||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
const hasChanges = computed(() => {
|
watch(bot, (val) => {
|
||||||
|
form.timezone = val?.timezone ?? ''
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
const hasSettingsChanges = computed(() => {
|
||||||
if (!settings.value) return true
|
if (!settings.value) return true
|
||||||
const s = settings.value
|
const s = settings.value
|
||||||
let changed =
|
return (
|
||||||
form.chat_model_id !== (s.chat_model_id ?? '')
|
form.chat_model_id !== (s.chat_model_id ?? '')
|
||||||
|| form.title_model_id !== (s.title_model_id ?? '')
|
|| form.title_model_id !== (s.title_model_id ?? '')
|
||||||
|| form.image_model_id !== (s.image_model_id ?? '')
|
|| form.image_model_id !== (s.image_model_id ?? '')
|
||||||
@@ -613,15 +662,25 @@ const hasChanges = computed(() => {
|
|||||||
|| form.language !== (s.language ?? '')
|
|| form.language !== (s.language ?? '')
|
||||||
|| form.reasoning_enabled !== (s.reasoning_enabled ?? false)
|
|| form.reasoning_enabled !== (s.reasoning_enabled ?? false)
|
||||||
|| form.reasoning_effort !== (s.reasoning_effort || 'medium')
|
|| 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() {
|
async function handleSave() {
|
||||||
try {
|
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'))
|
toast.success(t('bots.settings.saveSuccess'))
|
||||||
} catch {
|
} catch (error) {
|
||||||
return
|
toast.error(resolveApiErrorMessage(error, t('common.saveFailed')))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user