This commit is contained in:
hujiabin 2022-07-18 19:21:28 +08:00
commit c021e6ce48
113 changed files with 4861 additions and 0 deletions

3
.env Normal file
View File

@ -0,0 +1,3 @@
VUE_APP_API_HOST=10.0.235.4:9000
VUE_APP_TITLE=包管理系统
VUE_APP_TITLE_EN=PROJECT PACKAGE MANAGER

3
.env.production Normal file
View File

@ -0,0 +1,3 @@
VUE_APP_API_HOST=127.0.0.1:8000
VUE_APP_TITLE=包管理系统
VUE_APP_TITLE_EN=PROJECT PACKAGE MANAGER

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/node_modules/
.idea
dist/
/package-lock.json
/.env.development

19
README.md Normal file
View File

@ -0,0 +1,19 @@
# project package manager
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

5
babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/app'
]
}

63
package.json Normal file
View File

@ -0,0 +1,63 @@
{
"name": "hola_admin",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"build:dev": "vue-cli-service build --mode=test"
},
"dependencies": {
"autoprefixer": "^9.8.6",
"axios": "^0.21.1",
"ckeditor4-vue": "^1.2.0",
"core-js": "^3.6.5",
"crypto-js": "^4.0.0",
"echarts": "^5.2.1",
"element-ui": "^2.14.1",
"js-sha512": "^0.8.0",
"lodash": "^4.17.21",
"postcss": "^7.0.36",
"sprintf-js": "^1.1.2",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@2.1.0",
"vant": "^2.12.10",
"view-design": "^4.5.0",
"vue": "^2.6.11",
"vue-class-component": "^7.2.3",
"vue-i18n": "^8.22.2",
"vue-property-decorator": "^8.4.2",
"vue-router": "^3.2.0",
"vuedraggable": "2.20.0",
"vuex": "^3.4.0",
"vuex-class": "^0.3.2",
"vuex-persistedstate": "^4.0.0-beta.1",
"web3": "^1.7.4",
"weixin-js-sdk": "^1.6.0"
},
"devDependencies": {
"@tailwindcss/postcss7-compat": "^2.2.6",
"@types/echarts": "^4.9.10",
"@types/lodash": "^4.14.171",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-plugin-component": "^1.1.1",
"less": "^3.0.4",
"less-loader": "^5.0.0",
"svg-sprite-loader": "4.1.3",
"svgo": "^2.3.1",
"typescript": "~3.9.3",
"vue-cli-plugin-iview": "^2.0.0",
"vue-template-compiler": "^2.6.11",
"vuex-module-decorators": "^1.0.1"
},
"browserslist": [
"last 2 version",
"> 1%",
"iOS >= 7",
"Android > 4.1",
"Firefox > 20"
]
}

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

10
public/.htaccess Normal file
View File

@ -0,0 +1,10 @@
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.html [QSA,PT,L]
</IfModule>

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

16
public/index.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title><%= VUE_APP_TITLE %></title>
</head>
<body class="bg-gray-800">
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app" ></div>
<!-- built files will be auto injected -->
</body>
</html>

16
public/wap.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title><%= VUE_APP_TITLE %></title>
</head>
<body style="background:#f2f2f2">
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

21
src/app.d.ts vendored Normal file
View File

@ -0,0 +1,21 @@
import VueRouter from 'vue-router'
import { Toast } from "vant";
import * as _ from 'lodash'
declare module "vue/types/vue" {
import { Route } from "vue-router";
import {RequestInterface} from "@/views/admin/types/Types";
import VueI18n from "vue-i18n";
interface Vue {
$router : VueRouter;
$route : Route;
$http : RequestInterface,
$lang: Function,
$sprintf: Function,
$toast : Toast,
$_ : _,
$bus: any,
$isDev: boolean
}
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1632972646504" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11330" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M868.836693 520.02816V173.622613h-148.241066V28.603733H127.665493c-27.293013 0-49.411413 21.64736-49.411413 48.339627v870.11328c0 26.70592 22.1184 48.346453 49.411413 48.346453h33.355094l485.915306-475.374933h221.9008zM178.121387 745.424213c-20.473173 0.013653-37.0688-16.19968-37.082454-36.22912-0.013653-20.015787 16.56832-36.27008 37.034667-36.276906h0.047787c20.45952 0.013653 37.04832 16.267947 37.034666 36.276906s-16.5888 36.215467-37.034666 36.22912z m0-233.424213c-20.473173 0.013653-37.0688-16.213333-37.082454-36.235947s16.56832-36.263253 37.034667-36.276906h0.047787c20.45952 0.02048 37.04832 16.26112 37.034666 36.276906-0.013653 20.015787-16.5888 36.22912-37.034666 36.235947z m0-233.424213c-20.473173 0-37.055147-16.233813-37.055147-36.256427s16.5888-36.256427 37.055147-36.256427c20.45952 0 37.061973 16.233813 37.061973 36.256427s-16.602453 36.256427-37.061973 36.256427z" fill="#F27151" p-id="11331"></path><path d="M646.935893 520.02816l-485.915306 475.374933h658.404693c27.293013 0 49.411413-21.640533 49.411413-48.346453V520.02816H646.935893z" fill="#D56649" p-id="11332"></path><path d="M720.602453 173.622613h148.241067L720.602453 28.603733v145.01888z" fill="#F4C2BC" p-id="11333"></path><path d="M868.836693 318.641493V173.622613h-148.241066l148.241066 145.01888z" fill="#D66648" p-id="11334"></path><path d="M275.92704 253.699413h494.114133v48.339627h-494.114133v-48.339627z m0 152.55552h494.114133v48.339627h-494.114133V406.254933z m0 152.582827v48.346453h281.914027l49.411413-48.346453H275.92704zM275.92704 711.400107v48.3328h125.97248l49.411413-48.3328H275.92704z" fill="#C9C5DD" p-id="11335"></path><path d="M914.814293 674.829653c27.450027-39.478613 27.450027-134.22592 27.450027-134.22592H667.736747s-7.5776 94.76096 19.838293 134.22592c24.08448 34.6112 61.016747 54.59968 61.016747 80.909654v0.34816c0 26.344107-36.94592 44.455253-61.016747 79.0528-27.450027 39.478613-19.838293 131.679573-19.838293 131.679573h274.527573s0-92.03712-27.450027-131.52256c-24.08448-34.6112-68.642133-52.57216-68.642133-78.895787 0-26.29632 44.557653-46.96064 68.642133-81.57184z" fill="#FFFFFF" p-id="11336"></path><path d="M791.258453 540.596907h-96.085333s0 55.296 13.748907 94.747306c17.885867 51.452587 82.343253 67.106133 82.343253 119.74656 0 52.61312 0 171.677013 13.714773 171.677014 13.735253 0 13.735253-118.0672 13.735254-170.693974 0-52.61312 64.477867-69.440853 82.35008-120.907093 13.735253-39.46496 13.735253-94.59712 13.735253-94.59712h-123.528533v0.027307z" fill="#F9C82D" p-id="11337"></path><path d="M940.48256 943.158613H668.719787c-13.63968 0-24.705707 10.601813-24.705707 23.681707s11.066027 23.681707 24.705707 23.681707h271.762773c13.653333 0 24.705707-10.601813 24.705707-23.681707s-11.052373-23.681707-24.705707-23.681707z m0-426.236586H668.719787c-9.09312 0.08192-17.38752 4.983467-21.531307 12.731733h314.866347c-4.164267-7.76192-12.465493-12.670293-21.572267-12.731733z m-293.29408 12.731733a22.309547 22.309547 0 0 0-3.1744 10.949973c0 13.079893 11.066027 23.681707 24.705707 23.681707h271.762773c13.653333 0 24.705707-10.601813 24.705707-23.681707a22.65088 22.65088 0 0 0-3.13344-10.949973H647.18848z" fill="#3BAFDA" p-id="11338"></path></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1632972631189" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10965" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M190.57142832 174.5h642.85714336c70.71428584 0 128.57142832 57.85714248 128.57142832 128.57142832V865.57142832c0 45-35.35714248 80.35714248-80.35714248 80.35714336H190.57142832c-70.71428584 0-128.57142832-57.85714248-128.57142832-128.5714292v-514.28571416c0-70.71428584 57.85714248-128.57142832 128.57142832-128.57142832z" fill="#FFBB96" p-id="10966"></path><path d="M190.57142832 174.5h642.85714336c70.71428584 0 128.57142832 57.85714248 128.57142832 128.57142832V704.85714248l-128.57142832 115.71428584-3.21428584 3.21428584-128.57142832 122.14285752H190.57142832c-70.71428584 0-128.57142832-57.85714248-128.57142832-128.5714292v-514.28571416c0-70.71428584 57.85714248-128.57142832 128.57142832-128.57142832z" fill="#CF5A1A" p-id="10967"></path><path d="M190.57142832 174.5h642.85714336c70.71428584 0 128.57142832 57.85714248 128.57142832 128.57142832V704.85714248h-176.78571416c-45 0-80.35714248 35.35714248-80.35714336 80.35714336v160.71428584H190.57142832c-70.71428584 0-128.57142832-57.85714248-128.57142832-128.5714292v-514.28571416c0-70.71428584 57.85714248-128.57142832 128.57142832-128.57142832z" fill="#FF7A45" p-id="10968"></path><path d="M351.28571416 78.07142832c19.28571416 0 32.14285752 12.85714248 32.14285752 32.14285752v147.85714248c0 19.28571416-12.85714248 32.14285752-32.14285752 32.14285752s-32.14285752-12.85714248-32.14285664-32.14285752V110.21428584c0-19.28571416 12.85714248-32.14285752 32.14285664-32.14285752zM672.71428584 78.07142832c19.28571416 0 32.14285752 12.85714248 32.14285664 32.14285752v147.85714248c0 19.28571416-12.85714248 32.14285752-32.14285664 32.14285752s-32.14285752-12.85714248-32.14285752-32.14285752V110.21428584c0-19.28571416 12.85714248-32.14285752 32.14285752-32.14285752z" fill="#FFBB96" p-id="10969"></path><path d="M335.21428584 495.92857168m-48.21428584-1e-8a48.21428584 48.21428584 0 1 0 96.42857168 0 48.21428584 48.21428584 0 1 0-96.42857168 0Z" fill="#FFBB96" p-id="10970"></path><path d="M512 463.78571416h192.85714248c19.28571416 0 32.14285752 12.85714248 32.14285752 32.14285752s-12.85714248 32.14285752-32.14285752 32.14285665h-192.85714248c-19.28571416 0-32.14285752-12.85714248-32.14285752-32.14285665s12.85714248-32.14285752 32.14285752-32.14285752zM512 624.5h192.85714248c19.28571416 0 32.14285752 12.85714248 32.14285752 32.14285752s-12.85714248 32.14285752-32.14285752 32.14285664h-192.85714248c-19.28571416 0-32.14285752-12.85714248-32.14285752-32.14285664s12.85714248-32.14285752 32.14285752-32.14285752z" fill="#FFBB96" p-id="10971"></path><path d="M335.21428584 656.64285752m-48.21428584 0a48.21428584 48.21428584 0 1 0 96.42857168 0 48.21428584 48.21428584 0 1 0-96.42857168 0Z" fill="#FFBB96" p-id="10972"></path></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1632972704033" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15193" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M295.55 926.53c-30.12 0-54.59-24.5-54.59-54.59V144c0-30.1 24.47-54.59 54.59-54.59H732.3c30.09 0 54.59 24.5 54.59 54.59v727.93c0 30.09-24.5 54.59-54.59 54.59H295.55z" fill="#48CFAD" p-id="15194"></path><path d="M710.31 676.69l-1.52-0.48c-25.75-6.98-44.65-13.62-56.03-18.02-40.28-15.49-50.81-26.69-53.47-31.96-0.08-0.19-0.16-0.4-0.26-0.59l-0.35-49.29-172.15 1.97-0.35 47.32c-0.08 0.19-0.16 0.4-0.24 0.59-2.69 5.28-13.17 16.47-53.47 31.99-11.36 4.37-30.28 11.01-55.82 17.94l-1.73 0.53c-23.25 7.76-38.87 29.54-38.87 54.11v68.32h473.14V730.8c-0.01-24.57-15.63-46.35-38.88-54.11z" fill="#5D9CEC" p-id="15195"></path><path d="M646.68 486.31c0 87.12-83.06 170.87-134.09 170.87-51.02 0-134.06-83.76-134.06-170.87s60.03-144.59 134.06-144.59c74.06 0 134.09 57.47 134.09 144.59z" fill="#EAC6BB" p-id="15196"></path><path d="M598.99 325.97c-22.82-20.66-99.25-46.01-175.48 0-38.6 23.27-66.7 126.17-66.7 126.17s75.31 42.68 173.32-3.44c58.67-27.62 57.37-13.14 74.11 11.99 9.94 14.9 42.44 25.62 42.44 25.62s25.27-94.32-47.69-160.34z" fill="#A85D5D" p-id="15197"></path><path d="M732.31 71.23H295.55c-40.23 0-72.8 32.57-72.8 72.77v727.93c0 40.2 32.57 72.8 72.8 72.8H732.3c40.2 0 72.77-32.6 72.77-72.8V144c0.01-40.2-32.57-72.77-72.76-72.77z m36.38 800.7c0 20.07-16.34 36.39-36.39 36.39H295.55c-20.07 0-36.41-16.32-36.41-36.39V144c0-20.07 16.34-36.39 36.41-36.39H732.3c20.05 0 36.39 16.31 36.39 36.39v727.93z" fill="#434A54" p-id="15198"></path><path d="M690.05 144c-11.97 0-22.79 4.93-30.55 12.85-7.76-7.92-18.58-12.85-30.58-12.85-23.62 0-42.79 19.17-42.79 42.79 0 48.91 73.36 91.7 73.36 91.7s73.33-42.79 73.33-91.7c0.02-23.62-19.12-42.79-42.77-42.79z" fill="#ED5564" p-id="15199"></path><path d="M240.96 762.74v109.19c0 30.09 24.47 54.59 54.59 54.59H732.3c30.09 0 54.59-24.5 54.59-54.59V762.74H240.96z" fill="#434A54" p-id="15200"></path><path d="M513.93 799.13c-30.18 0-54.59 24.44-54.59 54.59 0 30.15 24.42 54.59 54.59 54.59 30.15 0 54.59-24.44 54.59-54.59 0-30.14-24.44-54.59-54.59-54.59z m0 72.8c-10.05 0-18.21-8.16-18.21-18.21 0-10.02 8.16-18.18 18.21-18.18 10.02 0 18.18 8.16 18.18 18.18 0 10.05-8.16 18.21-18.18 18.21z" fill="#656D78" p-id="15201"></path></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1632972602532" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8591" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M287 62h530.35714248C862.35714248 62 897.71428584 97.35714248 897.71428584 142.35714248V447.71428584H158.42857168V190.57142832c0-70.71428584 57.85714248-128.57142832 128.57142832-128.57142832z" fill="#B5F5EC" p-id="8592"></path><path d="M647 62H238.78571416C193.78571416 62 158.42857168 97.35714248 158.42857168 142.35714248v739.28571504C158.42857168 926.64285752 193.78571416 962 238.78571416 962h575.35714336c45 0 80.35714248-35.35714248 80.35714248-80.35714248V309.5L647 62z" fill="#36CFC9" p-id="8593"></path><path d="M650.21428584 62v186.42857168c0 32.14285752 25.71428584 61.07142832 61.07142832 61.07142832H897.71428584L650.21428584 62z" fill="#08979C" p-id="8594"></path><path d="M335.21428584 415.57142832m-48.21428584 0a48.21428584 48.21428584 0 1 0 96.42857168 0 48.21428584 48.21428584 0 1 0-96.42857168 0Z" fill="#B5F5EC" p-id="8595"></path><path d="M512 383.42857168h192.85714248c19.28571416 0 32.14285752 12.85714248 32.14285752 32.14285664s-12.85714248 32.14285752-32.14285752 32.14285752h-192.85714248c-19.28571416 0-32.14285752-12.85714248-32.14285752-32.14285752s12.85714248-32.14285752 32.14285752-32.14285664zM512 544.14285752h192.85714248c19.28571416 0 32.14285752 12.85714248 32.14285752 32.14285664s-12.85714248 32.14285752-32.14285752 32.14285752h-192.85714248c-19.28571416 0-32.14285752-12.85714248-32.14285752-32.14285752s12.85714248-32.14285752 32.14285752-32.14285664zM512 704.85714248h192.85714248c19.28571416 0 32.14285752 12.85714248 32.14285752 32.14285752s-12.85714248 32.14285752-32.14285752 32.14285752h-192.85714248c-19.28571416 0-32.14285752-12.85714248-32.14285752-32.14285752s12.85714248-32.14285752 32.14285752-32.14285752z" fill="#B5F5EC" p-id="8596"></path><path d="M335.21428584 576.28571416m-48.21428584 0a48.21428584 48.21428584 0 1 0 96.42857168 0 48.21428584 48.21428584 0 1 0-96.42857168 0Z" fill="#B5F5EC" p-id="8597"></path><path d="M335.21428584 737m-48.21428584 0a48.21428584 48.21428584 0 1 0 96.42857168 0 48.21428584 48.21428584 0 1 0-96.42857168 0Z" fill="#B5F5EC" p-id="8598"></path></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
src/assets/image/del.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
src/assets/image/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 KiB

17
src/assets/js/wwlogin.js Normal file
View File

@ -0,0 +1,17 @@
! function (a, b, c) {
function d(c) {
var d = b.createElement("iframe"),
e = c.goto;
e += c.style ? "&style=" + c.style : "", e += c.href ? "&href=" + c.href : "", d.src = e, d.frameBorder = "0", d.allowTransparency = "true", d.scrolling = "no", d.width = "300px", d.height = "320px", d.style.transform = 'scale(0.8) translateY(-10%)';
var f = b.getElementById(c.id);
f.innerHTML = "", f.appendChild(d), d.onload = function () {
a.addEventListener("message", function (b) {
if(b.data && typeof b.data == "string" && b.data.substring(0,4) != 'http'){
return false;
}
b.data && b.origin.indexOf("work.weixin.qq.com") > -1 && (a.location.href = b.data)
}), d.contentWindow.postMessage("ask_usePostMessage", "*")
}
}
a.WwLogin = d
}(window, document);

View File

@ -0,0 +1,13 @@
html,
body,
#app {
width: 100%;
height: 100%;
font-family: arial;
background-color: white;
}
* {
margin: 0;
padding: 0;
}

View File

@ -0,0 +1,33 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
.max-w-8xl {
max-width: 90rem;
}
.max-w-4xl {
max-width: 45rem;
}
.max-w-2xl {
max-width: 22.5rem;
}
.lg\:h-\(screen-14\) {
height: calc(100vh - 3.5rem);
}
@supports ((position: -webkit-sticky) or (position:sticky)) {
@media (min-width:1024px) {
.sticky\?lg\:h-screen {
height:100vh!important
}
lg\:.h-\(screen-14\) {
height: calc(100vh - 3.5rem);
}
.sticky\?lg\:h-\(screen-18\) {
height: calc(100vh - 4.5rem);
}
}
}

View File

@ -0,0 +1,166 @@
<template>
<div class="dndList">
<div :style="{width:width1}" class="dndList-list">
<h3>{{ list1Title }}</h3>
<draggable :set-data="setData" :list="list1" group="article" class="dragArea">
<div v-for="element in list1" :key="element.id" class="list-complete-item" >
<div class="list-complete-item-handle">
{{ element.scriptName }}
</div>
<div style="position:absolute;right:0px;">
<span style="float: right ;margin-top: -20px;margin-right:5px;" @click="deleteEle(element)">
<i style="color:#ff4949" class="el-icon-delete" />
</span>
</div>
</div>
</draggable>
</div>
<div :style="{width:width2}" class="dndList-list">
<h3>{{ list2Title }}</h3>
<draggable :list="list2" group="article" class="dragArea">
<div v-for="element in list2" :key="element.id" class="list-complete-item">
<div class="list-complete-item-handle2" @click="pushEle(element)">
{{ element.scriptName }}
</div>
</div>
</draggable>
</div>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: 'DndList',
components: { draggable },
props: {
list1: {
type: Array,
default() {
return []
}
},
list2: {
type: Array,
default() {
return []
}
},
list1Title: {
type: String,
default: 'list1'
},
list2Title: {
type: String,
default: 'list2'
},
width1: {
type: String,
default: '48%'
},
width2: {
type: String,
default: '48%'
}
},
methods: {
isNotInList1(v) {
return this.list1.every(k => v.id !== k.id)
},
isNotInList2(v) {
return this.list2.every(k => v.id !== k.id)
},
deleteEle(ele) {
for (const item of this.list1) {
if (item.id === ele.id) {
const index = this.list1.indexOf(item)
this.list1.splice(index, 1)
break
}
}
if (this.isNotInList2(ele)) {
this.list2.unshift(ele)
}
},
pushEle(ele) {
for (const item of this.list2) {
if (item.id === ele.id) {
const index = this.list2.indexOf(item)
this.list2.splice(index, 1)
break
}
}
if (this.isNotInList1(ele)) {
this.list1.push(ele)
}
},
setData(dataTransfer) {
// to avoid Firefox bug
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
dataTransfer.setData('Text', '')
}
}
}
</script>
<style lang="less" scoped>
.dndList {
background: #fff;
padding-bottom: 40px;
&:after {
content: "";
display: table;
clear: both;
}
.dndList-list {
float: left;
padding-bottom: 30px;
&:first-of-type {
margin-right: 2%;
}
.dragArea {
margin-top: 15px;
min-height: 50px;
padding-bottom: 30px;
}
}
}
.list-complete-item {
cursor: pointer;
position: relative;
font-size: 14px;
padding: 5px 12px;
margin-top: 4px;
border: 1px solid #bfcbd9;
transition: all 1s;
}
.list-complete-item-handle {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 50px;
}
.list-complete-item-handle2 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 20px;
}
.list-complete-item.sortable-chosen {
background: #4AB7BD;
}
.list-complete-item.sortable-ghost {
background: #30B08F;
}
.list-complete-enter,
.list-complete-leave-active {
opacity: 0;
}
</style>

View File

@ -0,0 +1,109 @@
<template>
<div class="board-column">
<div class="board-column-header">
{{ headerText }}
</div>
<draggable
:list="list"
v-bind="$attrs"
class="board-column-content"
:set-data="setData"
@start="start"
@end="end"
>
<div v-for="element in list" :key="element.id" class="board-item">
{{ element.name }} - {{element.user.name}}
</div>
</draggable>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: 'DragKanbanDemo',
components: {
draggable
},
props: {
headerText: {
type: String,
default: 'Header'
},
options: {
type: Object,
default() {
return {}
}
},
list: {
type: Array,
default() {
return []
}
}
},
methods: {
setData(dataTransfer) {
// to avoid Firefox bug
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
dataTransfer.setData('Text', '')
},
start(e){
this.$emit('start')
},
end(){
this.$emit('end')
}
}
}
</script>
<style lang="less" scoped>
.board-column {
min-width: 145px;
min-height: 100px;
height: auto;
overflow: hidden;
background: #f0f0f0;
/*background: black;*/
border-radius: 3px;
.board-column-header {
height: 50px;
line-height: 50px;
overflow: hidden;
padding: 0 20px;
text-align: center;
background: #374151;
color: #fff;
border-radius: 3px 3px 0 0;
}
.board-column-content {
height: 660px;
overflow-y: auto;
border: 10px solid transparent;
min-height: 660px;
display: flex;
justify-content: flex-start;
flex-direction: column;
align-items: center;
.board-item {
cursor: pointer;
width: 100%;
min-height: 36px;
margin: 5px 0;
background-color: #fff;
text-align: left;
line-height: 30px;
padding: 5px 10px;
box-sizing: border-box;
box-shadow: 0px 1px 3px 0 rgba(0, 0, 0, 0.2);
}
}
}
</style>

View File

@ -0,0 +1,42 @@
<template>
<svg :class="svgClass" aria-hidden="true">
<use :xlink:href="iconName"></use>
</svg>
</template>
<script>
export default {
name: 'svg-icon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String
}
},
computed: {
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>

0
src/components/page.less Normal file
View File

72
src/components/page.ts Normal file
View File

@ -0,0 +1,72 @@
import { Component, Vue, Watch, Prop, Emit, PropSync } from 'vue-property-decorator'
import {
State,
Getter,
Action,
Mutation,
} from "vuex-class";
import { Table, TableColumn } from "view-design";
import Events from "@/utils/Events";
import { TableExportCsvParams } from "view-design/types/table";
import { stringify } from "postcss";
import Project from "@/views/home/pages/project/project";
@Component
export default class page extends Vue {
name : string = "CustomPage"
@Prop({type : Number, default : 0})
total : number;
@Prop({default : true})
showPaginate : boolean
@PropSync('page', {type : Number}) currentPage! : Number
@Prop({type : Number, default : 10})
pageSize : number;
@Prop({type : Array, default : () => []})
columns : TableColumn[];
@Prop({type : Array, default : () => []})
dataList : Array<any>;
@Prop({default : false})
rowKey : Boolean | string
@Prop({type: String, default: "暂无数据"})
noDataText: string;
sizeOpt : number[] = [20, 40, 80, 120];
get currentPageSize() {
return this.pageSize;
}
mounted() {
if (!this.sizeOpt.includes(this.pageSize)) {
this.sizeOpt.push(this.pageSize);
this.sizeOpt = this.sizeOpt.sort((a, b) => a - b);
}
}
public exportCsv(params : TableExportCsvParams) {
let table : Table = this.$refs["table"] as Table;
table.exportCsv(params);
}
public onPageSizeChange(size : number) {
this.$emit("update:pageSize", size);
}
@Emit(Events.ON_PAGE_CHANGE)
public onPageChange(page : number) : number {
return page;
}
@Emit(Events.ON_SELECTION_CHANGE)
public onSelectionChange(selection : any[]) : any[] {
return selection
}
}

19
src/components/page.vue Normal file
View File

@ -0,0 +1,19 @@
<template>
<div class="custom-page">
<Table :columns="columns" :data="dataList" ref="table"
@on-selection-change="onSelectionChange" :row-key="rowKey"
:no-data-text="noDataText"
stripe highlight-row border>
<template v-for="column in columns" :slot="column.slot?column.slot:''" slot-scope="params" >
<slot :name="column.slot?column.slot:''" v-bind="params" ></slot>
</template>
</Table>
<Page style="margin-top: 10px;float: right" :current.sync="currentPage" :total="total" :page-size="currentPageSize" show-total
show-elevator show-sizer @on-page-size-change="onPageSizeChange"
:page-size-opts="sizeOpt" v-if="showPaginate"
prev-text="上一页" next-text="下一页"></Page>
</div>
</template>
<script lang="ts" src="./page.ts"></script>
<style scoped lang="less" src="./page.less"></style>

10
src/icons/index.ts Normal file
View File

@ -0,0 +1,10 @@
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon.vue'// svg component
// register globally
Vue.component('svg-icon', SvgIcon)
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)

1
src/icons/svg/device.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1632972704033" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15193" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M295.55 926.53c-30.12 0-54.59-24.5-54.59-54.59V144c0-30.1 24.47-54.59 54.59-54.59H732.3c30.09 0 54.59 24.5 54.59 54.59v727.93c0 30.09-24.5 54.59-54.59 54.59H295.55z" fill="#48CFAD" p-id="15194"></path><path d="M710.31 676.69l-1.52-0.48c-25.75-6.98-44.65-13.62-56.03-18.02-40.28-15.49-50.81-26.69-53.47-31.96-0.08-0.19-0.16-0.4-0.26-0.59l-0.35-49.29-172.15 1.97-0.35 47.32c-0.08 0.19-0.16 0.4-0.24 0.59-2.69 5.28-13.17 16.47-53.47 31.99-11.36 4.37-30.28 11.01-55.82 17.94l-1.73 0.53c-23.25 7.76-38.87 29.54-38.87 54.11v68.32h473.14V730.8c-0.01-24.57-15.63-46.35-38.88-54.11z" fill="#5D9CEC" p-id="15195"></path><path d="M646.68 486.31c0 87.12-83.06 170.87-134.09 170.87-51.02 0-134.06-83.76-134.06-170.87s60.03-144.59 134.06-144.59c74.06 0 134.09 57.47 134.09 144.59z" fill="#EAC6BB" p-id="15196"></path><path d="M598.99 325.97c-22.82-20.66-99.25-46.01-175.48 0-38.6 23.27-66.7 126.17-66.7 126.17s75.31 42.68 173.32-3.44c58.67-27.62 57.37-13.14 74.11 11.99 9.94 14.9 42.44 25.62 42.44 25.62s25.27-94.32-47.69-160.34z" fill="#A85D5D" p-id="15197"></path><path d="M732.31 71.23H295.55c-40.23 0-72.8 32.57-72.8 72.77v727.93c0 40.2 32.57 72.8 72.8 72.8H732.3c40.2 0 72.77-32.6 72.77-72.8V144c0.01-40.2-32.57-72.77-72.76-72.77z m36.38 800.7c0 20.07-16.34 36.39-36.39 36.39H295.55c-20.07 0-36.41-16.32-36.41-36.39V144c0-20.07 16.34-36.39 36.41-36.39H732.3c20.05 0 36.39 16.31 36.39 36.39v727.93z" fill="#434A54" p-id="15198"></path><path d="M690.05 144c-11.97 0-22.79 4.93-30.55 12.85-7.76-7.92-18.58-12.85-30.58-12.85-23.62 0-42.79 19.17-42.79 42.79 0 48.91 73.36 91.7 73.36 91.7s73.33-42.79 73.33-91.7c0.02-23.62-19.12-42.79-42.77-42.79z" fill="#ED5564" p-id="15199"></path><path d="M240.96 762.74v109.19c0 30.09 24.47 54.59 54.59 54.59H732.3c30.09 0 54.59-24.5 54.59-54.59V762.74H240.96z" fill="#434A54" p-id="15200"></path><path d="M513.93 799.13c-30.18 0-54.59 24.44-54.59 54.59 0 30.15 24.42 54.59 54.59 54.59 30.15 0 54.59-24.44 54.59-54.59 0-30.14-24.44-54.59-54.59-54.59z m0 72.8c-10.05 0-18.21-8.16-18.21-18.21 0-10.02 8.16-18.18 18.21-18.18 10.02 0 18.18 8.16 18.18 18.18 0 10.05-8.16 18.21-18.18 18.21z" fill="#656D78" p-id="15201"></path></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1644572465653" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2032" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M511.333 63.333c-247.424 0-448 200.576-448 448s200.576 448 448 448 448-200.576 448-448-200.576-448-448-448z m0 832c-51.868 0-102.15-10.144-149.451-30.15-36.011-15.231-69.123-35.67-98.812-60.897 12.177-31.985 42.226-63.875 84.223-88.903C396.189 686.243 456.222 669.53 512 669.53c55.631 0 115.416 16.658 164.026 45.703 41.762 24.953 71.689 56.812 83.863 88.804-29.764 25.342-62.976 45.865-99.106 61.146-47.299 20.006-97.582 30.15-149.45 30.15z m296.268-139.658c-20.493-35.937-54.353-68.855-98.747-95.381C649.75 624.979 579.839 605.53 512 605.53c-67.964 0-138.094 19.488-197.471 54.875-44.644 26.606-78.656 59.594-99.195 95.586-23.835-28.755-43.234-60.652-57.85-95.208-20.006-47.3-30.15-97.583-30.15-149.451s10.144-102.15 30.15-149.451c19.337-45.719 47.034-86.792 82.321-122.078 35.286-35.287 76.359-62.983 122.078-82.321 47.3-20.006 97.583-30.15 149.451-30.15 51.868 0 102.15 10.144 149.451 30.15 45.719 19.337 86.792 47.034 122.078 82.321 35.287 35.286 62.983 76.359 82.321 122.078 20.006 47.3 30.15 97.583 30.15 149.451s-10.144 102.15-30.15 149.451c-14.563 34.429-33.869 66.22-57.583 94.892z" fill="" p-id="2033"></path><path d="M512 220.223c-88.224 0-160 71.776-160 160s71.776 160 160 160c88.225 0 160-71.775 160-160s-71.775-160-160-160z m0 256c-52.935 0-96-43.065-96-96s43.065-96 96-96 96 43.065 96 96-43.065 96-96 96z" fill="" p-id="2034"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1644572535996" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2944" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M730.033231 915.692308H236.307692a108.465231 108.465231 0 0 1-108.307692-108.307693v-275.692307a108.465231 108.465231 0 0 1 78.966154-104.369231V216.694154A69.080615 69.080615 0 0 1 276.007385 147.692308h473.757538a69.080615 69.080615 0 0 1 69.001846 69.001846v211.259077A108.465231 108.465231 0 0 1 896 531.692308v275.692307A108.465231 108.465231 0 0 1 787.692308 915.692308zM787.692308 856.615385a49.270154 49.270154 0 0 0 49.230769-49.23077v-275.692307a49.309538 49.309538 0 0 0-43.323077-48.876308 29.774769 29.774769 0 0 1-4.332308 0.315077 29.735385 29.735385 0 0 1-6.301538-0.669539H242.806154a29.617231 29.617231 0 0 1-6.301539 0.669539 29.696 29.696 0 0 1-5.04123-0.433231A49.309538 49.309538 0 0 0 187.076923 531.692308v275.692307A49.270154 49.270154 0 0 0 236.307692 856.615385h168.054154v-128.393847a29.538462 29.538462 0 0 1 29.538462-29.538461h296.132923a29.538462 29.538462 0 0 1 29.538461 29.538461v128.393847z m-87.236923 0v-98.855385h-237.016616v98.855385z m59.076923-433.23077V216.694154a9.924923 9.924923 0 0 0-9.924923-9.924923H275.849846a9.924923 9.924923 0 0 0-9.964308 9.924923v206.690461z m-483.682462 177.23077a29.577846 29.577846 0 0 1-29.577846-29.577847 29.577846 29.577846 0 0 1 29.577846-29.538461h59.076923a29.538462 29.538462 0 0 1 29.538462 29.538461 29.538462 29.538462 0 0 1-29.538462 29.577847z" p-id="2945"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

2
src/icons/svg/login.svg Normal file
View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1657867308126" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1964" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
</style></defs><path d="M517.77536 972.8c133.98016-30.19776 211.70176-70.42048 301.2608-156.3136 29.32736-28.11904 174.04928-63.68256 190.4128-100.83328 23.72608-53.8624-58.2144-115.72224-58.2144-178.40128 0-240.54784-194.06848-435.5584-433.4592-435.5584-239.38048 0-433.44896 195.01056-433.44896 435.5584 0 68.21888-86.13888 124.14976-58.3168 181.62688 15.28832 31.56992 164.1472 73.216 189.51168 97.60768C305.08032 902.68672 383.55968 942.60224 517.77536 972.8z" fill="#FFD797" p-id="1965"></path><path d="M908.77952 65.5872c-0.88064 1.40288 79.0528 276.7872 32.68608 379.32032a439.0912 439.0912 0 0 1 9.76896 92.34432c0 62.6688 81.94048 124.54912 58.2144 178.40128-1.71008 3.8912-4.88448 7.74144-9.216 11.55072-146.59584-39.44448-297.6768-4.31104-453.25312 105.41056l-5.64224 4.00384H494.592C336.24064 723.2 182.55872 686.92992 33.536 727.78752c-3.54304-3.0208-6.11328-5.9904-7.5264-8.9088-27.82208-57.47712 58.3168-113.408 58.3168-181.62688 0-27.4944 2.53952-54.39488 7.38304-80.47616-59.62752-90.97216 27.136-389.7344 26.22464-391.18848 81.39776-14.41792 159.06816 8.76544 233.03168 69.5296a430.40768 430.40768 0 0 1 166.8096-33.42336 430.44864 430.44864 0 0 1 160.94208 31.01696C751.77984 73.728 828.4672 51.36384 908.77952 65.5872z" fill="#FF6F24" p-id="1966"></path><path d="M283.1872 315.51488c31.10912-104.9088-23.97184-186.24512-165.25312-244.0192 1.024 1.65888-77.7216 340.93056 28.89728 383.11936-3.51232 2.92864 41.94304-43.42784 136.35584-139.10016z m461.48608 0c-31.10912-104.9088 23.97184-186.24512 165.24288-244.0192-1.024 1.65888 77.7216 340.93056-28.89728 383.11936 3.52256 2.92864-41.9328-43.42784-136.3456-139.10016z" fill="#922101" opacity=".574" p-id="1967"></path><path d="M647.69024 629.00224c-0.21504-60.29312 44.56448-83.94752 134.3488-70.95296-2.78528 3.072 6.78912 44.93312-40.51968 69.15072-11.264 5.76512-42.53696 6.36928-93.82912 1.80224z m-251.46368 0c0.22528-60.29312-44.55424-83.94752-134.33856-70.95296 2.78528 3.072-6.79936 44.93312 40.50944 69.15072 11.264 5.76512 42.53696 6.36928 93.82912 1.80224z" fill="#350000" p-id="1968"></path><path d="M743.10656 565.72928l0.04096 0.1536c0.78848 3.42016 1.20832 6.99392 1.20832 10.6496 0 23.4496-17.14176 42.82368-39.424 46.00832-3.42016 0.1536-7.2192 0.21504-11.39712 0.18432a45.93664 45.93664 0 0 1-36.7104-26.61376c10.25024-24.832 39.0144-34.95936 86.28224-30.38208z m-459.84768 0l-0.04096 0.1536a47.13472 47.13472 0 0 0-1.20832 10.6496c0 23.4496 17.14176 42.82368 39.424 46.00832 3.40992 0.1536 7.2192 0.21504 11.39712 0.18432a45.93664 45.93664 0 0 0 36.7104-26.61376c-10.26048-24.832-39.0144-34.95936-86.28224-30.38208z" fill="#923320" p-id="1969"></path><path d="M328.6528 574.72c-4.12672 10.24-6.25664 17.43872-6.38976 21.58592-0.23552 6.89152 1.8944 12.5952 6.38976 17.1008 0.768 0.54272 6.44096-5.30432 6.144-17.87904-0.1024-3.72736-2.1504-10.6496-6.144-20.80768z m372.72576 0c-4.12672 10.24-6.2464 17.43872-6.38976 21.58592-0.22528 6.89152 1.90464 12.5952 6.38976 17.1008 0.77824 0.54272 6.44096-5.30432 6.144-17.87904-0.09216-3.72736-2.14016-10.6496-6.144-20.80768z" fill="#FFFFFF" opacity=".632" p-id="1970"></path><path d="M520.42752 863.8464c37.24288 0 67.4304-27.81184 67.4304-62.1056 0-34.304-134.8608-34.304-134.8608 0s30.19776 62.1056 67.4304 62.1056z" fill="#040C12" p-id="1971"></path><path d="M520.15104 152.02304c8.86784 146.97472 40.57088 238.83776 95.10912 275.57888l2.19136 1.4336-0.24576 0.07168c-6.42048 2.53952-71.46496 73.5744-96.80896 138.2912-0.0512 0.08192-0.09216 0-0.12288-0.26624-0.04096 0.256-0.08192 0.34816-0.12288 0.26624-25.35424-64.7168-90.38848-135.75168-96.8192-138.2912l-0.24576-0.07168 2.2016-1.4336c53.71904-36.1984 85.1968-125.87008 94.45376-269.03552l0.4096-6.5536z" fill="#C74D22" p-id="1972"></path><path d="M735.15008 721.8688c49.55136 0 89.72288-17.17248 89.72288-38.3488s-40.17152-38.3488-89.72288-38.3488c-49.5616 0-89.72288 17.17248-89.72288 38.3488s40.17152 38.3488 89.72288 38.3488z m-443.89376 0c49.5616 0 89.72288-17.17248 89.72288-38.3488s-40.17152-38.3488-89.72288-38.3488-89.72288 17.17248-89.72288 38.3488 40.17152 38.3488 89.72288 38.3488z" fill="#C53028" fill-opacity=".3" p-id="1973"></path></svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1632972602532" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8591" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M287 62h530.35714248C862.35714248 62 897.71428584 97.35714248 897.71428584 142.35714248V447.71428584H158.42857168V190.57142832c0-70.71428584 57.85714248-128.57142832 128.57142832-128.57142832z" fill="#B5F5EC" p-id="8592"></path><path d="M647 62H238.78571416C193.78571416 62 158.42857168 97.35714248 158.42857168 142.35714248v739.28571504C158.42857168 926.64285752 193.78571416 962 238.78571416 962h575.35714336c45 0 80.35714248-35.35714248 80.35714248-80.35714248V309.5L647 62z" fill="#36CFC9" p-id="8593"></path><path d="M650.21428584 62v186.42857168c0 32.14285752 25.71428584 61.07142832 61.07142832 61.07142832H897.71428584L650.21428584 62z" fill="#08979C" p-id="8594"></path><path d="M335.21428584 415.57142832m-48.21428584 0a48.21428584 48.21428584 0 1 0 96.42857168 0 48.21428584 48.21428584 0 1 0-96.42857168 0Z" fill="#B5F5EC" p-id="8595"></path><path d="M512 383.42857168h192.85714248c19.28571416 0 32.14285752 12.85714248 32.14285752 32.14285664s-12.85714248 32.14285752-32.14285752 32.14285752h-192.85714248c-19.28571416 0-32.14285752-12.85714248-32.14285752-32.14285752s12.85714248-32.14285752 32.14285752-32.14285664zM512 544.14285752h192.85714248c19.28571416 0 32.14285752 12.85714248 32.14285752 32.14285664s-12.85714248 32.14285752-32.14285752 32.14285752h-192.85714248c-19.28571416 0-32.14285752-12.85714248-32.14285752-32.14285752s12.85714248-32.14285752 32.14285752-32.14285664zM512 704.85714248h192.85714248c19.28571416 0 32.14285752 12.85714248 32.14285752 32.14285752s-12.85714248 32.14285752-32.14285752 32.14285752h-192.85714248c-19.28571416 0-32.14285752-12.85714248-32.14285752-32.14285752s12.85714248-32.14285752 32.14285752-32.14285752z" fill="#B5F5EC" p-id="8596"></path><path d="M335.21428584 576.28571416m-48.21428584 0a48.21428584 48.21428584 0 1 0 96.42857168 0 48.21428584 48.21428584 0 1 0-96.42857168 0Z" fill="#B5F5EC" p-id="8597"></path><path d="M335.21428584 737m-48.21428584 0a48.21428584 48.21428584 0 1 0 96.42857168 0 48.21428584 48.21428584 0 1 0-96.42857168 0Z" fill="#B5F5EC" p-id="8598"></path></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

1
src/icons/svg/script.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1632972646504" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11330" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M868.836693 520.02816V173.622613h-148.241066V28.603733H127.665493c-27.293013 0-49.411413 21.64736-49.411413 48.339627v870.11328c0 26.70592 22.1184 48.346453 49.411413 48.346453h33.355094l485.915306-475.374933h221.9008zM178.121387 745.424213c-20.473173 0.013653-37.0688-16.19968-37.082454-36.22912-0.013653-20.015787 16.56832-36.27008 37.034667-36.276906h0.047787c20.45952 0.013653 37.04832 16.267947 37.034666 36.276906s-16.5888 36.215467-37.034666 36.22912z m0-233.424213c-20.473173 0.013653-37.0688-16.213333-37.082454-36.235947s16.56832-36.263253 37.034667-36.276906h0.047787c20.45952 0.02048 37.04832 16.26112 37.034666 36.276906-0.013653 20.015787-16.5888 36.22912-37.034666 36.235947z m0-233.424213c-20.473173 0-37.055147-16.233813-37.055147-36.256427s16.5888-36.256427 37.055147-36.256427c20.45952 0 37.061973 16.233813 37.061973 36.256427s-16.602453 36.256427-37.061973 36.256427z" fill="#F27151" p-id="11331"></path><path d="M646.935893 520.02816l-485.915306 475.374933h658.404693c27.293013 0 49.411413-21.640533 49.411413-48.346453V520.02816H646.935893z" fill="#D56649" p-id="11332"></path><path d="M720.602453 173.622613h148.241067L720.602453 28.603733v145.01888z" fill="#F4C2BC" p-id="11333"></path><path d="M868.836693 318.641493V173.622613h-148.241066l148.241066 145.01888z" fill="#D66648" p-id="11334"></path><path d="M275.92704 253.699413h494.114133v48.339627h-494.114133v-48.339627z m0 152.55552h494.114133v48.339627h-494.114133V406.254933z m0 152.582827v48.346453h281.914027l49.411413-48.346453H275.92704zM275.92704 711.400107v48.3328h125.97248l49.411413-48.3328H275.92704z" fill="#C9C5DD" p-id="11335"></path><path d="M914.814293 674.829653c27.450027-39.478613 27.450027-134.22592 27.450027-134.22592H667.736747s-7.5776 94.76096 19.838293 134.22592c24.08448 34.6112 61.016747 54.59968 61.016747 80.909654v0.34816c0 26.344107-36.94592 44.455253-61.016747 79.0528-27.450027 39.478613-19.838293 131.679573-19.838293 131.679573h274.527573s0-92.03712-27.450027-131.52256c-24.08448-34.6112-68.642133-52.57216-68.642133-78.895787 0-26.29632 44.557653-46.96064 68.642133-81.57184z" fill="#FFFFFF" p-id="11336"></path><path d="M791.258453 540.596907h-96.085333s0 55.296 13.748907 94.747306c17.885867 51.452587 82.343253 67.106133 82.343253 119.74656 0 52.61312 0 171.677013 13.714773 171.677014 13.735253 0 13.735253-118.0672 13.735254-170.693974 0-52.61312 64.477867-69.440853 82.35008-120.907093 13.735253-39.46496 13.735253-94.59712 13.735253-94.59712h-123.528533v0.027307z" fill="#F9C82D" p-id="11337"></path><path d="M940.48256 943.158613H668.719787c-13.63968 0-24.705707 10.601813-24.705707 23.681707s11.066027 23.681707 24.705707 23.681707h271.762773c13.653333 0 24.705707-10.601813 24.705707-23.681707s-11.052373-23.681707-24.705707-23.681707z m0-426.236586H668.719787c-9.09312 0.08192-17.38752 4.983467-21.531307 12.731733h314.866347c-4.164267-7.76192-12.465493-12.670293-21.572267-12.731733z m-293.29408 12.731733a22.309547 22.309547 0 0 0-3.1744 10.949973c0 13.079893 11.066027 23.681707 24.705707 23.681707h271.762773c13.653333 0 24.705707-10.601813 24.705707-23.681707a22.65088 22.65088 0 0 0-3.13344-10.949973H647.18848z" fill="#3BAFDA" p-id="11338"></path></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

1
src/icons/svg/task.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1632972631189" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10965" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M190.57142832 174.5h642.85714336c70.71428584 0 128.57142832 57.85714248 128.57142832 128.57142832V865.57142832c0 45-35.35714248 80.35714248-80.35714248 80.35714336H190.57142832c-70.71428584 0-128.57142832-57.85714248-128.57142832-128.5714292v-514.28571416c0-70.71428584 57.85714248-128.57142832 128.57142832-128.57142832z" fill="#FFBB96" p-id="10966"></path><path d="M190.57142832 174.5h642.85714336c70.71428584 0 128.57142832 57.85714248 128.57142832 128.57142832V704.85714248l-128.57142832 115.71428584-3.21428584 3.21428584-128.57142832 122.14285752H190.57142832c-70.71428584 0-128.57142832-57.85714248-128.57142832-128.5714292v-514.28571416c0-70.71428584 57.85714248-128.57142832 128.57142832-128.57142832z" fill="#CF5A1A" p-id="10967"></path><path d="M190.57142832 174.5h642.85714336c70.71428584 0 128.57142832 57.85714248 128.57142832 128.57142832V704.85714248h-176.78571416c-45 0-80.35714248 35.35714248-80.35714336 80.35714336v160.71428584H190.57142832c-70.71428584 0-128.57142832-57.85714248-128.57142832-128.5714292v-514.28571416c0-70.71428584 57.85714248-128.57142832 128.57142832-128.57142832z" fill="#FF7A45" p-id="10968"></path><path d="M351.28571416 78.07142832c19.28571416 0 32.14285752 12.85714248 32.14285752 32.14285752v147.85714248c0 19.28571416-12.85714248 32.14285752-32.14285752 32.14285752s-32.14285752-12.85714248-32.14285664-32.14285752V110.21428584c0-19.28571416 12.85714248-32.14285752 32.14285664-32.14285752zM672.71428584 78.07142832c19.28571416 0 32.14285752 12.85714248 32.14285664 32.14285752v147.85714248c0 19.28571416-12.85714248 32.14285752-32.14285664 32.14285752s-32.14285752-12.85714248-32.14285752-32.14285752V110.21428584c0-19.28571416 12.85714248-32.14285752 32.14285752-32.14285752z" fill="#FFBB96" p-id="10969"></path><path d="M335.21428584 495.92857168m-48.21428584-1e-8a48.21428584 48.21428584 0 1 0 96.42857168 0 48.21428584 48.21428584 0 1 0-96.42857168 0Z" fill="#FFBB96" p-id="10970"></path><path d="M512 463.78571416h192.85714248c19.28571416 0 32.14285752 12.85714248 32.14285752 32.14285752s-12.85714248 32.14285752-32.14285752 32.14285665h-192.85714248c-19.28571416 0-32.14285752-12.85714248-32.14285752-32.14285665s12.85714248-32.14285752 32.14285752-32.14285752zM512 624.5h192.85714248c19.28571416 0 32.14285752 12.85714248 32.14285752 32.14285752s-12.85714248 32.14285752-32.14285752 32.14285664h-192.85714248c-19.28571416 0-32.14285752-12.85714248-32.14285752-32.14285664s12.85714248-32.14285752 32.14285752-32.14285752z" fill="#FFBB96" p-id="10971"></path><path d="M335.21428584 656.64285752m-48.21428584 0a48.21428584 48.21428584 0 1 0 96.42857168 0 48.21428584 48.21428584 0 1 0-96.42857168 0Z" fill="#FFBB96" p-id="10972"></path></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

22
src/icons/svgo.yml Normal file
View File

@ -0,0 +1,22 @@
# replace default config
# multipass: true
# full: true
plugins:
# - name
#
# or:
# - name: false
# - name: true
#
# or:
# - name:
# param1: 1
# param2: 2
- removeAttrs:
attrs:
- 'fill'
- 'fill-rule'

9
src/interface/User.ts Normal file
View File

@ -0,0 +1,9 @@
export interface User {
id : number;
name : string;
}
enum StatusEnum {
DISABLED,
ENABLED
}

View File

@ -0,0 +1,162 @@
import store from "@/views/home/store/index";
import Web3 from 'web3'
import { Message } from 'element-ui';
import axios from 'axios';
import {Http_getNonce,Http_login} from "@/utils/login-request"
// import Cookie from "@u/cookie";
interface Window {
ethereum: any
web3: any
celo: any
}
declare let window: Window
export default class ChainManager {
public provider:any
public web3:Web3
public chainId:number
public account:string
public nonce:string
private async login( account, chainId, nonce ) {
nonce += ''
// console.log(nonce);return
const tips = 'This signature is only used for verify your account'
const signMsg = {
tips,
nonce
}
const EIP721_DOMAIN_DATA = [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' }
]
const signObj = {
types: {
EIP712Domain: EIP721_DOMAIN_DATA,
set: [
{ name: 'tips', type: 'string' },
{ name: 'nonce', type: 'string' }
]
},
primaryType: 'set',
domain: {
name: 'Auth',
version: '1'
},
message: signMsg
}
const signature = await this.signData(signObj, account)
const authData = {
account,
nonce,
signature,
tips,
net_id: chainId
}
// console.log('login data: ', authData)
const res = await Http_login(authData)
console.log(res)
// if (!res.errcode && res.token) {
// store.commit('set_token',res.token)
//
// }
}
public async checkNance() {
try {
let nonce = store.getters.nonce
// console.log(store.getters);return
if (!nonce) {
let params = {account:this.account, net_id:this.chainId}
const res = await Http_getNonce(params);
// store.commit('set_nonce',res.nonce)
// this.nonce = res.nonce
}
// console.log(this.account,this.chainId,this.nonce);return
this.login(this.account,this.chainId,this.nonce)
} catch (err) {
console.log(err)
Promise.reject(err)
}
}
public async connect(){
if (!this.hasMetamask()) {
Message({
message: '请先安装MetaMask插件',
type: 'error',
duration: 5 * 1000
})
return
}
this.provider = await this.connectMetaMask()
if (!this.provider) {
return
}
console.log(this.provider);return
this.web3 = new Web3(this.provider)
this.chainId = await this.web3.eth.getChainId()
const accounts = await this.web3.eth.getAccounts()
if (accounts && accounts.length > 0) {
this.account =accounts[0];
}
}
private async connectMetaMask() {
let provider = window.ethereum
try {
await provider.request({ method: 'eth_requestAccounts' })
} catch (error) {
if (error.code === -32002) {
throw new Error('MeatMask not login, Open MeatMask and login first')
} else {
throw new Error('User Rejected')
}
}
return provider
}
private verifyInjectedProvider(check) {
return window.ethereum
? window.ethereum[check]
: window.web3 &&
window.web3.currentProvider &&
window.web3.currentProvider[check]
}
private hasMetamask() {
if (typeof window.ethereum !== 'undefined') {
return true;
}else {
return false;
}
}
private async signData(signObj, signer) {
const msgParams = JSON.stringify(signObj)
const from = signer
// console.log('clicked, sending personal sign req', 'from', from, msgParams)
const params = [from, msgParams]
const result = await this.sendCmd({
method: 'eth_signTypedData_v4',
params,
from
})
console.log(result)
// return result.result
}
private async sendCmd({ method, params, from }) {
return new Promise((resolve, reject) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.web3.currentProvider.sendAsync({
method,
params,
from
}, async function(err, result) {
if (err) {
reject && reject(err)
return
}
resolve && resolve(result)
})
})
}
}

3
src/plugins/CKEditor.js Normal file
View File

@ -0,0 +1,3 @@
import Vue from 'vue';
import CKEditor from 'ckeditor4-vue'
Vue.use(CKEditor);

5
src/plugins/element.js Normal file
View File

@ -0,0 +1,5 @@
import 'element-ui/lib/theme-chalk/index.css';
import Vue from 'vue';
import ElementUI from 'element-ui';
Vue.use(ElementUI);

5
src/plugins/iview.js Normal file
View File

@ -0,0 +1,5 @@
import Vue from 'vue'
import ViewUI from 'view-design'
Vue.use(ViewUI)
import 'view-design/dist/styles/iview.css'

4
src/plugins/vant.js Normal file
View File

@ -0,0 +1,4 @@
import Vue from 'vue'
import Vant from "vant";
Vue.use(Vant)
import 'vant/lib/index.css';

View File

@ -0,0 +1 @@
export function _import( file: string) { return import(/* webpackChunkName: "about" */ '../views/About.vue')}

13
src/shims-tsx.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any
}
}
}

4
src/shims-vue.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}

7
src/utils/Events.ts Normal file
View File

@ -0,0 +1,7 @@
export default class Events {
static readonly ON_PAGE_CHANGE : string = "on-page-change";
static readonly ON_PAGE_SIZE_CHANGE : string = "on-page-size-change";
static readonly ON_SELECTION_CHANGE : string = 'on-selection-change';
static readonly ON_REFRESH_USER: string = 'on-refresh-user'
}

190
src/utils/Http.ts Normal file
View File

@ -0,0 +1,190 @@
import axios, {AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, Method} from "axios";
import {Store} from "vuex";
import {VueRouter} from "vue-router/types/router";
import {ServerRetEnum} from "@/views/admin/types/Types";
const sprintf = require('sprintf-js')
let apiHost = process.env.NODE_ENV !== 'production' ? process.env.VUE_APP_API_HOST : document.location.host;
let scheme = document.location.protocol;
class Http {
private readonly _handler : AxiosInstance;
static _instance : Http;
private _store? : Store<any>;
private _router : VueRouter | any;
private _vue : Vue | any;
private _baseUri : string = '';
static instance() : Http {
if (!this._instance) {
this._instance = new Http();
}
return this._instance;
}
set store(store : Store<any>) {
this._store = store;
}
set router(router : VueRouter) {
this._router = router;
}
set vue(vue : Vue) {
this._vue = vue;
}
get handler () : AxiosInstance {
return this._handler;
}
get uri() {
// console.log(this._handler.defaults, 'get_uri');
return this._handler.defaults.baseURL;
}
set baseUri(uri : string) {
this._baseUri = uri;
}
get baseUri () : string {
return this._baseUri;
}
constructor() {
this._handler = axios.create({
baseURL : scheme + "//" + apiHost,
timeout : 50000,
});
this._handler.interceptors.response.use((response : AxiosResponse) => {
// console.log(response, '基本请求结果')
if (response.status == 200) {
return response.data;
} else {
// console.log(response, '未知错误')
}
}, (error : AxiosError) => {
let response = error.response;
if (response?.status === 401) {
this._router.replace("/login");
} else {
console.error('response error', error.response);
}
return Promise.reject(error);
})
this._handler.interceptors.request.use((config : AxiosRequestConfig) => {
config.headers["X-Requested-With"] = "XMLHttpRequest";
if (this._store) {
config.headers["Authorization"] = "Bearer " + this._store.getters.token;
}
return config;
})
}
public sprintf(...params) : string {
return sprintf.sprintf(...params);
}
public get(url : string, params? : object | any, ...args : any[]) : Promise<ResponseInterface> {
return this.request('GET', url, params, ...args);
}
public post(url : string, data? : object | any, ...args : any[]) : Promise<ResponseInterface> {
return this.request('POST', url, data, ...args);
}
public put(url : string, params? : object | any, ...args : any[]) : Promise<ResponseInterface> {
return this.request('PUT', url, params, ...args);
}
public delete(url : string, params? : object | any, ...args : any[]) : Promise<ResponseInterface> {
return this.request('DELETE', url, params, ...args);
}
public request(method : Method, url : string, data? : object | any, headers? : any) : Promise<ResponseInterface> {
url = this._baseUri + url;
let params : AxiosRequestConfig = {
url, method, headers
}
if (method.toLocaleUpperCase() === 'GET') {
params.params = data;
} else {
params.data = data;
}
return new Promise<ResponseInterface>((resolve, reject) => {
this._handler.request(params).then((res : any) => {
if (res.code === ServerRetEnum.SUCCESS) {
resolve(res);
} else if(res.code === ServerRetEnum.NO_PERMISSION) {
if(this._vue.$Message){
this._vue.$Message.error(res.message);
}
} else {
console.error(res);
reject(res.message);
}
}).catch((err : any) => {
reject(err);
})
})
}
public download(method : Method, url: string) : Promise<AxiosResponse> {
this._handler.interceptors.response.use((response : AxiosResponse) => {
return Promise.resolve(response)
});
return new Promise((resolve, reject) => {
url = this._baseUri + url;
let params : AxiosRequestConfig = {
url, method, responseType: "blob", timeout: 600e3
}
this._handler.request(params).then((res : any) => {
resolve(res);
}).catch((reason) => {
console.error(reason);
reject(reason);
})
});
}
}
const instance : Http = Http.instance();
export default instance;
export const get = instance.get;
export const post = instance.post;
export interface ResponseInterface {
data? : Array<any> | object | object[] | any,
code : ServerRetEnum,
message : string
}
export interface ResponseErrorInterface {
result : number,
message : string
}
export interface RequestInterface {
store : Store<any>;
router : VueRouter;
handler : AxiosInstance;
baseUri : string;
instance();
get(url : string, params? : object | any, ...args : any[]) : Promise<ResponseInterface>;
post(url : string, params? : object | any, ...args : any[]) : Promise<ResponseInterface>;
put(url : string, params? : object | any, ...args : any[]) : Promise<ResponseInterface>;
delete(url : string, params? : object | any, ...args : any[]) : Promise<ResponseInterface>;
request(method : Method, url : string, data? : object | any, headers? : any) : Promise<ResponseInterface>;
download(method : Method, url : string) : Promise<AxiosResponse>
}

21
src/utils/Parnet.ts Normal file
View File

@ -0,0 +1,21 @@
import Vue from "vue";
import { RawLocation } from "vue-router/types/router";
import { Route } from "vue-router";
export default class Parent extends Vue {
protected _push(location : RawLocation, success_cb? : Function, error_cb? : Function) {
this.$router.push(location).catch(err => {
if (error_cb) {
error_cb.apply(this, err);
}
}).then((value : Route) => {
if (success_cb) {
success_cb.apply(this, value);
}
})
}
public goPage(name : string, success_cb? : Function, error_cb? : Function) {
this._push({name}, success_cb, error_cb);
}
}

110
src/utils/Url.ts Normal file
View File

@ -0,0 +1,110 @@
/**
*
*/
export default class Url {
static readonly WORK_LOGIN : string = 'work/login'
static readonly LOGIN : string = "login";
static readonly LOGOUT : string = "logout";
// TODO 菜单
static readonly MENUS : string = "menu";
static readonly DELETE_MENU : string = "menu/%d";
static readonly SHOW_MENU : string = "menu/%d";
static readonly UPDATE_MENU : string = "menu/%d";
// TODO 用户
static readonly USERS : string = "user";
static readonly DISABLE_USER : string = 'user/%d';
static readonly ENABLE_USER : string = "user/start/%d";
static readonly SHOW_USER : string = 'user/%d';
static readonly UPDATE_USER : string = 'user/%d';
static readonly UPDATE_USERNAME : string = 'user/update_name/%d';
static readonly MY_MENUS : string = "my_menu";
static readonly MY_POWER : string = "my_power";
static readonly MY_PROJECT : string = "my_project";
static readonly UPDATE_MY_NAME : string = "update/my/name";
// TODO 权限
static readonly POWER : string = "power";
static readonly SHOW_POWER : string = "power/%d";
static readonly UPDATE_POWER : string = "power/%d";
static readonly DELETE_POWER : string = "power/%d";
// TODO 权限组
static readonly POWER_GROUP : string = "power_group";
static readonly SHOW_POWER_GROUP : string = "power_group/%d";
static readonly UPDATE_POWER_GROUP : string = "power_group/%d";
static readonly DELETE_POWER_GROUP : string = "power_group/%d";
// TODO 项目
static readonly PROJECT : string = 'project';
static readonly SHOW_PROJECT : string = "project/%d";
static readonly UPDATE_PROJECT : string = "project/%d";
static readonly DELETE_PROJECT : string = "project/%d";
static readonly WATCH_PROJECT : string = "project/watch/%d";
static readonly UN_WATCH_PROJECT : string = "project/watch/%d";
static readonly SHOW_PROJECT_ITEM : string = "project_item/%d";
static readonly DElETE_PROJECT_ITEM : string = "project_item/%d";
static readonly DElETE_PROJECT_PRODUCT : string = "project_product/%d";
static readonly UPDATE_PROJECT_PRODUCT : string = "project_product/%d";
static readonly SHOW_PROJECT_PRODUCT : string = "project_product/%d";
static readonly DOWNLOAD_PROJECT_PRODUCT : string = "project_product/download/%d";
static readonly SHOW_PRODUCT_Log : string = "show_log/%d";
static readonly SHOW_DOWNLOAD_IP : string = "product/download_ip/%d";
static readonly DOWNLOAD_IP : string = "download_ip";
static readonly DELETE_DOWNLOAD_IP : string = "download_ip/%d";
static readonly UPDATE_DOWNLOAD_IP : string = "download_ip/%d";
//TODO 用户分配
static readonly ALLOT_MENT : string = 'allot_menu/%d';
static readonly ALLOT_POWER : string = 'allot_power/%d';
static readonly ALLOT_PROJECT : string = 'allot_project/%d';
//TODO 自动化(设备)
static readonly DEVICE : string = 'device';
static readonly UPLOAD_IMAGE : string = 'device/upload_pic';
static readonly DELETE_DEVICE : string = 'device/%d';
static readonly UPDATE_DEVICE : string = 'device/%d';
//TODO 自动化(脚本)
static readonly SCRIPT : string = 'script';
static readonly UPLOAD_SCRIPT : string = 'script/upload';
static readonly UPDATE_SCRIPT : string = 'script/%d';
static readonly DELETE_SCRIPT : string = 'script/%d';
static readonly DOWNLOAD_SCRIPT : string = 'script/download/%d';
//TODO 自动化(脚本集)
static readonly SCRIPT_COLLECTION : string = 'collection';
static readonly UPDATE_SCRIPT_COLLECTION : string = 'collection/%d';
static readonly DELETE_SCRIPT_COLLECTION : string = 'collection/%d';
static readonly DOWNLOAD_SCRIPT_COLLECTION : string = 'collection/download/%d';
//TODO 自动化(任务)
static readonly TASK : string = 'task';
static readonly UPDATE_TASK : string = 'task/%d';
static readonly DELETE_TASK : string = 'task/%d';
static readonly TASK_LOG : string = 'task_log';
static readonly TASK_DEFAULT_STATUS_CHANGE : string = 'task/set_default/%d'
static readonly TASK_REPORT : string = 'task_report/%d'
//TODO 自动化(数据仓库)
static readonly DATA :string = 'data'
static readonly DELETE_DATA :string = 'data/%d'
//TODO 首页
static readonly OVERVIEW : string = 'overview'
static readonly OVERVIEW_SCRIPT : string = 'overview_scripts'
static readonly OVERVIEW_PRODUCT : string = 'overview_products'
}
/**
*
*/
export class AdminUrl {
static readonly LOGIN : string = "login"; // 登陆
}

View File

@ -0,0 +1,52 @@
import axios from 'axios';
import store from "@/views/home/store/index";
// 创建axios实例设置配置得默认值
// const instance = axios.create({
// baseUrl:'https://market.cebg.games', // 这里写接口的http地址
// timeout: 5000, // 设置请求超时的默认值
// })
const instance = axios.create({
baseURL: 'https://market.cebg.games',
timeout: 5000
})
// 设置请求拦截器
instance.interceptors.request.use(
config => {
// 判断当前是否有token有则在请求头上加上token
if (store.getters.token) {
config.headers.Authorization = "Bearer " + store.getters.token
}
// console.log(config)
return config
},
error => {
// 请求错误进行拦截并返回错误信息
// console.log(error)
return Promise.reject(error)
}
)
// 设置响应拦截
instance.interceptors.response.use(
response => {
const res = response.data
return res
},
error => {
return Promise.reject(error)
}
)
export const Http_getNonce = (params) =>
instance({
url: 'https://market.cebg.games/webapp/index.php?c=Market&a=getNonce',
method: 'get',
params: params
})
export const Http_login = (data) =>
instance({
url: 'https://market.cebg.games/webapp/index.php?c=Market&a=auth',
method: 'get',
params: data
})

7
src/views/admin/App.vue Normal file
View File

@ -0,0 +1,7 @@
<template>
<div id="app">
<router-view/>
</div>
</template>
<style lang="less" src="../../assets/style/main.less"></style>

View File

@ -0,0 +1,35 @@
import Parent from "@/utils/Parnet";
export default class AdminParent extends Parent {
public success(content? : string, callback? : Function) {
this.$Message.success({
content : content ? content : "操作成功",
onClose : () => {
this.$Message.destroy()
callback && callback();
}
})
}
public error(content? : string, callback? : Function) {
this.$Message.error({
content : content ? content : "操作失败",
onClose : () => {
this.$Message.destroy();
callback && callback();
}
})
}
doDeleteItem(title : string = "", callback : Function) {
this.$Modal.confirm({
title : "删除",
content : title.length > 0 ? title : "确认要删除该数据吗?",
loading : true,
onOk : () => {
this.$Modal.remove();
callback.call(this)
}
})
}
}

30
src/views/admin/main.ts Normal file
View File

@ -0,0 +1,30 @@
import Vue from 'vue'
import App from "./App.vue"
import router from './router'
import store from './store'
import Http, { get, post } from "@/utils/Http";
import "../../plugins/iview"
import "../../plugins/element";
import "@/assets/style/tailwind.css"
const sprintf = require('sprintf-js');
Http.store = store;
Http.router = router;
Http.baseUri = '/rest/admin/'
Vue.prototype.$http = Http;
Vue.config.devtools = process.env.NODE_ENV !== 'production';
Vue.config.silent = process.env.NODE_ENV === 'production';
Vue.config.productionTip = process.env.NODE_ENV !== 'production';
Vue.prototype.$isDev = process.env.NODE_ENV !== 'production';
Vue.prototype.$sprintf = sprintf.sprintf;
new Vue({
router,
store,
render : h => h(App),
created() {
this.$http.vue = this;
}
}).$mount('#app')

View File

View File

@ -0,0 +1,17 @@
import { Component, Vue } from 'vue-property-decorator'
import Url, {AdminUrl} from "@/utils/Url";
import {
State,
Getter,
Action,
Mutation,
} from "vuex-class";
import AdminParent from "@/views/admin/Common/AdminParent";
@Component
export default class home extends AdminParent {
public created() {
}
}

View File

@ -0,0 +1,8 @@
<template>
<div class="home">
首页
</div>
</template>
<script lang="ts" src="./home.ts"></script>
<style scoped lang="less" src="./home.less"></style>

View File

View File

@ -0,0 +1,14 @@
import { Component, Vue, Watch } from 'vue-property-decorator'
import {
State,
Getter,
Action,
Mutation,
} from "vuex-class";
import AdminParent from "@/views/admin/Common/AdminParent";
@Component
export default class layout extends AdminParent {
}

View File

@ -0,0 +1,8 @@
<template>
<div class="layout">
<router-view></router-view>
</div>
</template>
<script lang="ts" src="./layout.ts"></script>
<style scoped lang="less" src="./layout.less"></style>

View File

@ -0,0 +1,3 @@
<template>
<router-view></router-view>
</template>

View File

View File

@ -0,0 +1,14 @@
import { Component, Vue } from 'vue-property-decorator'
import Url from "@/utils/Url";
import {
State,
Getter,
Action,
Mutation,
} from "vuex-class";
import AdminParent from "@/views/admin/Common/AdminParent";
@Component
export default class Login extends AdminParent {
}

View File

@ -0,0 +1,9 @@
<template>
<div class="justify-center align-middle">
登陆
</div>
</template>
<script lang="ts" src="./login.ts"></script>
<style scoped lang="less" src="./login.less"></style>

View File

@ -0,0 +1,33 @@
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import Routers from './router'
import { LoadingBar } from 'view-design'
Vue.use(VueRouter)
const routes : Array<RouteConfig> = [
...Routers
]
const router = new VueRouter({
base : "/admin/",
mode : "history",
routes,
})
function setTitle(title : string) : void {
window.document.title = title;
}
router.beforeEach((to, from, next) => {
LoadingBar.start();
let title : string = process.env.VUE_APP_TITLE;
setTitle(to.meta.title + '-' + title);
next();
})
router.afterEach((to, from) => {
LoadingBar.finish();
})
export default router

View File

@ -0,0 +1,27 @@
import { RouteConfig } from "vue-router";
import Layout from '../pages/layout/layout.vue'
const routers : RouteConfig[] = [
{
path : '/login',
name : 'login',
component : () => import(/* webpackChunkName: "admin-login" */ '@/views/admin/pages/login/login.vue'),
meta : {title : "登陆", isMenu : false}
}, {
path : "/",
name : 'layout',
component : Layout,
redirect : "/index",
meta : {title : "", isMenu : false},
children : [{
path : "index",
name : "index",
meta : {title : "首页", isMenu : true, icon : "", path : "/index"},
// redirect : "/index/dashboard",
component : () => import(/* webpackChunkName: "admin-login" */ '@/views/admin/pages/home/home.vue'),
},]
}
]
export default routers

View File

@ -0,0 +1,37 @@
import Vue from 'vue'
import Vuex from 'vuex'
import user, {UserState} from "@/views/admin/store/user";
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
interface StateInterface {
user : UserState,
}
const localStorage : any = window.localStorage;
export default new Vuex.Store({
getters : {
token : (state : StateInterface) => state.user.token,
},
modules : {
user,
},
plugins : [
createPersistedState({
key : 'admin',
storage : {
getItem : (key : string) => {
return localStorage.getItem(key);
},
setItem : (key : string, value : any) => {
return localStorage.setItem(key, value);
},
removeItem : (key : string) => {
return localStorage.removeItem(key);
}
}
})
]
})

View File

@ -0,0 +1,6 @@
export default class Types {
static readonly SET_USER_INFO : string = 'SET_USER_INFO';
static readonly SET_USER_TOKEN : string = 'SET_USER_TOKEN';
static readonly SET_LANG : string = 'SET_LANG';
static readonly SET_SITE_INFO : string = "SET_SITE_INFO";
}

View File

@ -0,0 +1,41 @@
import {ActionContext} from "vuex";
import Http, { ResponseErrorInterface, ResponseInterface } from "@/utils/Http";
import {AdminUrl} from "@/utils/Url";
import Types from "@/views/admin/store/types";
import { User } from "@/interface/User";
const sprintf = require('sprintf-js');
export interface UserState {
user : User,
token : string | undefined
}
export default {
name : "user",
namespace : true,
state : {
user : {},
token : undefined
},
actions : {
Login(context : ActionContext<UserState, any>, form : any) {
return Http.post(AdminUrl.LOGIN, form).then((res : ResponseInterface) => {
console.log(res);
context.commit(Types.SET_USER_INFO, res.data);
context.commit(Types.SET_USER_TOKEN, res.data.access_token);
return Promise.resolve(res.data);
}).catch((err : ResponseErrorInterface) => {
return Promise.reject(err);
})
},
},
mutations : {
[Types.SET_USER_INFO](state : UserState, user : User) {
state.user = user;
},
[Types.SET_USER_TOKEN](state : UserState, token : string) {
state.token = token;
}
}
}

View File

@ -0,0 +1,24 @@
export interface MetaInterface {
title : string,
isMenu : boolean,
icon : string,
path : string,
isHide : boolean
}
export interface MenuInterface {
id? : number,
title : string,
icon : string,
name : string,
path : string,
isHide : boolean,
children : MenuInterface[] | any[]
}
export enum ServerRetEnum {
SUCCESS,
NO_PERMISSION = 1001,
NOT_DATA = 1056,
}

9
src/views/home/App.vue Normal file
View File

@ -0,0 +1,9 @@
<template>
<div id="app">
<router-view/>
</div>
</template>
<script lang="ts" src="./main-app.ts"></script>
<style lang="less" src="../../assets/style/main.less"></style>

View File

@ -0,0 +1,28 @@
import AdminParent from "@/views/admin/Common/AdminParent";
import { Route } from "vue-router";
import { Location } from "vue-router/types/router";
export interface BreadItem {
location: Location
route: Route
href: string
normalizedTo: Location
resolved: Route
name?: string
}
export default class HomeParent extends AdminParent {
public loading: boolean = false;
public startLoading() : void {
this.loading = true;
}
public stopLoading() : void {
this.loading = false;
}
}

View File

@ -0,0 +1,80 @@
export interface MenuInterface {
name : string;
url : string;
pid ?: number;
state ?: number;
id? : number;
children? : MenuInterface[]
}
export interface PowerInterface {
id? : number;
name : string;
pid : number;
controller : string;
action : string;
children? : PowerInterface[]
}
export interface PowerGroupInterface {
id? : number;
name : string;
}
export interface ProjectInterface {
name : string;
tapd_id : any;
id? : number;
}
export interface ProjectItemInterface {
id? : number;
name : string;
type : string;
}
export interface ProjectProductInterface {
id? : number;
name : string;
commit_message : string;
days : number;
download_times : number;
build_result?: string;
state ?: boolean;
}
export interface ProjectProductLogInterface {
id? : number;
userName : string;
addTime : any;
productName : string;
days : number;
content : string
}
export interface ProductIpsInterface {
id? : number;
project_item_id ?: number;
server_ip :string;
created_at ?: string;
old_ip ?: string;
}
export interface UserInterface {
id? : number;
username : string;
name : string;
openid : string;
deleted_at : string;
old_name ?: string;
}
export interface ElTreeDataInterface {
id : number;
label : string;
children : ElTreeDataInterface[]
}
export enum EVENT_TYPE {
UPDATE_BREAD_ITEM = "UPDATE_BREAD_ITEM",
UPDATE_MY_PROJECT = "UPDATE_MY_PROJECT"
}

View File

@ -0,0 +1,45 @@
export interface DeviceInterface {
id ?: number;
name : string;
model : number;
version : number;
serial_number : number;
connect_url : string;
pic_url : string;
is_running ?: number;
platform : string;
}
export interface ScriptInterface {
id ?: number;
name : string;
project_id : number;
project ?: string;
user_id ?: number;
user ?: string;
description : string;
path : string;
checkFile?:[]
}
export interface CollectionInterface {
id ?: number;
name : string;
project_id : number;
user_id ?: number;
description : string;
script : []
list?:object
}
export interface TaskInterface {
id ?: number;
name : string;
project_id : number;
project ?: string;
user_id ?: number;
description : string;
execute_count ?: number;
script_number ?: number;
list ?: []
}

6
src/views/home/cebg.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
interface Window {
ethereum: any
web3: any
celo: any
}
declare let window: Window

View File

@ -0,0 +1,178 @@
<template>
<div class='box'>
<div class='dots childDots1'></div>
<div class='dots childDots2'></div>
<div class='dots childDots3'></div>
<div class='dots childDots4'></div>
<div class='dots childDots5'></div>
<div class='dots childDots6'></div>
<div class='dots childDots7'></div>
<div class='dots childDots8'></div>
<div class='dots childDots9'></div>
<div class='dots childDots10'></div>
<div class='dots childDots11'></div>
<div class='dots childDots12'></div>
</div>
</template>
<script>
export default {
name: "DownLoading"
}
</script>
<style>
* {
margin: 0;
padding: 0
}
body {
margin: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: #EEE;
}
.box {
width: 3.7em;
height: 2em;
position: relative;
margin: auto;
/*background-color: red;*/
float: left;
}
.box .dots {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
.box .dots:before {
content: '';
display: block;
margin: 0 auto;
width: 15%;
height: 15%;
background-color: #00BFFF;
border-radius: 50%;
animation: animate 1.2s infinite ease-in-out both;
}
.box .childDots2 {
transform: rotate(30deg);
}
.box .childDots3 {
transform: rotate(60deg);
}
.box .childDots4 {
transform: rotate(90deg);
}
.box .childDots5 {
transform: rotate(120deg);
}
.box .childDots6 {
transform: rotate(150deg);
}
.box .childDots7 {
transform: rotate(180deg);
}
.box .childDots8 {
transform: rotate(210deg);
}
.box .childDots9 {
transform: rotate(240deg);
}
.box .childDots10 {
transform: rotate(270deg);
}
.box .childDots11 {
transform: rotate(300deg);
}
.box .childDots12 {
transform: rotate(330deg);
}
.box .childDots2:before {
animation-delay: -1.1s;
}
.box .childDots3:before {
animation-delay: -1s;
}
.box .childDots4:before {
animation-delay: -0.9s;
}
.box .childDots5:before {
animation-delay: -0.8s;
}
.box .childDots6:before {
animation-delay: -0.7s;
}
.box .childDots7:before {
animation-delay: -0.6s;
}
.box .childDots8:before {
animation-delay: -0.5s;
}
.box .childDots9:before {
animation-delay: -0.4s;
}
.box .childDots10:before {
animation-delay: -0.3s;
}
.box .childDots11:before {
animation-delay: -0.2s;
}
.box .childDots12:before {
animation-delay: -0.1s;
}
@-webkit-keyframes animate {
0%,
80%,
100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
}
@keyframes animate {
0%,
80%,
100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
}
</style>

View File

@ -0,0 +1,162 @@
<template>
<div ref="wrap" class="wrap">
<div ref="content" class="content" :class="animationClass" :style="contentStyle" @animationend="onAnimationEnd"
@webkitAnimationEnd="onAnimationEnd">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
props: {
content: {
default: ''
},
delay: {
type: Number,
default: 0.5
},
speed: {
type: Number,
default: 70
},
direction: {
type: String,
default: "left"
}
},
mounted() {
},
data() {
return {
wrapWidth: 0,
firstRound: true,
duration: 0,
offsetWidth: 0,
animationClass: '',
};
},
computed: {
contentStyle() {
let styles_row = {
paddingLeft: (this.firstRound ? 0 : this.wrapWidth) + 'px',
animationDelay: (this.firstRound ? this.delay : 0) + 's',
animationDuration: this.duration + 's',
};
let styles_up = {
paddingTop: (this.firstRound ? 0 : this.wrapWidth) + 'px',
animationDelay: (this.firstRound ? this.delay : 0) + 's',
animationDuration: this.duration + 's',
flexFlow:'column nowrap'
}
return this.direction == 'up' ? styles_up : styles_row;
}
},
watch: {
content: {
immediate: true,
handler() {
this.$nextTick(() => {
if (this.direction == 'left') {
this.onLeft();
} else if (this.direction == 'up') {
this.onUp();
}
});
}
}
},
methods: {
onLeft() {
const {
wrap,
content
} = this.$refs;
const wrapWidth = wrap.getBoundingClientRect().width;
const offsetWidth = content.getBoundingClientRect().width;
this.wrapWidth = wrapWidth;
this.offsetWidth = offsetWidth;
this.duration = offsetWidth / this.speed;
this.animationClass = 'animate';
},
onUp() {
const {
wrap,
content
} = this.$refs;
const wrapWidth = wrap.getBoundingClientRect().height;
const offsetWidth = content.getBoundingClientRect().height;
this.wrapWidth = wrapWidth;
this.offsetWidth = offsetWidth;
this.duration = offsetWidth / this.speed;
this.animationClass = 'animate-up';
},
onAnimationEnd() {
this.firstRound = false
this.duration = (this.offsetWidth + this.wrapWidth) / this.speed;
this.animationClass = this.direction == 'left' ? "animate-infinite" : "animate-infinite-up";
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.wrap {
width: 100%;
height: 30px;
line-height: 30px;
overflow: hidden;
position: relative;
padding: 0;
color: #333;
}
.wrap .content {
position: absolute;
white-space: nowrap;
display: flex;
}
.animate {
animation: paomadeng linear;
}
.animate-infinite {
animation: paomadeng-infinite linear infinite;
}
.animate-up {
animation: paomadeng-up linear;
}
.animate-infinite-up {
animation: paomadeng-infinite-up linear infinite;
}
@keyframes paomadeng {
to {
transform: translate3d(-100%, 0, 0);
}
}
@keyframes paomadeng-infinite {
to {
transform: translate3d(-100%, 0, 0);
}
}
@keyframes paomadeng-up {
to {
transform: translate3d(0, -100%, 0);
}
}
@keyframes paomadeng-infinite-up {
to {
transform: translate3d(0, -100%, 0);
}
}
</style>

View File

@ -0,0 +1,86 @@
.device-item {
display: block;
float: left;
width: 220px;
padding: 5px 0;
margin-left: 40px;
margin-bottom: 20px;
border: 1px solid #e1e1e1;
font-weight: 500;
cursor: pointer;
background-color: white;
}
.top {
overflow: hidden;
border-bottom: 1px solid #eee;
padding: 5px 10px;
display: inline-block;
height: auto;
width: 100%;
.icon{
width: 20px;
height: 20px;
float: left;
//margin-top: 3px;
font-size: 18px;
}
.button {
text-align: right;
//margin-left: 40px;
float: right;
// margin-top: -40px;
display: inline-block;
vertical-align: middle;
}
.link{
font-size: 16px;
margin-left: 5px;
}
.status-color1{
color: #eb4e3d;
}
.status-color2{
color: #2d8cf0;
}
}
.div-inline{
display: inline-block;
}
.left {
float: left;
margin-left: 10px;
width: 20%;
}
.title {
font-family: "PingFang SC", "Helvetica Neue", Helvetica, "Hiragino Sans GB", STHeitiSC-Light, "Microsoft YaHei", "微软雅黑", Arial, sans-serif; width: 100%;
// height: 66px;
text-align: left;
margin-left: 20px;
margin-top: 10px;
margin-bottom: 10px;
// padding-left: 30px;
// margin-bottom: 30px;
font-size: 13px;
color: #333;
// line-height: 33px;
}
.device-info {
float: left;
width: 65%;
margin-left: 20px;
margin-top: 10px;
margin-bottom: 50px;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
word-wrap:break-word;
word-break:break-all;
font-size: 12px;
}

View File

@ -0,0 +1,40 @@
import HomeParent from "@/views/home/Common/HomeParent";
import {Component, Prop, Vue} from "vue-property-decorator";
import SvgIcon from '@/components/SvgIcon.vue'
@Component({
components : {
SvgIcon
}
})
export default class phoneCard extends Vue {
name:string = 'phoneCard'
@Prop({type : Array, default : () => []})
dataList : Array<any>;
public edit(e) {
if (e.is_running == 1) {
this.$Message.error({
content:'设备正在运行中,无法编辑'
})
return
}
this.$emit('edit-device',e)
}
public del(e) {
this.$emit('del-device',e)
}
public exec(e) {
this.$emit('exec',e)
}
public copyLink(e){
this.$Notice.info({
title:'链接地址',
desc:e.connect_url,
duration:3
})
}
}

View File

@ -0,0 +1,40 @@
<template>
<div>
<div style="display: flex;flex-wrap:wrap;">
<div class="device-item" v-for="item in dataList" :key="item.id" >
<div class="top">
<!-- <Icon type="ios-link" class="link" />-->
<div class="icon"><svg-icon :icon-class=" item.is_running ?'device_busy': 'device_free' " :class="! item.is_running ?'status-color2': 'status-color1'"/></div>
<span v-text="! item.is_running ? '空闲中': '占用中'" :class="! item.is_running ?'': 'status-color1'"></span>
<Icon type="ios-link" class="link" @click="copyLink(item)"/>
<Button size="small" type="primary" class="button" v-if="! item.is_running" @click="exec(item)">立即使用</Button>
<Button size="small" disabled class="button" style="background-color: #f90;color: white;border-color: #f90" v-else>运行中...</Button>
<!-- <Icon type="md-link" class="link"/>-->
</div>
<div>
<div class="title">{{item.name}}</div>
<div class="div-inline">
<div class="left">
<img src="https://inside.testin.cn/cfgfiles/images/model/1632454023561/f6aec1729de246f99e672b301dadca88.jpg"/>
<!-- <img :src="item.pic_url" />-->
</div>
<div class="device-info">
<div style="width:200px; white-space:nowrap;overflow:hidden;text-overflow:ellipsis; ">型号:{{item.model}}</div>
<div >版本号:{{item.version}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script src="./phoneCard.ts"></script>
<style scoped lang="less" src="./phoneCard.less"></style>

View File

@ -0,0 +1,17 @@
import {Component, Mixins, Vue, Watch} from 'vue-property-decorator'
import Url, {AdminUrl} from "@/utils/Url";
import {
State,
Getter,
Action,
Mutation,
} from "vuex-class";
import HomeParent from "@/views/home/Common/HomeParent";
@Component
export default class MainApp extends HomeParent {
public created() {
}
}

36
src/views/home/main.ts Normal file
View File

@ -0,0 +1,36 @@
import Vue from 'vue'
import App from "./App.vue"
import router from './router/index'
import store from './store'
import Http, { get, post } from "@/utils/Http";
import "../../plugins/iview"
import "../../plugins/element";
import "@/assets/style/tailwind.css"
import * as _ from 'lodash'
const sprintf = require('sprintf-js');
import '@/icons'
Http.store = store;
Http.router = router;
Http.baseUri = '/rest/'
Vue.prototype.$http = Http;
Vue.config.devtools = process.env.NODE_ENV !== 'production';
// Vue.config.devtools = true;
Vue.config.silent = process.env.NODE_ENV === 'production';
// Vue.config.silent = false;
Vue.config.productionTip = process.env.NODE_ENV !== 'production';
Vue.prototype.$isDev = process.env.NODE_ENV !== 'production';
Vue.prototype.$sprintf = sprintf.sprintf;
Vue.prototype.$_ = _;
Vue.prototype.$bus = new Vue({});
new Vue({
router,
store,
render : h => h(App),
created() {
this.$http.vue = this;
}
}).$mount('#app')

View File

@ -0,0 +1,87 @@
.boxx{
margin: 0 0 18px 20px;
font-size: 40px
}
.demo-split1{
height: 450px;
}
.demo-split-pane1{
padding: 10px;
}
.demo-split{
height: 680px;
margin-top: 50px;
background-color: #FFFFFF;
}
.demo-split-pane{
padding: 10px;
//border: 1px solid red;
}
.demo-split-pane.no-padding{
height: 680px;
padding: 0;
}
.tag{
background-color: #374151;
color: white;
padding: 8px 100px 8px 20px;
}
.arrow1 {
padding: 15px 0;
margin-left: 20px;
&-wrap {
//margin: 0 auto;
width: 120px;
height: 34px;
background-image: linear-gradient(to right, #227190 , #63c7ed);
color: #FFFFFF;
line-height: 34px;
text-align: start;
position: relative;
&::after {
content: '';
position: absolute;
right: -34px;
border: 17px solid transparent;
border-left: 17px solid #63c7ed;
}
}
}
.arrow2 {
padding: 15px 0;
margin-left: 20px;
&-wrap {
//margin: 0 auto;
width: 180px;
height: 34px;
background-image: linear-gradient(to right, #008B8B , #7FFFD4);
color: #FFFFFF;
line-height: 34px;
text-align: start;
position: relative;
&::after {
content: '';
position: absolute;
right: -34px;
border: 17px solid transparent;
border-left: 17px solid #7FFFD4;
}
}
}
.progress:hover:before{
content:attr(content);
background:#585e6b;
color:#fff;
padding:.8em 1em;
position:absolute;
bottom: -35px;
border-radius: 5px;
margin-left:30%;
}

View File

@ -0,0 +1,192 @@
import { Component, Mixins, Vue, Watch } from 'vue-property-decorator'
import Url from "@/utils/Url";
import {
State,
Getter,
Action,
Mutation
} from "vuex-class";
import HomeParent from "@/views/home/Common/HomeParent";
import CustomPage from "@/components/page.vue";
import SvgIcon from '@/components/SvgIcon.vue'
import * as echarts from 'echarts'
@Component({
components : {
SvgIcon
}
})
export default class Home extends HomeParent {
name : string = 'home'
public scriptList :any = [{ script_name: undefined, date: undefined,content:undefined}];
public projectList :any = [{ product_name: undefined, date: undefined}]
public myChart;
public cardProductData:object = {
product_count_trashed:undefined,
product_today:undefined,
day_rate:undefined,
day_icon:undefined,
week_rate:undefined,
week_icon:undefined,
}
public cardScriptData:object = {
script_week:undefined,
use_rate:undefined
}
public cardTaskData:object = {
day_rate:undefined,
task_count:undefined,
task_month_sum:undefined
}
public userNumber:number=0;
public created() {
// this._getData()
// this._getScriptLog()
// this._getProductLog()
}
private createChart(data) {
this.myChart = echarts.init(this.$refs['myChart'] as HTMLCanvasElement);
this.myChart.setOption(this.option(data));
this.myChart.on('updateAxisPointer', (event)=> {
const xAxisInfo = event.axesInfo[0];
if (xAxisInfo) {
const dimension = xAxisInfo.value + 1;
this.myChart.setOption({
series: {
id: 'pie',
label: {
formatter: '{b}: {@[' + dimension + ']} ({d}%)'
},
encode: {
value: dimension,
tooltip: dimension
}
}
});
}
});
}
public shortcut(str){
this._push({name:str})
}
private option(data) {
return {
legend: {},
tooltip: {
trigger: 'axis',
showContent: false
},
dataset: {
source: data
},
xAxis: { type: 'category' },
yAxis: { gridIndex: 0 },
grid: { top: '55%' },
series: [
{
type: 'line',
smooth: true,
seriesLayoutBy: 'row',
emphasis: { focus: 'series' }
},
{
type: 'line',
smooth: true,
seriesLayoutBy: 'row',
emphasis: { focus: 'series' }
},
{
type: 'line',
smooth: true,
seriesLayoutBy: 'row',
emphasis: { focus: 'series' }
},
{
type: 'line',
smooth: true,
seriesLayoutBy: 'row',
emphasis: { focus: 'series' }
},
{
type: 'pie',
id: 'pie',
radius: '30%',
center: ['50%', '25%'],
emphasis: {
focus: 'self'
},
label: {
formatter: '{b}: {@2012} ({d}%)'
},
encode: {
itemName: 'product',
value: '2012',
tooltip: '2012'
}
}
]
}
}
private _getData():void{
this.$http.get(Url.OVERVIEW).then((res)=>{
this.createChart(res.data.recent_task_logs)
this.cardProductData = this.alterProduct(res.data.card_data.product)
this.cardTaskData = this.alterTask(res.data.card_data.task)
this.cardScriptData = this.alterScript(res.data.card_data.script)
this.userNumber = res.data.card_data.user
}).catch((msg:string)=>{
this.error(msg)
})
}
private _getScriptLog():void{
this.$http.get(Url.OVERVIEW_SCRIPT).then((res)=>{
this.scriptList = res.data
}).catch((msg:string)=>{
this.error(msg)
})
}
private _getProductLog():void{
this.$http.get(Url.OVERVIEW_PRODUCT).then((res)=>{
this.projectList = res.data
}).catch((msg:string)=>{
this.error(msg)
})
}
private alterProduct(data):object{
return {
product_count_trashed:data.product_count_trashed,
product_today:data.product_today,
day_rate:data.product_yesterday==0?'(昨日无构建)':Math.abs(Math.floor(((parseInt(data.product_today)-parseInt(data.product_yesterday)) / parseInt(data.product_yesterday ))*100))+'%',
day_icon:parseInt(data.product_today) > parseInt(data.product_yesterday)?1:2,
week_rate:Math.abs(Math.floor(((parseInt(data.product_current_week)-parseInt(data.product_last_week)) / parseInt(data.product_last_week ))*100))+'%',
week_icon:parseInt(data.product_current_week) > parseInt(data.product_last_week)?1:2,
}
}
private alterTask(data):object{
return {
task_count:data.task_count,
task_month_sum:data.task_month_sum,
day_rate:Math.abs(Math.floor((parseInt(data.task_end_sum)/ parseInt(data.task_logs_sum ))*100))
}
}
private alterScript(data):object{
return {
script_week:data.script_week,
use_rate:Math.abs(Math.floor((parseInt(data.script_used)/ parseInt(data.script_count ))*100))+'%'
}
}
}

View File

@ -0,0 +1,184 @@
<template>
<div>
<!-- <project-list is-my-project></project-list>-->
<div class="demo-split1">
<Split v-model="0.5" mode="vertical">
<div slot="top" class="demo-split-pane1">
<Row>
<Col span="5" style="margin-left: 20px">
<Card >
<p slot="title" style="font-weight: bold;">构建量</p>
<p slot="extra"><Icon type="ios-keypad" style="font-size: 24px;color: #36cfc9"/></p>
<p > <span style="font-size: 28px">{{cardProductData.product_today}}</span>
<Tag type="border" color="success" style="width: 20px;float: right"> <span style="margin-left: -5px"></span></Tag></p>
<p>
<span>日同比 {{cardProductData.day_rate}}
<Icon type="md-arrow-dropup" color="#19be6b" v-if="cardProductData.day_icon==1"/>
<Icon type="md-arrow-dropdown" color="#ed4014" v-else/>
</span>&emsp;
<span>周同比 {{cardProductData.week_rate}}
<Icon type="md-arrow-dropup" color="#19be6b" v-if="cardProductData.week_icon==1"/>
<Icon type="md-arrow-dropdown" color="#ed4014" v-else/>
</span>
</p>
<div style="border-top: 1px solid #e8eaec;margin: 10px -16px 0 -16px;">
<div style="height:24px;padding-top:10px;display: flex;justify-content: space-between;">
<span style="margin-left: 10px">总构建量</span>
<span style="margin-right: 10px">{{cardProductData.product_count_trashed}}</span>
</div>
</div>
</Card>
</Col>
<Col span="5" offset="1">
<Card >
<p slot="title" style="font-weight: bold;">任务量</p>
<p slot="extra"><Icon type="ios-clipboard" style="font-size: 24px;color: #ff7a45" /></p>
<p>
<span style="font-size: 28px">{{cardTaskData.task_month_sum}}</span>
<Tag type="border" color="primary" style="width: 20px;float: right"> <span style="margin-left: -5px"></span></Tag>
</p>
<Progress :percent="cardTaskData.day_rate" hide-info class="progress" :content="'已完成'+cardTaskData.day_rate+'%'"/>
<div style="border-top: 1px solid #e8eaec;margin: 10px -16px 0 -16px;">
<div style="height:24px;padding-top:10px;display: flex;justify-content: space-between;">
<span style="margin-left: 10px">总任务数</span>
<span style="margin-right: 10px">{{cardTaskData.task_count}}</span>
</div>
</div>
</Card>
</Col>
<Col span="5" offset="1">
<Card >
<p slot="title" style="font-weight: bold;">脚本数</p>
<p slot="extra"><Icon type="logo-python" style="font-size: 24px;color: #47cb89"></Icon></p>
<p>
<span style="font-size: 28px">{{cardScriptData.script_week}}</span>
<Tag type="border" color="error" style="width: 20px;float: right"> <span style="margin-left: -5px"></span></Tag>
</p>
<p> &nbsp;</p>
<div style="border-top: 1px solid #e8eaec;margin: 10px -16px 0 -16px;">
<div style="height:24px;padding-top:10px;display: flex;justify-content: space-between;">
<span style="margin-left: 10px">使用率</span>
<span style="margin-right: 10px">{{cardScriptData.use_rate}}</span>
</div>
</div>
</Card>
</Col>
<Col span="5" offset="1">
<Card >
<p slot="title" style="font-weight: bold;">用户量</p>
<p slot="extra"><Icon type="ios-people" style="font-size: 24px;color: #57a3f3"/></p>
<p style="font-size: 28px">{{userNumber}}</p>
<p> &nbsp;</p>
<div style="border-top: 1px solid #e8eaec;margin: 10px -16px 0 -16px;">
<div style="height:24px;padding-top:10px;display: flex;justify-content: space-between;">
</div>
</div>
</Card>
</Col>
</Row>
</div>
<div slot="bottom" class="demo-split-pane1">
<div style="margin-top: 50px;display: flex;justify-content: space-around">
<Card style="width:120px;border-radius: 5px">
<div style="text-align:center" @click="shortcut('my_project')">
<div class="boxx"><svg-icon icon-class="project" /></div>
<h3>我的项目</h3>
</div>
</Card>
<Card style="width:120px;" >
<div style="text-align:center;" @click="shortcut('task')">
<div class="boxx"><svg-icon icon-class="task" /></div>
<h3>任务中心</h3>
</div>
</Card>
<Card style="width:120px;" >
<div style="text-align:center" @click="shortcut('script')">
<div class="boxx"><svg-icon icon-class="script" /></div>
<h3>脚本市场</h3>
</div>
</Card>
<Card style="width:120px;" >
<div style="text-align:center" @click="shortcut('device')">
<div class="boxx"><svg-icon icon-class="device" /></div>
<h3>设备大全</h3>
</div>
</Card>
<Card style="width:120px;" v-for="item in 4">
<div style="text-align:center">
<img src="@/assets/image/edit.png" width="40" style="margin-left: 20px"/><br >
<h3>尽情期待...</h3>
</div>
</Card>
</div>
</div>
</Split>
</div>
<div class="demo-split">
<Split v-model="0.5">
<div slot="left" class="demo-split-pane no-padding">
<Split v-model="0.5" mode="vertical" >
<div slot="top" class="demo-split-pane">
<section class="arrow1">
<div class="arrow1-wrap"><span style="padding-left:22px ">构建动态</span></div>
</section>
<List item-layout="horizontal" size="small">
<ListItem v-for="item in projectList">
<ListItemMeta :title="item.product_name" />
<template slot="action">
<li>
<span>{{item.date}}</span>
</li>
</template>
</ListItem>
<ListItem v-show="projectList.length>=5">
<ListItemMeta />
<template slot="action">
<li>
<a href="my_project">更多</a>
</li>
</template>
</ListItem>
</List>
</div>
<div slot="bottom" class="demo-split-pane">
<section class="arrow2">
<div class="arrow2-wrap"><span style="padding-left:22px ">脚本动态</span></div>
</section>
<List item-layout="horizontal" size="small">
<ListItem v-for="item in scriptList">
<ListItemMeta :title="item.content" />
<template slot="action">
<li>
<span>{{item.date}}</span>
</li>
</template>
</ListItem>
<ListItem v-show="scriptList.length>=5">
<ListItemMeta />
<template slot="action">
<li>
<a href="auto-test/script">更多</a>
</li>
</template>
</ListItem>
</List>
</div>
</Split>
</div>
<div slot="right" class="demo-split-pane">
<p style="text-align: center;font-size: 20px;font-weight: bolder;margin-top: 15px">任务执行情况</p>
<div ref="myChart" style="width: 800px; height:550px;margin-top:40px" ></div>
</div>
</Split>
</div>
</div>
</template>
<script lang="ts" src="./home.ts"></script>
<style lang="less" src="./home.less" scoped></style>

View File

@ -0,0 +1,15 @@
.item-menu {
color: hsla(0,0%,100%,.7);
}
.layout{
border: 1px solid #d7dde4;
background: #f5f7f9;
position: relative;
border-radius: 4px;
overflow: hidden;
}

View File

@ -0,0 +1,154 @@
import { Component, Mixins, Vue, Watch } from 'vue-property-decorator'
import Url, { AdminUrl } from "@/utils/Url";
import {
State,
Getter,
Action,
Mutation,
} from "vuex-class";
import Types from "@/views/admin/store/types";
import HomeParent, { BreadItem } from "@/views/home/Common/HomeParent";
import { EVENT_TYPE, MenuInterface, ProjectInterface } from "@/views/home/Common/Types";
import { Route, RouteRecord } from "vue-router";
import { Menu } from "view-design";
@Component
export default class Layout extends HomeParent {
public siteLogo: object = {}
public siteMarquee: object[] = [];
public visible: boolean = false;
public myMenus: MenuInterface[] = [];
public routes: BreadItem[] = [];
public myProject: ProjectInterface[] = [];
public created() {
this._getMyMenu()
// this._getMyProject()
// this.$bus.$on(EVENT_TYPE.UPDATE_BREAD_ITEM, this._updateRoutes.bind(this));
// this.$bus.$on(EVENT_TYPE.UPDATE_MY_PROJECT, this._getMyProject.bind(this));
}
@Watch("$route", {immediate: true, deep: true})
private _watchRoute(route: Route): void {
this.routes.length = 0;
route.matched.forEach((item: RouteRecord) => {
let ret: BreadItem = this.$router.resolve({name: item.name, params: route.params});
ret.name = item.meta.title;
this.routes.push(ret);
// console.log(ret);
});
}
public get isLogin() {
return this.$store.state.user.token.length > 0;
}
public onDropdownEvent(name: string): void {
switch (name) {
case "logout" : this.logout();break;
case "updateName" : this.updateName();break;
}
}
public logout() {
this.$http.get(Url.LOGOUT).then(res => {
this._push('/login')
});
}
public updateName(){
let self = this;
this.$Modal.confirm({
title:"修改昵称",
render: function(h) {
return h('Input', {
props: {
value: self.$store.getters.user.name,
autofocus: true,
placeholder: 'Please enter your name...'
},
on: {
input: (val) => {
this.value = val;
}
}
})
},
onOk :function() {
self.onVisibleChange(true)
if (!this.value || this.value == self.$store.getters.user.name || this.value == '') {
return;
}
let data = {name:this.value};
self.$http.put(Url.UPDATE_MY_NAME,data).then(res=>{
self.success('修改成功',()=>{
self.$store.getters.user.name = res.data.name
let obj = JSON.parse(window.localStorage.getItem('home'));
obj.user.user.name = res.data.name
window.localStorage.setItem('home',JSON.stringify(obj));
})
}).catch((err)=>{
self.error(err)
})
}
})
}
/**
* dropdown
* @param name
*/
public onMenuChange(name): void {
this.visible = false;
}
/**
* dropdown显示状态
* @param st
*/
public onVisibleChange(st: boolean) : void {
this.visible = !this.visible;
}
private _getMyMenu() {
this.myMenus = [
{id:1,name:'用户管理',url:'/user',children:[
{id:2,name:'用户列表',url:'/user/list',children:[]},
{id:3,name:'用户权限',url:'/user/node',children:[]},
{id:4,name:'用户菜单',url:'/user/menu',children:[]},
]},
{id:5,name:'表格',url:'/table',children:[
{id:6,name:'普通表格',url:'/table/base',children:[]},
{id:7,name:'复杂表格',url:'/table/complex',children:[]},
]}
]
}
private _getMyProject() {
this.$http.get(Url.MY_PROJECT).then(res => {
this.myProject = res.data;
});
}
private _parseTreeData(list: any[]) {
return list.map((item: any) => {
if (item.children.length > 0) {
item._showChildren = true;
item.children = this._parseTreeData(item.children);
}
return item
});
}
private _updateRoutes(name: string, idx: number): void {
if (this.routes.length >= idx - 1) {
this.routes[idx].name = name;
}
}
}

View File

@ -0,0 +1,54 @@
<template>
<div class="layout w-screen h-screen bg-white" >
<Sider :style="{position: 'fixed', height: '100vh', left: 0, overflow: 'auto'}" width="300" class="bg-white" style="margin-top: -2px">
<h2 class="text-3xl italic pl-8 text-center " style="height: 80px;line-height: 80px;">系统</h2>
<Menu width="300">
<Submenu :name="item.id" v-for="item in myMenus" :key="item.id" accordion >
<template slot="title">
<span v-if="item.children.length>0">{{item.name}}</span>
<router-link v-else :to="item.url">{{item.name}}</router-link>
</template>
<MenuItem :name="sonChildren.id" v-for="sonChildren in item.children" :key="sonChildren.id"
:to="sonChildren.url">
{{sonChildren.name}}
</MenuItem>
</Submenu>
</Menu>
</Sider>
<div style="margin-left:300px ">
<div style="height: 66px;box-shadow: 0 2px 3px 2px rgba(0,0,0,.1);line-height: 66px">
<div style="float: left;margin-left: 25px">
<Breadcrumb params="params" >
<BreadcrumbItem :to="route.route.fullPath" :key="route.href"
v-for="route in routes" class="text-base "><span class="text-current ">{{route.name}}</span></BreadcrumbItem>
</Breadcrumb>
</div>
<div class="pr-8 text-current" style="float: right">
<Dropdown ref="dropdown" trigger="custom" @on-click="onDropdownEvent" :visible="visible">
<a href="javascript:void(0)" @click="onVisibleChange">
<Icon class="text-4xl " type="ios-contacts"/>
<!-- <span>{{this.$store.getters.user.name}}</span>-->
<Icon type="ios-arrow-down"></Icon>
</a>
<DropdownMenu slot="list" >
<DropdownItem name="updateName" class="text-black hover:text-white hover:bg-gray-700" >修改昵称</DropdownItem>
<DropdownItem name="logout" class="text-black hover:text-white hover:bg-gray-700" >退出</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</div>
<div>
<div class="p-4 overflow-y-auto lg:h-(screen-14)" >
<router-view ></router-view>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" src="./layout.ts"></script>
<style lang="less" src="./layout.less" scoped></style>

View File

@ -0,0 +1,3 @@
<template>
<router-view></router-view>
</template>

View File

@ -0,0 +1,102 @@
.login {
height: 100%;
width: 100%;
background: url(../../../../assets/image/loginBg.jpg) no-repeat center center;
background-size: 100% 100%;
overflow: hidden;
}
.loginBox {
height: 455px;
width: 550px;
margin: 0 auto;
position: relative;
top: 50%;
margin-top: -287px;
}
.loginH2 {
font-size: 38px;
color: #fff;
text-align: center;
}
.loginCon {
margin-top: 30px;
background: #eee;
border-radius: 4px;
.titleDiv {
padding: 0 28px;
background: #fff;
position: relative;
height: 120px;
border-radius: 4px 4px 0 0;
h3 {
font-size: 22px;
color: #555;
font-weight: initial;
padding-top: 23px;
}
p {
font-size: 16px;
color: #888;
padding-top: 12px;
}
i {
font-size: 65px;
color: #ddd;
position: absolute;
right: 27px;
top: 29px;
}
}
.el-form {
padding: 25px 25px 30px 25px;
background: #eee;
border-radius: 0 0 4px 4px;
}
}
.loginBtn {
width: 100%;
background: #19b9e7;
}
.slideShadow {
position: fixed;
z-index: 999;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.6);
}
.slideSty {
position: absolute;
width: 380px;
height: 311px;
background: #e8e8e8;
border: 1px solid #dcdcdc;
left: 50%;
top: 50%;
margin-left: -188px;
margin-top: -176px;
z-index: 99;
border-radius: 5px;
}
.iconBtn {
padding: 9px 0 0 19px;
color: #5f5f5f;
border-top: 1px solid #d8d8d8;
margin-top: 17px;
i {
font-size: 22px;
cursor: pointer;
}
i:last-child {
margin-left: 7px;
}
}
.slideSty .slide-verify {
margin: 13px auto 0 auto;
width: 350px !important;
}
.slideSty .slide-verify-slider {
width: 100% !important;
}
.slideSty .slide-verify-refresh-icon {
display: none;
}

View File

@ -0,0 +1,227 @@
import { Component, Mixins, Vue, Watch } from 'vue-property-decorator'
import Url, { AdminUrl } from "@/utils/Url";
import {
State,
Getter,
Action,
Mutation,
} from "vuex-class";
import { Form } from "view-design";
import "@/assets/js/wwlogin";
import HomeParent from "@/views/home/Common/HomeParent";
import Types from "@/views/admin/store/types";
import SvgIcon from '@/components/SvgIcon.vue'
import { Message } from 'element-ui';
import ChainManager from '@/metamask/ChainManager'
import Web3 from 'web3'
import {Http_getNonce, Http_login} from "@/utils/login-request";
import store from "@/views/home/store";
import {ActionContext} from "vuex";
import {UserState} from "@/views/home/store/user";
interface Window {
ethereum: any
web3: any
celo: any
}
declare let window: Window
@Component({
components : {
SvgIcon
}
})
export default class Login extends HomeParent {
@Action('Login') Login;
public context : ActionContext<UserState, any>
public provider:any
public web3:Web3
public chainId:number
public account:string
public nonce:string
public async btn(){
if (!this.$store.getters.step){
try {
await this.connect()
await this.checkNance()
} catch (err) {
Message({
message: err.message,
type: 'error',
duration: 5 * 1000
})
Promise.reject(err)
}
}
}
public get accessToken(): string {
let token: string = this.$route.query.access_token as string;
return token;
}
public rules: object = {
username: [{required: true, trigger: 'blur', message: "请输入登录名"}],
password: [{required: true, trigger: 'blur', message: "请输入登录密码"}],
}
public created() {
// if(!this.accessToken) {
// this._getData();
// }
}
private _getData(): void {
this.$http.get(Url.WORK_LOGIN).then(res => {
window["WwLogin"]({
id: "loginPage",
goto: res.data
})
})
}
// public doLogin(): void {
// let form: Form = this.$refs["login"] as Form;
// form.validate((valid: boolean) => {
// if (!valid) {
// return;
// }
// // this.Login(this.form).then((user) => {
// // this.goPage("home");
// // }).catch((msg: string) => {
// // this.error(msg);
// // })
// });
// }
private async login( account, chainId, nonce) {
nonce += ''
const tips = 'This signature is only used for verify your account'
const signMsg = {
tips,
nonce
}
const EIP721_DOMAIN_DATA = [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' }
]
const signObj = {
types: {
EIP712Domain: EIP721_DOMAIN_DATA,
set: [
{ name: 'tips', type: 'string' },
{ name: 'nonce', type: 'string' }
]
},
primaryType: 'set',
domain: {
name: 'Auth',
version: '1'
},
message: signMsg
}
const signature = await this.signData(signObj, account)
const authData = {
account,
nonce,
signature,
tips,
net_id: chainId
}
const res:any = await Http_login(authData)
if (!res.errcode && res.token) {
store.commit('set_token',res.token)
console.log(store.getters)
}
}
private async checkNance() {
try {
let nonce = store.getters.nonce
if (!nonce) {
let params = {account:this.account, net_id:this.chainId}
const res:any = await Http_getNonce(params);
store.commit('set_nonce',res.nonce)
this.nonce = res.nonce
}
this.login(this.account,this.chainId,this.nonce)
} catch (err) {
Promise.reject(err)
}
}
private async connect(){
if (!this.hasMetamask()) {
Message({
message: '请先安装MetaMask插件',
type: 'error',
duration: 5 * 1000
})
return
}
this.provider = await this.connectMetaMask()
if (!this.provider) {
return
}
this.web3 = new Web3(this.provider)
this.chainId = await this.web3.eth.getChainId()
const accounts = await this.web3.eth.getAccounts()
if (accounts && accounts.length > 0) {
this.account =accounts[0];
}
}
private async connectMetaMask() {
let provider = window.ethereum
try {
await provider.request({ method: 'eth_requestAccounts' })
} catch (error) {
if (error.code === -32002) {
throw new Error('MeatMask not login, Open MeatMask and login first')
} else {
throw new Error('User Rejected')
}
}
return provider
}
private hasMetamask():boolean {
if (typeof window.ethereum !== 'undefined') {
return true;
}else {
return false;
}
}
private async signData(signObj: any, signer: string) {
const msgParams = JSON.stringify(signObj)
const from = signer
const params = [from, msgParams]
const result: any = await this.sendCmd({
method: 'eth_signTypedData_v4',
params,
from
})
return result.result
}
private async sendCmd({ method, params, from }:any) {
return new Promise((resolve, reject) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.web3.currentProvider.sendAsync({
method,
params,
from
}, async function(err: any, result: any) {
if (err) {
reject && reject(err)
return
}
resolve && resolve(result)
})
})
}
}

View File

@ -0,0 +1,14 @@
<template>
<div class="login">
<div class="w-full h-3/5 flex items-center justify-center align-middle">系统</div>
<div class="w-full flex justify-center align-middle">
<div class=" rounded-xl" @click="btn">
<svg-icon icon-class="login" style="width: 100px;height: 100px;cursor:pointer"/>
</div>
</div>
</div>
</template>
<script lang="ts" src="./login.ts"></script>
<style lang="less" scoped src="./login.less"></style>

View File

@ -0,0 +1,41 @@
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import Routers from './router'
import Store from '../store'
import { LoadingBar } from 'view-design'
import Types from "@/views/home/store/types";
Vue.use(VueRouter)
const routes : Array<RouteConfig> = [
...Routers
]
const router = new VueRouter({
base : "/dashboard/",
mode : "history",
routes,
})
function setTitle(title : string) : void {
window.document.title = title;
}
router.beforeEach((to, from, next) => {
LoadingBar.start();
let title : string = process.env.VUE_APP_TITLE;
setTitle(to.meta.title + '-' + title);
let token : string = to.query.access_token as string;
if (token) {
Store.commit(Types.SET_USER_TOKEN, token);
next({name : to.name === 'login'? 'home' : to.name})
return;
}
next();
})
router.afterEach((to, from) => {
LoadingBar.finish();
})
export default router

View File

@ -0,0 +1,23 @@
import { RouteConfig } from "vue-router";
import Layout from '../pages/layout/layout.vue'
const routers: RouteConfig[] = [{
path: "/",
name: 'layout',
component: Layout,
redirect: "/home",
meta: {title: "首页", isMenu: false},
children: [{
path: "home",
name: 'home',
meta: {title: "dashboard", isMenu: false},
component: () => import(/* webpackChunkName: "home-home" */ '@/views/home/pages/home/home.vue'),
}]
}, {
path: "/login",
name: 'login',
meta: {title: "", isMenu: false},
component: () => import(/* webpackChunkName: "home-login" */ '@/views/home/pages/login/login.vue'),
}];
export default routers;

View File

@ -0,0 +1,54 @@
import Vue from 'vue'
import Vuex from 'vuex'
import user, { UserState } from "@/views/home/store/user";
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
interface StateInterface {
user : UserState,
}
const localStorage : any = window.localStorage;
export default new Vuex.Store({
getters : {
token : (state : StateInterface) => state.user.token,
user : (state : StateInterface) => state.user.user,
step:state => state.step,
nonce:state => state.nonce
},
modules : {
user
},
plugins : [
createPersistedState({
key : 'home',
storage : {
getItem : (key : string) => {
return localStorage.getItem(key);
},
setItem : (key : string, value : any) => {
return localStorage.setItem(key, value);
},
removeItem : (key : string) => {
return localStorage.removeItem(key);
}
}
})
],
mutations: {
set_nonce(state, nonce) {
state.nonce = nonce
// sessionStorage.nonce = nonce
},
set_token(state, token) {
state.token = token
sessionStorage.token = token
},
del_token(state) {
state.token = ''
sessionStorage.removeItem('token')
}
}
})

View File

@ -0,0 +1,9 @@
export default class Types {
static readonly SET_USER_INFO : string = 'SET_USER_INFO';
static readonly SET_USER_TOKEN : string = 'SET_USER_TOKEN';
static readonly SET_LANG : string = 'SET_LANG';
static readonly SET_SITE_INFO : string = "SET_SITE_INFO";
static readonly SET_USER_CURRENCY : string = 'SET_USER_CURRENCY';
static readonly UPDATE_USER_CREDIT : string = 'UPDATE_USER_CREDIT'
}

View File

@ -0,0 +1,46 @@
import {ActionContext} from "vuex";
import Http, { ResponseErrorInterface, ResponseInterface } from "@/utils/Http";
import Url from "@/utils/Url";
import Types from "@/views/home/store/types";
import { UserInterface } from "@/views/home/Common/Types";
const sprintf = require('sprintf-js');
export interface UserState {
user : UserInterface,
token : string | undefined,
isLogin : boolean,
}
export default {
name : "user",
namespace : true,
state : {
user : {},
token : undefined,
isLogin : false
},
actions : {
Login(context : ActionContext<UserState, any>, form : any) {
return Http.post(Url.LOGIN, form).then((res : ResponseInterface) => {
context.commit(Types.SET_USER_INFO, res.data);
context.commit(Types.SET_USER_TOKEN, res.data.access_token);
return Promise.resolve(res.data);
}).catch((err : string) => {
return Promise.reject(err);
})
},
getMenus() {
// return Http.get()
}
},
mutations : {
[Types.SET_USER_INFO](state : UserState, user : UserInterface) {
state.user = user;
},
[Types.SET_USER_TOKEN](state : UserState, token : string) {
state.token = token;
state.isLogin = token.length > 0;
},
}
}

12
src/views/wap/App.vue Normal file
View File

@ -0,0 +1,12 @@
<template>
<div id="app">
<router-view/>
</div>
</template>
<style>
html {
height: 100%;
background: #f2f2f2;
}
</style>

View File

@ -0,0 +1,20 @@
import Parent from "@/utils/Parnet";
export default class WapParent extends Parent {
public success(content? : string, callback? : Function) {
this.$toast.success( {
message: content ? content : "操作成功",
onClose : () => {
callback && callback();
}
})
}
public error(content? : string, callback? : Function){
this.$toast.fail( {
message: content ? content : "操作成功",
onClose : () => {
callback && callback();
}
})
}
}

View File

Some files were not shown because too many files have changed in this diff Show More