This commit is contained in:
王思川 2022-10-09 09:54:47 +08:00
parent aed2951935
commit 2c81f9adb9
64 changed files with 17159 additions and 86 deletions

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@ -1,92 +1,19 @@
# webapp_wideRating
## Getting started
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
## Add your files
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
# webapp_data-manager
## Project setup
```
cd existing_repo
git remote add origin http://gitlab.fecribd.com/xuyucheng/webapp_widerating.git
git branch -M main
git push -uf origin main
npm install
```
## Integrate with your tools
### Compiles and hot-reloads for development
```
npm run serve
```
- [ ] [Set up project integrations](http://gitlab.fecribd.com/xuyucheng/webapp_widerating/-/settings/integrations)
### Compiles and minifies for production
```
npm run build
```
## Collaborate with your team
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
## Test and Deploy
Use the built-in continuous integration in GitLab.
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
***
# Editing this README
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
## Suggestions for a good README
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
## Name
Choose a self-explaining name for your project.
## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
## Badges
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.
## Contributing
State if you are open to contributions and what your requirements are for accepting them.
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.
## License
For open source projects, say how it is licensed.
## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
### 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/cli-plugin-babel/preset'
]
}

12231
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

56
package.json Normal file
View File

@ -0,0 +1,56 @@
{
"name": "webapp_data_manager",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.21.1",
"core-js": "^3.6.5",
"echarts": "^4.9.0",
"element-china-area-data": "^5.0.2",
"element-ui": "^2.15.6",
"jsencrypt": "^3.1.0",
"prismjs": "^1.29.0",
"vue": "^2.6.11",
"vue-monoplasty-slide-verify": "^1.1.3",
"vue-prism-editor": "^1.3.0",
"vue-router": "^3.5.1",
"vuex": "^3.6.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"element-theme-chalk": "^2.15.9",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"vue-template-compiler": "^2.6.11",
"vue2-ace-editor": "0.0.15"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

17
public/index.html Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<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">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<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>

23
src/App.vue Normal file
View File

@ -0,0 +1,23 @@
<template>
<div id="app">
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
</div>
</template>
<script>
export default {
name: "App",
mounted() {},
methods: {},
};
</script>
<style lang='scss' scoped>
#app{
height: 100%;
}
</style>

169
src/api/request.js Normal file
View File

@ -0,0 +1,169 @@
import axios from 'axios'
import store from "store/index"
import router from 'router/index'
import { Message } from 'element-ui';
import { rsaKey } from "utils/rsaKey.js";
import { message } from 'utils/message.js'
// import { Loading } from 'element-ui'
//创建axios实例
axios.defaults.timeout = 10000
axios.defaults.baseURL = 'http://116.63.159.166:5001'
// axios.defaults.baseURL="http://127.0.0.1:4523/m1/1152927-0-default"
//请求拦截器
axios.interceptors.request.use(config => {
// const token = store.state.token
// if (token) {
// config.headers.token = token
// }
return config
}, error => {
Promise.reject(error)
})
//响应拦截器
axios.interceptors.response.use(response => {
if (response.status === 200) {
return response;
} else {
return response;
}
}, error => {
const { status } = error.response;
switch (status) {
case 300:
break;
case 400:
Message({
message: "请求错误,请重试",
type: "warning",
});
break;
case 401:
store.commit("del_token")
store.commit("del_login")
store.commit("set_user_verify", false)
router.push('login')
break;
case 500:
message.warning("服务器错误");
break
}
return Promise.reject(error);
});
export default {
get: function (path = '', data = {}) {
return new Promise(function (resolve, reject) {
axios.get(path, {
params: data
})
.then(function (response) {
resolve(response.data);
})
.catch(function (error) {
reject(error);
});
});
},
// FromData - post
FormDataPost: function (path = '', data = {}) {
let formData = new FormData()
for (let key in data) {
formData.append(key, data[key])
}
return new Promise(function (resolve, reject) {
axios.post(path, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(function (response) {
resolve(response.data);
})
.catch(function (error) {
reject(error);
});
});
},
// json - post
post: function (path = '', data = {}) {
return new Promise(function (resolve, reject) {
axios.post(path, data)
.then(function (response) {
resolve(response.data);
})
.catch(function (error) {
reject(error);
});
});
},
// blob - get
BlobGet: function (path = '',type) {
return new Promise(function (resolve, reject) {
axios.get(
path, { responseType: 'blob' })
.then(function (response) {
const blob = new Blob([response.data],{type:type});
resolve(URL.createObjectURL(blob));
})
.catch(function (error) {
reject(error);
});
})
},
// blob - post
BlobPost: function (path = '', data = {}) {
return new Promise(function (resolve, reject) {
axios.post(
path, data, { responseType: 'blob' })
.then(function (response) {
const blob = new Blob([response.data],{type:"application/zip"});
resolve(URL.createObjectURL(blob));
})
.catch(function (error) {
reject(error);
});
})
},
// headers - rsaKey - post
VerifyPost: function (path = '', data = {}) {
return new Promise(function (resolve, reject) {
axios.post(
path, data, {
headers: {
verify: rsaKey(Date.now().toString())
}
})
.then(function (response) {
resolve(response.data);
})
.catch(function (error) {
reject(error);
});
})
},
// headers - rsaKey(滑块验证) - post
BlockPost: function (path = '', data = {}) {
return new Promise(function (resolve, reject) {
axios.post(
path, data, {
headers: {
block: rsaKey(Date.now().toString())
}
})
.then(function (response) {
resolve(response.data);
})
.catch(function (error) {
reject(error);
});
})
},
}

Binary file not shown.

2
src/assets/img/QQ.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="1649730397018" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3893" 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="M824.8 613.2c-16-51.4-34.4-94.6-62.7-165.3C766.5 262.2 689.3 112 511.5 112 331.7 112 256.2 265.2 261 447.9c-28.4 70.8-46.7 113.7-62.7 165.3-34 109.5-23 154.8-14.6 155.8 18 2.2 70.1-82.4 70.1-82.4 0 49 25.2 112.9 79.8 159-26.4 8.1-85.7 29.9-71.6 53.8 11.4 19.3 196.2 12.3 249.5 6.3 53.3 6 238.1 13 249.5-6.3 14.1-23.8-45.3-45.7-71.6-53.8 54.6-46.2 79.8-110.1 79.8-159 0 0 52.1 84.6 70.1 82.4 8.5-1.1 19.5-46.4-14.5-155.8z" p-id="3894" fill="#1296db"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/assets/img/avatar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
src/assets/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

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="1658281261954" class="icon" viewBox="0 0 1040 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2986" xmlns:xlink="http://www.w3.org/1999/xlink" width="203.125" 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="M509.92 176C325.504 176 176 325.504 176 509.92c0 184.416 149.504 333.92 333.92 333.92 184.416 0 333.92-149.504 333.92-333.92C843.84 325.504 694.32 176 509.92 176z m0 48c157.904 0 285.92 128 285.92 285.92 0 157.904-128.016 285.92-285.92 285.92C352 795.84 224 667.808 224 509.92 224 352 352 224 509.92 224z m0 96C405.024 320 320 405.024 320 509.92c0 104.88 85.024 189.92 189.92 189.92 104.88 0 189.92-85.04 189.92-189.92 0-104.896-85.04-189.92-189.92-189.92z" p-id="2987" fill="#409EFF"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

2
src/assets/img/radio.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="1658281705607" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2551" 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="M512 170.666667a341.333333 341.333333 0 1 1 0 682.666666 341.333333 341.333333 0 0 1 0-682.666666z m0 42.666666a298.666667 298.666667 0 1 0 0 597.333334 298.666667 298.666667 0 0 0 0-597.333334z" fill="#A2B2C3" p-id="2552"></path></svg>

After

Width:  |  Height:  |  Size: 928 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

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="1649731015829" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8261" width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink"><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="M150.528 431.104q37.888 0 58.368 24.064t20.48 51.712l0 11.264q0 34.816-17.92 58.88t-59.904 24.064l-7.168 0q-38.912 0-61.952-21.504t-23.04-59.392l0-14.336q0-13.312 5.632-26.624t15.872-24.064 25.6-17.408 33.792-6.656l10.24 0zM519.168 431.104q37.888 0 58.368 24.064t20.48 51.712l0 11.264q0 34.816-17.92 58.88t-59.904 24.064l-7.168 0q-38.912 0-61.952-21.504t-23.04-59.392l0-14.336q0-13.312 5.632-26.624t15.872-24.064 25.6-17.408 33.792-6.656l10.24 0zM887.808 431.104q37.888 0 58.368 24.064t20.48 51.712l0 11.264q0 34.816-17.92 58.88t-59.904 24.064l-7.168 0q-38.912 0-61.952-21.504t-23.04-59.392l0-14.336q0-13.312 5.632-26.624t15.872-24.064 25.6-17.408 33.792-6.656l10.24 0z" p-id="8262" fill="#dbdbdb"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

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="1649730468936" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6333" 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="M474.697931 463.133036c52.537564-55.155181 119.125121-74.453712 197.466374-70.189595-1.747807-8.009418-1.814322-14.22191-4.389984-19.11843-12.712533-24.170492-22.698978-50.789757-39.787168-71.474868-102.242616-123.764791-308.057121-138.461515-427.463652-31.18935-61.691037 55.423287-87.38421 124.68986-69.739341 206.809159 11.298324 52.575426 43.588751 92.715635 85.609797 124.551714 13.697977 10.382465 15.916505 19.665899 10.030447 34.608216-7.30436 18.535145-12.809747 37.781488-19.0908 56.720839 17.596773-3.874237 31.816636-9.761318 44.911886-17.542538 30.707372-18.24555 61.189617-28.17162 98.18623-16.900925 22.337751 6.800893 47.565319 4.123924 74.762751 5.92085C405.477406 585.486688 421.574013 518.909363 474.697931 463.133036zM497.939261 319.220369c19.834744-0.284479 31.798217 10.92277 32.226982 30.178323 0.442068 19.85521-10.726296 31.997762-29.841655 32.44597-21.970384 0.51677-38.566364-12.741185-38.723953-30.930453C461.449185 333.410556 477.38411 319.510988 497.939261 319.220369zM309.594639 381.837498c-21.693068 0.073678-37.788651-13.573133-37.541011-31.828916 0.233314-17.353227 16.143679-30.628578 36.897352-30.79333 19.576871-0.150426 33.157167 13.06148 32.867572 31.983435C341.537142 369.591593 328.722278 381.778146 309.594639 381.837498z" p-id="6334" fill="#15ad31"></path><path d="M835.363224 471.499587c-81.796958-78.773088-215.099986-91.444689-312.212768-29.66974-125.474736 79.81379-124.392078 243.768933 2.771113 320.735885 61.081147 36.97103 127.145795 47.321772 196.581214 28.592198 14.377452-3.879354 26.002211-2.758834 38.630832 5.067412 17.174148 10.645454 35.464723 19.495006 53.278437 29.115108 1.274016-0.950651 2.548032-1.901303 3.822049-2.852978-4.882194-17.019629-10.796904-33.842783-14.117532-51.16531-1.249457-6.507204 1.530866-15.896038 5.932106-20.968567 11.326976-13.038968 25.615401-23.515576 36.914748-36.58115C913.685034 636.613112 908.943033 542.366611 835.363224 471.499587zM589.682755 564.978609c-14.864546 0.228197-26.891464-11.264555-26.424836-25.248034 0.456395-13.707187 11.322883-23.429619 26.14752-23.38971 16.312524 0.041956 29.684066 11.452843 29.205159 24.921599C618.16239 553.809221 604.82257 564.746318 589.682755 564.978609zM737.859539 565.009308c-13.485129-0.203638-26.317389-11.747555-26.63359-23.958668-0.340761-13.07069 12.692067-24.846898 27.374464-24.735357 16.766872 0.12996 28.897144 11.084453 28.241204 25.499767C766.255263 554.683125 753.061776 565.241598 737.859539 565.009308z" p-id="6335" fill="#15ad31"></path></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

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="1649730523725" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7390" 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="M761.984 1009.28H253.696C138.368 1009.28 44.8 915.712 44.8 800.384V292.096C44.8 176.768 138.368 83.2 253.696 83.2h508.288c115.328 0 208.896 93.568 208.896 208.896v508.288c0 115.328-93.568 208.896-208.896 208.896z" fill="#00E560" p-id="7391"></path><path d="M507.776 265.856c-72.576 0-139.776 20.352-193.536 54.528-54.272 34.432-94.592 83.2-113.408 139.264-7.552 22.528-11.648 46.464-11.648 71.04 0 92.16 56.448 172.928 142.208 220.544 7.68 4.224 11.648 13.44 8.32 21.632-6.4 15.616-14.592 30.592-24.32 44.672-3.072 4.352 0.768 10.368 6.016 8.96 29.44-7.808 56.96-19.072 81.92-33.28 9.088-5.248 19.456-6.912 29.696-4.864 23.936 4.736 49.024 7.296 74.88 7.296 176 0 318.592-118.656 318.592-264.832 0-146.304-142.72-264.96-318.72-264.96z" fill="#FFFFFF" p-id="7392"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,85 @@
$theme-color:#8276c9;
$theme-base-color: #d9d9d9;
$background-color-light-aside: #FFFFFC;
$background-color-dark-aside: #1F1F1F;
$background-color-light-main: #f0f2f5;
$background-color-dark-main: #303030;
$background-color-light-card: #fff;
$background-color-dark-card: #40403F;
$font-color-light:#000;
$font-color-dark:#fff;
$success: #87d068;
$failure: rgb(255,85,0,0.87);
$warning: #E6A23C;
$info: #909399;
@mixin themeBackgroundColorHeader($color) {
background-color: $color;
[data-theme="light"] & {
background-color: $background-color-light-header;
}
[data-theme="dark"] & {
background-color: $background-color-dark-header;
}
}
@mixin themeBackgroundColorAside($color) {
background-color: $color;
[data-theme="light"] & {
background-color: $background-color-light-aside;
}
[data-theme="dark"] & {
background-color: $background-color-dark-aside;
}
}
@mixin themeBackgroundColorMain($color) {
background-color: $color;
[data-theme="light"] & {
background-color: $background-color-light-main;
}
[data-theme="dark"] & {
background-color: $background-color-dark-main;
}
}
@mixin themeBackgroundColorCard($color) {
background-color: $color;
[data-theme="light"] & {
background-color: $background-color-light-card !important;
}
[data-theme="dark"] & {
background-color: $background-color-dark-card !important;
}
}
@mixin themeFontColor($color) {
color: $color;
[data-theme="light"] & {
color: $font-color-light;
}
[data-theme="dark"] & {
color: $font-color-dark;
}
}
@mixin flex($justify) {
display: flex;
justify-content: $justify;
align-items: center;
}

View File

@ -0,0 +1,4 @@
@font-face {
font-family: 'SRL';
src: url('../font/OPPOSans-L-2.ttf');
}

View File

@ -0,0 +1,38 @@
.el-container {
height: 100%;
// padding: 40px;
background-color: #050F31;
.el-aside {
background:#27282e;
overflow: hidden;
height: 100%;
border-radius: 6px;
.el-menu {
height: calc(100% - 240px);
border: none;
background-color: transparent;
margin-top: 20px;
&-item {
i,
span {
font-size: 12px;
color: rgb(255,255,255,0.87);
}
span {
margin-right: 80px;
}
}
}
}
.el-main {
padding: 20px 40px;
overflow: hidden;
background-color: #232429
}
}

View File

@ -0,0 +1,538 @@
@import './font.css';
html {
overflow-y: scroll;
height: 100%;
}
body {
margin: 0;
padding: 0;
font: 12px "SRL";
background: #ffffff;
height: 100%;
}
div,
dl,
dt,
dd,
ul,
ol,
li,
h1,
h2,
h3,
h4,
h5,
h6,
pre,
form,
fieldset,
input,
textarea,
blockquote,
p,
span {
padding: 0;
margin: 0;
font-family: 'SRL';
color: rgb(255, 255, 255, 0.87);
font-size: 14px;
}
table,
td,
tr,
th {
font-size: 12px;
}
li {
list-style-type: none;
}
img {
vertical-align: top;
border: 0;
}
ol,
ul {
list-style: none;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: 12px;
font-weight: normal;
}
address,
cite,
code,
em,
th {
font-weight: normal;
font-style: normal;
}
::-webkit-scrollbar {
width: 5px;
height: 10px;
}
::-webkit-scrollbar-thumb {
background-color: $info;
border-radius: 3px;
}
.el-form-item__label {
color: rgb(255, 255, 255, 0.9);
}
.el-pagination {
span {
color: rgb(255, 255, 255, 0.9);
}
}
.el-card__header {
border-bottom: 0px !important;
}
.el-tabs__item {
background-color: #212121
}
.el-tabs__item.is-active {
span {
color: #8276c9 !important;
}
}
h2 {
font-size: 16px;
font-family: 800;
padding-left: 20px;
}
h2:before {
content: "";
display: inline-block;
width: 3px;
height: 18px;
background: #ff9347;
border-radius: 2px;
position: relative;
top: 12px;
left: -8px;
transform: translateY(-50%);
}
.el-tag {
cursor: pointer;
margin-right: 15px;
border: none;
background-color: $theme-color;
color: rgb(255, 255, 255, 0.87);
&__close {
color: rgb(255, 255, 255, 0.87) !important;
}
&__close:hover {
background-color: rgb(255, 255, 255, 0.87) !important;
color: $theme-color !important;
}
}
.el-tag--dark.el-tag--success {
background-color: #4ab793;
color: rgb(255, 255, 255, 0.87);
border: none;
}
.el-tag--dark.el-tag--warning {
background-color: #ce7933;
border: none;
}
.operation-tag {
width: 100%;
text-align: center;
position: relative;
background-color: $theme-color;
color: rgb(255, 255, 255, 0.87);
border: none;
}
// 省略描述
.omit-des {
width: 300px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
box-sizing: border-box;
}
.el-button {
font-size: 12px;
border-radius: 2px;
&--default {
span {
color: $theme-color;
}
}
&--primary,
&--primary:hover,
&--primary:focus {
background-color: $theme-color;
border: none;
}
}
.button-edit {
span {
color: #409EFF;
}
}
// 删除按钮
.button-delete {
span {
color: #E6A23C;
}
}
.el-button--text {
padding: 0;
}
.el-textarea .el-textarea__inner {
resize: none;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
input[type="number"] {
-moz-appearance: textfield;
}
.el-input__inner {
border-radius: 0px;
}
.el-popconfirm__main {
margin: 10px 0;
}
.el-card {
background-color: #27282e;
border: none;
height: 100%;
}
.el-descriptions {
&__header {
margin-bottom: 10px;
}
&__body {
background-color: transparent;
}
&-item__label {
font-weight: 800;
@include flex(center);
}
&-item__label.is-bordered-label {
background-color: transparent;
border: none;
min-height: 62px;
}
}
.el-collapse-item {
&__header {
background-color: #212121;
}
&__content {
background-color: #212121;
.normal {
color: #fff !important;
}
}
}
.el-dialog {
background-color: #212121;
&__title {
color: #fff;
}
}
.el-input {
&__inner {
background-color: #191a1f;
border: 1px solid transparent;
color: rgb(255, 255, 255, 0.87);
}
&__inner:hover,
&__inner:focus {
border: 1px solid $theme-color;
}
&.is-disabled &__inner {
background-color: #191a1f;
border: none;
}
}
.el-select {
.el-input.is-focus .el-input__inner {
border: 1px solid $theme-color;
}
.el-input__inner:focus {
border: 1px solid $theme-color;
}
&-dropdown {
background-color: #232429;
border: none;
&__item:hover,
&__item.hover {
background-color: $theme-color;
color: rgb(255, 255, 255, 0.87);
}
}
}
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover,
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected {
background-color: $theme-color;
color: rgb(255, 255, 255, 0.87);
}
.el-textarea {
&__inner {
background-color: #191a1f;
border: 1px solid transparent;
color: rgb(255, 255, 255, 0.87);
}
&__inner:hover,
&__inner:focus {
border: 1px solid $theme-color;
}
}
.el-pagination.is-background .el-pager li:not(.disabled).active {
background-color: $theme-color;
}
.ace-tm {
background-color: #232429 !important;
}
.ace_gutter {
background-color: #232429 !important;
color: rgb(255, 255, 255, 0.87);
}
.ace_gutter-cell {
background-color: #232429 !important;
color: rgb(255, 255, 255, 0.87);
}
.ace_line {
span {
color: rgb(255, 255, 255, 0.87) !important;
}
}
.el-popover {
background-color: #232429;
border: none;
}
.popper__arrow::after {
border-width: 0px !important;
border-color: #232429 !important;
}
.el-popper[x-placement^=bottom] .popper__arrow {
border-color: transparent;
}
.el-popper[x-placement^=right] .popper__arrow {
border-color: transparent;
}
.el-popper[x-placement^=left] .popper__arrow {
border-color: transparent;
}
.el-popper[x-placement^=top] .popper__arrow {
border-color: transparent;
}
.el-radio__input.is-checked .el-radio__inner {
background-color: $theme-color;
border: 1px solid $theme-color;
}
.el-radio__input.is-checked+.el-radio__label {
color: $theme-color;
}
.el-checkbox__input.is-checked+.el-checkbox__label {
color: $theme-color;
}
.el-checkbox__input.is-checked .el-checkbox__inner,
.el-checkbox__input.is-indeterminate .el-checkbox__inner {
background-color: $theme-color;
border: 1px solid $theme-color;
}
.el-notification {
background-color: #232429;
border: none;
&__title {
color: $theme-color;
}
&__content {
p {
color: $theme-color;
}
}
}
.el-divider {
background-color: #434343;
&__text {
background-color: #434343;
color: rgb(255, 255, 255, 0.87);
}
}
.el-picker-panel {
background-color: #232429;
border: none;
&__icon-btn {
color: rgb(255, 255, 255, 0.87);
}
}
.el-date-picker__header-label {
color: rgb(255, 255, 255, 0.87);
}
.el-date-table th {
color: rgb(255, 255, 255, 0.87);
}
.el-date-table td.current:not(.disabled) span {
background-color: $theme-color;
}
.el-dropdown-menu {
background-color: #232429;
border: none;
&__item {
color: rgb(255, 255, 255, 0.87);
}
}
.el-dropdown-menu__item:focus,
.el-dropdown-menu__item:not(.is-disabled):hover {
background-color: $theme-color;
color: rgb(255, 255, 255, 0.87);
}
.el-cascader__dropdown {
background-color: #232429;
border: none;
}
.el-cascader-panel {
background: #232429;
border: none;
}
.el-cascader-node:not(.is-disabled):focus,
.el-cascader-node:not(.is-disabled):hover {
background: $theme-color !important;
}
.el-cascader-node.in-active-path {
background: $theme-color;
}
.el-cascader-menu {
border: none;
}
.my-editor {
width: 100%;
min-height: 50px;
padding: 10px 0;
background: #2d2d2d;
color: #ccc;
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
font-size: 14px;
line-height: 1.5;
}
// optional
.prism-editor__textarea:focus {
outline: none;
}
// not required:
.height-300 {
height: 300px;
}
.el-tree {
background-color: transparent;
&-node__content {
margin: 5px 0;
padding: 10px 0;
}
&-node__content:hover {
border-radius: 6px;
background-color: $theme-color;
}
&-node:focus>&-node__content {
border-radius: 6px;
background: $theme-color;
}
}

View File

@ -0,0 +1,59 @@
.el-container {
height: 100%;
background: url("~@/assets/img/background.jpg") center no-repeat;
.el-main {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
.form-box {
width: 350px;
.el-image {
width: 200px;
margin-bottom: 20px;
}
.el-button {
width: 100%;
}
.el-form {
width: 100%;
padding: 20px 50px;
background-color: #fff;
border-radius: 20px;
.el-form-item {
width: 100%;
::v-deep .el-input__inner {
border-radius: 2px;
font-family: "SRL";
}
}
.el-row {
margin-top: 10px;
.el-image {
width: 30px;
margin-right: 20px;
}
.el-input {
margin-right: 20px;
}
.el-button {
width: 120px;
border-radius: 2px;
}
}
}
}
}
}

157
src/components/FecrBar.vue Normal file
View File

@ -0,0 +1,157 @@
<template>
<div :id="id" style="width: 100%; height: 400px"></div>
</template>
<script>
export default {
props: {
data: [Object, Array],
id: {
type: String,
default: "bar",
},
xAxis: [Object, Array],
},
data() {
return {};
},
methods: {
parseData() {
this.init();
},
parseSeries() {
const color = ["#66c18c", "#1177b0"];
if (Object.keys(this.data).length === 1) {
const key = Object.keys(this.data)[0]
return {
name:key,
type: "bar",
data: Object.keys(this.data[key]).map(i=>{
return this.data[key][i];
}),
barWidth: 30,
itemStyle: {
normal: {
color: function (params) {
return params.data < 0 ? "#f50" : "#5970CA";
},
},
},
};
} else {
return Object.keys(this.data).map((key, index) => {
return {
name: key,
type: "bar",
data: Object.keys(this.data[key]).map((i) => {
return this.data[key][i];
}),
barWidth: 30,
itemStyle: {
color: color[index],
barBorderRadius: [2],
},
};
});
}
},
init() {
var chart = this.$echarts.init(document.getElementById(this.id), "shine");
var option = {
tooltip: {
trigger: "axis",
},
grid: {
top: 50,
left: 50,
right: 50,
bottom: 50,
},
legend: {
// orient: "vertical",
x: "right",
y: "top",
padding: [0, 50, 0, 0],
data: Object.keys(this.data).map((key) => {
return key;
}),
},
title: {
text: "",
textAlign: "left",
x: "center",
y: "top",
},
// x
xAxis: {
data: this.xAxis,
axisLine: {
//
lineStyle: {
color: "#D5D5D5",
},
},
nameGap: 5,
axisTick: {
show: false,
},
axisLabel: {
interval: 0,
rotate: 30,
textStyle: {
color: "#566770",
fontSize: "15px",
},
},
},
yAxis: {
splitLine: {
show: true,
lineStyle: {
color: "#D5D5D5",
},
},
axisTick: {
show: false, //线
},
axisLine: {
lineStyle: {
type: "solid",
color: "#fff",
width: "2",
},
},
axisLabel: {
textStyle: {
color: "#566770",
},
},
},
//
series: this.parseSeries(),
};
// 使
chart.setOption(option);
window.addEventListener("resize", function () {
chart.resize();
});
},
},
watch: {
data: {
handler(val) {
this.data = val;
this.$nextTick(() => {
this.parseData();
});
},
immediate: true,
deep: true,
},
},
};
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,88 @@
<!-- 卡片列表 -->
<template>
<el-container>
<el-main>
<el-row
:gutter="20"
type="flex"
justify="start"
style="flex-wrap: wrap; width: 100%"
>
<el-col :span="span" v-for="(item, index) in data.records" :key="index">
<el-card @click.native="handleClickCard(item)" shadow="hover">
<slot name="card" :data="item" />
</el-card>
</el-col>
</el-row>
</el-main>
<el-footer>
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="current"
:page-sizes="[5, 10, 20]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next"
:total="data.total"
>
</el-pagination>
</el-footer>
</el-container>
</template>
<script>
export default {
props: {
data: [Object],
current: [Number],
pageSize: [Number],
span: {
type: Number,
default: 6,
},
},
data() {
return {};
},
methods: {
handleSizeChange(size) {
this.$emit("handlePageChange", {
page_size: size,
});
},
handleCurrentChange(current) {
this.$emit("handlePageChange", {
page_num: current||this.current,
});
},
handleClickCard(params) {
this.$emit("handleClickCard", params);
},
},
};
</script>
<style lang='scss' scoped>
.el-container {
height: auto;
}
.el-main {
height: 100%;
padding: 0;
}
.el-footer {
@include flex(flex-end);
}
.el-col {
margin-bottom: 20px;
}
.el-card {
cursor: pointer;
background-color: #212121
}
</style>

View File

@ -0,0 +1,70 @@
<!-- 弹窗组件 -->
<template>
<el-dialog
:title="title"
:visible.sync="visible"
:width="width || '500px'"
:before-close="handleClose"
:close-on-click-modal="modalClose"
:destroy-on-close="true"
>
<div slot="title" v-if="!title">
<slot name="dialog-title" />
</div>
<slot name="body" />
<span slot="footer" class="dialog-footer" >
<el-row v-if="!footerSlot">
<el-button @click="cancel" size="mini"> </el-button>
<el-button type="primary" @click="submit" size="mini"> </el-button>
</el-row>
<slot name="dialog-footer"/>
</span>
</el-dialog>
</template>
<script>
export default {
props: {
width: [String],
title: [String],
footerSlot: {
type: Boolean,
default: false,
},
visible: [Boolean],
modalClose: {
type: Boolean,
default: false,
},
},
data() {
return {};
},
methods: {
handleClose() {
this.$emit("cancel", false);
},
cancel() {
this.$emit("cancel", false);
},
submit() {
this.$emit("submit", false);
},
},
};
</script>
<style lang='scss' scoped>
.el-dialog {
::v-deep &__title {
font-weight: 800;
font-size: 16px;
}
::v-deep &__body {
max-height: calc(70vh - 184px) !important;
overflow: auto;
padding: 10px 20px;
}
}
</style>

145
src/components/FecrLine.vue Normal file
View File

@ -0,0 +1,145 @@
<template>
<div id="line" :style="{ width: '100%', height: height }"></div>
</template>
<script>
export default {
props: {
data: [Object],
height: {
type: String,
default: "400px",
},
},
data() {
return {};
},
mounted() {
if (Object.keys(this.data).length !== 0) {
this.initLine();
}
},
methods: {
initLine() {
var chart = this.$echarts.init(document.getElementById("line"), "shine");
var option = {
tooltip: {
trigger: "item",
showContent: true,
formatter: function (params) {
return params.data;
},
},
xAxis: {
type: "category",
boundaryGap: false,
data: this.data["日期"].map((item) => {
return item;
}),
axisLine: {
lineStyle: {
color: "#d9d9d9",
},
},
axisLabel: {
textStyle: {
color: "#B5B5B5",
fontSize: 12,
},
},
axisTick: {
show: false,
},
splitLine: {
show: true,
lineStyle: {
color: "#d9d9d9",
type: "dashed",
},
},
},
yAxis: {
type: "value",
axisLine: {
lineStyle: {
color: "#d9d9d9",
},
},
axisLabel: {
textStyle: {
color: "#B5B5B5",
fontSize: 12,
},
},
axisTick: {
show: false,
},
splitLine: {
show: true,
lineStyle: {
color: "#d9d9d9",
type: "dashed",
},
},
},
series: [
{
data: this.data["次数"].map((item) => {
return item;
}),
type: "line",
smooth: true,
areaStyle: {
normal: {
color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#1890ff" },
{ offset: 1, color: "#bae7ff" },
]),
},
},
lineStyle: {
normal: {
color: "#1890ff",
},
},
itemStyle: {
borderColor: "#1890ff",
},
},
],
};
chart.setOption(option);
window.addEventListener("resize", function () {
chart.resize();
});
},
},
watch: {
data: {
handler(newVal, oldVal) {
if (newVal !== oldVal) {
this.data = newVal;
this.initLine();
}
},
// immediate: true,
deep: true,
},
height: {
handler(newVal, oldVal) {
if (newVal !== oldVal) {
this.height = newVal;
}
},
},
},
};
</script>
<style lang="scss" scoped>
canvas {
display: flex;
justify-content: center;
}
</style>

View File

@ -0,0 +1,146 @@
<template>
<div
id="line_new"
style="width: 100%; height: 400px; min-width: 300px; min-height: 300px"
></div>
</template>
<script>
export default {
props: {
data: [Object],
},
data() {
return {
color: [
{
head: "rgb(255, 0, 135)",
foot: "rgb(135, 0, 157)",
},
{
head: "rgb(255, 191, 0)",
foot: "rgb(224, 62, 76)",
},
{
head: "rgb(55, 162, 255)",
foot: "rgb(116, 21, 219)",
},
],
};
},
mounted() {
this.initLine();
},
methods: {
initLine() {
var chart = this.$echarts.init(
document.getElementById("line_new"),
"shine"
);
var option = {
tooltip: {
trigger: "axis",
axisPointer: {
type: "cross",
label: {
backgroundColor: "#6a7985",
},
},
},
legend: {
data: Object.keys(this.data).map((key) => {
return key;
}),
},
toolbox: {
feature: {
saveAsImage: {},
},
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
xAxis: [
{
type: "category",
boundaryGap: false,
data: Object.keys(this.data).map((key) => {
return Object.keys(this.data[key]).map((item) => {
return item;
});
})[0],
},
],
yAxis: [
{
type: "value",
axisTick:{
show:false
},
axisLine:{
show:false
}
},
],
series: Object.keys(this.data).map((key, index) => {
return {
name: key,
type: "line",
stack: "Total",
smooth: true,
lineStyle: {
width: 0,
},
showSymbol: false,
areaStyle: {
opacity: 0.8,
color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: this.color[index].head,
},
{
offset: 1,
color: this.color[index].foot,
},
]),
},
emphasis: {
focus: "series",
},
data: Object.keys(this.data[key]).map((item) => {
return this.data[key][item]? this.data[key][item] : 0;
})
};
}),
};
// 使
chart.setOption(option);
window.addEventListener("resize", function () {
chart.resize();
});
},
},
watch: {
data: {
handler(val) {
this.data = val
this.initLine()
},
// immediate: true,
deep: true,
},
},
};
</script>
<style lang="scss" scoped>
canvas {
display: flex;
justify-content: center;
}
</style>

139
src/components/FecrMap.vue Normal file
View File

@ -0,0 +1,139 @@
<template>
<div id="map" :style="{ width: '100%', height: height }"></div>
</template>
<script>
import { get } from "api/api";
import china from "echarts/map/json/china";
export default {
props: {
height: {
type: String,
default: "400px",
},
// data: [Object, Array],
},
data() {
return {
data: [],
};
},
created() {
this.getRatingProvince();
},
methods: {
getRatingProvince() {
get("/admin/board/map/country").then((res) => {
this.data = res.result.map((item) => {
return {
name: this.parseProvinceName(item["省份"]),
value: item["数量"],
};
});
this.initMap();
});
},
parseProvinceName(params) {
const target = [
"省",
"自治区",
"市",
"壮族",
"维吾尔",
"回族",
"特别行政区",
];
for (let i in target) {
if (params.includes(target[i])) {
const reg = new RegExp(target[i], "g");
params = params.replace(reg, "");
return this.parseProvinceName(params);
}
}
return params;
},
initMap() {
const that = this;
this.$echarts.registerMap("china", china);
var chart = this.$echarts.init(document.getElementById("map"), "shine");
var option = {
visualMap: {
min: 0,
max: 10,
text: ["100", "0"],
realtime: false,
calculable: false,
inRange: {
color: ["#e6f7ff", "#1890FF"],
},
},
series: [
{
type: "map",
mapType: "china",
selectedMode: "single",
itemStyle: {
normal: {
label: {
show: true,
textStyle: {
color: "#6A7BAB",
fontFamily: "黑体",
fontSize: 12,
fontWeight: "bold",
},
},
borderWidth: 0.5,
borderColor: "#fff",
areaColor: "#E6E8EA",
},
emphasis: {
label: {
show: true,
textStyle: {
color: "#6A7BAB",
fontFamily: "黑体",
fontSize: 12,
fontWeight: "bold",
},
},
borderColor: "#fff",
areaColor: "#C9D4EB",
},
},
data: this.data,
},
],
tooltip: {
show: true,
trigger: "item",
triggerOn: "click",
enterable: true,
axisPointer: {
type: "line",
},
showContent: true,
alwaysShowContent: false,
showDelay: 0,
hideDelay: 100,
textStyle: {
fontSize: 12,
},
padding: 5,
formatter: function (params) {
that.$emit("getProvince",params.data.name)
},
},
};
chart.setOption(option);
window.addEventListener("resize", function () {
chart.resize();
});
},
},
};
</script>
<style lang='scss' scoped>
</style>

115
src/components/FecrPie.vue Normal file
View File

@ -0,0 +1,115 @@
<template>
<div :id="id" :style="{ width: '100%', height: height }"></div>
</template>
<script>
export default {
props: {
data: [Object, Array],
id: {
type: String,
default: "pie",
},
height: {
type: String,
default: "400px",
},
},
data() {
return {
nameArray: [],
seriesData: [],
};
},
mounted() {
if (this.data) {
this.$nextTick(() => {
this.parseData();
});
}
},
methods: {
parseData() {
this.nameArray = this.data.map((item) => {
return Object.keys(item)[0];
});
this.seriesData = this.data.map((item) => {
return {
name: Object.keys(item)[0],
value: item[Object.keys(item)[0]],
};
});
this.init();
},
init() {
const that = this;
var chart = this.$echarts.init(document.getElementById(this.id), "shine");
var option = {
tooltip: {
trigger: "item",
triggerOn: "click",
showContent: true,
formatter: function (params) {
that.$emit("getLevel", params.data.name);
},
},
backgroundColor: "#fff",
grid: {
top: 0,
left: 0,
right: 0,
bottom: 0,
},
color: [
"#5470c6",
"#91cc75",
"#fac858",
"#ee6666",
"#73c0de",
"#3ba272",
"#fc8452",
"#9a60b4",
"#ea7ccc",
],
series: [
{
name: "访问来源",
type: "pie",
radius: ["40%", "80%"],
data: this.seriesData,
itemStyle: {
borderRadius: 50,
borderColor: "#fff",
borderWidth: 10,
},
},
],
};
chart.setOption(option);
window.addEventListener("resize", function () {
chart.resize();
});
},
},
watch: {
data: {
handler(val) {
this.data = val;
this.$nextTick(() => {
this.parseData();
});
},
// immediate: true,
deep: true,
},
height: {
handler(newVal) {
this.height = newVal;
},
},
},
};
</script>
<style>
</style>

View File

@ -0,0 +1,135 @@
<template>
<div id="radar" style="width: 100%; height: 400px"></div>
</template>
<script>
export default {
props: {
data: [Object],
},
data() {
return {};
},
methods: {
init() {
const that = this;
var chart = this.$echarts.init(document.getElementById("radar"), "shine");
var option = {
title: {
text: "",
textAlign: "left",
x: "center",
y: "top",
},
grid: {
top: 0,
left: 0,
right: 0,
bottom: 0,
},
radar: {
splitNumber: 4,
name: {
textStyle: {
color: "#000",
fontFamily: "SRL",
padding: [0, 0],
},
},
splitLine: {
show: true,
lineStyle: {
width: 1,
borderRadius: 5,
color: "#DBE3EB", //
},
},
axisLine: {
show: false,
},
splitArea: {
show: false,
areaStyle: {
color: "#fff", //
},
},
indicator: Object.keys(that.data["指标雷达"]["最大分数"]).map(
(key) => {
return {
name: key,
max: that.data["指标雷达"]["最大分数"][key],
};
}
),
},
series: [
{
type: "radar",
areaStyle: {
color: "rgba(74,138,240,0.3)",
},
lineStyle: {
normal: {
color: "#5380E6",
shadowColor: "#5380E6",
shadowBlur: 2,
},
},
itemStyle: {
normal: {
borderColor: "#5380E6",
},
},
data: [
{
value: Object.keys(that.data["指标雷达"]["指标得分"]).map(
(key) => {
return that.data["指标雷达"]["指标得分"][key];
}
),
label: {
normal: {
show: false,
padding: [6, 10],
borderRadius: [5, 5, 5, 5],
color: "#5380E6",
// backgroundColor: "#5380E6",
},
},
},
],
},
],
};
// 使
chart.setOption(option);
window.addEventListener("resize", function () {
chart.resize();
});
},
},
watch: {
data: {
handler(val) {
this.$nextTick(() => {
if (val !== undefined) {
this.data = val;
this.init();
}
});
},
immediate: true,
deep: true,
},
},
};
</script>
<style scoped lang='scss'>
canvas {
width: 100% !important;
height: 100% !important;
}
</style>

View File

@ -0,0 +1,85 @@
<!-- 打分 -->
<template>
<el-row type="flex" align="middle" style="margin: 0">
<span>x</span>
<el-popover trigger="manual" v-model="visible['0']">
<el-row
type="flex"
justify="space-around"
v-for="(v, k) in data"
:key="k"
>
<span @click="setSymbol({ 0: v })">{{ v }}</span>
</el-row>
<i
class="el-icon-question"
slot="reference"
style="margin: 0 10px"
@click="setVisible({ 0: true })"
v-if="!symbol['0']"
/>
</el-popover>
<span v-if="symbol['0']">{{ symbol["0"] }}</span>
<el-input size="small" type="number" v-model="value['0']"></el-input>
<i
class="el-icon-circle-plus"
@click="andVisible = true"
v-if="!andVisible"
/>
<span v-if="andVisible">
<span>and x</span>
<el-popover trigger="manual" v-model="visible['1']">
<el-row
type="flex"
justify="space-around"
v-for="(v, k) in data"
:key="k"
>
<span @click="setSymbol({ 1: v })">{{ v }}</span>
</el-row>
<i
class="el-icon-question"
slot="reference"
style="margin: 0 10px"
@click="setVisible({ 1: true })"
v-if="!symbol['1']"
/>
</el-popover>
<span v-if="symbol['1']">{{ symbol["1"] }}</span>
<el-input size="small" type="number" v-model="value['1']"></el-input>
</span>
</el-row>
</template>
<script>
export default {
props: {
visible: [Object],
symbol: [Object],
value: [Object],
andVisible: [Boolean],
},
data() {
return {
data: [">", ">=", "=", "!=", "<=", "<"],
};
},
methods: {
setSymbol(val) {
Object.assign(this.visible, { 0: false, 1: false });
Object.assign(this.symbol, val);
},
setVisible(val) {
Object.assign(this.visible, val);
},
},
};
</script>
<style lang='scss' scoped>
.el-input {
width: 100px;
margin: 0 10px;
}
</style>

View File

@ -0,0 +1,196 @@
<template>
<div style="width: 100%; height: 100%">
<el-table
border
ref="table"
:data="data"
:height="height"
:row-key="rowKey"
:span-method="spanMethod"
@cell-click="cellClick"
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
width="55"
v-if="selection"
align="center"
:reserve-selection="true"
/>
<el-table-column
v-for="(item, index) in column"
:key="index"
:prop="item.prop"
:label="item.label"
align="center"
:width="item.label === '操作' ? 200 : 'auto'"
>
<template slot-scope="scope">
<span
v-if="
typeof scope.row[scope.column.property] == 'string' ||
typeof scope.row[scope.column.property] == 'number'
"
>{{ scope.row[scope.column.property] }}</span
>
<el-row v-if="Array.isArray(scope.row[scope.column.property])">
<el-tag v-for="item in scope.row[scope.column.property]" :key="item" style="display:block;margin:10px 0" type="success" effect="dark">{{item}}</el-tag>
</el-row>
<slot
name="operation"
:value="scope.row"
v-if="item.prop === 'operation'"
></slot>
</template>
</el-table-column>
</el-table>
<el-pagination
style="float: right; margin-top: 20px"
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:page-sizes="[5, 10, 15]"
:page-size="page.page_size"
layout="total, sizes, prev, pager, next"
:total="total"
v-if="page"
>
</el-pagination>
</div>
</template>
<script>
export default {
props: {
//
data: [Array],
height: {
type: String,
default: "calc(100% - 52px)",
},
//
column: [Array],
//
sort: {
type: Boolean,
default: false,
},
//
selection: {
type: Boolean,
default: false,
},
page: {
type: Object,
default: null,
},
total: [Number],
selectKey: {
type: String,
default: "tag_id",
},
selectValue: [Array],
spanMethod:[Function]
},
methods: {
rowKey(row) {
return row[this.selectKey];
},
handleSelectionChange(val) {
this.$emit("handleSelectChange", val);
},
handleSizeChange(val) {
this.$emit("handlePageChange", {
page_size: val,
page_num: 1,
});
},
handleCurrentChange(val) {
this.$emit("handlePageChange", {
page_num: val,
});
},
cellClick(row) {
this.$emit("cellClick", row);
},
},
watch: {
data: {
handler(newVal) {
this.data = newVal;
if (this.selection) {
this.$nextTick(() => {
this.$refs.table.clearSelection();
this.data.forEach((row) => {
this.selectValue.forEach((item) => {
if (row[this.selectKey] === item[this.selectKey]) {
this.$refs.table.toggleRowSelection(row, true);
}
});
});
});
}
},
immediate: true,
deep: true,
},
selectValue: {
handler(newVal) {
if (this.selection) {
this.selectValue = newVal;
this.$refs.table.clearSelection();
this.data.forEach((row) => {
this.selectValue.forEach((item) => {
if (row[this.selectKey] === item[this.selectKey]) {
this.$refs.table.toggleRowSelection(row, true);
}
});
});
}
},
},
},
};
</script>
<style lang="scss" scoped>
::v-deep .el-table,
::v-deep .el-table__expanded-cell {
background-color: transparent !important;
border-radius: 6px;
cursor: pointer;
}
::v-deep .el-table th {
background-color: $theme-color;
}
/* 表格内背景颜色 */
::v-deep .el-table tr,
::v-deep .el-table td {
background-color: transparent !important;
border: 1px solid rgb(67, 67, 67, 0.5) !important;
}
/*去除底边框*/
::v-deep.el-table td.el-table__cell {
border: 1px solid rgb(67, 67, 67, 0.5) !important;
}
::v-deep.el-table th.el-table__cell.is-leaf {
border: 1px solid rgb(67, 67, 67, 0.5) !important;
}
.el-table::before,
.el-table::after {
background-color: transparent;
}
.el-table--border,
.el-table--group {
border: 1px solid rgb(67, 67, 67, 0.5) !important;
}
</style>

View File

@ -0,0 +1,120 @@
<!-- 标签管理 -->
<template>
<el-popover
placement="left-end"
width="400"
trigger="manual"
v-model="tagVisible"
>
<el-row
type="flex"
justify="space-between"
style="margin-bottom: 20px"
align="middle"
>
<el-input
placeholder="标签名"
style="width: 200px"
v-model="tagForm.tag_name"
size="small"
>
</el-input>
<el-button
type="primary"
@click="searchTag"
icon="el-icon-search"
size="small"
></el-button>
<el-button type="primary" size="small" @click="submit">确定</el-button>
</el-row>
<fecr-table
:selectKey="'tag_id'"
:selection="true"
:page="tagPage"
:data="tagData"
:selectValue="selectValue"
:column="tagData.setColumn()"
@handlePageChange="tagPageChange"
@handleSelectChange="handleSelectChange"
></fecr-table>
<el-button
type="primary"
size="small"
circle
icon="el-icon-search"
slot="reference"
@click="searchTag"
></el-button>
</el-popover>
</template>
<script>
import request from "@/api/request";
import FecrTable from "@/components/FecrTable";
export default {
props: {
selectValue:[Array],
tagCategory: [String],
},
components: { FecrTable },
data() {
return {
tagVisible: false,
tagPage: {
total: 0,
},
tagForm: {
tag_name: "",
tag_category: "",
page_no: 1,
page_size: 10,
},
tagData: [],
selectData: [],
};
},
methods: {
searchTag() {
this.tagVisible = true
request
.get(
"/rating/indicator/tags/search_tags",
Object.assign(this.tagForm, { tag_category: this.tagCategory })
)
.then((res) => {
this.tagData = res.result.records;
Object.assign(this.tagPage, this.tagForm, {
total: res.result.total,
});
});
},
tagPageChange(obj) {
Object.assign(this.tagForm, obj);
this.searchTag();
},
handleSelectChange(val) {
this.selectData = val;
},
submit() {
this.tagVisible = false
this.$emit("handleSelectData", this.selectData);
},
},
watch:{
selectValue:{
handler(newVal){
this.selectValue = newVal
},
immediate:true,
deep:true,
}
}
};
</script>
<style lang='scss' scoped>
</style>

View File

@ -0,0 +1,124 @@
<!-- 标签查询 -->
<template>
<el-collapse v-model="activeKey">
<el-collapse-item name="1">
<div slot="title">筛选条件</div>
<el-row
type="flex"
align="middle"
class="conditions"
v-for="(item, index) in category"
:key="index"
>
<p>{{ item.name }}</p>
<span
v-for="(v, k) in item.data"
:key="index + k"
@click="fillSearch(item.key, v)"
:class="
item.key !== 'sort'
? item.key === 'tags'
? data.tags && data.tags.includes(v)
? 'checked'
: 'normal'
: data[item.key] === allConversion(v)
? 'checked'
: 'normal'
: data.sort === conversion(v)
? 'checked'
: 'normal'
"
>{{ item.key === "tags" ? v.tag_name : v }}</span
>
</el-row>
</el-collapse-item>
</el-collapse>
</template>
<script>
import { remove } from "@/utils/utils";
export default {
props: {
category: [Object, Array],
data: [Object, Array],
},
data() {
return {
activeKey: "0",
};
},
methods: {
// sort: => asc, => desc
conversion(val) {
return val === "正序" ? "asc" : "desc";
},
allConversion(val) {
return val === "全部" ? "" : val;
},
fillSearch(label, data) {
if (label === "sort") {
this.data.sort = data === "正序" ? "asc" : "desc";
} else if (label === "tags") {
this.data.tags.every((item) => {
return item !== data;
})
? this.data.tags.push(data)
: (this.data.tags = remove(this.data.tags, data, "tag_id"));
} else {
data === "全部" ? (this.data[label] = "") : (this.data[label] = data);
}
this.$emit("handleChangeTag");
},
},
};
</script>
<style lang='scss' scoped>
.el-collapse {
border: 1px dashed transparent;
margin-bottom: 20px;
}
::v-deep .el-collapse-item__header {
padding: 0 20px;
height: 40px;
line-height: 40px;
border-bottom: 1px dashed transparent;
}
::v-deep .el-collapse-item__wrap{
border-bottom: 0px;
}
::v-deep .el-collapse-item__content {
padding: 0 20px;
border: none;
.conditions {
padding: 10px 0;
p {
width: 50px;
}
}
}
span {
margin-right: 15px;
cursor: pointer;
}
span:hover {
color: $theme-color;
}
.normal {
color: #333;
}
.checked {
color: $theme-color;
}
</style>

View File

@ -0,0 +1,147 @@
<!-- 转置表格 -->
<template>
<div style="width: 100%">
<el-table
border
:data="transData"
:stripe="stripe"
:header-cell-style="headClass"
:cell-style="cellClass"
@cell-click="cellClick"
>
<el-table-column
v-for="(item, index) in transTitle"
:label="item"
:key="index"
align="center"
:index="index"
>
<template slot-scope="scope">
<span>{{ scope.row[index] }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
props: {
//
data: [Array],
//
column: [Array, Object],
stripe: {
type: Boolean,
default: true,
},
},
data() {
return {
transData: [], //
transTitle: [],
};
},
mounted() {},
methods: {
init() {
var matrixData = this.data.map((row) => {
let temp;
return this.column.map((key) => {
if (Object.prototype.hasOwnProperty.call(row, key)) {
if (row[key] instanceof Object) {
temp = key;
return null;
} else {
return row[key];
}
} else {
return row[temp][key];
}
});
});
this.transData = matrixData[0].map((_, i) => {
return [
this.column[i],
...matrixData.map((row) => {
return row[i];
}),
];
});
this.transTitle = this.transData.filter((item) => {
return item[0] === "报告期" ? item : false;
})[0];
if (this.transData[this.transData.length - 1][0] === "报告期") {
this.transData.pop();
}
},
headClass({ columnIndex }) {
if (columnIndex === 0) {
return "font-family:SR;font-size:12px;textAlign:left";
} else {
return "font-family:SR;font-size:12px;textAlign:right";
}
},
cellClass({ row, columnIndex }) {
const data = Object.keys(this.data[0]).flat();
const flag = Object.keys(this.data[0]).some((key) => {
return this.data[0][key] instanceof Object;
});
if (columnIndex === 0) {
if (flag) {
if (data.includes(row[0])) {
return { color: "rgb(255, 153, 51)", textAlign: "left" };
} else {
return { textAlign: "left", paddingLeft: "20px" };
}
} else {
return { textAlign: "left" };
}
} else {
return { textAlign: "right" };
}
},
cellClick(row, column) {
this.$emit("cellClick", {
label: row[0],
value: row[column.index],
column: column.label,
index: column.index
});
},
},
watch: {
data: {
handler(val) {
if (val.length !== 0) {
this.data = val;
this.init();
}
},
immediate: true,
deep: true,
},
column: {
handler(val) {
if (val.length !== 0) {
this.column = val;
this.init();
}
},
// immediate: true,
deep: true,
},
},
};
</script>
<style lang="scss" scoped>
</style>

36
src/main.js Normal file
View File

@ -0,0 +1,36 @@
import Vue from 'vue'
import App from './App.vue'
import router from 'router/index.js'
import store from 'store/index.js'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import echarts from 'echarts'
import "assets/style/public.scss"
import { message } from 'utils/message.js'
Vue.use(ElementUI)
Vue.prototype.$message = message;
Vue.prototype.$echarts = echarts
Vue.config.productionTip = false
Array.prototype.setColumn = function(){
if (this.length) {
return Object.keys(this[0]).map((item) => {
return {
prop: item,
label: item,
};
});
} else {
return [];
}
}
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app')

48
src/router/index.js Normal file
View File

@ -0,0 +1,48 @@
import Vue from 'vue'
import store from "store/index"
import Router from 'vue-router'
import {
Message
} from 'element-ui'
Vue.use(Router)
const router = new Router({
routes: [{
path: '/',
redirect: '/home'
}, {
path: '/home',
name: '首页',
meta: {
requireAuth: false
},
component: () => import("view/home/index")
}, {
path: '/login',
name: 'login',
component: () => import("view/user/login")
}],
mode: 'history',
})
router.beforeEach((to, from, next) => {
if (to.matched.some(r => r.meta.requireAuth)) {
if (store.state.token) {
next();
} else {
Message({
message: "请登录",
type: "warning",
});
next({
path: '/login',
// query: { redirect: to.path }
})
}
} else {
next();
}
})
export default router

74
src/store/index.js Normal file
View File

@ -0,0 +1,74 @@
import Vue from 'vue'
import Vuex from 'vuex'
import { remove } from "utils/utils"
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
token: localStorage.getItem("token") || "",
user: {
name: localStorage.getItem("user_name") || "",
email: "",
},
tabsKey: localStorage.getItem("tabsKey") || '1',
tabs: JSON.parse(localStorage.getItem('tabs')) || [],
},
getters: {
getTabs(state) {
return state.tabs
},
getTabsKey(state) {
return state.tabsKey
}
},
mutations: {
set_token(state, token) {
localStorage.setItem("token", token)
state.token = localStorage.getItem("token")
},
del_token(state) {
state.token = ""
localStorage.removeItem("token")
},
set_user_name(state, user_name) {
localStorage.setItem("user_name", user_name)
state.user = {
name: user_name,
}
state.user = Object.assign(state.user, { name: user_name })
},
set_tabs(state, tabs) {
if (state.tabs.every(item => {
return item.id !== tabs.id
})) {
state.tabs.push(tabs)
}
localStorage.setItem('tabs', JSON.stringify(state.tabs))
},
delete_tabs_item(state, tabs) {
state.tabs = remove(state.tabs, tabs)
localStorage.setItem('tabs', JSON.stringify(state.tabs))
},
delete_tabs_all(state){
state.tabs = []
state.tabsKey = "1"
localStorage.setItem("tabsKey", state.tabsKey)
localStorage.setItem("tabs",JSON.stringify(state.tabs))
},
set_tabs_key(state, key) {
localStorage.setItem("tabsKey", key)
state.tabsKey = key
}
}
})
export default store

1
src/utils/area.json Normal file

File diff suppressed because one or more lines are too long

1
src/utils/city.json Normal file

File diff suppressed because one or more lines are too long

22
src/utils/message.js Normal file
View File

@ -0,0 +1,22 @@
import { Message } from "element-ui";
let messageInstance = null;
let mainMessage = function DoneMessage(options) {
//如果弹窗已存在先关闭
if (messageInstance) {
messageInstance.close();
}
messageInstance = Message(options);
}
let arr = ['success', 'warning', 'info', 'error'];
arr.forEach(function (type) {
mainMessage[type] = function (options) {
if (typeof options === 'string') {
options = {
message: options
};
}
options.type = type;
return mainMessage(options);
};
});
export const message = mainMessage;

1
src/utils/province.json Normal file
View File

@ -0,0 +1 @@
[{"id":1,"name":"北京"},{"id":2,"name":"上海"},{"id":3,"name":"天津"},{"id":4,"name":"重庆"},{"id":5,"name":"河北"},{"id":6,"name":"山西"},{"id":7,"name":"河南"},{"id":8,"name":"辽宁"},{"id":9,"name":"吉林"},{"id":10,"name":"黑龙江"},{"id":11,"name":"内蒙古"},{"id":12,"name":"江苏"},{"id":13,"name":"山东"},{"id":14,"name":"安徽"},{"id":15,"name":"浙江"},{"id":16,"name":"福建"},{"id":17,"name":"湖北"},{"id":18,"name":"湖南"},{"id":19,"name":"广东"},{"id":20,"name":"广西"},{"id":21,"name":"江西"},{"id":22,"name":"四川"},{"id":23,"name":"海南"},{"id":24,"name":"贵州"},{"id":25,"name":"云南"},{"id":26,"name":"西藏"},{"id":27,"name":"陕西"},{"id":28,"name":"甘肃"},{"id":29,"name":"青海"},{"id":30,"name":"宁夏"},{"id":31,"name":"新疆"},{"id":52993,"name":"港澳"},{"id":32,"name":"台湾"},{"id":84,"name":"钓鱼岛"}]

7
src/utils/rsaKey.js Normal file
View File

@ -0,0 +1,7 @@
import JSEncrypt from "jsencrypt";
export function rsaKey(args) {
var _0x2beb=['pz4YAB/sIhWUdiVoB1866/HnYHbf/+5sVx1Nvh8Vp85sgOZchIdmS/wIDAQAB','7TkN+Dqc4nHgnnSq7y4AjPGd3C7qLej1mXBvh2wpjNlpMIlIfhBIsOHW3+H/VmuCpW','setPublicKey'];var _0x4fa0=function(_0x2bebb3){_0x2bebb3=_0x2bebb3-0x0;var _0x5bf311=_0x2beb[_0x2bebb3];return _0x5bf311;};var encryptor=new JSEncrypt();var a='MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLoijMi4Ng';var b=_0x4fa0('0x1');var c='BtWk73P7VhkAdqiMZOC9OWBEwVuzNOPid+/YjoGup';var d=_0x4fa0('0x0');encryptor['noproblem']=encryptor[_0x4fa0('0x2')];encryptor[_0x4fa0('0x2')](add(a,b,c,d));var rsa=encryptor['encrypt'](args);return rsa;
}
function add(_0x510da3,_0x2ac1ee,_0x101279,_0x5aae7d){return _0x510da3+_0x2ac1ee+_0x101279+_0x5aae7d;}

25
src/utils/utils.js Normal file
View File

@ -0,0 +1,25 @@
export function download(res, type, filename) {
const blob = new Blob([res], {
type: type
})
const a = document.createElement('a')
const URL = window.URL || window.webkitURL
const href = URL.createObjectURL(blob)
a.href = href
a.download = filename
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
window.URL.revokeObjectURL(href)
}
export function remove(params, deleteItem,index) {
const data = []
const key = index || "id"
for (let i = 0; i < params.length; i++) {
if (params[i][key] !== deleteItem[key]) {
data.push(params[i])
}
}
return data
}

View File

@ -0,0 +1,120 @@
<!-- 消息 -->
<template>
<el-dialog
:visible.sync="visible"
width="500px"
title="通知"
:before-close="handleClose"
>
<el-row type="flex" justify="space-between" align="middle">
<el-row type="flex">
<p
v-for="(item, index) in bell"
:key="index"
:class="{
'active-bell': activeKey === index,
bell: activeKey !== index,
}"
@click="handleChangeActive(index)"
>
<span
>{{ item.title }}<span class="bell-num">{{ item.num }}</span></span
>
</p>
</el-row>
<el-dropdown trigger="click">
<i class="el-icon-more drop"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>标记所有消息已读</el-dropdown-item>
<el-dropdown-item>删除所有已读消息</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-row>
<el-divider></el-divider>
</el-dialog>
</template>
<script>
export default {
props: {
visible: [Boolean],
},
data() {
return {
bell: [
{ title: "未读", num: "99+" },
{ title: "已读", num: "20" },
{ title: "@我的", num: "0" },
{ title: "紧急", num: "1" },
],
activeKey: 0,
};
},
methods: {
handleClose() {
this.$emit("cancel", false);
},
handleChangeActive(index) {
this.activeKey = index;
},
},
};
</script>
<style lang='scss' scoped>
::v-deep .el-dialog__title {
font-weight: 800;
font-size: 14px;
}
::v-deep .el-dialog__body {
padding: 20px;
}
.el-input {
width: 400px;
}
::v-deep .el-input__inner {
border-radius: 25px;
padding-left: 40px;
}
::v-deep .el-input__icon {
margin-left: 10px;
}
.bell {
background-color: #fff;
padding: 5px 10px;
border-radius: 6px;
cursor: pointer;
margin-right: 20px;
&-num {
font-size: 12px;
margin-left: 2px;
}
}
.bell:hover{
background-color: #ecf5ff;
color:#66b1ff;
padding: 5px 10px;
}
.active-bell {
background-color: $theme-color;
color: #fff;
padding: 5px 10px;
border-radius: 6px;
cursor: pointer;
margin-right: 20px;
}
.drop{
width:30px;
text-align: right;
}
</style>

View File

@ -0,0 +1,206 @@
<!-- 首页Aside -->
<template>
<el-row
type="flex"
style="flex-wrap: wrap; height: 100%; align-items: space-between"
>
<el-image class="logo" :src="logo" />
<el-menu
:default-active="activeKey"
class="el-menu-vertical-demo"
active-text-color="#409EFF"
@select="handleSelect"
>
<el-menu-item :index="item.id" v-for="item in menu" :key="item.id">
<i :class="item.icon" style="font-size: 18px" />
<span>{{ item.title }}</span>
</el-menu-item>
</el-menu>
<el-row class="footer">
<el-col
><el-tooltip class="item" effect="dark" content="搜索" placement="right"
><i
class="el-icon-search"
@click="searchVisible = true" /></el-tooltip
></el-col>
<el-col
><el-tooltip class="item" effect="dark" content="消息" placement="right"
><el-badge is-dot class="item"
><i
class="el-icon-bell"
@click="bellVisible = true" /></el-badge></el-tooltip
></el-col>
<el-col>
<el-popover trigger="click">
<p class="menu-t">个人</p>
<el-row type="flex" align="bottom" class="menu-p"
><el-avatar
size="small"
:src="avatar"
style="margin-right: 15px"
/></el-row
>
<el-divider></el-divider>
<p class="menu-p">账号设置</p>
<p class="menu-p">主题设置</p>
<p class="menu-p">退出登录</p>
<i class="el-icon-user" slot="reference" />
</el-popover>
</el-col>
</el-row>
<search :visible.sync="searchVisible" @cancel="cancel" />
<bell :visible.sync="bellVisible" @cancel="cancel" />
</el-row>
</template>
<script>
import avatar from "assets/img/avatar.jpg";
import logo from "assets/img/small_logo.jpg";
import search from "./search";
import bell from "./bell";
export default {
components: { search, bell },
props: {
activeKey: {
type: String,
default: "1",
},
},
data() {
return {
logo,
avatar,
searchVisible: false,
bellVisible: false,
menu: [
{
id: "1",
title: "模型",
icon: "el-icon-data-line",
content:"model"
},
{
id: "2",
title: "指标",
icon: "el-icon-connection",
content:"indicators"
},
{
id: "3",
title: "评级",
icon: "el-icon-document",
content:"rating"
},
{
id: "4",
title: "系统",
icon: "el-icon-copy-document",
content:"setting"
},
],
};
},
methods: {
handleSelect(key, keyPath) {
this.$store.commit("set_tabs_key", key);
this.$store.commit(
"set_tabs",
this.menu[Number(keyPath[0]) - 1]
);
},
cancel() {
this.searchVisible = false;
this.bellVisible = false;
},
},
};
</script>
<style lang='scss' scoped>
@import "assets/style/home/index.scss";
.logo {
width: 40px;
height: 40px;
margin: 20px 30px 20px 30px;
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 100px;
min-height: 400px;
}
.el-menu-item {
background-color: transparent;
margin-bottom: 20px;
line-height: 36px;
i {
width: 100%;
display: block;
text-align: center;
}
span {
width: 100%;
display: block;
text-align: center;
}
}
.el-menu-item.is-active {
background-color: transparent !important;
i,
span {
color: #8276c9 !important;
}
}
.footer {
width: 150px;
height: 120px;
color: #fff;
.el-col {
height: 40px;
line-height: 40px;
text-align: center;
i {
width: 24px;
font-size: 14px;
text-align: center;
cursor: pointer;
color: #fff;
}
}
}
::v-deep .el-badge__content {
top: 15px;
left: 5px;
}
::v-deep .el-badge__content.is-dot {
width: 6px;
height: 6px;
}
.menu-t {
margin: 10px;
color: #bfbfbf;
}
.menu-p {
margin: 20px;
color: #8c8c8c;
cursor: pointer;
}
.menu-p:hover,
.menu-p:active,
.menu-p:focus {
color: $theme-color;
}
</style>

View File

@ -0,0 +1,62 @@
<!-- 搜索 -->
<template>
<el-dialog :visible.sync="visible" width="500px" :show-close="false">
<el-row type="flex" justify="space-between" align="middle">
<el-input v-model="value" placeholder="搜索">
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
<i class="el-icon-close" @click="cancel"></i>
</el-row>
<el-divider></el-divider>
<el-row type="flex" justify="space-between" align="middle">
<span>高级搜素</span>
<i class="el-icon-arrow-right"></i>
</el-row>
</el-dialog>
</template>
<script>
export default {
props: {
visible: [Boolean],
},
data() {
return {
value: "",
};
},
methods: {
cancel() {
this.$emit("cancel", false);
},
},
};
</script>
<style lang='scss' scoped>
::v-deep .el-dialog__header {
padding: 0;
}
::v-deep .el-dialog__body {
padding: 20px;
}
.el-input {
width: 400px;
}
::v-deep .el-input__inner {
border-radius: 25px;
padding-left: 40px;
}
::v-deep .el-input__icon {
margin-left: 10px;
}
span {
margin-left: 20px;
cursor: pointer;
}
</style>

View File

@ -0,0 +1,97 @@
<!-- 指标构建 -->
<template>
<el-card>
<el-row type="flex" justify="space-between" slot="header" align="middle">
<h2>{{ status ? "定量" : "定性" }}指标构建</h2>
<el-row type="flex">
<el-upload v-if="!status">
<el-button type="primary">上传Excel模板</el-button>
</el-upload>
<el-button type="primary" style="margin-left:20px">创建</el-button>
</el-row>
</el-row>
<el-form :model="form">
<el-form-item label="指标名称">
<el-input v-model="form['指标名称']"></el-input>
</el-form-item>
<el-form-item label="指标备注">
<el-input v-model="form['指标备注']"></el-input>
</el-form-item>
<el-form-item label="指标描述">
<el-input type="textarea" v-model="form['指标描述']"></el-input>
</el-form-item>
<el-form-item v-if="status" label="指标参数" style="position: relative">
<i
class="el-icon-plus params"
@click="form['指标参数'].push({ 参数: '', 备注: '' })"
></i>
<el-row
type="flex"
justify="space-between"
style="width: 100%"
v-for="(item, index) in form['指标参数']"
:key="index"
>
<el-col :span="11"
><el-input placeholder="参数名称" v-model="item['名称']"></el-input
></el-col>
<el-col :span="11"
><el-input placeholder="参数备注" v-model="item['备注']"></el-input
></el-col>
</el-row>
</el-form-item>
<el-form-item label="指标分类">
<el-select v-model="form['指标分类']"></el-select>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
export default {
data() {
return {
status: 0,
form: {
指标名称: "",
指标备注: "",
指标分类: "",
指标描述: "",
},
};
},
created() {
this.init();
},
methods: {
init() {
this.status =
this.$store.state.tabs.find((item) => {
return this.$store.state.tabsKey === item.id;
}).status === "quantitative"
? 1
: 0;
this.status
? this.$set(this.form, "指标参数", [{ 名称: "", 备注: "" }])
: null;
},
},
};
</script>
<style lang='scss' scoped>
::v-deep .el-textarea__inner {
min-height: 200px !important;
}
.el-select {
width: 100%;
}
.params {
position: absolute;
right: 0;
top: 13px;
}
</style>

View File

@ -0,0 +1,164 @@
<!-- 指标详情 -->
<template>
<el-card>
<el-row type="flex" justify="space-between" slot="header" align="middle">
<h2>基本信息</h2>
<el-button
type="primary"
@click="openDialog"
>编辑</el-button
>
</el-row>
<el-descriptions :column="1">
<el-descriptions-item label="指标名称">{{
data["指标名称"]
}}</el-descriptions-item>
<el-descriptions-item label="指标备注">{{
data["指标备注"]
}}</el-descriptions-item>
<el-descriptions-item label="指标分类">{{
data["指标分类"]
}}</el-descriptions-item>
<el-descriptions-item label="指标描述">
<p class="des">{{ data["指标描述"] }}</p></el-descriptions-item
>
</el-descriptions>
<p class="title">指标参数:</p>
<el-row style="width: 50%; margin: 20px 0 20px 10px"
><fecr-table
:data="data['指标参数']"
:column="data['指标参数'].setColumn()"
>
</fecr-table
></el-row>
<p class="title">打分参照:</p>
<el-row style="width: 50%; margin: 20px 0 20px 10px"
><fecr-table
:data="data['打分参照']"
:column="data['打分参照'].setColumn()"
>
</fecr-table
></el-row>
<fecr-dialog
:width="'700px'"
:title="'指标编辑'"
:visible.sync="visible"
@cancel="visible = false"
@submit="submit"
>
<template slot="body">
<el-form :model="form">
<el-form-item label="指标名称">
<el-input v-model="form['指标名称']"></el-input>
</el-form-item>
<el-form-item label="指标备注">
<el-input v-model="form['指标备注']"></el-input>
</el-form-item>
<el-form-item label="指标描述">
<el-input type="textarea" v-model="form['指标描述']"></el-input>
</el-form-item>
<el-form-item
label="指标参数"
style="position: relative"
>
<i class="el-icon-plus params" @click="form['指标参数'].push({参数:'',备注:''})"></i>
<el-row type="flex" justify="space-between" style="width: 100%" v-for="(item,index) in form['指标参数']" :key="index">
<el-col :span="11"
><el-input placeholder="参数名称" v-model="item['名称']"></el-input
></el-col>
<el-col :span="11"
><el-input placeholder="参数备注" v-model="item['备注']"></el-input
></el-col>
</el-row>
</el-form-item>
<el-form-item label="指标分类">
<el-select v-model="form['指标分类']"></el-select>
</el-form-item>
</el-form>
</template>
</fecr-dialog>
</el-card>
</template>
<script>
import FecrTable from "@/components/FecrTable";
import FecrDialog from "@/components/FecrDialog";
export default {
components: { FecrTable, FecrDialog },
data() {
return {
visible: false,
form: {},
data: {
指标名称: "ProfitRadio",
指标备注: "区域碳风险",
指标分类: "指标分类项",
指标描述:
"建模,就是建立模型,就是为了理解事物而对事物做出的一种抽象,是对事物的一种无歧义的书面描述。建立系统模型的过程,又称模型化。建模是研究系统的重要手段和前提。凡是用模型描述系统的因果关系或相互关系的过程都属于建模。因描述的关系各异,所以实现这一过程的手段和方法也是多种多样的。可以通过对系统本身运动规律的分析,根据事物的机理来建模;也可以通过对系统的实验或统计数据的处理,并根据关于系统的已有的知识和经验来建模。还可以同时使用几种方法。",
指标参数: [
{ 名称: "profit_total", 备注: "利润总额" },
{ 名称: "profit_total", 备注: "利润总额" },
{ 名称: "profit_total", 备注: "利润总额" },
],
打分参照: [
{
指标名称: "营业总收入",
第一档: "[250,+∞)",
第二档: "[180,250)",
第三档: "[120,180)",
},
],
},
};
},
methods: {
openDialog(){
Object.keys(this.data).forEach(key=>{
this.$set(this.form,key,this.data[key])
})
this.visible = true
},
submit() {
this.visible = false;
},
},
};
</script>
<style lang='scss' scoped>
.des {
padding: 20px;
border: 1px dashed #d9d9d9;
}
.el-descriptions-item {
::v-deep &__label {
width: 70px;
}
::v-deep &__content {
width: calc(100% - 67px);
margin: 10px 0 10px 20px;
}
}
::v-deep .el-textarea__inner {
min-height: 100px !important;
}
.el-select {
width: 100%;
}
.title {
margin: 10px 0px 10px 10px;
font-weight: 800;
}
.params{
position: absolute;
right: 0;
top:13px;
}
</style>

View File

@ -0,0 +1,237 @@
<!-- 模型 -->
<template>
<el-card>
<el-row slot="header" type="flex">
<el-dropdown>
<el-button type="primary">新建</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="create('qualitative')">定性指标构建</el-dropdown-item>
<el-dropdown-item @click.native="create('quantitative')">定量指标构建</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-input
placeholder="请输入搜索内容"
suffix-icon="el-icon-search"
></el-input>
</el-row>
<el-container>
<el-aside style="width: 250px">
<el-checkbox-group v-model="checkList">
<el-checkbox label="定性指标"></el-checkbox>
<el-checkbox label="定量指标"></el-checkbox>
</el-checkbox-group>
<p
:class="selectKey === -1 ? 'tree-active' : ''"
@click="selectKey = -1"
>
全部
</p>
<el-tree
class="filter-tree"
:data="data"
:props="defaultProps"
default-expand-all
:filter-node-method="filterNode"
@node-click="nodeClick"
ref="tree"
>
</el-tree>
</el-aside>
<el-main>
<fecr-table
:page="page"
:total="tableData.total"
:data="tableData.records"
:column="tableData.records.setColumn()"
@cellClick="cellClick"
>
<template slot="operation">
<el-button type="text" class="button-edit">编辑</el-button>
<el-button type="text" class="button-delete">删除</el-button>
</template></fecr-table
>
</el-main>
</el-container>
</el-card>
</template>
<script>
import FecrTable from "@/components/FecrTable";
export default {
components: { FecrTable },
data() {
return {
selectKey: -1,
filterText: "",
checkList: [],
data: [
{
id: 1,
label: "ESG指标",
children: [
{
id: 7,
label: "区域碳风险",
},
{
id: 8,
label: "企业碳治理",
},
{
id: 9,
label: "企业碳地位",
},
],
},
{
id: 2,
label: "绿色指标",
children: [],
},
],
defaultProps: {
children: "children",
label: "label",
},
page: {
page_num: 1,
page_size: 10,
},
tableData: {
total: 50,
records: [
{
指标ID: "20220923142",
指标名称: "区域碳风险",
指标英文名: "ProfitRadio",
参数: ["profit-total", "operation-income"],
备注: ["利润表-利润总额", "利润表-营业收入"],
操作: "",
},
{
指标ID: "20220923142",
指标名称: "区域碳风险",
指标英文名: "ProfitRadio",
参数: ["profit-total", "operation-income"],
备注: ["利润表-利润总额", "利润表-营业收入"],
操作: "",
},
{
指标ID: "20220923142",
指标名称: "区域碳风险",
指标英文名: "ProfitRadio",
参数: ["profit-total", "operation-income"],
备注: ["利润表-利润总额", "利润表-营业收入"],
操作: "",
},
{
指标ID: "20220923142",
指标名称: "区域碳风险",
指标英文名: "ProfitRadio",
参数: ["profit-total", "operation-income"],
备注: ["利润表-利润总额", "利润表-营业收入"],
操作: "",
},
{
指标ID: "20220923142",
指标名称: "区域碳风险",
指标英文名: "ProfitRadio",
参数: ["profit-total", "operation-income"],
备注: ["利润表-利润总额", "利润表-营业收入"],
操作: "",
},
],
},
};
},
methods: {
create(val){
this.$store.commit("set_tabs_key", val === 'quantitative'?"2-1-2":'2-1-1');
this.$store.commit("set_tabs", {
id: val === 'quantitative'?"2-1-2":'2-1-1',
title: val === 'quantitative'?"定量指标构建":"定性指标构建",
content: "indicatorsCreate",
status:val
});
},
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
nodeClick(node) {
this.selectKey = node.$treeNodeId;
},
cellClick(val) {
this.$store.commit("set_tabs_key", val['指标ID']);
this.$store.commit("set_tabs", {
id: val['指标ID'],
title: val['指标名称'],
content: "indicatorsDetails",
});
},
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
},
},
};
</script>
<style lang='scss' scoped>
.el-input {
width: 250px;
margin-left: 20px;
}
::v-deep .el-card__body {
height: calc(100% - 112px);
}
.el-container {
width: 100%;
height: 100%;
.el-aside {
border-radius: 6px;
padding: 20px;
height: 100%;
background-color: #2d2d2d;
.el-checkbox-group {
margin-bottom: 20px;
}
p {
height: 26px;
font-size: 14px;
margin: 5px 0;
padding: 10px 0 10px 25px;
line-height: 26px;
cursor: pointer;
}
p:hover {
background-color: $theme-color;
border-radius: 6px;
}
.tree-active {
background-color: $theme-color;
border-radius: 6px;
}
}
.el-main {
width: calc(100% - 250px);
margin-left: 50px;
border-radius: 6px;
padding: 20px;
height: 100%;
background-color: #2d2d2d;
}
}
</style>

View File

@ -0,0 +1,433 @@
<!-- 模型详情 -->
<template>
<el-card>
<h2 slot="header">{{ data.name }}</h2>
<el-descriptions :column="1">
<el-descriptions-item label="更新时间">{{
data.update_time
}}</el-descriptions-item>
<el-descriptions-item label="模型归类"
><el-tag size="small" v-for="item in data.type" :key="item">{{
item
}}</el-tag></el-descriptions-item
>
<el-descriptions-item label="模型描述">
<p class="des">{{ data.des }}</p></el-descriptions-item
>
</el-descriptions>
<el-button type="primary" class="edit" @click="openDialog">编辑</el-button>
<el-tabs v-model="activeKey">
<el-tab-pane name="sheet" label="模型">
<fecr-table
:data="data.sheet"
:column="sheet_column"
:spanMethod="spanMethod"
>
<template slot="operation">
<el-button type="text">展开</el-button>
</template></fecr-table
>
</el-tab-pane>
<el-tab-pane name="indicators" label="指标">
<fecr-table
:data="data.indicators"
:column="indicators_column"
></fecr-table>
</el-tab-pane>
<el-tab-pane name="data" label="数据">
<el-container>
<el-aside>
<p
v-for="(item, index) in data.data"
:key="index"
:class="currentIndex === index ? 'tree-active' : null"
@click="currentIndex = index"
>
{{ index }}
</p>
</el-aside>
<el-main>
<el-tree :data="data.data[currentIndex]" :props="defaultProps">
</el-tree>
</el-main>
</el-container>
</el-tab-pane>
</el-tabs>
<fecr-dialog :visible="visible" @submit="submit" @cancel="visible = false">
<template slot="body">
<prism-editor
class="my-editor"
v-model="checkedValue"
:highlight="highlighter"
:line-numbers="true"
></prism-editor>
</template>
</fecr-dialog>
</el-card>
</template>
<script>
import FecrTable from "@/components/FecrTable";
import FecrDialog from "@/components/FecrDialog";
import { PrismEditor } from "vue-prism-editor";
import "vue-prism-editor/dist/prismeditor.min.css";
import { highlight, languages } from "prismjs/components/prism-core";
import "prismjs/components/prism-clike";
import "prismjs/components/prism-javascript";
import "prismjs/themes/prism-tomorrow.css";
export default {
components: { FecrTable, FecrDialog, PrismEditor },
data() {
return {
visible: false,
activeKey: "sheet",
currentIndex: "资产利润表",
checkedValue: "",
data: {
name: "碳资信模型",
update_time: "2022-09-29",
type: ["绿色债券", "ESG模型"],
des: "建模,就是建立模型,就是为了理解事物而对事物做出的一种抽象,是对事物的一种无歧义的书面描述。建立系统模型的过程,又称模型化。建模是研究系统的重要手段和前提。凡是用模型描述系统的因果关系或相互关系的过程都属于建模。因描述的关系各异,所以实现这一过程的手段和方法也是多种多样的。可以通过对系统本身运动规律的分析,根据事物的机理来建模;也可以通过对系统的实验或统计数据的处理,并根据关于系统的已有的知识和经验来建模。还可以同时使用几种方法。",
sheet: [
{
index_1: "区域碳风险",
index_2: "区域经济发展",
index_3: "GDP规模",
weight: 3,
},
{
index_1: "区域碳风险",
index_2: "区域经济发展",
index_3: "GDP增速",
weight: 2,
},
{
index_1: "区域碳风险",
index_2: "区域能源结构",
index_3: "区域能源结构",
weight: 4,
},
{
index_1: "区域碳风险",
index_2: "区域绿色金融",
index_3: "多层次绿色金融市场建设",
weight: 3,
},
{
index_1: "区域碳风险",
index_2: "区域环保",
index_3: "区域环保投入",
weight: 2,
},
{
index_1: "区域碳风险",
index_2: "区域环保",
index_3: "区域碳排放强度",
weight: 4,
},
{
index_1: "区域碳风险",
index_2: "区域环保",
index_3: "区域生态环境",
weight: 2,
},
],
indicators: [
{
indicators: "ProfitRadio",
Chinese: "营业利润率",
path: ["利润表-利润总额", "利润表-营业收入"],
param: ["profit-total", "operation-income"],
},
{
indicators: "ProfitRadio",
Chinese: "营业利润率",
path: ["利润表-利润总额", "利润表-营业收入"],
param: ["profit-total", "operation-income"],
},
{
indicators: "ProfitRadio",
Chinese: "营业利润率",
path: ["利润表-利润总额", "利润表-营业收入"],
param: ["profit-total", "operation-income"],
},
],
data: {
资产利润表: [
{
label: "2022",
children: [
{
label: "流动资产",
children: [
{ label: "货币资金" },
{ label: "交易性金融资产" },
{ label: "应收账款" },
{ label: "预付款项" },
{ label: "其他应收款项" },
{ label: "一年内到期非流动资产" },
],
},
{
label: "非流动资产",
children: [
{ label: "可供出售金融资产" },
{ label: "债权投资" },
{ label: "长期应收款" },
{ label: "长期股权投资" },
{ label: "其他非流动金融资产" },
],
},
],
},
{
label: "2021",
children: [
{
label: "流动资产",
children: [
{ label: "货币资金" },
{ label: "交易性金融资产" },
{ label: "应收账款" },
{ label: "预付款项" },
{ label: "其他应收款项" },
{ label: "一年内到期非流动资产" },
],
},
{
label: "非流动资产",
children: [
{ label: "可供出售金融资产" },
{ label: "债权投资" },
{ label: "长期应收款" },
{ label: "长期股权投资" },
{ label: "其他非流动金融资产" },
],
},
],
},
{
label: "2021",
children: [
{
label: "流动资产",
children: [
{ label: "货币资金" },
{ label: "交易性金融资产" },
{ label: "应收账款" },
{ label: "预付款项" },
{ label: "其他应收款项" },
{ label: "一年内到期非流动资产" },
],
},
{
label: "非流动资产",
children: [
{ label: "可供出售金融资产" },
{ label: "债权投资" },
{ label: "长期应收款" },
{ label: "长期股权投资" },
{ label: "其他非流动金融资产" },
],
},
],
},
{
label: "2019",
children: [
{
label: "流动资产",
children: [
{ label: "货币资金" },
{ label: "交易性金融资产" },
{ label: "应收账款" },
{ label: "预付款项" },
{ label: "其他应收款项" },
{ label: "一年内到期非流动资产" },
],
},
{
label: "非流动资产",
children: [
{ label: "可供出售金融资产" },
{ label: "债权投资" },
{ label: "长期应收款" },
{ label: "长期股权投资" },
{ label: "其他非流动金融资产" },
],
},
],
},
],
利润表: [],
现金流量表: [],
},
},
sheet_column: [
{ label: "一级指标", prop: "index_1" },
{ label: "二级指标", prop: "index_2" },
{ label: "三级指标", prop: "index_3" },
{ label: "权重", prop: "weight" },
{ label: "操作", prop: "operation" },
],
indicators_column: [
{ label: "指标", prop: "indicators" },
{ label: "中文", prop: "Chinese" },
{ label: "加载路径", prop: "path" },
{ label: "输入参数", prop: "param" },
],
defaultProps: {
children: "children",
label: "label",
},
};
},
methods: {
highlighter(code) {
return highlight(code, languages.js); //returns html
},
openDialog() {
this.visible = true;
this.checkedValue = JSON.stringify(this.data[this.activeKey], null, 4);
},
mergeCell(key) {
const data = [];
for (let i = 0; i < this.data.sheet.length - 1; i++) {
if (this.data.sheet[i][key] !== this.data.sheet[i + 1][key]) {
data.push(i + 1);
}
}
data.unshift(0);
data.push(this.data.sheet.length);
return data;
},
spanMethod({ rowIndex, columnIndex }) {
if (columnIndex === 0) {
const data = this.mergeCell("index_1");
for (let i = 0; i < data.length - 1; i++) {
if (rowIndex === data[i]) {
return [data[i + 1] - data[i], 1];
} else if (i === data.length - 2) {
return [0, 0];
} else {
continue;
}
}
} else if (columnIndex === 1) {
const data = this.mergeCell("index_2");
for (let i = 0; i < data.length - 1; i++) {
if (rowIndex === data[i]) {
return [data[i + 1] - data[i], 1];
} else if (i === data.length - 2) {
return [0, 0];
} else {
continue;
}
}
}
},
submit() {
this.data[this.activeKey] = JSON.parse(this.checkedValue);
this.visible = false;
},
},
};
</script>
<style lang='scss' scoped>
.el-card {
height: auto;
}
.el-card_body {
padding: 0 20px;
}
.el-descriptions-item {
::v-deep &__label {
width: 70px;
}
::v-deep &__content {
width: calc(100% - 67px);
margin: 10px 0 10px 20px;
}
}
.des {
padding: 20px;
border: 1px dashed #d9d9d9;
}
.el-tab-pane {
height: 100%;
}
.el-tabs {
height: calc(100% - 80px);
::v-deep &__item {
color: rgb(255, 255, 255, 0.87) !important;
}
::v-deep &__item.is-active {
color: $theme-color !important;
}
::v-deep &__item:hover,
::v-deep &__item:focus {
color: $theme-color !important;
}
}
.el-container {
width: 100%;
height: 800px;
.el-aside {
border-radius: 6px;
padding: 20px;
background-color: #2d2d2d;
p {
height: 26px;
font-size: 14px;
margin: 5px 0;
padding: 10px 0 10px 25px;
line-height: 26px;
cursor: pointer;
}
p:hover {
background-color: $theme-color;
border-radius: 6px;
}
.tree-active {
background-color: $theme-color;
border-radius: 6px;
}
}
.el-main {
width: calc(100% - 250px);
margin-left: 50px;
border-radius: 6px;
padding: 20px;
height: 100%;
background-color: #2d2d2d;
}
}
.edit {
position: relative;
left: calc(100% - 68px);
top: 38px;
z-index: 99;
}
</style>

View File

@ -0,0 +1,212 @@
<!-- 模型 -->
<template>
<el-card>
<el-row slot="header" type="flex">
<el-button type="primary">新建</el-button>
<el-input
placeholder="请输入搜索内容"
suffix-icon="el-icon-search"
></el-input>
</el-row>
<el-container>
<el-aside style="width: 250px">
<p
:class="selectKey === -1 ? 'tree-active' : ''"
@click="selectKey = -1"
>
全部
</p>
<el-tree
class="filter-tree"
:data="data"
:props="defaultProps"
default-expand-all
:filter-node-method="filterNode"
@node-click="nodeClick"
ref="tree"
>
</el-tree>
</el-aside>
<el-main>
<fecr-table
:page="page"
:total="tableData.total"
:data="tableData.records"
:column="tableData.records.setColumn()"
@cellClick="cellClick"
>
<template slot="operation">
<el-button type="text" class="button-edit">编辑</el-button>
<el-button type="text" class="button-delete">删除</el-button>
</template></fecr-table
>
</el-main>
</el-container>
</el-card>
</template>
<script>
import FecrTable from "@/components/FecrTable";
export default {
components: { FecrTable },
data() {
return {
selectKey: -1,
filterText: "",
data: [
{
id: 1,
label: "ESG模型",
children: [],
},
{
id: 2,
label: "绿色债券",
children: [],
},
{
id: 3,
label: "行业主体评级模型",
children: [
{
id: 7,
label: "制造业",
},
{
id: 8,
label: "信息技术业",
},
],
},
],
defaultProps: {
children: "children",
label: "label",
},
page: {
page_num: 1,
page_size: 10,
},
tableData: {
total: 50,
records: [
{
模型ID: "20220923142",
模型名称: "碳资信模型",
状态: "启用",
归类: "绿色债券",
时间: "2022-09-23",
操作: "",
},
{
模型ID: "20220923142",
模型名称: "碳资信模型",
状态: "启用",
归类: "绿色债券",
时间: "2022-09-23",
操作: "",
},
{
模型ID: "20220923142",
模型名称: "碳资信模型",
状态: "启用",
归类: "绿色债券",
时间: "2022-09-23",
操作: "",
},
{
模型ID: "20220923142",
模型名称: "碳资信模型",
状态: "启用",
归类: "绿色债券",
时间: "2022-09-23",
操作: "",
},
{
模型ID: "20220923142",
模型名称: "碳资信模型",
状态: "启用",
归类: "绿色债券",
时间: "2022-09-23",
操作: "",
},
],
},
};
},
methods: {
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
nodeClick(node) {
this.selectKey = node.$treeNodeId;
},
cellClick() {
this.$store.commit("set_tabs_key", "1-1");
this.$store.commit("set_tabs", {
id: "1-1",
title: "碳资信模型",
content: "modelDetails",
});
},
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
},
},
};
</script>
<style lang='scss' scoped>
.el-input {
width: 250px;
margin-left: 20px;
}
::v-deep .el-card__body {
height: calc(100% - 112px);
}
.el-container {
width: 100%;
height: 100%;
.el-aside {
border-radius: 6px;
padding: 20px;
height: 100%;
background-color: #2d2d2d;
p {
height: 26px;
font-size: 14px;
margin: 5px 0;
padding: 10px 0 10px 25px;
line-height: 26px;
cursor: pointer;
}
p:hover {
background-color: $theme-color;
border-radius: 6px;
}
.tree-active {
background-color: $theme-color;
border-radius: 6px;
}
}
.el-main {
width: calc(100% - 250px);
margin-left: 50px;
border-radius: 6px;
padding: 20px;
height: 100%;
background-color: #2d2d2d;
}
}
</style>

View File

@ -0,0 +1,19 @@
<!-- 模型 -->
<template>
<div>111</div>
</template>
<script>
export default {
data () {
return {
};
},
methods: {}
}
</script>
<style lang='scss' scoped>
</style>

View File

@ -0,0 +1,19 @@
<!-- 模型 -->
<template>
<div>111</div>
</template>
<script>
export default {
data () {
return {
};
},
methods: {}
}
</script>
<style lang='scss' scoped>
</style>

191
src/view/home/index.vue Normal file
View File

@ -0,0 +1,191 @@
<!-- 首页 -->
<template>
<el-container>
<el-aside style="width: 100px">
<fecr-aside :activeKey="$store.state.tabsKey" />
</el-aside>
<el-main>
<el-tabs
v-model="$store.state.tabsKey"
closable
@edit="handleTabsEdit"
@tab-click="handleClick"
>
<el-tab-pane
:key="item.id"
v-for="item in $store.state.tabs"
:name="item.id"
>
<span slot="label"><i :class="item.icon"></i> {{ item.title }}</span>
<div class="pane-box">
<component
:is="item.content"
:key="item.id"
v-if="item.id == $store.state.tabsKey"
/>
</div>
</el-tab-pane>
</el-tabs>
<el-dropdown trigger="click">
<span class="el-dropdown-link">
<el-button
icon="el-icon-more"
type="primary"
size="small"
></el-button>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="clearAll"
>关闭所有窗口</el-dropdown-item
>
<el-dropdown-item @click.native="clearLeft"
>关闭当前左侧窗口</el-dropdown-item
>
<el-dropdown-item @click.native="clearRight"
>关闭当前右侧窗口</el-dropdown-item
>
</el-dropdown-menu>
</el-dropdown>
</el-main>
</el-container>
</template>
<script>
import FecrAside from "./aside/index";
import model from "./content/model/index";
import modelDetails from "./content/model/details"
import indicators from "./content/indicators/index";
import indicatorsCreate from "./content/indicators/create"
import indicatorsDetails from "./content/indicators/details"
import rating from "./content/rating/index";
import setting from "./content/setting/index";
export default {
components: {
FecrAside,
model,
modelDetails,
indicators,
indicatorsCreate,
indicatorsDetails,
rating,
setting,
},
data() {
return {
tabs: this.$store.getters.getTabs,
tabsKey: this.$store.getters.getTabsKey,
};
},
methods: {
handleClick(key) {
this.$store.commit("set_tabs_key", key.name);
},
handleTabsEdit(targetName, action) {
if (action === "remove") {
const key = this.$store.state.tabs.findIndex((item) => {
return targetName == item.id;
});
var deleteItem;
for (let i in this.$store.state.tabs) {
if (this.$store.state.tabs[i].id === targetName) {
deleteItem = this.$store.state.tabs[i];
}
}
this.$store.commit("delete_tabs_item", deleteItem);
this.$store.commit("set_tabs_key", this.$store.state.tabs[key - 1].id);
}
},
clearAll() {
this.$store.commit("delete_tabs_all");
},
clearLeft() {
const key = this.$store.getters.getTabsKey;
const tabs = this.$store.getters.getTabs;
for (let i = 0; i < tabs.length; i++) {
if (tabs[i].id !== key) {
this.$store.commit("delete_tabs_item", tabs[i]);
} else {
break;
}
}
},
clearRight() {
const key = this.$store.getters.getTabsKey;
const tabs = this.$store.getters.getTabs;
for (let i = tabs.length - 1; i > 0; i--) {
if (tabs[i].id !== key) {
this.$store.commit("delete_tabs_item", tabs[i]);
} else {
break;
}
}
},
},
watch: {
"this.$store.state.tabsKey"() {
this.tabsKey = this.$store.getters.getTabsKey;
},
"this.$store.state.tabs"() {
this.tabs = this.$store.getters.getTabs;
},
},
};
</script>
<style lang='scss' scoped>
@import "assets/style/home/index.scss";
.el-tabs {
height: 100%;
::v-deep &__item {
margin-right: 10px;
border-radius: 6px;
span {
font-size: 12px;
}
}
::v-deep &__item.is-top:nth-child(2) {
padding-left: 20px;
}
::v-deep &__item.is-top:last-child {
padding-right: 20px;
}
::v-deep &__nav-wrap::after {
background-color: transparent;
}
::v-deep &__content {
height: calc(100% - 50px);
}
::v-deep &__active-bar {
background-color: transparent;
}
.el-tab-pane {
height: 100%;
overflow: hidden;
.pane-box {
width: calc(100% + 5px);
height: 100%;
overflow: auto;
}
}
}
.el-dropdown {
position: absolute;
right: 40px;
top: 20px;
}
</style>

0
src/view/index.vue Normal file
View File

193
src/view/user/login.vue Normal file
View File

@ -0,0 +1,193 @@
<template>
<el-container>
<el-main>
<div class="form-box">
<el-image :src="logo" />
<el-form
label-position="top"
:model="form"
:inline="true"
status-icon
:rules="rules"
ref="form"
>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" type="text"></el-input>
</el-form-item>
<el-form-item label="密码" prop="pwd">
<el-input
v-model="form.pwd"
type="password"
show-password
></el-input>
</el-form-item>
<el-form-item label="邮箱验证码" prop="vcode">
<el-row type="flex" justify="center">
<el-input v-model="form.vcode"></el-input>
<el-button @click="mail" type="primary" v-if="flag"
>发送</el-button
><el-button disabled v-else>已发送({{ this.timing }})</el-button>
</el-row>
</el-form-item>
<el-form-item>
<el-row>
<el-button
style="width: 100%"
@click="login('form')"
@keyup.enter="login('form')"
type="primary"
>登录</el-button
>
</el-row>
</el-form-item>
<el-row>
<el-image
:src="item"
v-for="(item, index) in svg"
:key="index"
></el-image>
</el-row>
</el-form>
</div>
</el-main>
</el-container>
</template>
<script>
import logo from "assets/img/logo.png";
import QQ from "assets/img/QQ.svg";
import wx from "assets/img/微信.svg";
import ot from "assets/img/其它.svg";
import ms from "assets/img/短信.svg";
import { rsaKey } from "utils/rsaKey.js";
import request from "api/request";
export default {
data() {
var validateEmail = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入邮箱"));
} else {
const reg = new RegExp(
"^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$"
);
if (!reg.test(value)) {
callback(new Error("非邮箱格式"));
}
callback();
}
};
var validateMail = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入邮箱验证码"));
} else {
callback();
}
};
var validatePass = (rule, value, callback) => {
const reg = new RegExp(
"(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,30}"
);
if (value === "") {
callback(new Error("请输入密码"));
} else if (!reg.test(value)) {
callback(
new Error("密码中必须包含大小字母、数字、特称字符8-30个字符")
);
} else {
if (this.form.checkpwd !== "") {
this.$refs.form.validateField("checkpwd");
}
callback();
}
};
return {
logo,
flag: true,
timing: 60,
form: {
email: "",
pwd: "",
vcode: "",
},
rules: {
email: [{ validator: validateEmail, trigger: "blur" }],
vcode: [{ validator: validateMail, trigger: "blur" }],
pwd: [{ validator: validatePass, trigger: "blur" }],
},
svg: [QQ, wx, ms, ot],
};
},
methods: {
checkEmail() {
const reg = new RegExp(
"^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$"
);
if (!reg.test(this.form.email)) {
return true;
} else {
return false;
}
},
//
timer() {
const time = setInterval(() => {
if (this.timing > 0) {
this.timing--;
this.flag = false;
} else {
clearInterval(time);
this.flag = true;
this.timing = 60;
}
}, 1000);
},
//
mail() {
if (!this.checkEmail()) {
request.BlockPost("/admin/user/send_vcode", {
email: this.form.email,
}).then((res) => {
if (res.info === "发送成功") {
this.$message.success(res.info);
this.timer();
} else {
this.$message.error(res.info);
}
});
} else {
this.$message.warning("邮箱格式不正确");
}
},
login(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
const obj = {
email: this.form.email,
pwd: rsaKey(this.form.pwd),
vcode: this.form.vcode,
};
request.BlockPost("/admin/user/login", obj)
.then((res) => {
if (res.info === "登录成功") {
this.$message.success(res.info);
this.$store.commit("set_token", res.result.token);
this.$store.commit("set_user_name", res.result["用户名"]);
this.$router.push("/home");
} else {
this.$message.error(res.info);
}
})
.catch((err) => {
console.log(err);
});
} else {
return false;
}
});
},
},
};
</script>
<style lang='scss' scoped>
@import "assets/style/user.scss";
</style>

51
vue.config.js Normal file
View File

@ -0,0 +1,51 @@
module.exports = {
publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
outputDir: "dist",
productionSourceMap: false,
lintOnSave: true,
runtimeCompiler: true,
css: {
loaderOptions: {
sass: {
prependData: `@import "@/assets/sass/_variable.scss";`
}
}
},
configureWebpack: {
resolve: {
alias: {
components: "@/components",
assets: "@/assets",
api: "@/api",
view: "@/view",
router: "@/router",
utils: "@/utils",
store: "@/store"
},
},
},
devServer: {
disableHostCheck:true,
open: false,//open 在devServer启动且第一次构建完成时自动用我们的系统的默认浏览器去打开要开发的网页
host: '0.0.0.0',//默认是 localhost。如果你希望服务器外部可访问指定如下 host: '0.0.0.0'设置之后之后可以访问ip地址
port: 8081,
hot: true,//hot配置是否启用模块的热替换功能devServer的默认行为是在发现源代码被变更后通过自动刷新整个页面来做到事实预览开启hot后将在不刷新整个页面的情况下通过新模块替换老模块来做到实时预览。
https: false,
hotOnly: false,// hot 和 hotOnly 的区别是在某些模块不支持热更新的情况下,前者会自动刷新页面,后者不会刷新页面,而是在控制台输出热更新失败
proxy: {
'/api': {
target: 'http://o339q23220.goho.co/', //目标接口域名
secure: false, //false为http访问true为https访问
changeOrigin: true, //是否跨域
pathRewrite: {
'^/api': '' //重写接口
}
}
}, // 设置代理
before: app => { }
},
// 第三方插件配置
pluginOptions: {
// ...
}
}