mirror of
https://github.com/VueTorrent/VueTorrent.git
synced 2025-02-26 12:21:00 +03:00
updated vuetify & fucked everything up
This commit is contained in:
parent
57e22c88db
commit
ebc026460b
18 changed files with 7293 additions and 4130 deletions
39
.eslintrc.js
39
.eslintrc.js
|
@ -1,30 +1,25 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
root: true,
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
node: true
|
||||||
commonjs: true,
|
|
||||||
es6: true
|
|
||||||
},
|
|
||||||
extends: ['plugin:vue/essential', 'airbnb-base'],
|
|
||||||
globals: {
|
|
||||||
Atomics: 'readonly',
|
|
||||||
SharedArrayBuffer: 'readonly'
|
|
||||||
},
|
},
|
||||||
|
extends: ['plugin:vue/essential', 'eslint:recommended', '@vue/prettier'],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 2018
|
parser: 'babel-eslint'
|
||||||
},
|
},
|
||||||
plugins: ['vue', 'prettier'],
|
|
||||||
rules: {
|
rules: {
|
||||||
semi: ['warn', 'never'],
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
'no-console': 0,
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||||
camelcase: 0,
|
},
|
||||||
'no-restricted-syntax': 0,
|
overrides: [
|
||||||
'no-shadow': 0,
|
{
|
||||||
'class-methods-use-this': 0,
|
files: [
|
||||||
'prefer-promise-reject-errors': 0,
|
'**/__tests__/*.{j,t}s?(x)',
|
||||||
'no-underscore-dangle': 0,
|
'**/tests/unit/**/*.spec.{j,t}s?(x)'
|
||||||
'no-param-reassign': 0,
|
],
|
||||||
'no-unused-vars': 0,
|
env: {
|
||||||
indent: 0,
|
jest: true
|
||||||
'comma-dangle': 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ['@vue/app'],
|
presets: ['@vue/cli-plugin-babel/preset']
|
||||||
}
|
}
|
||||||
|
|
3
jest.config.js
Normal file
3
jest.config.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
preset: '@vue/cli-plugin-unit-jest'
|
||||||
|
}
|
10602
package-lock.json
generated
10602
package-lock.json
generated
File diff suppressed because it is too large
Load diff
77
package.json
77
package.json
|
@ -9,69 +9,44 @@
|
||||||
"format": "pretty-quick"
|
"format": "pretty-quick"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/polyfill": "^7.4.4",
|
||||||
|
"core-js": "^3.6.4",
|
||||||
"apexcharts": "^3.5.0",
|
"apexcharts": "^3.5.0",
|
||||||
"axios": "^0.18.1",
|
"axios": "^0.18.1",
|
||||||
"cors": "^2.8.5",
|
|
||||||
"date-fns": "^1.30.1",
|
"date-fns": "^1.30.1",
|
||||||
"dotenv": "^8.2.0",
|
|
||||||
"eslint-plugin-prettier": "^3.1.3",
|
"eslint-plugin-prettier": "^3.1.3",
|
||||||
"express": "^4.17.1",
|
|
||||||
"filepond": "^4.13.6",
|
|
||||||
"filepond-plugin-file-validate-size": "^2.2.1",
|
|
||||||
"filepond-plugin-file-validate-type": "^1.2.5",
|
|
||||||
"filepond-plugin-image-preview": "^4.6.4",
|
|
||||||
"lodash": "^4.17.15",
|
|
||||||
"multer": "^1.4.2",
|
"multer": "^1.4.2",
|
||||||
"prettier": "^2.0.5",
|
|
||||||
"pretty-quick": "^2.0.1",
|
|
||||||
"qbittorrent-api-v2": "^1.2.0",
|
|
||||||
"register-service-worker": "^1.7.1",
|
"register-service-worker": "^1.7.1",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-apexcharts": "^1.3.0",
|
"vue-apexcharts": "^1.3.0",
|
||||||
"vue-filepond": "^5.1.3",
|
"vue-context": "^5.1.0",
|
||||||
"vue-router": "^3.2.0",
|
"vue-router": "^3.2.0",
|
||||||
"vue-toastification": "^1.7.1",
|
"vue-toastification": "^1.7.1",
|
||||||
"vuetify": "^1.5.24",
|
"vuetify": "^2.2.11",
|
||||||
"vuex": "^3.4.0"
|
"vuex": "^3.4.0",
|
||||||
|
"vuex-persist": "^2.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^3.12.1",
|
"@vue/cli-plugin-babel": "~4.3.0",
|
||||||
"@vue/cli-plugin-eslint": "^3.12.1",
|
"@vue/cli-plugin-eslint": "~4.3.0",
|
||||||
"@vue/cli-plugin-pwa": "^3.12.1",
|
"@vue/cli-plugin-pwa": "~4.3.0",
|
||||||
"@vue/cli-service": "^3.12.1",
|
"@vue/cli-plugin-router": "~4.3.0",
|
||||||
"eslint": "^6.8.0",
|
"@vue/cli-plugin-unit-jest": "~4.3.0",
|
||||||
"eslint-config-airbnb-base": "^14.1.0",
|
"@vue/cli-plugin-vuex": "~4.3.0",
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"@vue/cli-service": "~4.3.0",
|
||||||
"eslint-plugin-vue": "^5.2.3",
|
"@vue/eslint-config-prettier": "^6.0.0",
|
||||||
"stylus": "^0.54.7",
|
"@vue/test-utils": "1.0.0-beta.31",
|
||||||
"stylus-loader": "^3.0.1",
|
"babel-eslint": "^10.1.0",
|
||||||
"vue-cli-plugin-vuetify": "^0.4.6",
|
"eslint": "^6.7.2",
|
||||||
|
"eslint-plugin-prettier": "^3.1.1",
|
||||||
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
|
"laravel-mix": "^5.0.4",
|
||||||
|
"prettier": "^1.19.1",
|
||||||
|
"pretty-quick": "^2.0.1",
|
||||||
|
"sass-loader": "^8.0.2",
|
||||||
|
"tailwindcss": "^1.4.2",
|
||||||
|
"vue-cli-plugin-vuetify": "~2.0.5",
|
||||||
"vue-template-compiler": "^2.6.11",
|
"vue-template-compiler": "^2.6.11",
|
||||||
"vuetify-loader": "^1.4.3",
|
"vuetify-loader": "^1.4.3"
|
||||||
"webpack": "^4.43.0",
|
|
||||||
"webpack-cli": "^3.3.11"
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"root": true,
|
|
||||||
"env": {
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"plugin:vue/essential"
|
|
||||||
],
|
|
||||||
"rules": {},
|
|
||||||
"parserOptions": {
|
|
||||||
"parser": "babel-eslint"
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"postcss": {
|
|
||||||
"plugins": {
|
|
||||||
"autoprefixer": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"browserslist": [
|
|
||||||
"> 1%",
|
|
||||||
"last 2 versions",
|
|
||||||
"not ie <= 8"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
11
src/App.vue
11
src/App.vue
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<v-app class="grey lighten-4">
|
<v-app :style="{ background: $vuetify.theme.themes[theme].background }">
|
||||||
<div v-if="authenticated">
|
<div v-if="authenticated">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<v-content class="mx-4 mb-4">
|
<v-content class="mx-4 mb-4">
|
||||||
|
@ -29,10 +29,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
import Navbar from './components/Navbar.vue'
|
import Navbar from './components/Navbar.vue'
|
||||||
import Login from './components/Login.vue'
|
import Login from './components/Login.vue'
|
||||||
import qbit from './services/qbit'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { Navbar, Login },
|
components: { Navbar, Login },
|
||||||
|
@ -41,7 +40,11 @@ export default {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['authenticated', 'rid', 'mainData', 'preferences'])
|
...mapState(['authenticated', 'rid', 'mainData', 'preferences']),
|
||||||
|
...mapGetters(['getTheme']),
|
||||||
|
theme() {
|
||||||
|
return this.getTheme() ? 'dark' : 'light'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
<template>
|
|
||||||
<v-dialog max-width="400px" v-model="dialog">
|
|
||||||
<v-btn flat small fab slot="activator" color="grey" class="mr-0 ml-0">
|
|
||||||
<v-icon color="grey">add</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
<v-card>
|
|
||||||
<v-container :class="`pa-0 project done`">
|
|
||||||
<v-card-title class="justify-center">
|
|
||||||
<h2>Add a new Torrent</h2>
|
|
||||||
</v-card-title>
|
|
||||||
<div class="mr-5 ml-5">
|
|
||||||
<file-pond
|
|
||||||
name="file"
|
|
||||||
ref="pond"
|
|
||||||
label-idle="Drop file here..."
|
|
||||||
accepted-file-types="application/x-bittorrent"
|
|
||||||
data-max-file-size="1MB"
|
|
||||||
server="/upload"
|
|
||||||
v-model="Files"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<v-card-text>
|
|
||||||
<v-form class="px-3" ref="form">
|
|
||||||
<!-- <v-text-field
|
|
||||||
v-model="filename"
|
|
||||||
label="File"
|
|
||||||
prepend-icon="attach_file"
|
|
||||||
:rules="inputRules"
|
|
||||||
></v-text-field>-->
|
|
||||||
<v-text-field v-model="directory" label="Download Directory" prepend-icon="folder"></v-text-field>
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<v-card-actions class="justify-center">
|
|
||||||
<v-btn
|
|
||||||
:loading="loading"
|
|
||||||
flat
|
|
||||||
@click="submit"
|
|
||||||
class="blue_accent white--text mx-0 mt-3"
|
|
||||||
>Add Torrent</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-form>
|
|
||||||
</v-card-text>
|
|
||||||
</v-container>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Import Vue FilePond
|
|
||||||
import vueFilePond from 'vue-filepond'
|
|
||||||
|
|
||||||
// Import FilePond styles
|
|
||||||
import 'filepond/dist/filepond.min.css'
|
|
||||||
|
|
||||||
// Import image preview plugin styles
|
|
||||||
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css'
|
|
||||||
|
|
||||||
// Import image preview and file type validation plugins
|
|
||||||
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type'
|
|
||||||
import FilePondPluginImagePreview from 'filepond-plugin-image-preview'
|
|
||||||
import FilePondPluginFileValidateSize from 'filepond-plugin-file-validate-size'
|
|
||||||
|
|
||||||
// Create component
|
|
||||||
const FilePond = vueFilePond(
|
|
||||||
FilePondPluginFileValidateType,
|
|
||||||
FilePondPluginImagePreview,
|
|
||||||
FilePondPluginFileValidateSize,
|
|
||||||
)
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
filename: '',
|
|
||||||
directory: '',
|
|
||||||
inputRules: [
|
|
||||||
(v) => v.indexOf('magnet') > -1
|
|
||||||
|| v.indexOf('http') > -1
|
|
||||||
|| this.validFile
|
|
||||||
|| 'Not a valid magnet link',
|
|
||||||
],
|
|
||||||
loading: false,
|
|
||||||
dialog: false,
|
|
||||||
Files: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
submit() {
|
|
||||||
if (this.$refs.form.validate()) {
|
|
||||||
this.loading = true
|
|
||||||
|
|
||||||
this.$store.dispatch('ADD_TORRENT', {
|
|
||||||
name: this.filename,
|
|
||||||
dir: this.directory,
|
|
||||||
})
|
|
||||||
|
|
||||||
// reset input
|
|
||||||
this.$refs.form.reset()
|
|
||||||
this.filename = ''
|
|
||||||
this.directory = ''
|
|
||||||
|
|
||||||
this.$refs.pond.removeFiles()
|
|
||||||
this.dialog = false
|
|
||||||
this.loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
validFile() {
|
|
||||||
return this.Files.length > 0
|
|
||||||
},
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
vueFilePond,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
108
src/components/Modals/AddModal.vue
Normal file
108
src/components/Modals/AddModal.vue
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
<template>
|
||||||
|
<v-dialog max-width="400px" v-model="dialog">
|
||||||
|
<v-card>
|
||||||
|
<v-container :class="`pa-0 project done`">
|
||||||
|
<v-card-title class="justify-center">
|
||||||
|
<h2>Add a new Torrent</h2>
|
||||||
|
</v-card-title>
|
||||||
|
<v-container>
|
||||||
|
<div ref="fileZone">
|
||||||
|
<v-file-input
|
||||||
|
v-show="files.length"
|
||||||
|
v-model="files"
|
||||||
|
ref="file"
|
||||||
|
multiple
|
||||||
|
chips
|
||||||
|
outlined
|
||||||
|
label="files"
|
||||||
|
/>
|
||||||
|
<v-textarea
|
||||||
|
v-show="!files.length"
|
||||||
|
label="URL"
|
||||||
|
placeholder="add Torrent"
|
||||||
|
prepend-icon="mdi-link"
|
||||||
|
append-outer-icon="mdi-attachment"
|
||||||
|
:rules="[v => !!files.length || !!v]"
|
||||||
|
:rows="$vuetify.breakpoint.xsOnly ? 1 : 3"
|
||||||
|
required
|
||||||
|
:autofocus="!phoneLayout"
|
||||||
|
:value="params.urls"
|
||||||
|
@input="setParams('urls', $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</v-container>
|
||||||
|
<v-card-text>
|
||||||
|
<v-form class="px-3" ref="form">
|
||||||
|
<v-text-field
|
||||||
|
v-model="directory"
|
||||||
|
label="Download Directory"
|
||||||
|
prepend-icon="folder"
|
||||||
|
></v-text-field>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-card-actions class="justify-center">
|
||||||
|
<v-btn
|
||||||
|
:loading="loading"
|
||||||
|
flat
|
||||||
|
@click="submit"
|
||||||
|
class="blue_accent white--text mx-0 mt-3"
|
||||||
|
>Add Torrent</v-btn
|
||||||
|
>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
</v-container>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Modal from '@/mixins/Modal'
|
||||||
|
export default {
|
||||||
|
name: 'AddModal',
|
||||||
|
mixins: [Modal],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
files: [],
|
||||||
|
directory: '',
|
||||||
|
inputRules: [
|
||||||
|
v =>
|
||||||
|
v.indexOf('magnet') > -1 ||
|
||||||
|
v.indexOf('http') > -1 ||
|
||||||
|
this.validFile ||
|
||||||
|
'Not a valid magnet link'
|
||||||
|
],
|
||||||
|
loading: false,
|
||||||
|
params: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
if (this.$refs.form.validate()) {
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
this.$store.dispatch('ADD_TORRENT', {
|
||||||
|
name: this.filename,
|
||||||
|
dir: this.directory
|
||||||
|
})
|
||||||
|
|
||||||
|
// reset input
|
||||||
|
this.$refs.form.reset()
|
||||||
|
this.filename = ''
|
||||||
|
this.directory = ''
|
||||||
|
|
||||||
|
this.$refs.pond.removeFiles()
|
||||||
|
this.dialog = false
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
validFile() {
|
||||||
|
return this.Files.length > 0
|
||||||
|
},
|
||||||
|
phoneLayout() {
|
||||||
|
return this.$vuetify.breakpoint.xsOnly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<nav>
|
<nav>
|
||||||
<!--title-->
|
<!--title-->
|
||||||
<v-toolbar flat app>
|
<v-app-bar flat>
|
||||||
<v-toolbar-side-icon
|
<v-app-bar-nav-icon
|
||||||
@click="drawer = !drawer"
|
@click="drawer = !drawer"
|
||||||
class="grey--text"
|
class="grey--text text--darken-5"
|
||||||
></v-toolbar-side-icon>
|
></v-app-bar-nav-icon>
|
||||||
<v-toolbar-title
|
<v-toolbar-title
|
||||||
:class="[
|
:class="[
|
||||||
'grey--text',
|
'grey--text',
|
||||||
|
@ -18,20 +18,29 @@
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
|
|
||||||
<!--right corner functions-->
|
<!--right corner functions-->
|
||||||
<AddTorrent @torrentAdded="snackbar = true" />
|
<v-btn
|
||||||
<v-btn small fab flat class="mr-0 ml-0" @click="removeTorrents">
|
text
|
||||||
|
small
|
||||||
|
fab
|
||||||
|
color="grey"
|
||||||
|
class="mr-0 ml-0"
|
||||||
|
@click="toggleModal('addmodal')"
|
||||||
|
>
|
||||||
|
<v-icon color="grey">add</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn small fab text class="mr-0 ml-0" @click="removeTorrents">
|
||||||
<v-icon color="grey">remove</v-icon>
|
<v-icon color="grey">remove</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn small fab flat class="mr-0 ml-0" @click="resumeTorrents">
|
<v-btn small fab text class="mr-0 ml-0" @click="resumeTorrents">
|
||||||
<v-icon color="grey">play_arrow</v-icon>
|
<v-icon color="grey">play_arrow</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn small fab flat class="mr-0 ml-0" @click="pauseTorrents">
|
<v-btn small fab text class="mr-0 ml-0" @click="pauseTorrents">
|
||||||
<v-icon color="grey">pause</v-icon>
|
<v-icon color="grey">pause</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn small fab flat class="mr-0 ml-0" @click="refreshTorrents">
|
<v-btn small fab text class="mr-0 ml-0" @click="refreshTorrents">
|
||||||
<v-icon color="grey">autorenew</v-icon>
|
<v-icon color="grey">autorenew</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-toolbar>
|
</v-app-bar>
|
||||||
<!--navigation drawer itself -->
|
<!--navigation drawer itself -->
|
||||||
<v-navigation-drawer app v-model="drawer" class="primary allow-spacer">
|
<v-navigation-drawer app v-model="drawer" class="primary allow-spacer">
|
||||||
<!--current download speeds -->
|
<!--current download speeds -->
|
||||||
|
@ -135,51 +144,17 @@
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<v-layout class="align-end">
|
|
||||||
<Settings />
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<v-tooltip top v-if="paused">
|
|
||||||
<v-btn
|
|
||||||
small
|
|
||||||
fab
|
|
||||||
flat
|
|
||||||
class="mr-4"
|
|
||||||
@click="startInterval"
|
|
||||||
slot="activator"
|
|
||||||
>
|
|
||||||
<v-icon color="green_accent">play_arrow</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
<span>Resumes connection to client</span>
|
|
||||||
</v-tooltip>
|
|
||||||
<v-tooltip top v-else>
|
|
||||||
<v-btn
|
|
||||||
small
|
|
||||||
fab
|
|
||||||
flat
|
|
||||||
class="mr-4"
|
|
||||||
@click="clearInterval"
|
|
||||||
slot="activator"
|
|
||||||
>
|
|
||||||
<v-icon color="green_accent">pause</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
<span>Pauses connection to client</span>
|
|
||||||
</v-tooltip>
|
|
||||||
</v-layout>
|
|
||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapMutations, mapGetters, mapState } from 'vuex'
|
import { mapMutations, mapState } from 'vuex'
|
||||||
import { setInterval } from 'timers'
|
import { setInterval } from 'timers'
|
||||||
import VueApexCharts from 'vue-apexcharts'
|
import VueApexCharts from 'vue-apexcharts'
|
||||||
import ApexCharts from 'apexcharts'
|
|
||||||
import AddTorrent from './AddTorrent'
|
|
||||||
import Settings from './Settings'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { AddTorrent, Settings, apexcharts: VueApexCharts },
|
components: { apexcharts: VueApexCharts },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
drawer: false,
|
drawer: false,
|
||||||
|
@ -244,6 +219,9 @@ export default {
|
||||||
refreshTorrents() {},
|
refreshTorrents() {},
|
||||||
updateChart() {
|
updateChart() {
|
||||||
this.$refs.chart.updateSeries(this.series, true)
|
this.$refs.chart.updateSeries(this.series, true)
|
||||||
|
},
|
||||||
|
toggleModal(name) {
|
||||||
|
this.$store.commit('TOGGLE_MODAL', name)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
<template>
|
|
||||||
<v-dialog max-width="600px" max-height="800px">
|
|
||||||
<v-btn flat small fab slot="activator" color="grey" class="mr-0 ml-0">
|
|
||||||
<v-icon color="blue_accent">settings</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
<v-container class="grey lighten-4">
|
|
||||||
<v-card class="pa-3">
|
|
||||||
<div>Nothing here yet!</div>
|
|
||||||
</v-card>
|
|
||||||
<div class="mt-3">
|
|
||||||
<v-card-actions class="justify-center">
|
|
||||||
<v-btn target="blank" href="https://paypal.me/Dwijns" class="blue_accent white--text">
|
|
||||||
<v-icon>attach_money</v-icon>support me on paypal (or dont)
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</div>
|
|
||||||
</v-container>
|
|
||||||
</v-dialog>
|
|
||||||
</template>
|
|
26
src/main.js
26
src/main.js
|
@ -1,19 +1,33 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import './plugins/vuetify'
|
import App from '@/App.vue'
|
||||||
import Toast from 'vue-toastification'
|
import '@/registerServiceWorker'
|
||||||
import App from './App.vue'
|
import router from '@/router'
|
||||||
import router from './router'
|
import store from '@/store'
|
||||||
import store from './services/store'
|
import vuetify from '@/plugins/vuetify'
|
||||||
import './registerServiceWorker'
|
import '@babel/polyfill'
|
||||||
|
|
||||||
|
import Toast from 'vue-toastification'
|
||||||
import 'vue-toastification/dist/index.css'
|
import 'vue-toastification/dist/index.css'
|
||||||
|
|
||||||
Vue.use(Toast)
|
Vue.use(Toast)
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
// register modals
|
||||||
|
const files = require.context('@/components/Modals', true, /\.vue$/i)
|
||||||
|
files.keys().map(key =>
|
||||||
|
Vue.component(
|
||||||
|
key
|
||||||
|
.split('/')
|
||||||
|
.pop()
|
||||||
|
.split('.')[0],
|
||||||
|
files(key).default
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
router,
|
router,
|
||||||
store,
|
store,
|
||||||
|
vuetify,
|
||||||
render: h => h(App)
|
render: h => h(App)
|
||||||
}).$mount('#app')
|
}).$mount('#app')
|
||||||
|
|
17
src/mixins/Modal.js
Normal file
17
src/mixins/Modal.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['getAvailableTiles', 'getModalState']),
|
||||||
|
availableTiles() {
|
||||||
|
return this.getAvailableTiles()
|
||||||
|
},
|
||||||
|
dialog: {
|
||||||
|
get() {
|
||||||
|
return this.getModalState(this.$options.name)
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
this.$store.commit('TOGGLE_MODAL', this.$options.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,5 +17,4 @@ export default class Stat {
|
||||||
const f = Math.floor(Math.log(a) / Math.log(c))
|
const f = Math.floor(Math.log(a) / Math.log(c))
|
||||||
return `${parseFloat((a / Math.pow(c, f)).toFixed(d))} ${e[f]}`
|
return `${parseFloat((a / Math.pow(c, f)).toFixed(d))} ${e[f]}`
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuetify from 'vuetify/lib'
|
import Vuetify from 'vuetify/lib'
|
||||||
import 'vuetify/src/stylus/app.styl'
|
import 'vuetify/dist/vuetify.min.css'
|
||||||
|
|
||||||
Vue.use(Vuetify, {
|
Vue.use(Vuetify)
|
||||||
|
|
||||||
|
export default new Vuetify({
|
||||||
iconfont: 'md',
|
iconfont: 'md',
|
||||||
theme: {
|
theme: {
|
||||||
|
themes: {
|
||||||
|
light: {
|
||||||
primary: '#35495e',
|
primary: '#35495e',
|
||||||
secondary: '#3e556d',
|
secondary: '#3e556d',
|
||||||
secondary_lighter: '#56718c',
|
secondary_lighter: '#56718c',
|
||||||
|
@ -14,5 +18,20 @@ Vue.use(Vuetify, {
|
||||||
green_accent: '#3cd1c2',
|
green_accent: '#3cd1c2',
|
||||||
download: '#64CEAA',
|
download: '#64CEAA',
|
||||||
upload: '#00b3fa',
|
upload: '#00b3fa',
|
||||||
|
background: '#0000'
|
||||||
},
|
},
|
||||||
|
dark: {
|
||||||
|
primary: '#35495e',
|
||||||
|
secondary: '#3e556d',
|
||||||
|
secondary_lighter: '#56718c',
|
||||||
|
blue_accent: '#3cd1c2',
|
||||||
|
info: '#ffaa2c',
|
||||||
|
error: '#f83e70',
|
||||||
|
green_accent: '#3cd1c2',
|
||||||
|
download: '#64CEAA',
|
||||||
|
upload: '#00b3fa',
|
||||||
|
background: '#0000'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -62,7 +62,7 @@ class Qbit {
|
||||||
} else {
|
} else {
|
||||||
data = new URLSearchParams(params)
|
data = new URLSearchParams(params)
|
||||||
}
|
}
|
||||||
return this.axios.post('/torrents/add', data).then(Api.handleResponse)
|
return this.axios.post('/torrents/add', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
switchToOldUi() {
|
switchToOldUi() {
|
||||||
|
@ -90,9 +90,7 @@ class Qbit {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = new URLSearchParams(params)
|
const data = new URLSearchParams(params)
|
||||||
return this.axios
|
return this.axios.post('/torrents/filePrio', data)
|
||||||
.post('/torrents/filePrio', data)
|
|
||||||
.then(Api.handleResponse)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getLogs(lastId) {
|
getLogs(lastId) {
|
||||||
|
@ -100,11 +98,9 @@ class Qbit {
|
||||||
last_known_id: lastId
|
last_known_id: lastId
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.axios
|
return this.axios.get('/log/main', {
|
||||||
.get('/log/main', {
|
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
.then(Api.handleResponse)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSpeedLimitsMode() {
|
toggleSpeedLimitsMode() {
|
||||||
|
@ -140,11 +136,9 @@ class Qbit {
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.axios
|
return this.axios.get('/torrents/trackers', {
|
||||||
.get('/torrents/trackers', {
|
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
.then(Api.handleResponse)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getTorrentPeers(hash, rid) {
|
getTorrentPeers(hash, rid) {
|
||||||
|
@ -153,11 +147,9 @@ class Qbit {
|
||||||
rid
|
rid
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.axios
|
return this.axios.get('/sync/torrentPeers', {
|
||||||
.get('/sync/torrentPeers', {
|
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
.then(Api.handleResponse)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
editTracker(hash, origUrl, newUrl) {
|
editTracker(hash, origUrl, newUrl) {
|
||||||
|
@ -173,11 +165,9 @@ class Qbit {
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.axios
|
return this.axios.get('/torrents/properties', {
|
||||||
.get('/torrents/properties', {
|
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
.then(Api.handleResponse)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getTorrentPieceStates(hash) {
|
getTorrentPieceStates(hash) {
|
||||||
|
@ -185,11 +175,9 @@ class Qbit {
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.axios
|
return this.axios.get('/torrents/pieceStates', {
|
||||||
.get('/torrents/pieceStates', {
|
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
.then(Api.handleResponse)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getTorrentFiles(hash) {
|
getTorrentFiles(hash) {
|
||||||
|
@ -197,11 +185,9 @@ class Qbit {
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.axios
|
return this.axios.get('/torrents/files', {
|
||||||
.get('/torrents/files', {
|
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
.then(Api.handleResponse)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getRssItems() {
|
getRssItems() {
|
||||||
|
@ -209,11 +195,9 @@ class Qbit {
|
||||||
withData: true
|
withData: true
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.axios
|
return this.axios.get('/rss/items', {
|
||||||
.get('/rss/items', {
|
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
.then(Api.handleResponse)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addRssFeed(url, path = '') {
|
addRssFeed(url, path = '') {
|
||||||
|
@ -223,7 +207,7 @@ class Qbit {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = new URLSearchParams(params)
|
const data = new URLSearchParams(params)
|
||||||
return this.axios.post('/rss/addFeed', data).then(Api.handleResponse)
|
return this.axios.post('/rss/addFeed', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRssFeed(path) {
|
removeRssFeed(path) {
|
||||||
|
@ -232,7 +216,7 @@ class Qbit {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = new URLSearchParams(params)
|
const data = new URLSearchParams(params)
|
||||||
return this.axios.post('/rss/removeItem', data).then(Api.handleResponse)
|
return this.axios.post('/rss/removeItem', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshRssFeed(path) {
|
refreshRssFeed(path) {
|
||||||
|
@ -241,9 +225,7 @@ class Qbit {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = new URLSearchParams(params)
|
const data = new URLSearchParams(params)
|
||||||
return this.axios
|
return this.axios.post('/rss/refreshItem', data)
|
||||||
.post('/rss/refreshItem', data)
|
|
||||||
.then(Api.handleResponse)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
moveRssFeed(path, newPath) {
|
moveRssFeed(path, newPath) {
|
||||||
|
@ -253,11 +235,11 @@ class Qbit {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = new URLSearchParams(params)
|
const data = new URLSearchParams(params)
|
||||||
return this.axios.post('/rss/moveItem', data).then(Api.handleResponse)
|
return this.axios.post('/rss/moveItem', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
getRssRules() {
|
getRssRules() {
|
||||||
return this.axios.get('/rss/rules').then(Api.handleResponse)
|
return this.axios.get('/rss/rules')
|
||||||
}
|
}
|
||||||
|
|
||||||
setRssRule(name, def) {
|
setRssRule(name, def) {
|
||||||
|
@ -267,7 +249,7 @@ class Qbit {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = new URLSearchParams(params)
|
const data = new URLSearchParams(params)
|
||||||
return this.axios.post('/rss/setRule', data).then(Api.handleResponse)
|
return this.axios.post('/rss/setRule', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRssRule(name) {
|
removeRssRule(name) {
|
||||||
|
@ -276,7 +258,7 @@ class Qbit {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = new URLSearchParams(params)
|
const data = new URLSearchParams(params)
|
||||||
return this.axios.post('/rss/removeRule', data).then(Api.handleResponse)
|
return this.axios.post('/rss/removeRule', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
actionTorrents(action, hashes, extra) {
|
actionTorrents(action, hashes, extra) {
|
||||||
|
@ -285,9 +267,7 @@ class Qbit {
|
||||||
...extra
|
...extra
|
||||||
}
|
}
|
||||||
const data = new URLSearchParams(params)
|
const data = new URLSearchParams(params)
|
||||||
return this.axios
|
return this.axios.post(`/torrents/${action}`, data)
|
||||||
.post(`/torrents/${action}`, data)
|
|
||||||
.then(Api.handleResponse)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
const axios = require('axios')
|
|
||||||
|
|
||||||
class Qbit {
|
|
||||||
constructor() {
|
|
||||||
this._axios = axios.create({
|
|
||||||
timeout: 1000
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAll(sort) {
|
|
||||||
const res = await this._axios.post('/all', sort)
|
|
||||||
return res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async get_sessions_stats() {
|
|
||||||
const res = await this._axios.get('/session')
|
|
||||||
return res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async pause_torrents(torrents) {
|
|
||||||
const res = await this._axios.post('/pause', torrents)
|
|
||||||
return res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async pause_all() {
|
|
||||||
const res = await this._axios.post('/pause_all')
|
|
||||||
return res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async resume_torrents(torrents) {
|
|
||||||
const res = await this._axios.post('/resume', torrents)
|
|
||||||
return res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async resume_all() {
|
|
||||||
const res = await this._axios.post('/resume_all')
|
|
||||||
return res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async add_torrent(torrent) {
|
|
||||||
const res = await this._axios.post('/add', torrent)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
async remove_torrents(torrents) {
|
|
||||||
const res = await this._axios.post('/remove', torrents)
|
|
||||||
return res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async login(credentials) {
|
|
||||||
let timeout = false
|
|
||||||
const res = await this._axios
|
|
||||||
.post('/login', credentials)
|
|
||||||
.catch(error => {
|
|
||||||
if (error.code === 'ECONNABORTED') timeout = true
|
|
||||||
else throw error
|
|
||||||
})
|
|
||||||
return timeout ? 'timeout' : res.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const qbit = new Qbit()
|
|
||||||
|
|
||||||
export default qbit
|
|
|
@ -1,15 +1,22 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
import { cloneDeep, merge, map, groupBy, sortBy } from 'lodash'
|
import VuexPersist from 'vuex-persist'
|
||||||
|
|
||||||
import Torrent from '../models/torrent'
|
import Torrent from '../models/torrent'
|
||||||
import Stat from '../models/sessionStat'
|
import Stat from '../models/sessionStat'
|
||||||
|
import qbit from '../services/qbit'
|
||||||
|
|
||||||
import qbit from './qbit'
|
const vuexPersist = new VuexPersist({
|
||||||
|
key: 'vuetorrent',
|
||||||
|
storage: window.localStorage
|
||||||
|
})
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
|
plugins: [vuexPersist.plugin],
|
||||||
state: {
|
state: {
|
||||||
|
darkTheme: false,
|
||||||
intervals: [],
|
intervals: [],
|
||||||
stats: null,
|
stats: null,
|
||||||
upload_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
upload_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
@ -22,16 +29,29 @@ export default new Vuex.Store({
|
||||||
rid: 0,
|
rid: 0,
|
||||||
mainData: undefined,
|
mainData: undefined,
|
||||||
preferences: null,
|
preferences: null,
|
||||||
pasteUrl: null
|
pasteUrl: null,
|
||||||
|
modals: {
|
||||||
|
addmodal: false,
|
||||||
|
deletemodal: false,
|
||||||
|
settingsmodal: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
CONTAINS_TORRENT: state => hash =>
|
CONTAINS_TORRENT: state => hash =>
|
||||||
state.selected_torrents.includes(hash)
|
state.selected_torrents.includes(hash),
|
||||||
|
getTheme: state => () => state.darkTheme,
|
||||||
|
getModalState: state => name => state.modals[name.toLowerCase()]
|
||||||
},
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
REMOVE_INTERVALS: state => {
|
REMOVE_INTERVALS: state => {
|
||||||
state.intervals.forEach(el => clearInterval(el))
|
state.intervals.forEach(el => clearInterval(el))
|
||||||
},
|
},
|
||||||
|
TOGGLE_MODAL(state, modal) {
|
||||||
|
state.modals[modal.toLowerCase()] = !state.modals[
|
||||||
|
modal.toLowerCase()
|
||||||
|
]
|
||||||
|
},
|
||||||
ADD_SELECTED: (state, payload) => {
|
ADD_SELECTED: (state, payload) => {
|
||||||
state.selected_torrents.push(payload)
|
state.selected_torrents.push(payload)
|
||||||
},
|
},
|
||||||
|
@ -45,19 +65,17 @@ export default new Vuex.Store({
|
||||||
state.selected_torrents = []
|
state.selected_torrents = []
|
||||||
},
|
},
|
||||||
PAUSE_TORRENTS: async state => {
|
PAUSE_TORRENTS: async state => {
|
||||||
let res
|
|
||||||
if (state.selected_torrents.length === 0) {
|
if (state.selected_torrents.length === 0) {
|
||||||
res = await qbit.pause_all()
|
qbit.pause_all()
|
||||||
} else {
|
} else {
|
||||||
res = await qbit.pause_torrents(state.selected_torrents)
|
qbit.pause_torrents(state.selected_torrents)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RESUME_TORRENTS: async state => {
|
RESUME_TORRENTS: async state => {
|
||||||
let res
|
|
||||||
if (state.selected_torrents.length === 0) {
|
if (state.selected_torrents.length === 0) {
|
||||||
res = await qbit.resume_all()
|
await qbit.resume_all()
|
||||||
} else {
|
} else {
|
||||||
res = await qbit.resume_torrents(state.selected_torrents)
|
await qbit.resume_torrents(state.selected_torrents)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ADD_TORRENT: async (state, payload) => {
|
ADD_TORRENT: async (state, payload) => {
|
||||||
|
@ -78,7 +96,7 @@ export default new Vuex.Store({
|
||||||
},
|
},
|
||||||
REMOVE_TORRENTS: async state => {
|
REMOVE_TORRENTS: async state => {
|
||||||
if (state.selected_torrents.length !== 0) {
|
if (state.selected_torrents.length !== 0) {
|
||||||
const res = await qbit.remove_torrents(state.selected_torrents)
|
qbit.remove_torrents(state.selected_torrents)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
LOGIN: async (state, payload) => {
|
LOGIN: async (state, payload) => {
|
|
@ -126,7 +126,9 @@
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<v-chip
|
<v-chip
|
||||||
small
|
small
|
||||||
:class="`${torrent.state} white--text my-2 caption`"
|
:class="
|
||||||
|
`${torrent.state} white--text my-2 caption`
|
||||||
|
"
|
||||||
>{{ torrent.state }}</v-chip
|
>{{ torrent.state }}</v-chip
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -148,7 +150,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapMutations, mapGetters } from 'vuex'
|
import { mapState, mapMutations } from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
@ -249,8 +251,12 @@ export default {
|
||||||
|
|
||||||
this.$store.state.sort_options = { name, reverse }
|
this.$store.state.sort_options = { name, reverse }
|
||||||
},
|
},
|
||||||
selectTorrent(hash) {},
|
selectTorrent(hash) {
|
||||||
containsTorrent(hash) {},
|
return hash
|
||||||
|
},
|
||||||
|
containsTorrent(hash) {
|
||||||
|
return hash
|
||||||
|
},
|
||||||
resetSelected() {}
|
resetSelected() {}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
@ -262,7 +268,7 @@ export default {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
.project.done {
|
.project.done {
|
||||||
border-left: 4px solid #3cd1c2;
|
border-left: 4px solid #3cd1c2;
|
||||||
}
|
}
|
||||||
|
@ -291,4 +297,5 @@ export default {
|
||||||
.pointer {
|
.pointer {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Add table
Reference in a new issue