mirror of
https://github.com/CzBiX/qb-web.git
synced 2026-04-28 04:41:24 +08:00
Refine UI code
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
<main-toolbar v-model="drawer" />
|
||||
|
||||
<v-content>
|
||||
<v-container pa-0 fluid>
|
||||
<v-container fluid>
|
||||
<torrents />
|
||||
</v-container>
|
||||
</v-content>
|
||||
@@ -141,7 +141,7 @@ export default Vue.extend({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
margin-bottom: 80px;
|
||||
padding: 0 0 80px;
|
||||
}
|
||||
.drawer {
|
||||
display: flex;
|
||||
|
||||
13
src/assets/mdi.scss
Normal file
13
src/assets/mdi.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
@import "~@mdi/font/scss/variables";
|
||||
@import "~@mdi/font/scss/functions";
|
||||
@import "~@mdi/font/scss/core";
|
||||
@import "~@mdi/font/scss/icons";
|
||||
@import "~@mdi/font/scss/extras";
|
||||
@import "~@mdi/font/scss/animated";
|
||||
|
||||
@font-face {
|
||||
font-family: "Material Design Icons";
|
||||
src: url("~@mdi/font/fonts/materialdesignicons-webfont.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -50,7 +50,7 @@
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
</v-list-group>
|
||||
<template v-if="item.filterGroups">
|
||||
<template v-else-if="item.filterGroups">
|
||||
<filter-group
|
||||
v-for="(child, i) in item.filterGroups"
|
||||
:key="i"
|
||||
@@ -153,6 +153,7 @@ export default {
|
||||
...mapGetters([
|
||||
'isDataReady',
|
||||
'allTorrents',
|
||||
'allCategories',
|
||||
'torrentGroupByCategory',
|
||||
'torrentGroupBySite',
|
||||
'torrentGroupByState',
|
||||
@@ -187,21 +188,26 @@ export default {
|
||||
],
|
||||
});
|
||||
|
||||
const categories: any[] = _.sortBy(Object.entries(this.torrentGroupByCategory).map(([key, value]) => {
|
||||
const categories: any[] = [{
|
||||
key: '',
|
||||
name: 'Uncategorized',
|
||||
}].concat(this.allCategories).map(category => {
|
||||
let value = this.torrentGroupByCategory[category.key]
|
||||
if (_.isUndefined(value)) {
|
||||
value = [];
|
||||
}
|
||||
const size = formatSize(_.sumBy(value, 'size'));
|
||||
const title = (key ? key : 'Uncategorized') + ` (${value.length})`;
|
||||
const title = category.name + ` (${value.length})`;
|
||||
const append = `[${size}]`;
|
||||
return { icon: 'mdi-folder-open', title, key, append};
|
||||
}), 'key');
|
||||
return { icon: 'mdi-folder-open', title, key: category.key, append};
|
||||
});
|
||||
filterGroups.push({
|
||||
'icon': 'mdi-menu-up',
|
||||
'icon-alt': 'mdi-menu-down',
|
||||
'title': 'Categories',
|
||||
'model': false,
|
||||
'model': true,
|
||||
'select': 'category',
|
||||
'children': [
|
||||
...categories,
|
||||
],
|
||||
'children': categories,
|
||||
});
|
||||
|
||||
const sites: any[] = _.sortBy(Object.entries(this.torrentGroupBySite).map(([key, value]) => {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
:align-center="!phoneLayout"
|
||||
>
|
||||
<v-flex v-if="!phoneLayout">
|
||||
<v-tooltip top>
|
||||
<v-tooltip top lazy>
|
||||
<template v-slot:activator="{ on }">
|
||||
<span v-on="on">
|
||||
qBittorrent {{ app.version }}
|
||||
@@ -21,12 +21,13 @@
|
||||
</v-flex>
|
||||
<v-divider vertical class="mx-2" v-if="!phoneLayout"/>
|
||||
<v-flex class="icon-label">
|
||||
<v-icon color="info">mdi-nas</v-icon>
|
||||
Free: {{ info.free_space_on_disk | formatSize }}
|
||||
<v-icon>mdi-sprout</v-icon>
|
||||
{{ allTorrents.length }} [{{ totalSize | formatSize }}]
|
||||
</v-flex>
|
||||
<v-divider vertical class="mx-2" v-if="!phoneLayout"/>
|
||||
<v-flex v-if="!phoneLayout">
|
||||
Torrents: {{ allTorrents.length }} ({{ totalSize | formatSize }})
|
||||
<v-flex class="icon-label">
|
||||
<v-icon>mdi-nas</v-icon>
|
||||
{{ info.free_space_on_disk | formatSize }}
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-flex>
|
||||
@@ -35,12 +36,13 @@
|
||||
:column="phoneLayout"
|
||||
:align-center="!phoneLayout"
|
||||
>
|
||||
<v-flex v-if="!phoneLayout">
|
||||
DHT: {{ info.dht_nodes }} nodes
|
||||
<v-flex v-if="!phoneLayout" class="icon-label">
|
||||
<v-icon>mdi-access-point-network</v-icon>
|
||||
{{ info.dht_nodes }} nodes
|
||||
</v-flex>
|
||||
<v-divider vertical class="mx-2" v-if="!phoneLayout"/>
|
||||
<v-flex class="icon-label">
|
||||
<v-tooltip top>
|
||||
<v-tooltip top lazy>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon
|
||||
v-on="on"
|
||||
@@ -71,7 +73,7 @@
|
||||
>mdi-speedometer</v-icon>
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-tooltip top v-else>
|
||||
<v-tooltip top lazy v-else>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-icon
|
||||
v-on="on"
|
||||
@@ -86,7 +88,9 @@
|
||||
</v-flex>
|
||||
<v-divider vertical class="mx-2" v-if="!phoneLayout"/>
|
||||
<v-flex class="icon-label">
|
||||
<v-icon color="success">mdi-download</v-icon>
|
||||
<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">
|
||||
@@ -99,7 +103,9 @@
|
||||
</v-flex>
|
||||
<v-divider vertical class="mx-2" v-if="!phoneLayout"/>
|
||||
<v-flex class="icon-label">
|
||||
<v-icon color="warning">mdi-upload</v-icon>
|
||||
<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">
|
||||
@@ -266,7 +272,7 @@ export default Vue.extend({
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
.in-drawer {
|
||||
padding: 0 16px;
|
||||
padding: 0 16px 0 20px;
|
||||
|
||||
.no-icon {
|
||||
margin-left: 24px;
|
||||
|
||||
@@ -35,25 +35,29 @@
|
||||
Set category
|
||||
</v-subheader>
|
||||
<v-list-tile
|
||||
v-for="(item, i) in categories"
|
||||
v-for="(item, i) in allCategories"
|
||||
:key="i"
|
||||
@click="setTorrentsCategory(item)"
|
||||
@click="setTorrentsCategory(item.key)"
|
||||
>
|
||||
<v-list-tile-action>
|
||||
<v-icon>mdi-folder-open</v-icon>
|
||||
</v-list-tile-action>
|
||||
<v-list-tile-title>
|
||||
{{ item }}
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
{{ item.name }}
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
<v-divider />
|
||||
<v-list-tile @click="setTorrentsCategory('')">
|
||||
<v-list-tile-action>
|
||||
<v-icon>mdi-folder-remove</v-icon>
|
||||
</v-list-tile-action>
|
||||
<v-list-tile-title>
|
||||
Reset
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
Reset
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
@@ -152,6 +156,7 @@ export default Vue.extend({
|
||||
...mapGetters([
|
||||
'isDataReady',
|
||||
'allTorrents',
|
||||
'allCategories',
|
||||
'torrentGroupByCategory',
|
||||
'torrentGroupBySite',
|
||||
'torrentGroupByState',
|
||||
@@ -160,9 +165,6 @@ export default Vue.extend({
|
||||
filter(state, getters) {
|
||||
return getters.config.filter;
|
||||
},
|
||||
categories(state, getters) {
|
||||
return Object.keys(getters.torrentGroupByCategory).filter(_.identity);
|
||||
},
|
||||
}),
|
||||
hasSelected() {
|
||||
return this.selectedRows.length;
|
||||
|
||||
@@ -24,11 +24,12 @@ export interface DurationOptions {
|
||||
|
||||
export function formatDuration(value: number, options?: DurationOptions) {
|
||||
const minute = 60;
|
||||
const hour = 3600;
|
||||
const day = 3600 * 24;
|
||||
const hour = minute * 60;
|
||||
const day = hour * 24;
|
||||
const year = day * 365;
|
||||
|
||||
const durations = [day, hour, minute, 1];
|
||||
const units = 'dhms';
|
||||
const durations = [year, day, hour, minute, 1];
|
||||
const units = 'ydhms';
|
||||
|
||||
let index = 0;
|
||||
let unitSize = 0;
|
||||
@@ -41,6 +42,10 @@ export function formatDuration(value: number, options?: DurationOptions) {
|
||||
|
||||
const opt = options ? Object.assign(defaultOptions, options) : defaultOptions;
|
||||
|
||||
if (opt.dayLimit && value >= opt.dayLimit * day) {
|
||||
return '∞';
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if ((opt.maxUnitSize && unitSize === opt.maxUnitSize) || index === durations.length) {
|
||||
break;
|
||||
@@ -52,9 +57,6 @@ export function formatDuration(value: number, options?: DurationOptions) {
|
||||
continue;
|
||||
}
|
||||
const result = Math.floor(value / duration);
|
||||
if (index === 0 && opt.dayLimit && result >= opt.dayLimit) {
|
||||
return '∞';
|
||||
}
|
||||
parts.push(result + units[index]);
|
||||
|
||||
value %= duration;
|
||||
|
||||
@@ -6,7 +6,7 @@ import './directives';
|
||||
import App from './App.vue';
|
||||
// import './registerServiceWorker';
|
||||
import 'roboto-fontface/css/roboto/roboto-fontface.css';
|
||||
import '@mdi/font/css/materialdesignicons.css';
|
||||
import '@/assets/mdi.scss';
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
|
||||
18
src/store.ts
18
src/store.ts
@@ -9,7 +9,7 @@ Vue.use(Vuex);
|
||||
const defaultConfig = {
|
||||
updateInterval: 2000,
|
||||
pagination: {
|
||||
rowsPerPage: 100,
|
||||
rowsPerPage: 50,
|
||||
},
|
||||
filter: {
|
||||
state: null,
|
||||
@@ -53,6 +53,12 @@ export default new Vuex.Store({
|
||||
}
|
||||
delete payload.torrents_removed;
|
||||
}
|
||||
if (payload.categories_removed) {
|
||||
for (const key of payload.categories_removed) {
|
||||
delete tmp.categories[key];
|
||||
}
|
||||
delete payload.categories_removed;
|
||||
}
|
||||
state.mainData = _.merge(tmp, payload);
|
||||
}
|
||||
},
|
||||
@@ -84,6 +90,16 @@ export default new Vuex.Store({
|
||||
return _.merge({}, value, { hash: key });
|
||||
});
|
||||
},
|
||||
allCategories(state) {
|
||||
if (!state.mainData) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const categories = _.map(state.mainData.categories, (value, key) => {
|
||||
return _.merge({}, value, { key });
|
||||
});
|
||||
return _.sortBy(categories, 'name')
|
||||
},
|
||||
torrentGroupByCategory(state, getters) {
|
||||
return _.groupBy(getters.allTorrents, (torrent) => torrent.category);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user