feat(web): 添加 sm-crypto 库支持 SM3 加密及导出功能

- 在登录模块中增加 SM3 加密调用逻辑
- 新增导出按钮和相关处理逻辑,支持统计分析数据导出
- 调整多个页面样式布局,优化组件结构与代码可读性
- 更新设备类型配置,新增“冷机”和“网关”类型
- 添加 download 工具函数,支持通过 URL 下载文件
- 配置 webpack 代理,支持文件下载路径转发
```
This commit is contained in:
zhoumengru
2025-09-23 14:19:36 +08:00
parent ee98556eec
commit 65d1ad93ef
15 changed files with 192 additions and 93 deletions

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>