mirror of
https://github.com/CzBiX/qb-web.git
synced 2026-04-05 11:58:07 +08:00
Apply lint
This commit is contained in:
12
.eslintrc.js
12
.eslintrc.js
@@ -6,13 +6,20 @@ module.exports = {
|
||||
plugins: [
|
||||
],
|
||||
extends: [
|
||||
'plugin:vue/essential',
|
||||
'plugin:vue/strongly-recommended',
|
||||
'eslint:recommended',
|
||||
'@vue/typescript',
|
||||
'@vue/typescript/recommended',
|
||||
],
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
|
||||
'vue/singleline-html-element-content-newline': ['warn', {
|
||||
ignores: ['pre', 'textarea', 'span', 'v-icon'],
|
||||
}]
|
||||
},
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
@@ -21,6 +28,7 @@ module.exports = {
|
||||
{
|
||||
files: [
|
||||
'**/__tests__/*.{j,t}s?(x)',
|
||||
'**/tests/unit/**/*.spec.{j,t}s?(x)',
|
||||
],
|
||||
env: {
|
||||
jest: true,
|
||||
|
||||
737
package-lock.json
generated
737
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -28,6 +28,8 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.9.1",
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@typescript-eslint/eslint-plugin": "^2.25.0",
|
||||
"@typescript-eslint/parser": "^2.25.0",
|
||||
"@vue/cli-plugin-babel": "^4.2.3",
|
||||
"@vue/cli-plugin-eslint": "^4.2.3",
|
||||
"@vue/cli-plugin-router": "^4.2.3",
|
||||
@@ -35,10 +37,10 @@
|
||||
"@vue/cli-plugin-unit-jest": "^4.2.3",
|
||||
"@vue/cli-plugin-vuex": "^4.2.3",
|
||||
"@vue/cli-service": "^4.2.3",
|
||||
"@vue/eslint-config-typescript": "^4.0.0",
|
||||
"@vue/eslint-config-typescript": "^5.0.2",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"lint-staged": "^9.5.0",
|
||||
"sass": "^1.26.3",
|
||||
"sass-loader": "^8.0.2",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Axios, { AxiosInstance, AxiosResponse, AxiosError } from 'axios';
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import Axios, { AxiosInstance, AxiosResponse } from 'axios';
|
||||
import { RssNode, RssRule } from '@/types';
|
||||
|
||||
class Api {
|
||||
@@ -193,7 +194,7 @@ class Api {
|
||||
}).then(Api.handleResponse);
|
||||
}
|
||||
|
||||
public addRssFeed(url: string, path: string = '') {
|
||||
public addRssFeed(url: string, path = '') {
|
||||
const params: any = {
|
||||
url,
|
||||
path,
|
||||
|
||||
28
src/App.vue
28
src/App.vue
@@ -13,12 +13,19 @@
|
||||
<v-divider />
|
||||
<v-expansion-panels
|
||||
class="drawer-footer"
|
||||
>
|
||||
<v-expansion-panel
|
||||
lazy
|
||||
@input="drawerFooterOpen"
|
||||
>
|
||||
<v-expansion-panel lazy @input="drawerFooterOpen">
|
||||
<v-expansion-panel-header>
|
||||
<div class="d-flex align-center">
|
||||
<v-icon class="footer-icon shrink">mdi-information-outline</v-icon>
|
||||
<span class="footer-title">Status info</span>
|
||||
<v-icon class="footer-icon shrink">
|
||||
mdi-information-outline
|
||||
</v-icon>
|
||||
<span class="footer-title">
|
||||
Status info
|
||||
</span>
|
||||
</div>
|
||||
</v-expansion-panel-header>
|
||||
<v-expansion-panel-content>
|
||||
@@ -36,8 +43,14 @@
|
||||
</v-content>
|
||||
|
||||
<add-form v-if="preferences" />
|
||||
<login-form v-if="needAuth" v-model="needAuth" />
|
||||
<logs-dialog v-if="drawerOptions.showLogs" v-model="drawerOptions.showLogs" />
|
||||
<login-form
|
||||
v-if="needAuth"
|
||||
v-model="needAuth"
|
||||
/>
|
||||
<logs-dialog
|
||||
v-if="drawerOptions.showLogs"
|
||||
v-model="drawerOptions.showLogs"
|
||||
/>
|
||||
<RssDialog
|
||||
v-if="drawerOptions.showRss"
|
||||
v-model="drawerOptions.showRss"
|
||||
@@ -60,10 +73,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import {
|
||||
mapActions, mapGetters, mapState, mapMutations,
|
||||
} from 'vuex';
|
||||
import Axios, { AxiosError } from 'axios';
|
||||
import { mapGetters, mapState, mapMutations } from 'vuex';
|
||||
import GlobalDialog from './components/GlobalDialog.vue';
|
||||
import GlobalSnackBar from './components/GlobalSnackBar.vue';
|
||||
|
||||
|
||||
@@ -63,7 +63,10 @@
|
||||
</v-row>
|
||||
<v-row no-gutters>
|
||||
<template v-if="showMore">
|
||||
<v-col cols="12" sm="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
sm="6"
|
||||
>
|
||||
<v-combobox
|
||||
:label="$t('category', 1)"
|
||||
prepend-icon="mdi-folder"
|
||||
@@ -74,7 +77,10 @@
|
||||
@input="setParams('category', $event.value)"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
sm="6"
|
||||
>
|
||||
<v-checkbox
|
||||
prepend-icon="mdi-file-tree"
|
||||
:label="$t('label.create_subfolder')"
|
||||
@@ -83,14 +89,20 @@
|
||||
/>
|
||||
</v-col>
|
||||
</template>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
sm="6"
|
||||
>
|
||||
<v-checkbox
|
||||
v-model="autoStart"
|
||||
:label="$t('label.start_torrent')"
|
||||
prepend-icon="mdi-play-pause"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
sm="6"
|
||||
>
|
||||
<v-checkbox
|
||||
prepend-icon="mdi-progress-check"
|
||||
:label="$t('label.skip_hash_check')"
|
||||
@@ -99,7 +111,10 @@
|
||||
/>
|
||||
</v-col>
|
||||
<template v-if="showMore">
|
||||
<v-col cols="12" sm="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
sm="6"
|
||||
>
|
||||
<v-checkbox
|
||||
:label="$t('label.in_sequential_order')"
|
||||
prepend-icon="mdi-sort-descending"
|
||||
@@ -107,7 +122,10 @@
|
||||
@change="setParams('sequentialDownload', $event.value)"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
sm="6"
|
||||
>
|
||||
<v-checkbox
|
||||
prepend-icon="mdi-ray-start-end"
|
||||
:label="$t('label.first_and_last_pieces_first')"
|
||||
@@ -126,9 +144,18 @@
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn text @click="showMore = !showMore" v-text="showMore ? $t('less') : $t('more')" />
|
||||
<v-btn
|
||||
text
|
||||
@click="showMore = !showMore"
|
||||
v-text="showMore ? $t('less') : $t('more')"
|
||||
/>
|
||||
<v-spacer />
|
||||
<v-btn text @click="dialog = false">{{ $t('cancel') }}</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
@click="dialog = false"
|
||||
>
|
||||
{{ $t('cancel') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
@click="submit"
|
||||
@@ -149,11 +176,11 @@ import { isNil } from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import api from '../Api';
|
||||
import Component from 'vue-class-component';
|
||||
import { Watch } from 'vue-property-decorator';
|
||||
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
const defaultParams = {
|
||||
urls: null,
|
||||
category: null,
|
||||
@@ -163,6 +190,7 @@ const defaultParams = {
|
||||
sequentialDownload: false,
|
||||
firstLastPiecePrio: false,
|
||||
};
|
||||
/* eslint-enable @typescript-eslint/camelcase */
|
||||
|
||||
@Component({
|
||||
computed: {
|
||||
@@ -189,9 +217,9 @@ export default class AddForm extends Vue {
|
||||
pasteUrl!: string | null
|
||||
prefs!: any
|
||||
$refs!: {
|
||||
form: any,
|
||||
file: any,
|
||||
fileZone: HTMLElement,
|
||||
form: any;
|
||||
file: any;
|
||||
fileZone: HTMLElement;
|
||||
}
|
||||
|
||||
get params() {
|
||||
@@ -213,6 +241,7 @@ export default class AddForm extends Vue {
|
||||
|
||||
created() {
|
||||
defaultParams.paused = this.prefs.start_paused_enabled;
|
||||
/* eslint-disable-next-line @typescript-eslint/camelcase */
|
||||
defaultParams.root_path = this.prefs.create_subfolder_enabled;
|
||||
this.showMore = !this.phoneLayout;
|
||||
}
|
||||
@@ -301,7 +330,7 @@ export default class AddForm extends Vue {
|
||||
}
|
||||
|
||||
@Watch('files')
|
||||
onFilesChange(v: FileList) {
|
||||
onFilesChange() {
|
||||
this.$refs.form.validate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,11 @@
|
||||
:group="child"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item v-else :key="item.title" @click="item.click ? item.click() : null">
|
||||
<v-list-item
|
||||
v-else
|
||||
:key="item.title"
|
||||
@click="item.click ? item.click() : null"
|
||||
>
|
||||
<v-list-item-icon>
|
||||
<v-icon>{{ item.icon }}</v-icon>
|
||||
</v-list-item-icon>
|
||||
@@ -58,14 +62,14 @@
|
||||
<script lang="ts">
|
||||
import { sortBy, sumBy, defaultTo, isUndefined } from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import { mapState, mapMutations, mapGetters } from 'vuex';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import { Torrent, Category } from '@/types';
|
||||
import FilterGroup from './drawer/FilterGroup.vue';
|
||||
import api from '../Api';
|
||||
import { formatSize } from '../filters';
|
||||
import { SiteMap, StateType, AllStateTypes } from '../consts';
|
||||
import { SiteMap, StateType } from '../consts';
|
||||
import Component from 'vue-class-component';
|
||||
import { Prop, Emit } from 'vue-property-decorator';
|
||||
|
||||
@@ -113,24 +117,18 @@ const stateList = [
|
||||
];
|
||||
|
||||
interface MenuItem {
|
||||
icon: string,
|
||||
'icon-alt'?: string,
|
||||
title: string,
|
||||
model?: boolean,
|
||||
select?: string,
|
||||
click?: () => void,
|
||||
children?: MenuChildrenItem[],
|
||||
icon: string;
|
||||
'icon-alt'?: string;
|
||||
title: string;
|
||||
model?: boolean;
|
||||
select?: string;
|
||||
click?: () => void;
|
||||
children?: MenuChildrenItem[];
|
||||
}
|
||||
|
||||
interface MenuChildrenItem extends MenuItem {
|
||||
key: string | null,
|
||||
append?: string,
|
||||
}
|
||||
|
||||
interface Data {
|
||||
tr: any,
|
||||
basicItems: MenuItem[],
|
||||
endItems: MenuItem[],
|
||||
key: string | null;
|
||||
append?: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,130 +1,180 @@
|
||||
<template>
|
||||
<div
|
||||
class="footer d-flex"
|
||||
:class="topLayoutClass"
|
||||
v-if="isDataReady">
|
||||
<div
|
||||
class="d-flex shrink"
|
||||
:class="phoneLayout ? 'flex-column' : 'align-center'"
|
||||
v-if="app"
|
||||
class="footer d-flex"
|
||||
:class="topLayoutClass"
|
||||
v-if="isDataReady"
|
||||
>
|
||||
<div v-if="!phoneLayout">
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<span v-on="on">
|
||||
qBittorrent {{ app.version }}
|
||||
<div
|
||||
class="d-flex shrink"
|
||||
:class="phoneLayout ? 'flex-column' : 'align-center'"
|
||||
v-if="app"
|
||||
>
|
||||
<div v-if="!phoneLayout">
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<span v-on="on">
|
||||
qBittorrent {{ app.version }}
|
||||
</span>
|
||||
</template>
|
||||
<span>
|
||||
API version: {{ app.apiVersion }}
|
||||
</span>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
<v-divider
|
||||
vertical
|
||||
class="mx-2"
|
||||
v-if="!phoneLayout"
|
||||
/>
|
||||
<div class="icon-label">
|
||||
<v-icon>mdi-sprout</v-icon>
|
||||
{{ allTorrents.length }} [{{ totalSize | formatSize }}]
|
||||
</div>
|
||||
<v-divider
|
||||
vertical
|
||||
class="mx-2"
|
||||
v-if="!phoneLayout"
|
||||
/>
|
||||
<div class="icon-label">
|
||||
<v-icon>mdi-nas</v-icon>
|
||||
{{ info.free_space_on_disk | formatSize }}
|
||||
</div>
|
||||
<v-divider
|
||||
vertical
|
||||
class="mx-2"
|
||||
v-if="!phoneLayout"
|
||||
/>
|
||||
<div
|
||||
class="icon-label"
|
||||
v-if="!phoneLayout"
|
||||
>
|
||||
<v-icon class="icon-upload-download">
|
||||
mdi-swap-vertical-bold
|
||||
</v-icon>
|
||||
<span>
|
||||
API version: {{ app.apiVersion }}
|
||||
{{ info.alltime_dl | formatSize }}/{{ info.alltime_ul | formatSize }}
|
||||
</span>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<v-divider vertical class="mx-2" v-if="!phoneLayout"/>
|
||||
<div class="icon-label">
|
||||
<v-icon>mdi-sprout</v-icon>
|
||||
{{ allTorrents.length }} [{{ totalSize | formatSize }}]
|
||||
</div>
|
||||
<v-divider vertical class="mx-2" v-if="!phoneLayout"/>
|
||||
<div class="icon-label">
|
||||
<v-icon>mdi-nas</v-icon>
|
||||
{{ info.free_space_on_disk | formatSize }}
|
||||
</div>
|
||||
<v-divider vertical class="mx-2" v-if="!phoneLayout"/>
|
||||
<div class="icon-label" v-if="!phoneLayout">
|
||||
<v-icon class="icon-upload-download">mdi-swap-vertical-bold</v-icon>
|
||||
<span>
|
||||
{{ info.alltime_dl | formatSize }}/{{ info.alltime_ul | formatSize }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex shrink"
|
||||
:class="phoneLayout ? 'flex-column' : 'align-center'"
|
||||
v-if="info"
|
||||
>
|
||||
<div v-if="!phoneLayout" class="icon-label">
|
||||
<v-icon>mdi-access-point-network</v-icon>
|
||||
{{ info.dht_nodes }} nodes
|
||||
</div>
|
||||
<v-divider vertical class="mx-2" v-if="!phoneLayout"/>
|
||||
<div class="icon-label">
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon
|
||||
v-on="on"
|
||||
:color="info.connection_status | connectionIconColor"
|
||||
>mdi-{{ info.connection_status | connectionIcon }}</v-icon>
|
||||
<span v-if="phoneLayout">
|
||||
<div
|
||||
class="d-flex shrink"
|
||||
:class="phoneLayout ? 'flex-column' : 'align-center'"
|
||||
v-if="info"
|
||||
>
|
||||
<div
|
||||
v-if="!phoneLayout"
|
||||
class="icon-label"
|
||||
>
|
||||
<v-icon>mdi-access-point-network</v-icon>
|
||||
{{ info.dht_nodes }} nodes
|
||||
</div>
|
||||
<v-divider
|
||||
vertical
|
||||
class="mx-2"
|
||||
v-if="!phoneLayout"
|
||||
/>
|
||||
<div class="icon-label">
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon
|
||||
v-on="on"
|
||||
:color="info.connection_status | connectionIconColor"
|
||||
>
|
||||
mdi-{{ info.connection_status | connectionIcon }}
|
||||
</v-icon>
|
||||
<span v-if="phoneLayout">
|
||||
Network {{ info.connection_status }}
|
||||
</span>
|
||||
</template>
|
||||
<span>
|
||||
Network {{ info.connection_status }}
|
||||
</span>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
<v-divider
|
||||
vertical
|
||||
class="mx-2"
|
||||
v-if="!phoneLayout"
|
||||
/>
|
||||
<div class="icon-label">
|
||||
<v-switch
|
||||
v-if="phoneLayout"
|
||||
hide-details
|
||||
:value="speedLimited"
|
||||
@change="toggleSpeedLimitsMode"
|
||||
label="Alternative speed limits"
|
||||
class="mt-0 pt-0 speed-switch"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon
|
||||
v-bind="speedModeBind"
|
||||
>
|
||||
mdi-speedometer
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-tooltip
|
||||
top
|
||||
v-else
|
||||
>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon
|
||||
v-on="on"
|
||||
v-bind="speedModeBind"
|
||||
@click="toggleSpeedLimitsMode"
|
||||
>
|
||||
mdi-speedometer
|
||||
</v-icon>
|
||||
</template>
|
||||
<span>
|
||||
Alternative speed limits {{ speedLimited ? 'enabled' : 'disabled' }}
|
||||
</span>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
<v-divider
|
||||
vertical
|
||||
class="mx-2"
|
||||
v-if="!phoneLayout"
|
||||
/>
|
||||
<div class="icon-label">
|
||||
<v-icon
|
||||
:color=" info.dl_info_speed > 0 ? 'success' : null"
|
||||
>
|
||||
mdi-download
|
||||
</v-icon>
|
||||
<span>
|
||||
Network {{ info.connection_status }}
|
||||
{{ info.dl_info_speed | formatSize }}/s
|
||||
<template v-if="info.dl_rate_limit">
|
||||
({{ info.dl_rate_limit | formatSize }}/s)
|
||||
</template>
|
||||
<template v-if="!phoneLayout">
|
||||
[{{ info.dl_info_data | formatSize }}]
|
||||
</template>
|
||||
</span>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
<v-divider vertical class="mx-2" v-if="!phoneLayout"/>
|
||||
<div class="icon-label">
|
||||
<v-switch
|
||||
v-if="phoneLayout"
|
||||
hide-details
|
||||
:value="speedLimited"
|
||||
@change="toggleSpeedLimitsMode"
|
||||
label="Alternative speed limits"
|
||||
class="mt-0 pt-0 speed-switch"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon
|
||||
v-bind="speedModeBind"
|
||||
>mdi-speedometer</v-icon>
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-tooltip top v-else>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon
|
||||
v-on="on"
|
||||
v-bind="speedModeBind"
|
||||
@click="toggleSpeedLimitsMode"
|
||||
>mdi-speedometer</v-icon>
|
||||
</template>
|
||||
</div>
|
||||
<v-divider
|
||||
vertical
|
||||
class="mx-2"
|
||||
v-if="!phoneLayout"
|
||||
/>
|
||||
<div class="icon-label">
|
||||
<v-icon
|
||||
:color=" info.up_info_speed > 0 ? 'warning' : null"
|
||||
>
|
||||
mdi-upload
|
||||
</v-icon>
|
||||
<span>
|
||||
Alternative speed limits {{ speedLimited ? 'enabled' : 'disabled' }}
|
||||
{{ info.up_info_speed | formatSize }}/s
|
||||
<template v-if="info.up_rate_limit">
|
||||
({{ info.up_rate_limit | formatSize }}/s)
|
||||
</template>
|
||||
<template v-if="!phoneLayout">
|
||||
[{{ info.up_info_data | formatSize }}]
|
||||
</template>
|
||||
</span>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
<v-divider vertical class="mx-2" v-if="!phoneLayout"/>
|
||||
<div class="icon-label">
|
||||
<v-icon
|
||||
:color=" info.dl_info_speed > 0 ? 'success' : null"
|
||||
>mdi-download</v-icon>
|
||||
<span>
|
||||
{{ info.dl_info_speed | formatSize }}/s
|
||||
<template v-if="info.dl_rate_limit">
|
||||
({{ info.dl_rate_limit | formatSize}}/s)
|
||||
</template>
|
||||
<template v-if="!phoneLayout">
|
||||
[{{ info.dl_info_data | formatSize }}]
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
<v-divider vertical class="mx-2" v-if="!phoneLayout"/>
|
||||
<div class="icon-label">
|
||||
<v-icon
|
||||
:color=" info.up_info_speed > 0 ? 'warning' : null"
|
||||
>mdi-upload</v-icon>
|
||||
<span>
|
||||
{{ info.up_info_speed | formatSize }}/s
|
||||
<template v-if="info.up_rate_limit">
|
||||
({{ info.up_rate_limit | formatSize}}/s)
|
||||
</template>
|
||||
<template v-if="!phoneLayout">
|
||||
[{{ info.up_info_data | formatSize }}]
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
@@ -35,15 +35,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import {
|
||||
computed, ref, watch, Ref,
|
||||
} from '@vue/composition-api';
|
||||
import { computed, ref, watch } from '@vue/composition-api';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import { DialogType, DialogConfig } from '@/store/types';
|
||||
import { useMutations, useState } from '@/store';
|
||||
import { timeout } from '@/utils';
|
||||
|
||||
const BUTTONS = {
|
||||
[DialogType.Alert]: [
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
<script>
|
||||
import { useMutations, useState } from '@/store';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import { timeout } from '@/utils';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const mutations = useMutations(['closeSnackBar']);
|
||||
@@ -48,7 +45,6 @@ export default {
|
||||
}
|
||||
|
||||
return {
|
||||
tr,
|
||||
config,
|
||||
changed,
|
||||
clickBtn,
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
<template>
|
||||
<v-dialog v-model="value" persistent width="25em">
|
||||
<v-dialog
|
||||
v-model="value"
|
||||
persistent
|
||||
width="25em"
|
||||
>
|
||||
<v-card>
|
||||
<v-toolbar dark color="primary">
|
||||
<v-toolbar
|
||||
dark
|
||||
color="primary"
|
||||
>
|
||||
<v-toolbar-title>{{ $t('login') }}</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<v-card-text>
|
||||
@@ -9,9 +16,11 @@
|
||||
ref="form"
|
||||
v-model="valid"
|
||||
>
|
||||
<div class="pa-0"
|
||||
@keyup.enter.capture="submit"
|
||||
v-bind="{ [`grid-list-${$vuetify.breakpoint.name}`]: true }">
|
||||
<div
|
||||
class="pa-0"
|
||||
@keyup.enter.capture="submit"
|
||||
v-bind="{ [`grid-list-${$vuetify.breakpoint.name}`]: true }"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="params.username"
|
||||
prepend-icon="mdi-account"
|
||||
|
||||
@@ -151,7 +151,9 @@
|
||||
<v-icon :color="row.item.state | stateColor">
|
||||
{{ row.item.state | stateIcon }}
|
||||
</v-icon>
|
||||
<span class="torrent-title">{{ row.item.name }}</span>
|
||||
<span class="torrent-title">
|
||||
{{ row.item.name }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ row.item.size | formatSize }}</td>
|
||||
<td>
|
||||
@@ -215,9 +217,7 @@ import ConfirmSetCategoryDialog from './dialogs/ConfirmSetCategoryDialog.vue'
|
||||
import EditTrackerDialog from './dialogs/EditTrackerDialog.vue'
|
||||
import InfoDialog from './dialogs/InfoDialog.vue'
|
||||
import api from '../Api'
|
||||
import { formatSize, formatDuration } from '../filters'
|
||||
import { torrentIsState } from '../utils'
|
||||
import { StateType } from '../consts'
|
||||
import { formatSize } from '../filters'
|
||||
import { DialogType, TorrentFilter, ConfigPayload, DialogConfig, SnackBarConfig } from '../store/types'
|
||||
import Component from 'vue-class-component'
|
||||
import { Torrent, Category } from '../types'
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<v-dialog :value="true" @input="closeDialog" :fullscreen="phoneLayout" width="40em">
|
||||
<v-dialog
|
||||
:value="true"
|
||||
@input="closeDialog"
|
||||
:fullscreen="phoneLayout"
|
||||
width="40em"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title
|
||||
class="headline grey lighten-4"
|
||||
@@ -10,7 +15,10 @@
|
||||
<v-card-text class="pb-0">
|
||||
{{ $t('dialog.delete_torrents.msg') }}
|
||||
<ol class="torrents pt-6">
|
||||
<li v-for="(row, i) in torrents" :key="i">
|
||||
<li
|
||||
v-for="(row, i) in torrents"
|
||||
:key="i"
|
||||
>
|
||||
{{ row.name }}
|
||||
</li>
|
||||
</ol>
|
||||
@@ -30,7 +38,12 @@
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn text @click="closeDialog">{{ $t('cancel') }}</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
@click="closeDialog"
|
||||
>
|
||||
{{ $t('cancel') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
@click="submit"
|
||||
color="warning"
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<v-dialog :value="true" @input="closeDialog" :fullscreen="phoneLayout" width="40em">
|
||||
<v-dialog
|
||||
:value="true"
|
||||
@input="closeDialog"
|
||||
:fullscreen="phoneLayout"
|
||||
width="40em"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title
|
||||
class="headline grey lighten-4"
|
||||
@@ -15,7 +20,10 @@
|
||||
{{ $t('dialog.set_category.reset') }}
|
||||
</template>
|
||||
<ol class="torrents pt-6">
|
||||
<li v-for="(row, i) in torrents" :key="i">
|
||||
<li
|
||||
v-for="(row, i) in torrents"
|
||||
:key="i"
|
||||
>
|
||||
{{ row.name }}
|
||||
</li>
|
||||
</ol>
|
||||
@@ -25,12 +33,17 @@
|
||||
v-model="moveSameNamed"
|
||||
prepend-icon="mdi-file-multiple"
|
||||
class="mt-0"
|
||||
:label="$t('dialog.set_category.also.move_same_name_torrents', sameNamedTorrents.length)"
|
||||
:label="$t('dialog.set_category.also_move_same_name_torrents', sameNamedTorrents.length)"
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn text @click="closeDialog">{{ $t('cancel') }}</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
@click="closeDialog"
|
||||
>
|
||||
{{ $t('cancel') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
@click="submit"
|
||||
color="warning"
|
||||
|
||||
@@ -16,11 +16,26 @@
|
||||
<v-card-text class="pa-0">
|
||||
<v-stepper v-model="step">
|
||||
<v-stepper-header>
|
||||
<v-stepper-step :complete="step > 1" step="1">Search</v-stepper-step>
|
||||
<v-stepper-step
|
||||
:complete="step > 1"
|
||||
step="1"
|
||||
>
|
||||
Search
|
||||
</v-stepper-step>
|
||||
<v-divider />
|
||||
<v-stepper-step :complete="step > 2" step="2">Preview</v-stepper-step>
|
||||
<v-stepper-step
|
||||
:complete="step > 2"
|
||||
step="2"
|
||||
>
|
||||
Preview
|
||||
</v-stepper-step>
|
||||
<v-divider />
|
||||
<v-stepper-step :complete="step > 3" step="3">Result</v-stepper-step>
|
||||
<v-stepper-step
|
||||
:complete="step > 3"
|
||||
step="3"
|
||||
>
|
||||
Result
|
||||
</v-stepper-step>
|
||||
</v-stepper-header>
|
||||
<v-stepper-items>
|
||||
<v-stepper-content step="1">
|
||||
@@ -41,7 +56,10 @@
|
||||
<v-stepper-content step="2">
|
||||
{{ toEdit.length }} torrent(s) to update.
|
||||
<ol class="torrents pt-6">
|
||||
<li v-for="(row, i) in toEdit" :key="i">
|
||||
<li
|
||||
v-for="(row, i) in toEdit"
|
||||
:key="i"
|
||||
>
|
||||
{{ row.name }}
|
||||
<br>
|
||||
{{ row.origUrl }}
|
||||
@@ -69,7 +87,9 @@
|
||||
@click="back"
|
||||
v-if="step < 3"
|
||||
v-text="step == 1 ? 'Cancel' : 'Back'"
|
||||
>Back</v-btn>
|
||||
>
|
||||
Back
|
||||
</v-btn>
|
||||
<v-btn
|
||||
@click="foward"
|
||||
color="warning"
|
||||
|
||||
@@ -108,7 +108,7 @@ import Trackers from './Trackers.vue';
|
||||
import Peers from './Peers.vue';
|
||||
import Panel from './Panel.vue';
|
||||
import Component from 'vue-class-component';
|
||||
import { Prop, Emit, Watch, PropSync } from 'vue-property-decorator';
|
||||
import { Prop, Emit, PropSync } from 'vue-property-decorator';
|
||||
import { Torrent } from '../../types';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -20,7 +20,11 @@
|
||||
v-if="!logs.length"
|
||||
/>
|
||||
<ol class="logs caption">
|
||||
<li v-for="(row, i) in logs" :key="i" :class="row.type | typeColor">
|
||||
<li
|
||||
v-for="(row, i) in logs"
|
||||
:key="i"
|
||||
:class="row.type | typeColor"
|
||||
>
|
||||
[{{ row.type | formatType }} {{ row.timestamp / 1000 | formatTimestamp }}]
|
||||
<span v-html="row.message" />
|
||||
</li>
|
||||
@@ -29,14 +33,18 @@
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn text @click="closeDialog">Close</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
@click="closeDialog"
|
||||
>
|
||||
Close
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import api from '@/Api';
|
||||
import Component from 'vue-class-component';
|
||||
import HasTask from '../../mixins/hasTask';
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
<template>
|
||||
<fieldset class="panel" v-if="!single">
|
||||
<legend v-text="title" />
|
||||
<div class="inner">
|
||||
<fieldset
|
||||
class="panel"
|
||||
v-if="!single"
|
||||
>
|
||||
<legend v-text="title" />
|
||||
<div class="inner">
|
||||
<slot />
|
||||
</div>
|
||||
</fieldset>
|
||||
<div
|
||||
v-else
|
||||
class="inner"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="inner" v-else>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -16,7 +22,10 @@ import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
single: Boolean,
|
||||
title: String,
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -15,16 +15,20 @@
|
||||
:title="row.item.country"
|
||||
:alt="codeToFlag(row.item.country_code).char"
|
||||
:src="codeToFlag(row.item.country_code).url"
|
||||
/>
|
||||
>
|
||||
<template v-else>
|
||||
{{ codeToFlag(row.item.country_code).char }}
|
||||
</template>
|
||||
</template>
|
||||
{{ row.item.ip }}
|
||||
<span class="grey--text">:{{ row.item.port }}</span>
|
||||
<span class="grey--text">
|
||||
:{{ row.item.port }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ row.item.connection }}</td>
|
||||
<td :title="row.item.flags_desc">{{ row.item.flags }}</td>
|
||||
<td :title="row.item.flags_desc">
|
||||
{{ row.item.flags }}
|
||||
</td>
|
||||
<td>{{ row.item.client }}</td>
|
||||
<td>{{ row.item.progress | progress }}</td>
|
||||
<td>{{ row.item.dl_speed | networkSpeed }}</td>
|
||||
@@ -40,7 +44,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { map, merge, cloneDeep } from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import { codeToFlag, isWindows } from '../../utils';
|
||||
import api from '../../Api';
|
||||
import { formatSize } from '../../filters';
|
||||
|
||||
@@ -12,16 +12,28 @@
|
||||
<v-icon class="mr-2">mdi-rss-box</v-icon>
|
||||
<span>RSS</span>
|
||||
<v-spacer />
|
||||
<v-btn icon @click="closeDialog">
|
||||
<v-btn
|
||||
icon
|
||||
@click="closeDialog"
|
||||
>
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="toolbar">
|
||||
<v-btn icon @click="addRssItem" :title="$t('dialog.rss.add_feed')">
|
||||
<v-btn
|
||||
icon
|
||||
@click="addRssItem"
|
||||
:title="$t('dialog.rss.add_feed')"
|
||||
>
|
||||
<v-icon>mdi-link-plus</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon :disabled="!selectNode" @click="deleteRssItem" :title="$t('delete')">
|
||||
<v-btn
|
||||
icon
|
||||
:disabled="!selectNode"
|
||||
@click="deleteRssItem"
|
||||
:title="$t('delete')"
|
||||
>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
<v-spacer />
|
||||
@@ -38,16 +50,22 @@
|
||||
:label="$t('dialog.rss.auto_download')"
|
||||
hide-details
|
||||
/>
|
||||
<v-btn icon @click="showRulesDialog = true" :title="$t('settings')">
|
||||
<v-btn
|
||||
icon
|
||||
@click="showRulesDialog = true"
|
||||
:title="$t('settings')"
|
||||
>
|
||||
<v-icon>mdi-settings</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-divider />
|
||||
<div class="content">
|
||||
<div class="content-inner">
|
||||
<div v-if="!rssNode" class="loading">
|
||||
<v-progress-circular indeterminate>
|
||||
</v-progress-circular>
|
||||
<div
|
||||
v-if="!rssNode"
|
||||
class="loading"
|
||||
>
|
||||
<v-progress-circular indeterminate />
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="rss-items">
|
||||
@@ -67,7 +85,10 @@
|
||||
size="22"
|
||||
width="2"
|
||||
/>
|
||||
<v-icon v-else v-text="getRowIcon(row)" />
|
||||
<v-icon
|
||||
v-else
|
||||
v-text="getRowIcon(row)"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:label="row">
|
||||
{{ row.item.name }}
|
||||
@@ -82,17 +103,24 @@
|
||||
<div class="rss-info">
|
||||
<p>
|
||||
{{ $t('title._') }}:
|
||||
<a v-if="selectItem" target="_blank" :href="selectItem.url">{{ selectItem.title }}</a>
|
||||
<a
|
||||
v-if="selectItem"
|
||||
target="_blank"
|
||||
:href="selectItem.url"
|
||||
>{{ selectItem.title }}</a>
|
||||
</p>
|
||||
<p>{{ $t('date') }}: {{ (selectItem ? selectItem.lastBuildDate : null) | date }}</p>
|
||||
</div>
|
||||
<v-divider/>
|
||||
<v-divider />
|
||||
<div class="list-wrapper">
|
||||
<v-list
|
||||
v-if="selectItem"
|
||||
dense
|
||||
>
|
||||
<v-list-item-group v-model="selectArticle" color="primary" >
|
||||
<v-list-item-group
|
||||
v-model="selectArticle"
|
||||
color="primary"
|
||||
>
|
||||
<v-list-item
|
||||
v-for="article in selectItem.articles"
|
||||
:key="article.id"
|
||||
@@ -100,11 +128,17 @@
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
<span :title="article.title" v-text="article.title"/>
|
||||
<span
|
||||
:title="article.title"
|
||||
v-text="article.title"
|
||||
/>
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<v-btn icon @click.stop="downloadTorrent(article)">
|
||||
<v-btn
|
||||
icon
|
||||
@click.stop="downloadTorrent(article)"
|
||||
>
|
||||
<v-icon>mdi-download</v-icon>
|
||||
</v-btn>
|
||||
</v-list-item-action>
|
||||
@@ -118,13 +152,22 @@
|
||||
<div class="rss-info">
|
||||
<p>
|
||||
{{ $t('title._') }}:
|
||||
<a v-if="selectArticle" target="_blank" :href="selectArticle.link">{{ selectArticle.title }}</a>
|
||||
<a
|
||||
v-if="selectArticle"
|
||||
target="_blank"
|
||||
:href="selectArticle.link"
|
||||
>{{ selectArticle.title }}</a>
|
||||
</p>
|
||||
<p>{{ `${$t('category', 1)}: ${selectArticle ? selectArticle.category: ''}` }}</p>
|
||||
<p>{{ $t('date') }}: {{ (selectArticle ? selectArticle.date: null) | date }}</p>
|
||||
</div>
|
||||
<v-divider/>
|
||||
<iframe class="iframe" sandbox="allow-same-origin" v-if="selectArticle" v-body="selectArticle.description" />
|
||||
<v-divider />
|
||||
<iframe
|
||||
class="iframe"
|
||||
sandbox="allow-same-origin"
|
||||
v-if="selectArticle"
|
||||
v-body="selectArticle.description"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -132,13 +175,16 @@
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<RssRulesDialog v-if="showRulesDialog" :rss-node="rssNode" v-model="showRulesDialog"/>
|
||||
<RssRulesDialog
|
||||
v-if="showRulesDialog"
|
||||
:rss-node="rssNode"
|
||||
v-model="showRulesDialog"
|
||||
/>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { get } from 'lodash'
|
||||
import Vue from 'vue'
|
||||
import { mapActions, mapMutations, mapState } from 'vuex'
|
||||
import Component from 'vue-class-component'
|
||||
import { Prop, Watch, Emit } from 'vue-property-decorator'
|
||||
|
||||
@@ -12,31 +12,48 @@
|
||||
<v-icon class="mr-2">mdi-filter</v-icon>
|
||||
<span v-text="$t('dialog.rss_rule.title')" />
|
||||
<v-spacer />
|
||||
<v-btn icon @click="closeDialog">
|
||||
<v-btn
|
||||
icon
|
||||
@click="closeDialog"
|
||||
>
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="toolbar">
|
||||
<v-btn icon @click="addRssRule" :title="$t('dialog.rss_rule.add_rule')">
|
||||
<v-btn
|
||||
icon
|
||||
@click="addRssRule"
|
||||
:title="$t('dialog.rss_rule.add_rule')"
|
||||
>
|
||||
<v-icon>mdi-plus</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon :disabled="!selectedRuleName" @click="deleteRssRule" :title="$t('delete')">
|
||||
<v-btn
|
||||
icon
|
||||
:disabled="!selectedRuleName"
|
||||
@click="deleteRssRule"
|
||||
:title="$t('delete')"
|
||||
>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-divider />
|
||||
<div class="content">
|
||||
<div v-if="!rssRules" class="loading">
|
||||
<v-progress-circular indeterminate>
|
||||
</v-progress-circular>
|
||||
<div
|
||||
v-if="!rssRules"
|
||||
class="loading"
|
||||
>
|
||||
<v-progress-circular indeterminate />
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="rss-rules">
|
||||
<v-list
|
||||
dense
|
||||
>
|
||||
<v-list-item-group v-model="selectedRuleName" color="primary" >
|
||||
<v-list-item-group
|
||||
v-model="selectedRuleName"
|
||||
color="primary"
|
||||
>
|
||||
<v-list-item
|
||||
v-for="(value, key) in rssRules"
|
||||
:key="key"
|
||||
@@ -58,35 +75,50 @@
|
||||
<v-divider vertical />
|
||||
<div class="rule-details">
|
||||
<v-form class="rule-form">
|
||||
<p class="form-title" v-text="$t('dialog.rss_rule.rule_settings')" />
|
||||
<p
|
||||
class="form-title"
|
||||
v-text="$t('dialog.rss_rule.rule_settings')"
|
||||
/>
|
||||
|
||||
<v-checkbox dense :label="$t('dialog.rss_rule.use_regex')"
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="$t('dialog.rss_rule.use_regex')"
|
||||
:disabled="!selectedRule.enabled"
|
||||
:value="selectedRule.useRegex"
|
||||
@change="editRule('useRegex', $event)"
|
||||
/>
|
||||
<v-text-field dense :label="$t('dialog.rss_rule.must_contain')"
|
||||
<v-text-field
|
||||
dense
|
||||
:label="$t('dialog.rss_rule.must_contain')"
|
||||
:disabled="!selectedRule.enabled"
|
||||
:value="selectedRule.mustContain"
|
||||
@change="editRule('mustContain', $event)"
|
||||
/>
|
||||
<v-text-field dense :label="$t('dialog.rss_rule.must_not_contain')"
|
||||
<v-text-field
|
||||
dense
|
||||
:label="$t('dialog.rss_rule.must_not_contain')"
|
||||
:disabled="!selectedRule.enabled"
|
||||
:value="selectedRule.mustNotContain"
|
||||
@change="editRule('mustNotContain', $event)"
|
||||
/>
|
||||
<v-text-field dense :label="$t('dialog.rss_rule.episode_filter')"
|
||||
<v-text-field
|
||||
dense
|
||||
:label="$t('dialog.rss_rule.episode_filter')"
|
||||
:disabled="!selectedRule.enabled"
|
||||
:value="selectedRule.episodeFilter"
|
||||
@change="editRule('episodeFilter', $event)"
|
||||
/>
|
||||
<v-checkbox dense :label="$t('dialog.rss_rule.smart_episode')"
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="$t('dialog.rss_rule.smart_episode')"
|
||||
:disabled="!selectedRule.enabled"
|
||||
:value="selectedRule.smartFilter"
|
||||
@change="editRule('smartFilter', $event)"
|
||||
/>
|
||||
|
||||
<v-select dense :label="$t('dialog.rss_rule.assign_category')"
|
||||
<v-select
|
||||
dense
|
||||
:label="$t('dialog.rss_rule.assign_category')"
|
||||
:items="categoryItems"
|
||||
:disabled="!selectedRule.enabled"
|
||||
:value="selectedRule.assignedCategory"
|
||||
@@ -94,9 +126,12 @@
|
||||
/>
|
||||
</v-form>
|
||||
|
||||
<v-divider/>
|
||||
<v-divider />
|
||||
|
||||
<p class="feeds-title" v-text="$t('dialog.rss_rule.apply_to_feeds')"/>
|
||||
<p
|
||||
class="feeds-title"
|
||||
v-text="$t('dialog.rss_rule.apply_to_feeds')"
|
||||
/>
|
||||
<v-list
|
||||
dense
|
||||
v-if="selectedRule.enabled"
|
||||
@@ -132,7 +167,7 @@ import Component from 'vue-class-component';
|
||||
|
||||
import { tr } from '@/locale'
|
||||
import { Prop, Emit, Watch } from 'vue-property-decorator';
|
||||
import { RssRule, Category, RssNode, RssItem } from '../../types';
|
||||
import { RssRule, Category, RssNode } from '../../types';
|
||||
import api from '../../Api';
|
||||
import { mapActions, mapMutations, mapGetters } from 'vuex';
|
||||
import { DialogConfig, DialogType, SnackBarConfig } from '../../store/types';
|
||||
|
||||
@@ -21,11 +21,10 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { groupBy } from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import api from '../../Api';
|
||||
import BaseTorrentInfo from './baseTorrentInfo'
|
||||
import Component from 'vue-class-component';
|
||||
import { Prop, Watch } from 'vue-property-decorator';
|
||||
import { Prop } from 'vue-property-decorator';
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
interface File {
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
<script lang="ts">
|
||||
import { chunk, countBy } from 'lodash';
|
||||
|
||||
import Vue from 'vue';
|
||||
import api from '../../Api';
|
||||
import {
|
||||
formatDuration, formatSize, formatTimestamp, toPrecision,
|
||||
@@ -135,7 +134,7 @@ export default class TorrentInfo extends BaseTorrentInfo {
|
||||
{ label: 'Created on', value: prop => formatTimestamp(prop.creation_date) },
|
||||
{ label: 'Added on', value: prop => formatTimestamp(prop.addition_date) },
|
||||
{ label: 'Completed on', value: prop => formatTimestamp(prop.completion_date) },
|
||||
{ label: 'Torrent hash', value: prop => this.torrent.hash },
|
||||
{ label: 'Torrent hash', value: () => this.torrent.hash },
|
||||
{ label: 'Save path', value: prop => prop.save_path },
|
||||
{ label: 'Comment', value: prop => prop.comment },
|
||||
]
|
||||
|
||||
@@ -21,10 +21,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import api from '../../Api';
|
||||
import Component from 'vue-class-component';
|
||||
import { Prop, Watch } from 'vue-property-decorator';
|
||||
import { Prop } from 'vue-property-decorator';
|
||||
import BaseTorrentInfo from './baseTorrentInfo';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -18,9 +18,15 @@
|
||||
@click.stop="select(child.key)"
|
||||
>
|
||||
<v-list-item-icon>
|
||||
<v-icon v-if="isFontIcon(child.icon)">{{ child.icon }}</v-icon>
|
||||
<v-icon v-if="isFontIcon(child.icon)">
|
||||
{{ child.icon }}
|
||||
</v-icon>
|
||||
<div v-else>
|
||||
<v-img :src="child.icon" width='20px' height="20px" />
|
||||
<v-img
|
||||
:src="child.icon"
|
||||
width="20px"
|
||||
height="20px"
|
||||
/>
|
||||
</div>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
@@ -42,25 +48,19 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue, { PropType } from 'vue';
|
||||
import { mapState, mapMutations } from 'vuex';
|
||||
import { FilterGroup as types } from '../types';
|
||||
import Vue from 'vue';
|
||||
import Component from 'vue-class-component';
|
||||
import { Prop } from 'vue-property-decorator';
|
||||
import { Group } from '../types'
|
||||
|
||||
interface Data {
|
||||
model: boolean;
|
||||
selected: string | null;
|
||||
}
|
||||
@Component
|
||||
export default class FilterGroup extends Vue {
|
||||
@Prop()
|
||||
readonly group!: Group
|
||||
|
||||
model = false
|
||||
selected: string | null = null
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
group: Object as PropType<types.Group>,
|
||||
},
|
||||
data(): Data {
|
||||
return {
|
||||
model: this.group.model,
|
||||
selected: null,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
const s = this.$store.getters.config.filter[this.group.select];
|
||||
if (this.group.children.some(child => child.key === s)) {
|
||||
@@ -68,22 +68,22 @@ export default Vue.extend({
|
||||
} else {
|
||||
this.select(null);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
select(key: string | null) {
|
||||
this.selected = this.selected === key ? null : key;
|
||||
this.$store.commit('updateConfig', {
|
||||
key: 'filter',
|
||||
value: {
|
||||
[this.group.select]: this.selected,
|
||||
},
|
||||
});
|
||||
},
|
||||
isFontIcon(icon: string) {
|
||||
return icon.startsWith('mdi-');
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
select(key: string | null) {
|
||||
this.selected = this.selected === key ? null : key;
|
||||
this.$store.commit('updateConfig', {
|
||||
key: 'filter',
|
||||
value: {
|
||||
[this.group.select]: this.selected,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
isFontIcon(icon: string) {
|
||||
return icon.startsWith('mdi-');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
export declare module FilterGroup {
|
||||
export interface Group {
|
||||
title: string;
|
||||
icon: string;
|
||||
children: Child[];
|
||||
model: boolean;
|
||||
select: string;
|
||||
}
|
||||
|
||||
export interface Child {
|
||||
title: string;
|
||||
key: string | null;
|
||||
icon: string;
|
||||
append: string | null;
|
||||
}
|
||||
export interface Group {
|
||||
title: string;
|
||||
icon: string;
|
||||
children: Child[];
|
||||
model: boolean;
|
||||
select: string;
|
||||
}
|
||||
|
||||
export interface Child {
|
||||
title: string;
|
||||
key: string | null;
|
||||
icon: string;
|
||||
append: string | null;
|
||||
}
|
||||
@@ -6,7 +6,7 @@ export default class HasTask extends Vue {
|
||||
destroy?: boolean
|
||||
call?: CallableFunction
|
||||
taskId?: number
|
||||
interval: number = 2000
|
||||
interval = 2000
|
||||
|
||||
setTaskAndRun(call: CallableFunction, interval?: number) {
|
||||
this.call = call
|
||||
|
||||
@@ -7,6 +7,7 @@ Vue.use(Vuetify);
|
||||
let locale = i18n.locale();
|
||||
locale = locale === 'zh-CN' ? 'zh-Hans' : locale.split('-', 1)[0];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { default: translation } = require('vuetify/src/locale/' + locale);
|
||||
|
||||
export default new Vuetify({
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
function getSiteIcon(name: String) {
|
||||
// eslint-disable-next-line
|
||||
function getSiteIcon(name: string) {
|
||||
return require(`@/assets/site_icons/${name}.png`);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export function loadConfig() {
|
||||
return JSON.parse(tmp);
|
||||
}
|
||||
|
||||
export const configStore : Module<ConfigState, any> = {
|
||||
export const configStore: Module<ConfigState, any> = {
|
||||
state() {
|
||||
return {
|
||||
userConfig: loadConfig(),
|
||||
|
||||
@@ -2,7 +2,7 @@ import { merge, isString, cloneDeep } from 'lodash'
|
||||
import { Module } from 'vuex';
|
||||
import { DialogState } from './types';
|
||||
|
||||
export const dialogStore : Module<DialogState, any> = {
|
||||
export const dialogStore: Module<DialogState, any> = {
|
||||
state() {
|
||||
return {
|
||||
config: null,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { cloneDeep, isString } from 'lodash';
|
||||
import { Module } from 'vuex';
|
||||
import { SnackBarState } from './types';
|
||||
|
||||
export const snackBarStore : Module<SnackBarState, any> = {
|
||||
export const snackBarStore: Module<SnackBarState, any> = {
|
||||
state() {
|
||||
return {
|
||||
config: null,
|
||||
|
||||
@@ -8,9 +8,9 @@ export interface RootState {
|
||||
}
|
||||
|
||||
export interface TorrentFilter {
|
||||
state: string
|
||||
category: string
|
||||
site: string
|
||||
state: string;
|
||||
category: string;
|
||||
site: string;
|
||||
}
|
||||
|
||||
export interface ConfigState {
|
||||
@@ -18,8 +18,8 @@ export interface ConfigState {
|
||||
}
|
||||
|
||||
export interface ConfigPayload {
|
||||
key: string,
|
||||
value: any,
|
||||
key: string;
|
||||
value: any;
|
||||
}
|
||||
|
||||
export enum DialogType {
|
||||
@@ -32,28 +32,28 @@ export enum DialogType {
|
||||
|
||||
export interface DialogConfig {
|
||||
content: {
|
||||
title?: String,
|
||||
text: String,
|
||||
callback?: CallableFunction,
|
||||
type?: DialogType,
|
||||
buttons?: any,
|
||||
title?: string;
|
||||
text: string;
|
||||
callback?: CallableFunction;
|
||||
type?: DialogType;
|
||||
buttons?: any;
|
||||
|
||||
rules?: CallableFunction[],
|
||||
placeholder?: string,
|
||||
},
|
||||
width?: string,
|
||||
rules?: CallableFunction[];
|
||||
placeholder?: string;
|
||||
};
|
||||
width?: string;
|
||||
}
|
||||
|
||||
export interface DialogState {
|
||||
config: DialogConfig | null,
|
||||
config: DialogConfig | null;
|
||||
}
|
||||
|
||||
export interface SnackBarConfig {
|
||||
text: string,
|
||||
btnText?: string,
|
||||
callback?: CallableFunction,
|
||||
text: string;
|
||||
btnText?: string;
|
||||
callback?: CallableFunction;
|
||||
}
|
||||
|
||||
export interface SnackBarState {
|
||||
config: SnackBarConfig | null,
|
||||
config: SnackBarConfig | null;
|
||||
}
|
||||
|
||||
66
src/types.ts
66
src/types.ts
@@ -49,9 +49,9 @@ export interface Torrent extends BaseTorrent {
|
||||
}
|
||||
|
||||
export interface Category {
|
||||
key: string
|
||||
name: string
|
||||
savePath?: string
|
||||
key: string;
|
||||
name: string;
|
||||
savePath?: string;
|
||||
}
|
||||
|
||||
export interface ServerState {
|
||||
@@ -88,45 +88,45 @@ export interface MainData {
|
||||
}
|
||||
|
||||
export interface RssTorrent {
|
||||
category?: string
|
||||
comment?: string
|
||||
date?: string
|
||||
description?: string
|
||||
id: string
|
||||
link: string
|
||||
title: string
|
||||
torrentURL: string
|
||||
category?: string;
|
||||
comment?: string;
|
||||
date?: string;
|
||||
description?: string;
|
||||
id: string;
|
||||
link: string;
|
||||
title: string;
|
||||
torrentURL: string;
|
||||
}
|
||||
|
||||
export interface RssItem {
|
||||
articles: RssTorrent[]
|
||||
hasError: boolean
|
||||
isLoading: boolean
|
||||
lastBuildDate: string
|
||||
title: string
|
||||
uid: string
|
||||
url: string
|
||||
articles: RssTorrent[];
|
||||
hasError: boolean;
|
||||
isLoading: boolean;
|
||||
lastBuildDate: string;
|
||||
title: string;
|
||||
uid: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface RssNode {
|
||||
[key: string]: RssNode | RssItem
|
||||
[key: string]: RssNode | RssItem;
|
||||
}
|
||||
|
||||
export interface RssRule {
|
||||
enabled: boolean,
|
||||
mustContain: string,
|
||||
mustNotContain: string,
|
||||
useRegex: boolean,
|
||||
episodeFilter: string,
|
||||
smartFilter: boolean,
|
||||
previouslyMatchedEpisodes: string[],
|
||||
affectedFeeds: string[],
|
||||
createSubfolder: boolean | null,
|
||||
ignoreDays: number,
|
||||
lastMatch: string,
|
||||
addPaused: boolean | null,
|
||||
assignedCategory: string,
|
||||
savepath: string,
|
||||
enabled: boolean;
|
||||
mustContain: string;
|
||||
mustNotContain: string;
|
||||
useRegex: boolean;
|
||||
episodeFilter: string;
|
||||
smartFilter: boolean;
|
||||
previouslyMatchedEpisodes: string[];
|
||||
affectedFeeds: string[];
|
||||
createSubfolder: boolean | null;
|
||||
ignoreDays: number;
|
||||
lastMatch: string;
|
||||
addPaused: boolean | null;
|
||||
assignedCategory: string;
|
||||
savepath: string;
|
||||
}
|
||||
|
||||
export interface TorrentProperties {
|
||||
|
||||
@@ -2,7 +2,7 @@ import Vuex from 'vuex';
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import '@/directives';
|
||||
import FilterGroup from '@/components/drawer/FilterGroup.vue';
|
||||
import { FilterGroup as types } from '@/components/types';
|
||||
import * as types from '@/components/types';
|
||||
import { mock } from '../utils';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
@@ -101,7 +101,6 @@ test('unselect if can not found children', () => {
|
||||
const wrapper = mount({
|
||||
group,
|
||||
});
|
||||
const { vm } = wrapper;
|
||||
|
||||
expect((wrapper.vm as any).selected).toBeNull();
|
||||
});
|
||||
|
||||
@@ -47,6 +47,7 @@ describe('all torrents getter', () => {
|
||||
store.replaceState(mockState({
|
||||
mainData: {
|
||||
categories: {},
|
||||
// eslint-disable-next-line @typescript-eslint/camelcase
|
||||
server_state: undefined as any,
|
||||
torrents: {
|
||||
a: mockBaseTorrent({}),
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import {
|
||||
findSameNamedTorrents, timeout, codeToFlag, sleep,
|
||||
} from '@/utils';
|
||||
import { findSameNamedTorrents, codeToFlag, sleep } from '@/utils';
|
||||
import { mockTorrent } from './utils';
|
||||
|
||||
test('timeout', async () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Torrent, BaseTorrent } from '@/types';
|
||||
|
||||
const emptyBaseTorrent: BaseTorrent = {
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
added_on: 0,
|
||||
amount_left: 0,
|
||||
auto_tmm: false,
|
||||
@@ -43,6 +44,7 @@ const emptyBaseTorrent: BaseTorrent = {
|
||||
uploaded: 0,
|
||||
uploaded_session: 0,
|
||||
upspeed: 0,
|
||||
/* eslint-enable @typescript-eslint/camelcase */
|
||||
};
|
||||
|
||||
const emptyTorrent: Torrent = Object.assign({}, emptyBaseTorrent, {
|
||||
|
||||
Reference in New Issue
Block a user