mirror of
https://git.mirrors.martin98.com/https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-09 23:59:01 +08:00
Sub-Store 1.0版本
1. 移除了所有基于关键词的节点操作,统一使用基于正则表达式的节点操作。 2. UI的大量改进。
This commit is contained in:
parent
4d1b80ff63
commit
2766e23aa0
@ -54,6 +54,8 @@ function service() {
|
||||
.get(getAllSubscriptions)
|
||||
.post(createSubscription);
|
||||
|
||||
$app.get("/api/sub/statistics/:name");
|
||||
|
||||
// collection API
|
||||
$app.route("/api/collection/:name")
|
||||
.get(getCollection)
|
||||
@ -1584,14 +1586,15 @@ var ProxyUtils = (function () {
|
||||
};
|
||||
}
|
||||
|
||||
// sort by keywords
|
||||
function KeywordSortOperator(keywords) {
|
||||
// sort by regex
|
||||
function RegexSortOperator(expressions) {
|
||||
expressions = expressions.map(expr => new RegExp(expr));
|
||||
return {
|
||||
name: "Keyword Sort Operator",
|
||||
name: "Regex Sort Operator",
|
||||
func: (proxies) =>
|
||||
proxies.sort((a, b) => {
|
||||
const oA = getKeywordOrder(keywords, a.name);
|
||||
const oB = getKeywordOrder(keywords, b.name);
|
||||
const oA = getRegexOrder(expressions, a.name);
|
||||
const oB = getRegexOrder(expressions, b.name);
|
||||
if (oA && !oB) return -1;
|
||||
if (oB && !oA) return 1;
|
||||
if (oA && oB) return oA < oB ? -1 : 1;
|
||||
@ -1601,10 +1604,10 @@ var ProxyUtils = (function () {
|
||||
};
|
||||
}
|
||||
|
||||
function getKeywordOrder(keywords, str) {
|
||||
function getRegexOrder(expressions, str) {
|
||||
let order = null;
|
||||
for (let i = 0; i < keywords.length; i++) {
|
||||
if (str.indexOf(keywords[i]) !== -1) {
|
||||
for (let i = 0; i < expressions.length; i++) {
|
||||
if (expressions[i].test(str)) {
|
||||
order = i + 1; // plus 1 is important! 0 will be treated as false!!!
|
||||
break;
|
||||
}
|
||||
@ -1612,22 +1615,6 @@ var ProxyUtils = (function () {
|
||||
return order;
|
||||
}
|
||||
|
||||
// rename by keywords
|
||||
// keywords: [{old: "old", now: "now"}]
|
||||
function KeywordRenameOperator(keywords) {
|
||||
return {
|
||||
name: "Keyword Rename Operator",
|
||||
func: (proxies) => {
|
||||
return proxies.map((proxy) => {
|
||||
for (const {old, now} of keywords) {
|
||||
proxy.name = proxy.name.replaceAll(old, now).trim();
|
||||
}
|
||||
return proxy;
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// rename by regex
|
||||
// keywords: [{expr: "string format regex", now: "now"}]
|
||||
function RegexRenameOperator(regex) {
|
||||
@ -1644,21 +1631,6 @@ var ProxyUtils = (function () {
|
||||
};
|
||||
}
|
||||
|
||||
// delete keywords operator
|
||||
// keywords: ['a', 'b', 'c']
|
||||
function KeywordDeleteOperator(keywords) {
|
||||
const keywords_ = keywords.map((k) => {
|
||||
return {
|
||||
old: k,
|
||||
now: "",
|
||||
};
|
||||
});
|
||||
return {
|
||||
name: "Keyword Delete Operator",
|
||||
func: KeywordRenameOperator(keywords_).func,
|
||||
};
|
||||
}
|
||||
|
||||
// delete regex operator
|
||||
// regex: ['a', 'b', 'c']
|
||||
function RegexDeleteOperator(regex) {
|
||||
@ -1693,16 +1665,10 @@ var ProxyUtils = (function () {
|
||||
(function () {
|
||||
// interface to get internal operators
|
||||
const $get = (name, args) => {
|
||||
const item = AVAILABLE_OPERATORS[name];
|
||||
const item = PROXY_PROCESSORS[name];
|
||||
return item(args);
|
||||
};
|
||||
const $process = (item, proxies) => {
|
||||
if (item.name.indexOf("Filter") !== -1) {
|
||||
return ApplyOperator(item, proxies);
|
||||
} else if (item.name.indexOf("Operator") !== -1) {
|
||||
return ApplyFilter(item, proxies);
|
||||
}
|
||||
};
|
||||
const $process = ApplyProcessor;
|
||||
eval(script);
|
||||
output = operator(proxies);
|
||||
})();
|
||||
@ -1712,19 +1678,6 @@ var ProxyUtils = (function () {
|
||||
}
|
||||
|
||||
/**************************** Filters ***************************************/
|
||||
// filter by keywords
|
||||
function KeywordFilter({keywords = [], keep = true}) {
|
||||
return {
|
||||
name: "Keyword Filter",
|
||||
func: (proxies) => {
|
||||
return proxies.map((proxy) => {
|
||||
const selected = keywords.some((k) => proxy.name.indexOf(k) !== -1);
|
||||
return keep ? selected : !selected;
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// filter useless proxies
|
||||
function UselessFilter() {
|
||||
const KEYWORDS = [
|
||||
@ -1738,8 +1691,8 @@ var ProxyUtils = (function () {
|
||||
];
|
||||
return {
|
||||
name: "Useless Filter",
|
||||
func: KeywordFilter({
|
||||
keywords: KEYWORDS,
|
||||
func: RegexFilter({
|
||||
regex: KEYWORDS,
|
||||
keep: false,
|
||||
}).func,
|
||||
};
|
||||
@ -2031,7 +1984,6 @@ var ProxyUtils = (function () {
|
||||
}
|
||||
|
||||
return {
|
||||
"Keyword Filter": KeywordFilter,
|
||||
"Useless Filter": UselessFilter,
|
||||
"Region Filter": RegionFilter,
|
||||
"Regex Filter": RegexFilter,
|
||||
@ -2041,9 +1993,7 @@ var ProxyUtils = (function () {
|
||||
"Set Property Operator": SetPropertyOperator,
|
||||
"Flag Operator": FlagOperator,
|
||||
"Sort Operator": SortOperator,
|
||||
"Keyword Sort Operator": KeywordSortOperator,
|
||||
"Keyword Rename Operator": KeywordRenameOperator,
|
||||
"Keyword Delete Operator": KeywordDeleteOperator,
|
||||
"Regex Sort Operator": RegexSortOperator,
|
||||
"Regex Rename Operator": RegexRenameOperator,
|
||||
"Regex Delete Operator": RegexDeleteOperator,
|
||||
"Script Operator": ScriptOperator,
|
||||
|
4
backend/sub-store.min.js
vendored
4
backend/sub-store.min.js
vendored
File diff suppressed because one or more lines are too long
84
web/package-lock.json
generated
84
web/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -3291,6 +3291,11 @@
|
||||
"integrity": "sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=",
|
||||
"dev": true
|
||||
},
|
||||
"chartist": {
|
||||
"version": "0.11.4",
|
||||
"resolved": "https://registry.npmjs.org/chartist/-/chartist-0.11.4.tgz",
|
||||
"integrity": "sha512-H4AimxaUD738/u9Mq8t27J4lh6STsLi4BQHt65nOtpLk3xyrBPaLiLMrHw7/WV9CmsjGA02WihjuL5qpSagLYw=="
|
||||
},
|
||||
"check-types": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz",
|
||||
@ -3354,6 +3359,11 @@
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||
"dev": true
|
||||
},
|
||||
"chroma-js": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-1.4.1.tgz",
|
||||
"integrity": "sha512-jTwQiT859RTFN/vIf7s+Vl/Z2LcMrvMv3WUFmd/4u76AdlFC0NTNgqEEFPcRiHmAswPsMiQEDZLM8vX8qXpZNQ=="
|
||||
},
|
||||
"chrome-trace-event": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz",
|
||||
@ -4719,14 +4729,6 @@
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"echarts": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/echarts/-/echarts-4.9.0.tgz",
|
||||
"integrity": "sha512-+ugizgtJ+KmsJyyDPxaw2Br5FqzuBnyOWwcxPKO6y0gc5caYcfnEUIlNStx02necw8jmKmTafmpHhGo4XDtEIA==",
|
||||
"requires": {
|
||||
"zrender": "4.3.2"
|
||||
}
|
||||
},
|
||||
"ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@ -7139,7 +7141,8 @@
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.defaultsdeep": {
|
||||
"version": "4.6.1",
|
||||
@ -7542,20 +7545,6 @@
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"monaco-editor": {
|
||||
"version": "0.19.3",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.19.3.tgz",
|
||||
"integrity": "sha512-2n1vJBVQF2Hhi7+r1mMeYsmlf18hjVb6E0v5SoMZyb4aeOmYPKun+CE3gYpiNA1KEvtSdaDHFBqH9d7Wd9vREg=="
|
||||
},
|
||||
"monaco-editor-vue": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor-vue/-/monaco-editor-vue-1.0.10.tgz",
|
||||
"integrity": "sha512-FO6rioCXkonKmDZYB2WDhDNekXuNJgjxoAPfzwYX+5NTMaxRJcWl3bcUByRpL6LQQ1bTEYLZ+ZdDpoJ+la2trQ==",
|
||||
"requires": {
|
||||
"monaco-editor": "^0.19.3",
|
||||
"vue": "^2.6.11"
|
||||
}
|
||||
},
|
||||
"move-concurrently": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||
@ -9511,11 +9500,6 @@
|
||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
|
||||
"dev": true
|
||||
},
|
||||
"resize-detector": {
|
||||
"version": "0.1.10",
|
||||
"resolved": "https://registry.npmjs.org/resize-detector/-/resize-detector-0.1.10.tgz",
|
||||
"integrity": "sha512-iLcXC8A6Fb0DfA+TRiywrK/0A22bFqkhntjMJMEzXDA4XkcEkfwpNbv7W8iewUiD0xYIaeiXOfiEehTqGKsUFw=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.17.0.tgz?cache=0&sync_timestamp=1589682751623&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fresolve%2Fdownload%2Fresolve-1.17.0.tgz",
|
||||
@ -11245,6 +11229,11 @@
|
||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
|
||||
"dev": true
|
||||
},
|
||||
"vee-validate": {
|
||||
"version": "3.4.5",
|
||||
"resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-3.4.5.tgz",
|
||||
"integrity": "sha512-ZEcLqOAZzSkMhDvPcTx0xcwVOijFnMW9J+BA20j+rDmo24T8RCCqVQyRwwrDrcWJZV2dRYl/yYNa2GB6UCoBvg=="
|
||||
},
|
||||
"vendors": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz",
|
||||
@ -11273,6 +11262,14 @@
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz",
|
||||
"integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg=="
|
||||
},
|
||||
"vue-chartist": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-chartist/-/vue-chartist-2.3.1.tgz",
|
||||
"integrity": "sha512-DNM+eHBA/vebLChmPsWuNzOP0E38yDs6nvBq/VZPLsl+DjMUt9sDiQYAivOHDJ7ss5fCMMYyxRTXd6yZqSt5MQ==",
|
||||
"requires": {
|
||||
"chartist": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"vue-cli-plugin-vuetify": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npm.taobao.org/vue-cli-plugin-vuetify/download/vue-cli-plugin-vuetify-2.0.7.tgz",
|
||||
@ -11292,16 +11289,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-echarts": {
|
||||
"version": "5.0.0-beta.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-5.0.0-beta.0.tgz",
|
||||
"integrity": "sha512-QZFKGXDAYFQo+F20REpzcdLx79nsl4kOorJRpN+08aYq4YiIlmtWss1Lxadm7Fo+NYyWm8nnT+h4xHv3uqWIDQ==",
|
||||
"requires": {
|
||||
"core-js": "^3.4.4",
|
||||
"lodash": "^4.17.15",
|
||||
"resize-detector": "^0.1.10"
|
||||
}
|
||||
},
|
||||
"vue-eslint-parser": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npm.taobao.org/vue-eslint-parser/download/vue-eslint-parser-7.1.0.tgz?cache=0&sync_timestamp=1589684321779&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-eslint-parser%2Fdownload%2Fvue-eslint-parser-7.1.0.tgz",
|
||||
@ -11334,6 +11321,11 @@
|
||||
"integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==",
|
||||
"dev": true
|
||||
},
|
||||
"vue-i18n": {
|
||||
"version": "8.22.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.22.2.tgz",
|
||||
"integrity": "sha512-rb569fVJInPUgS/bbCxEQ9DrAoFTntuJvYoK4Fpk2VfNbA09WzdTKk57ppjz3S+ps9hW+p9H+2ASgMvojedkow=="
|
||||
},
|
||||
"vue-loader": {
|
||||
"version": "15.9.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.3.tgz",
|
||||
@ -11403,6 +11395,15 @@
|
||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
||||
"dev": true
|
||||
},
|
||||
"vue-world-map": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-world-map/-/vue-world-map-0.1.1.tgz",
|
||||
"integrity": "sha1-ehy6G3er5n0JZFzE3TbUeoTEegI=",
|
||||
"requires": {
|
||||
"chroma-js": "^1.3.5",
|
||||
"vue": "^2.5.2"
|
||||
}
|
||||
},
|
||||
"vuetify": {
|
||||
"version": "2.3.10",
|
||||
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.3.10.tgz",
|
||||
@ -12289,11 +12290,6 @@
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"zrender": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/zrender/-/zrender-4.3.2.tgz",
|
||||
"integrity": "sha512-bIusJLS8c4DkIcdiK+s13HiQ/zjQQVgpNohtd8d94Y2DnJqgM1yjh/jpDb8DoL6hd7r8Awagw8e3qK/oLaWr3g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
@ -11,16 +11,17 @@
|
||||
"dependencies": {
|
||||
"@dzangolab/vue-country-flag-icon": "^0.2.0",
|
||||
"axios": "^0.20.0",
|
||||
"chartist": "^0.11.4",
|
||||
"core-js": "^3.6.5",
|
||||
"echarts": "^4.9.0",
|
||||
"lodash": "^4.17.20",
|
||||
"material-design-icons-iconfont": "^5.0.1",
|
||||
"monaco-editor-vue": "^1.0.10",
|
||||
"v-clipboard": "^2.2.3",
|
||||
"vee-validate": "^3.4.5",
|
||||
"vue": "^2.6.12",
|
||||
"vue-echarts": "^5.0.0-beta.0",
|
||||
"vue-chartist": "^2.3.1",
|
||||
"vue-i18n": "^8.22.2",
|
||||
"vue-qrcode-component": "^2.1.1",
|
||||
"vue-router": "^3.4.3",
|
||||
"vue-world-map": "^0.1.1",
|
||||
"vuetify": "^2.3.10",
|
||||
"vuex": "^3.5.1"
|
||||
},
|
||||
|
41
web/public/404.html
Normal file
41
web/public/404.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Vuetify Md Pro | 404</title>
|
||||
<script type="text/javascript">
|
||||
// Single Page Apps for GitHub Pages
|
||||
// https://github.com/rafrex/spa-github-pages
|
||||
// Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
|
||||
// ----------------------------------------------------------------------
|
||||
// This script takes the current url and converts the path and query
|
||||
// string into just a query string, and then redirects the browser
|
||||
// to the new url with only a query string and hash fragment,
|
||||
// e.g. http://www.foo.tld/one/two?a=b&c=d#qwe, becomes
|
||||
// http://www.foo.tld/?p=/one/two&q=a=b~and~c=d#qwe
|
||||
// Note: this 404.html file must be at least 512 bytes for it to work
|
||||
// with Internet Explorer (it is currently > 512 bytes)
|
||||
|
||||
// If you're creating a Project Pages site and NOT using a custom domain,
|
||||
// then set segmentCount to 1 (enterprise users may need to set it to > 1).
|
||||
// This way the code will only replace the route part of the path, and not
|
||||
// the real directory in which the app resides, for example:
|
||||
// https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes
|
||||
// https://username.github.io/repo-name/?p=/one/two&q=a=b~and~c=d#qwe
|
||||
// Otherwise, leave segmentCount as 0.
|
||||
var segmentCount = 1;
|
||||
|
||||
var l = window.location;
|
||||
l.replace(
|
||||
l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
|
||||
l.pathname.split('/').slice(0, 1 + segmentCount).join('/') + '/?p=/' +
|
||||
l.pathname.slice(1).split('/').slice(segmentCount).join('/').replace(/&/g, '~and~') +
|
||||
(l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +
|
||||
l.hash
|
||||
);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<TopToolbar></TopToolbar>
|
||||
<v-content>
|
||||
<v-main>
|
||||
<router-view></router-view>
|
||||
</v-content>
|
||||
</v-main>
|
||||
<BottomNav></BottomNav>
|
||||
<v-snackbar
|
||||
:value="successMessage"
|
||||
@ -28,6 +28,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import TopToolbar from "@/components/TopToolbar";
|
||||
import BottomNav from "@/components/BottomNav";
|
||||
import {showError} from "@/utils";
|
||||
@ -52,10 +53,6 @@ export default {
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$vuetify.theme.dark = true;
|
||||
this.$vuetify.theme.themes.dark.primary = '#0899ab';
|
||||
this.$vuetify.theme.themes.light.primary = '#d73964';
|
||||
|
||||
initStore(this.$store);
|
||||
},
|
||||
|
||||
@ -82,6 +79,7 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@import "css/app.css";
|
||||
|
||||
<style lang="scss">
|
||||
@import "./assets/css/app";
|
||||
</style>
|
1
web/src/assets/clint-mckoy.jpg
Normal file
1
web/src/assets/clint-mckoy.jpg
Normal file
@ -0,0 +1 @@
|
||||
module.exports = __webpack_public_path__ + "img/clint-mckoy.36f95307.jpg";
|
1391
web/src/assets/css/app.css
Normal file
1391
web/src/assets/css/app.css
Normal file
File diff suppressed because it is too large
Load Diff
1525
web/src/assets/css/app.scss
Normal file
1525
web/src/assets/css/app.scss
Normal file
File diff suppressed because it is too large
Load Diff
1
web/src/assets/lock.jpg
Normal file
1
web/src/assets/lock.jpg
Normal file
@ -0,0 +1 @@
|
||||
module.exports = __webpack_public_path__ + "img/lock.9ae20e99.jpg";
|
1
web/src/assets/login.jpg
Normal file
1
web/src/assets/login.jpg
Normal file
@ -0,0 +1 @@
|
||||
module.exports = __webpack_public_path__ + "img/login.d6d3bb09.jpg";
|
Binary file not shown.
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 67 B |
@ -1,10 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style>
|
||||
</defs>
|
||||
<title>Artboard 46</title>
|
||||
<polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/>
|
||||
<polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/>
|
||||
<polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/>
|
||||
<polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/>
|
||||
</svg>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>
|
||||
|
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 539 B |
1
web/src/assets/pricing.jpg
Normal file
1
web/src/assets/pricing.jpg
Normal file
@ -0,0 +1 @@
|
||||
module.exports = __webpack_public_path__ + "img/pricing.f76b550f.jpg";
|
1
web/src/assets/register.jpg
Normal file
1
web/src/assets/register.jpg
Normal file
@ -0,0 +1 @@
|
||||
module.exports = __webpack_public_path__ + "img/register.85b37874.jpg";
|
1
web/src/assets/vuetify.svg
Normal file
1
web/src/assets/vuetify.svg
Normal file
@ -0,0 +1 @@
|
||||
module.exports = __webpack_public_path__ + "img/vuetify.31b0d032.svg";
|
@ -1,16 +1,11 @@
|
||||
<template>
|
||||
<v-bottom-navigation
|
||||
app
|
||||
color="primary"
|
||||
fixed
|
||||
grow
|
||||
color="primary"
|
||||
v-model="activeItem"
|
||||
>
|
||||
<!-- <v-btn :to="{path:'/dashboard'}" value="dashboard">-->
|
||||
<!-- <span>首页</span>-->
|
||||
<!-- <v-icon>speed</v-icon>-->
|
||||
<!-- </v-btn>-->
|
||||
|
||||
<v-btn :to="{path: '/'}" value="subscription">
|
||||
<span>订阅</span>
|
||||
<v-icon>mdi-cloud</v-icon>
|
||||
@ -20,8 +15,6 @@
|
||||
<span>我的</span>
|
||||
<v-icon>mdi-account</v-icon>
|
||||
</v-btn>
|
||||
|
||||
|
||||
</v-bottom-navigation>
|
||||
|
||||
</template>
|
||||
|
@ -1,109 +0,0 @@
|
||||
<template>
|
||||
<v-card class="ml-1 mr-1 mb-1 mt-1">
|
||||
<v-card-title>
|
||||
<v-icon left color="primary">filter_list</v-icon>
|
||||
关键词删除
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="$emit('up', idx)">
|
||||
<v-icon>keyboard_arrow_up</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="$emit('down', idx)">
|
||||
<v-icon>keyboard_arrow_down</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="$emit('deleteProcess', idx)">
|
||||
<v-icon color="error">mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
<v-dialog>
|
||||
<template #activator="{on}">
|
||||
<v-btn icon v-on="on">
|
||||
<v-icon>help</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-title class="headline">
|
||||
关键词删除
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
删除节点名中的关键词
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
关键词
|
||||
<v-chip-group>
|
||||
<v-chip
|
||||
close
|
||||
close-icon="mdi-delete"
|
||||
v-for="(keyword, idx) in keywords"
|
||||
:key="idx"
|
||||
@click:close="remove(idx)"
|
||||
@click="edit(idx)"
|
||||
>
|
||||
{{ keyword }}
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
<v-text-field
|
||||
placeholder="添加新关键词"
|
||||
clearable
|
||||
clear-icon="clear"
|
||||
solo
|
||||
v-model="form.keyword"
|
||||
append-icon="mdi-send"
|
||||
@click:append="add(form.keyword)"
|
||||
@keyup.enter="add(form.keyword)"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['args'],
|
||||
data: function () {
|
||||
return {
|
||||
idx: this.$vnode.key,
|
||||
mode: "IN",
|
||||
form: {
|
||||
keyword: ""
|
||||
},
|
||||
keywords: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add(keyword) {
|
||||
if (keyword) {
|
||||
this.keywords.push(keyword);
|
||||
this.form.keyword = "";
|
||||
} else {
|
||||
this.$store.commit("SET_ERROR_MESSAGE", "关键词不能为空!");
|
||||
}
|
||||
},
|
||||
edit(idx) {
|
||||
this.form.keyword = this.keywords[idx];
|
||||
this.remove(idx);
|
||||
},
|
||||
remove(idx) {
|
||||
this.keywords.splice(idx, 1);
|
||||
},
|
||||
save() {
|
||||
this.$emit("dataChanged", {
|
||||
idx: this.idx,
|
||||
args: this.keywords
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.keywords = this.args || [];
|
||||
},
|
||||
watch: {
|
||||
keywords() {
|
||||
this.save();
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,130 +0,0 @@
|
||||
<template>
|
||||
<v-card class="ml-1 mr-1 mb-1 mt-1">
|
||||
<v-card-title>
|
||||
<v-icon left color="primary">filter_list</v-icon>
|
||||
关键词过滤
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="$emit('up', idx)">
|
||||
<v-icon>keyboard_arrow_up</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="$emit('down', idx)">
|
||||
<v-icon>keyboard_arrow_down</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="$emit('deleteProcess', idx)">
|
||||
<v-icon color="error">mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
<v-dialog>
|
||||
<template #activator="{on}">
|
||||
<v-btn icon v-on="on">
|
||||
<v-icon>help</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-title class="headline">
|
||||
关键词过滤
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
根据关键词过滤节点。如果设置为保留模式,则含有关键词的节点会被保留,否则会被过滤。
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
工作模式
|
||||
<v-radio-group v-model="mode">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-radio label="保留模式" value="IN"/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-radio label="过滤模式" value="OUT"/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-radio-group>
|
||||
关键词
|
||||
<v-chip-group>
|
||||
<v-chip
|
||||
close
|
||||
close-icon="mdi-delete"
|
||||
v-for="(keyword, idx) in keywords"
|
||||
:key="idx"
|
||||
@click:close="remove(idx)"
|
||||
@click="edit(idx)"
|
||||
>
|
||||
{{ keyword }}
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
<v-text-field
|
||||
placeholder="添加新关键词"
|
||||
clearable
|
||||
clear-icon="clear"
|
||||
solo
|
||||
v-model="form.keyword"
|
||||
append-icon="mdi-send"
|
||||
@click:append="add(form.keyword)"
|
||||
@keyup.enter="add(form.keyword)"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['args'],
|
||||
data: function () {
|
||||
return {
|
||||
idx: this.$vnode.key,
|
||||
mode: "IN",
|
||||
form: {
|
||||
keyword: ""
|
||||
},
|
||||
keywords: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add(keyword) {
|
||||
if (keyword) {
|
||||
this.keywords.push(keyword);
|
||||
this.form.keyword = "";
|
||||
} else {
|
||||
this.$store.commit("SET_ERROR_MESSAGE", "关键词不能为空!");
|
||||
}
|
||||
},
|
||||
edit(idx) {
|
||||
this.form.keyword = this.keywords[idx];
|
||||
this.remove(idx);
|
||||
},
|
||||
remove(idx) {
|
||||
this.keywords.splice(idx, 1);
|
||||
},
|
||||
save() {
|
||||
this.$emit("dataChanged", {
|
||||
idx: this.idx,
|
||||
args: {
|
||||
keywords: this.keywords,
|
||||
keep: this.mode === 'IN'
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.args) {
|
||||
this.keywords = this.args.keywords || [];
|
||||
if (typeof this.args.keep !== 'undefined') this.mode = this.args.keep ? "IN" : "OUT";
|
||||
else this.mode = "IN";
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
mode() {
|
||||
this.save();
|
||||
},
|
||||
keywords() {
|
||||
this.save();
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,137 +0,0 @@
|
||||
<template>
|
||||
<v-card class="ml-1 mr-1 mb-1 mt-1">
|
||||
<v-card-title>
|
||||
<v-icon left color="primary">filter_list</v-icon>
|
||||
关键词重命名
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="$emit('up', idx)">
|
||||
<v-icon>keyboard_arrow_up</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="$emit('down', idx)">
|
||||
<v-icon>keyboard_arrow_down</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="$emit('deleteProcess', idx)">
|
||||
<v-icon color="error">mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
<v-dialog>
|
||||
<template #activator="{on}">
|
||||
<v-btn icon v-on="on">
|
||||
<v-icon>help</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-title class="headline">
|
||||
关键词重命名
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
替换节点名中的关键词。
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
关键词
|
||||
<v-chip-group>
|
||||
<v-chip
|
||||
close
|
||||
close-icon="mdi-delete"
|
||||
v-for="(chip, idx) in chips"
|
||||
:key="idx"
|
||||
@click:close="remove(idx)"
|
||||
@click="edit(idx)"
|
||||
>
|
||||
{{ chip }}
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
clearable
|
||||
clear-icon="clear"
|
||||
placeholder="关键词"
|
||||
solo
|
||||
v-model="form.keyword"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
clearable
|
||||
clear-icon="clear"
|
||||
placeholder="替换为"
|
||||
solo
|
||||
v-model="form.replace"
|
||||
append-icon="mdi-send"
|
||||
@click:append="add"
|
||||
@keyup.enter="add"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['args'],
|
||||
data: function () {
|
||||
return {
|
||||
idx: this.$vnode.key,
|
||||
mode: "IN",
|
||||
form: {
|
||||
keyword: "",
|
||||
replace: ""
|
||||
},
|
||||
keywords: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
chips() {
|
||||
return this.keywords.map(k => {
|
||||
const {old, now} = k;
|
||||
return `${old} ⇒ ${now.length === 0 ? "∅" : now}`;
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add() {
|
||||
if (this.form.keyword) {
|
||||
this.keywords.push({
|
||||
old: this.form.keyword,
|
||||
now: this.form.replace || ""
|
||||
});
|
||||
this.form.keyword = "";
|
||||
this.form.replace = "";
|
||||
} else {
|
||||
this.$store.commit("SET_ERROR_MESSAGE", "关键词不能为空!");
|
||||
}
|
||||
},
|
||||
edit(idx) {
|
||||
this.form.keyword = this.keywords[idx].old;
|
||||
this.form.replace = this.keywords[idx].now;
|
||||
this.remove(idx);
|
||||
},
|
||||
remove(idx) {
|
||||
this.keywords.splice(idx, 1);
|
||||
},
|
||||
save() {
|
||||
this.$emit("dataChanged", {
|
||||
idx: this.idx,
|
||||
args: this.keywords
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.keywords = this.args || [];
|
||||
},
|
||||
watch: {
|
||||
keywords() {
|
||||
this.save();
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -44,9 +44,7 @@
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
>
|
||||
<v-card
|
||||
color="primary darken-1"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
{{ info.name }}
|
||||
</v-card-title>
|
||||
@ -63,15 +61,11 @@
|
||||
<v-dialog
|
||||
v-model="showQR"
|
||||
>
|
||||
<v-card
|
||||
color="primary darken-1"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
{{ info.name }}
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
icon
|
||||
color="white"
|
||||
@click="copyLink()"
|
||||
>
|
||||
<v-icon>content_copy</v-icon>
|
||||
|
@ -34,7 +34,9 @@
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
正则表达式
|
||||
<v-chip-group>
|
||||
<v-chip-group
|
||||
column
|
||||
>
|
||||
<v-chip
|
||||
close
|
||||
close-icon="mdi-delete"
|
||||
|
@ -46,7 +46,9 @@
|
||||
</v-row>
|
||||
</v-radio-group>
|
||||
正则表达式
|
||||
<v-chip-group>
|
||||
<v-chip-group
|
||||
column
|
||||
>
|
||||
<v-chip
|
||||
close
|
||||
close-icon="mdi-delete"
|
||||
|
@ -31,7 +31,9 @@
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
正则表达式
|
||||
<v-chip-group>
|
||||
<v-chip-group
|
||||
column
|
||||
>
|
||||
<v-chip
|
||||
close
|
||||
close-icon="mdi-delete"
|
||||
|
@ -2,7 +2,7 @@
|
||||
<v-card class="ml-1 mr-1 mb-1 mt-1">
|
||||
<v-card-title>
|
||||
<v-icon left color="primary">sort</v-icon>
|
||||
关键词排序
|
||||
正则排序
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="$emit('up', idx)">
|
||||
<v-icon>keyboard_arrow_up</v-icon>
|
||||
@ -21,38 +21,39 @@
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-title class="headline">
|
||||
关键词排序
|
||||
正则排序
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
根据给出的关键词的顺序对节点进行排序,没有出现的关键词将会按照正序排列。
|
||||
根据给出的正则表达式的顺序对节点进行排序,无法匹配的节点将会按照正序排列。
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
关键词
|
||||
<v-chip-group column
|
||||
正则表达式
|
||||
<v-chip-group
|
||||
column
|
||||
>
|
||||
<v-chip
|
||||
close
|
||||
close-icon="mdi-delete"
|
||||
v-for="(keyword, idx) in keywords"
|
||||
v-for="(item, idx) in items"
|
||||
:key="idx"
|
||||
@click="edit(idx)"
|
||||
@click:close="remove(idx)"
|
||||
>
|
||||
{{ keyword }}
|
||||
{{ item }}
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
<v-text-field
|
||||
placeholder="添加新关键词"
|
||||
placeholder="添加新正则表达式"
|
||||
clearable
|
||||
clear-icon="clear"
|
||||
solo
|
||||
v-model="form.keyword"
|
||||
v-model="form.item"
|
||||
append-icon="mdi-send"
|
||||
@click:append="add(form.keyword)"
|
||||
@keyup.enter="add(form.keyword)"
|
||||
@click:append="add(form.item)"
|
||||
@keyup.enter="add(form.item)"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
@ -67,41 +68,41 @@ export default {
|
||||
selection: null,
|
||||
currentTag: null,
|
||||
form: {
|
||||
keyword: ""
|
||||
item: ""
|
||||
},
|
||||
keywords: []
|
||||
items: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.args) {
|
||||
this.keywords = this.args || [];
|
||||
this.items = this.args || [];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
keywords() {
|
||||
items() {
|
||||
this.save();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
add(keyword) {
|
||||
if (keyword) {
|
||||
this.keywords.push(keyword);
|
||||
this.form.keyword = "";
|
||||
add(item) {
|
||||
if (item) {
|
||||
this.items.push(item);
|
||||
this.form.item = "";
|
||||
} else {
|
||||
this.$store.commit("SET_ERROR_MESSAGE", "关键词不能为空!");
|
||||
this.$store.commit("SET_ERROR_MESSAGE", "正则表达式不能为空!");
|
||||
}
|
||||
},
|
||||
edit(idx) {
|
||||
this.form.keyword = this.keywords[idx];
|
||||
this.form.item = this.items[idx];
|
||||
this.remove(idx);
|
||||
},
|
||||
remove(idx) {
|
||||
this.keywords.splice(idx, 1);
|
||||
this.items.splice(idx, 1);
|
||||
},
|
||||
save() {
|
||||
this.$emit("dataChanged", {
|
||||
idx: this.idx,
|
||||
args: this.keywords
|
||||
args: this.items
|
||||
});
|
||||
},
|
||||
}
|
@ -41,14 +41,15 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-radio-group>
|
||||
<v-textarea
|
||||
solo
|
||||
clearable
|
||||
auto-grow
|
||||
clear-icon="clear"
|
||||
:label="hint"
|
||||
v-model="content"
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-textarea
|
||||
solo
|
||||
clearable
|
||||
clear-icon="clear"
|
||||
:label="hint"
|
||||
v-model="content"
|
||||
/>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
|
@ -41,14 +41,15 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-radio-group>
|
||||
<v-textarea
|
||||
clearable
|
||||
clear-icon="clear"
|
||||
solo
|
||||
auto-grow
|
||||
:label="hint"
|
||||
v-model="content"
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-textarea
|
||||
clearable
|
||||
clear-icon="clear"
|
||||
solo
|
||||
:label="hint"
|
||||
v-model="content"
|
||||
/>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
fab
|
||||
>
|
||||
<v-icon v-if="opened">mdi-close</v-icon>
|
||||
<v-icon v-else>apps</v-icon>
|
||||
<v-icon v-else>mdi-plus</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-btn
|
||||
|
@ -1,41 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-navigation-drawer
|
||||
app
|
||||
fixed
|
||||
v-model="showMenu"
|
||||
>
|
||||
<v-list dense>
|
||||
<v-list-item @click="doNothing">
|
||||
<v-list-item-action>
|
||||
<v-icon>settings</v-icon>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Settings</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item @click="doNothing">
|
||||
<v-list-item-action>
|
||||
<v-icon>help</v-icon>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Help</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
|
||||
<v-app-bar
|
||||
app
|
||||
color="primary"
|
||||
dark
|
||||
fixed
|
||||
:mini-variant="false" :clipped="true"
|
||||
>
|
||||
<v-app-bar-nav-icon @click.stop="toggleMenu"></v-app-bar-nav-icon>
|
||||
|
||||
<v-toolbar-title>{{title}}</v-toolbar-title>
|
||||
|
||||
<v-toolbar-title><h3>{{title}}</h3></v-toolbar-title>
|
||||
</v-app-bar>
|
||||
|
||||
</div>
|
||||
@ -50,12 +20,6 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleMenu: function () {
|
||||
this.showMenu = !this.showMenu;
|
||||
},
|
||||
doNothing: function () {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
9
web/src/components/base/Card.vue
Normal file
9
web/src/components/base/Card.vue
Normal file
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
import { VCard } from 'vuetify/lib'
|
||||
|
||||
export default {
|
||||
name: 'Card',
|
||||
|
||||
extends: VCard
|
||||
}
|
||||
</script>
|
69
web/src/components/base/Item.vue
Normal file
69
web/src/components/base/Item.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<v-list-item
|
||||
:href="href"
|
||||
:rel="href && href !== '#' ? 'noopener' : undefined"
|
||||
:target="href && href !== '#' ? '_blank' : undefined"
|
||||
:to="item.to"
|
||||
:active-class="`primary ${!isDark ? 'black' : 'white'}--text`"
|
||||
>
|
||||
<v-list-item-icon
|
||||
v-if="text"
|
||||
class="v-list-item__icon--text"
|
||||
v-text="computedText"
|
||||
/>
|
||||
|
||||
<v-list-item-icon v-else-if="item.icon">
|
||||
<v-icon v-text="item.icon" />
|
||||
</v-list-item-icon>
|
||||
|
||||
<v-list-item-content v-if="item.title || item.subtitle">
|
||||
<v-list-item-title v-text="item.title" />
|
||||
|
||||
<v-list-item-subtitle v-text="item.subtitle" />
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Themeable from 'vuetify/lib/mixins/themeable'
|
||||
|
||||
export default {
|
||||
name: 'Item',
|
||||
|
||||
mixins: [Themeable],
|
||||
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
href: undefined,
|
||||
icon: undefined,
|
||||
subtitle: undefined,
|
||||
title: undefined,
|
||||
to: undefined
|
||||
})
|
||||
},
|
||||
text: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
computedText() {
|
||||
if (!this.item || !this.item.title) return ''
|
||||
|
||||
let text = ''
|
||||
|
||||
this.item.title.split(' ').forEach(val => {
|
||||
text += val.substring(0, 1)
|
||||
})
|
||||
|
||||
return text
|
||||
},
|
||||
href() {
|
||||
return this.item.href || (!this.item.to ? '#' : undefined)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
123
web/src/components/base/ItemGroup.vue
Normal file
123
web/src/components/base/ItemGroup.vue
Normal file
@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<v-list-group
|
||||
:group="group"
|
||||
:prepend-icon="item.icon"
|
||||
:sub-group="subGroup"
|
||||
append-icon="mdi-menu-down"
|
||||
:color="barColor !== 'rgba(255, 255, 255, 1), rgba(255, 255, 255, 0.7)' ? 'white' : 'grey darken-1'"
|
||||
>
|
||||
<template v-slot:activator>
|
||||
<v-list-item-icon
|
||||
v-if="text"
|
||||
class="v-list-item__icon--text"
|
||||
v-text="computedText"
|
||||
/>
|
||||
|
||||
<v-list-item-avatar
|
||||
v-else-if="item.avatar"
|
||||
class="align-self-center"
|
||||
color="grey"
|
||||
>
|
||||
<v-img src="https://demos.creative-tim.com/material-dashboard-pro/assets/img/faces/avatar.jpg" />
|
||||
</v-list-item-avatar>
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-title v-text="item.title" />
|
||||
</v-list-item-content>
|
||||
</template>
|
||||
|
||||
<template v-for="(child, i) in children">
|
||||
<base-item-sub-group
|
||||
v-if="child.children"
|
||||
:key="`sub-group-${i}`"
|
||||
:item="child"
|
||||
/>
|
||||
|
||||
<base-item
|
||||
v-else
|
||||
:key="`item-${i}`"
|
||||
:item="child"
|
||||
text
|
||||
/>
|
||||
</template>
|
||||
</v-list-group>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Utilities
|
||||
import kebabCase from 'lodash/kebabCase'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'ItemGroup',
|
||||
|
||||
inheritAttrs: false,
|
||||
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
avatar: undefined,
|
||||
group: undefined,
|
||||
title: undefined,
|
||||
children: []
|
||||
})
|
||||
},
|
||||
subGroup: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
text: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['barColor']),
|
||||
children() {
|
||||
return this.item.children.map(item => ({
|
||||
...item,
|
||||
to: !item.to ? undefined : `${this.item.group}/${item.to}`
|
||||
}))
|
||||
},
|
||||
computedText() {
|
||||
if (!this.item || !this.item.title) return ''
|
||||
|
||||
let text = ''
|
||||
|
||||
this.item.title.split(' ').forEach(val => {
|
||||
text += val.substring(0, 1)
|
||||
})
|
||||
|
||||
return text
|
||||
},
|
||||
group() {
|
||||
return this.genGroup(this.item.children)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
genGroup(children) {
|
||||
return children
|
||||
.filter(item => item.to)
|
||||
.map(item => {
|
||||
const parent = item.group || this.item.group
|
||||
let group = `${parent}/${kebabCase(item.to)}`
|
||||
|
||||
if (item.children) {
|
||||
group = `${group}|${this.genGroup(item.children)}`
|
||||
}
|
||||
|
||||
return group
|
||||
}).join('|')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.v-list-group__activator p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
25
web/src/components/base/ItemSubGroup.vue
Normal file
25
web/src/components/base/ItemSubGroup.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<base-item-group
|
||||
:item="item"
|
||||
text
|
||||
sub-group
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ItemSubGroup',
|
||||
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
avatar: undefined,
|
||||
group: undefined,
|
||||
title: undefined,
|
||||
children: []
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
64
web/src/components/base/MaterialAlert.vue
Normal file
64
web/src/components/base/MaterialAlert.vue
Normal file
@ -0,0 +1,64 @@
|
||||
<script>
|
||||
// Components
|
||||
import { VAlert, VBtn, VIcon } from 'vuetify/lib'
|
||||
|
||||
export default {
|
||||
name: 'MaterialAlert',
|
||||
|
||||
extends: VAlert,
|
||||
|
||||
computed: {
|
||||
__cachedDismissible() {
|
||||
if (!this.dismissible) return null
|
||||
|
||||
const color = 'white'
|
||||
|
||||
return this.$createElement(VBtn, {
|
||||
staticClass: 'v-alert__dismissible',
|
||||
props: {
|
||||
color,
|
||||
icon: true,
|
||||
small: true
|
||||
},
|
||||
attrs: {
|
||||
'aria-label': this.$vuetify.lang.t(this.closeLabel)
|
||||
},
|
||||
on: {
|
||||
// eslint-disable-next-line
|
||||
click: () => (this.isActive = false)
|
||||
}
|
||||
}, [
|
||||
this.$createElement(VIcon, {
|
||||
props: { color }
|
||||
}, '$vuetify.icons.cancel')
|
||||
])
|
||||
},
|
||||
classes() {
|
||||
return {
|
||||
...VAlert.options.computed.classes.call(this),
|
||||
'v-alert--material': true
|
||||
}
|
||||
},
|
||||
hasColoredIcon() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
|
||||
.v-alert--material
|
||||
margin-top: 32px
|
||||
|
||||
.v-alert__icon
|
||||
background-color: #FFFFFF
|
||||
height: 44px
|
||||
min-width: 44px
|
||||
top: -36px
|
||||
|
||||
.v-alert__dismissible
|
||||
align-self: flex-start
|
||||
margin: 0 !important
|
||||
padding: 0 !important
|
||||
</style>
|
168
web/src/components/base/MaterialCard.vue
Normal file
168
web/src/components/base/MaterialCard.vue
Normal file
@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<v-card
|
||||
v-bind="$attrs"
|
||||
:class="classes"
|
||||
class="v-card--material pa-3"
|
||||
>
|
||||
<div class="d-flex grow flex-wrap">
|
||||
<v-avatar
|
||||
v-if="avatar"
|
||||
size="128"
|
||||
class="mx-auto v-card--material__avatar elevation-12"
|
||||
color="grey"
|
||||
>
|
||||
<v-img :src="avatar" />
|
||||
</v-avatar>
|
||||
|
||||
<v-sheet
|
||||
v-else
|
||||
:class="{
|
||||
'pa-7': !$slots.image
|
||||
}"
|
||||
:color="color"
|
||||
:max-height="icon ? 90 : undefined"
|
||||
:width="inline || icon ? 'auto' : '100%'"
|
||||
class="text-start v-card--material__heading mb-n6"
|
||||
dark
|
||||
>
|
||||
<slot
|
||||
v-if="$slots.heading"
|
||||
name="heading"
|
||||
/>
|
||||
|
||||
<slot
|
||||
v-else-if="$slots.image"
|
||||
name="image"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-else-if="title && !icon"
|
||||
class="display-1 font-weight-light"
|
||||
v-text="title"
|
||||
/>
|
||||
|
||||
<v-icon
|
||||
v-else-if="icon"
|
||||
size="32"
|
||||
v-text="icon"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="text"
|
||||
class="headline font-weight-thin"
|
||||
v-text="text"
|
||||
/>
|
||||
</v-sheet>
|
||||
|
||||
<div
|
||||
v-if="$slots['after-heading']"
|
||||
class="ml-6"
|
||||
>
|
||||
<slot name="after-heading" />
|
||||
</div>
|
||||
|
||||
<v-col
|
||||
v-if="hoverReveal"
|
||||
cols="12"
|
||||
class="text-center py-0 mt-n12"
|
||||
>
|
||||
<slot name="reveal-actions" />
|
||||
</v-col>
|
||||
|
||||
<div
|
||||
v-else-if="icon && title"
|
||||
class="ml-4"
|
||||
>
|
||||
<div
|
||||
|
||||
class="card-title font-weight-light"
|
||||
v-text="title"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<slot />
|
||||
|
||||
<template v-if="$slots.actions">
|
||||
<v-divider class="mt-2" />
|
||||
|
||||
<v-card-actions class="pb-0">
|
||||
<slot name="actions" />
|
||||
</v-card-actions>
|
||||
</template>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'MaterialCard',
|
||||
|
||||
props: {
|
||||
avatar: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: 'success'
|
||||
},
|
||||
hoverReveal: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
image: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
classes() {
|
||||
return {
|
||||
'v-card--material--has-heading': this.hasHeading,
|
||||
'v-card--material--hover-reveal': this.hoverReveal
|
||||
}
|
||||
},
|
||||
hasHeading() {
|
||||
return Boolean(this.$slots.heading || this.title || this.icon)
|
||||
},
|
||||
hasAltHeading() {
|
||||
return Boolean(this.$slots.heading || (this.title && this.icon))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.v-card--material
|
||||
&__avatar
|
||||
position: relative
|
||||
top: -64px
|
||||
margin-bottom: -32px
|
||||
|
||||
&__heading
|
||||
position: relative
|
||||
top: -40px
|
||||
transition: .3s ease
|
||||
z-index: 1
|
||||
|
||||
&.v-card--material--hover-reveal:hover
|
||||
.v-card--material__heading
|
||||
transform: translateY(-40px)
|
||||
</style>
|
95
web/src/components/base/MaterialChartCard.vue
Normal file
95
web/src/components/base/MaterialChartCard.vue
Normal file
@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<base-material-card
|
||||
class="v-card--material-chart"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<template v-slot:heading>
|
||||
<chartist
|
||||
:data="data"
|
||||
:event-handlers="eventHandlers"
|
||||
:options="options"
|
||||
:ratio="ratio"
|
||||
:responsive-options="responsiveOptions"
|
||||
:type="type"
|
||||
style="max-height: 150px;"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<slot
|
||||
slot="reveal-actions"
|
||||
name="reveal-actions"
|
||||
/>
|
||||
|
||||
<slot />
|
||||
|
||||
<slot
|
||||
slot="actions"
|
||||
name="actions"
|
||||
/>
|
||||
</base-material-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'MaterialChartCard',
|
||||
|
||||
inheritAttrs: false,
|
||||
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
eventHandlers: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
ratio: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
responsiveOptions: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator: v => ['Bar', 'Line', 'Pie'].includes(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.v-card--material-chart
|
||||
p
|
||||
color: #999
|
||||
|
||||
.v-card--material__heading
|
||||
max-height: 185px
|
||||
|
||||
.ct-label
|
||||
color: inherit
|
||||
opacity: .7
|
||||
font-size: 0.975rem
|
||||
font-weight: 100
|
||||
|
||||
.ct-grid
|
||||
stroke: rgba(255, 255, 255, 0.2)
|
||||
|
||||
.ct-series-a .ct-point,
|
||||
.ct-series-a .ct-line,
|
||||
.ct-series-a .ct-bar,
|
||||
.ct-series-a .ct-slice-donut
|
||||
stroke: rgba(255,255,255,.8)
|
||||
|
||||
.ct-series-a .ct-slice-pie,
|
||||
.ct-series-a .ct-area
|
||||
fill: rgba(255,255,255,.4)
|
||||
</style>
|
70
web/src/components/base/MaterialDropdown.vue
Normal file
70
web/src/components/base/MaterialDropdown.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<v-menu
|
||||
v-model="value"
|
||||
:transition="transition"
|
||||
offset-y
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<template v-slot:activator="{ attrs, on }">
|
||||
<v-btn
|
||||
:color="color"
|
||||
default
|
||||
min-width="200"
|
||||
rounded
|
||||
v-bind="attrs"
|
||||
v-on="on"
|
||||
>
|
||||
<slot />
|
||||
|
||||
<v-icon>
|
||||
mdi-{{ value ? 'menu-up' : 'menu-down' }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<v-sheet>
|
||||
<v-list dense>
|
||||
<v-list-item
|
||||
v-for="(item, i) in items"
|
||||
:key="i"
|
||||
@click="$(`click:action-${item.id}`)"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title v-text="item.text" />
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-sheet>
|
||||
</v-menu>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Mixins
|
||||
import Proxyable from 'vuetify/lib/mixins/proxyable'
|
||||
|
||||
export default {
|
||||
name: 'MaterialDropdown',
|
||||
|
||||
mixins: [Proxyable],
|
||||
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: 'primary'
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => ([
|
||||
{
|
||||
id: undefined,
|
||||
text: undefined
|
||||
}
|
||||
])
|
||||
},
|
||||
transition: {
|
||||
type: String,
|
||||
default: 'scale-transition'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
66
web/src/components/base/MaterialSnackbar.vue
Normal file
66
web/src/components/base/MaterialSnackbar.vue
Normal file
@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<v-snackbar
|
||||
:class="classes"
|
||||
:value="value"
|
||||
v-bind="{
|
||||
...$attrs,
|
||||
...$props,
|
||||
'color': 'transparent'
|
||||
}"
|
||||
@change="$emit('change', $event)"
|
||||
>
|
||||
<base-material-alert
|
||||
:color="color"
|
||||
:dismissible="dismissible"
|
||||
:type="type"
|
||||
class="ma-0"
|
||||
dark
|
||||
>
|
||||
<slot />
|
||||
</base-material-alert>
|
||||
</v-snackbar>
|
||||
</template>
|
||||
<script>
|
||||
// Components
|
||||
import { VSnackbar } from 'vuetify/lib'
|
||||
|
||||
export default {
|
||||
name: 'BaseMaterialSnackbar',
|
||||
|
||||
extends: VSnackbar,
|
||||
|
||||
props: {
|
||||
dismissible: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
classes() {
|
||||
return {
|
||||
...VSnackbar.options.computed.classes.call(this),
|
||||
'v-snackbar--material': true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.v-snackbar--material
|
||||
margin-top: 32px
|
||||
margin-bottom: 32px
|
||||
|
||||
.v-alert--material,
|
||||
.v-snack__wrapper
|
||||
border-radius: 4px
|
||||
|
||||
.v-snack__content
|
||||
overflow: visible
|
||||
padding: 0
|
||||
</style>
|
113
web/src/components/base/MaterialStatsCard.vue
Normal file
113
web/src/components/base/MaterialStatsCard.vue
Normal file
@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<base-material-card
|
||||
:icon="icon"
|
||||
class="v-card--material-stats"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<template v-slot:after-heading>
|
||||
<div class="ml-auto text-right">
|
||||
<div
|
||||
class="body-3 grey--text font-weight-light"
|
||||
v-text="title"
|
||||
/>
|
||||
|
||||
<h3 class="display-2 font-weight-light text--primary">
|
||||
{{ value }} <small>{{ smallValue }}</small>
|
||||
</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<v-col
|
||||
cols="12"
|
||||
class="px-0"
|
||||
>
|
||||
<v-divider />
|
||||
</v-col>
|
||||
|
||||
<v-icon
|
||||
:color="subIconColor"
|
||||
size="16"
|
||||
class="ml-2 mr-1"
|
||||
>
|
||||
{{ subIcon }}
|
||||
</v-icon>
|
||||
|
||||
<span
|
||||
:class="subTextColor"
|
||||
class="caption grey--text font-weight-light"
|
||||
v-text="subText"
|
||||
/>
|
||||
</base-material-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Card from './Card'
|
||||
|
||||
export default {
|
||||
name: 'MaterialStatsCard',
|
||||
|
||||
inheritAttrs: false,
|
||||
|
||||
props: {
|
||||
...Card.props,
|
||||
icon: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
subIcon: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
subIconColor: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
subTextColor: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
subText: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
smallValue: {
|
||||
type: String,
|
||||
default: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.v-card--material-stats
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
position: relative
|
||||
|
||||
> div:first-child
|
||||
justify-content: space-between
|
||||
|
||||
.v-card
|
||||
border-radius: 4px
|
||||
flex: 0 1 auto
|
||||
|
||||
.v-card__text
|
||||
display: inline-block
|
||||
flex: 1 0 calc(100% - 120px)
|
||||
position: absolute
|
||||
top: 0
|
||||
right: 0
|
||||
width: 100%
|
||||
|
||||
.v-card__actions
|
||||
flex: 1 0 100%
|
||||
</style>
|
43
web/src/components/base/MaterialTabs.vue
Normal file
43
web/src/components/base/MaterialTabs.vue
Normal file
@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<v-tabs
|
||||
v-model="internalValue"
|
||||
:active-class="`${color} ${$vuetify.theme.dark ? 'black' : 'white'}--text`"
|
||||
class="v-tabs--pill"
|
||||
hide-slider
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot />
|
||||
|
||||
<slot name="items" />
|
||||
</v-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Mixins
|
||||
import Proxyable from 'vuetify/lib/mixins/proxyable'
|
||||
|
||||
export default {
|
||||
name: 'MaterialTabs',
|
||||
|
||||
mixins: [Proxyable],
|
||||
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: 'primary'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.v-tabs--pill
|
||||
.v-tab,
|
||||
.v-tab:before
|
||||
border-radius: 24px
|
||||
|
||||
&.v-tabs--icons-and-text
|
||||
.v-tab,
|
||||
.v-tab:before
|
||||
border-radius: 4px
|
||||
</style>
|
75
web/src/components/base/MaterialTestimony.vue
Normal file
75
web/src/components/base/MaterialTestimony.vue
Normal file
@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<v-card class="text-center v-card--testimony">
|
||||
<div class="pt-6">
|
||||
<v-icon
|
||||
color="black"
|
||||
x-large
|
||||
>
|
||||
mdi-format-quote-close
|
||||
</v-icon>
|
||||
</div>
|
||||
|
||||
<v-card-text
|
||||
class="display-1 font-weight-light font-italic mb-3"
|
||||
v-text="blurb"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="display-2 font-weight-light mb-2"
|
||||
v-text="author"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="body-2 text-uppercase grey--text"
|
||||
v-text="handle"
|
||||
/>
|
||||
|
||||
<v-avatar
|
||||
color="grey"
|
||||
size="100"
|
||||
>
|
||||
<v-img
|
||||
:alt="`${author} Testimonial`"
|
||||
:src="avatar"
|
||||
/>
|
||||
</v-avatar>
|
||||
|
||||
<div />
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BaseMaterialTestimony',
|
||||
|
||||
props: {
|
||||
author: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
default: 'https://demos.creative-tim.com/material-dashboard-pro/assets/img/faces/card-profile1-square.jpg'
|
||||
},
|
||||
blurb: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
handle: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.v-card--testimony
|
||||
padding-bottom: 72px
|
||||
margin-bottom: 64px
|
||||
|
||||
.v-avatar
|
||||
position: absolute
|
||||
left: calc(50% - 64px)
|
||||
top: calc(100% - 64px)
|
||||
</style>
|
109
web/src/components/base/MaterialWizard.vue
Normal file
109
web/src/components/base/MaterialWizard.vue
Normal file
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<v-card
|
||||
class="v-card--wizard"
|
||||
elevation="12"
|
||||
max-width="700"
|
||||
>
|
||||
<v-card-title class="justify-center display-2 font-weight-light pt-5">
|
||||
Build your profile
|
||||
</v-card-title>
|
||||
|
||||
<div class="text-center display-1 grey--text font-weight-light mb-6">
|
||||
This information will let us know more about you.
|
||||
</div>
|
||||
|
||||
<v-tabs
|
||||
ref="tabs"
|
||||
v-model="internalValue"
|
||||
background-color="green lighten-5"
|
||||
color="white"
|
||||
grow
|
||||
slider-size="50"
|
||||
>
|
||||
<v-tabs-slider
|
||||
class="mt-1"
|
||||
color="success"
|
||||
/>
|
||||
|
||||
<v-tab
|
||||
v-for="(item, i) in items"
|
||||
:key="i"
|
||||
:ripple="false"
|
||||
:disabled="!availableSteps.includes(i)"
|
||||
>
|
||||
{{ item }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
|
||||
<div class="my-6" />
|
||||
|
||||
<v-card-text>
|
||||
<v-tabs-items v-model="internalValue">
|
||||
<slot />
|
||||
</v-tabs-items>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions class="pb-4 pa-4">
|
||||
<v-btn
|
||||
:disabled="internalValue === 0"
|
||||
class="white--text"
|
||||
color="grey darken-2"
|
||||
min-width="125"
|
||||
@click="$emit('click:prev')"
|
||||
>
|
||||
Previous
|
||||
</v-btn>
|
||||
|
||||
<v-spacer />
|
||||
|
||||
<v-btn
|
||||
:disabled="!availableSteps.includes(internalValue + 1)"
|
||||
color="success"
|
||||
min-width="100"
|
||||
@click="$emit('click:next')"
|
||||
>
|
||||
{{ internalValue === items.length - 1 ? 'Finish' : 'Next' }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Mixins
|
||||
import Proxyable from 'vuetify/lib/mixins/proxyable'
|
||||
|
||||
export default {
|
||||
name: 'BaseMaterialWizard',
|
||||
|
||||
mixins: [Proxyable],
|
||||
|
||||
props: {
|
||||
availableSteps: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.v-card--wizard
|
||||
overflow: visible
|
||||
|
||||
.v-tabs-bar
|
||||
height: 56px
|
||||
padding: 0 8px
|
||||
|
||||
.v-slide-group__wrapper
|
||||
overflow: visible
|
||||
|
||||
.v-tabs-slider
|
||||
border-radius: 4px
|
||||
|
||||
.v-slide-group__wrapper
|
||||
contain: initial
|
||||
</style>
|
34
web/src/components/base/Subheading.vue
Normal file
34
web/src/components/base/Subheading.vue
Normal file
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="display-2 font-weight-light col col-12 text-left text--primary pa-0 mb-8">
|
||||
<h5 class="font-weight-light">
|
||||
{{ subheading }}
|
||||
<template v-if="text">
|
||||
<span
|
||||
class="subtitle-1"
|
||||
v-text="text"
|
||||
/>
|
||||
</template>
|
||||
</h5>
|
||||
|
||||
<div class="pt-2">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Subheading',
|
||||
|
||||
props: {
|
||||
subheading: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
42
web/src/components/base/VComponent.vue
Normal file
42
web/src/components/base/VComponent.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<section class="mb-12 text-center">
|
||||
<h1
|
||||
class="font-weight-light mb-2"
|
||||
style="color:#3c4858; font-size:24px"
|
||||
v-text="`Vuetify ${heading}`"
|
||||
/>
|
||||
|
||||
<span
|
||||
class="font-weight-light"
|
||||
style="font-size: 16px; color: #3c4858"
|
||||
>
|
||||
Please checkout the
|
||||
<a
|
||||
:href="`https://vuetifyjs.com/${link}`"
|
||||
rel="noopener"
|
||||
target="_blank"
|
||||
class="secondary--text"
|
||||
style="text-decoration:none;"
|
||||
>
|
||||
full documentation
|
||||
</a>
|
||||
</span>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VComponent',
|
||||
|
||||
props: {
|
||||
heading: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
link: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,48 +0,0 @@
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
.text-pre-wrap {
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
.v-navigation-drawer {
|
||||
padding-top: constant(safe-area-inset-top) !important;
|
||||
padding-top: env(safe-area-inset-top) !important;
|
||||
}
|
||||
.v-bottom-sheet.v-dialog--fullscreen {
|
||||
padding-top: constant(safe-area-inset-top) !important;
|
||||
padding-top: env(safe-area-inset-top) !important;
|
||||
}
|
||||
.v-app-bar {
|
||||
height: auto !important;
|
||||
padding-top: constant(safe-area-inset-top) !important;
|
||||
padding-top: env(safe-area-inset-top) !important;
|
||||
}
|
||||
.v-toolbar {
|
||||
height: auto !important;
|
||||
padding-top: constant(safe-area-inset-top) !important;
|
||||
padding-top: env(safe-area-inset-top) !important;
|
||||
}
|
||||
.v-toolbar__content {
|
||||
padding-left: 12px !important;
|
||||
padding-right: 12px !important;
|
||||
}
|
||||
.v-main {
|
||||
margin-top: constant(safe-area-inset-top) !important;
|
||||
margin-top: env(safe-area-inset-top) !important;
|
||||
margin-bottom: constant(safe-area-inset-bottom) !important;
|
||||
margin-bottom: env(safe-area-inset-bottom) !important;
|
||||
}
|
||||
.v-main .container {
|
||||
height: 100%;
|
||||
}
|
||||
.v-bottom-navigation,
|
||||
.v-bottom-sheet {
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
.v-bottom-navigation {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
.v-bottom-navigation button {
|
||||
box-sizing: border-box;
|
||||
}
|
24
web/src/i18n.js
Normal file
24
web/src/i18n.js
Normal file
@ -0,0 +1,24 @@
|
||||
import Vue from 'vue'
|
||||
import VueI18n from 'vue-i18n'
|
||||
|
||||
import ar from 'vuetify/lib/locale/ar'
|
||||
import en from 'vuetify/lib/locale/en'
|
||||
|
||||
Vue.use(VueI18n)
|
||||
|
||||
const messages = {
|
||||
en: {
|
||||
...require('@/locales/en.json'),
|
||||
$vuetify: en
|
||||
},
|
||||
ar: {
|
||||
...require('@/locales/ar.json'),
|
||||
$vuetify: ar
|
||||
}
|
||||
}
|
||||
|
||||
export default new VueI18n({
|
||||
locale: process.env.VUE_APP_I18N_LOCALE || 'en',
|
||||
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
|
||||
messages
|
||||
})
|
44
web/src/locales/ar.json
Normal file
44
web/src/locales/ar.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"avatar": "تانيا أندرو",
|
||||
"buttons": "وصفت",
|
||||
"calendar": "التقويم",
|
||||
"charts": "الرسوم البيانية",
|
||||
"components": "المكونات",
|
||||
"ct": "CT",
|
||||
"dashboard": "لوحة القيادة",
|
||||
"dtables": "جداول البيانات",
|
||||
"eforms": "أشكال موسعة",
|
||||
"error": "صفحة الخطأ",
|
||||
"etables": "الجداول الموسعة",
|
||||
"example": "مثال",
|
||||
"forms": "إستمارات",
|
||||
"fullscreen": "خريطة الشاشة الكاملة",
|
||||
"google": "خرائط جوجل",
|
||||
"grid": "نظام الشبكة",
|
||||
"icons": "الرموز",
|
||||
"lock": "قفل الشاشة الصفحة",
|
||||
"login": "صفحة تسجيل الدخول",
|
||||
"maps": "خرائط",
|
||||
"multi": "متعدد المستويات انهيار",
|
||||
"notifications": "إخطارات",
|
||||
"pages": "صفحات",
|
||||
"plan": "اختر خطة",
|
||||
"pricing": "التسعير",
|
||||
"my-profile": "ملفي",
|
||||
"edit-profile": "تعديل الملف الشخصي",
|
||||
"register": "تسجيل الصفحة",
|
||||
"rforms": "النماذج العادية",
|
||||
"rtables": "الجداول العادية",
|
||||
"rtl": "دعم RTL",
|
||||
"search": "بحث...",
|
||||
"settings": "الإعدادات",
|
||||
"tables": "الجداول",
|
||||
"tabs": "Tabs",
|
||||
"tim": "تيم الإبداعية",
|
||||
"timeline": "الجدول الزمني",
|
||||
"typography": "طباعة",
|
||||
"user": "ملف تعريفي للمستخدم",
|
||||
"vforms": "نماذج التحقق من الصحة",
|
||||
"widgets": "الحاجيات",
|
||||
"wizard": "ساحر"
|
||||
}
|
44
web/src/locales/en.json
Normal file
44
web/src/locales/en.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"avatar": "Tania Andrew",
|
||||
"buttons": "Buttons",
|
||||
"calendar": "Calendar",
|
||||
"charts": "Charts",
|
||||
"components": "Components",
|
||||
"ct": "CT",
|
||||
"dashboard": "Dashboard",
|
||||
"dtables": "Data Tables",
|
||||
"eforms": "Extended Forms",
|
||||
"error": "Error Page",
|
||||
"etables": "Extended Tables",
|
||||
"example": "Example",
|
||||
"forms": "Forms",
|
||||
"fullscreen": "Full Screen Map",
|
||||
"google": "Google Maps",
|
||||
"grid": "Grid System",
|
||||
"icons": "Icons",
|
||||
"lock": "Lock Screen Page",
|
||||
"login": "Login Page",
|
||||
"maps": "Maps",
|
||||
"multi": "Multi Level Collapse",
|
||||
"notifications": "Notifications",
|
||||
"pages": "Pages",
|
||||
"plan": "Choose Plan",
|
||||
"pricing": "Pricing",
|
||||
"my-profile": "My Profile",
|
||||
"edit-profile": "Edit Profile",
|
||||
"register": "Register Page",
|
||||
"rforms": "Regular Forms",
|
||||
"rtables": "Regular Tables",
|
||||
"rtl": "RTL Support",
|
||||
"search": "Search",
|
||||
"settings": "Settings",
|
||||
"tables": "Tables",
|
||||
"tabs": "Tabs",
|
||||
"tim": "Creative Tim",
|
||||
"timeline": "Timeline",
|
||||
"typography": "Typography",
|
||||
"user": "User Profile",
|
||||
"vforms": "Validation Forms",
|
||||
"widgets": "Widgets",
|
||||
"wizard": "Wizard"
|
||||
}
|
@ -1,6 +1,12 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import vuetify from './plugins/vuetify';
|
||||
import 'material-design-icons-iconfont/dist/material-design-icons.css'
|
||||
import './plugins/base';
|
||||
import './plugins/chartist';
|
||||
// import './plugins/vee-validate';
|
||||
import './plugins/vue-world-map';
|
||||
import i18n from './i18n';
|
||||
import router from './router';
|
||||
import store from './store';
|
||||
import Clipboard from 'v-clipboard';
|
||||
@ -12,5 +18,6 @@ new Vue({
|
||||
router,
|
||||
store,
|
||||
Clipboard,
|
||||
i18n,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
|
17
web/src/plugins/base.js
Normal file
17
web/src/plugins/base.js
Normal file
@ -0,0 +1,17 @@
|
||||
import Vue from 'vue'
|
||||
import upperFirst from 'lodash/upperFirst'
|
||||
import camelCase from 'lodash/camelCase'
|
||||
|
||||
const requireComponent = require.context(
|
||||
'@/components/base', true, /\.vue$/
|
||||
)
|
||||
|
||||
requireComponent.keys().forEach(fileName => {
|
||||
const componentConfig = requireComponent(fileName)
|
||||
|
||||
const componentName = upperFirst(
|
||||
camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, ''))
|
||||
)
|
||||
|
||||
Vue.component(`Base${componentName}`, componentConfig.default || componentConfig)
|
||||
})
|
4
web/src/plugins/chartist.js
Normal file
4
web/src/plugins/chartist.js
Normal file
@ -0,0 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import 'chartist/dist/chartist.min.css'
|
||||
|
||||
Vue.use(require('vue-chartist'))
|
5
web/src/plugins/vee-validate.js
Normal file
5
web/src/plugins/vee-validate.js
Normal file
@ -0,0 +1,5 @@
|
||||
import Vue from 'vue'
|
||||
// import * as VeeValidate from 'vee-validate'
|
||||
import VeeValidate from 'vee-validate'
|
||||
|
||||
Vue.use(VeeValidate)
|
4
web/src/plugins/vue-world-map.js
Normal file
4
web/src/plugins/vue-world-map.js
Normal file
@ -0,0 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import VueWorldMap from 'vue-world-map'
|
||||
|
||||
Vue.component('v-world-map', VueWorldMap)
|
@ -1,7 +1,24 @@
|
||||
import Vue from 'vue';
|
||||
import Vuetify from 'vuetify/lib';
|
||||
import 'material-design-icons-iconfont/dist/material-design-icons.css'
|
||||
import Vue from 'vue'
|
||||
import Vuetify from 'vuetify/lib'
|
||||
import i18n from '@/i18n'
|
||||
|
||||
Vue.use(Vuetify);
|
||||
Vue.use(Vuetify)
|
||||
|
||||
export default new Vuetify({});
|
||||
const theme = {
|
||||
primary: '#E91E63',
|
||||
secondary: '#9C27b0',
|
||||
accent: '#9C27b0',
|
||||
info: '#00CAE3'
|
||||
}
|
||||
|
||||
export default new Vuetify({
|
||||
lang: {
|
||||
t: (key, ...params) => i18n.t(key, params)
|
||||
},
|
||||
theme: {
|
||||
themes: {
|
||||
dark: theme,
|
||||
light: theme
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1,110 +0,0 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<v-card class="mb-4">
|
||||
<v-card-title>订阅配置</v-card-title>
|
||||
<v-form class="pl-4 pr-4 pb-4" v-model="valid">
|
||||
<v-subheader class="pl-0">订阅名称</v-subheader>
|
||||
<v-text-field
|
||||
v-model="name"
|
||||
class="mt-2"
|
||||
:rules="validations.nameRules"
|
||||
required
|
||||
placeholder="填入订阅名称,名称需唯一"
|
||||
/>
|
||||
<v-divider></v-divider>
|
||||
<v-subheader class="pl-0">包含的订阅</v-subheader>
|
||||
<v-list dense>
|
||||
<v-list-item v-for="sub in availableSubs" :key="sub.name">
|
||||
<v-list-item-avatar dark>
|
||||
<v-icon>mdi-cloud</v-icon>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
{{ sub.name }}
|
||||
</v-list-item-content>
|
||||
<v-spacer></v-spacer>
|
||||
<v-checkbox
|
||||
:value="sub.name"
|
||||
v-model="selected"
|
||||
class="pr-1"
|
||||
/>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-form>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="save"><v-icon>save_alt</v-icon></v-btn>
|
||||
<v-btn icon @click="discard"><v-icon>settings_backup_restore</v-icon></v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {showInfo, showError} from "@/utils";
|
||||
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
valid: false,
|
||||
validations: {
|
||||
nameRules: [
|
||||
v => !!v || "名字不能为空",
|
||||
v => /^[\w-_]*$/.test(v) || "订阅名称只能包含英文字符、横杠和下划线!"
|
||||
]
|
||||
},
|
||||
selected: [],
|
||||
name: ""
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
availableSubs() {
|
||||
return this.$store.state.subscriptions;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
save() {
|
||||
if (!this.valid || this.selected.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (this.$route.params.name === 'UNTITLED') {
|
||||
this.$store.dispatch("NEW_COLLECTION", {
|
||||
name: this.name,
|
||||
subscriptions: this.selected
|
||||
}).then(() => {
|
||||
showInfo(`成功创建订阅:${this.name}!`)
|
||||
}).catch(() => {
|
||||
showError(`发生错误,无法创建订阅!`)
|
||||
});
|
||||
} else {
|
||||
this.$store.dispatch("UPDATE_COLLECTION", {
|
||||
name: this.$route.params.name,
|
||||
collection: {
|
||||
name: this.name,
|
||||
subscriptions: this.selected
|
||||
}
|
||||
}).then(() => {
|
||||
showInfo(`成功保存订阅:${this.name}!`)
|
||||
}).catch(() => {
|
||||
showError(`发生错误,无法保存订阅!`)
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
discard() {
|
||||
this.$router.back();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const name = this.$route.params.name;
|
||||
const collection = this.$store.state.collections[name] || {};
|
||||
this.$store.commit("SET_NAV_TITLE", collection.name ? `组合订阅编辑 -- ${collection.name}` : "新建组合订阅");
|
||||
this.name = collection.name;
|
||||
this.selected = collection.subscriptions || [];
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,41 +1,11 @@
|
||||
<template>
|
||||
<!-- <v-card-->
|
||||
<!-- class="ml-4 mt-4 mb-4 mr-4"-->
|
||||
<!-- >-->
|
||||
<!-- <v-card-title>Nexitally</v-card-title>-->
|
||||
<!-- <v-carousel-->
|
||||
<!-- cycle-->
|
||||
<!-- height="250"-->
|
||||
<!-- :show-arrows="false"-->
|
||||
<!-- >-->
|
||||
<!-- <v-carousel-item>-->
|
||||
<!-- <v-chart-->
|
||||
<!-- :options="pie"-->
|
||||
<!-- class="remains !important"-->
|
||||
<!-- autoresize-->
|
||||
<!-- />-->
|
||||
<!-- </v-carousel-item>-->
|
||||
<!-- <v-carousel-item>-->
|
||||
<!-- <v-chart-->
|
||||
<!-- :options="pie"-->
|
||||
<!-- class="remains !important"-->
|
||||
<!-- autoresize-->
|
||||
<!-- />-->
|
||||
<!-- </v-carousel-item>-->
|
||||
<!-- </v-carousel>-->
|
||||
<!-- </v-card>-->
|
||||
<v-container></v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ECharts from 'vue-echarts';
|
||||
import 'echarts/lib/chart/pie';
|
||||
|
||||
export default {
|
||||
name: "Dashboard",
|
||||
components: {
|
||||
// eslint-disable-next-line vue/no-unused-components
|
||||
"v-chart": ECharts
|
||||
},
|
||||
computed: {
|
||||
pie() {
|
||||
|
@ -24,6 +24,7 @@
|
||||
label="订阅链接"
|
||||
placeholder="填入机场原始订阅链接"
|
||||
clearable
|
||||
auto-grow
|
||||
clear-icon="clear"
|
||||
/>
|
||||
<!--For Collection-->
|
||||
@ -213,17 +214,14 @@
|
||||
import {showError, showInfo} from "@/utils";
|
||||
import TypeFilter from "@/components/TypeFilter";
|
||||
import RegionFilter from "@/components/RegionFilter";
|
||||
import KeywordFilter from "@/components/KeywordFilter";
|
||||
import RegexFilter from "@/components/RegexFilter";
|
||||
import SortOperator from "@/components/SortOperator";
|
||||
import KeywordRenameOperator from "@/components/KeywordRenameOperator";
|
||||
import RegexRenameOperator from "@/components/RegexRenameOperator";
|
||||
import KeywordDeleteOperator from "@/components/KeywordDeleteOperator";
|
||||
import RegexDeleteOperator from "@/components/RegexDeleteOperator";
|
||||
import FlagOperator from "@/components/FlagOperator";
|
||||
import ScriptFilter from "@/components/ScriptFilter";
|
||||
import ScriptOperator from "@/components/ScriptOperator";
|
||||
import KeywordSortOperator from "@/components/KeywordSortOperator";
|
||||
import RegexSortOperator from "@/components/RegexSortOperator";
|
||||
|
||||
const AVAILABLE_PROCESSORS = {
|
||||
"Flag Operator": {
|
||||
@ -238,10 +236,6 @@ const AVAILABLE_PROCESSORS = {
|
||||
component: "RegionFilter",
|
||||
name: "区域过滤器"
|
||||
},
|
||||
"Keyword Filter": {
|
||||
component: "KeywordFilter",
|
||||
name: "关键词过滤器"
|
||||
},
|
||||
"Regex Filter": {
|
||||
component: "RegexFilter",
|
||||
name: "正则过滤器"
|
||||
@ -250,22 +244,14 @@ const AVAILABLE_PROCESSORS = {
|
||||
component: "SortOperator",
|
||||
name: "节点排序"
|
||||
},
|
||||
"Keyword Sort Operator": {
|
||||
component: "KeywordSortOperator",
|
||||
name: "关键词排序"
|
||||
},
|
||||
"Keyword Rename Operator": {
|
||||
component: "KeywordRenameOperator",
|
||||
name: "关键词重命名"
|
||||
"Regex Sort Operator": {
|
||||
component: "RegexSortOperator",
|
||||
name: "正则排序"
|
||||
},
|
||||
"Regex Rename Operator": {
|
||||
component: "RegexRenameOperator",
|
||||
name: "正则重命名"
|
||||
},
|
||||
"Keyword Delete Operator": {
|
||||
component: "KeywordDeleteOperator",
|
||||
name: "删除关键词"
|
||||
},
|
||||
"Regex Delete Operator": {
|
||||
component: "RegexDeleteOperator",
|
||||
name: "删除正则"
|
||||
@ -291,15 +277,12 @@ export default {
|
||||
},
|
||||
components: {
|
||||
FlagOperator,
|
||||
KeywordFilter,
|
||||
RegexFilter,
|
||||
RegionFilter,
|
||||
TypeFilter,
|
||||
SortOperator,
|
||||
KeywordRenameOperator,
|
||||
KeywordSortOperator,
|
||||
RegexSortOperator,
|
||||
RegexRenameOperator,
|
||||
KeywordDeleteOperator,
|
||||
RegexDeleteOperator,
|
||||
ScriptFilter,
|
||||
ScriptOperator,
|
||||
@ -367,7 +350,7 @@ export default {
|
||||
},
|
||||
|
||||
processors() {
|
||||
return this.process.map(p => {
|
||||
return this.process.filter(p => AVAILABLE_PROCESSORS[p.type]).map(p => {
|
||||
return {
|
||||
component: AVAILABLE_PROCESSORS[p.type].component,
|
||||
args: p.args,
|
||||
|
@ -109,7 +109,7 @@
|
||||
fab
|
||||
>
|
||||
<v-icon v-if="opened">mdi-close</v-icon>
|
||||
<v-icon v-else>apps</v-icon>
|
||||
<v-icon v-else>mdi-apps</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-btn
|
||||
@ -131,10 +131,10 @@
|
||||
<v-dialog fullscreen hide-overlay transition="dialog-bottom-transition" v-model="showProxyList" scrollable>
|
||||
<v-card>
|
||||
<v-card-title class="pa-0">
|
||||
<v-toolbar dark color="primary">
|
||||
<v-toolbar>
|
||||
<v-icon>mdi-cloud</v-icon>
|
||||
<v-spacer></v-spacer>
|
||||
<v-toolbar-title>节点列表</v-toolbar-title>
|
||||
<v-toolbar-title><h4>节点列表</h4></v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-toolbar-items>
|
||||
<v-btn icon @click="refreshProxyList" v-if="sub">
|
||||
|
Loading…
x
Reference in New Issue
Block a user