commit
f8c235487a
39 changed files with 22899 additions and 0 deletions
@ -0,0 +1,8 @@ |
|||||
|
[*.{js,jsx,ts,tsx,vue}] |
||||
|
indent_style = space |
||||
|
indent_size = 2 |
||||
|
end_of_line = lf |
||||
|
trim_trailing_whitespace = true |
||||
|
insert_final_newline = true |
||||
|
max_line_length = 140 |
||||
|
root = true |
@ -0,0 +1 @@ |
|||||
|
VITE_API_URL=http://localhost:4001 |
@ -0,0 +1 @@ |
|||||
|
VITE_API_URL=http://139.196.27.233:29001 |
@ -0,0 +1 @@ |
|||||
|
VITE_API_URL=https://test.tall.wiki |
@ -0,0 +1,9 @@ |
|||||
|
node_modules |
||||
|
dist/ |
||||
|
test |
||||
|
build/ |
||||
|
babel.config.js |
||||
|
package.json |
||||
|
postcss.config.js |
||||
|
.eslintrc.js |
||||
|
vite.config.js |
@ -0,0 +1,56 @@ |
|||||
|
module.exports = { |
||||
|
env: { |
||||
|
browser: true, |
||||
|
es2021: true, |
||||
|
}, |
||||
|
extends: ['plugin:vue/essential', 'airbnb-base', 'plugin:prettier/recommended'], |
||||
|
parserOptions: { |
||||
|
ecmaVersion: 12, |
||||
|
sourceType: 'module', |
||||
|
}, |
||||
|
plugins: ['vue'], |
||||
|
rules: { |
||||
|
'import/no-unresolved': 0, |
||||
|
'import/extensions': 0, |
||||
|
'import/no-extraneous-dependencies': 0, |
||||
|
'no-plusplus': 0, |
||||
|
'no-use-before-define': [ |
||||
|
'error', |
||||
|
{ |
||||
|
functions: false, |
||||
|
classes: true, |
||||
|
variables: true, |
||||
|
}, |
||||
|
], |
||||
|
'consistent-return': 0, |
||||
|
'vue/html-self-closing': 'off', |
||||
|
'no-unused-expressions': 'off', |
||||
|
'vue/no-mutating-props': 'off', |
||||
|
'vue/no-multiple-template-root': 'off', |
||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', |
||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', |
||||
|
'no-param-reassign': 'off', |
||||
|
'max-len': [ |
||||
|
'error', |
||||
|
{ |
||||
|
code: 140, |
||||
|
tabWidth: 2, |
||||
|
}, |
||||
|
], |
||||
|
'object-curly-newline': ['error', { multiline: true }], |
||||
|
'arrow-parens': ['error', 'as-needed'], |
||||
|
'linebreak-style': 'off', |
||||
|
'vue/attributes-order': 'off', |
||||
|
'vue/singleline-html-element-content-newline': 'off', |
||||
|
'vue/max-attributes-per-line': 'off', |
||||
|
'vue/multiline-html-element-content-newline': 'off', |
||||
|
'vue/html-indent': 'off', |
||||
|
'vue/html-closing-bracket-newline': [ |
||||
|
'error', |
||||
|
{ |
||||
|
singleline: 'never', |
||||
|
multiline: 'always', |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
}; |
@ -0,0 +1,8 @@ |
|||||
|
node_modules |
||||
|
.DS_Store |
||||
|
dist |
||||
|
dist-ssr |
||||
|
*.local |
||||
|
.eslintcache |
||||
|
.idea |
||||
|
.vscode |
@ -0,0 +1,4 @@ |
|||||
|
#!/bin/sh |
||||
|
. "$(dirname "$0")/_/husky.sh" |
||||
|
|
||||
|
npx lint-staged |
@ -0,0 +1 @@ |
|||||
|
registry=https://registry.npm.taobao.org |
@ -0,0 +1,13 @@ |
|||||
|
{ |
||||
|
"printWidth": 140, |
||||
|
"singleQuote": true, |
||||
|
"semi": true, |
||||
|
"trailingComma": "all", |
||||
|
"arrowParens": "avoid", |
||||
|
"tabWidth": 2, |
||||
|
"useTabs": false, |
||||
|
"bracketSpacing": true, |
||||
|
"jsxBracketSameLine": false, |
||||
|
"proseWrap": "always", |
||||
|
"endOfLine": "lf" |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
# Vue 3 + Vite |
||||
|
|
||||
|
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more. |
||||
|
|
||||
|
## Recommended IDE Setup |
||||
|
|
||||
|
- [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) |
@ -0,0 +1,18 @@ |
|||||
|
const path = require('path'); |
||||
|
|
||||
|
const resolve = dir => path.join(__dirname, dir); |
||||
|
|
||||
|
module.exports = { |
||||
|
resolve: { |
||||
|
alias: { |
||||
|
'~': __dirname, |
||||
|
'@': resolve('src'), |
||||
|
views: resolve('src/views'), |
||||
|
components: resolve('src/components'), |
||||
|
assets: resolve('src/assets'), |
||||
|
utils: resolve('src/utils'), |
||||
|
store: resolve('src/store'), |
||||
|
apis: resolve('src/apis'), |
||||
|
}, |
||||
|
}, |
||||
|
}; |
@ -0,0 +1 @@ |
|||||
|
module.exports = {extends: ['@commitlint/config-angular']}; |
@ -0,0 +1,13 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8" /> |
||||
|
<link rel="icon" href="/favicon.ico" /> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
|
<title>时物链条插件商城</title></title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div id="app"></div> |
||||
|
<script type="module" src="/src/main.js"></script> |
||||
|
</body> |
||||
|
</html> |
File diff suppressed because it is too large
@ -0,0 +1,74 @@ |
|||||
|
{ |
||||
|
"name": "vue3-vite-template", |
||||
|
"version": "0.0.0", |
||||
|
"scripts": { |
||||
|
"dev": "vite --mode test", |
||||
|
"dev:prod": "vite --mode production", |
||||
|
"dev-test": "vite --mode development", |
||||
|
"build:test": "vite build --mode test", |
||||
|
"build": "vite build --mode production", |
||||
|
"serve": "vite preview", |
||||
|
"cz": "git add . && git cz", |
||||
|
"format": "prettier --write ./src", |
||||
|
"lint": "eslint ./src --ext .vue,.js,.ts", |
||||
|
"fix": "eslint --fix ./src --ext .vue,.js,.ts", |
||||
|
"prepare": "husky install" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"@vitejs/plugin-vue": "^1.9.3", |
||||
|
"axios": "^0.23.0", |
||||
|
"dayjs": "^1.10.7", |
||||
|
"echarts": "^5.2.2", |
||||
|
"element-plus": "^1.1.0-beta.24", |
||||
|
"lodash": "^4.17.21", |
||||
|
"vite": "^2.6.4", |
||||
|
"vite-plugin-compression": "^0.3.5", |
||||
|
"vite-plugin-windicss": "^1.4.11", |
||||
|
"vue": "^3.2.16", |
||||
|
"vue-router": "^4.0.12", |
||||
|
"vuex": "^4.0.2", |
||||
|
"windicss": "^3.1.9" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@commitlint/cli": "^13.2.1", |
||||
|
"@commitlint/config-angular": "^13.2.0", |
||||
|
"commitizen": "^4.2.4", |
||||
|
"cz-conventional-changelog": "^3.3.0", |
||||
|
"eslint": "^7.32.0", |
||||
|
"eslint-config-airbnb-base": "^14.2.1", |
||||
|
"eslint-config-prettier": "^8.3.0", |
||||
|
"eslint-plugin-html": "^6.2.0", |
||||
|
"eslint-plugin-import": "^2.25.2", |
||||
|
"eslint-plugin-prettier": "^4.0.0", |
||||
|
"eslint-plugin-vue": "^7.19.1", |
||||
|
"husky": "^7.0.2", |
||||
|
"lint-staged": "^11.2.3", |
||||
|
"prettier": "^2.4.1", |
||||
|
"unplugin-vue-components": "^0.15.6", |
||||
|
"vite-plugin-linter": "^1.0.1", |
||||
|
"vite-plugin-pwa": "^0.11.6" |
||||
|
}, |
||||
|
"browserslist": [ |
||||
|
"Android >= 4", |
||||
|
"ios >= 8" |
||||
|
], |
||||
|
"config": { |
||||
|
"commitizen": { |
||||
|
"path": "./node_modules/cz-conventional-changelog" |
||||
|
} |
||||
|
}, |
||||
|
"lint-staged": { |
||||
|
"src/**/*.{js,ts,jsx,json,css,vue}": [ |
||||
|
"eslint --fix", |
||||
|
"git add" |
||||
|
], |
||||
|
"*.js": "eslint --cache --fix" |
||||
|
}, |
||||
|
"husky": { |
||||
|
"hooks": { |
||||
|
"prepare-commit-msg": "exec < /dev/tty && git cz --hook || true", |
||||
|
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS", |
||||
|
"pre-commit": "lint-staged" |
||||
|
} |
||||
|
} |
||||
|
} |
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,44 @@ |
|||||
|
<template> |
||||
|
<el-config-provider :locale="local"> |
||||
|
<el-container> |
||||
|
<router-view></router-view> |
||||
|
</el-container> |
||||
|
</el-config-provider> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { useStore } from 'vuex'; |
||||
|
import { useRouter } from 'vue-router'; |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const router = useRouter(); |
||||
|
const userString = sessionStorage.getItem('user'); |
||||
|
|
||||
|
if (userString) { |
||||
|
const user = JSON.parse(userString); |
||||
|
store.commit('user/setUser', user); |
||||
|
} else { |
||||
|
router.push({ name: 'signin' }); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
html, |
||||
|
body, |
||||
|
#app, |
||||
|
#app > section { |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.el-menu { |
||||
|
min-height: 100%; |
||||
|
} |
||||
|
|
||||
|
.el-form--label-top .el-form-item__label { |
||||
|
padding-bottom: 0 !important; |
||||
|
} |
||||
|
|
||||
|
.el-form-item { |
||||
|
margin-bottom: 10px !important; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,15 @@ |
|||||
|
// noinspection SpellCheckingInspection
|
||||
|
|
||||
|
import http from 'utils/axios'; |
||||
|
|
||||
|
const apiUrl = import.meta.env.VITE_API_URL; |
||||
|
const users = `${apiUrl}/gateway/tall3/v3.0/users`; |
||||
|
|
||||
|
// 根据userId 获取token
|
||||
|
export const getToken = userId => http.get(`${users}/userId`, { params: { userId } }); |
||||
|
|
||||
|
// 登录
|
||||
|
export const signIn = params => http.post(`${users}/signin`, params); |
||||
|
|
||||
|
// 修改密码
|
||||
|
export const changePassword = params => http.post(`${users}/password/account`, params); |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 71 KiB |
@ -0,0 +1,56 @@ |
|||||
|
<template> |
||||
|
<div class="flex items-center justify-between pr-5 shadow-sm"> |
||||
|
<h1 class="text-lg font-medium py-3 px-6"> |
||||
|
<i |
||||
|
v-if="menu.show" |
||||
|
:class="{ 'text-gray-800': !menu.collapse, 'text-gray-400': menu.collapse }" |
||||
|
class="el-icon-guide mr-2" |
||||
|
@click="toggleCollapse" |
||||
|
></i> |
||||
|
智能大气腐蚀监测平台【{{ $route.meta.title }}】 |
||||
|
</h1> |
||||
|
|
||||
|
<el-dropdown> |
||||
|
<span class="flex items-center"> |
||||
|
<el-avatar class="mr-2" size="small" src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png"></el-avatar> |
||||
|
<div class="mr-3">{{ account }}</div> |
||||
|
<el-icon class="el-icon--right"> |
||||
|
<ArrowDown /> |
||||
|
</el-icon> |
||||
|
</span> |
||||
|
<template #dropdown> |
||||
|
<el-dropdown-menu> |
||||
|
<el-dropdown-item @click="openChangePassword">修改密码</el-dropdown-item> |
||||
|
<el-dropdown-item @click="signOut">退出登录</el-dropdown-item> |
||||
|
</el-dropdown-menu> |
||||
|
</template> |
||||
|
</el-dropdown> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { computed } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import { useRouter } from 'vue-router'; |
||||
|
import { ArrowDown } from '@element-plus/icons'; |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const router = useRouter(); |
||||
|
const toggleCollapse = () => { |
||||
|
console.log('Toggle Collapse'); |
||||
|
store.commit('toggleCollapse'); |
||||
|
}; |
||||
|
const menu = computed(() => store.state.menu); |
||||
|
const account = computed(() => store.getters['user/account']); |
||||
|
|
||||
|
// 打开修改密码的页面 |
||||
|
function openChangePassword() { |
||||
|
router.push({ name: 'pw-change' }); |
||||
|
} |
||||
|
|
||||
|
// 退出登录 |
||||
|
function signOut() { |
||||
|
store.commit('user/setUser', null); |
||||
|
router.push({ name: 'signin' }); |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,192 @@ |
|||||
|
/* eslint-disable max-len */ |
||||
|
export const colors = ['#EAB308', '#F97316', '#EC4899', '#F43F5E', '#D946EF', '#06B6D4', '#B45309', '#1E40AF', '#166534']; |
||||
|
|
||||
|
export const itemColor = { |
||||
|
'钢腐蚀电流(nA)': colors[5], |
||||
|
'铜腐蚀电流(nA)': colors[6], |
||||
|
'铝腐蚀电流(nA)': colors[7], |
||||
|
'锌腐蚀电流(nA)': colors[8], |
||||
|
'SO2(ppb)': colors[0], |
||||
|
'盐分阻抗(Ω)': colors[1], |
||||
|
'盐分温度(℃)': colors[2], |
||||
|
'环境温度(℃)': colors[3], |
||||
|
'环境湿度(RH%)': colors[4], |
||||
|
}; |
||||
|
|
||||
|
export const legendData = [ |
||||
|
{ |
||||
|
name: 'SO2(ppb)', |
||||
|
itemStyle: { color: colors[0] }, |
||||
|
listStyle: { color: colors[0] }, |
||||
|
}, |
||||
|
{ |
||||
|
name: '盐分阻抗(Ω)', |
||||
|
itemStyle: { color: colors[1] }, |
||||
|
listStyle: { color: colors[1] }, |
||||
|
}, |
||||
|
{ |
||||
|
name: '盐分温度(℃)', |
||||
|
itemStyle: { color: colors[2] }, |
||||
|
listStyle: { color: colors[2] }, |
||||
|
}, |
||||
|
{ |
||||
|
name: '环境温度(℃)', |
||||
|
itemStyle: { color: colors[3] }, |
||||
|
listStyle: { color: colors[3] }, |
||||
|
}, |
||||
|
{ |
||||
|
name: '环境湿度(RH%)', |
||||
|
itemStyle: { color: colors[4] }, |
||||
|
listStyle: { color: colors[4] }, |
||||
|
}, |
||||
|
{ |
||||
|
name: '锌腐蚀电流(nA)', |
||||
|
itemStyle: { color: colors[5] }, |
||||
|
listStyle: { color: colors[5] }, |
||||
|
}, |
||||
|
{ |
||||
|
name: '铜腐蚀电流(nA)', |
||||
|
itemStyle: { color: colors[6] }, |
||||
|
listStyle: { color: colors[6] }, |
||||
|
}, |
||||
|
{ |
||||
|
name: '铝腐蚀电流(nA)', |
||||
|
itemStyle: { color: colors[7] }, |
||||
|
listStyle: { color: colors[7] }, |
||||
|
}, |
||||
|
{ |
||||
|
name: '钢腐蚀电流(nA)', |
||||
|
itemStyle: { color: colors[8] }, |
||||
|
listStyle: { color: colors[8] }, |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
// y轴定义
|
||||
|
export const yAxisData = [ |
||||
|
{ |
||||
|
type: 'value', |
||||
|
name: '腐蚀电流(nA)', |
||||
|
offset: 0, |
||||
|
position: 'left', |
||||
|
axisLine: { |
||||
|
show: true, |
||||
|
lineStyle: { color: colors[7] }, |
||||
|
}, |
||||
|
axisLabel: { formatter: '{value}' }, |
||||
|
axisPointer: { show: false }, |
||||
|
}, |
||||
|
{ |
||||
|
type: 'value', |
||||
|
name: '温度(℃)', |
||||
|
offset: 0, |
||||
|
position: 'right', |
||||
|
axisLine: { |
||||
|
show: true, |
||||
|
lineStyle: { color: colors[3] }, |
||||
|
}, |
||||
|
axisLabel: { formatter: '{value}' }, |
||||
|
axisPointer: { show: false }, |
||||
|
}, |
||||
|
{ |
||||
|
type: 'value', |
||||
|
name: '湿度(RH%)', |
||||
|
offset: 70, |
||||
|
position: 'right', |
||||
|
axisLine: { |
||||
|
show: true, |
||||
|
lineStyle: { color: colors[4] }, |
||||
|
}, |
||||
|
axisLabel: { formatter: '{value}' }, |
||||
|
axisPointer: { show: false }, |
||||
|
}, |
||||
|
{ |
||||
|
type: 'value', |
||||
|
name: 'SO2(ppb)', |
||||
|
position: 'right', |
||||
|
show: false, |
||||
|
offset: 150, |
||||
|
axisLine: { |
||||
|
show: true, |
||||
|
lineStyle: { color: colors[0] }, |
||||
|
}, |
||||
|
axisLabel: { formatter: '{value}' }, |
||||
|
axisPointer: { show: false }, |
||||
|
}, |
||||
|
{ |
||||
|
type: 'value', |
||||
|
name: '盐分阻抗(Ω)', |
||||
|
show: false, |
||||
|
position: 'right', |
||||
|
offset: 220, |
||||
|
axisLine: { |
||||
|
show: true, |
||||
|
lineStyle: { color: colors[1] }, |
||||
|
}, |
||||
|
axisLabel: { formatter: '{value}' }, |
||||
|
axisPointer: { show: false }, |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
/** |
||||
|
* 生成默认数据 |
||||
|
* @param {Object} data |
||||
|
* @returns {[{data: (number|[]|string|*), name: string, type: string},{data: (number|[]|BufferSource|string|*), name: string, type: string, yAxisIndex: number},{data: ([]|string|*), name: string, type: string, yAxisIndex: number},{data: ([]|string|*), name: string, type: string, yAxisIndex: number},{data: [], name: string, type: string, yAxisIndex: number},null,null,null]} |
||||
|
*/ |
||||
|
export function generateDefaultSeries(data) { |
||||
|
return [ |
||||
|
{ |
||||
|
name: '锌腐蚀电流(nA)', |
||||
|
type: 'line', |
||||
|
yAxisIndex: 0, |
||||
|
data: data.corrosionXIN, |
||||
|
}, |
||||
|
{ |
||||
|
name: '铜腐蚀电流(nA)', |
||||
|
type: 'line', |
||||
|
yAxisIndex: 0, |
||||
|
data: data.corrosionTONG, |
||||
|
}, |
||||
|
{ |
||||
|
name: '铝腐蚀电流(nA)', |
||||
|
type: 'line', |
||||
|
yAxisIndex: 0, |
||||
|
data: data.corrosionLV, |
||||
|
}, |
||||
|
{ |
||||
|
name: '钢腐蚀电流(nA)', |
||||
|
type: 'line', |
||||
|
yAxisIndex: 0, |
||||
|
data: data.corrosionGANG, |
||||
|
}, |
||||
|
{ |
||||
|
name: '环境温度(℃)', |
||||
|
type: 'line', |
||||
|
yAxisIndex: 1, |
||||
|
data: data.environmentTemperature, |
||||
|
}, |
||||
|
{ |
||||
|
name: '盐分温度(℃)', |
||||
|
type: 'line', |
||||
|
yAxisIndex: 1, |
||||
|
data: data.saltT, |
||||
|
}, |
||||
|
{ |
||||
|
name: '环境湿度(RH%)', |
||||
|
type: 'line', |
||||
|
yAxisIndex: 2, |
||||
|
data: data.environmentHumidity, |
||||
|
}, |
||||
|
{ |
||||
|
name: 'SO2(ppb)', |
||||
|
type: 'line', |
||||
|
yAxisIndex: 3, |
||||
|
data: data.so2, |
||||
|
}, |
||||
|
{ |
||||
|
name: '盐分阻抗(Ω)', |
||||
|
type: 'line', |
||||
|
yAxisIndex: 4, |
||||
|
data: data.saltR, |
||||
|
}, |
||||
|
]; |
||||
|
} |
@ -0,0 +1,164 @@ |
|||||
|
// 网络参数设置
|
||||
|
export const networkConfig = { |
||||
|
ip1: '', |
||||
|
port1: '', |
||||
|
ip2: '', |
||||
|
port2: '', |
||||
|
ip3: '', |
||||
|
port3: '', |
||||
|
ipBackup: '', |
||||
|
portBackup: '', |
||||
|
account: '', |
||||
|
password: '', |
||||
|
apn: '', |
||||
|
status: '', |
||||
|
}; |
||||
|
|
||||
|
// 功能参数设置
|
||||
|
export const functionConfig = { |
||||
|
frequency: { |
||||
|
so2: 0, // SO2采样频率
|
||||
|
metal: 0, // 金属腐蚀采样频率
|
||||
|
th: 0, // 温湿度 采样频率
|
||||
|
salt: 0, // 盐雾 采样频率
|
||||
|
}, // 采样频率
|
||||
|
count: 0, // 采集个数
|
||||
|
time: Date.now(), // 设置时间
|
||||
|
batteryLow: 0, // 电池电压低阈值
|
||||
|
batteryHigh: 0, // 电池电压高阈值
|
||||
|
sunHigh: 0, // 太阳能电压高阈值
|
||||
|
humidityHigh: 0, // 湿度高阈值
|
||||
|
temperatureLow: 0, // 温度低阈值
|
||||
|
temperatureHigh: 0, // 温度高阈值
|
||||
|
securityMode: 'OPEN', // 安全模式 OPEN->不加密 ENCRYPTION->加密
|
||||
|
report: { |
||||
|
type: 'CYCLE', // 上报周期类型 0->时间点 1->周期
|
||||
|
timePoints: [''], // 设置时间点
|
||||
|
cycle: 240, // 上报周期分钟数
|
||||
|
}, |
||||
|
status: '', |
||||
|
}; |
||||
|
|
||||
|
// 金属腐蚀类型
|
||||
|
export const corrosiveTypes = [ |
||||
|
{ |
||||
|
value: 'XIN', |
||||
|
type: '锌', |
||||
|
}, |
||||
|
{ |
||||
|
value: 'LV', |
||||
|
type: '铝', |
||||
|
}, |
||||
|
{ |
||||
|
value: 'TONG', |
||||
|
type: '铜', |
||||
|
}, |
||||
|
{ |
||||
|
value: 'GANG', |
||||
|
type: '钢', |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
// 添加设备
|
||||
|
export const deviceData = { |
||||
|
deviceId: '', // 设备id
|
||||
|
deviceFullId: '', // 设备完整id
|
||||
|
deviceDirection: '', // 设备朝向
|
||||
|
area: '', // 地区
|
||||
|
address: '', // 站点名称
|
||||
|
contact: '', // 联系人
|
||||
|
phone: '', // 联系人电话
|
||||
|
lon: '', // 经度
|
||||
|
lat: '', // 纬度
|
||||
|
head: '', // 负责人
|
||||
|
installLocation: '', // 安装位置
|
||||
|
installTime: '', // 安装时间
|
||||
|
runTime: '', // 正式运行时间
|
||||
|
linkAddress: '', // 链路地址
|
||||
|
probNo: '', // 探头编号
|
||||
|
simple: '', // 试样
|
||||
|
sim1: '', // sim卡1
|
||||
|
protocolVersion: '', // 协议版本
|
||||
|
joint: '', // 主站后台联调情况
|
||||
|
operationRecord: '', // 维修记录
|
||||
|
remark: '', // 备注
|
||||
|
type: 'IACD', // 产品类型
|
||||
|
}; |
||||
|
|
||||
|
// 设备添加编辑 规则
|
||||
|
export const deviceRules = { |
||||
|
deviceId: [ |
||||
|
{ |
||||
|
required: true, |
||||
|
message: '请输入设备ID号', |
||||
|
trigger: 'blur', |
||||
|
}, |
||||
|
{ |
||||
|
len: 6, |
||||
|
message: '请输入6位设备ID号', |
||||
|
trigger: 'blur', |
||||
|
}, |
||||
|
], |
||||
|
address: [ |
||||
|
{ |
||||
|
required: true, |
||||
|
message: '请输入站点名称', |
||||
|
trigger: 'blur', |
||||
|
}, |
||||
|
], |
||||
|
}; |
||||
|
|
||||
|
// 下发类型
|
||||
|
export const PEND_TYPE = { |
||||
|
PENDING: { |
||||
|
type: 'primary', |
||||
|
text: '待下发', |
||||
|
}, |
||||
|
FAIL: { |
||||
|
type: 'danger', |
||||
|
text: '配置失败', |
||||
|
}, |
||||
|
SUCCESS: { |
||||
|
type: 'success', |
||||
|
text: '配置成功', |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
// 上报类型
|
||||
|
export const REPORT_TYPE = { |
||||
|
CYCLE: '周期上报', |
||||
|
POINT: '定时上报', |
||||
|
}; |
||||
|
|
||||
|
// 下发类型
|
||||
|
export const PENDING_TYPE = { |
||||
|
DATA: { text: '业务上报' }, |
||||
|
EVENT: { text: '事件上报' }, |
||||
|
}; |
||||
|
|
||||
|
// 实时查询数据的轮询时间 1分钟
|
||||
|
export const REALTIME_DATA_INTERVAL = 60000 * 5; |
||||
|
|
||||
|
// 设备状态 情景
|
||||
|
export const STATUS_COLOR = { |
||||
|
BROKEN: { |
||||
|
text: '故障', |
||||
|
color: '#FCA5A5', |
||||
|
}, |
||||
|
WARNING: { |
||||
|
text: '报警', |
||||
|
color: '#FCD34D', |
||||
|
}, |
||||
|
NORMAL: { |
||||
|
text: '正常', |
||||
|
color: '#BEF264', |
||||
|
}, |
||||
|
OFFLINE: { |
||||
|
text: '离线', |
||||
|
color: '#CBD5E1', |
||||
|
}, |
||||
|
ONLINE: { |
||||
|
text: '在线', |
||||
|
color: '#6EE7B7', |
||||
|
}, |
||||
|
}; |
@ -0,0 +1,110 @@ |
|||||
|
// 功能码
|
||||
|
export const CODE = { |
||||
|
BUSINESS_REPORT: { |
||||
|
text: '业务上报', |
||||
|
code: 0, |
||||
|
}, |
||||
|
EVENT_REPORT: { |
||||
|
text: '事件上报', |
||||
|
code: 1, |
||||
|
}, |
||||
|
NET_PARAM_SETTING_OR_READING: { |
||||
|
code: 2, |
||||
|
text: '联网参数设置或读取', |
||||
|
}, |
||||
|
FUNC_PARAM_SETTING_OR_READING: { |
||||
|
code: 3, |
||||
|
text: '功能参数设置或读取', |
||||
|
}, |
||||
|
READ_HISTORY_DATA: { |
||||
|
code: 4, |
||||
|
text: '读取历史日志记录', |
||||
|
}, |
||||
|
READ_HISTORY_EVENT: { |
||||
|
code: 5, |
||||
|
text: '读取历史事件记录', |
||||
|
}, |
||||
|
UPGRADE: { |
||||
|
code: 6, |
||||
|
text: '升级启动、传输、结束', |
||||
|
}, |
||||
|
ACK: { |
||||
|
code: 7, |
||||
|
text: '应答帧(固定帧)', |
||||
|
}, |
||||
|
OVER: { |
||||
|
code: 8, |
||||
|
text: '结束帧', |
||||
|
}, |
||||
|
RESERVED: { |
||||
|
code: 9, |
||||
|
text: '保留', |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
// 传输方向
|
||||
|
export const DIRECTION = { |
||||
|
IACD2SERVER: '上行', |
||||
|
SERVER2IACD: '下行', |
||||
|
}; |
||||
|
|
||||
|
// 传输启动
|
||||
|
export const PRM = { |
||||
|
ACK: '传输结束', |
||||
|
REQ: '传输启动', |
||||
|
}; |
||||
|
|
||||
|
// 流控
|
||||
|
export const DFC = { |
||||
|
CONTINUE: '有后续包', |
||||
|
OVER: '无后续包', |
||||
|
}; |
||||
|
|
||||
|
// 安全方式
|
||||
|
export const SER = { |
||||
|
OPEN: '不加密', |
||||
|
ENCRYPTION: '加密', |
||||
|
}; |
||||
|
|
||||
|
// 类型标识
|
||||
|
export const ASDU_TYPE = { |
||||
|
IACD: { |
||||
|
code: 1, |
||||
|
text: '大气腐蚀', |
||||
|
}, |
||||
|
OTHER: { |
||||
|
code: 2, |
||||
|
text: '其他', |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
// 信息体序
|
||||
|
export const INFO_ORDER = { |
||||
|
NoOrder: '无序', |
||||
|
HasOrder: '有序', |
||||
|
None: '无', |
||||
|
}; |
||||
|
|
||||
|
// 传输原因
|
||||
|
export const REASON = { |
||||
|
None: { |
||||
|
code: 0, |
||||
|
text: '无', |
||||
|
}, |
||||
|
ReportData: { |
||||
|
code: 1, |
||||
|
text: '业务主动上报', |
||||
|
}, |
||||
|
ReportEvent: { |
||||
|
code: 2, |
||||
|
text: '事件主动上报', |
||||
|
}, |
||||
|
History: { |
||||
|
code: 3, |
||||
|
text: '历史数据记录或程序数据', |
||||
|
}, |
||||
|
HumanOperator: { |
||||
|
code: 4, |
||||
|
text: '人工上报', |
||||
|
}, |
||||
|
}; |
@ -0,0 +1,19 @@ |
|||||
|
import { ref } from 'vue'; |
||||
|
|
||||
|
export default function useDeviceCreate() { |
||||
|
const display = ref(false); |
||||
|
|
||||
|
function hide() { |
||||
|
display.value = false; |
||||
|
} |
||||
|
|
||||
|
function show() { |
||||
|
display.value = true; |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
display, |
||||
|
hide, |
||||
|
show, |
||||
|
}; |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
import 'virtual:windi.css'; |
||||
|
import 'element-plus/dist/index.css'; |
||||
|
|
||||
|
import { createApp } from 'vue'; |
||||
|
import App from './App.vue'; |
||||
|
import router from './routers/index'; |
||||
|
import store from './store/index'; |
||||
|
|
||||
|
const app = createApp(App); |
||||
|
|
||||
|
app.use(router).use(store).mount('#app'); |
@ -0,0 +1,52 @@ |
|||||
|
import { createRouter, createWebHistory } from 'vue-router'; |
||||
|
// 还有 createWebHashHistory 和 createMemoryHistory
|
||||
|
|
||||
|
export const routes = [ |
||||
|
// {
|
||||
|
// path: '/store/overview',
|
||||
|
// name: 'overview',
|
||||
|
// meta: {
|
||||
|
// title: '设备概览',
|
||||
|
// icon: 'el-icon-data-board',
|
||||
|
// },
|
||||
|
// component: () => import('@/views/overview.vue'),
|
||||
|
// },
|
||||
|
]; |
||||
|
|
||||
|
export const user = [ |
||||
|
{ |
||||
|
path: '/store/user/signin', |
||||
|
name: 'signin', |
||||
|
meta: {}, |
||||
|
component: () => import('views/user/sign-in.vue'), |
||||
|
}, |
||||
|
{ |
||||
|
path: '/store/user/pw-change', |
||||
|
name: 'pw-change', |
||||
|
meta: {}, |
||||
|
component: () => import('views/user/pw-change.vue'), |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
const router = createRouter({ |
||||
|
history: createWebHistory(), |
||||
|
routes: [ |
||||
|
{ |
||||
|
path: '/', |
||||
|
redirect: '/store/user/signin', |
||||
|
}, |
||||
|
{ |
||||
|
path: '/store', |
||||
|
redirect: '/store/user/signin', |
||||
|
}, |
||||
|
{ |
||||
|
path: '/store/home', |
||||
|
name: 'home', |
||||
|
component: () => import('views/Index.vue'), |
||||
|
children: routes, |
||||
|
}, |
||||
|
...user, |
||||
|
], |
||||
|
}); |
||||
|
|
||||
|
export default router; |
@ -0,0 +1,14 @@ |
|||||
|
import { createStore } from 'vuex'; |
||||
|
import user from './user'; |
||||
|
|
||||
|
export default createStore({ |
||||
|
modules: { user }, |
||||
|
state: { menu: { show: true, collapse: false } }, |
||||
|
getters: {}, |
||||
|
mutations: { |
||||
|
toggleCollapse(state) { |
||||
|
state.menu.collapse = !state.menu.collapse; |
||||
|
}, |
||||
|
}, |
||||
|
actions: {}, |
||||
|
}); |
@ -0,0 +1,61 @@ |
|||||
|
import { getToken } from 'apis/index'; |
||||
|
|
||||
|
export default { |
||||
|
namespaced: true, |
||||
|
|
||||
|
state: { user: null }, |
||||
|
|
||||
|
getters: { |
||||
|
token({ user }) { |
||||
|
if (!user) return null; |
||||
|
return user.token; |
||||
|
}, |
||||
|
userId({ user }) { |
||||
|
if (!user) return null; |
||||
|
return user.id; |
||||
|
}, |
||||
|
account({ user }) { |
||||
|
if (!user) return null; |
||||
|
return user.account; |
||||
|
}, |
||||
|
isAdmin({ user }) { |
||||
|
if (!user) return false; |
||||
|
return user.account === 'iacdadmin'; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
mutations: { |
||||
|
/** |
||||
|
* 设置state.user |
||||
|
* @param {*} state |
||||
|
* @param {object|null} user 用户信息 |
||||
|
*/ |
||||
|
setUser(state, user) { |
||||
|
state.user = user; |
||||
|
if (user) { |
||||
|
sessionStorage.setItem('token', user.token); |
||||
|
sessionStorage.setItem('user', JSON.stringify(user)); |
||||
|
} else { |
||||
|
sessionStorage.removeItem('token'); |
||||
|
sessionStorage.removeItem('user'); |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
actions: { |
||||
|
/** |
||||
|
* 根据userId获取token级user信息 |
||||
|
* @param {*} param0 |
||||
|
* @param {string} userId 用户id |
||||
|
*/ |
||||
|
async getTokenByUserId({ commit }, userId) { |
||||
|
try { |
||||
|
const data = await getToken(userId); |
||||
|
commit('setUser', data || null); |
||||
|
return data; |
||||
|
} catch (error) { |
||||
|
throw new Error(error); |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
}; |
@ -0,0 +1,51 @@ |
|||||
|
import Axios from 'axios'; |
||||
|
import { ElMessage } from 'element-plus'; |
||||
|
import store from 'store'; |
||||
|
|
||||
|
const baseUrl = '/gateway'; |
||||
|
|
||||
|
const instance = Axios.create({ |
||||
|
baseUrl, |
||||
|
timeout: 20000, |
||||
|
}); |
||||
|
|
||||
|
// request
|
||||
|
instance.interceptors.request.use( |
||||
|
config => { |
||||
|
const token = store.getters['user/token'] || sessionStorage.getItem('token'); |
||||
|
if (token) { |
||||
|
config.headers.Authorization = `Bearer ${token}`; |
||||
|
} |
||||
|
return config; |
||||
|
}, |
||||
|
error => { |
||||
|
return Promise.reject(error); |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
// response
|
||||
|
instance.interceptors.response.use( |
||||
|
response => { |
||||
|
if (response.status !== 200 || !response.data) { |
||||
|
return Promise.reject(response.statusText); |
||||
|
} |
||||
|
const { code, data, msg } = response.data; |
||||
|
if (code === 200) { |
||||
|
return data; |
||||
|
} |
||||
|
return Promise.reject(msg); |
||||
|
}, |
||||
|
error => { |
||||
|
if (error.response && error.response.data) { |
||||
|
const code = error.response.status; |
||||
|
const msg = error.response.data.message; |
||||
|
ElMessage.error(`Code: ${code}, Message: ${msg}`); |
||||
|
console.error(`[Axios Error]`, error.response); |
||||
|
} else { |
||||
|
// ElMessage.error(`${error}`);
|
||||
|
} |
||||
|
return Promise.reject(error); |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
export default instance; |
@ -0,0 +1,128 @@ |
|||||
|
import { STATUS_COLOR } from '@/config/config'; |
||||
|
|
||||
|
/** |
||||
|
* 生成设备概览 数量数据 |
||||
|
* @param {object} count |
||||
|
* @param {number} count.online 在线数量 |
||||
|
* @param {number} count.offline 离线数量 |
||||
|
* @param {number} count.fault 故障数量 |
||||
|
* @param {number} count.warning 报警数量 |
||||
|
* @param {number} count.total 总设备数 |
||||
|
* @returns {object} {} |
||||
|
*/ |
||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||
|
export function generateChartOption(count) { |
||||
|
return { |
||||
|
tooltip: { trigger: 'item' }, |
||||
|
title: { |
||||
|
text: '设备总数', |
||||
|
top: '45%', |
||||
|
left: '50%', |
||||
|
textAlign: 'center', |
||||
|
textVerticalAlign: 'middle', |
||||
|
textStyle: { fontSize: 22 }, |
||||
|
subtext: count.total, |
||||
|
subtextStyle: { fontSize: 18 }, |
||||
|
}, |
||||
|
|
||||
|
series: [ |
||||
|
{ |
||||
|
name: '设备统计', |
||||
|
type: 'pie', |
||||
|
radius: ['70%', '95%'], |
||||
|
avoidLabelOverlap: false, |
||||
|
itemStyle: { |
||||
|
borderRadius: 10, |
||||
|
borderColor: '#fff', |
||||
|
borderWidth: 2, |
||||
|
}, |
||||
|
label: { |
||||
|
show: true, |
||||
|
position: 'inside', |
||||
|
fontSize: 17, |
||||
|
color: '#323232', |
||||
|
// formatter: '{b}\n{c}',
|
||||
|
lineHeight: 40, |
||||
|
}, |
||||
|
emphasis: { |
||||
|
label: { |
||||
|
show: true, |
||||
|
fontSize: 19, |
||||
|
fontWeight: 'bold', |
||||
|
}, |
||||
|
}, |
||||
|
labelLine: { show: false }, |
||||
|
data: [ |
||||
|
{ |
||||
|
value: count.online, |
||||
|
name: STATUS_COLOR.ONLINE.text, |
||||
|
itemStyle: { color: STATUS_COLOR.ONLINE.color }, |
||||
|
label: { show: count.online }, |
||||
|
}, |
||||
|
{ |
||||
|
value: count.offline, |
||||
|
name: STATUS_COLOR.OFFLINE.text, |
||||
|
itemStyle: { color: STATUS_COLOR.OFFLINE.color }, |
||||
|
label: { show: count.offline }, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
name: '设备统计', |
||||
|
type: 'pie', |
||||
|
radius: ['35%', '60%'], |
||||
|
avoidLabelOverlap: false, |
||||
|
itemStyle: { |
||||
|
borderRadius: 10, |
||||
|
borderColor: '#fff', |
||||
|
borderWidth: 2, |
||||
|
}, |
||||
|
label: { |
||||
|
show: true, |
||||
|
position: 'inside', |
||||
|
fontSize: 14, |
||||
|
color: '#323232', |
||||
|
// formatter: '{b}\n{c}',
|
||||
|
lineHeight: 40, |
||||
|
}, |
||||
|
emphasis: { |
||||
|
label: { |
||||
|
show: true, |
||||
|
fontSize: 16, |
||||
|
fontWeight: 'bold', |
||||
|
}, |
||||
|
}, |
||||
|
labelLine: { show: false }, |
||||
|
data: [ |
||||
|
{ |
||||
|
value: count.fault, |
||||
|
name: STATUS_COLOR.BROKEN.text, |
||||
|
itemStyle: { color: STATUS_COLOR.BROKEN.color }, |
||||
|
label: { show: count.fault }, |
||||
|
}, |
||||
|
{ |
||||
|
value: count.warning, |
||||
|
name: STATUS_COLOR.WARNING.text, |
||||
|
itemStyle: { color: STATUS_COLOR.WARNING.color }, |
||||
|
label: { show: count.warning }, |
||||
|
}, |
||||
|
{ |
||||
|
value: count.normal, |
||||
|
name: STATUS_COLOR.NORMAL.text, |
||||
|
itemStyle: { color: STATUS_COLOR.NORMAL.color }, |
||||
|
label: { show: count.normal }, |
||||
|
}, |
||||
|
{ |
||||
|
value: count.offline, |
||||
|
name: STATUS_COLOR.OFFLINE.text, |
||||
|
itemStyle: { |
||||
|
color: STATUS_COLOR.OFFLINE.color, |
||||
|
opacity: 0, |
||||
|
}, |
||||
|
label: { show: count.offline }, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
], |
||||
|
}; |
||||
|
} |
@ -0,0 +1,181 @@ |
|||||
|
/* eslint-disable max-len,object-curly-newline */ |
||||
|
import dayjs from 'dayjs'; |
||||
|
import max from 'lodash/max'; |
||||
|
import isNaN from 'lodash/isNaN'; |
||||
|
import { colors, generateDefaultSeries, itemColor, legendData, yAxisData } from '@/config/chart'; |
||||
|
|
||||
|
/** |
||||
|
* 生成chart所需参数 |
||||
|
* @param {Object[]} data 服务端返回数据 |
||||
|
* @param {string} data[].time 时间 ms |
||||
|
* @param {string} data[].so2 SO2 |
||||
|
* @param {string} data[].saltR 盐阻 |
||||
|
* @param {string} data[].saltT 盐温 |
||||
|
* @param {string} data[].environmentTemperature 环温 |
||||
|
* @param {string} data[].environmentHumidity 环湿 |
||||
|
* @param {string} data[].corrosion1 锌 |
||||
|
* @param {string} data[].corrosion2 铜 |
||||
|
* @param {string} data[].corrosion3 铝 |
||||
|
* @param {string} data[].corrosion4 钢 |
||||
|
* @returns {{environmentTemperature: *[], corrosionXIN: *[], corrosionGANG: *[], corrosionTONG: *[], so2: *[], corrosionLV: *[], time: *[], saltT: *[], saltR: *[], environmentHumidity: *[]}} |
||||
|
*/ |
||||
|
function generateParams(data) { |
||||
|
const result = { |
||||
|
time: [], |
||||
|
so2: [], |
||||
|
saltR: [], |
||||
|
saltT: [], |
||||
|
environmentTemperature: [], |
||||
|
environmentHumidity: [], |
||||
|
corrosionXIN: [], |
||||
|
corrosionTONG: [], |
||||
|
corrosionLV: [], |
||||
|
corrosionGANG: [], |
||||
|
}; |
||||
|
data.forEach(item => { |
||||
|
result.time.push(dayjs(new Date(+item.time)).format('YY/MM/DD HH:mm')); |
||||
|
!isNaN(+item.so2) ? result.so2.push(+item.so2) : result.so2.push(''); |
||||
|
!isNaN(+item.saltR) ? result.saltR.push(+item.saltR) : result.saltR.push(''); |
||||
|
!isNaN(+item.saltT) ? result.saltT.push(+item.saltT) : result.saltT.push(''); |
||||
|
!isNaN(+item.environmentTemperature) |
||||
|
? result.environmentTemperature.push(+item.environmentTemperature) |
||||
|
: result.environmentTemperature.push(''); |
||||
|
!isNaN(+item.environmentHumidity) ? result.environmentHumidity.push(+item.environmentHumidity) : result.environmentHumidity.push(''); |
||||
|
!isNaN(+item.corrosion1) ? result.corrosionXIN.push(+item.corrosion1) : result.corrosionXIN.push(''); |
||||
|
!isNaN(+item.corrosion2) ? result.corrosionTONG.push(+item.corrosion2) : result.corrosionTONG.push(''); |
||||
|
!isNaN(+item.corrosion3) ? result.corrosionLV.push(+item.corrosion3) : result.corrosionLV.push(''); |
||||
|
!isNaN(+item.corrosion4) ? result.corrosionGANG.push(+item.corrosion4) : result.corrosionGANG.push(''); |
||||
|
}); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 计算Y轴的显示 |
||||
|
* @param {string} yName Y轴的name |
||||
|
* @param {Object} selectedLegend legends |
||||
|
* @returns {boolean} |
||||
|
*/ |
||||
|
export function computeYAxisShow(yName, selectedLegend) { |
||||
|
// eslint-disable-next-line guard-for-in,no-restricted-syntax
|
||||
|
for (const key in selectedLegend) { |
||||
|
if (key.includes(yName) && selectedLegend[key]) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生产y轴内容 |
||||
|
* @param {Object} selectedLegend |
||||
|
* @returns {({axisLabel: {formatter: string}, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string}|{axisLabel: {formatter: string}, offset: number, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string}|{axisLabel: {formatter: string}, offset: number, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string}|{axisLabel: {formatter: string}, offset: number, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string}|{axisLabel: {formatter: string}, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string})[]} |
||||
|
*/ |
||||
|
export function generateYAxis(selectedLegend) { |
||||
|
let leftIndex = 0; |
||||
|
let rightIndex = 0; |
||||
|
yAxisData.forEach(item => { |
||||
|
item.show = computeYAxisShow(item.name, selectedLegend); |
||||
|
if (item.show) { |
||||
|
if (item.position === 'left') { |
||||
|
item.offset = 100 * leftIndex; |
||||
|
leftIndex += 1; |
||||
|
} else { |
||||
|
item.offset = 80 * rightIndex; |
||||
|
rightIndex += 1; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
return yAxisData; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成series数据 |
||||
|
* @param {Object} data |
||||
|
* @param {Object[]} yAxis |
||||
|
* @returns {({data: ([]|number|string|*), name: string, type: string}|{data: ([]|number|BufferSource|string|*), name: string, type: string, yAxisIndex: number}|{data: ([]|string|*), name: string, type: string, yAxisIndex: number}|{data: ([]|string|*), name: string, type: string, yAxisIndex: number}|{data: [], name: string, type: string, yAxisIndex: number})[]|*[]} |
||||
|
*/ |
||||
|
function generateSeries(data, yAxis) { |
||||
|
const seriesArr = generateDefaultSeries(data); |
||||
|
const showArr = seriesArr.filter(item => yAxis.find(y => y.name === item.name)); |
||||
|
const hideArr = seriesArr.filter(item => !yAxis.find(y => y.name === item.name)); |
||||
|
const result = [...showArr, ...hideArr]; |
||||
|
result.forEach(item => { |
||||
|
item.itemStyle = { color: itemColor[item.name] }; |
||||
|
if (item.name.includes('电流')) { |
||||
|
item.yAxisIndex = 0; |
||||
|
} else if (item.name.includes('温度')) { |
||||
|
item.yAxisIndex = 1; |
||||
|
} else if (item.name.includes('湿度')) { |
||||
|
item.yAxisIndex = 2; |
||||
|
} else if (item.name.includes('SO2')) { |
||||
|
item.yAxisIndex = 3; |
||||
|
} else if (item.name.includes('阻抗')) { |
||||
|
item.yAxisIndex = 4; |
||||
|
} |
||||
|
}); |
||||
|
return result || []; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 计算图表grid left right值 |
||||
|
* @param {Object[]} yAxis |
||||
|
* @returns {{left: number, right: number}} |
||||
|
*/ |
||||
|
function generateGrid(yAxis) { |
||||
|
const left = []; |
||||
|
const right = []; |
||||
|
yAxis.forEach(item => { |
||||
|
if (item.show) { |
||||
|
if (item.position === 'left') { |
||||
|
left.push(item.offset || 0); |
||||
|
} else { |
||||
|
right.push(item.offset || 0); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
return { |
||||
|
left: max(left) + 100, |
||||
|
right: max(right) + 80, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成chart参数 |
||||
|
* @param {Object[]} rawData 返回段返回的data数据 |
||||
|
* @param {Object} selected 选中的legend |
||||
|
* @returns {{yAxis: ({axisLabel: {formatter: string}, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string}|{axisLabel: {formatter: string}, offset: number, axisLine: {lineStyle: {color: string}, show: boolean}, name: string, position: string, type: string})[], xAxis: [{data: *[], axisTick: {alignWithLabel: boolean}, type: string}], color: [string,string,string,string,string,string,string,string,string], grid: {left: number, right: number}, legend: {data: [string,string,string,string,string,string,string,string,string], type: string, selected}, series: (({data: ([]|number|string|*), name: string, type: string}|{data: ([]|number|BufferSource|string|*), name: string, type: string, yAxisIndex: number}|{data: ([]|string|*), name: string, type: string, yAxisIndex: number}|{data: [], name: string, type: string, yAxisIndex: number})[]|*[]), tooltip: {axisPointer: {type: string, snap: boolean}, trigger: string}, dataZoom: [{type: string},{type: string}]}} |
||||
|
*/ |
||||
|
export function generateChartOption(rawData, selected) { |
||||
|
const data = generateParams(rawData); |
||||
|
const yAxis = generateYAxis(selected); |
||||
|
const series = generateSeries(data, yAxis); |
||||
|
const grid = generateGrid(yAxis); |
||||
|
const option = { |
||||
|
color: colors, |
||||
|
tooltip: { |
||||
|
trigger: 'axis', |
||||
|
axisPointer: { |
||||
|
type: 'cross', |
||||
|
snap: true, |
||||
|
}, |
||||
|
}, |
||||
|
grid, |
||||
|
legend: { |
||||
|
type: 'scroll', |
||||
|
right: 140, |
||||
|
selected, |
||||
|
data: legendData, |
||||
|
}, |
||||
|
dataZoom: [{ type: 'inside' }, { type: 'slider' }], |
||||
|
xAxis: [ |
||||
|
{ |
||||
|
type: 'category', |
||||
|
axisTick: { alignWithLabel: true }, |
||||
|
data: data.time, |
||||
|
}, |
||||
|
], |
||||
|
yAxis, |
||||
|
series, |
||||
|
}; |
||||
|
return option; |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
import dayjs from 'dayjs'; |
||||
|
|
||||
|
/** |
||||
|
* 格式化时间 |
||||
|
* @param {string | number} time 时间戳字符串或数字 |
||||
|
* @param {string} formatStyle |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function formatMsTime(time, formatStyle = 'YYYY-MM-DD HH:mm:ss') { |
||||
|
return dayjs(new Date(+time)).format(formatStyle); |
||||
|
} |
||||
|
|
||||
|
// 图表时间轴的时间格式化
|
||||
|
export function formatChartTime(time) { |
||||
|
return formatMsTime(time, 'MM/DD HH:mm'); |
||||
|
} |
@ -0,0 +1,3 @@ |
|||||
|
<template> |
||||
|
index.vue |
||||
|
</template> |
@ -0,0 +1,114 @@ |
|||||
|
<template> |
||||
|
<div class="wrap"> |
||||
|
<div class="content border rounded-md"> |
||||
|
<h1 class="text-2xl text-center font-bold mb-10 text-gray-700">智能大气腐蚀监测云平台</h1> |
||||
|
<el-form ref="passwordChangeFormRef" :model="passwordChangeForm" :rules="rules" label-width="70px" status-icon> |
||||
|
<el-form-item class="mb-3" label="账号" prop="account" style="margin-bottom: 22px !important"> |
||||
|
<el-input v-model="passwordChangeForm.account" type="text"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="旧密码" prop="passwordOld" style="margin-bottom: 22px !important"> |
||||
|
<el-input v-model="passwordChangeForm.passwordOld" type="password"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="新密码" prop="passwordNew" style="margin-bottom: 22px !important"> |
||||
|
<el-input v-model="passwordChangeForm.passwordNew" type="password"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item> |
||||
|
<el-button class="float-left" style="display: block; margin-bottom: 22px !important" type="primary" @click="onSubmit"> |
||||
|
确认修改 |
||||
|
</el-button> |
||||
|
<el-button class="float-right" type="text"> |
||||
|
<router-link to="signin">返回登录</router-link> |
||||
|
</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { reactive, ref } from 'vue'; |
||||
|
import { changePassword } from 'apis'; |
||||
|
import { ElMessage } from 'element-plus'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import { useRouter } from 'vue-router'; |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const router = useRouter(); |
||||
|
const passwordChangeFormRef = ref(null); |
||||
|
const passwordChangeForm = reactive({ |
||||
|
account: '', |
||||
|
passwordOld: '', |
||||
|
passwordNew: '', |
||||
|
}); |
||||
|
|
||||
|
const rules = reactive({ |
||||
|
account: [ |
||||
|
{ |
||||
|
required: true, |
||||
|
message: '请输入账号', |
||||
|
trigger: 'blur', |
||||
|
}, |
||||
|
], |
||||
|
passwordOld: [ |
||||
|
{ |
||||
|
required: true, |
||||
|
message: '请输入旧密码', |
||||
|
trigger: 'blur', |
||||
|
}, |
||||
|
], |
||||
|
passwordNew: [ |
||||
|
{ |
||||
|
required: true, |
||||
|
message: '请输入新密码', |
||||
|
trigger: 'blur', |
||||
|
}, |
||||
|
], |
||||
|
}); |
||||
|
|
||||
|
// 提交 |
||||
|
function onSubmit() { |
||||
|
passwordChangeFormRef.value.validate(async valid => { |
||||
|
if (valid) { |
||||
|
try { |
||||
|
const params = { ...passwordChangeForm }; |
||||
|
const resData = await changePassword(params); |
||||
|
store.commit('user/setUser', resData); |
||||
|
ElMessage.success('修改成功'); |
||||
|
setTimeout(() => { |
||||
|
// 跳转到首页 |
||||
|
router.push({ name: 'signin' }); |
||||
|
}, 1000); |
||||
|
} catch (error) { |
||||
|
ElMessage.error(error); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.wrap { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
overflow: hidden; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
background: url('@/assets/u1.jpg') no-repeat center center; |
||||
|
background-size: cover; |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
width: 480px; |
||||
|
padding: 30px; |
||||
|
background: #fff; |
||||
|
} |
||||
|
|
||||
|
.content :deep(.el-form-item) { |
||||
|
display: flex; |
||||
|
margin-bottom: 22px !important; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,106 @@ |
|||||
|
<template> |
||||
|
<div class="wrap"> |
||||
|
<div class="content border rounded-md"> |
||||
|
<h1 class="text-2xl text-center font-bold mb-10 text-gray-700">时物链条插件商城</h1> |
||||
|
<el-form ref="signInFormRef" :model="signInForm" :rules="rules" label-width="60px" status-icon> |
||||
|
<el-form-item class="mb-3" label="账号" prop="username" style="margin-bottom: 22px !important"> |
||||
|
<el-input v-model="signInForm.username" type="text"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item label="密码" prop="password" style="margin-bottom: 22px !important"> |
||||
|
<el-input v-model="signInForm.password" type="password"></el-input> |
||||
|
</el-form-item> |
||||
|
|
||||
|
<el-form-item> |
||||
|
<el-button class="float-left" style="display: block; margin-bottom: 22px !important" type="primary" @click="onSubmit"> |
||||
|
立即登录 |
||||
|
</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { reactive, ref } from 'vue'; |
||||
|
import { signIn } from 'apis'; |
||||
|
import { ElMessage } from 'element-plus'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import { useRouter } from 'vue-router'; |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const router = useRouter(); |
||||
|
const signInFormRef = ref(null); |
||||
|
const signInForm = reactive({ |
||||
|
username: '', |
||||
|
password: '', |
||||
|
}); |
||||
|
|
||||
|
const rules = reactive({ |
||||
|
username: [ |
||||
|
{ |
||||
|
required: true, |
||||
|
message: '请输入账号', |
||||
|
trigger: 'blur', |
||||
|
}, |
||||
|
], |
||||
|
password: [ |
||||
|
{ |
||||
|
required: true, |
||||
|
message: '请输入密码', |
||||
|
trigger: 'blur', |
||||
|
}, |
||||
|
], |
||||
|
}); |
||||
|
|
||||
|
// 提交 |
||||
|
function onSubmit() { |
||||
|
signInFormRef.value.validate(async valid => { |
||||
|
if (valid) { |
||||
|
try { |
||||
|
const params = { |
||||
|
client: 1, |
||||
|
data: { |
||||
|
identifier: signInForm.username, |
||||
|
credential: signInForm.password, |
||||
|
}, |
||||
|
type: 3, |
||||
|
}; |
||||
|
const resData = await signIn(params); |
||||
|
store.commit('user/setUser', resData); |
||||
|
ElMessage.success('登录成功, 欢迎回来'); |
||||
|
setTimeout(() => { |
||||
|
// 跳转到首页 |
||||
|
router.push({ name: 'home' }); |
||||
|
}, 1000); |
||||
|
} catch (error) { |
||||
|
ElMessage.error(error); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.wrap { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
overflow: hidden; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
background: url('@/assets/u1.jpg') no-repeat center center; |
||||
|
background-size: cover; |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
width: 480px; |
||||
|
padding: 30px; |
||||
|
background: #fff; |
||||
|
} |
||||
|
|
||||
|
.content :deep(.el-form-item) { |
||||
|
display: flex; |
||||
|
margin-bottom: 22px !important; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,39 @@ |
|||||
|
import Components from 'unplugin-vue-components/vite'; |
||||
|
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; |
||||
|
import { VitePWA } from 'vite-plugin-pwa'; |
||||
|
import WindiCSS from 'vite-plugin-windicss'; |
||||
|
import { defineConfig } from 'vite'; |
||||
|
import path from 'path'; |
||||
|
import viteCompression from 'vite-plugin-compression'; |
||||
|
import vue from '@vitejs/plugin-vue'; |
||||
|
|
||||
|
const resolve = dir => path.join(__dirname, dir); |
||||
|
|
||||
|
// https://vitejs.dev/config/
|
||||
|
export default defineConfig({ |
||||
|
base: '/store/', |
||||
|
plugins: [vue(), VitePWA(), WindiCSS(), Components({ resolvers: [ElementPlusResolver()] }), viteCompression()], |
||||
|
resolve: { |
||||
|
alias: { |
||||
|
'~': __dirname, |
||||
|
'@': resolve('src'), |
||||
|
views: resolve('src/views'), |
||||
|
components: resolve('src/components'), |
||||
|
assets: resolve('src/assets'), |
||||
|
utils: resolve('src/utils'), |
||||
|
store: resolve('src/store'), |
||||
|
apis: resolve('src/apis'), |
||||
|
}, |
||||
|
}, |
||||
|
build: { |
||||
|
rollupOptions: { |
||||
|
output: { |
||||
|
manualChunks(id) { |
||||
|
if (id.includes('node_modules')) { |
||||
|
return id.toString().split('node_modules/')[1].split('/')[0].toString(); |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}); |
File diff suppressed because it is too large
Loading…
Reference in new issue