mirror of
https://gitee.com/js-yhsec/energy_storage.git
synced 2026-05-27 18:59:26 +08:00
```
feat(web): 添加 sm-crypto 库支持 SM3 加密及导出功能 - 在登录模块中增加 SM3 加密调用逻辑 - 新增导出按钮和相关处理逻辑,支持统计分析数据导出 - 调整多个页面样式布局,优化组件结构与代码可读性 - 更新设备类型配置,新增“冷机”和“网关”类型 - 添加 download 工具函数,支持通过 URL 下载文件 - 配置 webpack 代理,支持文件下载路径转发 ```
This commit is contained in:
16
web/package-lock.json
generated
16
web/package-lock.json
generated
@@ -14,6 +14,7 @@
|
||||
"echarts": "^6.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"qs": "^6.12.3",
|
||||
"sm-crypto": "^0.3.13",
|
||||
"vue": "^3.2.13",
|
||||
"vue-router": "^4.0.3",
|
||||
"vuex": "^4.0.0"
|
||||
@@ -9791,6 +9792,12 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jsbn": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz",
|
||||
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jsesc": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz",
|
||||
@@ -13633,6 +13640,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/sm-crypto": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmmirror.com/sm-crypto/-/sm-crypto-0.3.13.tgz",
|
||||
"integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jsbn": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sockjs": {
|
||||
"version": "0.3.24",
|
||||
"resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"echarts": "^6.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"qs": "^6.12.3",
|
||||
"sm-crypto": "^0.3.13",
|
||||
"vue": "^3.2.13",
|
||||
"vue-router": "^4.0.3",
|
||||
"vuex": "^4.0.0"
|
||||
|
||||
@@ -192,11 +192,12 @@ input:-internal-autofill-selected {
|
||||
color: var(--theme-text-default) !important;
|
||||
}
|
||||
.search {
|
||||
height:70px;
|
||||
// height:70px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// flex-direction: column;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
.page-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -321,17 +322,17 @@ input:-internal-autofill-selected {
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
// margin-bottom: 15px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.bottom {
|
||||
display: flex;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
// display: flex;
|
||||
// margin-top: 20px;
|
||||
// margin-bottom: 20px;
|
||||
// justify-content: space-between;
|
||||
// align-items: center;
|
||||
.button {
|
||||
// margin-left: 10px;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ const btnList = [
|
||||
{ label: '新增', type: 'add', disFlag: 'is_add', icon: 'icon-add' },
|
||||
{ label: '查看', type: 'read', disFlag: 'is_view' },
|
||||
{ label: '修改', type: 'edit', disFlag: 'is_edit' },
|
||||
{ label: '删除', type: 'del', disFlag: 'is_del', icon: 'icon-del' }
|
||||
{ label: '删除', type: 'del', disFlag: 'is_del', icon: 'icon-del' },
|
||||
{ label: '导出', type: 'output', disFlag: 'is_edit', icon: 'icon-add' }
|
||||
]
|
||||
function findNodeByRoute(tree, targetRoute) {
|
||||
for (const node of tree) {
|
||||
|
||||
@@ -80,6 +80,16 @@ export const deviceTypeList = [
|
||||
label: '视频监控',
|
||||
iconfont: 'icon-shipinjiankong'
|
||||
},
|
||||
{
|
||||
value: '14',
|
||||
label: '冷机',
|
||||
iconfont: 'icon-lengjitubiao'
|
||||
},
|
||||
{
|
||||
value: '15',
|
||||
label: '网关',
|
||||
iconfont: 'icon-wangguan'
|
||||
},
|
||||
{
|
||||
value: '100',
|
||||
label: '储能预制舱',
|
||||
|
||||
16
web/src/utils/download.js
Normal file
16
web/src/utils/download.js
Normal file
@@ -0,0 +1,16 @@
|
||||
export function downloadByUrl(file) {
|
||||
fetch(file.url)
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.style.display = 'none'
|
||||
a.href = url
|
||||
// 设置下载的文件名
|
||||
a.download = file.name
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
})
|
||||
.catch((error) => console.error('Error downloading the file:', error))
|
||||
}
|
||||
@@ -38,8 +38,10 @@
|
||||
</a-config-provider>
|
||||
</template>
|
||||
<script>
|
||||
const {sm3} = require('sm-crypto')
|
||||
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
|
||||
import { postReq,getReq } from '@/request/api.js'
|
||||
|
||||
export default {
|
||||
name: 'LoginView',
|
||||
components: {
|
||||
@@ -69,11 +71,20 @@ export default {
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// const hashData = sm3('123456');
|
||||
// console.log(hashData); // 输出SM3哈希值
|
||||
},
|
||||
methods: {
|
||||
async login() {
|
||||
try {
|
||||
const values = await this.$refs.ruleForm.validateFields()
|
||||
const res = await getReq('/login',this.form )
|
||||
const newForm={
|
||||
...this.form,
|
||||
// passwd:sm3(this.form.passwd)
|
||||
}
|
||||
|
||||
const res = await getReq('/login',newForm )
|
||||
this.loading = false
|
||||
|
||||
|
||||
|
||||
@@ -1,57 +1,63 @@
|
||||
<template>
|
||||
<div class="statisicalAn">
|
||||
<div style="display: flex; justify-content: space-between; height: 50px">
|
||||
<div class="tab-header">
|
||||
<div v-for="item in tabList" :key="item.key" class="tab">
|
||||
<span
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
<div class="header">
|
||||
<div
|
||||
v-for="item in tabList"
|
||||
:key="item.key"
|
||||
class="tab-item"
|
||||
:class="[activeKey == item.key ? 'actived' : 'uactived']"
|
||||
@click="activeKey = item.key"
|
||||
>{{ item.name }}</span
|
||||
>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<searchBox
|
||||
class="searchBox"
|
||||
ref="searchBox"
|
||||
:btn-option-list="[]"
|
||||
:search-options="searchOptions"
|
||||
:title-option="{ title: '', info: '' }"
|
||||
@onSearch="onSearch"
|
||||
>
|
||||
<template #stationSelect="">
|
||||
<a-select
|
||||
style="width: 200px"
|
||||
:dropdown-match-select-width="false"
|
||||
v-model:value="stationId"
|
||||
allow-clear
|
||||
@change="changeStation"
|
||||
>
|
||||
<a-select-option
|
||||
:value="option.value"
|
||||
v-for="option in stationList"
|
||||
:key="option.value"
|
||||
<searchBox
|
||||
class="searchBox"
|
||||
ref="searchBox"
|
||||
:btn-option-list="btnOptionList"
|
||||
:search-options="searchOptions"
|
||||
:title-option="{ title: '', info: '' }"
|
||||
@onSearch="onSearch"
|
||||
@operateForm="operateForm"
|
||||
>
|
||||
<template #stationSelect="">
|
||||
<a-select
|
||||
style="width: 200px"
|
||||
:dropdown-match-select-width="false"
|
||||
v-model:value="stationId"
|
||||
allow-clear
|
||||
@change="changeStation"
|
||||
>
|
||||
{{ option.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</searchBox>
|
||||
</div>
|
||||
<div class="main_content">
|
||||
<a-spin :spinning="loading.chart || loading.table">
|
||||
<energyEchart
|
||||
:key="`${activeKey}_${renderKey}`"
|
||||
:chart-options="echartsInfo[activeKey].chartOptions"
|
||||
:chart-data="echartsInfo[activeKey].chartData"
|
||||
:chart-datav="echartsInfo[activeKey].chartDatav"
|
||||
:columns="tableList[activeKey].columns"
|
||||
:page-option="tableList[activeKey].pageOption"
|
||||
:table-info="tableList[activeKey].tableInfo"
|
||||
:table-data="tableList[activeKey].tableData"
|
||||
@pagesizeChange="pagesizeChange"
|
||||
></energyEchart>
|
||||
</a-spin>
|
||||
</div>
|
||||
<a-select-option
|
||||
:value="option.value"
|
||||
v-for="option in stationList"
|
||||
:key="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<!-- <a-range-picker v-model:value="formData[item.key]" value-format="YYYY-MM-DD" @change="$emit('onSearch', formData)" /> -->
|
||||
</template>
|
||||
</searchBox>
|
||||
</div>
|
||||
<div class="main_content">
|
||||
<a-spin :spinning="loading.chart || loading.table">
|
||||
<energyEchart
|
||||
:key="`${activeKey}_${renderKey}`"
|
||||
:chart-options="echartsInfo[activeKey].chartOptions"
|
||||
:chart-data="echartsInfo[activeKey].chartData"
|
||||
:chart-datav="echartsInfo[activeKey].chartDatav"
|
||||
:columns="tableList[activeKey].columns"
|
||||
:page-option="tableList[activeKey].pageOption"
|
||||
:table-info="tableList[activeKey].tableInfo"
|
||||
:table-data="tableList[activeKey].tableData"
|
||||
@pagesizeChange="pagesizeChange"
|
||||
>
|
||||
</energyEchart>
|
||||
</a-spin>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -59,15 +65,14 @@
|
||||
import energyEchart from '@/components/statisticalAnalysis/energyEchart.vue'
|
||||
import searchBox from '@/components/SearchBox.vue'
|
||||
import { postReq, getReq } from '@/request/api'
|
||||
import { getRunDays, getDateDaysAgo } from '@/utils/dealWithData'
|
||||
import { contentQuotesLinter } from 'ant-design-vue/es/_util/cssinjs/linters'
|
||||
|
||||
import {downloadByUrl} from '@/utils/download'
|
||||
export default {
|
||||
name: 'StatisicalAnView',
|
||||
components: { energyEchart, searchBox },
|
||||
|
||||
data() {
|
||||
return {
|
||||
btnOptionList: [],
|
||||
loading: {
|
||||
chart: false,
|
||||
table: false
|
||||
@@ -493,6 +498,7 @@ export default {
|
||||
},
|
||||
async mounted() {
|
||||
// 优先加载第一个页面(activeKey=1)所需的数据
|
||||
this.btnOptionList = this.$getBtns(['导出'])
|
||||
await Promise.all([
|
||||
this.getStationList(),
|
||||
this.getEchartsListForActiveKey(),
|
||||
@@ -506,6 +512,34 @@ export default {
|
||||
clearInterval(this.interval) // 组件销毁时清除定时器
|
||||
},
|
||||
methods: {
|
||||
operateForm(type, record = {}) {
|
||||
if (type == 'output') {
|
||||
this.loading.chart=true
|
||||
this.handleOutput()
|
||||
}
|
||||
},
|
||||
async handleOutput() {
|
||||
|
||||
|
||||
const { formData:{time=[]} } = this.$refs.searchBox
|
||||
|
||||
const params = {
|
||||
station_id: this.stationId,
|
||||
category: this.activeKey,
|
||||
start_date: time[0] || '',
|
||||
end_date: time[1] || ''
|
||||
}
|
||||
try {
|
||||
const res = await getReq('/exportStatReport', params)
|
||||
if(res.errcode==0){
|
||||
window.open('/download/'+res.data,'_parent');
|
||||
this.loading.chart=false
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
},
|
||||
forceRerender() {
|
||||
this.renderKey += 1
|
||||
},
|
||||
@@ -521,10 +555,11 @@ export default {
|
||||
Object.keys(this.tableList).forEach((key) => {
|
||||
if (key != this.activeKey) {
|
||||
this.tableList[key].tableData = []
|
||||
this.tableList[key].pageOption={
|
||||
this.tableList[key].pageOption = {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
count: 1 }
|
||||
count: 1
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -579,9 +614,9 @@ export default {
|
||||
}
|
||||
} catch (error) {
|
||||
this.tableList[this.activeKey].tableData = []
|
||||
this.tableList[this.activeKey].pageOption.count = 0
|
||||
this.tableList[this.activeKey].pageOption.page = 0
|
||||
this.tableList[this.activeKey].pageOption.pageSize = 0
|
||||
this.tableList[this.activeKey].pageOption.count = 0
|
||||
this.tableList[this.activeKey].pageOption.page = 0
|
||||
this.tableList[this.activeKey].pageOption.pageSize = 0
|
||||
this.loading.table = false
|
||||
}
|
||||
},
|
||||
@@ -667,19 +702,19 @@ export default {
|
||||
padding: 20px;
|
||||
background: $bg1-color;
|
||||
border-radius: 15px;
|
||||
.tab-header {
|
||||
.header {
|
||||
display: flex;
|
||||
|
||||
.tab {
|
||||
& > span {
|
||||
font-size: 14px;
|
||||
margin-right: 15px;
|
||||
display: inline-block;
|
||||
padding: 10px 50px;
|
||||
cursor: pointer;
|
||||
border: 1px solid $tab-border;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.tab-item {
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
font-size: 14px;
|
||||
margin-right: 15px;
|
||||
display: inline-block;
|
||||
padding: 0 50px;
|
||||
cursor: pointer;
|
||||
border: 1px solid $tab-border;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,7 +730,7 @@ export default {
|
||||
}
|
||||
.main_content {
|
||||
overflow: scroll;
|
||||
height: calc(100% - 30px);
|
||||
height: calc(100% - 40px);
|
||||
// margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -51,7 +51,7 @@ import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
|
||||
import { createVNode } from 'vue'
|
||||
|
||||
import searchBox from '@/components/SearchBox.vue'
|
||||
import { deviceTypeList } from '@/utils/config'
|
||||
import { deviceTypeList } from '@/utils/config.js'
|
||||
export default {
|
||||
name: '',
|
||||
components: {
|
||||
@@ -112,8 +112,9 @@ export default {
|
||||
},
|
||||
//获取设备类型
|
||||
getType(type) {
|
||||
const deviceType = this.deviceTypeList.find((item) => item.value == type).label || ''
|
||||
return deviceType
|
||||
const device = this.deviceTypeList.find((item) => item.value == type) || null
|
||||
if (device) return device.label
|
||||
return type
|
||||
},
|
||||
async getList() {
|
||||
this.$refs.comTable.loading = true
|
||||
@@ -223,11 +224,11 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
.device {
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
padding: 20px;
|
||||
|
||||
.content-table {
|
||||
width: 100%;
|
||||
height: calc(100% - 90px);
|
||||
height: calc(100% - 52px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -146,7 +146,6 @@ export default {
|
||||
console.log(data)
|
||||
},
|
||||
operateForm(type, record = {}) {
|
||||
console.log(type, record)
|
||||
|
||||
this.formStatus = type
|
||||
switch (type) {
|
||||
@@ -232,10 +231,10 @@ export default {
|
||||
.policy {
|
||||
// width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
padding: 20px;
|
||||
.content-table {
|
||||
width: 100%;
|
||||
height: calc(100% - 90px);
|
||||
height: calc(100% - 52px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -375,9 +375,9 @@ export default {
|
||||
.role {
|
||||
height: 100%;
|
||||
|
||||
padding: 0 20px;
|
||||
padding: 20px;
|
||||
.content-table {
|
||||
height: calc(100% - 92px);
|
||||
height: calc(100% - 52px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -243,9 +243,9 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
.service {
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
padding: 20px;
|
||||
.content-table {
|
||||
height: calc(100% - 92px);
|
||||
height: calc(100% - 52px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -277,9 +277,9 @@ export default {
|
||||
.station {
|
||||
height: 100%;
|
||||
|
||||
padding: 0 20px;
|
||||
padding: 20px;
|
||||
.content-table {
|
||||
height: calc(100% - 92px);
|
||||
height: calc(100% - 52px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -211,9 +211,9 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
.user {
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
padding: 20px;
|
||||
.content-table {
|
||||
height: calc(100% - 92px);
|
||||
height: calc(100% - 52px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -19,6 +19,14 @@ module.exports = defineConfig({
|
||||
pathRewrite: {
|
||||
'^/api': '' // 重写路径,去掉 '/api' 前缀
|
||||
}
|
||||
},
|
||||
'/download': {
|
||||
// 代理前缀,可以自定义(如 '/api')
|
||||
target: 'http://192.168.0.187:19600', // 目标服务器地址
|
||||
changeOrigin: true, // 是否改变请求源(跨域必备)
|
||||
pathRewrite: {
|
||||
'^/download': '/download' // 重写路径,去掉 '/api' 前缀
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user