mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-03-19 19:37:14 +08:00
fix(ui): fix auth routing, i18n init, and component lifecycle issues
- useAuth: replace watcher with explicit router.replace on login/logout - useMyI18n: create single i18n instance at module level (avoid dupes) - usePasskey: detect WebAuthn support synchronously (no onMounted) - main.ts: import i18n from hook module instead of calling composable - ab-add-rss: hoist useApi composables outside functions to avoid recreating them on each call - calendar: prevent duplicate refreshes when already refreshing - downloader: guard interval polling against stale activation state - router: only mark setupChecked on successful status check - log store: stop SSE log updates on logout - i18n: add missing "edit" translation key (en + zh-CN) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2
backend/uv.lock
generated
2
backend/uv.lock
generated
@@ -61,7 +61,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "auto-bangumi"
|
||||
version = "3.2.3b4"
|
||||
version = "3.2.3b5"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "aiosqlite" },
|
||||
|
||||
@@ -30,6 +30,61 @@ const loading = reactive({
|
||||
subscribe: false,
|
||||
});
|
||||
|
||||
const { execute: addRssAggregate } = useApi(apiRSS.add, {
|
||||
showMessage: true,
|
||||
onBeforeExecute() {
|
||||
loading.analyze = true;
|
||||
},
|
||||
onSuccess() {
|
||||
show.value = false;
|
||||
},
|
||||
onFinally() {
|
||||
loading.analyze = false;
|
||||
},
|
||||
});
|
||||
|
||||
const { execute: analyzeRss } = useApi(apiDownload.analysis, {
|
||||
showMessage: true,
|
||||
onBeforeExecute() {
|
||||
loading.analyze = true;
|
||||
},
|
||||
onSuccess(res) {
|
||||
rule.value = res;
|
||||
step.value = 'confirm';
|
||||
},
|
||||
onFinally() {
|
||||
loading.analyze = false;
|
||||
},
|
||||
});
|
||||
|
||||
const { execute: executeCollect } = useApi(apiDownload.collection, {
|
||||
showMessage: true,
|
||||
onBeforeExecute() {
|
||||
loading.collect = true;
|
||||
},
|
||||
onSuccess() {
|
||||
getAll();
|
||||
show.value = false;
|
||||
},
|
||||
onFinally() {
|
||||
loading.collect = false;
|
||||
},
|
||||
});
|
||||
|
||||
const { execute: executeSubscribe } = useApi(apiDownload.subscribe, {
|
||||
showMessage: true,
|
||||
onBeforeExecute() {
|
||||
loading.subscribe = true;
|
||||
},
|
||||
onSuccess() {
|
||||
getAll();
|
||||
show.value = false;
|
||||
},
|
||||
onFinally() {
|
||||
loading.subscribe = false;
|
||||
},
|
||||
});
|
||||
|
||||
// Computed
|
||||
const posterSrc = computed(() => resolvePosterUrl(rule.value.poster_link));
|
||||
|
||||
@@ -82,34 +137,9 @@ function addRss() {
|
||||
}
|
||||
|
||||
if (rss.value.aggregate) {
|
||||
// Aggregate mode: directly add RSS
|
||||
useApi(apiRSS.add, {
|
||||
showMessage: true,
|
||||
onBeforeExecute() {
|
||||
loading.analyze = true;
|
||||
},
|
||||
onSuccess() {
|
||||
show.value = false;
|
||||
},
|
||||
onFinally() {
|
||||
loading.analyze = false;
|
||||
},
|
||||
}).execute(rss.value);
|
||||
addRssAggregate(rss.value);
|
||||
} else {
|
||||
// Single mode: analyze and show confirm
|
||||
useApi(apiDownload.analysis, {
|
||||
showMessage: true,
|
||||
onBeforeExecute() {
|
||||
loading.analyze = true;
|
||||
},
|
||||
onSuccess(res) {
|
||||
rule.value = res;
|
||||
step.value = 'confirm';
|
||||
},
|
||||
onFinally() {
|
||||
loading.analyze = false;
|
||||
},
|
||||
}).execute(rss.value);
|
||||
analyzeRss(rss.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,36 +176,12 @@ async function autoDetectOffset() {
|
||||
|
||||
function collect() {
|
||||
if (!rule.value) return;
|
||||
useApi(apiDownload.collection, {
|
||||
showMessage: true,
|
||||
onBeforeExecute() {
|
||||
loading.collect = true;
|
||||
},
|
||||
onSuccess() {
|
||||
getAll();
|
||||
show.value = false;
|
||||
},
|
||||
onFinally() {
|
||||
loading.collect = false;
|
||||
},
|
||||
}).execute(rule.value);
|
||||
executeCollect(rule.value);
|
||||
}
|
||||
|
||||
function subscribe() {
|
||||
if (!rule.value) return;
|
||||
useApi(apiDownload.subscribe, {
|
||||
showMessage: true,
|
||||
onBeforeExecute() {
|
||||
loading.subscribe = true;
|
||||
},
|
||||
onSuccess() {
|
||||
getAll();
|
||||
show.value = false;
|
||||
},
|
||||
onFinally() {
|
||||
loading.subscribe = false;
|
||||
},
|
||||
}).execute(rule.value, rss.value);
|
||||
executeSubscribe(rule.value, rss.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -9,14 +9,6 @@ export const useAuth = createSharedComposable(() => {
|
||||
|
||||
const isLoggedIn = useLocalStorage('isLoggedIn', false);
|
||||
|
||||
watch(isLoggedIn, (v) => {
|
||||
if (v) {
|
||||
router.replace({ name: 'Index' });
|
||||
} else {
|
||||
router.replace({ name: 'Login' });
|
||||
}
|
||||
});
|
||||
|
||||
const user = reactive<User>({
|
||||
username: '',
|
||||
password: '',
|
||||
@@ -51,6 +43,7 @@ export const useAuth = createSharedComposable(() => {
|
||||
isLoggedIn.value = true;
|
||||
clearUser();
|
||||
message.success(t('notify.login_success'));
|
||||
router.replace({ name: 'Index' });
|
||||
})
|
||||
.catch((err: ApiError) => {
|
||||
if (err.status === 404) {
|
||||
@@ -64,6 +57,7 @@ export const useAuth = createSharedComposable(() => {
|
||||
onSuccess() {
|
||||
clearUser();
|
||||
isLoggedIn.value = false;
|
||||
router.replace({ name: 'Login' });
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -16,18 +16,20 @@ function normalizeLocale(locale: string): Languages {
|
||||
return 'en';
|
||||
}
|
||||
|
||||
export const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: normalizeLocale(navigator.language),
|
||||
fallbackLocale: 'en',
|
||||
messages,
|
||||
});
|
||||
|
||||
export const useMyI18n = createSharedComposable(() => {
|
||||
const lang = useLocalStorage<Languages>(
|
||||
'lang',
|
||||
normalizeLocale(navigator.language)
|
||||
);
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: lang.value,
|
||||
fallbackLocale: 'en',
|
||||
messages,
|
||||
});
|
||||
i18n.global.locale.value = lang.value as unknown as Languages;
|
||||
|
||||
watch(lang, (val) => {
|
||||
i18n.global.locale.value = val as unknown as Languages;
|
||||
|
||||
@@ -15,12 +15,7 @@ export const usePasskey = createSharedComposable(() => {
|
||||
// 状态
|
||||
const passkeys = ref<PasskeyItem[]>([]);
|
||||
const loading = ref(false);
|
||||
const isSupported = ref(false);
|
||||
|
||||
// 检测浏览器支持
|
||||
onMounted(() => {
|
||||
isSupported.value = isWebAuthnSupported();
|
||||
});
|
||||
const isSupported = ref(isWebAuthnSupported());
|
||||
|
||||
// 加载 Passkey 列表
|
||||
async function loadPasskeys() {
|
||||
|
||||
@@ -151,6 +151,7 @@
|
||||
"delete": "Delete",
|
||||
"delete_hit": "Delete Local File?",
|
||||
"disable": "Disable",
|
||||
"edit": "Edit",
|
||||
"edit_rule": "Edit Rule",
|
||||
"enable": "Enable",
|
||||
"enable_hit": "Do you want to enable this rule?",
|
||||
|
||||
@@ -151,6 +151,7 @@
|
||||
"delete": "删除",
|
||||
"delete_hit": "是否删除本地文件?",
|
||||
"disable": "禁用",
|
||||
"edit": "编辑",
|
||||
"edit_rule": "编辑规则",
|
||||
"enable": "启用",
|
||||
"enable_hit": "确定启用该规则?",
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import { router } from './router';
|
||||
import { i18n } from './hooks/useMyI18n';
|
||||
import App from './App.vue';
|
||||
|
||||
import '@unocss/reset/tailwind-compat.css';
|
||||
import 'virtual:uno.css';
|
||||
|
||||
const pinia = createPinia();
|
||||
const { i18n } = useMyI18n();
|
||||
|
||||
const app = createApp(App);
|
||||
app.use(router);
|
||||
|
||||
@@ -25,6 +25,7 @@ async function refreshCalendar() {
|
||||
}
|
||||
|
||||
onActivated(() => {
|
||||
if (refreshing.value) return;
|
||||
refreshCalendar();
|
||||
});
|
||||
|
||||
|
||||
@@ -24,17 +24,20 @@ const isNull = computed(() => {
|
||||
return config.value.downloader.host === '';
|
||||
});
|
||||
|
||||
const isActive = ref(false);
|
||||
const { pause, resume } = useIntervalFn(getAll, 5000, { immediate: false });
|
||||
|
||||
onActivated(async () => {
|
||||
isActive.value = true;
|
||||
await getConfig();
|
||||
if (!isNull.value) {
|
||||
if (isActive.value && !isNull.value) {
|
||||
getAll();
|
||||
resume();
|
||||
}
|
||||
});
|
||||
|
||||
onDeactivated(() => {
|
||||
isActive.value = false;
|
||||
pause();
|
||||
clearSelection();
|
||||
});
|
||||
|
||||
@@ -16,10 +16,10 @@ router.beforeEach(async (to) => {
|
||||
try {
|
||||
const status = await apiSetup.getStatus();
|
||||
needSetup = status.need_setup;
|
||||
setupChecked = true;
|
||||
} catch {
|
||||
// If check fails, proceed normally
|
||||
// If check fails, retry on next navigation
|
||||
}
|
||||
setupChecked = true;
|
||||
}
|
||||
|
||||
// Redirect to setup if needed
|
||||
|
||||
@@ -27,6 +27,12 @@ export const useLogStore = defineStore('log', () => {
|
||||
immediateCallback: true,
|
||||
});
|
||||
|
||||
watch(isLoggedIn, (loggedIn) => {
|
||||
if (!loggedIn) {
|
||||
offUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
const { copy: clipboardCopy, isSupported: clipboardSupported } = useClipboard({
|
||||
legacy: true,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user