mirror of
https://git.mirrors.martin98.com/https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 02:39:01 +08:00
Merge branch 'dev'
This commit is contained in:
commit
9333d989bc
@ -54,6 +54,8 @@ function service() {
|
|||||||
.get(getAllSubscriptions)
|
.get(getAllSubscriptions)
|
||||||
.post(createSubscription);
|
.post(createSubscription);
|
||||||
|
|
||||||
|
$app.get("/api/sub/statistics/:name");
|
||||||
|
|
||||||
// collection API
|
// collection API
|
||||||
$app.route("/api/collection/:name")
|
$app.route("/api/collection/:name")
|
||||||
.get(getCollection)
|
.get(getCollection)
|
||||||
@ -1584,14 +1586,15 @@ var ProxyUtils = (function () {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort by keywords
|
// sort by regex
|
||||||
function KeywordSortOperator(keywords) {
|
function RegexSortOperator(expressions) {
|
||||||
|
expressions = expressions.map(expr => new RegExp(expr));
|
||||||
return {
|
return {
|
||||||
name: "Keyword Sort Operator",
|
name: "Regex Sort Operator",
|
||||||
func: (proxies) =>
|
func: (proxies) =>
|
||||||
proxies.sort((a, b) => {
|
proxies.sort((a, b) => {
|
||||||
const oA = getKeywordOrder(keywords, a.name);
|
const oA = getRegexOrder(expressions, a.name);
|
||||||
const oB = getKeywordOrder(keywords, b.name);
|
const oB = getRegexOrder(expressions, b.name);
|
||||||
if (oA && !oB) return -1;
|
if (oA && !oB) return -1;
|
||||||
if (oB && !oA) return 1;
|
if (oB && !oA) return 1;
|
||||||
if (oA && oB) return oA < oB ? -1 : 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;
|
let order = null;
|
||||||
for (let i = 0; i < keywords.length; i++) {
|
for (let i = 0; i < expressions.length; i++) {
|
||||||
if (str.indexOf(keywords[i]) !== -1) {
|
if (expressions[i].test(str)) {
|
||||||
order = i + 1; // plus 1 is important! 0 will be treated as false!!!
|
order = i + 1; // plus 1 is important! 0 will be treated as false!!!
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1612,22 +1615,6 @@ var ProxyUtils = (function () {
|
|||||||
return order;
|
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
|
// rename by regex
|
||||||
// keywords: [{expr: "string format regex", now: "now"}]
|
// keywords: [{expr: "string format regex", now: "now"}]
|
||||||
function RegexRenameOperator(regex) {
|
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
|
// delete regex operator
|
||||||
// regex: ['a', 'b', 'c']
|
// regex: ['a', 'b', 'c']
|
||||||
function RegexDeleteOperator(regex) {
|
function RegexDeleteOperator(regex) {
|
||||||
@ -1693,16 +1665,10 @@ var ProxyUtils = (function () {
|
|||||||
(function () {
|
(function () {
|
||||||
// interface to get internal operators
|
// interface to get internal operators
|
||||||
const $get = (name, args) => {
|
const $get = (name, args) => {
|
||||||
const item = AVAILABLE_OPERATORS[name];
|
const item = PROXY_PROCESSORS[name];
|
||||||
return item(args);
|
return item(args);
|
||||||
};
|
};
|
||||||
const $process = (item, proxies) => {
|
const $process = ApplyProcessor;
|
||||||
if (item.name.indexOf("Filter") !== -1) {
|
|
||||||
return ApplyOperator(item, proxies);
|
|
||||||
} else if (item.name.indexOf("Operator") !== -1) {
|
|
||||||
return ApplyFilter(item, proxies);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
eval(script);
|
eval(script);
|
||||||
output = operator(proxies);
|
output = operator(proxies);
|
||||||
})();
|
})();
|
||||||
@ -1712,19 +1678,6 @@ var ProxyUtils = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**************************** Filters ***************************************/
|
/**************************** 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
|
// filter useless proxies
|
||||||
function UselessFilter() {
|
function UselessFilter() {
|
||||||
const KEYWORDS = [
|
const KEYWORDS = [
|
||||||
@ -1738,8 +1691,8 @@ var ProxyUtils = (function () {
|
|||||||
];
|
];
|
||||||
return {
|
return {
|
||||||
name: "Useless Filter",
|
name: "Useless Filter",
|
||||||
func: KeywordFilter({
|
func: RegexFilter({
|
||||||
keywords: KEYWORDS,
|
regex: KEYWORDS,
|
||||||
keep: false,
|
keep: false,
|
||||||
}).func,
|
}).func,
|
||||||
};
|
};
|
||||||
@ -2031,7 +1984,6 @@ var ProxyUtils = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"Keyword Filter": KeywordFilter,
|
|
||||||
"Useless Filter": UselessFilter,
|
"Useless Filter": UselessFilter,
|
||||||
"Region Filter": RegionFilter,
|
"Region Filter": RegionFilter,
|
||||||
"Regex Filter": RegexFilter,
|
"Regex Filter": RegexFilter,
|
||||||
@ -2041,9 +1993,7 @@ var ProxyUtils = (function () {
|
|||||||
"Set Property Operator": SetPropertyOperator,
|
"Set Property Operator": SetPropertyOperator,
|
||||||
"Flag Operator": FlagOperator,
|
"Flag Operator": FlagOperator,
|
||||||
"Sort Operator": SortOperator,
|
"Sort Operator": SortOperator,
|
||||||
"Keyword Sort Operator": KeywordSortOperator,
|
"Regex Sort Operator": RegexSortOperator,
|
||||||
"Keyword Rename Operator": KeywordRenameOperator,
|
|
||||||
"Keyword Delete Operator": KeywordDeleteOperator,
|
|
||||||
"Regex Rename Operator": RegexRenameOperator,
|
"Regex Rename Operator": RegexRenameOperator,
|
||||||
"Regex Delete Operator": RegexDeleteOperator,
|
"Regex Delete Operator": RegexDeleteOperator,
|
||||||
"Script Operator": ScriptOperator,
|
"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",
|
"name": "web",
|
||||||
"version": "0.1.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -3291,6 +3291,11 @@
|
|||||||
"integrity": "sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=",
|
"integrity": "sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=",
|
||||||
"dev": true
|
"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": {
|
"check-types": {
|
||||||
"version": "8.0.3",
|
"version": "8.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz",
|
||||||
@ -3354,6 +3359,11 @@
|
|||||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||||
"dev": true
|
"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": {
|
"chrome-trace-event": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz",
|
||||||
@ -4719,14 +4729,6 @@
|
|||||||
"safer-buffer": "^2.1.0"
|
"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": {
|
"ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
@ -7139,7 +7141,8 @@
|
|||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.20",
|
"version": "4.17.20",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"lodash.defaultsdeep": {
|
"lodash.defaultsdeep": {
|
||||||
"version": "4.6.1",
|
"version": "4.6.1",
|
||||||
@ -7542,20 +7545,6 @@
|
|||||||
"minimist": "^1.2.5"
|
"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": {
|
"move-concurrently": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||||
@ -9511,11 +9500,6 @@
|
|||||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
|
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
|
||||||
"dev": true
|
"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": {
|
"resolve": {
|
||||||
"version": "1.17.0",
|
"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",
|
"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=",
|
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
|
||||||
"dev": true
|
"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": {
|
"vendors": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz",
|
||||||
"integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg=="
|
"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": {
|
"vue-cli-plugin-vuetify": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npm.taobao.org/vue-cli-plugin-vuetify/download/vue-cli-plugin-vuetify-2.0.7.tgz",
|
"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": {
|
"vue-eslint-parser": {
|
||||||
"version": "7.1.0",
|
"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",
|
"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==",
|
"integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==",
|
||||||
"dev": true
|
"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": {
|
"vue-loader": {
|
||||||
"version": "15.9.3",
|
"version": "15.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.3.tgz",
|
||||||
@ -11403,6 +11395,15 @@
|
|||||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
||||||
"dev": true
|
"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": {
|
"vuetify": {
|
||||||
"version": "2.3.10",
|
"version": "2.3.10",
|
||||||
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.3.10.tgz",
|
||||||
@ -12289,11 +12290,6 @@
|
|||||||
"dev": true
|
"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",
|
"name": "web",
|
||||||
"version": "0.1.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "vue-cli-service serve",
|
||||||
@ -11,16 +11,17 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dzangolab/vue-country-flag-icon": "^0.2.0",
|
"@dzangolab/vue-country-flag-icon": "^0.2.0",
|
||||||
"axios": "^0.20.0",
|
"axios": "^0.20.0",
|
||||||
|
"chartist": "^0.11.4",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"echarts": "^4.9.0",
|
|
||||||
"lodash": "^4.17.20",
|
|
||||||
"material-design-icons-iconfont": "^5.0.1",
|
"material-design-icons-iconfont": "^5.0.1",
|
||||||
"monaco-editor-vue": "^1.0.10",
|
|
||||||
"v-clipboard": "^2.2.3",
|
"v-clipboard": "^2.2.3",
|
||||||
|
"vee-validate": "^3.4.5",
|
||||||
"vue": "^2.6.12",
|
"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-qrcode-component": "^2.1.1",
|
||||||
"vue-router": "^3.4.3",
|
"vue-router": "^3.4.3",
|
||||||
|
"vue-world-map": "^0.1.1",
|
||||||
"vuetify": "^2.3.10",
|
"vuetify": "^2.3.10",
|
||||||
"vuex": "^3.5.1"
|
"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>
|
<template>
|
||||||
<v-app>
|
<v-app>
|
||||||
<TopToolbar></TopToolbar>
|
<TopToolbar></TopToolbar>
|
||||||
<v-content>
|
<v-main>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</v-content>
|
</v-main>
|
||||||
<BottomNav></BottomNav>
|
<BottomNav></BottomNav>
|
||||||
<v-snackbar
|
<v-snackbar
|
||||||
:value="successMessage"
|
:value="successMessage"
|
||||||
@ -28,6 +28,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import TopToolbar from "@/components/TopToolbar";
|
import TopToolbar from "@/components/TopToolbar";
|
||||||
import BottomNav from "@/components/BottomNav";
|
import BottomNav from "@/components/BottomNav";
|
||||||
import {showError} from "@/utils";
|
import {showError} from "@/utils";
|
||||||
@ -52,10 +53,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.$vuetify.theme.dark = true;
|
|
||||||
this.$vuetify.theme.themes.dark.primary = '#0899ab';
|
|
||||||
this.$vuetify.theme.themes.light.primary = '#d73964';
|
|
||||||
|
|
||||||
initStore(this.$store);
|
initStore(this.$store);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -82,6 +79,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
|
||||||
@import "css/app.css";
|
<style lang="scss">
|
||||||
|
@import "./assets/css/app";
|
||||||
</style>
|
</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">
|
<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>
|
||||||
<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>
|
<template>
|
||||||
<v-bottom-navigation
|
<v-bottom-navigation
|
||||||
app
|
app
|
||||||
color="primary"
|
|
||||||
fixed
|
fixed
|
||||||
grow
|
grow
|
||||||
|
color="primary"
|
||||||
v-model="activeItem"
|
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">
|
<v-btn :to="{path: '/'}" value="subscription">
|
||||||
<span>订阅</span>
|
<span>订阅</span>
|
||||||
<v-icon>mdi-cloud</v-icon>
|
<v-icon>mdi-cloud</v-icon>
|
||||||
@ -20,8 +15,6 @@
|
|||||||
<span>我的</span>
|
<span>我的</span>
|
||||||
<v-icon>mdi-account</v-icon>
|
<v-icon>mdi-account</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
|
|
||||||
</v-bottom-navigation>
|
</v-bottom-navigation>
|
||||||
|
|
||||||
</template>
|
</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-dialog
|
||||||
v-model="dialog"
|
v-model="dialog"
|
||||||
>
|
>
|
||||||
<v-card
|
<v-card>
|
||||||
color="primary darken-1"
|
|
||||||
>
|
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
{{ info.name }}
|
{{ info.name }}
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
@ -63,15 +61,11 @@
|
|||||||
<v-dialog
|
<v-dialog
|
||||||
v-model="showQR"
|
v-model="showQR"
|
||||||
>
|
>
|
||||||
<v-card
|
<v-card>
|
||||||
color="primary darken-1"
|
|
||||||
>
|
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
{{ info.name }}
|
{{ info.name }}
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<v-btn
|
<v-btn
|
||||||
icon
|
icon
|
||||||
color="white"
|
|
||||||
@click="copyLink()"
|
@click="copyLink()"
|
||||||
>
|
>
|
||||||
<v-icon>content_copy</v-icon>
|
<v-icon>content_copy</v-icon>
|
||||||
|
@ -34,7 +34,9 @@
|
|||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
正则表达式
|
正则表达式
|
||||||
<v-chip-group>
|
<v-chip-group
|
||||||
|
column
|
||||||
|
>
|
||||||
<v-chip
|
<v-chip
|
||||||
close
|
close
|
||||||
close-icon="mdi-delete"
|
close-icon="mdi-delete"
|
||||||
|
@ -46,7 +46,9 @@
|
|||||||
</v-row>
|
</v-row>
|
||||||
</v-radio-group>
|
</v-radio-group>
|
||||||
正则表达式
|
正则表达式
|
||||||
<v-chip-group>
|
<v-chip-group
|
||||||
|
column
|
||||||
|
>
|
||||||
<v-chip
|
<v-chip
|
||||||
close
|
close
|
||||||
close-icon="mdi-delete"
|
close-icon="mdi-delete"
|
||||||
|
@ -31,7 +31,9 @@
|
|||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
正则表达式
|
正则表达式
|
||||||
<v-chip-group>
|
<v-chip-group
|
||||||
|
column
|
||||||
|
>
|
||||||
<v-chip
|
<v-chip
|
||||||
close
|
close
|
||||||
close-icon="mdi-delete"
|
close-icon="mdi-delete"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<v-card class="ml-1 mr-1 mb-1 mt-1">
|
<v-card class="ml-1 mr-1 mb-1 mt-1">
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
<v-icon left color="primary">sort</v-icon>
|
<v-icon left color="primary">sort</v-icon>
|
||||||
关键词排序
|
正则排序
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn icon @click="$emit('up', idx)">
|
<v-btn icon @click="$emit('up', idx)">
|
||||||
<v-icon>keyboard_arrow_up</v-icon>
|
<v-icon>keyboard_arrow_up</v-icon>
|
||||||
@ -21,38 +21,39 @@
|
|||||||
</template>
|
</template>
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-title class="headline">
|
<v-card-title class="headline">
|
||||||
关键词排序
|
正则排序
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
根据给出的关键词的顺序对节点进行排序,没有出现的关键词将会按照正序排列。
|
根据给出的正则表达式的顺序对节点进行排序,无法匹配的节点将会按照正序排列。
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
关键词
|
正则表达式
|
||||||
<v-chip-group column
|
<v-chip-group
|
||||||
|
column
|
||||||
>
|
>
|
||||||
<v-chip
|
<v-chip
|
||||||
close
|
close
|
||||||
close-icon="mdi-delete"
|
close-icon="mdi-delete"
|
||||||
v-for="(keyword, idx) in keywords"
|
v-for="(item, idx) in items"
|
||||||
:key="idx"
|
:key="idx"
|
||||||
@click="edit(idx)"
|
@click="edit(idx)"
|
||||||
@click:close="remove(idx)"
|
@click:close="remove(idx)"
|
||||||
>
|
>
|
||||||
{{ keyword }}
|
{{ item }}
|
||||||
</v-chip>
|
</v-chip>
|
||||||
</v-chip-group>
|
</v-chip-group>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
placeholder="添加新关键词"
|
placeholder="添加新正则表达式"
|
||||||
clearable
|
clearable
|
||||||
clear-icon="clear"
|
clear-icon="clear"
|
||||||
solo
|
solo
|
||||||
v-model="form.keyword"
|
v-model="form.item"
|
||||||
append-icon="mdi-send"
|
append-icon="mdi-send"
|
||||||
@click:append="add(form.keyword)"
|
@click:append="add(form.item)"
|
||||||
@keyup.enter="add(form.keyword)"
|
@keyup.enter="add(form.item)"
|
||||||
/>
|
/>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
@ -67,41 +68,41 @@ export default {
|
|||||||
selection: null,
|
selection: null,
|
||||||
currentTag: null,
|
currentTag: null,
|
||||||
form: {
|
form: {
|
||||||
keyword: ""
|
item: ""
|
||||||
},
|
},
|
||||||
keywords: []
|
items: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
if (this.args) {
|
if (this.args) {
|
||||||
this.keywords = this.args || [];
|
this.items = this.args || [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
keywords() {
|
items() {
|
||||||
this.save();
|
this.save();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
add(keyword) {
|
add(item) {
|
||||||
if (keyword) {
|
if (item) {
|
||||||
this.keywords.push(keyword);
|
this.items.push(item);
|
||||||
this.form.keyword = "";
|
this.form.item = "";
|
||||||
} else {
|
} else {
|
||||||
this.$store.commit("SET_ERROR_MESSAGE", "关键词不能为空!");
|
this.$store.commit("SET_ERROR_MESSAGE", "正则表达式不能为空!");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
edit(idx) {
|
edit(idx) {
|
||||||
this.form.keyword = this.keywords[idx];
|
this.form.item = this.items[idx];
|
||||||
this.remove(idx);
|
this.remove(idx);
|
||||||
},
|
},
|
||||||
remove(idx) {
|
remove(idx) {
|
||||||
this.keywords.splice(idx, 1);
|
this.items.splice(idx, 1);
|
||||||
},
|
},
|
||||||
save() {
|
save() {
|
||||||
this.$emit("dataChanged", {
|
this.$emit("dataChanged", {
|
||||||
idx: this.idx,
|
idx: this.idx,
|
||||||
args: this.keywords
|
args: this.items
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -41,14 +41,15 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-radio-group>
|
</v-radio-group>
|
||||||
|
<v-textarea
|
||||||
|
solo
|
||||||
|
clearable
|
||||||
|
auto-grow
|
||||||
|
clear-icon="clear"
|
||||||
|
:label="hint"
|
||||||
|
v-model="content"
|
||||||
|
/>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-textarea
|
|
||||||
solo
|
|
||||||
clearable
|
|
||||||
clear-icon="clear"
|
|
||||||
:label="hint"
|
|
||||||
v-model="content"
|
|
||||||
/>
|
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -41,14 +41,15 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-radio-group>
|
</v-radio-group>
|
||||||
|
<v-textarea
|
||||||
|
clearable
|
||||||
|
clear-icon="clear"
|
||||||
|
solo
|
||||||
|
auto-grow
|
||||||
|
:label="hint"
|
||||||
|
v-model="content"
|
||||||
|
/>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-textarea
|
|
||||||
clearable
|
|
||||||
clear-icon="clear"
|
|
||||||
solo
|
|
||||||
:label="hint"
|
|
||||||
v-model="content"
|
|
||||||
/>
|
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
fab
|
fab
|
||||||
>
|
>
|
||||||
<v-icon v-if="opened">mdi-close</v-icon>
|
<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>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-btn
|
<v-btn
|
||||||
|
@ -1,41 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<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
|
<v-app-bar
|
||||||
app
|
app
|
||||||
color="primary"
|
|
||||||
dark
|
|
||||||
fixed
|
fixed
|
||||||
:mini-variant="false" :clipped="true"
|
:mini-variant="false" :clipped="true"
|
||||||
>
|
>
|
||||||
<v-app-bar-nav-icon @click.stop="toggleMenu"></v-app-bar-nav-icon>
|
<v-toolbar-title><h3>{{title}}</h3></v-toolbar-title>
|
||||||
|
|
||||||
<v-toolbar-title>{{title}}</v-toolbar-title>
|
|
||||||
|
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -50,12 +20,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
toggleMenu: function () {
|
|
||||||
this.showMenu = !this.showMenu;
|
|
||||||
},
|
|
||||||
doNothing: function () {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
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 Vue from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import vuetify from './plugins/vuetify';
|
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 router from './router';
|
||||||
import store from './store';
|
import store from './store';
|
||||||
import Clipboard from 'v-clipboard';
|
import Clipboard from 'v-clipboard';
|
||||||
@ -12,5 +18,6 @@ new Vue({
|
|||||||
router,
|
router,
|
||||||
store,
|
store,
|
||||||
Clipboard,
|
Clipboard,
|
||||||
|
i18n,
|
||||||
render: h => h(App)
|
render: h => h(App)
|
||||||
}).$mount('#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 Vue from 'vue'
|
||||||
import Vuetify from 'vuetify/lib';
|
import Vuetify from 'vuetify/lib'
|
||||||
import 'material-design-icons-iconfont/dist/material-design-icons.css'
|
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>
|
<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>
|
<v-container></v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ECharts from 'vue-echarts';
|
|
||||||
import 'echarts/lib/chart/pie';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Dashboard",
|
name: "Dashboard",
|
||||||
components: {
|
components: {
|
||||||
// eslint-disable-next-line vue/no-unused-components
|
|
||||||
"v-chart": ECharts
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
pie() {
|
pie() {
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
label="订阅链接"
|
label="订阅链接"
|
||||||
placeholder="填入机场原始订阅链接"
|
placeholder="填入机场原始订阅链接"
|
||||||
clearable
|
clearable
|
||||||
|
auto-grow
|
||||||
clear-icon="clear"
|
clear-icon="clear"
|
||||||
/>
|
/>
|
||||||
<!--For Collection-->
|
<!--For Collection-->
|
||||||
@ -213,17 +214,14 @@
|
|||||||
import {showError, showInfo} from "@/utils";
|
import {showError, showInfo} from "@/utils";
|
||||||
import TypeFilter from "@/components/TypeFilter";
|
import TypeFilter from "@/components/TypeFilter";
|
||||||
import RegionFilter from "@/components/RegionFilter";
|
import RegionFilter from "@/components/RegionFilter";
|
||||||
import KeywordFilter from "@/components/KeywordFilter";
|
|
||||||
import RegexFilter from "@/components/RegexFilter";
|
import RegexFilter from "@/components/RegexFilter";
|
||||||
import SortOperator from "@/components/SortOperator";
|
import SortOperator from "@/components/SortOperator";
|
||||||
import KeywordRenameOperator from "@/components/KeywordRenameOperator";
|
|
||||||
import RegexRenameOperator from "@/components/RegexRenameOperator";
|
import RegexRenameOperator from "@/components/RegexRenameOperator";
|
||||||
import KeywordDeleteOperator from "@/components/KeywordDeleteOperator";
|
|
||||||
import RegexDeleteOperator from "@/components/RegexDeleteOperator";
|
import RegexDeleteOperator from "@/components/RegexDeleteOperator";
|
||||||
import FlagOperator from "@/components/FlagOperator";
|
import FlagOperator from "@/components/FlagOperator";
|
||||||
import ScriptFilter from "@/components/ScriptFilter";
|
import ScriptFilter from "@/components/ScriptFilter";
|
||||||
import ScriptOperator from "@/components/ScriptOperator";
|
import ScriptOperator from "@/components/ScriptOperator";
|
||||||
import KeywordSortOperator from "@/components/KeywordSortOperator";
|
import RegexSortOperator from "@/components/RegexSortOperator";
|
||||||
|
|
||||||
const AVAILABLE_PROCESSORS = {
|
const AVAILABLE_PROCESSORS = {
|
||||||
"Flag Operator": {
|
"Flag Operator": {
|
||||||
@ -238,10 +236,6 @@ const AVAILABLE_PROCESSORS = {
|
|||||||
component: "RegionFilter",
|
component: "RegionFilter",
|
||||||
name: "区域过滤器"
|
name: "区域过滤器"
|
||||||
},
|
},
|
||||||
"Keyword Filter": {
|
|
||||||
component: "KeywordFilter",
|
|
||||||
name: "关键词过滤器"
|
|
||||||
},
|
|
||||||
"Regex Filter": {
|
"Regex Filter": {
|
||||||
component: "RegexFilter",
|
component: "RegexFilter",
|
||||||
name: "正则过滤器"
|
name: "正则过滤器"
|
||||||
@ -250,22 +244,14 @@ const AVAILABLE_PROCESSORS = {
|
|||||||
component: "SortOperator",
|
component: "SortOperator",
|
||||||
name: "节点排序"
|
name: "节点排序"
|
||||||
},
|
},
|
||||||
"Keyword Sort Operator": {
|
"Regex Sort Operator": {
|
||||||
component: "KeywordSortOperator",
|
component: "RegexSortOperator",
|
||||||
name: "关键词排序"
|
name: "正则排序"
|
||||||
},
|
|
||||||
"Keyword Rename Operator": {
|
|
||||||
component: "KeywordRenameOperator",
|
|
||||||
name: "关键词重命名"
|
|
||||||
},
|
},
|
||||||
"Regex Rename Operator": {
|
"Regex Rename Operator": {
|
||||||
component: "RegexRenameOperator",
|
component: "RegexRenameOperator",
|
||||||
name: "正则重命名"
|
name: "正则重命名"
|
||||||
},
|
},
|
||||||
"Keyword Delete Operator": {
|
|
||||||
component: "KeywordDeleteOperator",
|
|
||||||
name: "删除关键词"
|
|
||||||
},
|
|
||||||
"Regex Delete Operator": {
|
"Regex Delete Operator": {
|
||||||
component: "RegexDeleteOperator",
|
component: "RegexDeleteOperator",
|
||||||
name: "删除正则"
|
name: "删除正则"
|
||||||
@ -291,15 +277,12 @@ export default {
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
FlagOperator,
|
FlagOperator,
|
||||||
KeywordFilter,
|
|
||||||
RegexFilter,
|
RegexFilter,
|
||||||
RegionFilter,
|
RegionFilter,
|
||||||
TypeFilter,
|
TypeFilter,
|
||||||
SortOperator,
|
SortOperator,
|
||||||
KeywordRenameOperator,
|
RegexSortOperator,
|
||||||
KeywordSortOperator,
|
|
||||||
RegexRenameOperator,
|
RegexRenameOperator,
|
||||||
KeywordDeleteOperator,
|
|
||||||
RegexDeleteOperator,
|
RegexDeleteOperator,
|
||||||
ScriptFilter,
|
ScriptFilter,
|
||||||
ScriptOperator,
|
ScriptOperator,
|
||||||
@ -367,7 +350,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
processors() {
|
processors() {
|
||||||
return this.process.map(p => {
|
return this.process.filter(p => AVAILABLE_PROCESSORS[p.type]).map(p => {
|
||||||
return {
|
return {
|
||||||
component: AVAILABLE_PROCESSORS[p.type].component,
|
component: AVAILABLE_PROCESSORS[p.type].component,
|
||||||
args: p.args,
|
args: p.args,
|
||||||
|
@ -109,7 +109,7 @@
|
|||||||
fab
|
fab
|
||||||
>
|
>
|
||||||
<v-icon v-if="opened">mdi-close</v-icon>
|
<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>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-btn
|
<v-btn
|
||||||
@ -131,10 +131,10 @@
|
|||||||
<v-dialog fullscreen hide-overlay transition="dialog-bottom-transition" v-model="showProxyList" scrollable>
|
<v-dialog fullscreen hide-overlay transition="dialog-bottom-transition" v-model="showProxyList" scrollable>
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-title class="pa-0">
|
<v-card-title class="pa-0">
|
||||||
<v-toolbar dark color="primary">
|
<v-toolbar>
|
||||||
<v-icon>mdi-cloud</v-icon>
|
<v-icon>mdi-cloud</v-icon>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-toolbar-title>节点列表</v-toolbar-title>
|
<v-toolbar-title><h4>节点列表</h4></v-toolbar-title>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-toolbar-items>
|
<v-toolbar-items>
|
||||||
<v-btn icon @click="refreshProxyList" v-if="sub">
|
<v-btn icon @click="refreshProxyList" v-if="sub">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user