mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-04-13 18:11:03 +08:00
删除和更新规则
This commit is contained in:
@@ -4,9 +4,9 @@ import { NMessageProvider } from 'naive-ui';
|
||||
|
||||
<template>
|
||||
<Suspense>
|
||||
<n-message-provider>
|
||||
<NMessageProvider>
|
||||
<RouterView></RouterView>
|
||||
</n-message-provider>
|
||||
</NMessageProvider>
|
||||
</Suspense>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export const apiAuth = {
|
||||
const message = useMessage();
|
||||
if (password.length < 8) {
|
||||
message.error('密码至少8个字符!');
|
||||
return;
|
||||
|
||||
} else {
|
||||
const { data } = await axios.post<Update>('api/v1/auth/update', {
|
||||
username,
|
||||
|
||||
@@ -30,12 +30,15 @@ export const apiBangumi = {
|
||||
* @returns axios 请求返回的数据
|
||||
*/
|
||||
async updateRule(bangumiRule: BangumiRule) {
|
||||
const { data } = await axios.post('api/v1/bangumi/updateData', bangumiRule);
|
||||
const { data } = await axios.post<{
|
||||
msg: string;
|
||||
status: 'success';
|
||||
}>('api/v1/bangumi/updateRule', bangumiRule);
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除指定 bangumiId 的数据
|
||||
* 删除指定 bangumiId 的数据库规则,会在重新匹配到后重建
|
||||
* @param bangumiId - 需要删除的 bangumi 的 id
|
||||
* @returns axios 请求返回的数据
|
||||
*/
|
||||
@@ -53,14 +56,14 @@ export const apiBangumi = {
|
||||
* @returns axios 请求返回的数据
|
||||
*/
|
||||
async deleteRule(bangumiId: number, file: boolean) {
|
||||
const { data } = await axios.delete(
|
||||
`api/v1/bangumi/deleteRule/${bangumiId}`,
|
||||
{
|
||||
params: {
|
||||
file,
|
||||
},
|
||||
}
|
||||
);
|
||||
const { data } = await axios.delete<{
|
||||
msg: string;
|
||||
status: 'success';
|
||||
}>(`api/v1/bangumi/deleteRule/${bangumiId}`, {
|
||||
params: {
|
||||
file,
|
||||
},
|
||||
});
|
||||
return data;
|
||||
},
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { Write } from '@icon-park/vue-next';
|
||||
import { ErrorPicture , Write } from '@icon-park/vue-next';
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
@@ -16,7 +16,17 @@ defineEmits(['click']);
|
||||
<template>
|
||||
<div w-150px is-btn @click="() => $emit('click')">
|
||||
<div rounded-4px overflow-hidden poster-shandow rel>
|
||||
<img :src="`https://mikanani.me${poster}`" alt="poster" w-full h-210px />
|
||||
<div w-full h-210px>
|
||||
<template v-if="poster !== ''">
|
||||
<img :src="`https://mikanani.me${poster}`" alt="poster" wh-full />
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div wh-full f-cer border="1 white">
|
||||
<ErrorPicture theme="outline" size="24" fill="#333" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div
|
||||
abs
|
||||
|
||||
119
src/components/ab-edit-rule.vue
Normal file
119
src/components/ab-edit-rule.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<script lang="ts" setup>
|
||||
import type { AbEditRule, SettingItem } from '#/components';
|
||||
|
||||
const emit = defineEmits<{
|
||||
delete: [{ id: number; deleteFile: boolean }];
|
||||
apply: [item: AbEditRule];
|
||||
}>();
|
||||
|
||||
const show = defineModel('show', { default: false });
|
||||
const item = defineModel<AbEditRule>('item', {
|
||||
default: () => {
|
||||
return {
|
||||
id: -1,
|
||||
official_title: '',
|
||||
year: '',
|
||||
season: 1,
|
||||
offset: 0,
|
||||
filter: [],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const deleteRuleDialog = ref(false);
|
||||
watch(show, (val) => {
|
||||
if (!val) {
|
||||
deleteRuleDialog.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
function emitDelete(deleteFile: boolean) {
|
||||
emit('delete', {
|
||||
id: item.value.id,
|
||||
deleteFile,
|
||||
});
|
||||
}
|
||||
function emitApply() {
|
||||
emit('apply', item.value);
|
||||
}
|
||||
|
||||
const items: SettingItem<AbEditRule>[] = [
|
||||
{
|
||||
configKey: 'official_title',
|
||||
label: 'Officical Ttile',
|
||||
type: 'input',
|
||||
prop: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
{
|
||||
configKey: 'year',
|
||||
label: 'Year',
|
||||
type: 'input',
|
||||
css: 'w-72px',
|
||||
prop: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
{
|
||||
configKey: 'season',
|
||||
label: 'Season',
|
||||
type: 'input',
|
||||
css: 'w-72px',
|
||||
prop: {
|
||||
type: 'number',
|
||||
},
|
||||
bottomLine: true,
|
||||
},
|
||||
{
|
||||
configKey: 'offset',
|
||||
label: 'Offset',
|
||||
type: 'input',
|
||||
css: 'w-72px',
|
||||
prop: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
{
|
||||
configKey: 'filter',
|
||||
label: 'Exclude',
|
||||
type: 'dynamic-tags',
|
||||
bottomLine: true,
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ab-popup v-model:show="show" title="Edit Rule" css="w-380px">
|
||||
<div space-y-12px>
|
||||
<ab-setting
|
||||
v-for="i in items"
|
||||
:key="i.configKey"
|
||||
v-bind="i"
|
||||
v-model:data="item[i.configKey]"
|
||||
></ab-setting>
|
||||
|
||||
<div fx-cer justify-end space-x-10px>
|
||||
<ab-button
|
||||
size="small"
|
||||
type="warn"
|
||||
@click="() => (deleteRuleDialog = true)"
|
||||
>Delete</ab-button
|
||||
>
|
||||
<ab-button size="small" @click="emitApply">Apply</ab-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ab-popup v-model:show="deleteRuleDialog" title="Delete">
|
||||
<div>Delete Local File?</div>
|
||||
<div line my-8px></div>
|
||||
|
||||
<div fx-cer justify-center space-x-10px>
|
||||
<ab-button size="small" type="warn" @click="emitDelete(true)"
|
||||
>Yes</ab-button
|
||||
>
|
||||
<ab-button size="small" @click="emitDelete(false)">No</ab-button>
|
||||
</div>
|
||||
</ab-popup>
|
||||
</ab-popup>
|
||||
</template>
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { NDynamicTags } from 'naive-ui';
|
||||
import AbSwitch from '../basic/ab-switch.vue';
|
||||
import AbSelect from '../basic/ab-select.vue';
|
||||
import type { AbSettingProps } from '#/components';
|
||||
import { NDynamicTags } from 'naive-ui';
|
||||
|
||||
withDefaults(defineProps<AbSettingProps>(), {
|
||||
css: '',
|
||||
@@ -37,8 +37,8 @@ const data = defineModel<any>('data');
|
||||
v-bind="prop"
|
||||
/>
|
||||
|
||||
<div max-w-200px v-else-if="type === 'dynamic-tags'">
|
||||
<n-dynamic-tags v-model:value="data" size="small"></n-dynamic-tags>
|
||||
<div v-else-if="type === 'dynamic-tags'" max-w-200px>
|
||||
<NDynamicTags v-model:value="data" size="small"></NDynamicTags>
|
||||
</div>
|
||||
</ab-label>
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export const useAuth = createSharedComposable(() => {
|
||||
auth.value = '';
|
||||
message.success('Logout Success!');
|
||||
} else {
|
||||
message.error('Logout Fail!');
|
||||
message.error('Logout Failed!');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -66,7 +66,7 @@ export const useAuth = createSharedComposable(() => {
|
||||
message.success('Update Success!');
|
||||
clearUser();
|
||||
} else if (res === false) {
|
||||
message.error('Update Fail!');
|
||||
message.error('Update Failed!');
|
||||
} else {
|
||||
user.password = '';
|
||||
}
|
||||
|
||||
@@ -1,8 +1,65 @@
|
||||
<script lang="ts" setup>
|
||||
const { data } = storeToRefs(useBangumiStore());
|
||||
const { getAll } = useBangumiStore();
|
||||
import type { BangumiRule } from '#/bangumi';
|
||||
import { AbEditRule } from '#/components';
|
||||
|
||||
getAll();
|
||||
const { data } = storeToRefs(useBangumiStore());
|
||||
const { getAll, updateRule, removeRule } = useBangumiStore();
|
||||
|
||||
const editRule = reactive<{
|
||||
show: boolean;
|
||||
item: AbEditRule;
|
||||
}>({
|
||||
show: false,
|
||||
item: {
|
||||
id: -1,
|
||||
official_title: '',
|
||||
year: '',
|
||||
season: 1,
|
||||
offset: 0,
|
||||
filter: [],
|
||||
},
|
||||
});
|
||||
|
||||
function open(data: BangumiRule) {
|
||||
editRule.show = true;
|
||||
editRule.item = {
|
||||
id: data.id,
|
||||
official_title: data.official_title,
|
||||
year: data.year ?? '',
|
||||
season: data.season,
|
||||
offset: data.offset,
|
||||
filter: data.filter,
|
||||
};
|
||||
}
|
||||
|
||||
async function deleteRule({
|
||||
id,
|
||||
deleteFile,
|
||||
}: {
|
||||
id: number;
|
||||
deleteFile: boolean;
|
||||
}) {
|
||||
const res = await removeRule(id, deleteFile);
|
||||
if (res) {
|
||||
editRule.show = false;
|
||||
getAll();
|
||||
}
|
||||
}
|
||||
|
||||
async function applyRule(newData: AbEditRule) {
|
||||
const id = newData.id;
|
||||
const oldData = await apiBangumi.getRule(id);
|
||||
const data = Object.assign(oldData, newData);
|
||||
const res = await updateRule(data);
|
||||
if (res) {
|
||||
editRule.show = false;
|
||||
getAll();
|
||||
}
|
||||
}
|
||||
|
||||
onActivated(() => {
|
||||
getAll();
|
||||
});
|
||||
|
||||
definePage({
|
||||
name: 'Bangumi List',
|
||||
@@ -10,13 +67,22 @@ definePage({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div flex="~ wrap" space-y-12px space-x-50px>
|
||||
<div flex="~ wrap" gap-y-12px gap-x-50px>
|
||||
<ab-bangumi-card
|
||||
v-for="i in data"
|
||||
v-show="!i.deleted"
|
||||
:key="i.id"
|
||||
:poster="i.poster_link"
|
||||
:poster="i.poster_link ?? ''"
|
||||
:name="i.official_title"
|
||||
:season="i.season"
|
||||
@click="open(i)"
|
||||
></ab-bangumi-card>
|
||||
|
||||
<AbEditRule
|
||||
v-model:show="editRule.show"
|
||||
:item="editRule.item"
|
||||
@delete="deleteRule"
|
||||
@apply="applyRule"
|
||||
></AbEditRule>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,12 +1,41 @@
|
||||
import type { BangumiItem } from '#/bangumi';
|
||||
import type { BangumiRule } from '#/bangumi';
|
||||
|
||||
export const useBangumiStore = defineStore('bangumi', () => {
|
||||
const data = ref<BangumiItem[]>();
|
||||
const message = useMessage();
|
||||
const data = ref<BangumiRule[]>();
|
||||
|
||||
const getAll = async () => {
|
||||
const res = await apiBangumi.getAll();
|
||||
data.value = res;
|
||||
};
|
||||
|
||||
return { data, getAll };
|
||||
const updateRule = async (newRule: BangumiRule) => {
|
||||
try {
|
||||
const res = await apiBangumi.updateRule(newRule);
|
||||
if (res.status === 'success') {
|
||||
message.success('Update Success!');
|
||||
return true;
|
||||
} else {
|
||||
message.error('Update Failed!');
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('Operation Failed!');
|
||||
}
|
||||
};
|
||||
|
||||
const removeRule = async (bangumiId: number, deleteFile = false) => {
|
||||
try {
|
||||
const res = await apiBangumi.deleteRule(bangumiId, deleteFile);
|
||||
if (res.status === 'success') {
|
||||
message.success(`${res.msg} Success!`);
|
||||
return true;
|
||||
} else {
|
||||
message.error('Delete Failed!');
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('Operation Failed!');
|
||||
}
|
||||
};
|
||||
|
||||
return { data, getAll, updateRule, removeRule };
|
||||
});
|
||||
|
||||
@@ -59,9 +59,9 @@ export const useConfigStore = defineStore('config', () => {
|
||||
const setConfig = async () => {
|
||||
const status = await apiConfig.updateConfig(config.value);
|
||||
if (status) {
|
||||
message.success('apply success!');
|
||||
message.success('Apply Success!');
|
||||
} else {
|
||||
message.error('apply fail!');
|
||||
message.error('Apply Failed!');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ export const useProgramStore = defineStore('program', () => {
|
||||
if (success) {
|
||||
message.success(`${handle} Success!`);
|
||||
} else {
|
||||
message.error(`${handle} Fail!`);
|
||||
message.error(`${handle} Failed!`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
export interface BangumiItem {
|
||||
export interface BangumiRule {
|
||||
added: boolean;
|
||||
deleted: boolean;
|
||||
dpi: string;
|
||||
eps_collect: boolean;
|
||||
filter: string[];
|
||||
group_name: string;
|
||||
id: number;
|
||||
official_title: string;
|
||||
year: string | null;
|
||||
title_raw: string;
|
||||
offset: number;
|
||||
poster_link: string | null;
|
||||
rss_link: string[];
|
||||
rule_name: string;
|
||||
save_path: string;
|
||||
season: number;
|
||||
season_raw: string;
|
||||
group_name: string;
|
||||
dpi: string;
|
||||
source: string;
|
||||
source: string | null;
|
||||
subtitle: string;
|
||||
eps_collect: boolean;
|
||||
offset: number;
|
||||
filter: string[];
|
||||
rss_link: string[];
|
||||
poster_link: string;
|
||||
added: boolean;
|
||||
title_raw: string;
|
||||
year: string | null;
|
||||
}
|
||||
|
||||
@@ -15,5 +15,13 @@ export interface AbSettingProps {
|
||||
|
||||
export type SettingItem<T> = AbSettingProps & {
|
||||
configKey: keyof T;
|
||||
data?: any;
|
||||
};
|
||||
|
||||
export interface AbEditRule {
|
||||
id: number;
|
||||
official_title: string;
|
||||
year: string;
|
||||
season: number;
|
||||
offset: number;
|
||||
filter: string[];
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ export type LoginError = 'Password error' | 'User not found';
|
||||
|
||||
export type ApiErrorMessage = AuthError | LoginError;
|
||||
|
||||
export type ApiError = {
|
||||
export interface ApiError {
|
||||
status: 401 | 404 | 422;
|
||||
detail: ApiErrorMessage;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user