Commit 1ea95d2b878ac40a055874753bfa643e4e141f0b

Authored by 648540858
2 parents 4c53b107 97698ee7

Merge branch 'wvp-28181-2.0' of https://github.com/648540858/wvp-GB28181-pro into wvp-28181-2.0

.gitignore
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 3
4 # Log file 4 # Log file
5 *.log 5 *.log
6 - 6 +logs/*
7 # BlueJ files 7 # BlueJ files
8 *.ctxt 8 *.ctxt
9 9
@@ -90,8 +90,8 @@ @@ -90,8 +90,8 @@
90 <!-- druid数据库连接池 --> 90 <!-- druid数据库连接池 -->
91 <dependency> 91 <dependency>
92 <groupId>com.alibaba</groupId> 92 <groupId>com.alibaba</groupId>
93 - <artifactId>druid</artifactId>  
94 - <version>1.2.3</version> 93 + <artifactId>druid-spring-boot-starter</artifactId>
  94 + <version>1.1.22</version>
95 </dependency> 95 </dependency>
96 96
97 <!-- mysql数据库 --> 97 <!-- mysql数据库 -->
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp; @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp;
2 2
3 import java.util.logging.LogManager; 3 import java.util.logging.LogManager;
4 4
  5 +import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport;
5 import org.springframework.boot.SpringApplication; 6 import org.springframework.boot.SpringApplication;
6 import org.springframework.boot.autoconfigure.SpringBootApplication; 7 import org.springframework.boot.autoconfigure.SpringBootApplication;
7 import org.springframework.boot.web.servlet.ServletComponentScan; 8 import org.springframework.boot.web.servlet.ServletComponentScan;
@@ -17,6 +18,7 @@ import springfox.documentation.oas.annotations.EnableOpenApi; @@ -17,6 +18,7 @@ import springfox.documentation.oas.annotations.EnableOpenApi;
17 @SpringBootApplication 18 @SpringBootApplication
18 @EnableScheduling 19 @EnableScheduling
19 @EnableOpenApi 20 @EnableOpenApi
  21 +@EnableDruidSupport
20 public class VManageBootstrap extends LogManager { 22 public class VManageBootstrap extends LogManager {
21 private static String[] args; 23 private static String[] args;
22 private static ConfigurableApplicationContext context; 24 private static ConfigurableApplicationContext context;
src/main/java/com/genersoft/iot/vmp/conf/druid/DruidConfiguration.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf.druid;
  2 +
  3 +import com.alibaba.druid.support.http.StatViewServlet;
  4 +import com.alibaba.druid.support.http.WebStatFilter;
  5 +import org.springframework.beans.factory.annotation.Value;
  6 +import org.springframework.boot.web.servlet.FilterRegistrationBean;
  7 +import org.springframework.boot.web.servlet.ServletRegistrationBean;
  8 +import org.springframework.context.annotation.Bean;
  9 +
  10 +import javax.servlet.Filter;
  11 +import javax.servlet.Servlet;
  12 +
  13 +/**
  14 + * druid监控配置
  15 + * @author
  16 + */
  17 +public class DruidConfiguration {
  18 +
  19 + @Value("${rj-druid-manage.allow:127.0.0.1}")
  20 + private String allow;
  21 +
  22 + @Value("${rj-druid-manage.deny:}")
  23 + private String deny;
  24 +
  25 + @Value("${rj-druid-manage.loginUsername:admin}")
  26 + private String loginUsername;
  27 +
  28 + @Value("${rj-druid-manage.loginPassword:admin}")
  29 + private String loginPassword;
  30 +
  31 + @Value("${rj-druid-manage.resetEnable:false}")
  32 + private String resetEnable;
  33 +
  34 + /**
  35 + * druid监控页面开启
  36 + */
  37 + @Bean
  38 + public ServletRegistrationBean druidServlet() {
  39 + ServletRegistrationBean<Servlet> servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
  40 + // IP白名单
  41 + servletRegistrationBean.addInitParameter("allow", allow);
  42 + // IP黑名单(共同存在时,deny优先于allow)
  43 + servletRegistrationBean.addInitParameter("deny", deny);
  44 + //控制台管理用户
  45 + servletRegistrationBean.addInitParameter("loginUsername", loginUsername);
  46 + servletRegistrationBean.addInitParameter("loginPassword", loginPassword);
  47 + //是否能够重置数据 禁用HTML页面上的“Reset All”功能
  48 + servletRegistrationBean.addInitParameter("resetEnable", resetEnable);
  49 + return servletRegistrationBean;
  50 + }
  51 +
  52 + /**
  53 + * druid url监控配置
  54 + */
  55 + @Bean
  56 + public FilterRegistrationBean filterRegistrationBean() {
  57 + FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter());
  58 + filterRegistrationBean.addUrlPatterns("/*");
  59 + filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
  60 + return filterRegistrationBean;
  61 + }
  62 +
  63 +
  64 +}
0 \ No newline at end of file 65 \ No newline at end of file
src/main/java/com/genersoft/iot/vmp/conf/druid/EnableDruidSupport.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf.druid;
  2 +
  3 +import org.springframework.boot.web.servlet.ServletComponentScan;
  4 +import org.springframework.context.annotation.Import;
  5 +
  6 +import java.lang.annotation.*;
  7 +
  8 +/**
  9 + * druid监控支持注解
  10 + *
  11 + * @author
  12 + * {@link DruidConfiguration} druid监控页面安全配置支持
  13 + * {@link ServletComponentScan} druid监控页面需要扫描servlet
  14 + */
  15 +@Target(ElementType.TYPE)
  16 +@Retention(RetentionPolicy.RUNTIME)
  17 +@Documented
  18 +@Inherited
  19 +@Import({
  20 + DruidConfiguration.class,
  21 +})
  22 +@ServletComponentScan
  23 +public @interface EnableDruidSupport {
  24 +}
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java
@@ -3,7 +3,7 @@ package com.genersoft.iot.vmp.storager.dao; @@ -3,7 +3,7 @@ package com.genersoft.iot.vmp.storager.dao;
3 import com.genersoft.iot.vmp.gb28181.bean.GbStream; 3 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
4 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; 4 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
5 import org.apache.ibatis.annotations.*; 5 import org.apache.ibatis.annotations.*;
6 -import org.omg.PortableInterceptor.INACTIVE; 6 +// import org.omg.PortableInterceptor.INACTIVE;
7 import org.springframework.stereotype.Repository; 7 import org.springframework.stereotype.Repository;
8 8
9 import java.util.Collection; 9 import java.util.Collection;
src/main/resources/application-local.yml 0 → 100644
  1 +spring:
  2 + # REDIS数据库配置
  3 + redis:
  4 + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
  5 + host: 127.0.0.1
  6 + # [必须修改] 端口号
  7 + port: 6379
  8 + # [可选] 数据库 DB
  9 + database: 6
  10 + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
  11 + password:
  12 + # [可选] 超时时间
  13 + timeout: 10000
  14 + # [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置
  15 + # mysql数据源
  16 + datasource:
  17 + type: com.alibaba.druid.pool.DruidDataSource
  18 + driver-class-name: com.mysql.cj.jdbc.Driver
  19 + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false
  20 + username: root
  21 + password: root123
  22 + druid:
  23 + initialSize: 10 # 连接池初始化连接数
  24 + maxActive: 200 # 连接池最大连接数
  25 + minIdle: 5 # 连接池最小空闲连接数
  26 + maxWait: 60000 # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
  27 + keepAlive: true # 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。
  28 + validationQuery: select 1 # 检测连接是否有效sql,要求是查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
  29 + testWhileIdle: true # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
  30 + testOnBorrow: false # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
  31 + testOnReturn: false # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
  32 + poolPreparedStatements: false # 是否開啟PSCache,並且指定每個連線上PSCache的大小
  33 + timeBetweenEvictionRunsMillis: 60000 # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒
  34 + minEvictableIdleTimeMillis: 300000 # 配置一個連線在池中最小生存的時間,單位是毫秒
  35 + filters: stat,wall,slf4j # 配置监控统计拦截的filters,监控统计用的filter:sta, 日志用的filter:log4j, 防御sql注入的filter:wall
  36 + useGlobalDataSourceStat: true # 合并多个DruidDataSource的监控数据
  37 + # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
  38 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000
  39 + #stat-view-servlet.url-pattern: /admin/druid/*
  40 +
  41 + # druid管理监控页面的一些配置
  42 +rj-druid-manage:
  43 + allow: # 访问druid监控页面的IP白名单
  44 + deny: 192.168.1.100 # 访问druid监控页面IP黑名单
  45 + loginUsername: rjAdmin # 访问druid监控页面账号
  46 + loginPassword: rj@2022 # 访问druid监控页面密码
  47 + resetEnable: false # 是否能够重置数据 禁用HTML页面上的“Reset All”功能
  48 +#mybatis:
  49 +# configuration:
  50 +# # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
  51 +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  52 +# # 返回类型为Map,显示null对应的字段
  53 +# call-setters-on-nulls: true
  54 +## [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
  55 +server:
  56 + port: 18080
  57 +
  58 +# 作为28181服务器的配置
  59 +sip:
  60 + # [必须修改] 本机的IP
  61 + ip: 192.168.118.70
  62 + # [可选] 28181服务监听的端口
  63 + port: 5060
  64 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007)
  65 + # 后两位为行业编码,定义参照附录D.3
  66 + # 3701020049标识山东济南历下区 信息行业接入
  67 + # [可选]
  68 + domain: 4401020049
  69 + # [可选]
  70 + id: 44010200492000000001
  71 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验
  72 + password: admin123
  73 +
  74 +#zlm 默认服务器配置
  75 +media:
  76 + # [必须修改] zlm服务器的内网IP
  77 + ip: 192.168.118.70
  78 + # [必须修改] zlm服务器的http.port
  79 + http-port: 80
  80 + # [可选] zlm服务器的hook.admin_params=secret
  81 + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
  82 + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试
  83 + rtp:
  84 + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
  85 + enable: true
  86 + # [可选] 在此范围内选择端口用于媒体流传输,
  87 + port-range: 30000,30500 # 端口范围
  88 + # [可选] 国标级联在此范围内选择端口发送媒体流,
  89 + send-port-range: 30000,30500 # 端口范围
  90 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用
  91 + record-assist-port: 18081
  92 +# [可选] 日志配置, 一般不需要改
  93 +logging:
  94 + config: classpath:logback-spring-local.xml
  95 +
  96 +
  97 +# [根据业务需求配置]
  98 +user-settings:
  99 + # 推流直播是否录制
  100 + record-push-live: true
  101 + auto-apply-play: false
  102 +
  103 +# 在线文档: swagger-ui(生产环境建议关闭)
  104 +swagger-ui:
  105 + enabled: true
  106 +
  107 +# 版本信息, 不需修改
  108 +version:
  109 + version: "@project.version@"
  110 + description: "@project.description@"
  111 + artifact-id: "@project.artifactId@"
src/main/resources/logback-spring-local.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<configuration debug="false">
  3 + <!--定义日志文件的存储地址 -->
  4 + <springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue=""/>
  5 + <property name="LOG_HOME" value="logs/${spring.application.name}" />
  6 +
  7 + <!--<property name="COLOR_PATTERN" value="%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta( %replace(%caller{1}){'\t|Caller.{1}0|\r\n', ''})- %gray(%msg%xEx%n)" />-->
  8 + <!-- 控制台输出 -->
  9 + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  10 + <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  11 + <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
  12 + <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
  13 + </encoder>
  14 + </appender>
  15 +
  16 + <!-- 按照每天生成日志文件 DEBUG以上级别的日志,仅用于测试环境,正式环境为info级别以上的日志-->
  17 + <appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
  18 +
  19 + <!-- 文件路径 -->
  20 + <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
  21 + <!--历史日志文件输出的文件名 -->
  22 + <FileNamePattern>${LOG_HOME}/wvp-%d{yyyy-MM-dd}.%i.log</FileNamePattern>
  23 + <!--日志文件保留天数 -->
  24 + <MaxHistory>30</MaxHistory>
  25 + <maxFileSize>20MB</maxFileSize>
  26 + </rollingPolicy>
  27 + <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  28 + <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
  29 + <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
  30 + </encoder>
  31 + <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
  32 + <!--与ThresholdFilter的区别,允许onmatch-->
  33 + <!--设置日志级别 接收info级别的日志-->
  34 + <level>INFO</level>
  35 + </filter>
  36 + </appender>
  37 +
  38 + <!-- 生成 error格式日志开始 -->
  39 + <appender name="RollingFileError" class="ch.qos.logback.core.rolling.RollingFileAppender">
  40 +
  41 + <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
  42 + <!--历史日志文件输出的文件名 -->
  43 + <FileNamePattern>${LOG_HOME}/error-%d{yyyy-MM-dd}.%i.log</FileNamePattern>
  44 + <!--日志文件保留天数 -->
  45 + <MaxHistory>30</MaxHistory>
  46 + <maxFileSize>20MB</maxFileSize>
  47 + </rollingPolicy>
  48 + <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  49 + <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
  50 + <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
  51 + </encoder>
  52 + <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
  53 + <!--设置日志级别,过滤掉info日志,只输入error日志-->
  54 + <level>WARN</level>
  55 + <!-- <onMatch>ACCEPT</onMatch> &lt;!&ndash; 用过滤器,只接受ERROR级别的日志信息,其余全部过滤掉 &ndash;&gt;-->
  56 + <!-- <onMismatch>DENY</onMismatch>-->
  57 + </filter>
  58 + </appender>
  59 +
  60 + <!-- 生成 druid日志追加 -->
  61 + <appender name="druidSqlRollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
  62 + <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
  63 + <!--历史日志文件输出的文件名 -->
  64 + <FileNamePattern>${LOG_HOME}/druid-%d{yyyy-MM-dd}.%i.log</FileNamePattern>
  65 + <!--日志文件保留天数 -->
  66 + <MaxHistory>30</MaxHistory>
  67 + <maxFileSize>50MB</maxFileSize>
  68 + </rollingPolicy>
  69 + <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  70 + <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
  71 + <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
  72 + </encoder>
  73 + </appender>
  74 +
  75 +
  76 + <!-- 日志输出级别 -->
  77 + <root level="INFO">
  78 + <appender-ref ref="STDOUT" />
  79 + <appender-ref ref="RollingFile" />
  80 + <appender-ref ref="RollingFileError" />
  81 + </root>
  82 +
  83 + <!--记录druid-sql的记录-->
  84 + <logger name="druid.sql.Statement" level="debug" additivity="true">
  85 + <!--AppenderRef ref="Console"/-->
  86 + <!-- <appender-ref ref="RollingFile"/>-->
  87 + <appender-ref ref="RollingFileError"/>
  88 + <appender-ref ref="druidSqlRollingFile"/>
  89 + </logger>
  90 +</configuration>
0 \ No newline at end of file 91 \ No newline at end of file
web_src/src/components/control.vue
@@ -21,11 +21,11 @@ @@ -21,11 +21,11 @@
21 <div style="position: absolute; right: 1rem; top: 0.3rem;"> 21 <div style="position: absolute; right: 1rem; top: 0.3rem;">
22 <el-popover placement="bottom" width="900" height="300" trigger="click"> 22 <el-popover placement="bottom" width="900" height="300" trigger="click">
23 <div style="height: 600px; overflow:auto; padding: 20px"> 23 <div style="height: 600px; overflow:auto; padding: 20px">
24 - <el-descriptions v-for="(value, key, index) in serverConfig" border column="1" style="margin-bottom: 1rem"> 24 + <el-descriptions v-for="(value, key, index) in serverConfig" :key="key" border column="1" style="margin-bottom: 1rem">
25 <template slot="title"> 25 <template slot="title">
26 {{key}} 26 {{key}}
27 </template> 27 </template>
28 - <el-descriptions-item v-for="(value1, key1, index1) in serverConfig[key]"> 28 + <el-descriptions-item v-for="(value1, key1, index1) in serverConfig[key]" :key="key1">
29 <template slot="label" > 29 <template slot="label" >
30 {{ getMediaKeyNameFromKey(key1) }} 30 {{ getMediaKeyNameFromKey(key1) }}
31 </template> 31 </template>
@@ -42,7 +42,7 @@ @@ -42,7 +42,7 @@
42 <template slot="extra"> 42 <template slot="extra">
43 <el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="JSON.stringify(wvpServerConfig.base)" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button> 43 <el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="JSON.stringify(wvpServerConfig.base)" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button>
44 </template> 44 </template>
45 - <el-descriptions-item v-for="(value, key, index) in wvpServerConfig.base" > 45 + <el-descriptions-item v-for="(value, key, index) in wvpServerConfig.base" :key="key">
46 <template slot="label" > 46 <template slot="label" >
47 {{ getNameFromKey(key) }} 47 {{ getNameFromKey(key) }}
48 </template> 48 </template>
@@ -52,7 +52,7 @@ @@ -52,7 +52,7 @@
52 查看<i class="el-icon-arrow-down el-icon--right"></i> 52 查看<i class="el-icon-arrow-down el-icon--right"></i>
53 </span> 53 </span>
54 <el-dropdown-menu slot="dropdown"> 54 <el-dropdown-menu slot="dropdown">
55 - <el-dropdown-item v-for="(value, key, index) in wvpServerConfig.base.interfaceAuthenticationExcludes">{{value}}</el-dropdown-item> 55 + <el-dropdown-item v-for="(value, key, index) in wvpServerConfig.base.interfaceAuthenticationExcludes" :key="key">{{value}}</el-dropdown-item>
56 </el-dropdown-menu> 56 </el-dropdown-menu>
57 </el-dropdown> 57 </el-dropdown>
58 </div> 58 </div>
@@ -75,7 +75,7 @@ @@ -75,7 +75,7 @@
75 <template slot="extra"> 75 <template slot="extra">
76 <el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="JSON.stringify(wvpServerConfig.sip)" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button> 76 <el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="JSON.stringify(wvpServerConfig.sip)" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button>
77 </template> 77 </template>
78 - <el-descriptions-item v-for="(value, key, index) in wvpServerConfig.sip"> 78 + <el-descriptions-item v-for="(value, key, index) in wvpServerConfig.sip" :key="key">
79 <template slot="label"> 79 <template slot="label">
80 {{ getNameFromKey(key) }} 80 {{ getNameFromKey(key) }}
81 </template> 81 </template>
@@ -88,7 +88,7 @@ @@ -88,7 +88,7 @@
88 <template slot="extra"> 88 <template slot="extra">
89 <el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="JSON.stringify(wvpServerVersion)" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button> 89 <el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="JSON.stringify(wvpServerVersion)" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button>
90 </template> 90 </template>
91 - <el-descriptions-item v-for="(value, key, index) in wvpServerVersion"> 91 + <el-descriptions-item v-for="(value, key, index) in wvpServerVersion" :key="key">
92 <template slot="label"> 92 <template slot="label">
93 {{ getNameFromKey(key) }} 93 {{ getNameFromKey(key) }}
94 </template> 94 </template>