birt
2025-04-13 b0530ed9211230227a8f94e394eda779d5ae5fc1
birtzhang
已添加13个文件
已重命名1个文件
已修改94个文件
2855 ■■■■ 文件已修改
README.md 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-admin/src/main/java/com/zhitan/AdminApplication.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-admin/src/main/java/com/zhitan/AdminServletInitializer.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-admin/src/main/java/com/zhitan/web/controller/branchanalysis/BranchEnergyAnalysisController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-admin/src/main/java/com/zhitan/web/controller/carbonemission/CarbonEmissionController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-admin/src/main/java/com/zhitan/web/controller/homepage/HomePageController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-admin/src/main/java/com/zhitan/web/controller/model/ModelNodeController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-admin/src/main/java/com/zhitan/web/controller/system/SysLoginController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/annotation/Anonymous.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/annotation/DataScope.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/annotation/DataSource.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/annotation/Excels.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/annotation/Log.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/annotation/RepeatSubmit.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/config/BaseConfig.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/constant/CacheConstants.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/constant/CommonConst.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/constant/Constants.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/constant/GenConstants.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/constant/HttpStatus.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/constant/MessageConstant.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/constant/ScheduleConstants.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/constant/TimeTypeConst.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/constant/UserConstants.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/core/domain/AjaxResult.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/core/domain/BaseEntity.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/enums/BusinessStatus.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/enums/BusinessType.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/enums/CollectionModes.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/enums/DataSourceType.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/enums/DesensitizedType.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/enums/GroupTimeType.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/enums/HttpMethod.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/enums/RetrievalModes.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/exception/CustomException.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/utils/ChartUtils.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/utils/DateTimeUtil.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/utils/DoubleUtil.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/utils/IntegerUtil.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/utils/PropUtils.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/utils/ReflectionUtils.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/utils/StringUtil.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-common/src/main/java/com/zhitan/common/utils/id/IdGenUtil.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/Itemizedenergyanalysis/domain/VO/ItemizedEnergyAnalysisVO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/Itemizedenergyanalysis/service/impl/ItemizedEnergyAnalysisServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/branchanalysis/mapper/BranchAnalysisMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/branchanalysis/service/IBranchAnalysisService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/branchanalysis/service/impl/BranchAnalysisServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/carbonemission/service/impl/CarbonEmissionServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/dataitem/mapper/DataItemMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/dataitem/service/IDataItemService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/home/service/HomePageServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/model/domain/ModelNode.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/model/mapper/ModelNodeMapper.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/model/service/IModelNodeService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/model/service/impl/ModelNodeServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/saving/domain/dto/PoliciesRegulationsDTO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/saving/domain/dto/PoliciesRegulationsManagementPageDTO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/saving/domain/entity/PoliciesRegulationsManagement.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/saving/domain/vo/PoliciesRegulationsManagementDetailVO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/saving/domain/vo/PoliciesRegulationsManagementPageVO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/statisticalAnalysis/domain/dto/FlowChartsDTO.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/statisticalAnalysis/domain/vo/FlowChartsVO.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/statisticalAnalysis/domain/vo/QueryCompareRequest.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-system/src/main/java/com/zhitan/statisticalAnalysis/service/impl/EnergyConsumeDataServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/.env.development 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/icons/svg/ai.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/icons/svg/bell.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/images/2.png 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/images/3.png 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/images/5.png 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/images/6.png 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/images/7.png 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/images/alarm.png 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/images/login-background.png 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/images/login-background1.jpg 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/images/robot.png 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/styles/page.scss 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/styles/ruoyi.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/styles/sidebar.scss 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/styles/variables.module.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/components/Avatar/index.vue 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/components/BaseCard/BaseCard.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/components/BaseCard/index.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/components/Breadcrumb/index.vue 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/components/HeaderSearch/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/components/LeftTree/index.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/components/TagsView/index.vue 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/components/TopNav/index.vue 298 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/layout/components/AppMain.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/layout/components/Navbar.vue 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/layout/components/Settings/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/layout/components/Sidebar/SidebarItem.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/layout/components/Sidebar/index.vue 350 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/layout/index.vue 322 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/permission.js 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/router/index.js 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/store/modules/app.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/store/modules/permission.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/store/modules/settings.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/store/modules/tagsView.js 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/views/businessconfiguration/gatewaystatus/gatewayStatus.vue 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/views/dataMonitoring/historyDataTrend/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/views/energyanalysis/equipment/equipment.vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/views/index.vue 494 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/views/realtimemonitor/gatewaystatus/index.vue 383 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/views/realtimemonitor/realtimemonitor/realtimemonitor.vue 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/vite.config.js 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md
@@ -18,16 +18,9 @@
针对客户场景:政府、园区、企业、工矿、公共建筑等。
## ã€æ³¨æ„ã€‘完整能碳管理平台包含三个部分,<span style="color: red;">本仓库只包含能碳平台后台管理端</span>
1. èƒ½ç¢³å¹³å°åŽå°ç®¡ç†ç«¯ï¼š<span style="color: red;">也即本项目能碳平台后台展示部分,代码完备,运行正常。通过本项目,学习者可以掌握能源管理行业的功能和业务,以及技术架构。</span>
2. æ•°æ®é‡‡é›†ç¨‹åºï¼šä¹Ÿå³mqtt➡️时序库功能,请参考[我们另一个仓库(点击超链接),MQTT采集网关](https://github.com/zhitan-cloud/zhitan-gateway);
3. æ•°æ®æ¸…洗服务:也即时序库➡️关系库,学习者可以使用java自带的XXL job等计划任务工具自己按照业务功能,来实现数据清洗服务。
## æ–°æ‰‹å¿…读
最近有很多开发新手在尝试运行本项目(很多大学毕业生要写毕业论文),遇到了不少问题。解决办法:
一是可以加我微信,我拉你进开发微信群,或者你自助加qq群。我看到问题有空基本都会解答。
二是本项目底层是基于若依,大家可以移步若依仓库,运行好若依的示例再过来研究本项目,以避免耽误大家太多时间,或者给大家造成很多困扰。
物联网项目本身就有一些门槛,希望能帮到各位。
##### 1. èƒ½ç¢³å¹³å°åŽå°ç®¡ç†ç«¯ï¼š<span style="color: red;">也即本项目能碳平台后台展示部分,代码完备,运行正常。通过本项目,学习者可以掌握能源管理行业的功能和业务,以及技术架构。</span>
##### 2. æ•°æ®é‡‡é›†ç¨‹åºï¼šä¹Ÿå³mqtt➡️时序库功能,请参考[我们另一个仓库(点击超链接),MQTT采集网关](https://github.com/zhitan-cloud/zhitan-gateway);或者可参考thingsjs等知名项目,或者自己用netty自己实现。
##### 3. æ•°æ®æ¸…洗服务:也即时序库➡️关系库,学习者可以使用java自带的XXL job等计划任务工具自己按照业务功能,来实现数据清洗服务。
## æ–‡æ¡£--均在wiki目录
### github wiki地址:https://github.com/zhitan-cloud/zhitan-ems/wiki
zhitan-admin/src/main/java/com/zhitan/AdminApplication.java
@@ -7,7 +7,7 @@
/**
 * å¯åŠ¨ç¨‹åº
 * 
 * @author ZhiTan
 * @author zhitan
 */
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class AdminApplication
zhitan-admin/src/main/java/com/zhitan/AdminServletInitializer.java
@@ -6,7 +6,7 @@
/**
 * web容器中进行部署
 * 
 * @author ZhiTan
 * @author zhitan
 */
public class AdminServletInitializer extends SpringBootServletInitializer
{
zhitan-admin/src/main/java/com/zhitan/web/controller/branchanalysis/BranchEnergyAnalysisController.java
@@ -16,7 +16,7 @@
/**
 *支路用能分析
 *
 * @author ZhiTan
 * @author zt
 * @date 2025-03-26
 */
@RestController
zhitan-admin/src/main/java/com/zhitan/web/controller/carbonemission/CarbonEmissionController.java
@@ -23,7 +23,7 @@
/**
 * ç¢³æŽ’放核算Controller
 *
 * @author ZhiTan
 * @author lsk
 * @date 2024-10-29
 */
@RestController
zhitan-admin/src/main/java/com/zhitan/web/controller/homepage/HomePageController.java
@@ -16,7 +16,7 @@
/**
 * HomePageController
 *
 * @author ZhiTan
 * @author hmj
 * @date 2024-10-08
 */
@RestController
@@ -43,7 +43,7 @@
    /**
     * @description: èƒ½è€—趋势
     * @author ZhiTan
     * @author: hmj
     * @date: 2024/10/8 13:41
     */
    @GetMapping("/energyConsumptionTrend")
@@ -61,7 +61,7 @@
    /**
     * @description: ç§‘室能耗排名
     * @author ZhiTan
     * @author: hmj
     * @date: 2024/10/8 13:41
     */
    @GetMapping("/energyConsumptionRanking")
@@ -78,7 +78,7 @@
    /**
     * @description: å³°å¹³è°·å æ¯”
     * @author ZhiTan
     * @author: hmj
     * @date: 2024/10/8 13:41
     */
    @GetMapping("/peakValley")
zhitan-admin/src/main/java/com/zhitan/web/controller/model/ModelNodeController.java
@@ -26,7 +26,7 @@
/**
 * æ¨¡åž‹èŠ‚ç‚¹Controller
 *
 * @author ZhiTan
 * @author fanxinfu
 * @date 2020-02-10
 */
@RestController
zhitan-admin/src/main/java/com/zhitan/web/controller/system/SysLoginController.java
@@ -20,7 +20,7 @@
/**
 * ç™»å½•验证
 * 
 * @author ZhiTan
 * @author zhitan
 */
@RestController
public class SysLoginController
zhitan-common/src/main/java/com/zhitan/common/annotation/Anonymous.java
@@ -9,7 +9,7 @@
/**
 * åŒ¿åè®¿é—®ä¸é‰´æƒæ³¨è§£
 * 
 * @author ZhiTan
 * @author zhitan
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
zhitan-common/src/main/java/com/zhitan/common/annotation/DataScope.java
@@ -9,7 +9,7 @@
/**
 * æ•°æ®æƒé™è¿‡æ»¤æ³¨è§£
 * 
 * @author ZhiTan
 * @author zhitan
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
zhitan-common/src/main/java/com/zhitan/common/annotation/DataSource.java
@@ -13,7 +13,7 @@
 *
 * ä¼˜å…ˆçº§ï¼šå…ˆæ–¹æ³•,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
 *
 * @author ZhiTan
 * @author zhitan
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
zhitan-common/src/main/java/com/zhitan/common/annotation/Excels.java
@@ -8,7 +8,7 @@
/**
 * Excel注解集
 * 
 * @author ZhiTan
 * @author zhitan
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
zhitan-common/src/main/java/com/zhitan/common/annotation/Log.java
@@ -11,7 +11,7 @@
/**
 * è‡ªå®šä¹‰æ“ä½œæ—¥å¿—记录注解
 * 
 * @author ZhiTan
 * @author zhitan
 *
 */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
zhitan-common/src/main/java/com/zhitan/common/annotation/RepeatSubmit.java
@@ -10,7 +10,7 @@
/**
 * è‡ªå®šä¹‰æ³¨è§£é˜²æ­¢è¡¨å•重复提交
 * 
 * @author ZhiTan
 * @author zhitan
 *
 */
@Inherited
zhitan-common/src/main/java/com/zhitan/common/config/BaseConfig.java
@@ -6,7 +6,7 @@
/**
 * è¯»å–项目相关配置
 *
 * @author ZhiTan
 * @author zhitan
 */
@Component
@ConfigurationProperties(prefix = "base")
zhitan-common/src/main/java/com/zhitan/common/constant/CacheConstants.java
@@ -3,7 +3,7 @@
/**
 * ç¼“存的key å¸¸é‡
 * 
 * @author ZhiTan
 * @author zhitan
 */
public class CacheConstants
{
zhitan-common/src/main/java/com/zhitan/common/constant/CommonConst.java
@@ -4,7 +4,7 @@
/**
 * @Description: å¸¸é‡å°è£…
 * @author ZhiTan
 * @author: yxw
 * @date: 2022å¹´02月02日 11:37
 */
public class CommonConst {
zhitan-common/src/main/java/com/zhitan/common/constant/Constants.java
@@ -6,7 +6,7 @@
/**
 * é€šç”¨å¸¸é‡ä¿¡æ¯
 * 
 * @author ZhiTan
 * @author zhitan
 */
public class Constants
{
zhitan-common/src/main/java/com/zhitan/common/constant/GenConstants.java
@@ -3,7 +3,7 @@
/**
 * ä»£ç ç”Ÿæˆé€šç”¨å¸¸é‡
 * 
 * @author ZhiTan
 * @author zhitan
 */
public class GenConstants
{
zhitan-common/src/main/java/com/zhitan/common/constant/HttpStatus.java
@@ -3,7 +3,7 @@
/**
 * è¿”回状态码
 * 
 * @author ZhiTan
 * @author zhitan
 */
public class HttpStatus
{
zhitan-common/src/main/java/com/zhitan/common/constant/MessageConstant.java
@@ -1,7 +1,7 @@
package com.zhitan.common.constant;
/**
 * @author ZhiTan
 * @Author DYL
 **/
public class MessageConstant {
zhitan-common/src/main/java/com/zhitan/common/constant/ScheduleConstants.java
@@ -3,7 +3,7 @@
/**
 * ä»»åŠ¡è°ƒåº¦é€šç”¨å¸¸é‡
 * 
 * @author ZhiTan
 * @author zhitan
 */
public class ScheduleConstants
{
zhitan-common/src/main/java/com/zhitan/common/constant/TimeTypeConst.java
@@ -2,7 +2,7 @@
/**
 * @Description: å‘¨æœŸç±»åž‹
 * @author ZhiTan
 * @author: yxw
 * @date: 2022å¹´03月17日 12:33
 */
public class TimeTypeConst {
zhitan-common/src/main/java/com/zhitan/common/constant/UserConstants.java
@@ -3,7 +3,7 @@
/**
 * ç”¨æˆ·å¸¸é‡ä¿¡æ¯
 * 
 * @author ZhiTan
 * @author zhitan
 */
public class UserConstants
{
zhitan-common/src/main/java/com/zhitan/common/core/domain/AjaxResult.java
@@ -8,7 +8,7 @@
/**
 * æ“ä½œæ¶ˆæ¯æé†’
 * 
 * @author ZhiTan
 * @author zhitan
 */
public class AjaxResult extends HashMap<String, Object>
{
zhitan-common/src/main/java/com/zhitan/common/core/domain/BaseEntity.java
@@ -15,7 +15,7 @@
/**
 * Entity基类
 *
 * @author ZhiTan
 * @author zhitan
 */
public class BaseEntity implements Serializable
{
zhitan-common/src/main/java/com/zhitan/common/enums/BusinessStatus.java
@@ -3,7 +3,7 @@
/**
 * æ“ä½œçŠ¶æ€
 * 
 * @author ZhiTan
 * @author zhitan
 *
 */
public enum BusinessStatus
zhitan-common/src/main/java/com/zhitan/common/enums/BusinessType.java
@@ -3,7 +3,7 @@
/**
 * ä¸šåŠ¡æ“ä½œç±»åž‹
 * 
 * @author ZhiTan
 * @author zhitan
 */
public enum BusinessType
{
zhitan-common/src/main/java/com/zhitan/common/enums/CollectionModes.java
@@ -1,7 +1,7 @@
package com.zhitan.common.enums;
/**
 * @author ZhiTan
 * @author èŒƒæ–°å¯Œ
 *
 *         å®žæ—¶æ•°æ®ç»Ÿè®¡ç±»åž‹.
 */
zhitan-common/src/main/java/com/zhitan/common/enums/DataSourceType.java
@@ -3,7 +3,7 @@
/**
 * æ•°æ®æº
 * 
 * @author ZhiTan
 * @author zhitan
 */
public enum DataSourceType
{
zhitan-common/src/main/java/com/zhitan/common/enums/DesensitizedType.java
@@ -6,7 +6,7 @@
/**
 * è„±æ•ç±»åž‹
 *
 * @author ZhiTan
 * @author zhitan
 */
public enum DesensitizedType
{
zhitan-common/src/main/java/com/zhitan/common/enums/GroupTimeType.java
@@ -3,7 +3,7 @@
/**
 * åˆ†ç»„时间类型
 *
 * @author ZhiTan
 * @Author: Zhujw
 * @Date: 2023/5/24
 */
public enum GroupTimeType {
zhitan-common/src/main/java/com/zhitan/common/enums/HttpMethod.java
@@ -7,7 +7,7 @@
/**
 * è¯·æ±‚方式
 *
 * @author ZhiTan
 * @author zhitan
 */
public enum HttpMethod
{
zhitan-common/src/main/java/com/zhitan/common/enums/RetrievalModes.java
@@ -1,7 +1,7 @@
package com.zhitan.common.enums;
/**
 * @author ZhiTan
 * @author èŒƒæ–°å¯Œ
 *
 *     èŽ·å–å®žæ—¶æ•°æ®æ–¹å¼.
 */
zhitan-common/src/main/java/com/zhitan/common/exception/CustomException.java
@@ -3,7 +3,7 @@
/**
 * è‡ªå®šä¹‰å¼‚常
 * 
 * @author ZhiTan
 * @author ruoyi
 */
public class CustomException extends RuntimeException
{
zhitan-common/src/main/java/com/zhitan/common/utils/ChartUtils.java
@@ -9,7 +9,7 @@
/**
 * @Description: ç»Ÿè®¡å›¾ç›¸å…³æ•°æ®å·¥å…·ç±»
 * @author ZhiTan
 * @author: yxw
 * @date: 2022å¹´04月28日 15:29
 */
public class ChartUtils {
zhitan-common/src/main/java/com/zhitan/common/utils/DateTimeUtil.java
@@ -14,7 +14,7 @@
/**
 * @Description: æ—¶é—´å·¥å…·ç±»
 * @author ZhiTan
 * @author: yxw
 * @date: 2022å¹´02月02日 12:23
 */
@Slf4j
zhitan-common/src/main/java/com/zhitan/common/utils/DoubleUtil.java
@@ -4,7 +4,7 @@
/**
 * @Description: æ•°å­—工具类
 * @author ZhiTan
 * @author: yxw
 * @date: 2022å¹´02月07日 15:03
 */
public class DoubleUtil {
zhitan-common/src/main/java/com/zhitan/common/utils/IntegerUtil.java
@@ -2,7 +2,7 @@
/**
 * @Description: æ•´æ•°ç›¸å…³å·¥å…·ç±»
 * @author ZhiTan
 * @author: yxw
 * @date: 2022å¹´03月10日 17:31
 */
public class IntegerUtil {
zhitan-common/src/main/java/com/zhitan/common/utils/PropUtils.java
@@ -10,7 +10,7 @@
/**
 * @Description: å±žæ€§å€¼æ“ä½œå·¥å…·ç±»
 * @author ZhiTan
 * @author: yxw
 * @date: 2022å¹´03月07日 9:57
 */
public class PropUtils {
zhitan-common/src/main/java/com/zhitan/common/utils/ReflectionUtils.java
@@ -8,7 +8,7 @@
/**
 * åå°„相关工具类
 *
 * @author ZhiTan
 * @author Silence
 * @version 1.0
 */
public class ReflectionUtils {
zhitan-common/src/main/java/com/zhitan/common/utils/StringUtil.java
@@ -5,7 +5,7 @@
/**
 * @Description: å­—符串工具类
 * @author ZhiTan
 * @author: yxw
 * @date: 2022å¹´02月02日 12:27
 */
public class StringUtil {
zhitan-common/src/main/java/com/zhitan/common/utils/id/IdGenUtil.java
@@ -6,7 +6,7 @@
/**
 * ç”ŸæˆID
 * @author ZhiTan
 * @author Geoffrey
 * @date 2024/12/31
 */
public class IdGenUtil {
zhitan-system/src/main/java/com/zhitan/Itemizedenergyanalysis/domain/VO/ItemizedEnergyAnalysisVO.java
@@ -8,7 +8,7 @@
/**
 * åˆ†é¡¹ç”¨èƒ½åˆ†æžè¿”回类
 *
 * @author ZhiTan
 * @author å¼ 
 */
@Data
public class ItemizedEnergyAnalysisVO {
zhitan-system/src/main/java/com/zhitan/Itemizedenergyanalysis/service/impl/ItemizedEnergyAnalysisServiceImpl.java
@@ -29,7 +29,7 @@
/**
 * åˆ†é¡¹ç”¨èƒ½åˆ†æž
 *
 * @author ZhiTan
 * @author sys
 * @date 2025-03-25
 */
@Service
zhitan-system/src/main/java/com/zhitan/branchanalysis/mapper/BranchAnalysisMapper.java
@@ -10,7 +10,7 @@
/**
 *支路用能分析
 *
 * @author ZhiTan
 * @author sys
 * @date 2021-01-11
 */
public interface BranchAnalysisMapper {
zhitan-system/src/main/java/com/zhitan/branchanalysis/service/IBranchAnalysisService.java
@@ -9,7 +9,7 @@
    /**
     * æ”¯è·¯ç”¨èƒ½åˆ†æž
     *
     * @author ZhiTan
     * @author sys
     * @date 2021-01-11
     */
    BranchAnalysisVO getBranchAnalysisService(BranchAnalysisDTO dataItem);
zhitan-system/src/main/java/com/zhitan/branchanalysis/service/impl/BranchAnalysisServiceImpl.java
@@ -26,7 +26,7 @@
/**
 * æ”¯è·¯ç”¨èƒ½åˆ†æž
 *
 * @author ZhiTan
 * @author zt
 * @date 2025-03-27
 */
@Service
zhitan-system/src/main/java/com/zhitan/carbonemission/service/impl/CarbonEmissionServiceImpl.java
@@ -25,7 +25,7 @@
/**
 * ã€ç¢³æŽ’放核算】Service业务层处理
 *
 * @author ZhiTan
 * @author lsk
 * @date 2024-10-29
 */
@Service
zhitan-system/src/main/java/com/zhitan/dataitem/mapper/DataItemMapper.java
@@ -15,7 +15,7 @@
/**
 * é˜¶æ®µæ•°æ®å½•入接口
 *
 * @author ZhiTan
 * @author sys
 * @date 2020-03-25
 */
public interface DataItemMapper extends BaseMapper<DataItem> {
zhitan-system/src/main/java/com/zhitan/dataitem/service/IDataItemService.java
@@ -15,7 +15,7 @@
/**
 * é˜¶æ®µæ•°æ®å½•入接口
 * 
 * @author ZhiTan
 * @author sys
 * @date 2020-03-25
 */
public interface IDataItemService {
zhitan-system/src/main/java/com/zhitan/home/service/HomePageServiceImpl.java
@@ -37,7 +37,7 @@
/**
 * description todu
 *
 * @author ZhiTan
 * @author hmj
 * @date 2024-10-31 18:07
 */
@Service
@@ -76,7 +76,7 @@
            final Double tongbiCount = tongbiMap.get(energyNo).stream().map(HomeEnergyStatisticsVO::getCount).mapToDouble(Double::doubleValue).sum();
            final Double huanbiCount = huanbiMap.get(energyNo).stream().map(HomeEnergyStatisticsVO::getCount).mapToDouble(Double::doubleValue).sum();
            vo.setTonCount(format2Double(count));
            vo.setTonCount(format2Double(vo.getCount() * Double.valueOf(vo.getCoefficient())));
            if (tongbiCount != 0) {
                vo.setTongbi(format2Double((count - tongbiCount) / tongbiCount * 100));
            } else {
zhitan-system/src/main/java/com/zhitan/model/domain/ModelNode.java
@@ -13,7 +13,7 @@
/**
 * æ¨¡åž‹èŠ‚ç‚¹å¯¹è±¡ model_node
 *
 * @author ZhiTan
 * @author fanxinfu
 * @date 2020-02-10
 */
public class ModelNode extends BaseEntity {
zhitan-system/src/main/java/com/zhitan/model/mapper/ModelNodeMapper.java
@@ -19,7 +19,7 @@
/**
 * æ¨¡åž‹èŠ‚ç‚¹Mapper接口
 *
 * @author ZhiTan
 * @author fanxinfu
 * @date 2020-02-10
 */
public interface ModelNodeMapper  extends BaseMapper<ModelNode> {
@@ -182,7 +182,7 @@
    /**
     * @description: æ ¹æ®èŠ‚ç‚¹id和能源类型查询点位信息
     * @param nodeId
     * @author ZhiTan
     * @author: hmj
     * @date: 2024/10/16 19:16
     */
    List<ModelNodeIndexInfo> getModelNodeIndexIdByNodeId(@Param("nodeId")String nodeId, @Param("energyType")String energyType);
@@ -191,7 +191,7 @@
     * @description: æ ¹æ®nodeId查询子节点的所有统计指标
     * @param parentId
     * @return java.util.List<com.zhitan.model.domain.vo.ModelNodeIndexInfor>
     * @author ZhiTan
     * @author: hmj
     * @date: 2024/10/18 16:12
     */
    List<ModelNodeIndexInfo> getModelNodeByParentId(String parentId);
zhitan-system/src/main/java/com/zhitan/model/service/IModelNodeService.java
@@ -15,7 +15,7 @@
/**
 * æ¨¡åž‹èŠ‚ç‚¹Service接口
 *
 * @author ZhiTan
 * @author fanxinfu
 * @date 2020-02-10
 */
public interface IModelNodeService {
zhitan-system/src/main/java/com/zhitan/model/service/impl/ModelNodeServiceImpl.java
@@ -27,7 +27,7 @@
/**
 * æ¨¡åž‹èŠ‚ç‚¹Service业务层处理
 *
 * @author ZhiTan
 * @author fanxinfu
 * @date 2020-02-10
 */
@Service
zhitan-system/src/main/java/com/zhitan/saving/domain/dto/PoliciesRegulationsDTO.java
@@ -12,7 +12,7 @@
import java.util.Date;
/**
 * @author ZhiTan
 * @author Geoffrey
 * @date 2025/01/13
 */
@Data
zhitan-system/src/main/java/com/zhitan/saving/domain/dto/PoliciesRegulationsManagementPageDTO.java
@@ -4,7 +4,7 @@
import lombok.Data;
/**
 * @author ZhiTan
 * @author Geoffrey
 * @date 2025/01/13
 */
@Data
zhitan-system/src/main/java/com/zhitan/saving/domain/entity/PoliciesRegulationsManagement.java
@@ -12,7 +12,7 @@
/**
 * æ”¿ç­–法规对象
 * @author ZhiTan
 * @author Geoffrey
 * @date 2025/01/13
 */
@Data
zhitan-system/src/main/java/com/zhitan/saving/domain/vo/PoliciesRegulationsManagementDetailVO.java
@@ -11,7 +11,7 @@
/**
 * æ”¿ç­–法规对象vo
 * @author ZhiTan
 * @author Geoffrey
 * @date 2025/01/13
 */
@Data
zhitan-system/src/main/java/com/zhitan/saving/domain/vo/PoliciesRegulationsManagementPageVO.java
@@ -11,7 +11,7 @@
/**
 * æ”¿ç­–法规page vo
 * @author ZhiTan
 * @author Geoffrey
 * @date 2025/01/13
 */
@Data
zhitan-system/src/main/java/com/zhitan/statisticalAnalysis/domain/dto/FlowChartsDTO.java
@@ -55,15 +55,6 @@
        if (ObjectUtils.isEmpty(timeType)) {
            return TimeType.DAY;
        }
        switch (timeType) {
            case DAY:
                return TimeType.HOUR;
            case MONTH:
                return TimeType.DAY;
            case YEAR:
                return TimeType.MONTH;
            default:
                return timeType;
        }
    }
}
zhitan-system/src/main/java/com/zhitan/statisticalAnalysis/domain/vo/FlowChartsVO.java
@@ -55,8 +55,7 @@
                || BigDecimal.ZERO.compareTo(difference) == 0) {
            return BigDecimal.ZERO;
        }
        // å…ˆè®¡ç®—比例,再乘以 100 è½¬æ¢ä¸ºç™¾åˆ†æ•°
        return energyLossRatio = difference.divide(totalAccumulatedAmount, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
        return energyLossRatio = difference.divide(totalAccumulatedAmount, 2, RoundingMode.HALF_UP);
    }
    public FlowChartsVO() {
zhitan-system/src/main/java/com/zhitan/statisticalAnalysis/domain/vo/QueryCompareRequest.java
@@ -10,7 +10,7 @@
/**
 * @Description: æ•°æ®æŸ¥è¯¢æ¡ä»¶å®žä½“
 * @author ZhiTan
 * @author: yxw
 * @date: 2022å¹´01月28日 14:49
 */
@Data
zhitan-system/src/main/java/com/zhitan/statisticalAnalysis/service/impl/EnergyConsumeDataServiceImpl.java
@@ -32,7 +32,7 @@
/**
 * @Description: TODO
 * @author ZhiTan
 * @author: yxw
 * @date: 2022å¹´04月12日 14:15
 */
@Service
zhitan-vue/.env.development
@@ -5,6 +5,5 @@
VITE_APP_ENV = 'development'
# ç³»ç»Ÿ/开发环境
# test
VITE_APP_BASE_API = 'http://127.0.0.1:8080'
VITE_APP_BASE_API = '/dev-api'
zhitan-vue/src/assets/icons/svg/ai.svg
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 18a8 8 0 1 1 0-16 8 8 0 0 1 0 16z"></path><path d="M12 6a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm-4 6a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z"></path></svg>
zhitan-vue/src/assets/icons/svg/bell.svg
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.73 21a2 2 0 0 1-3.46 0"></path></svg>
zhitan-vue/src/assets/images/2.png
zhitan-vue/src/assets/images/3.png
zhitan-vue/src/assets/images/5.png
zhitan-vue/src/assets/images/6.png
zhitan-vue/src/assets/images/7.png
zhitan-vue/src/assets/images/alarm.png
zhitan-vue/src/assets/images/login-background.png
zhitan-vue/src/assets/images/login-background1.jpg

zhitan-vue/src/assets/images/robot.png
zhitan-vue/src/assets/styles/page.scss
@@ -4,10 +4,35 @@
      display: flex;
      .page-container-left {
        width: 280px;
        width: 220px;
        min-height: calc(100vh - 148px);
        border-right: 1px solid #1a235d;
        background: #1F1C49;
        background: #08234F;
        .el-tree {
          &.el-tree--highlight-current,
          &:not(.el-tree--highlight-current) {
            .el-tree-node {
              .el-tree-node__content {
                .el-tree-node__label,
                span {
                  font-size: 14px !important;
                }
              }
              .el-tree-node__expand-icon {
                font-size: 14px !important;
              }
            }
          }
        }
        .tree-container, .tree {
          .el-tree-node__label,
          .el-tree-node__content span {
            font-size: 14px !important;
          }
        }
        .tree {
          height: calc(100vh - 170px);
@@ -26,10 +51,64 @@
    }
    .form-card {
      background: #1a235d;
      border-radius: 0px 0px 0px 0px;
      border: 1px solid #000000;
      padding: 18px 0 0 15px;
      background: #08234F;
      border-radius: 0px;
      height: 52px;
      line-height: 52px;
      margin: 0 16px;
      display: flex;
      align-items: center;
      .el-form.el-form--inline {
        margin-left: 10px;
        display: flex;
        align-items: center;
        flex-wrap: nowrap;
        width: 100%;
        .el-form-item {
          margin-right: 15px;
          margin-bottom: 0;
          height: 32px;
          display: flex;
          align-items: center;
          &.is-required {
            .el-form-item__label::before {
              vertical-align: middle;
            }
          }
          .el-form-item__label {
            height: 32px;
            line-height: 32px;
            color: #ffffff;
          }
          .el-form-item__content {
            display: flex;
            align-items: center;
            height: 32px;
            line-height: 32px;
            .el-input, .el-select, .el-date-editor {
              height: 32px;
              line-height: 32px;
            }
            .el-input__wrapper {
              height: 32px;
            }
            .el-button {
              height: 32px;
              line-height: 32px;
              padding-top: 0;
              padding-bottom: 0;
            }
          }
        }
      }
    }
    .table-bg-style {
@@ -89,7 +168,31 @@
        border-right: 1px solid #fff;
        background: #f1f4fa;
        // border-right: 1px solid #1a235d;
        .el-tree {
          &.el-tree--highlight-current,
          &:not(.el-tree--highlight-current) {
            .el-tree-node {
              .el-tree-node__content {
                .el-tree-node__label,
                span {
                  font-size: 14px !important;
                }
              }
              .el-tree-node__expand-icon {
                font-size: 14px !important;
              }
            }
          }
        }
        .tree-container, .tree {
          .el-tree-node__label,
          .el-tree-node__content span {
            font-size: 14px !important;
          }
        }
        .tree {
          height: calc(100vh - 170px) !important;
          max-height: calc(100vh - 170px) !important;
@@ -108,10 +211,56 @@
    .form-card {
      background: #fff;
      // background: #F7F8FA;
      border-radius: 0px 0px 0px 0px;
      // border: 1px solid #000000;
      padding: 18px 0 0 15px;
      border-radius: 0px;
      height: 52px;
      line-height: 52px;
      margin: 0 16px;
      display: flex;
      align-items: center;
      .el-form.el-form--inline {
        margin-left: 10px;
        display: flex;
        align-items: center;
        flex-wrap: nowrap;
        width: 100%;
        .el-form-item {
          margin-right: 15px;
          margin-bottom: 0;
          height: 32px;
          display: flex;
          align-items: center;
          .el-form-item__label {
            height: 32px;
            line-height: 32px;
          }
          .el-form-item__content {
            display: flex;
            align-items: center;
            height: 32px;
            line-height: 32px;
            .el-input, .el-select, .el-date-editor {
              height: 32px;
              line-height: 32px;
            }
            .el-input__wrapper {
              height: 32px;
            }
            .el-button {
              height: 32px;
              line-height: 32px;
              padding-top: 0;
              padding-bottom: 0;
            }
          }
        }
      }
    }
    
    .table-bg-style {
@@ -159,6 +308,15 @@
}
// åˆ é™¤ä¹‹å‰æ·»åŠ çš„å…¨å±€æ ·å¼è¦†ç›–ï¼Œé¿å…å½±å“å…¨å±€
// ä¿ç•™è°ƒè¯•建议
/*
您可以在浏览器中使用以下内联样式进行调试:
document.querySelectorAll('.page-container-left .el-tree-node__label').forEach(el => {
  el.style.cssText = 'font-size: 14px !important';
});
*/
.padding {
  padding: 15px;
}
zhitan-vue/src/assets/styles/ruoyi.scss
@@ -394,7 +394,7 @@
  // æ ‘
  .el-tree {
    background: transparent;
    font-size: 16px;
    font-size: 14px;
    color: #ffffff;
    .el-tree-node__content {
zhitan-vue/src/assets/styles/sidebar.scss
@@ -1,7 +1,7 @@
.themeDark {
  #app {
    .el-menu-item.is-active {
      background: #3271eb !important;
     // background: #3271eb !important;
      // border-radius: 30px 30px 30px 30px !important;
      color: #fff;
    }
@@ -30,11 +30,12 @@
      background-color: $base-menu-background;
      height: 100%;
      position: fixed;
      top: 0;
      top: 60px;
      bottom: 0;
      left: 0;
      z-index: 1001;
      z-index: 999;
      overflow: hidden;
      background-color: #002866 !important;
      -webkit-box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
      box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
      font-family: OPPOSans, OPPOSans;
@@ -92,7 +93,7 @@
      .menu-title {
        overflow: hidden !important;
        font-weight: 400 !important;
        font-size: 16px !important;
        font-size: 14px !important;
      }
      // @media (min-width: 1440px) {
@@ -284,7 +285,7 @@
.themeLight {
  #app {
    .el-menu-item.is-active {
      background: #e0eafc !important;
      // border-radius: 30px 30px 30px 30px !important;
    }
@@ -311,10 +312,10 @@
      background-color: $base-menu-background;
      height: 100%;
      position: fixed;
      top: 0;
      top: 60px;
      bottom: 0;
      left: 0;
      z-index: 1001;
      z-index: 999;
      overflow: hidden;
      -webkit-box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
      box-shadow: none;
zhitan-vue/src/assets/styles/variables.module.scss
@@ -42,7 +42,7 @@
$--color-danger: #F56C6C;
$--color-info: #909399;
$base-sidebar-width: 260px;
$base-sidebar-width: 220px;
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
zhitan-vue/src/components/Avatar/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,100 @@
<template>
  <div class="avatar-container">
    <el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
      <div class="avatar-wrapper">
        <img :src="userStore.avatar" class="user-avatar" />
        <el-icon><caret-bottom /></el-icon>
      </div>
      <template #dropdown>
        <el-dropdown-menu>
          <router-link to="/user/profile">
            <el-dropdown-item>个人中心</el-dropdown-item>
          </router-link>
          <el-dropdown-item command="toggleTheme">
            <span>风格切换</span>
          </el-dropdown-item>
          <el-dropdown-item divided command="logout">
            <span>退出登录</span>
          </el-dropdown-item>
        </el-dropdown-menu>
      </template>
    </el-dropdown>
  </div>
</template>
<script setup>
import { ElMessageBox } from "element-plus"
import useUserStore from "@/store/modules/user"
import useSettingsStore from "@/store/modules/settings"
const userStore = useUserStore()
const settingsStore = useSettingsStore()
function handleCommand(command) {
  switch (command) {
    case "toggleTheme":
      const newTheme = settingsStore.sideTheme === "theme-dark" ? "theme-light" : "theme-dark";
      settingsStore.changeSetting({ key: "sideTheme", value: newTheme });
      document.querySelector("body").className = newTheme === "theme-dark" ? "themeDark" : "themeLight";
      break;
    case "logout":
      ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          userStore.logOut().then(() => {
            location.href = "/index";
          });
        })
        .catch(() => {});
      break;
  }
}
</script>
<style lang="scss" scoped>
.avatar-container {
  margin-right: 20px;
  .avatar-wrapper {
    margin-top: 5px;
    position: relative;
    display: flex;
    align-items: center;
    .user-avatar {
      cursor: pointer;
      width: 40px;
      height: 40px;
      border-radius: 10px;
    }
    .el-icon {
      cursor: pointer;
      margin-left: 8px;
      font-size: 12px;
      color: #ffffff;
    }
  }
}
.right-menu-item {
  display: inline-block;
  padding: 0 8px;
  height: 100%;
  font-size: 18px;
  color: #ffffff;
  vertical-align: text-bottom;
  &.hover-effect {
    cursor: pointer;
    transition: background 0.3s;
    &:hover {
      background: rgba(0, 0, 0, 0.1);
    }
  }
}
</style>
zhitan-vue/src/components/BaseCard/BaseCard.vue
@@ -67,6 +67,20 @@
      font-family: YouSheBiaoTiHei;
      font-size: 1.25vw; //24px;
      color: #fff;
      position: relative;
      padding-left: 12px;
      &::before {
        content: '';
        position: absolute;
        left: 0;
        top: 50%;
        transform: translateY(-50%);
        width: 5px;
        height: 18px;
        background-color: #3883FA;
        border-radius: 2px;
      }
    }
  }
zhitan-vue/src/components/BaseCard/index.vue
@@ -28,11 +28,26 @@
      border-radius: 7px 7px 0 0;
      padding: 20px;
      background-color: #22408c;
      .name {
        font-family: OPPOSans, OPPOSans;
        font-weight: bold;
        font-size: 18px;
        color: #fff;
        position: relative;
        padding-left: 12px;
        &::before {
          content: '';
          position: absolute;
          left: 0;
          top: 50%;
          transform: translateY(-50%);
          width: 5px;
          height: 18px;
          background-color: #3883FA;
          border-radius: 2px;
        }
      }
    }
  }
@@ -46,6 +61,7 @@
    border: 1px solid #ebebeb;
    padding-bottom: 10px;
    background-color: #fff;
    .mycard-title {
      display: flex;
      justify-content: flex-start;
@@ -54,11 +70,26 @@
      border-radius: 7px 7px 0 0;
      padding: 20px;
      background-color: #e7eefd;
      .name {
        font-family: OPPOSans, OPPOSans;
        font-weight: bold;
        font-size: 18px;
        color: #2d2e31;
        position: relative;
        padding-left: 12px;
        &::before {
          content: '';
          position: absolute;
          left: 0;
          top: 50%;
          transform: translateY(-50%);
          width: 5px;
          height: 18px;
          background-color: #3883FA;
          border-radius: 2px;
        }
      }
    }
  }
zhitan-vue/src/components/Breadcrumb/index.vue
@@ -18,10 +18,11 @@
  // only show routes with meta.title
  let matched = route.matched.filter(item => item.meta && item.meta.title);
  const first = matched[0]
  // åˆ¤æ–­æ˜¯å¦ä¸ºé¦–页
  if (!isDashboard(first)) {
    matched = [{ path: '/index', meta: { title: '首页' } }].concat(matched)
  }
  // ä¸è‡ªåŠ¨æ·»åŠ é¦–é¡µåˆ°é¢åŒ…å±‘ä¸­
  // if (!isDashboard(first)) {
  //   matched = [{ path: '/index', meta: { title: '首页' } }].concat(matched)
  // }
  levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
}
@@ -62,5 +63,19 @@
    color: #fff;
    cursor: text;
  }
  :deep(.el-breadcrumb__item) {
    .el-breadcrumb__inner {
      color: rgba(255, 255, 255, 0.8);
      &:hover {
        color: #fff;
      }
    }
    .el-breadcrumb__separator {
      color: rgba(255, 255, 255, 0.8);
    }
  }
}
</style>
zhitan-vue/src/components/HeaderSearch/index.vue
@@ -52,7 +52,7 @@
    window.open(path.substr(pindex, path.length), "_blank");
  } else {
    if (query) {
      router.push({ path: path, query: JSON.parse(query) });
      router.push({ path: path, query: query });
    } else {
      router.push(path)
    }
zhitan-vue/src/components/LeftTree/index.vue
@@ -53,14 +53,26 @@
getTree();
/** æŸ¥è¯¢éƒ¨é—¨ä¸‹æ‹‰æ ‘结构 */
function getTree() {
  // ç¡®ä¿query.value有初始值
  query.value = query.value || {};
  // ä¼˜å…ˆä½¿ç”¨props中传入的ParentModelCode
  if (props.ParentModelCode) {
    query.value = { modelCode: props.ParentModelCode };
    query.value.modelCode = props.ParentModelCode;
  } else if (useRoute().query.modelCode) {
    // å…¶æ¬¡ä½¿ç”¨è·¯ç”±ä¸­çš„modelCode
    query.value.modelCode = useRoute().query.modelCode;
  } else {
    query.value = { ...useRoute().query };
    // æœ€åŽä½¿ç”¨é»˜è®¤å€¼
    query.value.modelCode = 'JCZBK_CODE';
  }
  console.log('LeftTree getTree modelCode:', query.value.modelCode);
  treeList(query.value).then((response) => {
    nodeOptions.value = response.data;
    if (response.data.length > 0) {
      defaultExpandedKeys.value = []; // æ¸…空已有的key,避免重复
      response.data.map((item) => {
        defaultExpandedKeys.value.push(item.id);
      });
zhitan-vue/src/components/TagsView/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
function isTags(route) {
  return !route.hidden && route.name &&
    route.name !== 'login' &&
    route.name !== '404' &&
    route.name !== '401' &&
    route.name !== 'index' &&
    route.path !== '/index' &&
    route.path !== '/';
}
function addTags() {
  const { name } = route;
  if (name) {
    // æ£€æŸ¥æ˜¯å¦ä¸ºé¦–页
    if (name === 'index' || route.path === '/index' || route.path === '/') {
      return;
    }
    store.dispatch('tagsView/addView', route);
  }
  return false;
}
// åˆå§‹åŒ–标签,确保首页不被添加为固定标签
function initTags() {
  const affixTags = filterAffixTags(routes);
  for (const tag of affixTags) {
    // æŽ’除首页
    if (tag.path === '/index' || tag.path === '/' || tag.name === 'Index') {
      continue;
    }
    // æ·»åŠ å›ºå®šæ ‡ç­¾
    if (tag.name) {
      store.dispatch('tagsView/addVisitedView', tag);
    }
  }
}
zhitan-vue/src/components/TopNav/index.vue
@@ -1,35 +1,30 @@
<template>
  <div class="top-nav-container">
    <div class="scroll-arrow left-arrow" @click="scrollLeft" v-show="canScrollLeft">
      <el-icon><arrow-left /></el-icon>
    </div>
    <div class="menu-container" ref="menuContainer">
  <el-menu
    :default-active="activeMenu"
    mode="horizontal"
    @select="handleSelect"
    :ellipsis="false"
        class="top-menu"
        :class="{ 'theme-dark': theme === 'dark' }"
  >
    <template v-for="(item, index) in topMenus">
      <el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber">
        <svg-icon
        v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
        :icon-class="item.meta.icon"/>
        <template v-for="(item, index) in topMenus" :key="index">
          <el-menu-item :style="{'--theme': theme}" :index="item.path">
        {{ item.meta.title }}
      </el-menu-item>
    </template>
    <!-- é¡¶éƒ¨èœå•超出数量折叠 -->
    <el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
      <template #title>更多菜单</template>
      <template v-for="(item, index) in topMenus">
        <el-menu-item
          :index="item.path"
          :key="index"
          v-if="index >= visibleNumber">
        <svg-icon
          v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
          :icon-class="item.meta.icon"/>
        {{ item.meta.title }}
        </el-menu-item>
      </template>
    </el-sub-menu>
  </el-menu>
    </div>
    <div class="scroll-arrow right-arrow" @click="scrollRight" v-show="canScrollRight">
      <el-icon><arrow-right /></el-icon>
    </div>
  </div>
</template>
<script setup>
@@ -38,9 +33,13 @@
import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue'
// é¡¶éƒ¨æ åˆå§‹æ•°
const visibleNumber = ref(null);
// æ»šåŠ¨ç›¸å…³
const menuContainer = ref(null);
const canScrollLeft = ref(false);
const canScrollRight = ref(false);
// å½“前激活菜单的 index
const currentIndex = ref(null);
// éšè—ä¾§è¾¹æ è·¯ç”±
@@ -104,6 +103,10 @@
    if (!route.meta.link) {
        appStore.toggleSideBarHide(false);
    }
  } else if (path === '/index' || path === '/') {
    // é¦–页时隐藏侧边栏
    activePath = path;
    appStore.toggleSideBarHide(true);
  } else if(!route.children) {
    activePath = path;
    appStore.toggleSideBarHide(true);
@@ -112,36 +115,76 @@
  return activePath;
})
function setVisibleNumber() {
  const width = document.body.getBoundingClientRect().width / 3;
  visibleNumber.value = parseInt(width / 85);
function updateScrollButtons() {
  if (!menuContainer.value) return;
  const container = menuContainer.value;
  canScrollLeft.value = container.scrollLeft > 10;
  canScrollRight.value = container.scrollLeft < (container.scrollWidth - container.clientWidth - 10);
}
function scrollLeft() {
  if (!menuContainer.value) return;
  menuContainer.value.scrollBy({ left: -200, behavior: 'smooth' });
  setTimeout(updateScrollButtons, 300);
}
function scrollRight() {
  if (!menuContainer.value) return;
  menuContainer.value.scrollBy({ left: 200, behavior: 'smooth' });
  setTimeout(updateScrollButtons, 300);
}
function handleSelect(key, keyPath) {
  currentIndex.value = key;
  const route = routers.value.find(item => item.path === key);
  if (isHttp(key)) {
    // http(s):// è·¯å¾„新窗口打开
    window.open(key, "_blank");
  } else if (!route || !route.children) {
    // æ²¡æœ‰å­è·¯ç”±è·¯å¾„内部打开
    return;
  }
  if (key === '/index' || key === '/') {
    // é¦–页时显示折叠的侧边栏,而不是隐藏
    router.push({ path: key });
    appStore.showCollapsedSidebar();
    return;
  }
  // æ£€æŸ¥æ˜¯å¦æœ‰å­è·¯ç”±
  if (route && route.children && route.children.length > 0) {
    // æœ‰å­è·¯ç”±ï¼Œæ˜¾ç¤ºä¾§è¾¹æ 
    activeRoutes(key);
    const firstChild = route.children[0];
    const path = firstChild.path.startsWith('/') ? firstChild.path : `${key}/${firstChild.path}`;
    if (firstChild.query) {
      router.push({ path, query: firstChild.query });
    } else {
      router.push({ path });
    }
  } else {
    // æ²¡æœ‰å­è·¯ç”±ï¼Œéšè—ä¾§è¾¹æ 
    const routeMenu = childrenMenus.value.find(item => item.path === key);
    if (routeMenu && routeMenu.query) {
      let query = JSON.parse(routeMenu.query);
      router.push({ path: key, query: query });
      // query å·²ç»åœ¨ permission.js ä¸­è¢«å¤„理为对象,无需再次解析
      router.push({ path: key, query: routeMenu.query });
    } else {
      router.push({ path: key });
    }
    appStore.toggleSideBarHide(true);
  } else {
    // æ˜¾ç¤ºå·¦ä¾§è”动菜单
    activeRoutes(key);
    appStore.toggleSideBarHide(false);
  }
}
function activeRoutes(key) {
  let routes = [];
  if (key === '/index' || key === '/') {
    // é¦–页时显示折叠的侧边栏,而不是隐藏
    appStore.showCollapsedSidebar();
    return [];
  }
  // æŸ¥æ‰¾åŒ¹é…çš„路由
  if (childrenMenus.value && childrenMenus.value.length > 0) {
    childrenMenus.value.map((item) => {
      if (key == item.parentPath || (key == "index" && "" == item.path)) {
@@ -149,66 +192,183 @@
      }
    });
  }
  if(routes.length > 0) {
    // æœ‰å­è·¯ç”±ï¼Œåˆ™æ˜¾ç¤ºä¾§è¾¹æ 
    permissionStore.setSidebarRouters(routes);
    appStore.toggleSideBarHide(false);
  } else {
    // æ²¡æœ‰å­è·¯ç”±ï¼Œéšè—ä¾§è¾¹æ 
    appStore.toggleSideBarHide(true);
  }
  return routes;
}
onMounted(() => {
  window.addEventListener('resize', setVisibleNumber)
})
onBeforeUnmount(() => {
  window.removeEventListener('resize', setVisibleNumber)
  // æ ¹æ®å½“前路由决定是否显示侧边栏,而不是直接隐藏
  const currentPath = route.path;
  if (currentPath === '/index' || currentPath === '/') {
    // å¦‚果当前是首页,自动跳转到第一个动态路由
    if (topMenus.value.length > 0) {
      const firstRoute = topMenus.value[0];
      handleSelect(firstRoute.path);
    }
  } else {
    // æ£€æŸ¥å½“前路由是否需要显示侧边栏
    const routeConfig = routers.value.find(item => currentPath.startsWith(item.path) && item.path !== '/');
    if (routeConfig && routeConfig.children && routeConfig.children.length > 0) {
      // æœ‰å­èœå•,显示侧边栏
      activeRoutes(routeConfig.path);
      appStore.toggleSideBarHide(false);
    } else {
      // æ— å­èœå•,可以隐藏侧边栏
      appStore.toggleSideBarHide(true);
    }
  }
  // ç›‘听滚动状态
  if (menuContainer.value) {
    menuContainer.value.addEventListener('scroll', updateScrollButtons);
    nextTick(() => {
      updateScrollButtons();
    });
  }
  window.addEventListener('resize', () => {
    updateScrollButtons();
  });
})
onMounted(() => {
  setVisibleNumber()
onBeforeUnmount(() => {
  if (menuContainer.value) {
    menuContainer.value.removeEventListener('scroll', updateScrollButtons);
  }
  window.removeEventListener('resize', updateScrollButtons);
})
</script>
<style lang="scss">
.topmenu-container.el-menu--horizontal > .el-menu-item {
  float: left;
  height: 50px !important;
  line-height: 50px !important;
  color: #999093 !important;
  padding: 0 5px !important;
  margin: 0 10px !important;
.top-nav-container {
  display: flex;
  align-items: center;
  flex: 1;
  position: relative;
  height: 60px;
  overflow: hidden;
  padding: 0 40px; /* Increase padding for arrows */
  .scroll-arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    background: rgba(255, 255, 255, 0.2);
    border-radius: 50%;
    cursor: pointer;
    color: #ffffff;
    z-index: 20;
    opacity: 0;
    transition: opacity 0.3s;
    position: absolute;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
    &:hover {
      background: rgba(255, 255, 255, 0.3);
}
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
  border-bottom: 2px solid #{'var(--theme)'} !important;
  color: #303133;
    &.left-arrow {
      left: 8px;
}
/* sub-menu item */
.topmenu-container.el-menu--horizontal > .el-sub-menu .el-sub-menu__title {
  float: left;
  height: 50px !important;
  line-height: 50px !important;
  color: #999093 !important;
  padding: 0 5px !important;
  margin: 0 10px !important;
    &.right-arrow {
      right: 8px;
    }
}
/* èƒŒæ™¯è‰²éšè— */
.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
  background-color: #ffffff !important;
  &:hover {
    .scroll-arrow {
      opacity: 1;
    }
  }
  .menu-container {
    width: 100%;
    height: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none; /* IE and Edge */
    &::-webkit-scrollbar {
      display: none; /* Chrome, Safari, Opera */
    }
  }
}
.top-menu {
  height: 60px;
  border-bottom: none !important;
  white-space: nowrap;
  background: transparent !important;
  &.theme-dark {
    background: #002866 !important;
  }
}
.el-menu--horizontal {
  border-bottom: none !important;
  > .el-menu-item {
    display: inline-block;
    float: none;
    height: 60px !important;
    line-height: 60px !important;
    color: #ffffff !important;
    padding: 0 20px !important;
    margin: 0 !important;
    border-bottom: none !important;
    position: relative;
    font-size: 16px;
    &.is-active {
      background-color: transparent !important;
      color: #ffffff !important;
      font-weight: bold;
      &::after {
        content: '';
        position: absolute;
        bottom: 10px;
        left: 50%;
        transform: translateX(-50%);
        width: calc(100% - 40px);
        height: 2px;
        background-color: #ffffff;
      }
    }
    &:hover {
      background-color: rgba(255, 255, 255, 0.1) !important;
      color: #ffffff !important;
    }
  }
}
/* å›¾æ ‡å³é—´è· */
.topmenu-container .svg-icon {
  margin-right: 4px;
.svg-icon {
  margin-right: 8px;
  color: #ffffff;
}
/* topmenu more arrow */
.topmenu-container .el-sub-menu .el-sub-menu__icon-arrow {
  position: static;
  vertical-align: middle;
  margin-left: 8px;
  margin-top: 0px;
/* é¦–页按钮样式 */
.el-menu-item:first-child {
  margin-left: 0 !important;
  font-weight: bold;
  .svg-icon {
    font-size: 18px;
  }
}
</style>
zhitan-vue/src/layout/components/AppMain.vue
@@ -27,7 +27,8 @@
    position: relative;
    overflow: hidden;
    background: #110f2e;
    padding: 14px 0 0 14px;
    padding: 14px;
    box-sizing: border-box;
  }
  .fixed-header + .app-main {
@@ -37,8 +38,7 @@
  .hasTagsView {
    .app-main {
      /* 84 = navbar + tags-view + padding = 70 + 56 + 14 */
      min-height: calc(100vh - 125px);
      // padding: 20px;
      min-height: calc(100vh - 110px);
    }
    .fixed-header + .app-main {
@@ -55,7 +55,8 @@
    position: relative;
    overflow: hidden;
    background: #f7f8fa;
    padding: 14px 0 0 14px;
    padding: 14px;
    box-sizing: border-box;
  }
  .fixed-header + .app-main {
@@ -65,8 +66,7 @@
  .hasTagsView {
    .app-main {
      /* 84 = navbar + tags-view + padding = 70 + 56 + 14 */
      min-height: calc(100vh - 125px);
      // padding: 20px;
      min-height: calc(100vh - 110px);
    }
    .fixed-header + .app-main {
zhitan-vue/src/layout/components/Navbar.vue
@@ -12,108 +12,51 @@
    </div>
    <div class="right-menu">
      <!-- <template v-if="appStore.device !== 'mobile'">
        <header-search id="header-search" class="right-menu-item" />
        <screenfull id="screenfull" class="right-menu-item hover-effect" />
        <el-tooltip content="布局大小" effect="dark" placement="bottom">
          <size-select id="size-select" class="right-menu-item hover-effect" />
        </el-tooltip>
      </template> -->
      <!-- <el-button @click="toggleTheme">切换</el-button> -->
      <div class="avatar-container">
        <el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
          <div class="avatar-wrapper">
            <img :src="userStore.avatar" class="user-avatar" />
            <el-icon><caret-bottom /></el-icon>
      <!-- æŠ¥è­¦æŒ‰é’® -->
      <div class="right-menu-item hover-effect nav-btn-item">
        <el-tooltip content="报警" effect="dark" placement="bottom">
          <div class="nav-btn" @click="handleAlarm">
            <img src="@/assets/images/alarm.png" alt="报警" />
            <span>报警</span>
          </div>
          <template #dropdown>
            <el-dropdown-menu>
              <router-link to="/user/profile">
                <el-dropdown-item>个人中心</el-dropdown-item>
              </router-link>
              <el-dropdown-item command="toggleTheme">
                <span>风格切换</span>
              </el-dropdown-item>
              <!--
              <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
                <span>布局设置</span>
              </el-dropdown-item> -->
              <el-dropdown-item divided command="logout">
                <span>退出登录</span>
              </el-dropdown-item>
            </el-dropdown-menu>
          </template>
        </el-dropdown>
        </el-tooltip>
      </div>
      <!-- å¤§æ¨¡åž‹æŒ‰é’® -->
      <div class="right-menu-item hover-effect nav-btn-item">
        <el-tooltip content="大模型" effect="dark" placement="bottom">
          <div class="nav-btn" @click="handleRobot">
            <img src="@/assets/images/robot.png" alt="大模型" />
            <span>大模型</span>
          </div>
        </el-tooltip>
      </div>
    </div>
  </div>
</template>
<script setup>
import { ElMessageBox } from "element-plus"
import Breadcrumb from "@/components/Breadcrumb"
import TopNav from "@/components/TopNav"
import Hamburger from "@/components/Hamburger"
import Screenfull from "@/components/Screenfull"
import SizeSelect from "@/components/SizeSelect"
import HeaderSearch from "@/components/HeaderSearch"
import useAppStore from "@/store/modules/app"
import useUserStore from "@/store/modules/user"
import useSettingsStore from "@/store/modules/settings"
const appStore = useAppStore()
const userStore = useUserStore()
const settingsStore = useSettingsStore()
function toggleTheme() {
  if (settingsStore.sideTheme == "theme-dark") {
    settingsStore.sideTheme = "theme-light"
    document.querySelector("body").className = "themeLight"
  } else {
    settingsStore.sideTheme = "theme-dark"
    document.querySelector("body").className = "themeDark"
  }
}
function toggleSideBar() {
  appStore.toggleSideBar()
}
function handleCommand(command) {
  switch (command) {
    case "toggleTheme":
      toggleTheme()
      break
    case "setLayout":
      setLayout()
      break
    case "logout":
      logout()
      break
    default:
      break
  }
function handleAlarm() {
  // å¤„理报警按钮点击事件
  console.log('报警按钮被点击')
}
function logout() {
  ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      userStore.logOut().then(() => {
        location.href = "/index"
      })
    })
    .catch(() => {})
}
const emits = defineEmits(["setLayout"])
function setLayout() {
  emits("setLayout")
function handleRobot() {
  // å¤„理大模型按钮点击事件
  console.log('大模型按钮被点击')
}
</script>
@@ -188,26 +131,35 @@
        }
      }
      .avatar-container {
        margin-right: 40px;
      .nav-btn-item {
        display: flex;
        align-items: center;
        margin-right: 20px;
        height: 70px;
        .avatar-wrapper {
          margin-top: 5px;
          position: relative;
          .user-avatar {
        .nav-btn {
          display: flex;
          align-items: center;
          justify-content: center;
            cursor: pointer;
            width: 40px;
            height: 40px;
            border-radius: 10px;
          color: #fff;
          background-color: rgba(255, 255, 255, 0.1);
          border-radius: 4px;
          padding: 8px 16px;
          &:hover {
            background-color: rgba(255, 255, 255, 0.2);
          }
          i {
            cursor: pointer;
            position: absolute;
            right: -20px;
            top: 25px;
            font-size: 12px;
          img {
            width: 20px;
            height: 20px;
            margin-right: 6px;
          }
          span {
            font-size: 14px;
            font-weight: 500;
          }
        }
      }
@@ -284,26 +236,35 @@
        }
      }
      .avatar-container {
        margin-right: 40px;
      .nav-btn-item {
        display: flex;
        align-items: center;
        margin-right: 20px;
        height: 70px;
        .avatar-wrapper {
          margin-top: 5px;
          position: relative;
          .user-avatar {
        .nav-btn {
          display: flex;
          align-items: center;
          justify-content: center;
            cursor: pointer;
            width: 40px;
            height: 40px;
            border-radius: 10px;
          color: #333;
          background-color: rgba(0, 0, 0, 0.05);
          border-radius: 4px;
          padding: 8px 16px;
          &:hover {
            background-color: rgba(0, 0, 0, 0.1);
          }
          i {
            cursor: pointer;
            position: absolute;
            right: -20px;
            top: 25px;
            font-size: 12px;
          img {
            width: 20px;
            height: 20px;
            margin-right: 6px;
          }
          span {
            font-size: 14px;
            font-weight: 500;
          }
        }
      }
zhitan-vue/src/layout/components/Settings/index.vue
@@ -134,6 +134,8 @@
function handleTheme(val) {
  settingsStore.sideTheme = val
  sideTheme.value = val
  // Update body class to match the theme
  document.querySelector("body").className = val === 'theme-dark' ? "themeDark" : "themeLight"
}
function saveSetting() {
  proxy.$modal.loading("正在保存到本地,请稍候...")
zhitan-vue/src/layout/components/Sidebar/SidebarItem.vue
@@ -86,7 +86,15 @@
    return props.basePath
  }
  if (routeQuery) {
    let query = JSON.parse(routeQuery);
    let query = routeQuery;
    // å¦‚æžœ routeQuery æ˜¯å­—符串,则尝试解析它
    if (typeof routeQuery === 'string') {
      try {
        query = JSON.parse(routeQuery);
      } catch (error) {
        console.error('Error parsing query string:', routeQuery, error);
      }
    }
    return { path: getNormalPath(props.basePath + '/' + routePath), query: query }
  }
  return getNormalPath(props.basePath + '/' + routePath)
zhitan-vue/src/layout/components/Sidebar/index.vue
@@ -3,17 +3,19 @@
    :class="{ 'has-logo': showLogo }"
    :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"
  >
    <logo v-if="showLogo" :collapse="isCollapse" />
    <el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
      <!-- é¦–页时不显示任何菜单项 -->
      <el-menu
        v-if="!isHomePage"
        :default-active="activeMenu"
        :collapse="isCollapse"
        :background-color="sideTheme === 'theme-dark' ? '#232D70' : '#fff'"
        :background-color="'transparent'"
        :text-color="sideTheme === 'theme-dark' ? '#fff' : '#000'"
        :unique-opened="true"
        :active-text-color="theme"
        :collapse-transition="false"
        mode="vertical"
        class="custom-menu"
      >
        <sidebar-item
          v-for="(route, index) in sidebarRouters"
@@ -22,24 +24,91 @@
          :base-path="route.path"
        />
      </el-menu>
      <!-- é¦–页时的空白区域 -->
      <div v-else class="home-empty-menu"></div>
    </el-scrollbar>
    <!-- åº•部用户区域 -->
    <div class="sidebar-footer" :class="{ 'collapsed': isCollapse, 'theme-light': sideTheme === 'theme-light' }">
      <div class="user-avatar-container">
        <img :src="userStore.avatar" class="user-avatar" />
      </div>
      <!-- å±•开状态下显示完整内容 -->
      <div class="user-info" v-if="!isCollapse">
        <div class="username">{{ userStore.name || 'admin' }}</div>
        <div class="action-buttons">
          <div class="action-button" :class="{'theme-light': sideTheme === 'theme-light'}" @click="toUserProfile">
            <el-icon><User /></el-icon>
            <span>个人中心</span>
          </div>
          <div class="action-button" :class="{'theme-light': sideTheme === 'theme-light'}" @click="toggleTheme">
            <el-icon><Brush /></el-icon>
            <span>切换主题</span>
          </div>
          <div class="action-button" :class="{'theme-light': sideTheme === 'theme-light'}" @click="handleLogout">
            <el-icon><SwitchButton /></el-icon>
            <span>退出登录</span>
          </div>
        </div>
      </div>
      <!-- æŠ˜å çŠ¶æ€ä¸‹åªæ˜¾ç¤ºå›¾æ ‡æŒ‰é’® -->
      <div class="collapsed-actions" v-if="isCollapse">
        <div class="action-icon" :class="{'theme-light': sideTheme === 'theme-light'}" @click="toUserProfile" title="个人中心">
          <el-icon><User /></el-icon>
        </div>
        <div class="action-icon" :class="{'theme-light': sideTheme === 'theme-light'}" @click="toggleTheme" title="切换主题">
          <el-icon><Brush /></el-icon>
        </div>
        <div class="action-icon" :class="{'theme-light': sideTheme === 'theme-light'}" @click="handleLogout" title="退出登录">
          <el-icon><SwitchButton /></el-icon>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup>
import Logo from "./Logo"
import SidebarItem from "./SidebarItem"
import variables from "@/assets/styles/variables.module.scss"
import useAppStore from "@/store/modules/app"
import useSettingsStore from "@/store/modules/settings"
import usePermissionStore from "@/store/modules/permission"
import useUserStore from "@/store/modules/user"
import { User, Brush, SwitchButton } from '@element-plus/icons-vue'
import { ElMessageBox } from 'element-plus'
import { useRouter } from 'vue-router'
const router = useRouter()
const route = useRoute()
const appStore = useAppStore()
const settingsStore = useSettingsStore()
const permissionStore = usePermissionStore()
const userStore = useUserStore()
const sidebarRouters = computed(() => permissionStore.sidebarRouters)
// åˆ¤æ–­å½“前是否为首页
const isHomePage = computed(() => {
  return route.path === '/index' || route.path === '/' || route.fullPath.startsWith('/index')
})
// é¦–页专用路由,只有首页一个菜单项
const homePageRouters = computed(() => {
  // ä»ŽåŽŸå§‹è·¯ç”±ä¸­ç­›é€‰å‡ºé¦–é¡µè·¯ç”±
  const homeRoute = sidebarRouters.value.find(route => {
    return route.children && route.children.find(child => child.path === '/index')
  })
  return homeRoute ? [homeRoute] : []
})
const showLogo = computed(() => settingsStore.sidebarLogo)
const sideTheme = computed(() => settingsStore.sideTheme)
const theme = computed(() => settingsStore.theme)
@@ -53,5 +122,278 @@
  }
  return path
})
function toUserProfile() {
  router.push('/user/profile')
}
function toggleTheme() {
  if (settingsStore.sideTheme == "theme-dark") {
    settingsStore.sideTheme = "theme-light"
    document.querySelector("body").className = "themeLight"
  } else {
    settingsStore.sideTheme = "theme-dark"
    document.querySelector("body").className = "themeDark"
  }
}
function handleLogout() {
  ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      userStore.logOut().then(() => {
        location.href = "/index"
      })
    })
    .catch(() => {})
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
:deep(.custom-menu) {
  padding: 6px 0;
  height: calc(100% - 150px); // ç•™å‡ºåº•部用户区域的空间
  // Override Element Plus default menu styles
  .el-menu-item {
    height: 38px !important;
    line-height: 38px !important;
    border-radius: 4px;
    margin: 4px 10px;
    width: calc(100% - 20px);
    &.is-active {
      background-color: #3883FA !important;
      color: #fff !important;
    }
    &:hover {
      background-color: rgba(56, 131, 250, 0.1) !important;
    }
  }
  .el-sub-menu {
    .el-sub-menu__title {
      height: 38px !important;
      line-height: 38px !important;
      border-radius: 4px;
      margin: 4px 10px;
      width: calc(100% - 20px);
      &:hover {
        background-color: rgba(56, 131, 250, 0.1) !important;
      }
    }
    .el-menu-item {
      padding-left: 45px !important;
      min-width: auto !important;
      &.is-active {
        padding-left: 45px !important;
      }
    }
    // For nested submenus
    .el-menu {
      .el-menu-item,
      .el-sub-menu__title {
        height: 38px !important;
        line-height: 38px !important;
      }
    }
  }
}
// é¦–页空白菜单区域样式
.home-empty-menu {
  height: calc(100% - 150px);
}
// åº•部用户区域样式
.sidebar-footer {
  position: absolute;
  bottom: 72px;
  left: 0;
  width: 100%;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  padding: 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  &.collapsed {
    padding: 10px;
    .user-avatar-container {
      margin-bottom: 10px;
    }
  }
  &.theme-light {
    background-color: rgba(255, 255, 255, 0.6);
    border-top: 1px solid rgba(0, 0, 0, 0.1);
    .user-avatar-container {
      border-color: rgba(0, 0, 0, 0.1);
    }
    .user-info {
      .username {
        color: #333;
      }
    }
  }
  .user-avatar-container {
    margin-bottom: 10px;
    border: 2px dashed rgba(255, 255, 255, 0.3);
    border-radius: 4px;
    width: 54px;
    height: 54px;
    display: flex;
    align-items: center;
    justify-content: center;
    .user-avatar {
      width: 38px;
      height: 38px;
      border-radius: 4px;
    }
  }
  .user-info {
    width: 100%;
    text-align: center;
    .username {
      color: #fff;
      font-size: 16px;
      font-weight: 500;
      margin-bottom: 16px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .action-buttons {
      .action-button {
        display: flex;
        align-items: center;
        justify-content: center;
        background: rgba(56, 131, 250, 0.11);
        border-radius: 9px;
        border: 1px solid rgba(255, 255, 255, 0.3);
        color: #fff;
        padding: 10px;
        margin-bottom: 10px;
        border-radius: 4px;
        cursor: pointer;
        transition: background-color 0.3s;
        &:hover {
          background: rgba(56, 131, 250, 0.2);
        }
        .el-icon {
          margin-right: 8px;
          font-size: 16px;
        }
        span {
          font-size: 14px;
        }
        &.theme-light {
          background-color: rgba(56, 131, 250, 1);
          color: #fff;
          border: 1px solid rgba(56, 131, 250, 0.8);
          &:hover {
            background-color: rgba(56, 131, 250, 0.9);
          }
          .el-icon {
            color: #fff;
          }
        }
      }
    }
  }
  .collapsed-actions {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    .action-icon {
      width: 40px;
      height: 40px;
      margin-bottom: 8px;
      background: rgba(56, 131, 250, 0.11);
      border: 1px solid rgba(255, 255, 255, 0.3);
      border-radius: 4px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      &:hover {
        background: rgba(56, 131, 250, 0.2);
      }
      .el-icon {
        font-size: 20px;
        color: #fff;
      }
      &.theme-light {
        background: rgba(56, 131, 250, 1);
        border: 1px solid rgba(56, 131, 250, 0.8);
        &:hover {
          background: rgba(56, 131, 250, 0.9);
        }
        .el-icon {
          color: #fff;
        }
      }
    }
  }
}
.theme-light {
  :deep(.custom-menu) {
    // Override Element Plus menu styles for light theme
    .el-menu-item {
      &.is-active {
        background-color: #3883FA !important;
        color: #fff !important;
      }
      &:hover {
        background-color: rgba(56, 131, 250, 0.1) !important;
      }
    }
    .el-sub-menu {
      .el-sub-menu__title {
        &:hover {
          background-color: rgba(56, 131, 250, 0.1) !important;
        }
      }
    }
  }
}
// Add global style to override Element Plus defaults
:global(.el-menu--vertical .el-menu-item),
:global(.el-menu--vertical .el-sub-menu__title) {
  height: 38px !important;
  line-height: 38px !important;
}
</style>
zhitan-vue/src/layout/index.vue
@@ -1,34 +1,99 @@
<template>
  <div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
    <div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
    <div class="navbar-container">
      <div class="navbar">
        <div class="left">
          <div class="sidebar-logo-container" :class="{ collapse: !sidebar.opened }">
            <div class="logo" v-if="systemInfo && systemInfo.leftLogo">
              <img v-if="sideTheme === 'theme-dark'" :src="systemInfo.leftLogo" class="sidebar-logo" />
              <img v-else :src="systemInfo.leftLogo" class="sidebar-logo" />
            </div>
            <div class="name" v-if="sidebar.opened" :style="{ color: '#fff' }">
              {{ title }}
            </div>
          </div>
          <hamburger
            id="hamburger-container"
            :is-active="appStore.sidebar.opened"
            class="hamburger-container"
            @toggleClick="toggleSideBar"
          />
        </div>
        <top-nav />
        <div class="right">
          <!-- æŠ¥è­¦å’Œå¤§æ¨¡åž‹æŒ‰é’® -->
          <div class="right-menu">
            <!-- æŠ¥è­¦æŒ‰é’® -->
            <div class="right-menu-item alarm-btn" @click="goToAlarm">
              <el-badge :value="alarmCount" :max="99" class="alarm-badge">
                <svg-icon icon-class="bell" class="right-menu-icon" />
              </el-badge>
              <span class="right-menu-text">报警</span>
            </div>
            <!-- å¤§æ¨¡åž‹æŒ‰é’® -->
            <div class="right-menu-item ai-btn" @click="openAIModel">
              <svg-icon icon-class="ai" class="right-menu-icon" />
              <span class="right-menu-text">智能助手</span>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="content-container">
    <sidebar v-if="!sidebar.hide" class="sidebar-container" />
    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
      <div :class="{ 'fixed-header': fixedHeader }">
        <navbar @setLayout="setLayout" v-if="!sidebar.hide"/>
        <tags-view v-if="needTagsView" v-show="!sidebar.hide"/>
      </div>
      <app-main />
      <settings ref="settingRef" />
    </div>
  </div>
  </div>
</template>
<script setup>
import { ref, computed, watchEffect, onMounted } from 'vue'
import { useWindowSize } from '@vueuse/core'
import { useRoute, useRouter } from 'vue-router'
import Sidebar from './components/Sidebar/index.vue'
import { AppMain, Navbar, Settings, TagsView } from './components'
import { AppMain, Settings, TagsView } from './components'
import TopNav from '@/components/TopNav'
import Hamburger from '@/components/Hamburger'
import defaultSettings from '@/settings'
import Cookies from "js-cookie"
import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings'
const route = useRoute()
const router = useRouter()
const appStore = useAppStore()
const settingsStore = useSettingsStore()
const theme = computed(() => settingsStore.theme);
const sideTheme = computed(() => settingsStore.sideTheme);
const sidebar = computed(() => useAppStore().sidebar);
const device = computed(() => useAppStore().device);
const sidebar = computed(() => appStore.sidebar);
const device = computed(() => appStore.device);
const needTagsView = computed(() => settingsStore.tagsView);
const fixedHeader = computed(() => settingsStore.fixedHeader);
const systemInfo = JSON.parse(Cookies.get("SystemInfo") || '{"systemName":"智汕能源管理系统","leftLogo":""}')
const title = systemInfo.systemName || import.meta.env.VITE_APP_TITLE
// æŠ¥è­¦æ•°é‡ï¼Œå¯ä»¥ä»ŽæŽ¥å£èŽ·å–
const alarmCount = ref(5)
// è·³è½¬åˆ°æŠ¥è­¦é¡µé¢
function goToAlarm() {
  router.push('/alarm/list')
}
// æ‰“å¼€AI大模型对话框
function openAIModel() {
  // è¿™é‡Œå¯ä»¥å®žçŽ°æ‰“å¼€AI对话框的逻辑
  console.log('打开AI大模型对话框')
}
const classObj = computed(() => ({
  hideSidebar: !sidebar.value.opened,
@@ -38,22 +103,46 @@
}))
const { width, height } = useWindowSize();
const WIDTH = 992; // refer to Bootstrap's responsive design
const WIDTH = 992;
watchEffect(() => {
  if (device.value === 'mobile' && sidebar.value.opened) {
    useAppStore().closeSideBar({ withoutAnimation: false })
    appStore.closeSideBar({ withoutAnimation: false })
  }
  if (width.value - 1 < WIDTH) {
    useAppStore().toggleDevice('mobile')
    useAppStore().closeSideBar({ withoutAnimation: true })
    appStore.toggleDevice('mobile')
    appStore.closeSideBar({ withoutAnimation: true })
  } else {
    useAppStore().toggleDevice('desktop')
    appStore.toggleDevice('desktop')
  }
})
// ç›‘听路由变化,处理首页的侧边栏显示
watchEffect(() => {
  // æ£€æŸ¥æ˜¯å¦æ˜¯é¦–页路由
  if (route.path === '/index' || route.path === '/') {
    // é¦–页路由,确保侧边栏不隐藏,但状态是折叠的
    appStore.showCollapsedSidebar()
  } else if (route.meta && route.meta.showSidebar === false) {
    // å¦‚果路由明确指定隐藏侧边栏
    appStore.toggleSideBarHide(true)
  }
})
// ç»„件挂载时,确保首页侧边栏状态正确
onMounted(() => {
  // å¦‚果当前是首页,确保侧边栏是折叠的而不是隐藏的
  if (route.path === '/index' || route.path === '/') {
    appStore.showCollapsedSidebar()
  }
})
function handleClickOutside() {
  useAppStore().closeSideBar({ withoutAnimation: false })
  appStore.closeSideBar({ withoutAnimation: false })
}
function toggleSideBar() {
  appStore.toggleSideBar()
}
const settingRef = ref(null);
@@ -69,8 +158,11 @@
.app-wrapper {
  @include clearfix;
  position: relative;
  height: 100%;
  width: 100%;
  height: 100vh;
  width: 100vw;
  display: flex;
  flex-direction: column;
  overflow-x: hidden;
  &.mobile.openSidebar {
    position: fixed;
@@ -88,24 +180,218 @@
  z-index: 999;
}
.fixed-header {
.navbar-container {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 60px;
  z-index: 1000;
  width: 100%;
}
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: var(--current-color);
  height: 60px;
  width: 100%;
  padding: 0;
  .left {
    display: flex;
    align-items: center;
    padding-left: 16px;
    .sidebar-logo-container {
      display: flex;
      align-items: center;
      height: 60px;
      padding: 0 15px;
      min-width: 220px;
      .logo {
        width: 40px;
        height: 40px;
        margin-right: 10px;
        flex-shrink: 0;
        .sidebar-logo {
          width: 100%;
          height: 100%;
        }
      }
      .name {
        font-family: OPPOSans, OPPOSans;
        font-weight: bold;
        font-size: 20px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      &.collapse {
        min-width: 70px;
        .name {
          display: none;
        }
      }
    }
  }
  .right {
    display: flex;
    align-items: center;
    padding-right: 20px;
    .right-menu {
      display: flex;
      align-items: center;
      .right-menu-item {
        display: flex;
        align-items: center;
        margin-left: 20px;
        cursor: pointer;
        color: #fff;
        font-size: 14px;
        transition: all 0.3s;
        &:hover {
          opacity: 0.8;
        }
        .right-menu-icon {
          font-size: 18px;
          margin-right: 5px;
        }
        .right-menu-text {
          margin-left: 5px;
        }
      }
      .alarm-badge {
        :deep(.el-badge__content) {
          background-color: #f56c6c;
        }
      }
    }
  }
}
.content-container {
  display: flex;
  position: relative;
  margin-top: 60px;
  height: calc(100vh - 60px);
  width: 100%;
  overflow: hidden;
}
.sidebar-container {
  position: relative;
  height: 100%;
  z-index: 900;
  flex-shrink: 0;
  width: $base-sidebar-width;
}
.fixed-header {
  position: fixed;
  top: 60px;
  right: 0;
  z-index: 9;
  width: calc(100% - #{$base-sidebar-width});
  width: 100%;
  transition: width 0.28s;
  display: flex;
  flex-direction: column;
}
.hideSidebar .fixed-header {
  width: calc(100% - 54px);
}
.sidebarHide .fixed-header {
  width: 100%;
}
.mobile .fixed-header {
  width: 100%;
}
.main-container {
  flex: 1;
  position: relative;
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  transition: margin-left 0.28s;
  box-sizing: border-box;
}
.hideSidebar .main-container {
  margin-left: 0;
}
.sidebarHide .main-container {
  margin-left: 0;
}
.mobile .main-container {
  margin-left: 0;
}
.hideSidebar .sidebar-container {
  width: 54px;
}
.sidebarHide .sidebar-container {
  display: none;
}
.themeDark {
  .navbar-container {
    background: #1a235d;
    border-bottom: 2px solid #110f2e;
  }
  .navbar {
    background: transparent !important;
  }
  .sidebar-container {
    background-color: #002866 !important;
  }
}
.themeLight {
  .navbar-container {
    background: #3883FA;
  }
  .navbar {
    background: transparent !important;
    .left {
      .sidebar-logo-container {
        .name {
          color: #fff !important;
        }
      }
    }
    .right {
      .right-menu {
        .right-menu-item {
          color: #fff;
          .right-menu-icon {
            color: #fff;
          }
        }
      }
    }
  }
}
</style>
zhitan-vue/src/permission.js
@@ -8,6 +8,7 @@
import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
import useTagsViewStore from '@/store/modules/tagsView'
NProgress.configure({ showSpinner: false });
@@ -36,6 +37,28 @@
                router.addRoute(route) // åŠ¨æ€æ·»åŠ å¯è®¿é—®è·¯ç”±è¡¨
              }
            })
            // å¦‚果是首页,自动重定向到第一个菜单
            if (to.path === '/' || to.path === '/index') {
              const permissionStore = usePermissionStore()
              const topMenus = permissionStore.topbarRouters.filter(menu => !menu.hidden)
              if (topMenus.length > 0) {
                // è·³è½¬åˆ°ç¬¬ä¸€ä¸ªèœå•
                const firstMenu = topMenus[0]
                if (firstMenu.children && firstMenu.children.length > 0) {
                  // æœ‰å­èœå•,跳转到第一个子菜单
                  const firstChild = firstMenu.children[0]
                  const path = firstMenu.path.endsWith('/') ? firstMenu.path + firstChild.path : `${firstMenu.path}/${firstChild.path}`
                  next({ path: path, replace: true })
                  return
                } else {
                  // æ²¡æœ‰å­èœå•,直接跳转
                  next({ path: firstMenu.path, replace: true })
                  return
                }
              }
            }
            next({ ...to, replace: true }) // hack方法 ç¡®ä¿addRoutes已完成
          })
        }).catch(err => {
@@ -45,6 +68,26 @@
          })
        })
      } else {
        // å¦‚果是首页,自动重定向到第一个菜单
        if (to.path === '/' || to.path === '/index') {
          const permissionStore = usePermissionStore()
          const topMenus = permissionStore.topbarRouters.filter(menu => !menu.hidden)
          if (topMenus.length > 0) {
            // è·³è½¬åˆ°ç¬¬ä¸€ä¸ªèœå•
            const firstMenu = topMenus[0]
            if (firstMenu.children && firstMenu.children.length > 0) {
              // æœ‰å­èœå•,跳转到第一个子菜单
              const firstChild = firstMenu.children[0]
              const path = firstMenu.path.endsWith('/') ? firstMenu.path + firstChild.path : `${firstMenu.path}/${firstChild.path}`
              next({ path: path, replace: true })
              return
            } else {
              // æ²¡æœ‰å­èœå•,直接跳转
              next({ path: firstMenu.path, replace: true })
              return
            }
          }
        }
        next()
      }
    }
@@ -62,4 +105,12 @@
router.afterEach(() => {
  NProgress.done()
  // ç§»é™¤æ‰€æœ‰å¯èƒ½çš„首页标签
  const tagsViewStore = useTagsViewStore();
  if (tagsViewStore && tagsViewStore.visitedViews) {
    tagsViewStore.visitedViews = tagsViewStore.visitedViews.filter(
      tag => tag.path !== '/index' && tag.path !== '/' && tag.name !== 'Index'
    );
  }
})
zhitan-vue/src/router/index.js
@@ -1,6 +1,7 @@
import { createWebHistory, createRouter } from 'vue-router'
/* Layout */
import Layout from '@/layout'
import useAppStore from '@/store/modules/app'
/**
 * Note: è·¯ç”±é…ç½®é¡¹
@@ -66,7 +67,13 @@
        path: '/index',
        component: () => import('@/views/index'),
        name: 'Index',
        meta: { title: '首页', icon: 'dashboard', affix: true }
        meta: { title: '首页', icon: 'dashboard', affix: true, showSidebar: true, breadcrumb: false },
        beforeEnter: (to, from, next) => {
          // èŽ·å–app store并设置侧边栏为折叠状态
          const appStore = useAppStore()
          appStore.showCollapsedSidebar()
          next()
        }
      }
    ]
  },
zhitan-vue/src/store/modules/app.js
@@ -7,7 +7,7 @@
      sidebar: {
        opened: sessionStorage.getItem('sidebarStatus') ? !!+sessionStorage.getItem('sidebarStatus') : true,
        withoutAnimation: false,
        hide: false
        hide: sessionStorage.getItem('sidebarHide') ? JSON.parse(sessionStorage.getItem('sidebarHide')) : false
      },
      device: 'desktop',
      size: sessionStorage.getItem('size') || 'default'
@@ -39,6 +39,20 @@
      },
      toggleSideBarHide(status) {
        this.sidebar.hide = status
        sessionStorage.setItem('sidebarHide', status)
      },
      openMenu() {
        this.sidebar.hide = false
        this.sidebar.opened = true
        sessionStorage.setItem('sidebarHide', 'false')
        sessionStorage.setItem('sidebarStatus', 1)
      },
      showCollapsedSidebar() {
        this.sidebar.hide = false
        this.sidebar.opened = false
        this.sidebar.withoutAnimation = false
        sessionStorage.setItem('sidebarHide', 'false')
        sessionStorage.setItem('sidebarStatus', 0)
      }
    }
  })
zhitan-vue/src/store/modules/permission.js
@@ -73,6 +73,14 @@
        route.component = loadView(route.component)
      }
    }
    // å¤„理 query å‚数,将字符串转换为对象
    if (route.query && typeof route.query === 'string') {
      try {
        route.query = JSON.parse(route.query);
      } catch (error) {
        console.error('Error parsing query string:', route.query, error);
      }
    }
    if (route.children != null && route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children, route, type)
    } else {
zhitan-vue/src/store/modules/settings.js
@@ -23,7 +23,7 @@
      // ä¿®æ”¹å¸ƒå±€è®¾ç½®
      changeSetting(data) {
        const { key, value } = data
        if (this.hasOwnProperty(key)) {
        if (key in this.$state) {
          this[key] = value
        }
      },
zhitan-vue/src/store/modules/tagsView.js
@@ -20,6 +20,11 @@
        )
      },
      addVisitedView(view) {
        // è¿‡æ»¤é¦–页标签
        if (view.path === '/index' || view.path === '/' || view.name === 'Index') {
          return;
        }
        if (this.visitedViews.some(v => v.path === view.path)) return
        this.visitedViews.push(
          Object.assign({}, view, {
@@ -110,7 +115,8 @@
      },
      delAllVisitedViews(view) {
        return new Promise(resolve => {
          const affixTags = this.visitedViews.filter(tag => tag.meta.affix)
          // è¿‡æ»¤æŽ‰é¦–页标签,只保留其他固定标签
          const affixTags = this.visitedViews.filter(tag => tag.meta.affix && tag.path !== '/index' && tag.path !== '/' && tag.name !== 'Index')
          this.visitedViews = affixTags
          this.iframeViews = []
          resolve([...this.visitedViews])
zhitan-vue/src/views/businessconfiguration/gatewaystatus/gatewayStatus.vue
@@ -1,5 +1,11 @@
<template>
  <div class="page">
    <div class="page-title">
      <div class="title-bar">
        <span class="title-text">网关状态监测</span>
      </div>
    </div>
    <div class="table-box">
      <div class="border">
        <div class="table" v-for="(item, index) in dataList" :key="index">
@@ -460,14 +466,62 @@
])
</script>
<style lang="scss" scoped>
<style scoped lang="scss">
@import "@/assets/styles/page.scss";
.page {
  background: #08234F;
  min-height: calc(100vh - 145px)
}
.page-title {
  position: relative;
  .title-bar {
    position: relative;
    padding: 14px 0;
    padding-left: 16px;
    .title-text {
      font-size: 18px;
      font-weight: 600;
      position: relative;
      padding-left: 22px;
      color: #fff;
      &::before {
        content: '';
        position: absolute;
        left: 0;
        top: 50%;
        transform: translateY(-50%);
        width: 5px;
        height: 18px;
        background-color: #3883FA;
        border-radius: 2px;
      }
    }
  }
  &::after {
    content: '';
    position: absolute;
    bottom: -4px;
    opacity: 0.12;
    left: 0;
    width: 100%;
    height: 1px;
    background-color: #E6E6E6;
  }
}
.themeDark {
  .border {
    border: 1px solid #fff;
    color: #fff;
    .table {
      margin-top: 10px;
      display: flex;
      align-items: center;
      justify-content: flex-start;
@@ -544,6 +598,10 @@
        }
      }
    }
  }
  .table-box {
    margin: 20px 25px;
  }
}
@@ -630,5 +688,15 @@
      }
    }
  }
  .table-box {
    margin: 10px 25px;
  }
}
.page {
  .table-box {
    // ... ä¿æŒçŽ°æœ‰æ ·å¼
  }
}
</style>
zhitan-vue/src/views/dataMonitoring/historyDataTrend/index.vue
@@ -210,6 +210,10 @@
<style scoped lang="scss">
@import "@/assets/styles/page.scss";
.page {
  background-color: #08234F;
}
.themeDark {
  .card-list {
    width: 100%;
zhitan-vue/src/views/energyanalysis/equipment/equipment.vue
@@ -115,9 +115,10 @@
</template>
<script setup name="equipment">
import { listRegion, listDepartment } from "@/api/energyAnalysis/energyAnalysis"
import { listDepartment } from "@/api/energyAnalysis/energyAnalysis"
import { listEnergyTypeList } from "@/api/modelConfiguration/energyType"
import * as echarts from "echarts"
import request from "@/utils/request"
const { proxy } = getCurrentInstance()
const { period } = proxy.useDict("period")
import { useRoute } from "vue-router"
@@ -183,12 +184,16 @@
  // }
  const myChart1 = echarts.init(document.getElementById("Chart1"))
  // const myChart2 = echarts.init(document.getElementById("Chart2"));
  listRegion(
    proxy.addDateRange({
  // ä¿®æ”¹ä¸ºç›´æŽ¥è°ƒç”¨consumptionanalysis/getByArea接口
  request({
    url: "/consumptionanalysis/getByArea",
    method: "get",
    params: proxy.addDateRange({
      ...queryParams.value,
      ...query.value,
    })
  ).then((res) => {
  }).then((res) => {
    if (!!res.code && res.code == 200) {
      loading.value = false
      let xdata = []
@@ -551,7 +556,7 @@
// èƒ½è€—对比分析-设备能耗分析-导出
function handleExport() {
  proxy.download(
    "consumptionanalysis/energyExport",
    "consumptionanalysis/getByArea/export",
    {
      ...queryParams.value,
      ...query.value,
zhitan-vue/src/views/index.vue
@@ -1,22 +1,27 @@
<template>
  <div class="page" style="padding-left: 8px; padding-top: 8px">
    <CardHeader :showBtn="true" :active="'0'" :period="period" @handleClick="handleTimeType">
      <span>
      <span class="card-header-title">
        å…¨åŽ‚èƒ½è€—ç»Ÿè®¡
        <el-button @click="dialogVisible = true" v-if="list.length > 1"> æŸ¥çœ‹æ›´å¤š </el-button>
        <el-button @click="dialogVisible = true" v-if="list.length > 1" type="primary" size="small" class="header-more-btn"> æŸ¥çœ‹æ›´å¤š </el-button>
      </span>
    </CardHeader>
    <template v-for="(row, rowIndex) in list" :key="rowIndex" v-loading="loading02">
      <div class="card-list" v-if="settingsStore.sideTheme == 'theme-dark' && rowIndex == 0">
        <template v-for="(item, index) in row" :key="index">
          <div
            class="card-list-item"
          <div class="card-list-item">
            <div class="item-left">
              <div class="top-icon"
            :style="{
              backgroundImage: 'url(' + bgList[index].bg + ')',
                  backgroundImage: 'url(' + bgList[index].icon + ')',
                  backgroundColor: bgList[index].iconBg,
                  width: '73px',
                  height: '73px',
                  backgroundSize: '40px'
            }"
          >
            <div class="item-top">
              <div class="top-icon" :style="{ backgroundImage: 'url(' + bgList[index].icon + ')' }" />
              />
            </div>
            <div class="item-right">
              <div class="top-right">
                <div class="right-name">
                  {{ item.energyName }}
@@ -24,28 +29,27 @@
                </div>
                <div class="right-value">
                  <span> {{ item.count }}</span>
                  <!-- <span class="unit">{{ item.energyUnit }}</span> -->
                </div>
              </div>
            </div>
            <div class="item-bottom">
              <div class="bottom-left">
                <span>
                  åŒæ¯”: {{ Math.abs(item.tongbi) }}
                  <el-icon :color="item.tongbi > 0 ? 'green' : item.tongbi < 0 ? 'red' : ''">
                    åŒæ¯”: {{ Math.abs(item.tongbi).toFixed(1) }}
                    <el-icon :color="item.tongbi > 0 ? '#4CAF50' : item.tongbi < 0 ? '#F44336' : ''">
                    <Top v-if="item.tongbi > 0" />
                    <Bottom v-if="item.tongbi < 0" />
                  </el-icon>
                </span>
              </div>
              <div class="bottom-right">
                <span
                  >环比: {{ Math.abs(item.huanbi) }}
                  <el-icon :color="item.huanbi > 0 ? 'green' : item.huanbi < 0 ? 'red' : ''">
                  <span>
                    çŽ¯æ¯”: {{ Math.abs(item.huanbi).toFixed(1) }}
                    <el-icon :color="item.huanbi > 0 ? '#4CAF50' : item.huanbi < 0 ? '#F44336' : ''">
                    <Top v-if="item.huanbi > 0" />
                    <Bottom v-if="item.huanbi < 0" />
                  </el-icon>
                </span>
                </div>
              </div>
            </div>
          </div>
@@ -54,36 +58,46 @@
      <div class="card-list" v-if="settingsStore.sideTheme != 'theme-dark' && rowIndex == 0">
        <template v-for="(item, index) in row" :key="index" v-show="rowIndex == 0">
          <div class="card-list-item">
            <div class="item-top">
              <div class="top-icon" :style="{ backgroundImage: 'url(' + bgList[index].icon2 + ')' }" />
            <div class="item-left">
              <div class="top-icon"
                :style="{
                  backgroundImage: 'url(' + bgList[index].icon2 + ')',
                  backgroundColor: bgList[index].iconBg,
                  width: '73px',
                  height: '73px',
                  backgroundSize: '40px'
                }"
              />
            </div>
            <div class="item-right">
              <div class="top-right">
                <div class="right-name">
                  {{ item.energyName }}
                  <span v-if="item.energyUnit" class="unit">({{ item.energyUnit }})</span>
                </div>
                <div class="right-value">
                  <span>{{ item.count }}</span>
                  <span class="unit">{{ item.energyUnit }}</span>
                </div>
              </div>
            </div>
            <div class="item-bottom">
              <div class="bottom-left">
                <span>
                  åŒæ¯”: {{ Math.abs(item.tongbi) }}
                  <el-icon :color="item.tongbi > 0 ? 'green' : item.tongbi < 0 ? 'red' : ''">
                    åŒæ¯”: {{ Math.abs(item.tongbi).toFixed(1) }}
                    <el-icon :color="item.tongbi > 0 ? '#4CAF50' : item.tongbi < 0 ? '#F44336' : ''">
                    <Top v-if="item.tongbi > 0" />
                    <Bottom v-if="item.tongbi < 0" />
                  </el-icon>
                </span>
              </div>
              <div class="bottom-right">
                <span
                  >环比: {{ Math.abs(item.huanbi) }}
                  <el-icon :color="item.huanbi > 0 ? 'green' : item.huanbi < 0 ? 'red' : ''">
                  <span>
                    çŽ¯æ¯”: {{ Math.abs(item.huanbi).toFixed(1) }}
                    <el-icon :color="item.huanbi > 0 ? '#4CAF50' : item.huanbi < 0 ? '#F44336' : ''">
                    <Top v-if="item.huanbi > 0" />
                    <Bottom v-if="item.huanbi < 0" />
                  </el-icon>
                </span>
                </div>
              </div>
            </div>
          </div>
@@ -133,66 +147,71 @@
        </el-col>
      </el-row>
    </div>
    <el-dialog v-model="dialogVisible" title="查看全厂能耗统计" width="80%" v-if="dialogVisible">
    <el-dialog v-model="dialogVisible" title="查看全厂能耗统计" width="90%" v-if="dialogVisible">
      <template v-for="(row, rowIndex) in list" :key="rowIndex">
        <div class="card-list" v-if="settingsStore.sideTheme == 'theme-dark'">
          <template v-for="(item, index) in row" :key="index">
            <div
              class="card-list-item"
              :style="{
                backgroundImage: 'url(' + bgList[index].bg + ')',
              }"
            >
              <div class="item-top">
                <div
                  class="top-icon"
            <div class="card-list-item">
              <div class="item-left">
                <div class="top-icon"
                  :style="{
                    backgroundImage: 'url(' + bgList[index].icon + ')',
                    backgroundColor: bgList[index].iconBg,
                    width: '73px',
                    height: '73px',
                    backgroundSize: '40px'
                  }"
                />
              </div>
              <div class="item-right">
                <div class="top-right">
                  <div class="right-name">
                    {{ item.energyName }}
                    <span v-if="item.energyUnit" class="unit">({{ item.energyUnit }})</span>
                  </div>
                  <div class="right-value">
                    <span> {{ item.count }}</span>
                    <span class="unit">{{ item.energyUnit }}</span>
                  </div>
                </div>
              </div>
              <div class="item-bottom">
                <div class="bottom-left">
                  <span>
                    åŒæ¯”: {{ Math.abs(item.tongbi) }}
                    <el-icon :color="item.tongbi > 0 ? 'green' : item.tongbi < 0 ? 'red' : ''">
                      åŒæ¯”: {{ Math.abs(item.tongbi).toFixed(1) }}
                      <el-icon :color="item.tongbi > 0 ? '#4CAF50' : item.tongbi < 0 ? '#F44336' : ''">
                      <Top v-if="item.tongbi > 0" />
                      <Bottom v-if="item.tongbi < 0" />
                    </el-icon>
                  </span>
                </div>
                <div class="bottom-right">
                  <span
                    >环比: {{ Math.abs(item.huanbi) }}
                    <el-icon :color="item.huanbi > 0 ? 'green' : item.huanbi < 0 ? 'red' : ''">
                    <span>
                      çŽ¯æ¯”: {{ Math.abs(item.huanbi).toFixed(1) }}
                      <el-icon :color="item.huanbi > 0 ? '#4CAF50' : item.huanbi < 0 ? '#F44336' : ''">
                      <Top v-if="item.huanbi > 0" />
                      <Bottom v-if="item.huanbi < 0" />
                    </el-icon>
                  </span>
                  </div>
                </div>
              </div>
            </div>
          </template>
        </div>
        <div class="card-list" v-if="settingsStore.sideTheme != 'theme-dark'">
          <template v-for="(item, index) in row" :key="index" v-show="rowIndex == 0">
          <template v-for="(item, index) in row" :key="index">
            <div class="card-list-item">
              <div class="item-top">
                <div
                  class="top-icon"
              <div class="item-left">
                <div class="top-icon"
                  :style="{
                    backgroundImage: 'url(' + bgList[index].icon2 + ')',
                    backgroundColor: bgList[index].iconBg,
                    width: '73px',
                    height: '73px',
                    backgroundSize: '40px'
                  }"
                />
              </div>
              <div class="item-right">
                <div class="top-right">
                  <div class="right-name">
                    {{ item.energyName }}
@@ -202,21 +221,20 @@
                    <span class="unit">{{ item.energyUnit }}</span>
                  </div>
                </div>
              </div>
              <div class="item-bottom">
                <div class="bottom-left">
                  <span>
                    åŒæ¯”: {{ Math.abs(item.tongbi) }}
                    <el-icon :color="item.tongbi > 0 ? 'green' : item.tongbi < 0 ? 'red' : ''">
                      åŒæ¯”: {{ Math.abs(item.tongbi).toFixed(1) }}
                      <el-icon :color="item.tongbi > 0 ? '#4CAF50' : item.tongbi < 0 ? '#F44336' : ''">
                      <Top v-if="item.tongbi > 0" />
                      <Bottom v-if="item.tongbi < 0" />
                    </el-icon>
                  </span>
                </div>
                <div class="bottom-right">
                  <span
                    >环比: {{ Math.abs(item.huanbi) }}
                    <el-icon :color="item.huanbi > 0 ? 'green' : item.huanbi < 0 ? 'red' : ''">
                    <span>
                      çŽ¯æ¯”: {{ Math.abs(item.huanbi).toFixed(1) }}
                      <el-icon :color="item.huanbi > 0 ? '#4CAF50' : item.huanbi < 0 ? '#F44336' : ''">
                      <Top v-if="item.huanbi > 0" />
                      <Bottom v-if="item.huanbi < 0" />
                    </el-icon>
@@ -224,7 +242,7 @@
                </div>
              </div>
            </div>
            <div class="line"></div>
            </div>
          </template>
        </div>
      </template>
@@ -258,42 +276,47 @@
import index_card_3 from "@/assets/images/home/index-card-3.png"
import index_card_4 from "@/assets/images/home/index-card-4.png"
import index_card_5 from "@/assets/images/home/index-card-5.png"
import card_icon_1 from "@/assets/images/home/card-icon-1.png"
import card_icon_2 from "@/assets/images/home/card-icon-2.png"
import card_icon_3 from "@/assets/images/home/card-icon-3.png"
import card_icon_4 from "@/assets/images/home/card-icon-4.png"
import card_icon_5 from "@/assets/images/home/card-icon-5.png"
import card_icon2_1 from "@/assets/images/home/card-icon2-1.png"
import card_icon2_2 from "@/assets/images/home/card-icon2-2.png"
import card_icon2_3 from "@/assets/images/home/card-icon2-3.png"
import card_icon2_4 from "@/assets/images/home/card-icon2-4.png"
import card_icon2_5 from "@/assets/images/home/card-icon2-5.png"
import card_icon_1 from "@/assets/images/2.png"
import card_icon_2 from "@/assets/images/3.png"
import card_icon_3 from "@/assets/images/5.png"
import card_icon_4 from "@/assets/images/6.png"
import card_icon_5 from "@/assets/images/7.png"
import card_icon2_1 from "@/assets/images/2.png"
import card_icon2_2 from "@/assets/images/3.png"
import card_icon2_3 from "@/assets/images/5.png"
import card_icon2_4 from "@/assets/images/6.png"
import card_icon2_5 from "@/assets/images/7.png"
import { fa } from "element-plus/es/locales.mjs"
const bgList = ref([
  {
    bg: index_card_1,
    icon: card_icon_1,
    icon2: card_icon2_1,
    iconBg: "#3F7EE8"
  },
  {
    bg: index_card_2,
    icon: card_icon_2,
    icon2: card_icon2_2,
    iconBg: "#FFA024"
  },
  {
    bg: index_card_3,
    icon: card_icon_3,
    icon2: card_icon2_3,
    iconBg: "#FFCC00"
  },
  {
    bg: index_card_4,
    icon: card_icon_4,
    icon2: card_icon2_4,
    iconBg: "#3CC8D9"
  },
  {
    bg: index_card_5,
    icon: card_icon_5,
    icon2: card_icon2_5,
    iconBg: "#8833FF"
  },
])
const list = ref([[{}, {}, {}, {}, {}]])
@@ -857,10 +880,24 @@
}
</script>
<style scoped lang="scss">
.card-header-title {
  font-size: 18px;
  font-weight: bold;
  display: flex;
  align-items: center;
  .header-more-btn {
    margin-left: 12px;
    padding: 4px 10px;
    font-size: 12px;
    height: 28px;
  }
}
.themeDark {
  .page {
    padding: 20px;
    background: #120f2e;
    background: #05234A;
    .card-title {
      width: 132px;
@@ -873,89 +910,129 @@
    .card-list {
      margin-top: 14px;
      display: flex;
      // justify-content: space-between;
      width: 100%;
      flex-wrap: wrap;
      justify-content: space-between;
      gap: 15px;
      .card-list-item {
        width: 19%;
        margin-right: 1%;
        height: 157px;
        background-size: 100% 100%;
        box-sizing: border-box;
        padding: 25px 18px 12px 16px;
        color: #fff;
        .item-top {
          display: flex;
          .top-icon {
            width: 50px;
            height: 50px;
            background-size: 100% 100%;
      &:after {
        content: "";
        flex: auto;
          }
      .card-list-item {
        width: 320px;
        height: 127px;
        background: rgba(242, 246, 250, 0.1);
        box-sizing: border-box;
        padding: 16px;
        color: #fff;
        border-radius: 9px;
        box-shadow: none;
        border: none;
        display: flex;
        flex-direction: row;
        align-items: center;
        &:hover {
          background: rgba(242, 246, 250, 0.15);
        }
        .item-left {
          margin-right: 16px;
          .top-icon {
            width: 73px;
            height: 73px;
            background-size: 40px;
            background-repeat: no-repeat;
            background-position: center;
            border-radius: 50%;
          }
        }
        .item-right {
          flex: 1;
          display: flex;
          flex-direction: column;
          .top-right {
            margin-left: 12px;
            display: flex;
            flex-direction: column;
            .right-name {
              font-weight: bold;
              font-size: 16px;
              font-family: OPPOSans-Bold;
              font-weight: 400;
              font-size: 14px;
              font-family: OPPOSans-Regular;
              color: rgba(255, 255, 255, 0.65);
              .unit {
                color: rgba(255, 255, 255, 0.65);
                margin-left: 2px;
                font-size: 16px;
                font-size: 12px;
                font-weight: normal;
              }
            }
            .right-value {
              font-weight: 800;
              font-size: 25px;
              margin-top: 10px;
              font-weight: 500;
              font-size: 26px;
              margin-top: 4px;
              font-family: OPPOSans-Medium;
              .unit {
                margin-left: 5px;
                font-size: 16px;
                font-weight: normal;
              }
            }
              color: #fff;
              line-height: 1;
          }
        }
        .item-bottom {
          display: flex;
          justify-content: space-between;
          margin-top: 18px;
            margin-top: 14px;
          font-family: OPPOSans, OPPOSans;
          font-weight: bold;
          font-size: 14px;
            font-weight: normal;
            font-size: 12px;
            color: rgba(255, 255, 255, 0.5);
            line-height: 1;
            .bottom-left, .bottom-right {
              display: flex;
              align-items: center;
              :deep(.el-icon) {
                margin-left: 4px;
                font-size: 12px;
              }
            }
          }
        }
      }
    }
    .page-main {
      margin-top: 23px;
    }
  }
      margin-top: 20px;
  .chart {
    width: 100%;
    height: 292px;
    margin-top: 10px;
      .el-card {
        background-color: #0E2E5E;
        border: none;
        border-radius: 6px;
        :deep(.el-card__body) {
          padding: 12px;
        }
      }
  }
  .top-header {
    margin-top: 15px;
    height: 23px;
      margin-top: 12px;
      height: 32px;
    font-family: OPPOSans, OPPOSans;
    font-weight: 500;
    font-size: 14px;
    color: rgba(196, 213, 255, 0.6);
    border-bottom: 1px solid rgba(196, 213, 255, 0.6);
      color: rgba(255, 255, 255, 0.8);
      border-bottom: 1px solid rgba(255, 255, 255, 0.2);
    display: flex;
    justify-content: space-between;
      align-items: center;
      padding: 0 10px;
    .header-left {
      display: flex;
@@ -965,6 +1042,38 @@
        margin-right: 7px;
      }
    }
    }
  }
  .chart {
    width: 100%;
    height: 292px;
    margin-top: 10px;
  }
  :deep(.el-button--primary) {
    background-color: #1976D2;
    border-color: #1976D2;
  }
  :deep(.el-card__header) {
    padding: 10px 15px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  }
  :deep(.el-tabs--card > .el-tabs__header .el-tabs__item) {
    border-color: rgba(255, 255, 255, 0.1);
    background-color: transparent;
    color: #fff;
    &.is-active {
      background-color: #1976D2;
      border-color: #1976D2;
    }
  }
  :deep(.el-tabs--card > .el-tabs__header) {
    border-color: rgba(255, 255, 255, 0.1);
  }
}
@@ -978,101 +1087,131 @@
      height: 29px;
      font-weight: bold;
      font-size: 22px;
      color: #ffffff;
      color: #333;
    }
    .card-list {
      width: 100%;
      margin-top: 14px;
      display: flex;
      // justify-content: space-between;
      align-items: center;
      background-image: url("@/assets/images/home/index-card-bg2.png");
      background-size: 100% 100%;
      width: 100%;
      flex-wrap: wrap;
      border-radius: 20px;
      justify-content: space-between;
      gap: 15px;
      &:after {
        content: "";
        flex: auto;
      }
      .card-list-item {
        width: 19%;
        margin-right: 0.5%;
        height: 157px;
        background-size: 100% 100%;
        width: 320px;
        height: 127px;
        background: #fff;
        box-sizing: border-box;
        padding: 25px 18px 12px 16px;
        color: #fff;
        .item-top {
        padding: 16px;
        color: #333;
        border-radius: 9px;
        box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
          display: flex;
        flex-direction: row;
        align-items: center;
        .item-left {
          margin-right: 16px;
          .top-icon {
            width: 69px;
            height: 69px;
            background-size: 100% 100%;
            width: 73px;
            height: 73px;
            background-size: 40px;
            background-repeat: no-repeat;
            background-position: center;
            border-radius: 50%;
          }
        }
        .item-right {
          flex: 1;
          display: flex;
          flex-direction: column;
          .top-right {
            margin-left: 16px;
            display: flex;
            flex-direction: column;
            .right-name {
              font-weight: bold;
              font-size: 16px;
              font-family: OPPOSans-Bold;
            }
            .right-value {
              font-weight: 800;
              font-size: 30px;
              margin-top: 10px;
              font-family: OPPOSans-Medium;
              font-weight: 400;
              font-size: 14px;
              font-family: OPPOSans-Regular;
              color: rgba(0, 0, 0, 0.65);
              .unit {
                margin-left: 5px;
                font-size: 16px;
                color: rgba(0, 0, 0, 0.65);
                margin-left: 2px;
                font-size: 12px;
                font-weight: normal;
              }
            }
            .right-value {
              font-weight: 500;
              font-size: 26px;
              margin-top: 4px;
              font-family: OPPOSans-Medium;
              color: #333;
              line-height: 1;
          }
        }
        .item-bottom {
          display: flex;
          justify-content: space-between;
          margin-top: 18px;
            margin-top: 14px;
          font-family: OPPOSans, OPPOSans;
          font-weight: bold;
          font-size: 14px;
        }
      }
            font-weight: normal;
            font-size: 12px;
            color: rgba(0, 0, 0, 0.5);
            line-height: 1;
      .line {
        width: 1px;
        height: 64px;
        background-image: url("@/assets/images/home/line@2x.png");
        background-size: 100% 100%;
            .bottom-left, .bottom-right {
              display: flex;
              align-items: center;
              :deep(.el-icon) {
                margin-left: 4px;
                font-size: 12px;
              }
            }
          }
        }
      }
    }
    .page-main {
      margin-top: 23px;
    }
  }
      margin-top: 20px;
  .chart {
    width: 100%;
    height: 292px;
    margin-top: 10px;
      .el-card {
        background-color: #fff;
        border: none;
        border-radius: 6px;
        box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
        :deep(.el-card__body) {
          padding: 12px;
        }
      }
  }
  .top-header {
    margin-top: 15px;
    height: 23px;
      margin-top: 12px;
      height: 32px;
    font-family: OPPOSans, OPPOSans;
    font-weight: 500;
    font-size: 14px;
    color: rgba(29, 29, 29, 0.6);
    border-bottom: 1px solid rgba(196, 213, 255, 0.6);
      color: rgba(0, 0, 0, 0.6);
      border-bottom: 1px solid rgba(0, 0, 0, 0.1);
    display: flex;
    justify-content: space-between;
      align-items: center;
      padding: 0 10px;
    .header-left {
      display: flex;
@@ -1084,4 +1223,35 @@
    }
  }
}
  .chart {
    width: 100%;
    height: 292px;
    margin-top: 10px;
  }
  :deep(.el-button--primary) {
    background-color: #1976D2;
    border-color: #1976D2;
  }
  :deep(.el-card__header) {
    padding: 10px 15px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  }
  :deep(.el-tabs--card > .el-tabs__header .el-tabs__item) {
    border-color: rgba(0, 0, 0, 0.1);
    &.is-active {
      background-color: #1976D2;
      border-color: #1976D2;
      color: #fff;
    }
  }
  :deep(.el-tabs--card > .el-tabs__header) {
    border-color: rgba(0, 0, 0, 0.1);
  }
}
</style>
zhitan-vue/src/views/realtimemonitor/gatewaystatus/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,383 @@
<template>
  <div class="page">
    <!-- æ·»åŠ æ ‡é¢˜æ  -->
    <div class="page-title">
      <div class="title-bar">
        <span class="title-text">网关状态监测</span>
      </div>
    </div>
    <div class="table-box">
      <div class="border">
        <div class="table" v-for="(item, index) in dataList" :key="index">
          <div class="num">
            <div class="li name">{{ item.name }}</div>
            <div class="firstLi">
              <div :class="item.list.length < 16 ? 'li hasRightLine' : 'li'" v-for="(i, inde) in item.list" :key="inde">
                <div class="title_num" v-if="i.title_num">{{ i.title_num }}</div>
                <div class="dot" v-if="i.dot && i.dot.length > 0">
                  <div class="dot_li" v-for="(j, ind) in 3" :key="ind"></div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup>
let dataList = ref([
  {
    name: "网关",
    list: [
      {
        id: 1,
        title_num: "数据库",
        dot: [],
      },
      {
        id: 2,
        title_num: "200311",
        dot: [],
      },
      {
        id: 3,
        title_num: "200311",
        dot: [],
      },
      {
        id: 4,
        title_num: "200311",
        dot: [],
      },
      {
        id: 5,
        title_num: "200311",
        dot: [],
      },
      {
        id: 6,
        title_num: "200311",
        dot: [],
      },
      {
        id: 7,
        title_num: "200311",
        dot: [],
      },
      {
        id: 8,
        title_num: "200311",
        dot: [],
      },
      {
        id: 9,
        title_num: "200311",
        dot: [],
      },
      {
        id: 10,
        title_num: "200311",
        dot: [],
      },
      {
        id: 11,
        title_num: "200311",
        dot: [],
      },
      {
        id: 12,
        title_num: "200311",
        dot: [],
      },
      {
        id: 13,
        title_num: "200311",
        dot: [],
      },
      {
        id: 14,
        title_num: "200461",
        dot: [],
      },
      {
        id: 15,
        title_num: "200475",
        dot: [],
      },
    ],
  },
  {
    name: "计算器具",
    list: [
      {
        id: 1,
        title_num: "",
        dot: [
          {
            id: "001",
            dot_li: "",
          },
          {
            id: "002",
            dot_li: "",
          },
          {
            id: "003",
            dot_li: "",
          },
        ],
      },
      {
        id: 2,
        title_num: "",
        dot: [
          {
            id: "001",
            dot_li: "",
          },
          {
            id: "002",
            dot_li: "",
          },
        ],
      },
      {
        id: 3,
        title_num: "",
        dot: [
          {
            id: "001",
            dot_li: "",
          },
          {
            id: "002",
            dot_li: "",
          },
          {
            id: "003",
            dot_li: "",
          },
        ],
      },
    ],
  },
  {
    name: "网关",
    list: [
      {
        id: 1,
        title_num: "数据库",
        dot: [],
      },
      {
        id: 2,
        title_num: "200311",
        dot: [],
      },
      {
        id: 3,
        title_num: "200311",
        dot: [],
      },
      {
        id: 4,
        title_num: "200311",
        dot: [],
      },
    ],
  }
])
</script>
<style scoped lang="scss">
@import "@/assets/styles/page.scss";
.page {
  background: #08234F;
}
// æ ‡é¢˜æ ·å¼
.page-title {
  margin: 0 16px 16px;
  position: relative;
  .title-bar {
    position: relative;
    padding: 14px 0;
    padding-left: 16px;
    .title-text {
      font-size: 18px;
      font-weight: 600;
      position: relative;
      padding-left: 12px;
      color: #fff;
      &::before {
        content: '';
        position: absolute;
        left: 0;
        top: 50%;
        transform: translateY(-50%);
        width: 5px;
        height: 18px;
        background-color: #3883FA;
        border-radius: 2px;
      }
    }
  }
  &::after {
    content: '';
    position: absolute;
    bottom: -10px;
    left: 0;
    width: 100%;
    height: 1px;
    background-color: #E6E6E6;
  }
}
.themeDark {
  .table-box {
    margin: 10px 25px;
    .border {
      border: 1px solid #22408c;
      border-radius: 8px;
      background: #1a235d;
      padding: 10px 0;
      .table {
        margin: 15px;
        // border: 1px solid #22408c;
        .num {
          height: 56px;
          display: flex;
        }
        .li {
          height: 56px;
          padding: 0 10px;
          min-width: 60px;
          display: flex;
          flex-direction: column;
          justify-content: space-evenly;
          align-items: center;
          border-top: 1px solid #22408c;
          border-left: 1px solid #22408c;
          border-bottom: 1px solid #22408c;
          position: relative;
        }
        .hasRightLine {
          border-right: 1px solid #22408c;
        }
        .name {
          width: 80px;
          background: #1a235d;
          color: rgba(255, 255, 255, 0.8);
          text-align: center;
          font-family: OPPOSans, OPPOSans;
          font-weight: 500;
          font-size: 16px;
        }
        .title_num {
          color: #eeeeee;
          font-family: OPPOSans, OPPOSans;
          font-weight: 500;
          font-size: 16px;
        }
        .dot {
          display: flex;
          flex-direction: column;
          // justify-content: space-evenly;
          height: 30px;
          width: 12px;
          align-items: center;
        }
        .dot_li {
          height: 8px;
          width: 8px;
          margin: 2px 0;
          background: red;
          border-radius: 50%;
        }
        .firstLi {
          display: flex;
          flex: 1;
        }
      }
    }
  }
}
.themeLight {
  .table-box {
    margin: 10px 25px;
    .border {
      border: 1px solid #ebebeb;
      border-radius: 8px;
      background: #fff;
      padding: 10px 0;
      .table {
        margin: 15px;
        // border: 1px solid #22408c;
        .num {
          height: 56px;
          display: flex;
        }
        .li {
          height: 56px;
          padding: 0 10px;
          min-width: 60px;
          display: flex;
          flex-direction: column;
          justify-content: space-evenly;
          align-items: center;
          border-top: 1px solid #ebebeb;
          border-left: 1px solid #ebebeb;
          border-bottom: 1px solid #ebebeb;
          position: relative;
        }
        .hasRightLine {
          border-right: 1px solid #ebebeb;
        }
        .name {
          width: 80px;
          background: #fff;
          color: #0d0d0d;
          text-align: center;
          font-family: OPPOSans, OPPOSans;
          font-weight: 500;
          font-size: 16px;
        }
        .title_num {
          color: #0d0d0d;
          font-family: OPPOSans, OPPOSans;
          font-weight: 500;
          font-size: 16px;
        }
        .dot {
          display: flex;
          flex-direction: column;
          // justify-content: space-evenly;
          height: 30px;
          width: 12px;
          align-items: center;
        }
        .dot_li {
          height: 8px;
          width: 8px;
          margin: 2px 0;
          background: red;
          border-radius: 50%;
        }
        .firstLi {
          display: flex;
          flex: 1;
        }
      }
    }
  }
}
</style>
zhitan-vue/src/views/realtimemonitor/realtimemonitor/realtimemonitor.vue
@@ -2,7 +2,7 @@
  <div class="page">
    <div class="page-container">
      <div class="page-container-left">
        <LeftTree ref="leftTreeRef" @handleNodeClick="handleNodeClick" />
        <LeftTree ref="leftTreeRef" @handleNodeClick="handleNodeClick" ParentModelCode="YSCJMX" />
      </div>
      <div class="page-container-right">
        <div class="form-card">
@@ -207,8 +207,8 @@
      text-align: left;
      font-weight: bold;
      font-family: OPPOSans, OPPOSans;
      font-weight: 500;
      font-size: 16px;
      font-size: 14px;
      font-style: normal;
      text-transform: none;
    }
@@ -233,8 +233,8 @@
      .title {
        color: rgba(255, 255, 255, 0.8);
        font-family: OPPOSans, OPPOSans;
        font-weight: 500;
        font-size: 16px;
        font-size: 14px;
        line-height: 19px;
        text-align: left;
        font-style: normal;
@@ -242,7 +242,7 @@
      }
      .num {
        font-size: 24px;
        font-size: 26px;
        color: #36d3ff;
        font-family: OPPOSans, OPPOSans;
        font-weight: 800;
@@ -292,7 +292,7 @@
      text-align: center;
      margin: 5px 8px;
      border-radius: 8px;
      padding: 7px 10px;
      padding: 2px 6px;
      font-family: OPPOSans, OPPOSans;
      font-weight: 500;
      font-size: 16px;
@@ -349,7 +349,7 @@
      }
      .num {
        font-size: 24px;
        font-size: 26px;
        color: #3271eb;
        font-family: OPPOSans, OPPOSans;
        font-weight: 800;
@@ -399,7 +399,7 @@
      text-align: center;
      margin: 5px 8px;
      border-radius: 8px;
      padding: 7px 3px;
      padding: 2px 6px;
      font-family: OPPOSans, OPPOSans;
      font-weight: 500;
      font-size: 16px;
@@ -431,7 +431,7 @@
  text-align: center;
  margin: 2px 6px;
  border-radius: 8px;
  padding: 5px 10px;
  padding: 2px 6px;
  font-family: OPPOSans, OPPOSans;
  font-weight: 500;
  font-size: 14px;
zhitan-vue/vite.config.js
@@ -33,10 +33,15 @@
      proxy: {
        // https://cn.vitejs.dev/config/#server-proxy
        "/dev-api": {
          target: "http://localhost",
          target: "https://demo-ems.zhitancloud.com",
          changeOrigin: true,
          rewrite: (p) => p.replace(/^\/dev-api/, ""),
          rewrite: (p) => p.replace(/^\/dev-api/, "/prod-api"),
        },
        "/prod-api": {
          target: "https://demo-ems.zhitancloud.com",
          changeOrigin: true,
          secure: true,
        }
      },
    },
    //fix:error:stdin>:7356:1: warning: "@charset" must be the first rule in the file