Merge pull request #422 from Rewrite0/3.1-dev

webui: Code refactoring optimization
This commit is contained in:
Rewrite0
2023-08-10 12:24:08 +08:00
committed by GitHub
11 changed files with 170 additions and 212 deletions

View File

@@ -1 +1,3 @@
public-hoist-pattern[]=@vue/runtime-core
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*

View File

@@ -32,9 +32,11 @@ export const apiBangumi = {
* @returns axios 请求返回的数据
*/
async updateRule(bangumiId: number, bangumiRule: BangumiRule) {
const rule = omit(bangumiRule, ['id']);
const { data } = await axios.patch<ApiSuccess>(
`api/v1/bangumi/update/${bangumiId}`,
bangumiRule
rule
);
return data;
},

View File

@@ -4,35 +4,25 @@ interface Status {
status: 'Success';
}
interface AnalysisError {
status: 'Failed to parse link';
}
export const apiDownload = {
/**
* 解析 RSS 链接
* @param rss_link - RSS 链接
*/
async analysis(rss_link: string) {
const fetchResult = createEventHook<BangumiRule>();
const fetchError = createEventHook<AnalysisError>();
axios
.post<any>('api/v1/download/analysis', {
const { data } = await axios.post<BangumiRule & { status?: string }>(
'api/v1/download/analysis',
{
rss_link,
})
.then(({ data }) => {
if (data.status) {
fetchError.trigger(data as AnalysisError);
} else {
fetchResult.trigger(data as BangumiRule);
}
});
}
);
return {
onResult: fetchResult.on,
onError: fetchError.on,
};
// 解析失败抛出错误
if (data.status) {
throw data;
}
return data;
},
/**

View File

@@ -1,38 +1,21 @@
<script lang="ts" setup>
import { useMessage } from 'naive-ui';
import type { BangumiRule } from '#/bangumi';
import { ruleTemplate } from '#/bangumi';
const { getAll } = useBangumiStore();
/** v-model show */
const show = defineModel('show', { default: false });
const rss = ref('');
const message = useMessage();
const rule = ref<BangumiRule>({
added: false,
deleted: false,
dpi: '',
eps_collect: false,
filter: [],
group_name: '',
id: 0,
official_title: '',
offset: 0,
poster_link: '',
rss_link: [],
rule_name: '',
save_path: '',
season: 1,
season_raw: '',
source: null,
subtitle: '',
title_raw: '',
year: null,
});
const { getAll } = useBangumiStore();
const rss = ref('');
const rule = ref<BangumiRule>(ruleTemplate);
const analysis = reactive({
loading: false,
next: false,
});
const loading = reactive({
collect: false,
subscribe: false,
@@ -47,27 +30,22 @@ watch(show, (val) => {
}
});
async function analyser() {
async function analysisRss() {
if (rss.value === '') {
message.error('Please enter the RSS link!');
} else {
try {
analysis.loading = true;
const { onError, onResult } = await apiDownload.analysis(rss.value);
onResult((data) => {
rule.value = data;
analysis.loading = false;
analysis.next = true;
console.log('rule', data);
});
const data = await apiDownload.analysis(rss.value);
analysis.loading = false;
onError((err) => {
message.error(err.status);
analysis.loading = false;
console.log('error', err);
});
rule.value = data;
analysis.next = true;
console.log('rule', data);
} catch (error) {
message.error('Failed to analyser!');
const err = error as { status: string };
message.error(err.status);
console.log('error', err);
}
}
}
@@ -78,6 +56,7 @@ async function collect() {
loading.collect = true;
const res = await apiDownload.collection(rule.value);
loading.collect = false;
if (res) {
message.success('Collect Success!');
getAll();
@@ -90,6 +69,7 @@ async function collect() {
}
}
}
async function subscribe() {
if (rule.value) {
try {
@@ -124,9 +104,12 @@ async function subscribe() {
></ab-setting>
<div flex="~ justify-end">
<ab-button size="small" :loading="analysis.loading" @click="analyser">{{
$t('topbar.add.analyse')
}}</ab-button>
<ab-button
size="small"
:loading="analysis.loading"
@click="analysisRss"
>{{ $t('topbar.add.analyse') }}</ab-button
>
</div>
</div>

View File

@@ -20,6 +20,7 @@ export function useApi<
const fetchResult = createEventHook<TData>();
const fetchError = createEventHook<TError>();
const fetchFinally = createEventHook();
const message = useMessage();
function execute(...params: Parameters<TApi>) {
@@ -43,6 +44,7 @@ export function useApi<
})
.finally(() => {
isLoading.value = false;
fetchFinally.trigger('');
});
}
@@ -53,5 +55,6 @@ export function useApi<
execute,
onResult: fetchResult.on,
onError: fetchError.on,
onFinally: fetchFinally.on,
};
}

View File

@@ -1,92 +1,12 @@
<script lang="ts" setup>
import type { BangumiRule } from '#/bangumi';
const { data } = storeToRefs(useBangumiStore());
const { getAll, useUpdateRule, useDisableRule, useEnableRule, useDeleteRule } =
const { bangumi, editRule } = storeToRefs(useBangumiStore());
const { getAll, updateRule, enableRule, openEditPopup, ruleManage } =
useBangumiStore();
const editRule = reactive<{
show: boolean;
item: BangumiRule;
}>({
show: false,
item: {
added: false,
deleted: false,
dpi: '',
eps_collect: false,
filter: [],
group_name: '',
id: 0,
official_title: '',
offset: 0,
poster_link: '',
rss_link: [],
rule_name: '',
save_path: '',
season: 1,
season_raw: '',
source: null,
subtitle: '',
title_raw: '',
year: null,
},
});
function open(data: BangumiRule) {
editRule.show = true;
editRule.item = data;
}
function refresh() {
editRule.show = false;
getAll();
}
const { execute: updateRule, onResult: onUpdateRuleResult } = useUpdateRule();
const { execute: enableRule, onResult: onEnableRuleResult } = useEnableRule();
const { execute: disableRule, onResult: onDisableRuleResult } =
useDisableRule();
const { execute: deleteRule, onResult: onDeleteRuleResult } = useDeleteRule();
const message = useMessage();
onUpdateRuleResult(({ msg }) => {
message.success(msg);
refresh();
});
onDisableRuleResult(({ msg }) => {
message.success(msg);
refresh();
});
onEnableRuleResult(({ msg }) => {
message.success(msg);
refresh();
});
onDeleteRuleResult(({ msg }) => {
message.success(msg);
refresh();
});
onActivated(() => {
getAll();
});
function ruleManage(
type: 'disable' | 'delete',
id: number,
deleteFile: boolean
) {
if (type === 'disable') {
disableRule(id, deleteFile);
}
if (type === 'delete') {
deleteRule(id, deleteFile);
}
}
definePage({
name: 'Bangumi List',
});
@@ -96,13 +16,13 @@ definePage({
<div overflow-auto mt-12px flex-grow>
<div flex="~ wrap" gap-y-12px gap-x-50px>
<ab-bangumi-card
v-for="i in data"
v-for="i in bangumi"
:key="i.id"
:class="[i.deleted && 'grayscale']"
:poster="i.poster_link ?? ''"
:name="i.official_title"
:season="i.season"
@click="() => open(i)"
@click="() => openEditPopup(i)"
></ab-bangumi-card>
<ab-edit-rule

View File

@@ -1,67 +1,87 @@
import type { BangumiRule } from '#/bangumi';
import { ruleTemplate } from '#/bangumi';
export const useBangumiStore = defineStore('bangumi', () => {
const data = ref<BangumiRule[]>();
const message = useMessage();
function getAll() {
const { execute, onResult } = useApi(apiBangumi.getAll);
const bangumi = ref<BangumiRule[]>();
const editRule = reactive<{
show: boolean;
item: BangumiRule;
}>({
show: false,
item: ruleTemplate,
});
const { execute: getAll, onResult: onBangumiResult } = useApi(
apiBangumi.getAll
);
const { execute: updateRule, onResult: onUpdateRuleResult } = useApi(
apiBangumi.updateRule
);
const { execute: enableRule, onResult: onEnableRuleResult } = useApi(
apiBangumi.enableRule
);
const { execute: disableRule, onResult: onDisableRuleResult } = useApi(
apiBangumi.disableRule
);
const { execute: deleteRule, onResult: onDeleteRuleResult } = useApi(
apiBangumi.deleteRule
);
onBangumiResult((res) => {
function sort(arr: BangumiRule[]) {
return arr.sort((a, b) => b.id - a.id);
}
onResult((res) => {
const enabled = sort(res.filter((e) => !e.deleted));
const disabled = sort(res.filter((e) => e.deleted));
const enabled = sort(res.filter((e) => !e.deleted));
const disabled = sort(res.filter((e) => e.deleted));
data.value = [...enabled, ...disabled];
});
bangumi.value = [...enabled, ...disabled];
});
execute();
function refresh() {
editRule.show = false;
getAll();
}
function useUpdateRule() {
const { execute, onResult } = useApi(apiBangumi.updateRule);
function actionSuccess({ msg }) {
message.success(msg);
refresh();
}
onUpdateRuleResult(actionSuccess);
onDisableRuleResult(actionSuccess);
onEnableRuleResult(actionSuccess);
onDeleteRuleResult(actionSuccess);
return {
execute,
onResult,
};
function openEditPopup(data: BangumiRule) {
editRule.show = true;
editRule.item = data;
}
function useDisableRule() {
const { execute, onResult } = useApi(apiBangumi.disableRule);
return {
execute,
onResult,
};
}
function useEnableRule() {
const { execute, onResult } = useApi(apiBangumi.enableRule);
return {
execute,
onResult,
};
}
function useDeleteRule() {
const { execute, onResult } = useApi(apiBangumi.deleteRule);
return {
execute,
onResult,
};
function ruleManage(
type: 'disable' | 'delete',
id: number,
deleteFile: boolean
) {
if (type === 'disable') {
disableRule(id, deleteFile);
}
if (type === 'delete') {
deleteRule(id, deleteFile);
}
}
return {
data,
bangumi,
editRule,
getAll,
useUpdateRule,
useDisableRule,
useEnableRule,
useDeleteRule,
updateRule,
enableRule,
disableRule,
deleteRule,
openEditPopup,
ruleManage,
};
});

View File

@@ -0,0 +1,22 @@
import { expect, it } from 'vitest';
import { omit } from './omit';
it('test omit', () => {
const obj = {
a: 1,
b: 2,
c: 3,
d: 4,
};
expect(omit(obj, ['a'])).toStrictEqual({
b: 2,
c: 3,
d: 4,
});
expect(omit(obj, ['b', 'c'])).toStrictEqual({
a: 1,
d: 4,
});
});

12
webui/src/utils/omit.ts Normal file
View File

@@ -0,0 +1,12 @@
export function omit<T extends { [k: string]: any }>(
obj: T,
omitKeys: Array<keyof T>
) {
return Object.keys(obj).reduce((acc, key) => {
if (omitKeys.includes(key)) {
return acc;
} else {
return { ...acc, [key]: obj[key] };
}
}, {});
}

View File

@@ -20,23 +20,26 @@ export interface BangumiRule {
year: string | null;
}
export interface BangumiUpdate {
added: boolean;
deleted: boolean;
dpi: string;
eps_collect: boolean;
filter: string;
group_name: string;
official_title: string;
offset: number;
poster_link: string | null;
rss_link: string;
rule_name: string;
save_path: string;
season: number;
season_raw: string;
source: string | null;
subtitle: string;
title_raw: string;
year: string | null;
}
export type BangumiUpdate = Omit<BangumiRule, 'id'>;
export const ruleTemplate: BangumiRule = {
added: false,
deleted: false,
dpi: '',
eps_collect: false,
filter: [],
group_name: '',
id: 0,
official_title: '',
offset: 0,
poster_link: '',
rss_link: [],
rule_name: '',
save_path: '',
season: 1,
season_raw: '',
source: null,
subtitle: '',
title_raw: '',
year: null,
};

View File

@@ -70,6 +70,7 @@ declare global {
const mapWritableState: typeof import('pinia')['mapWritableState']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const omit: typeof import('../../src/utils/omit')['omit']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router/auto')['onBeforeRouteLeave']