mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-03-20 03:46:40 +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]]
|
[[package]]
|
||||||
name = "auto-bangumi"
|
name = "auto-bangumi"
|
||||||
version = "3.2.3b4"
|
version = "3.2.3b5"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiosqlite" },
|
{ name = "aiosqlite" },
|
||||||
|
|||||||
@@ -30,6 +30,61 @@ const loading = reactive({
|
|||||||
subscribe: false,
|
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
|
// Computed
|
||||||
const posterSrc = computed(() => resolvePosterUrl(rule.value.poster_link));
|
const posterSrc = computed(() => resolvePosterUrl(rule.value.poster_link));
|
||||||
|
|
||||||
@@ -82,34 +137,9 @@ function addRss() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rss.value.aggregate) {
|
if (rss.value.aggregate) {
|
||||||
// Aggregate mode: directly add RSS
|
addRssAggregate(rss.value);
|
||||||
useApi(apiRSS.add, {
|
|
||||||
showMessage: true,
|
|
||||||
onBeforeExecute() {
|
|
||||||
loading.analyze = true;
|
|
||||||
},
|
|
||||||
onSuccess() {
|
|
||||||
show.value = false;
|
|
||||||
},
|
|
||||||
onFinally() {
|
|
||||||
loading.analyze = false;
|
|
||||||
},
|
|
||||||
}).execute(rss.value);
|
|
||||||
} else {
|
} else {
|
||||||
// Single mode: analyze and show confirm
|
analyzeRss(rss.value);
|
||||||
useApi(apiDownload.analysis, {
|
|
||||||
showMessage: true,
|
|
||||||
onBeforeExecute() {
|
|
||||||
loading.analyze = true;
|
|
||||||
},
|
|
||||||
onSuccess(res) {
|
|
||||||
rule.value = res;
|
|
||||||
step.value = 'confirm';
|
|
||||||
},
|
|
||||||
onFinally() {
|
|
||||||
loading.analyze = false;
|
|
||||||
},
|
|
||||||
}).execute(rss.value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,36 +176,12 @@ async function autoDetectOffset() {
|
|||||||
|
|
||||||
function collect() {
|
function collect() {
|
||||||
if (!rule.value) return;
|
if (!rule.value) return;
|
||||||
useApi(apiDownload.collection, {
|
executeCollect(rule.value);
|
||||||
showMessage: true,
|
|
||||||
onBeforeExecute() {
|
|
||||||
loading.collect = true;
|
|
||||||
},
|
|
||||||
onSuccess() {
|
|
||||||
getAll();
|
|
||||||
show.value = false;
|
|
||||||
},
|
|
||||||
onFinally() {
|
|
||||||
loading.collect = false;
|
|
||||||
},
|
|
||||||
}).execute(rule.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function subscribe() {
|
function subscribe() {
|
||||||
if (!rule.value) return;
|
if (!rule.value) return;
|
||||||
useApi(apiDownload.subscribe, {
|
executeSubscribe(rule.value, rss.value);
|
||||||
showMessage: true,
|
|
||||||
onBeforeExecute() {
|
|
||||||
loading.subscribe = true;
|
|
||||||
},
|
|
||||||
onSuccess() {
|
|
||||||
getAll();
|
|
||||||
show.value = false;
|
|
||||||
},
|
|
||||||
onFinally() {
|
|
||||||
loading.subscribe = false;
|
|
||||||
},
|
|
||||||
}).execute(rule.value, rss.value);
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -9,14 +9,6 @@ export const useAuth = createSharedComposable(() => {
|
|||||||
|
|
||||||
const isLoggedIn = useLocalStorage('isLoggedIn', false);
|
const isLoggedIn = useLocalStorage('isLoggedIn', false);
|
||||||
|
|
||||||
watch(isLoggedIn, (v) => {
|
|
||||||
if (v) {
|
|
||||||
router.replace({ name: 'Index' });
|
|
||||||
} else {
|
|
||||||
router.replace({ name: 'Login' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const user = reactive<User>({
|
const user = reactive<User>({
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
@@ -51,6 +43,7 @@ export const useAuth = createSharedComposable(() => {
|
|||||||
isLoggedIn.value = true;
|
isLoggedIn.value = true;
|
||||||
clearUser();
|
clearUser();
|
||||||
message.success(t('notify.login_success'));
|
message.success(t('notify.login_success'));
|
||||||
|
router.replace({ name: 'Index' });
|
||||||
})
|
})
|
||||||
.catch((err: ApiError) => {
|
.catch((err: ApiError) => {
|
||||||
if (err.status === 404) {
|
if (err.status === 404) {
|
||||||
@@ -64,6 +57,7 @@ export const useAuth = createSharedComposable(() => {
|
|||||||
onSuccess() {
|
onSuccess() {
|
||||||
clearUser();
|
clearUser();
|
||||||
isLoggedIn.value = false;
|
isLoggedIn.value = false;
|
||||||
|
router.replace({ name: 'Login' });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -16,18 +16,20 @@ function normalizeLocale(locale: string): Languages {
|
|||||||
return 'en';
|
return 'en';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const i18n = createI18n({
|
||||||
|
legacy: false,
|
||||||
|
locale: normalizeLocale(navigator.language),
|
||||||
|
fallbackLocale: 'en',
|
||||||
|
messages,
|
||||||
|
});
|
||||||
|
|
||||||
export const useMyI18n = createSharedComposable(() => {
|
export const useMyI18n = createSharedComposable(() => {
|
||||||
const lang = useLocalStorage<Languages>(
|
const lang = useLocalStorage<Languages>(
|
||||||
'lang',
|
'lang',
|
||||||
normalizeLocale(navigator.language)
|
normalizeLocale(navigator.language)
|
||||||
);
|
);
|
||||||
|
|
||||||
const i18n = createI18n({
|
i18n.global.locale.value = lang.value as unknown as Languages;
|
||||||
legacy: false,
|
|
||||||
locale: lang.value,
|
|
||||||
fallbackLocale: 'en',
|
|
||||||
messages,
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(lang, (val) => {
|
watch(lang, (val) => {
|
||||||
i18n.global.locale.value = val as unknown as Languages;
|
i18n.global.locale.value = val as unknown as Languages;
|
||||||
|
|||||||
@@ -15,12 +15,7 @@ export const usePasskey = createSharedComposable(() => {
|
|||||||
// 状态
|
// 状态
|
||||||
const passkeys = ref<PasskeyItem[]>([]);
|
const passkeys = ref<PasskeyItem[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const isSupported = ref(false);
|
const isSupported = ref(isWebAuthnSupported());
|
||||||
|
|
||||||
// 检测浏览器支持
|
|
||||||
onMounted(() => {
|
|
||||||
isSupported.value = isWebAuthnSupported();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 加载 Passkey 列表
|
// 加载 Passkey 列表
|
||||||
async function loadPasskeys() {
|
async function loadPasskeys() {
|
||||||
|
|||||||
@@ -151,6 +151,7 @@
|
|||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"delete_hit": "Delete Local File?",
|
"delete_hit": "Delete Local File?",
|
||||||
"disable": "Disable",
|
"disable": "Disable",
|
||||||
|
"edit": "Edit",
|
||||||
"edit_rule": "Edit Rule",
|
"edit_rule": "Edit Rule",
|
||||||
"enable": "Enable",
|
"enable": "Enable",
|
||||||
"enable_hit": "Do you want to enable this rule?",
|
"enable_hit": "Do you want to enable this rule?",
|
||||||
|
|||||||
@@ -151,6 +151,7 @@
|
|||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
"delete_hit": "是否删除本地文件?",
|
"delete_hit": "是否删除本地文件?",
|
||||||
"disable": "禁用",
|
"disable": "禁用",
|
||||||
|
"edit": "编辑",
|
||||||
"edit_rule": "编辑规则",
|
"edit_rule": "编辑规则",
|
||||||
"enable": "启用",
|
"enable": "启用",
|
||||||
"enable_hit": "确定启用该规则?",
|
"enable_hit": "确定启用该规则?",
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import { createPinia } from 'pinia';
|
import { createPinia } from 'pinia';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
|
import { i18n } from './hooks/useMyI18n';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
|
|
||||||
import '@unocss/reset/tailwind-compat.css';
|
import '@unocss/reset/tailwind-compat.css';
|
||||||
import 'virtual:uno.css';
|
import 'virtual:uno.css';
|
||||||
|
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
const { i18n } = useMyI18n();
|
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ async function refreshCalendar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onActivated(() => {
|
onActivated(() => {
|
||||||
|
if (refreshing.value) return;
|
||||||
refreshCalendar();
|
refreshCalendar();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -24,17 +24,20 @@ const isNull = computed(() => {
|
|||||||
return config.value.downloader.host === '';
|
return config.value.downloader.host === '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isActive = ref(false);
|
||||||
const { pause, resume } = useIntervalFn(getAll, 5000, { immediate: false });
|
const { pause, resume } = useIntervalFn(getAll, 5000, { immediate: false });
|
||||||
|
|
||||||
onActivated(async () => {
|
onActivated(async () => {
|
||||||
|
isActive.value = true;
|
||||||
await getConfig();
|
await getConfig();
|
||||||
if (!isNull.value) {
|
if (isActive.value && !isNull.value) {
|
||||||
getAll();
|
getAll();
|
||||||
resume();
|
resume();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onDeactivated(() => {
|
onDeactivated(() => {
|
||||||
|
isActive.value = false;
|
||||||
pause();
|
pause();
|
||||||
clearSelection();
|
clearSelection();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ router.beforeEach(async (to) => {
|
|||||||
try {
|
try {
|
||||||
const status = await apiSetup.getStatus();
|
const status = await apiSetup.getStatus();
|
||||||
needSetup = status.need_setup;
|
needSetup = status.need_setup;
|
||||||
|
setupChecked = true;
|
||||||
} catch {
|
} catch {
|
||||||
// If check fails, proceed normally
|
// If check fails, retry on next navigation
|
||||||
}
|
}
|
||||||
setupChecked = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to setup if needed
|
// Redirect to setup if needed
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ export const useLogStore = defineStore('log', () => {
|
|||||||
immediateCallback: true,
|
immediateCallback: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(isLoggedIn, (loggedIn) => {
|
||||||
|
if (!loggedIn) {
|
||||||
|
offUpdate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const { copy: clipboardCopy, isSupported: clipboardSupported } = useClipboard({
|
const { copy: clipboardCopy, isSupported: clipboardSupported } = useClipboard({
|
||||||
legacy: true,
|
legacy: true,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user