信创国产化全家桶(统信UOS操作系统、瀚高数据库、东方通、电科网安、卫士通、海康威视国产视频插件arm、x86、mips)解决方案

发布于:2024-06-24 ⋅ 阅读:(165) ⋅ 点赞:(0)

序言:

最近由国产化项目,需要全套信创产品序列,涉及国产化软件和硬件的整合集成,后端工程是spring boot2,前端工程是vue2 ,记录分享一下:

1、国产化操作系统统信uos

后端java工程spring boot集成pom.xml

<properties>
		<spring-boot.version>2.7.3</spring-boot.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
		<maven.compiler.version>3.8.1</maven.compiler.version>
		<velocity.version>1.7</velocity.version>
		<commons.version>1.10</commons.version>
		<spring.checkstyle.version>0.0.34</spring.checkstyle.version>
		<git.commit.version>2.2.5</git.commit.version>
		<swagger.fox.version>3.0.0</swagger.fox.version>
		<jasypt.version>3.0.4</jasypt.version>
		<ttl.version>2.12.6</ttl.version>
		<docker.registry>172.17.0.111</docker.registry>
		<docker.host>http://172.17.0.111:2375</docker.host>
		<docker.namespace>library</docker.namespace>
		<docker.username>admin</docker.username>
		<docker.password>Harbor12345</docker.password>
		<docker.plugin.version>0.33.0</docker.plugin.version>
		<!--  默认忽略docker构建 -->
		<docker.skip>false</docker.skip>
	</properties>

2、瀚高数据库集成

pom.xml

<!--  瀚高数据库驱动  -->
		<!-- https://mvnrepository.com/artifact/com.highgo/HgdbJdbc -->
		<dependency>
			<groupId>com.highgo</groupId>
			<artifactId>HgdbJdbc</artifactId>
			<version>6.2.4</version>
		</dependency>

application-dev.yml

## 数据源
spring:
  cache:
    cache-names: menu_details,user_details,role_details
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
#      driver-class-name: com.mysql.cj.jdbc.Driver
#      username: ${MYSQL_USER:root}
#      password: ${MYSQL_PWD:root}
#      url: jdbc:mysql://${MYSQL_HOST:ewaycloud-jw-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:ewaycloud_json_flow}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true
#      validation-query: SELECT 1

#      driver-class-name: dm.jdbc.driver.DmDriver
#      username: SYSDBA
#      password: C##qlywadmin2023
#      url: jdbc:dm://127.0.0.1:5236?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
      driver-class-name: com.highgo.jdbc.Driver

#      本机
      url: jdbc:highgo://127.0.0.1:5866/highgo
      username: highgo
      password: Password@123

3、东方通中间件

pom.xml

<!--  东方通依赖  -->
		<dependency>
			<groupId>com.tongweb</groupId>
			<artifactId>tongweb-gmssl</artifactId>
			<version>1.0.0</version>
		</dependency>
		<dependency>
			<groupId>com.tongweb.springboot</groupId>
			<artifactId>tongweb-spring-boot-starter-2.x</artifactId>
			<version>7.0.E.6_P6</version>
		</dependency>
		<dependency>
			<groupId>com.tongweb.springboot</groupId>
			<artifactId>tongweb-spring-boot-data-jdbc-starter-2.x</artifactId>
			<version>7.0.E.6_P6</version>
		</dependency>
		<dependency>
			<groupId>com.tongweb.springboot</groupId>
			<artifactId>tongweb-spring-boot-websocket-2.x</artifactId>
			<version>7.0.E.6_P6</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

application-dev.yml

# 东方通 数据源
#server:
#  tongweb:
#    datasource:
#      type: com.tongweb.hulk.HulkDataSource
#      url: jdbc:highgo://localhost:5866/highgo
#      username: highgo
#      password: Password@123
#      driver-class-name: com.highgo.jdbc.Driver
#    license:
#      typy: file
#      path: classpath:license.dat
#  #表示SSL功能开启,使用国密SSL,首先确保SSL功能开启。
#  ssl:
#    enabled: false
#    #表示ssl通信协议使用国密通信协议。
#    protocol: GMSSLv1.1
#    #配置加密密钥库文件
#    key-store: classpath:sm2.enc.pfx
#    #配置加密密钥库文件的密码
#    key-store-password: xxxx
#    #配置加密密钥库文件的格式
#    key-store-type: PKCS12
#    #配置签名密钥库文件,路径支持绝对路径和classpath
#    trust-store: classpath:sm2.sig.pfx
#    #配置签名密钥库文件的密码
#    trust-store-password: xxxx
#    #配置签名密钥库文件的格式
#    trust-store-type: PKCS12

4、卫士通密码机

pom.xml

<!--卫士通密码机相关-->
		<dependency>
			<groupId>org.apache.mina</groupId>
			<artifactId>mina-core</artifactId>
			<version>2.0.5</version>
		</dependency>
		<dependency>
			<groupId>com.weston</groupId>  <!--自定义-->
			<artifactId>wst-hsm-api</artifactId>    <!--自定义-->
			<version>1.7.8</version> <!--自定义-->
			<scope>system</scope> <!--system,类似provided,需要显式提供依赖的jar以后,Maven就不会在Repository中查找它-->
			<systemPath>${project.basedir}/src/main/resources/lib/wst-hsm-api-1.7.8.jar</systemPath><!--项目根目录下的lib文件夹下-->
		</dependency>
<!--				<dependency>-->
<!--					<groupId>org.slf4j</groupId>-->
<!--					<artifactId>slf4j-log4j12</artifactId>-->
<!--					<version>1.7.25</version>-->
<!--				</dependency>-->
<!--				<dependency>-->
<!--					<groupId>org.slf4j</groupId>-->
<!--					<artifactId>slf4j-api</artifactId>-->
<!--					<version>1.7.25</version>-->
<!--				</dependency>-->
		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>5.7.8</version>
		</dependency>
		<dependency>
			<groupId>org.bouncycastle</groupId>
			<artifactId>bcprov-jdk15on</artifactId>
			<version>1.56</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
		</dependency>

SM4Util.java



import com.westone.pboc.hsm.HSMConstant;
import com.westone.pboc.mina.client.Client;
import com.westone.pboc.mina.client.ClientThreadPool;
import com.westone.pboc.service.imp.HSMWSTApiServiceImp;
import org.eclipse.jdt.internal.compiler.batch.Main;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//@SpringBootApplication
public class SM4Util {

	private static HSMWSTApiServiceImp imp ;

//	private static ClientThreadPool connectPool = ClientThreadPool.getInstance(URLDecoder.decode(Thread.currentThread().getContextClassLoader().getResource("hsminfo0901.properties").getPath()));

//	private static ClientThreadPool connectPool = ClientThreadPool.getInstance(URLDecoder.decode(Main.class.getResource("/hsminfo0901.properties").getPath()));

//	private static ShortClient shortClient = ShortClient.getInstance(URLDecoder.decode(Main.class.getResource("/hsminfo0901.properties").getPath()));

	//  private static ClientThreadPool connectPool = ClientThreadPool.getInstance("D:\\share\\hsminfo0901.properties");
	private static ClientThreadPool connectPool = ClientThreadPool.getInstance("/usr/local/hsminfo0901.properties");

	static {
		System.out.println("静态代码块被执行");
		// 静态初始化,如初始化静态变量等
		imp = new HSMWSTApiServiceImp();
		try {
			connectPool.initClient();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	//SM4加密
	public static Map<String,byte[]> SM4Encrypt(String inputString){

//		//如何按SM4 ECB_使用受SM2保护的密钥加密或解密
		int keyIndex = 1;
		int uiKeyBits = 16;
		int encryptAlgID = HSMConstant.SGD_SM4_ECB;
		Client client = null;
		byte[] data = inputString.getBytes();
		byte[] cipher = null;
		byte[] plain;

		try {
			client = connectPool.getClient();

			List<Object> list = imp.hsmSM2GenerateKeyWithIPK(keyIndex, uiKeyBits, client);

			byte[] keyHandle = (byte[]) list.get(0);
			imp.hsmDestoryKey(keyHandle, client);

			byte[] keyCipher = (byte[]) list.get(1);


			imp.hsmGetPrivateKeyAccessRight(keyIndex, "11111111", client);
			keyHandle = imp.hsmSM2ImportKeyWithISK(keyIndex, keyCipher, client);
			imp.hsmReleasePrivateKeyAccessRight(keyIndex, client);
			/*2、使用keyhandle加密明文数据data*/
			cipher = imp.hsmEncrypt(keyHandle, encryptAlgID, null, data, client); //加密
			plain = imp.hsmDecrypt(keyHandle, encryptAlgID, null, cipher, client); //解密

			imp.hsmDestoryKey(keyHandle, client);

			System.out.println("==============SM4加密前: "+inputString);
			System.out.println("==============SM4加密后byte数组: "+cipher);
			System.out.println("==============SM4加密后String: "+new String(cipher));
			System.out.println("==============SM4解密后: "+new String(plain));
			Map<String,byte[]> map = new HashMap<>();
			map.put("cipher",cipher);
			map.put("keyCipher",keyCipher);
//			return cipher;
			return map;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			connectPool.release(client);
			client.disposeConnector();
		}
//		return cipher;
		return new HashMap<>();
	}
	//SM4解密
	@Test
	public static String SM4Decrypt(Map<String,byte[]> map){
		//如何按SM4 ECB_使用受SM2保护的密钥加密或解密
		int keyIndex = 1;
		int uiKeyBits = 16;
		int encryptAlgID = HSMConstant.SGD_SM4_ECB;
		Client client = null;
//		byte[] cipher  = inputBytes;
		byte[] plain = null;
		try {
			client = connectPool.getClient();

			List<Object> list = imp.hsmSM2GenerateKeyWithIPK(keyIndex, uiKeyBits, client);

			byte[] keyHandle = (byte[]) list.get(0);
			imp.hsmDestoryKey(keyHandle, client);

//			byte[] keyCipher = (byte[]) list.get(1);

			imp.hsmGetPrivateKeyAccessRight(keyIndex, "11111111", client);
			keyHandle = imp.hsmSM2ImportKeyWithISK(keyIndex, map.get("keyCipher"), client);
			imp.hsmReleasePrivateKeyAccessRight(keyIndex, client);
			plain = imp.hsmDecrypt(keyHandle, encryptAlgID, null,  map.get("cipher"), client); //解密

			imp.hsmDestoryKey(keyHandle, client);

//			System.out.println("==============SM4解密前byte数组: "+ inputBytes);
			System.out.println("==============SM4解密后: "+new String(plain));
			return new String(plain);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			connectPool.release(client);
			client.disposeConnector();
		}
		return new String(plain);
	}

//	public static void HowToEncryptOrDecryptBySM4ECB_WithKeyProtectedBySM2() {
//		//如何按SM4 ECB_使用受SM2保护的密钥加密或解密
//		imp = new HSMWSTApiServiceImp();
//		try {
//			connectPool.initClient();
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//
//
//		int keyIndex = 1;
//		int uiKeyBits = 16;
//		int encryptAlgID = HSMConstant.SGD_SM4_ECB;
//		Client client = null;
//		String string = "张店区第一谈话室";
        byte[] data = new byte[16];
//		byte[] data = string.getBytes();
//		byte[] cipher;
//		byte[] plain;
//
//		try {
//			client = connectPool.getClient();
//
//			/*前提条件:1号索引存在SM2密钥*/
//			/*【系统的初始化阶段】
//			 * 需要准备用于数据加解密的密钥,这个或者这一些密钥的密文需要在业务系统测进行持久化存储
//			 * !!!!产生密钥的动作不需要在每次加密或者解密时调用,加密和解密所用密钥必须是同一个!!!!*/
//
//			/*1、在某个会话通道client中产生一个会话密钥,记为keyA,这个密钥的密文由1号索引的SM2密钥加密保护*/
//			List<Object> list = imp.hsmSM2GenerateKeyWithIPK(keyIndex, uiKeyBits, client);
//
//			/*2、获得这个会话密钥的句柄:上述产生会话密钥的接口会在产生的同时
//			 * 就将该密钥放置在会话空间中形成一个会话密钥句柄,可直接调用该句柄进行数据加解密,
//			 * 但我们推荐将该句柄直接销毁,后续做加密解密时再重新导入密钥密文拿到新的密钥句柄来使用*/
//			byte[] keyHandle = (byte[]) list.get(0);
//			/*3、销毁keyHandleA:当会话密钥不再使用时,应及时销毁该句柄,以释放密钥句柄资源,否则造成句柄资源泄漏*/
//			imp.hsmDestoryKey(keyHandle, client);
//
//			/*4、获得这个会话密钥的密文:这个密文需要在业务系统中长期存储,
//			 * 将来需要将该密钥恢复回来进行数据加解密时,需要用该密文进行相应的导入接口的调用,
//			 * 以形成一个新的会话密钥句柄*/
//			byte[] keyCipher = (byte[]) list.get(1);
//
//
//            /*【加解密处理阶段】
//            /*以上,我们准备产生了一个会话密钥密文keyCipher,这个密钥密文是由1号索引的SM2加密的
//
//             *下面的代码展示使用这个会话密钥对数据进行处理时的接口调用
//             */
//
//			/*1、将会话密钥密文keyCipher导入到密码机的某个会话通道client中,得到对应的会话密钥句柄keyHandle
//			 *   这里将使用私钥对会话密钥进行解密,从而获得密钥句柄。
//			 *   使用私钥进行运算时需要先获取私钥使用权限,本例在通过密码机控制台软件进行SM2密钥产生时设置的访问口令为“11111111”
//			 */
//			imp.hsmGetPrivateKeyAccessRight(keyIndex, "11111111", client);
//			keyHandle = imp.hsmSM2ImportKeyWithISK(keyIndex, keyCipher, client);
//			imp.hsmReleasePrivateKeyAccessRight(keyIndex, client);
//
//			/*2、使用keyhandle加密明文数据data*/
//			cipher = imp.hsmEncrypt(keyHandle, encryptAlgID, null, data, client); //加密
//			plain = imp.hsmDecrypt(keyHandle, encryptAlgID, null, cipher, client); //解密
//
//			/*3、销毁keyHandleA:当会话密钥不再使用时,应及时销毁该句柄,以释放密钥句柄资源*/
//			imp.hsmDestoryKey(keyHandle, client);
//
//			System.out.println("data length =" + data.length + "\ncipher length =" + cipher.length + "\ndecrypted plain length = " + plain.length);
//
//			System.out.println("==============SM4加密前: "+string);
//			System.out.println("==============SM4加密后byte数组: "+cipher);
//			System.out.println("==============SM4加密后String: "+new String(cipher));
//			System.out.println("==============SM4解密后: "+new String(plain));
//
			Assert.assertArrayEquals(data, plain);
//		} catch (Exception e) {
//			e.printStackTrace();
//		} finally {
//			connectPool.release(client);
//		}
//	}
//
//	public void HowToCalculateSM3HMAC() {
//		//如何计算SM3HMAC
//		Client client = null;
//		int sm2KeyIndex = 1;
//		/*数据如果有中文,应约定将数据转码成byte数组时的转码方式*/
//		byte[] data = "HelloWorld".getBytes();
//
//		try {
//			client = connectPool.getClient();
//			/*这里是一个初始化动作,目的是产生一个用于计算HMAC的随机密钥,计算HMAC和校验这个HMAC的时候必须确保用到这个相同的随机密钥*/
//			List<Object> wk = imp.hsmSM2GenerateKeyWithIPK(sm2KeyIndex, 16, client);
//			/*这里取出的密钥句柄,如果不需要马上用于计算HMAC,可以直接调用hsmDestroyKey将其销毁,回收句柄资源*/
//			byte[] keyHandle = (byte[]) wk.get(0);
//			imp.hsmDestoryKey(keyHandle, client);
//			/*这里的随机密钥密文是需要保存的,以便下次再使用这个密钥时用于导入后重新拿到密钥句柄*/
//			byte[] keyCipher = (byte[]) wk.get(1);
//
//            /*从这里开始,下面的接口调用序列为计算HMAC的完整的日常过程,
//            其中的keyCipher就是初始化阶段产生的随机密钥密文,将其导入后重新得到一个密钥句柄,并用这个句柄进行HMAC运算,
//            运算结束后必须回收该句柄资源*/
//			imp.hsmGetPrivateKeyAccessRight(sm2KeyIndex, "11111111", client);
//			byte[] keyHandleNew = imp.hsmSM2ImportKeyWithISK(sm2KeyIndex, keyCipher, client);
//			imp.hsmReleasePrivateKeyAccessRight(sm2KeyIndex, client);
//
//			byte[] context = imp.hsmHashCrossSessionInit(HSMConstant.SGD_HASH_HMAC_SM3, keyHandleNew, null, null, null, null, client);
//			byte[] context11 = imp.hsmHashCrossSessionUpdate(context, data, client);
//			byte[] hashResult = imp.hsmHashCrossSessionFinal(context11, client);
//
//
//			/*!!!!注意!!!!*/
//			/*计算完成后一定要释放密钥句柄资源*/
//			boolean ok = imp.hsmDestoryKey(keyHandleNew, client);
//
//			System.out.println("HMAC = " + ByteArrayUtil.toHexString(hashResult));
//		} catch (Exception e1) {
//			e1.printStackTrace();
			Assert.fail();
//		} finally {
//			connectPool.release(client);
//		}
//	}
//
//	public void HowToBuildSM2PublicKeyFromRefByteArray() {
//		//如何从RefByteArray构建SM2公钥
//		int keyIndex = 1;
//		Client client = null;
//
//		try {
//			client = connectPool.getClient();
//
//			/*一个RefByteArray格式的SM2公钥以16进制字符串形式打印出来看起来像这样:
//			 *00010000e3b74ed47bbe6dabae26fba66630577a34d66f7e885cc23938db06e321d34f9344c35de1f59ef7164425484faac603df10caa369cfe72e24edfd6b2904e655a5
//			 * 即00010000||X||Y
//			 *通常解析X509证书得到的SM2公钥按照16进制字符串形式打印出来像下面这样:
//			 *04e3b74ed47bbe6dabae26fba66630577a34d66f7e885cc23938db06e321d34f9344c35de1f59ef7164425484faac603df10caa369cfe72e24edfd6b2904e655a5
//			 * 即04||X||Y
//			 *本例仅展示从RefByteArray构造一个PublicKey对象,如果是04||X||Y,只需要将前面的04部分替换为00010000 */
//
//			/*前提条件:1号索引存在SM2密钥,且其访问口令为11111111*/
//			/*为了方便验证转换的正确性,我们取出了1号索引的公钥,将其转换为00010000||X||Y形式
//			 *再对00010000||X||Y形式的公钥进行了PublicKey的对象构造动作*/
//			PublicKey externalPK = imp.hsmSM2GetPublicKey(keyIndex, client);
//			byte[] pkAsRefByteArray = SM2PKUtil.SM2PublicKey2Bytes(externalPK);
//			System.out.println("pkAsRefByteArray = " + ByteArrayUtil.toHexString(pkAsRefByteArray));
//
//			PublicKey builtPK = SM2PKUtil.buildPublicKey(pkAsRefByteArray);
//
//			/*验证正确性:
//			 *由于以上构造出的公钥实际来源于1号索引,因此我们用构造出来的公钥对象进行加密,再用1号索引的SM2私钥解密,
//			 *如果解密结果与原始明文一致,则认为验证通过 */
//			byte[] dataPlain = "1111111111111111".getBytes();
//			byte[] dataCipher = imp.hsmSM2ExternalPublicKeyEnc(HSMConstant.WST_ALG_TYPE, builtPK, dataPlain, client);
//			imp.hsmGetPrivateKeyAccessRight(keyIndex, "11111111", client);
//			byte[] outPlain = imp.hsmSM2InternalPrivateKeyDec(keyIndex, HSMConstant.WST_ALG_TYPE, dataCipher, client);
//			imp.hsmReleasePrivateKeyAccessRight(keyIndex, client);
//			System.out.println("dataPlain=" + ByteArrayUtil.toHexString(dataPlain) + "\noutPlain =" + ByteArrayUtil.toHexString(outPlain));
			Assert.assertArrayEquals(dataPlain, outPlain);
//		} catch (Exception e) {
//			e.printStackTrace();
//		} finally {
//			connectPool.release(client);
//		}
//	}
//
//	public void HowToInternalEncryptAndInternalDecryptBySM2() {
//		//如何通过SM2进行内部加密和内部解密
//		int keyIndex = 1;
//		Client client = null;
//		try {
//			client = connectPool.getClient();
//
//			byte[] data = "11111111".getBytes();
//			byte[] result = imp.hsmSM2InternalPublicKeyEnc(HSMConstant.WST_ALG_TYPE, 6, keyIndex, data, client);
//			System.out.println("Cipher = " + ByteArrayUtil.toHexString(result));
//
//			byte[] decrypted = imp.hsmSM2InternalPrivateKeyDec(1, keyIndex, "11111111", result, client);
//			System.out.println("Plain = " + ByteArrayUtil.toHexString(decrypted));
//
//		} catch (Exception e) {
//			e.printStackTrace();
//		} finally {
//			connectPool.release(client);
//		}
//	}
//
//	@Test
//	public void HowToInternalSignAndExternalVerifyBySM2() {
//		//如何通过SM2进行内部签名和外部验证
//		int keyIndex = 1;
//		String algType = HSMConstant.WST_ALG_TYPE;
//		String src = "1111111111ffffffffff000000000012";
//		String userId = "1234567812345678";
//		Client client = null;
//		try {
//			client = connectPool.getClient();
//
//			/*签名方:使用keyIndex索引的私钥对数据进行签名*/
//			/*1、用keyIndex索引对应的公钥对原始数据计算SM2-SM3-USERID摘要*/
//			byte[] hashData1 = imp.doHash(keyIndex, null, src.getBytes(), userId, client);
//			/*2、对摘要结果计算签名*/
//			byte[] signData = imp.hsmSM2InternalSign(algType, keyIndex, hashData1, "11111111", client);
//			System.out.println("外部SM2签名成功,签名内容:" + ByteArrayUtil.toHexString(signData));
//
//			/*验签方:验签方用签名方的公钥进行验签*/
//			/*0、通过某种方式,验签方获得签名方的公钥,例如从签名方的公钥证书中提取,或者从签名方直接拿到公钥明文
//			 *    这里为了让用例流程往下开展,我们假设性的从相同索引获取其公钥*/
//			PublicKey pk = imp.hsmSM2GetPublicKey(keyIndex, client);
//			System.out.println("PK = "+ByteArrayUtil.toHexString(pk.getEncoded()));
//
//			/*1、用签名方提供的公钥对原始数据计算SM2-SM3-USERID摘要*/
//			byte[] hashData2 = imp.doHash(-1, pk, src.getBytes(), userId, client);
//			/*2、对摘要结果计算签名*/
//			boolean ret = imp.hsmSM2ExternalVerify(algType, pk, signData, hashData2, client);
//			System.out.println("外部SM2验签密成功,结果:" + ret);
//
//		} catch (Exception e) {
//			e.printStackTrace();
//		} finally {
//			connectPool.release(client);
//		}
//	}

	public static void main(String[] args) {
//		String str = "测试加密数据";
//		Map<String,byte[]> jsonObject= SM4Util.SM4Encrypt(str);
//		String newstr = SM4Util.SM4Decrypt(jsonObject);
		HowToEncryptOrDecryptBySM4ECB_WithKeyProtectedBySM2();
//		System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@    ");
		String path = System.getProperty("user.dir") + File.separator + "/hsminfo0901.properties";
		System.out.println(Main.class.getResource("/hsminfo0901.properties").getPath());

	}
}

5、海康威视 实时视频、视频回放

pom.xml

<!--  海康威视驱动  -->
		<dependency>
			<groupId>com.hikvision.ga</groupId>
			<artifactId>artemis-http-client</artifactId>
			<version>1.1.3</version>
		</dependency>

HikConstants.java

public class HikConstants {

    /** 查询监控点列表 */
    public static final String findCameraSearch = "/api/resource/v1/camera/advance/cameraList";
//    /** 分页获取设备列表 */
    public static final String findCameraList = "/api/resource/v1/cameras";
    /** 分页获取设备列表 */
//    public static final String findCameraList = "/api/resource/v2/encodeDevice/search";
    /** 获取视频流url */
    public static final String previewURLs = "/api/video/v1/cameras/previewURLs";

    /**
     * 获取电视墙预览
     */
//    public static final String tvWallURL = "/api/v1/preview/tvWall/get";
    public static final String tvWallURL = "/api/v1/preview/tvWall/getPreviewParam";


    /**
     * 释放永久有效url
     */
    public static final String clearPlayUrls = "/api/v1/clearPlayUrls";

    /** 获取监控点回放取流URL */
    public static final String playbackURLs = "/api/video/v1/cameras/playbackURLs";


}

HikController.java



import cn.hutool.json.JSONObject;
import com.ewaycloud.jw.channel.dto.ConditionBaseVO;
import com.ewaycloud.jw.channel.dto.HikCameraSearchDTO;
import com.ewaycloud.jw.channel.service.HikService;
import com.ewaycloud.jw.common.core.util.R;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/channel/hik")
public class HikController {

    @Autowired
    private HikService hkService;



    /**
     * 获取监控点列表
     * */
    @GetMapping("/findCameraSearch")
    public R findCameraSearch(HikCameraSearchDTO hilCameraSearchDTO){
        try {
            return R.ok(hkService.findCameraSearch(hilCameraSearchDTO));
        }catch (Exception e){
            log.error("获取设备编码列表失败!"+e.getMessage());
            return R.failed("获取设备编码列表失败!");
        }
    }

    /**
	 * 获取设备列表
	 * */
	@GetMapping("/findDeviceList")
	public R findDeviceList(ConditionBaseVO conditionBaseVO){
		try {
			List<JSONObject> list = hkService.findCameraList(conditionBaseVO);
			return R.ok(list);
		}catch (Exception e){
			log.error("获取设备编码列表失败!"+e.getMessage());
			return R.failed("获取设备编码列表失败!");
		}
	}

    /**
     * 获取视频流url
     * */
    @GetMapping("/previewUrls")
    public R previewUrls(@RequestParam String cameraIndexCode){
        try {
            return R.ok(hkService.previewUrls(cameraIndexCode));
        }catch (Exception e){
			log.error("获取设备编码列表失败!"+e.getMessage());
			return R.failed("获取设备编码列表失败!");
		}
    }

    /**
     * 获取 电视墙预览 视频流url
     * */
    @GetMapping("/tvWallURL")
    public R tvWallURL(@RequestParam String cameraIndexCode){
        try {
            return R.ok(hkService.tvWallURL(cameraIndexCode));
        }catch (Exception e){
            log.error("获取设备编码列表失败!"+e.getMessage());
            return R.failed("获取设备编码列表失败!");
        }
    }

    /**
     * 释放永久有效url
     * */
    @GetMapping("/clearPlayUrls")
    public R clearPlayUrls(@RequestParam String url){
        try {
            return R.ok(hkService.clearPlayUrls(url));
        }catch (Exception e){
            log.error("释放永久有效url!"+e.getMessage());
            return R.failed("释放永久有效url!");
        }
    }

    /**
     * 获取 回放 视频流url
     * */
    @GetMapping("/playbackURLs")
    public R playbackURLs(@RequestParam String cameraIndexCode, @RequestParam String beginTime, @RequestParam String endTime){
        try {
            return R.ok(hkService.playbackURLs(cameraIndexCode, beginTime, endTime));
        }catch (Exception e){
            log.error("获取设备编码列表失败!"+e.getMessage());
            return R.failed("获取设备编码列表失败!");
        }
    }

}

HikService.java



import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.ewaycloud.jw.channel.constants.HikConstants;
import com.ewaycloud.jw.channel.dto.ConditionBaseVO;
import com.ewaycloud.jw.channel.dto.HikCameraSearchDTO;
import com.ewaycloud.jw.channel.util.GetCameraPreviewURL;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.List;

/**
 * 海康service
 * */
@Service
@Slf4j
public class HikService {

    public List findCameraSearch(HikCameraSearchDTO hilCameraSearchDTO) throws Exception{
        JSONObject json = new JSONObject();
//        json.put("pageNo",hilCameraSearchDTO.getPageNum());
//        json.put("pageSize",hilCameraSearchDTO.getPageSize());
        json.put("name",hilCameraSearchDTO.getName());

        log.info("请求第三方参数--->"+ JSONUtil.toJsonStr(json));
        log.info("请求第三方接口--->"+ HikConstants.findCameraSearch);
        String result = GetCameraPreviewURL.GetCameraPreviewURL(json, HikConstants.findCameraSearch);
        log.info("第三方返回请求结果--->"+result);

        json = JSONObject.parseObject(result);
        if(!json.get("code").equals("0")){
            log.error("获取监控点列表失败!"+json.get("msg"));
            throw new Exception("获取监控点列表失败!"+json.get("msg"));
        }
        JSONObject data = json.getJSONObject("data");
        return data.getJSONArray("list");
    }

    public List findCameraList(ConditionBaseVO conditionBaseVO) throws Exception{
        JSONObject json = new JSONObject();
        json.put("pageNo",conditionBaseVO.getPageNum());
        json.put("pageSize",conditionBaseVO.getPageSize());
        json.put("treeCode","0");
        log.info("请求第三方参数--->"+JSONUtil.toJsonStr(json));
        log.info("请求第三方接口--->"+ HikConstants.findCameraList);
        String result = GetCameraPreviewURL.GetCameraPreviewURL(json, HikConstants.findCameraList);
        log.info("第三方返回请求结果--->"+result);
        json = JSONObject.parseObject(result);
        if(!json.get("code").equals("0")){
            log.error("获取设备编码列表失败!"+json.get("msg"));
            throw new Exception("获取设备编码列表失败!"+json.get("msg"));
        }
        JSONObject data = json.getJSONObject("data");
        return data.getJSONArray("list");
    }

    public JSONObject previewUrls(String cameraIndexCode) throws Exception{
        JSONObject json = new JSONObject();
        json.put("cameraIndexCode",cameraIndexCode);
        log.info("请求第三方参数--->"+JSONUtil.toJsonStr(json));
        log.info("请求第三方接口--->"+ HikConstants.previewURLs);
        String result = GetCameraPreviewURL.GetCameraPreviewURL(json, HikConstants.previewURLs);
        log.info("第三方返回请求结果--->"+result);

        json = JSONObject.parseObject(result);
        if(!json.get("code").equals("0")){
            log.error("获取视频url失败!"+json.get("msg"));
            throw new Exception("获取视频url失败!"+json.get("msg"));
        }
        JSONObject data = json.getJSONObject("data");
        return data;
    }

    /**
     * 获取 电视墙预览 视频流url
     * */
    public JSONObject tvWallURL(String cameraIndexCode) throws Exception{
        JSONObject json = new JSONObject();
        json.put("indexCode",cameraIndexCode);
        json.put("expand", "streamform=rtp");
        json.put("netZoneCode", "0");
        json.put("protocol", "rtsp");
        json.put("streamType", 0);
        json.put("transmode", 0);

        log.info("请求第三方参数--->"+JSONUtil.toJsonStr(json));
        log.info("请求第三方接口--->"+ HikConstants.tvWallURL);
        String result = GetCameraPreviewURL.GetCameraPreviewURL(json, HikConstants.tvWallURL);
        log.info("第三方返回请求结果--->"+result);

        json = JSONObject.parseObject(result);
        if(!json.get("code").equals("0")){
            log.error("获取视频url失败!"+json.get("msg"));
            throw new Exception("获取视频url失败!"+json.get("msg"));
        }
        JSONObject data = json.getJSONObject("data");
        return data;
    }

    /**
     * 释放永久有效url
     * */
    public JSONObject clearPlayUrls(String url) throws Exception{
        JSONObject json = new JSONObject();
        JSONArray array = new JSONArray();
        array.add(url);
        json.put("urls",array);
        log.info("请求第三方参数--->"+JSONUtil.toJsonStr(json));
        log.info("请求第三方接口--->"+ HikConstants.clearPlayUrls);
        String result = GetCameraPreviewURL.GetCameraPreviewURL(json, HikConstants.clearPlayUrls);
        log.info("第三方返回请求结果--->"+result);

        json = JSONObject.parseObject(result);
        if(!json.get("code").equals("0")){
            log.error("释放永久有效url!"+json.get("msg"));
            throw new Exception("释放永久有效url!"+json.get("msg"));
        }
        JSONObject data = json.getJSONObject("data");
        return data;
    }

    /**
     * 获取 回放 视频流url
     * */
    public JSONObject playbackURLs(String cameraIndexCode, String beginTime, String endTime) throws Exception{
        JSONObject json = new JSONObject();
        json.put("cameraIndexCode",cameraIndexCode);
		//String  startDate = DateUtil.format(DateUtil.parse(beginTime),"yyyy-MM-dd'T'HH:mm:ss.SSSZ");
		//String  endDate = DateUtil.format(DateUtil.parse(endTime),"yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        String startDate = Stringios(beginTime);
        String endDate = Stringios(endTime);
        json.put("beginTime", startDate);// 开始查询时间(IOS8601格式yyyy-MM-dd'T'HH:mm:ss.SSSzzz,和结束时间相差不超过三天)  "beginTime": "2024-04-15T07:50:00.000+08:00",
        json.put("endTime", endDate); //     "endTime": "2024-04-15T08:10:00.000+08:00",
        json.put("recordLocation", "1");
        json.put("protocol", "rtsp");
//        json.put("needReturnClipInfo", true);
        json.put("expand", "streamform=rtp");
//        UUID uuid = UUID.randomUUID();
//        json.put("uuid", uuid.toString());

        log.info("请求第三方参数--->"+JSONUtil.toJsonStr(json));
        log.info("请求第三方接口--->"+ HikConstants.playbackURLs);
        String result = GetCameraPreviewURL.GetCameraPreviewURL(json, HikConstants.playbackURLs);
        log.info("第三方返回请求结果--->"+result);

        json = JSONObject.parseObject(result);
        if(!json.get("code").equals("0")){
            log.error("获取回放视频url失败!"+json.get("msg"));
            System.out.println("获取回放视频url失败!:" +json.get("msg"));
            //throw new Exception("获取回放视频url失败!"+json.get("msg"));
        }
        JSONObject data = json.getJSONObject("data");
        return data;
    }

    /**
     * String类型的时间(yyyy-MM-dd HH:mm:ss)转换成IOS-8601格式
     * @param data
     * @return
     */
    public String Stringios(String data) {
        data = data.substring(0, 19);
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime dt1 = LocalDateTime.parse(data, dateTimeFormatter);
        ZoneOffset offset = ZoneOffset.of("+08:00");
        OffsetDateTime date = OffsetDateTime.of(dt1, offset);
        DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
        String datas = date.format(formatter2);
        return datas;
    }

}

GetCameraPreviewURL.java



import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.hikvision.artemis.sdk.ArtemisHttpUtil;
import com.hikvision.artemis.sdk.config.ArtemisConfig;

import java.util.HashMap;
import java.util.Map;

/**
 * 海康接口
 * */
public class GetCameraPreviewURL {

    public static String GetCameraPreviewURL(JSONObject jsonObject, String apiUrl) {
        /**
         * STEP1:设置平台参数,根据实际情况,设置host appkey appsecret 三个参数.
         */
        // artemis网关服务器ip端口
        ArtemisConfig.host = "159.61.64.249:443";
        // 秘钥appkey
        ArtemisConfig.appKey = "22547186";
        // 秘钥appSecret
        ArtemisConfig.appSecret = "sPteT21oUI2CCav79heE";

        /**
         * STEP2:设置OpenAPI接口的上下文
         */
        final String ARTEMIS_PATH = "/artemis";

        /**
         * STEP3:设置接口的URI地址
         */
        final String previewURLsApi = ARTEMIS_PATH + apiUrl;
        Map<String, String> path = new HashMap<String, String>(2) {
            {
                put("https://", previewURLsApi);//根据现场环境部署确认是http还是https
            }
        };

        /**
         * STEP4:设置参数提交方式
         */
        String contentType = "application/json";

        /**
         * STEP5:组装请求参数
         */
//        JSONObject jsonBody = new JSONObject();
//        jsonBody.put("cameraIndexCode", "748d84750e3a4a5bbad3cd4af9ed5101");
//        jsonBody.put("streamType", 0);
//        jsonBody.put("protocol", "rtsp");
//        jsonBody.put("transmode", 1);
//        jsonBody.put("expand", "streamform=ps");
        String body = JSONUtil.toJsonStr(jsonObject);
        /**
         * STEP6:调用接口
         */
        // post请求application/json类型参数
        String result = ArtemisHttpUtil.doPostStringArtemis(path, body, null, null, contentType , null);
        return result;
    }
}

6、前端视频插件

实时视频:demo_embedded_for_iframe.html

<!doctype html>
<html>
<head>
    <title>预览Demo</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Cache-Control" content="no-cache, must-revalidate" />
    <meta http-equiv="Expires" content="0" />
</head>
<style>
html, body {
    padding: 0;
    margin: 0;
}
.playWnd {
    /* margin: 30px 0 0 50px; */
    //width: 800px;
    //height: 400px;
    /* border: 1px solid red; */
}
.cbInfoDiv {
    float: left;
    width: 360px;
    /* margin-left: 16px; */
    border:1px solid #7F9DB9;
}
.cbInfo {
    height: 200px;
    padding: 5px;
    border: 1px solid #7F9DB9;
    word-break: break-all;
	  overflow: scroll auto;
}
.operate {
    /* margin-top: 24px; */
}
.operate::after {
    content: '';
    display: block;
    clear: both;
}
.operate .btns {
    height: 32px;
}
.module {
    float: left;
    width: 120px;
    min-height: 290px;
    margin-left: 10px;
    padding: 16px 8px;
    box-sizing: border-box;
    border: 1px solid #e5e5e5;
}
.module .item {
    margin-bottom: 4px;
}
.module .label {
    width: 150px;
    display: inline-block;
    vertical-align: middle;
    margin-right: 8px;
    text-align: right;
}
.module input[type="text"],
.module select {
    box-sizing: border-box;
    display: inline-block;
    vertical-align: middle;
    margin-left: 0;
    width: 150px;
    min-height: 20px;
}
.module .btn {
    min-width: 80px;
    min-height: 24px;
    margin-top: 16px;
    margin-left: 158px;
}
</style>
<body>
    <div id="playWnd" class="playWnd" style="left: 109px; top: 133px;"></div>
    <div id="operate" class="operate" style="display: none;">
        <!--初始化、反初始化、设置认证信息接口调用入口。
        1.插件所有接口都需要在调用初始化并返回成功后才能调用
        2.设置认证信息仅适用于对接多平台时的情况,具体参照开发指南
        3.反初始化后,插件资源销毁-->
        <div class="module" style="left:30px;height:30px;width:280px;padding:10px;margin:10px;">
            <div class="item">
                <label >初始化相关参数:</label>
                <!-- 4097, -->
                <textarea id="initParam" type="text" style="width:260px;height:200px;">
                  {
                    "argument": {
                        "appkey": "22547186",
                        "ip": "159.61.64.249",
                        "port": 443,
                        "secret": "sPteT21oUI2CCav79heE",
                        "enableHTTPS": 1,
                        "language": "zh_CN",
                        "layout": "3x3",
                        "playMode": 0,
                        "reconnectDuration": 5,
                        "reconnectTimes": 5,
                        "showSmart": 0,
                        "showToolbar": 1,
                        "toolBarButtonIDs": "2048,2049,2050,2304,2306,2305,2307,2308,2309,4096,4097,4608,4099,4098,4609,4100",
                        "snapDir": "C:/snap",
                        "videoDir": "C:/video"
                    },
                    "funcName": "init"
                }
                </textarea>
            </div>
            <div class="item">
                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                <button style="width:10px;padding:0;margin:0;" id="initBtn" class="btn">执行</button>
            </div>
        </div>

        <!--单个点位播放、批量点位播放、批量停止播放、全部停止播放接口调用入口。
        1.authUuid为对接多平台时必须的播放字段,对接单平台时,可不指定-->
        <div class="module" style="height:30px;width:280px;padding:10px;margin:10px;">
            <div class="item">
                <label >播放相关参数:</label>
                <!-- {
                    "argument": {
                        "authUuid": "",
                        "cameraIndexCode": "d597b77b8c73459792c82ded3acb6eba",
                        "ezvizDirect": 0,
                        "gpuMode": 0,
                        "streamMode": 0,
                        "transMode": 0,
                        "wndId": -1,
                        "cascade": 1
                    },
                    "funcName": "startPreview"
                } -->
                <textarea id="playParam" type="text" style="width:260px;height:200px;">
                {
                 "funcName": "startPreview",
                 "argument":
                	{

                	}
                }

                </textarea>
            </div>
            <div class="item">
                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                <button style="width:10px;padding:0;margin:0;" id="playBtn" class="btn">执行</button>
            </div>
        </div>
		<div class="module" style="height:50px;width:300px;padding:10px;margin:10px;">
			<legend>返回值信息</legend>
            <div id="cbInfo" class="cbInfo"></div>
            <button style="width:80px;height:24px;padding:30px;margin:0;" id="clear">清空</button>
		</div>
    </div>
</body>
<script src="jquery-1.12.4.min.js"></script>
<script src="jsencrypt.min.js"></script>
<script src="web-control_1.2.5.min.js"></script>

<script type="text/javascript">
  var div = window.parent.document.getElementById('iframeVideo');
  var widthData = div.offsetWidth; // 元素的宽度
  var heightData = div.offsetHeight; // 元素的高度
    // 插件对象实例,初始化为null,需要创建多个插件窗口时,需要定义多个插件对象实例变量,各个变量唯一标志对应的插件实例
    var oWebControl = null;
    var bIE = (!!window.ActiveXObject || 'ActiveXObject' in window);// 是否为IE浏览器
    var pubKey = '';                  // demo中未使用加密,可根据需求参照开发指南自行使用加密功能
	var initCount = 0;                // 异常重启计数
	var iframePos = {
		top:0,
		left:0
	};               // iframe相对文档的位置
  let default_left = 528;//默认left
  let default_top = 132;//默认top
  let default_width = widthData;//默认宽度
  let default_height = heightData;//默认高度
  let deviation_top = 120; //偏移量
	var parentTitle = '';	          // 父页面标题
	var iframeClientPos = null;       // iframe相对视窗的位置
	var iframeParentShowSize = null;  // 视窗大小 width height
  var videoType = ''
  var cameraCode =''
  var cameraCodeArr = []//code集合
  var cameraWndId = -1;
    // 标签关闭
    $(window).unload(function () {
        if (oWebControl != null){
				oWebControl.JS_HideWnd();  // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题
                oWebControl.JS_Disconnect().then(function(){}, function() {});
            }
    });
    $(window).resize(function () {
      var div = window.parent.document.getElementById('iframeVideo');
      var widthData = div.offsetWidth; // 元素的宽度
      var heightData = div.offsetHeight; // 元素的高度
      if (oWebControl != null){
        oWebControl.JS_Resize(widthData, heightData);
      }
      console.log('宽度: ' + widthData + ', 高度: ' + heightData);

    });
   //监听父页面的消息
	window.addEventListener('message', function(e){
		if(e && e.data){
			switch (e.data.action){
				case 'sendTitle':              // 父页面将其标题发送过来,子页面保存该标题,以便创建插件窗口成功后将标题设置回给父页面
					parentTitle = e.data.info;
          // cameraCode = e.data.cameraCode;
          // console.log(cameraCode,'ybu')
          videoType = e.data.videoType;
          console.log(videoType,'type');
					break;
        case 'sendCameraCode': //传递code
          cameraCode = e.data.cameraCode;
          console.log('传递的参数:',cameraCode,"::",cameraWndId)
          //如果大于9,则从1 开始
          if(cameraWndId>9){
            cameraWndId=-1
            cameraCodeArr.shift();
          }
          if(videoType == 'live'){
            //监控
            let flag = false;
            cameraCodeArr.forEach(e=>{
              console.log("播放的摄像头:",e)
              if(e.cameraIndexCode==cameraCode){
                flag = true;
              }
            })

            if(!flag){
              let obj = {
                 "cameraIndexCode": cameraCode,
                 "transMode": 0,
                 // "wndId": cameraWndId,
                 "wndId": -1,
                 "gpuMode": 0
              }
              cameraCodeArr.push(obj)
              cameraCode = obj;
            }else{
              console.log("存在:",cameraCode);
            }
            cameraWndId++
          }else{
            //回放
            let startTimeStamp = e.data.startTime
            let endTimeStamp = e.data.endTime
            console.log("回放:",startTimeStamp,endTimeStamp);
            let obj = {
                "cameraIndexCode": cameraCode,
                "startTimeStamp": startTimeStamp,//"1714444801",
                "endTimeStamp": endTimeStamp,
                "recordLocation": 1,
                "transMode": 0,
                // "wndId": cameraWndId,//cameraCodeArr.length+1,
                "wndId": -1,
                "gpuMode": 0
            }
            cameraCodeArr.push(obj)
            cameraCode = obj;
          }

          console.log("封装参数:",cameraCodeArr)
          // getLayout();
          initPayBtn()
					break;
				case 'updatePos':              // 更新插件位置
					var scrollValue = e.data.scrollValue;     // 滚动条滚动偏移量
					console.log("~~~~~scrollValue~~~~~",scrollValue)
					oWebControl.JS_SetDocOffset({
						left: iframePos.left + scrollValue.left,
						top: iframePos.top + scrollValue.top
					});  // 更新插件窗口位置
					console.log("~~~~~~~~~~",oWebControl)
					if(oWebControl){
						oWebControl.JS_SetDocOffset({
							left: default_left,
							top: default_top
						});
					}
					oWebControl.JS_Resize(default_width, default_height);
					// setWndCover();
					break;
				case 'scroll':
					iframeParentShowSize = e.data.showSize;   // 视窗大小
					iframePos = e.data.iframeOffset;          // iframe与文档的偏移量
					iframeClientPos = e.data.iframeClientPos; // iframe相对视窗的位置
					var scrollValue = e.data.scrollValue;     // 滚动条滚动偏移量
					if(oWebControl){
						oWebControl.JS_SetDocOffset({
							left: default_left,//iframePos.left + scrollValue.left,
							top: iframePos.top + scrollValue.top + deviation_top
						});    // 更新插件窗口位置
						oWebControl.JS_Resize(default_width, default_height);
						// setWndCover();
					}
					break;
				default:
					break;
			}
		}
	});

  let editLayout = 0;
  let layout = "4x4"
  function getLayout(){
    if(oWebControl){
      oWebControl.JS_RequestInterface({
        funcName: "getLayout"
      }).then(function (oData) {
        console.log("查看:",oData.responseMsg.data)
        if(oData.responseMsg.data){
          // debugger
            let data = oData.responseMsg.data;
            data = JSON.parse(data)
           let wndNum = data.wndNum;
           let len = cameraCodeArr.length;
           console.log("判断:",len);
           //布局不大于25,设置5*5
           if(len<9){

           }

        }
      })
    }
  }

  function getAndSetLayout(){
    if(oWebControl){
      oWebControl.JS_RequestInterface({
        funcName: "getLayout"
      }).then(function (oData) {
        console.log("查看:",oData.responseMsg.data)
        if(oData.responseMsg.data){
          // debugger
            let data = oData.responseMsg.data;
            data = JSON.parse(data)
           let wndNum = data.wndNum;
           let len = cameraCodeArr.length;
           console.log("判断:",len);
           //布局不大于25,设置5*5
           if(len<26){
             if(len>wndNum){
                //默认是3*3,第一次修改4*4,暂时这样处理,后期可以动态
                if(editLayout==1){
                  layout = "5x5";
                }
                oWebControl.JS_RequestInterface({
                  funcName: "setLayout",
                  argument:  {layout: layout}
                }).then(function (oData) {
                 console.log("设置布局:",oData);
                })
                editLayout++
             }
           }

        }
      })
    }
  }
	// 顶部:iframe.getBoundingClientRect().top小于0并且其绝对值超过DIV.get(0).getBoundingClientRect().top部分需要剪切
	// 底部:(iframe.getBoundingClientRect().bottom - iframe父窗口可视域高度,为H1)为不可见部分
	//       ($(window).height() - DIV.get(0).getBoundingClientRect().bottom)
	//        为DIV底部与其所在iframe底部之间的距离H2,H1-H2的值大于0则表示DIV有部分在不可见区域
	// 左边:iframe.getBoundingClientRect().left小于0并且其绝对值超过DIV.get(0).getBoundingClientRect().left部分需要剪切
	// 右边:(iframe宽度 - DIV.get(0).getBoundingClientRect().right表示DIV右边与其父iframe右边之间的距离,为W1)
	//       (iframe父窗口可视域宽度-iframe.getBoundingClientRect().left表示iframe左边与iframe父窗口可视域右边之间的距离,为W2)
	//       (iframe宽度 - W2 - W1)如果大于0,则表示DIV右边超出了iframe父窗口可视域,需要剪切超过的部分
	function setWndCover() {
		if (oWebControl){
			// 准备要用到的一些数据
			var iframeWndHeight = $(window).height();  // iframe窗口高度
			var iframeWndWidth = $(window).width();    // iframe窗口宽度
			var divLeft = $("#playWnd").get(0).getBoundingClientRect().left;
			var divTop = $("#playWnd").get(0).getBoundingClientRect().top;
			var divRight = $("#playWnd").get(0).getBoundingClientRect().right;
			var divBottom = $("#playWnd").get(0).getBoundingClientRect().bottom;
			var divWidth = $("#playWnd").width();
			var divHeight = $("#playWnd").height();

			oWebControl.JS_RepairPartWindow(0, 0, 801, 401);  // 多1个像素点防止还原后边界缺失一个像素条

			// 判断剪切矩形的上边距
			if (iframeClientPos.top < 0 && Math.abs(iframeClientPos.top) > divTop){
				var deltaTop = Math.abs(iframeClientPos.top) - divTop;
				oWebControl.JS_CuttingPartWindow(0, 0, 801, deltaTop + 1);
				//console.log({deltaTop: deltaTop});
			}

			// 判断剪切矩形的左边距
			if (iframeClientPos.left < 0 && Math.abs(iframeClientPos.left) > divLeft){
				var deltaLeft = Math.abs(iframeClientPos.left) - divLeft;
				//console.log({deltaLeft: deltaLeft});
				oWebControl.JS_CuttingPartWindow(0, 0, deltaLeft, 401);  // 多剪掉一个像素条,防止出现剪掉一部分窗口后出现一个像素条
			}

			// 判断剪切矩形的右边距
			var W1 = iframeWndWidth - divRight;
			var W2 = iframeParentShowSize.width - iframeClientPos.left;
			if (W2 < divWidth){
				var deltaRight = iframeWndWidth - W2 - W1;
				if (deltaRight > 0) {
					oWebControl.JS_CuttingPartWindow(800 - deltaRight, 0, deltaRight + 1, 401);
				}
			}

			// 判断剪切矩形的下边距
			var H1 = iframeClientPos.bottom - iframeParentShowSize.height;
			var H2 = iframeWndHeight - divBottom;
			var deltaBottom = H1 - H2;
			//console.log({deltaBottom: deltaBottom});
			if (deltaBottom > 0) {
				oWebControl.JS_CuttingPartWindow(0, 400 - deltaBottom, 801, deltaBottom + 1);
			}
		}
    }

    // 创建插件实例,并启动本地服务建立websocket连接,创建插件窗口
	function initPlugin () {
    // console.log("初始化实例:",oWebControl);
    // if(oWebControl){
      oWebControl = new WebControl({
        szPluginContainer: "playWnd",
        iServicePortStart: 15900,
        iServicePortEnd: 15900,
        szClassId:"23BF3B0A-2C56-4D97-9C03-0CB103AA8F11",   // 用于IE10使用ActiveX的clsid
        cbConnectSuccess: function () {
          initCount = 0;
          setCallbacks();
          oWebControl.JS_StartService("window", {
            dllPath: "./VideoPluginConnect.dll"
          }).then(function () {
            // 步骤2:JS_CreateWnd时指定cbSetDocTitle回调,并在回调中向父页面发送更新标题消息,标题为回调出来的uuid
            oWebControl.JS_CreateWnd("playWnd", 800, 400, {
                bEmbed: true,
              cbSetDocTitle: function (uuid) {
                oWebControl._pendBg = false;
                window.parent.postMessage({
                  action:'updateTitle',
                  msg:'子页面通知父页面修改title',
                  info:uuid
                }, '\*');    // '\*'表示跨域参数,请结合自身业务合理设置
              }
            }).then(function () {
              oWebControl.JS_SetDocOffset({
              	left: default_left,
              	top: default_top
              });
              // 步骤3:JS_CreateWnd成功后通知父页面将其标题修改回去
              console.log("JS_CreateWnd success",oWebControl);
              window.parent.postMessage({
                  action:'updateTitle',
                  msg:'子页面通知父页面更新title',
                  info: parentTitle
                }, '\*');

              // 步骤4:发消息更新插件窗口位置:这里不直接更新的原因是,父页面默认可能就存在滚动条,此时有滚动量
              window.parent.postMessage({
                  action:'updatePos',
                  msg:'更新Pos'
                }, '\*');

              initBtnClicked();
            });
          }, function () {
            console.log("JS_CreateWnd fail");
          });
        },
        cbConnectError: function () {
          console.log("cbConnectError");
          oWebControl = null;
          $("#playWnd").html("插件未启动,正在尝试启动,请稍候...");
          WebControl.JS_WakeUp("VideoWebPlugin://");
          initCount ++;
          if (initCount < 3) {
            setTimeout(function () {
              initPlugin();
            }, 3000)
          } else {
            $("#playWnd").html("插件启动失败,请检查插件是否安装!");
          }
        },
        cbConnectClose: function (bNormalClose) {
          // 异常断开:bNormalClose = false
          // JS_Disconnect正常断开:bNormalClose = true
          if (true == bNormalClose){
            console.log("cbConnectClose normal");
          }else{
            console.log("cbConnectClose exception");
          }

          oWebControl = null;
          $("#playWnd").html("插件未启动,正在尝试启动,请稍候...");
          WebControl.JS_WakeUp("VideoWebPlugin://");
          initCount ++;
          if (initCount < 3) {
            setTimeout(function () {
              initPlugin();
            }, 3000)
          } else {
            $("#playWnd").html("插件启动失败,请检查插件是否安装!");
          }
        }
      });
    // }
	}

	initPlugin();

    // 获取公钥
    function getPubKey (callback) {
        oWebControl.JS_RequestInterface({
            funcName: "getRSAPubKey",
            argument: JSON.stringify({
                keyLength: 1024
            })
        }).then(function (oData) {
            console.log(oData)
            if (oData.responseMsg.data) {
                pubKey = oData.responseMsg.data
                callback()
            }
        })
    }

    // 设置窗口控制回调
    function setCallbacks() {
        oWebControl.JS_SetWindowControlCallback({
            cbIntegrationCallBack: cbIntegrationCallBack
        });
    }

    // 推送消息
    function cbIntegrationCallBack(oData) {
        showCBInfo(JSON.stringify(oData.responseMsg));
    }

    // RSA加密
    function setEncrypt (value) {
        var encrypt = new JSEncrypt();
        encrypt.setPublicKey(pubKey);
        return encrypt.encrypt(value);
    }

    function requestInterface(value)
    {
        oWebControl.JS_RequestInterface(JSON.parse(value)).then(function (oData) {
            console.log("执行结果:",oData,value)
            showCBInfo(JSON.stringify(oData ? oData.responseMsg : ''));
        });
    }

    // 显示接口返回的消息及插件回调信息
    function showCBInfo(szInfo, type) {
        if (type === 'error') {
            szInfo = "<div style='color: red;'>" + dateFormat(new Date(), "yyyy-MM-dd hh:mm:ss") + " " + szInfo + "</div>";
        } else {
            szInfo = "<div>" + dateFormat(new Date(), "yyyy-MM-dd hh:mm:ss") + " " + szInfo + "</div>";
        }
        $("#cbInfo").html(szInfo + $("#cbInfo").html());

		/* oWebControl.JS_SetDocOffset({
			left: 150,
			top: 150
		});
		oWebControl.JS_Resize(400, 400);
		setWndCover(); */
    }

	function initBtnClicked(){
		var param = $("#initParam").val();
        //删除字符串中的回车换行

    console.log(param,'initpatam');
    const paramStr = JSON.parse(param)
    console.log(paramStr,'paramsta');
    if(videoType == "live"){
      paramStr.argument.playMode = 0
    }else{
      paramStr.argument.playMode = 1
    }
    param = JSON.stringify(paramStr)
    param = param.replace(/(\s*)/g, "");
		// 执行初始化
		requestInterface(param);
	}

    $("#initBtn").click(function() {
        initBtnClicked();
    })

    $("#playBtn").click(function() {
        var param = $("#playParam").val();
        //删除字符串中的回车换行
        param = param.replace(/(\s*)/g, "");

        // 执行预览
        requestInterface(param);
    })

    // 清空
    $("#clear").click(function() {
        $("#cbInfo").html('');
    })
    function initPayBtn(){
        setTimeout(() => {
          // initBtnClicked();
        }, 500);

        setTimeout(() => {
          var param = $("#playParam").val();
          const paramStr2 = JSON.parse(param)
          if(videoType == 'live'){
            /* paramStr2.funcName = "startMultiPreviewByCameraIndexCode"
            let argumentList = paramStr2.argument.list;
            paramStr2.argument.list =cameraCodeArr;//*/

            paramStr2.funcName = "startPreview"
            let argumentList = paramStr2.argument;
            paramStr2.argument =cameraCode;//

            console.log("摄像头参数:",paramStr2);
            let test = [
              {
                 "cameraIndexCode": "d597b77b8c73459792c82ded3acb6eba",
                 "transMode": 0,
                 "wndId": 1,
                 "gpuMode": 0
              },
              {
                 "cameraIndexCode": "b8f8e561950a4e3bbc4f2e3bf55828e7",
                 "transMode": 0,
                 "wndId": 2,
                 "gpuMode": 0
              },
              {
                 "cameraIndexCode": "38e0da0b6fe04690a403abc0ed45438f",
                 "transMode": 0,
                 "wndId": 3,
                 "gpuMode": 0
              },
            ]
            // paramStr2.argument.list = test;

          }else{
            /* paramStr2.funcName = "startMultiPlaybackByCameraIndexCode"
            let argumentList = paramStr2.argument.list;
            paramStr2.argument.list =cameraCodeArr;
            console.log("摄像头参数:",paramStr2); */
            paramStr2.funcName = "startPlayback"
            let argumentList = paramStr2.argument;
            paramStr2.argument =cameraCode;//

            console.log("摄像头参数:",paramStr2);
            /* {
                "argument": {
                    "cameraIndexCode": "25603528001310681100",
                    "startTimeStamp": "1714444801",
                    "endTimeStamp": "1714466401",
                    "gpuMode": 0,
                    "streamMode": 0,
                    "transMode": 1,
                    "wndId": -1
                },
                "funcName": "startPreview"
            } */
            let test = [{
            "cameraIndexCode": "4cc3b6a7961f4083b50ee8a67a840ec4",
            "startTimeStamp": 171544320000,
            "endTimeStamp": 171549360000,
            "recordLocation": 1,
            "transMode": 0,
            "wndId": 1,
            "gpuMode": 0
            }]
            // paramStr2.argument.list = test;

          }
          param = JSON.stringify(paramStr2)
          //删除字符串中的回车换行
          param = param.replace(/(\s*)/g, "");

          // 执行预览
          requestInterface(param);
        }, 1000);

    }

    // setTimeout(function(){
    //   initPayBtn()
    // },2000)
	// 格式化时间
    function dateFormat(oDate, fmt) {
        var o = {
            "M+": oDate.getMonth() + 1, //月份
            "d+": oDate.getDate(), //日
            "h+": oDate.getHours(), //小时
            "m+": oDate.getMinutes(), //分
            "s+": oDate.getSeconds(), //秒
            "q+": Math.floor((oDate.getMonth() + 3) / 3), //季度
            "S": oDate.getMilliseconds()//毫秒
        };
        if (/(y+)/.test(fmt)) {
            fmt = fmt.replace(RegExp.$1, (oDate.getFullYear() + "").substr(4 - RegExp.$1.length));
        }
        for (var k in o) {
            if (new RegExp("(" + k + ")").test(fmt)) {
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
            }
        }
        return fmt;
    }
</script>
</html>

视频回放:playback_for_iframe.html

<!doctype html>
<html>
<head>
    <title>视频回放</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta http-equiv="Pragma" content="no-cache"/>
    <meta http-equiv="Cache-Control" content="no-cache, must-revalidate"/>
    <meta http-equiv="Expires" content="0"/>
</head>
<style>
    html, body {
        padding: 0;
        margin: 0;
    }

    .playWnd {
        margin: 30px 0 0 30px;
        width: 600px;
        height: 400px;
        border: 1px solid red;
    }

    .cbInfoDiv {
        float: left;
        width: 360px;
        margin-left: 16px;
        border: 1px solid #7F9DB9;
    }

    .cbInfo {
        height: 200px;
        padding: 5px;
        border: 1px solid #7F9DB9;
        word-break: break-all;
        overflow: auto;
    }

    .operate {
        margin-top: 24px;
    }

    .operate::after {
        content: '';
        display: block;
        clear: both;
    }

    .operate .btns {
        height: 32px;
    }

    .module {
        float: left;
        width: 120px;
        min-height: 290px;
        margin-left: 10px;
        padding: 16px 8px;
        box-sizing: border-box;
        border: 1px solid #e5e5e5;
    }

    .module .item {
        margin-bottom: 4px;
    }

    .module .label {
        width: 150px;
        display: inline-block;
        vertical-align: middle;
        margin-right: 8px;
        text-align: right;
    }

    .module input[type="text"],
    .module select {
        box-sizing: border-box;
        display: inline-block;
        vertical-align: middle;
        margin-left: 0;
        width: 150px;
        min-height: 20px;
    }

    .module .btn {
        min-width: 80px;
        min-height: 24px;
        margin-top: 16px;
        margin-left: 158px;
    }
</style>
<body>
<div id="playWnd" class="playWnd" style="left: 109px; top: 133px;"></div>
<div id="operate" class="operate">
    <!--初始化、反初始化、设置认证信息接口调用入口。
    1.插件所有接口都需要在调用初始化并返回成功后才能调用
    2.设置认证信息仅适用于对接多平台时的情况,具体参照开发指南
    3.反初始化后,插件资源销毁-->
    <div class="module" style="left:30px;height:30px;width:280px;padding:10px;margin:10px;">
        <div class="item">
            <label>初始化相关参数:</label>
            <textarea id="initParam" type="text" style="width:260px;height:200px;">
{
    "argument": {
        "appkey": "22547186",
        "ip": "159.61.64.249",
        "port": 443,
        "secret": "sPteT21oUI2CCav79heE",
        "enableHTTPS": 1,
        "language": "zh_CN",
        "layout": "3x3",
        "playMode": 0,
        "reconnectDuration": 5,
        "reconnectTimes": 5,
        "showSmart": 0,
        "showToolbar": 1,
        "toolBarButtonIDs": "2048,2049,2050,2304,2306,2305,2307,2308,2309,4096,4608,4097,4099,4098,4609,4100",
        "snapDir": "C:/snap",
        "videoDir": "C:/video"
    },
    "funcName": "init"
}
                </textarea>
        </div>
        <div class="item">
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <button style="width:10px;padding:0;margin:0;" id="initBtn" class="btn">执行</button>
        </div>
    </div>

    <!--单个点位播放、批量点位播放、批量停止播放、全部停止播放接口调用入口。
    1.authUuid为对接多平台时必须的播放字段,对接单平台时,可不指定-->
    <div class="module" style="height:30px;width:280px;padding:10px;margin:10px;">
        <div class="item">
            <label>播放相关参数:</label>
            <textarea id="playParam" type="text" style="width:260px;height:200px;">
              {
                "argument": {
                    "authUuid": "",
                    "cameraIndexCode": "d597b77b8c73459792c82ded3acb6eba",
                    "ezvizDirect": 0,
                    "gpuMode": 0,
                    "streamMode": 0,
                    "transMode": 0,
                    "wndId": -1,
                    "cascade": 1
                },
                "funcName": "startPreview"
            }
                </textarea>
        </div>
        <div class="item">
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <button style="width:10px;padding:0;margin:0;" id="playBtn" class="btn">执行</button>
        </div>
    </div>
    <div class="module" style="height:50px;width:300px;padding:10px;margin:10px;">
        <legend>返回值信息</legend>
        <div id="cbInfo" class="cbInfo"></div>
        <button style="width:80px;height:24px;padding:30px;margin:0;" id="clear">清空</button>
    </div>
</div>
</body>
<script src="jquery-1.12.4.min.js"></script>
<script src="jsencrypt.min.js"></script>
<script src="web-control_1.2.5.min.js"></script>

<script type="text/javascript">
    // 插件对象实例,初始化为null,需要创建多个插件窗口时,需要定义多个插件对象实例变量,各个变量唯一标志对应的插件实例
    var oWebControl = null;
    var bIE = (!!window.ActiveXObject || 'ActiveXObject' in window);// 是否为IE浏览器
    var pubKey = '';                  // demo中未使用加密,可根据需求参照开发指南自行使用加密功能
    var initCount = 0;                // 异常重启计数
    var iframePos = {   };               // iframe相对文档的位置
    var parentTitle = '';	          // 父页面标题
    var iframeClientPos = {
      left:200,
      top:200
    };       // iframe相对视窗的位置
    var iframeParentShowSize = null;  // 视窗大小 width height

    // 标签关闭
    $(window).unload(function () {
        if (oWebControl != null) {
            oWebControl.JS_HideWnd();  // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题
            oWebControl.JS_Disconnect().then(function () {
            }, function () {
            });
        }
    });

    // 监听resize事件,使插件窗口尺寸跟随DIV窗口变化
    $(window).resize(function () {
        if (oWebControl != null) {
            oWebControl.JS_Resize(600, 400);
            setWndCover();
        }
    });

        // 监听滚动条scroll事件,使插件窗口跟随浏览器滚动而移动
        $(window).scroll(function () {
        if (oWebControl != null) {
            oWebControl.JS_Resize(600, 400);
            setWndCover();
        }
    });
    //监听父页面的消息
    window.addEventListener('message', function (e) {
        if (e && e.data) {
            switch (e.data.action) {
                case 'sendTitle':              // 父页面将其标题发送过来,子页面保存该标题,以便创建插件窗口成功后将标题设置回给父页面
                    parentTitle = e.data.info;
                    break;
                case 'updatePos':              // 更新插件位置
                    var scrollValue = e.data.scrollValue;     // 滚动条滚动偏移量
                    oWebControl.JS_SetDocOffset({
                        left: iframePos.left + scrollValue.left,
                        top: iframePos.top + scrollValue.top
                    });  // 更新插件窗口位置
                    console.log("~~~~~~~~~~~~~~~~~")
                    oWebControl.JS_Resize(600, 400);
                    setWndCover();
                    break;
                case 'scroll':
                    iframeParentShowSize = e.data.showSize;   // 视窗大小
                    iframePos = e.data.iframeOffset;          // iframe与文档的偏移量
                    iframeClientPos = e.data.iframeClientPos; // iframe相对视窗的位置
                    var scrollValue = e.data.scrollValue;     // 滚动条滚动偏移量
                    if (oWebControl) {
                        oWebControl.JS_SetDocOffset({
                            left: iframePos.left + scrollValue.left,
                            top: iframePos.top + scrollValue.top
                        });    // 更新插件窗口位置
                        oWebControl.JS_Resize(600, 400);
                        setWndCover();
                    }
                    break;
                default:
                    break;
            }
        }
    });

    // 顶部:iframe.getBoundingClientRect().top小于0并且其绝对值超过DIV.get(0).getBoundingClientRect().top部分需要剪切
    // 底部:(iframe.getBoundingClientRect().bottom - iframe父窗口可视域高度,为H1)为不可见部分
    //       ($(window).height() - DIV.get(0).getBoundingClientRect().bottom)
    //        为DIV底部与其所在iframe底部之间的距离H2,H1-H2的值大于0则表示DIV有部分在不可见区域
    // 左边:iframe.getBoundingClientRect().left小于0并且其绝对值超过DIV.get(0).getBoundingClientRect().left部分需要剪切
    // 右边:(iframe宽度 - DIV.get(0).getBoundingClientRect().right表示DIV右边与其父iframe右边之间的距离,为W1)
    //       (iframe父窗口可视域宽度-iframe.getBoundingClientRect().left表示iframe左边与iframe父窗口可视域右边之间的距离,为W2)
    //       (iframe宽度 - W2 - W1)如果大于0,则表示DIV右边超出了iframe父窗口可视域,需要剪切超过的部分
    function setWndCover() {
        if (oWebControl) {
            // 准备要用到的一些数据
            var iframeWndHeight = $(window).height();  // iframe窗口高度
            var iframeWndWidth = $(window).width();    // iframe窗口宽度
            var divLeft = $("#playWnd").get(0).getBoundingClientRect().left;
            var divTop = $("#playWnd").get(0).getBoundingClientRect().top;
            var divRight = $("#playWnd").get(0).getBoundingClientRect().right;
            var divBottom = $("#playWnd").get(0).getBoundingClientRect().bottom;
            var divWidth = $("#playWnd").width();
            var divHeight = $("#playWnd").height();

            oWebControl.JS_RepairPartWindow(0, 0, 801, 401);  // 多1个像素点防止还原后边界缺失一个像素条

            // 判断剪切矩形的上边距
            if (iframeClientPos.top < 0 && Math.abs(iframeClientPos.top) > divTop) {
                var deltaTop = Math.abs(iframeClientPos.top) - divTop;
                oWebControl.JS_CuttingPartWindow(0, 0, 601, deltaTop + 1);
                //console.log({deltaTop: deltaTop});
            }

            // 判断剪切矩形的左边距
            if (iframeClientPos.left < 0 && Math.abs(iframeClientPos.left) > divLeft) {
                var deltaLeft = Math.abs(iframeClientPos.left) - divLeft;
                //console.log({deltaLeft: deltaLeft});
                oWebControl.JS_CuttingPartWindow(0, 0, deltaLeft, 401);  // 多剪掉一个像素条,防止出现剪掉一部分窗口后出现一个像素条
            }

            // 判断剪切矩形的右边距
            var W1 = iframeWndWidth - divRight;
            var W2 = iframeParentShowSize.width - iframeClientPos.left;
            if (W2 < divWidth) {
                var deltaRight = iframeWndWidth - W2 - W1;
                if (deltaRight > 0) {
                    oWebControl.JS_CuttingPartWindow(600 - deltaRight, 0, deltaRight + 1, 401);
                }
            }

            // 判断剪切矩形的下边距
            var H1 = iframeClientPos.bottom - iframeParentShowSize.height;
            var H2 = iframeWndHeight - divBottom;
            var deltaBottom = H1 - H2;
            //console.log({deltaBottom: deltaBottom});
            if (deltaBottom > 0) {
                oWebControl.JS_CuttingPartWindow(0, 400 - deltaBottom, 601, deltaBottom + 1);
            }
        }
    }

    // 创建插件实例,并启动本地服务建立websocket连接,创建插件窗口
    function initPlugin() {
        oWebControl = new WebControl({
            szPluginContainer: "playWnd",
            iServicePortStart: 15900,
            iServicePortEnd: 15900,
            szClassId: "23BF3B0A-2C56-4D97-9C03-0CB103AA8F11",   // 用于IE10使用ActiveX的clsid
            cbConnectSuccess: function () {
                initCount = 0;
                setCallbacks();
                oWebControl.JS_StartService("window", {
                    dllPath: "./VideoPluginConnect.dll"
                }).then(function () {
                    // 步骤2:JS_CreateWnd时指定cbSetDocTitle回调,并在回调中向父页面发送更新标题消息,标题为回调出来的uuid
                    oWebControl.JS_CreateWnd("playWnd", 600, 400, {
                        bEmbed: true,
                        cbSetDocTitle: function (uuid) {
                            oWebControl._pendBg = false;
                            window.parent.postMessage({
                                action: 'updateTitle',
                                msg: '子页面通知父页面修改title',
                                info: uuid
                            }, '\*');    // '\*'表示跨域参数,请结合自身业务合理设置
                        }
                    }).then(function () {
                        // 步骤3:JS_CreateWnd成功后通知父页面将其标题修改回去
                        console.log("JS_CreateWnd success");

                        oWebControl.JS_SetDocOffset({
                          left: 300,
                            top: 100
                        });

                        window.parent.postMessage({
                            action: 'updateTitle',
                            msg: '子页面通知父页面更新title',
                            info: parentTitle
                        }, '\*');

                        // 步骤4:发消息更新插件窗口位置:这里不直接更新的原因是,父页面默认可能就存在滚动条,此时有滚动量
                        window.parent.postMessage({
                            action: 'updatePos',
                            msg: '更新Pos'
                        }, '\*');

                        initBtnClicked();
                    });
                }, function () {
                    console.log("JS_CreateWnd fail");
                });
            },
            cbConnectError: function () {
                console.log("cbConnectError");
                oWebControl = null;
                $("#playWnd").html("插件未启动,正在尝试启动,请稍候...");
                WebControl.JS_WakeUp("VideoWebPlugin://");
                initCount++;
                if (initCount < 3) {
                    setTimeout(function () {
                        initPlugin();
                    }, 3000)
                } else {
                    $("#playWnd").html("插件启动失败,请检查插件是否安装!");
                }
            },
            cbConnectClose: function (bNormalClose) {
                // 异常断开:bNormalClose = false
                // JS_Disconnect正常断开:bNormalClose = true
                if (true == bNormalClose) {
                    console.log("cbConnectClose normal");
                } else {
                    console.log("cbConnectClose exception");
                }

                oWebControl = null;
                $("#playWnd").html("插件未启动,正在尝试启动,请稍候...");
                WebControl.JS_WakeUp("VideoWebPlugin://");
                initCount++;
                if (initCount < 3) {
                    setTimeout(function () {
                        initPlugin();
                    }, 3000)
                } else {
                    $("#playWnd").html("插件启动失败,请检查插件是否安装!");
                }
            }
        });
    }

    initPlugin();
    setTimeout(() => {

    }, 3000)

    // 获取公钥
    function getPubKey(callback) {
        oWebControl.JS_RequestInterface({
            funcName: "getRSAPubKey",
            argument: JSON.stringify({
                keyLength: 1024
            })
        }).then(function (oData) {
            console.log(oData)
            if (oData.responseMsg.data) {
                pubKey = oData.responseMsg.data
                callback()
            }
        })
    }

    // 设置窗口控制回调
    function setCallbacks() {
        oWebControl.JS_SetWindowControlCallback({
            cbIntegrationCallBack: cbIntegrationCallBack
        });
    }

    // 推送消息
    function cbIntegrationCallBack(oData) {
        showCBInfo(JSON.stringify(oData.responseMsg));
    }

    // RSA加密
    function setEncrypt(value) {
        var encrypt = new JSEncrypt();
        encrypt.setPublicKey(pubKey);
        return encrypt.encrypt(value);
    }

    function requestInterface(value) {
        oWebControl.JS_RequestInterface(JSON.parse(value)).then(function (oData) {
            console.log(oData)
            showCBInfo(JSON.stringify(oData ? oData.responseMsg : ''));
        });
    }

    // 显示接口返回的消息及插件回调信息
    function showCBInfo(szInfo, type) {
        if (type === 'error') {
            szInfo = "<div style='color: red;'>" + dateFormat(new Date(), "yyyy-MM-dd hh:mm:ss") + " " + szInfo + "</div>";
        } else {
            szInfo = "<div>" + dateFormat(new Date(), "yyyy-MM-dd hh:mm:ss") + " " + szInfo + "</div>";
        }
        $("#cbInfo").html(szInfo + $("#cbInfo").html());
    }

    function initBtnClicked() {
        var param = $("#initParam").val();
        //删除字符串中的回车换行
        param = param.replace(/(\s*)/g, "");

        // 执行初始化
        requestInterface(param);
    }

    $("#initBtn").click(function () {
        initBtnClicked();
    })

    $("#playBtn").click(function () {
        var param = $("#playParam").val();
        //删除字符串中的回车换行
        param = param.replace(/(\s*)/g, "");

        // 执行预览
        requestInterface(param);
    })

    // 清空
    $("#clear").click(function () {
        $("#cbInfo").html('');
    })

    // 格式化时间
    function dateFormat(oDate, fmt) {
        var o = {
            "M+": oDate.getMonth() + 1, //月份
            "d+": oDate.getDate(), //日
            "h+": oDate.getHours(), //小时
            "m+": oDate.getMinutes(), //分
            "s+": oDate.getSeconds(), //秒
            "q+": Math.floor((oDate.getMonth() + 3) / 3), //季度
            "S": oDate.getMilliseconds()//毫秒
        };
        if (/(y+)/.test(fmt)) {
            fmt = fmt.replace(RegExp.$1, (oDate.getFullYear() + "").substr(4 - RegExp.$1.length));
        }
        for (var k in o) {
            if (new RegExp("(" + k + ")").test(fmt)) {
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
            }
        }
        return fmt;
    }
</script>
</html>

indexTab.vue

<template>
  <div class="user" style="height: 100vh;overflow: hidden;">
    <basic-container>
      <el-tabs :lazy="true" v-model="activeName">
        <el-tab-pane label="实时视频" name="实时视频">
          <Video :activeName="activeName"></Video>
        </el-tab-pane>
        <el-tab-pane label="视频回放" name="视频回放">
          <rtspPlayer :activeName="activeName"></rtspPlayer>
        </el-tab-pane>
      </el-tabs>
    </basic-container>
  </div>
</template>

<script>
import Video from "@/views/video/index.vue";
import rtspPlayer from "@/views/video/rtspPlayer.vue";

export default {
  components: {
    Video, rtspPlayer
  },
  data() {
    return {
      activeName: '实时视频'
    };
  },
  computed: {},
  created() {
  },
  mounted() {
  },
  methods: {}
};
</script>
<style scoped lang="scss">
::v-deep .el-tabs__item {
  font-size: 18px !important;
  /* 设置你想要的字体大小 */
  letter-spacing: 2px
}
::v-deep #avue-view{
  overflow: hidden !important;
}
</style>

rtspPlayer.vue

<template>
  <div>
    <div>
      <span>回放时间:</span>
      <br>
      <el-date-picker style="width: 12%;margin-bottom: 5px;margin-top: 5px" v-model="startTime" type="datetime"
                      value-format="yyyy-MM-dd HH:mm:ss"  @blur="dateBlur" @focus="dateFocus" placeholder="开始时间"
      >
      </el-date-picker>
      <br>
      <el-date-picker style="width: 12%;" v-model="endTime" type="datetime"
                      value-format="yyyy-MM-dd HH:mm:ss"  @blur="dateBlur" @focus="dateFocus" placeholder="结束时间"
      >
      </el-date-picker>
      <el-row style="display: flex;">
        <div class="user__tree" style="margin-top: 30px;overflow:auto;height: 72vh;font-size: 18px;width: 280px;">
<!--          <avue-tree :option="treeOption" :data="treeData" style="width: 320px;">-->
<!--            <span class="el-tree-node__label" slot-scope="{ node, data }" style="display: flex;align-items: center;font-size: 18px">-->
<!--&lt;!&ndash;              <span v-if="data.isOnline===0" class="tree_icon_red"></span>&ndash;&gt;-->
<!--&lt;!&ndash;              <span v-if="data.isOnline===1" class="tree_icon_green"></span>&ndash;&gt;-->
<!--              <span :class="{ 'last-level-node': !data.children || data.children.length === 0 }" @dblclick="dblclick(node, data)">{{ node.label }}</span>-->
<!--            </span>-->
<!--          </avue-tree>-->
          <el-tree
            :data="treeData"
            :option="treeOption"
            class="disable-selection"
            empty-text="加载中,请稍后"
            node-key="areaId"
            :default-expanded-keys="defaultExpandedKeys"
            :props="defaultProps"
          >
          <span
            class="el-tree-node__label"
            slot-scope="{ node, data }"
            :class="{ 'last-level-node': !data.children || data.children.length === 0 }"
            @dblclick="dblclick(node, data)"
            style="display: flex;align-items: center;font-size: 18px;"
          >
            <span>{{ data.name }}</span>
            </span>
          </el-tree>
        </div>

        <el-col :span="10" class="user__main" style="width: 100%;">
          <div v-if="activeName == '视频回放' && iframeShow" style="width: 100%;">
            <iframe style="width: 100%;height: 75vh" scrolling="no" ref="iframe"
              :src="`${rtsp_ifarme_url}/in/demo_for_iframe.html?videoType=back`"
              frameborder="0"></iframe>
              <!-- cameraCode=${cameraCode}& -->
          </div>
        </el-col>
      </el-row>
    </div>
  </div>
</template>

<script>
import { deptRoleList } from "@/api/admin/role";
import { fetchTree } from "@/api/admin/dept";
import { tableOption } from "@/const/crud/admin/user";
import { mapGetters } from "vuex";

import Preview from "@/views/video/rtsp_video_c/Preview.vue";
import request from "@/router/axios";
import PreviewS from "@/views/video/rtsp_video_c/PreviewS.vue";

export default {
  name: "Video",
  components: {
    PreviewS,
    Preview
  },
  //父组件传过来的
  props: {
    activeName: {
      type: String,
      default: ''
    },
  },
  data() {
    return {
      defaultProps: {
        children: "children",
        label: "name"
      },
      defaultExpandedKeys:[],
      sliderShow: true,
      sliderValue: 0,
      totalPage: 0,
      queryParams: {},
      tableData: [], //列表
      playUrl: '', //播放的url
      endUrl: '',//关闭时传的url
      treeOption: {
        nodeKey: "id",
        addBtn: false,
        defaultExpandAll: true,
        menu: false,
        props: {
          label: "name",
          value: "id"
        }
      },
      treeData: [],
      rtsp: '',
      node: undefined,
      row: undefined,
      playbackTime: [],
      sliderUrl: '',
      cameraCode: '',
      iframeShow: true,
      startTime: '',
      endTime: '',
      startDate:'',
      endDate:'',
      rtsp_ifarme_url:process.env.rtsp_ifarme_url
    };
  },
  computed: {
    sliderMax() {
      let start = new Date(this.startTime).getTime();
      let end = new Date(this.endTime).getTime();
      console.log(start)
      console.log(end)

      let time = (end - start) / 1000

      this.sliderValue = time;

      return time;
    }
  },
  created() {
    this.init();
  },
  mounted() {
    this.defaultExpandedKeys = '3703';
  },
  methods: {
    //选择时间弹框弹出
    dateFocus() {
      this.iframeShow = false
    },
    //选择时间弹框隐藏,视频显示
    dateBlur() {
      this.iframeShow = true
    },
    convertDateStringToTimestamp(dateString) {
      var date = new Date(dateString);
      let ts = Math.round(new Date(dateString).getTime()/1000).toString();
      console.log("时间:",ts)
      // 返回时间戳
      return ts;
    },
    jie() {
      request({
        url: '/ewaycloud/video/record',
        method: 'get',
        params: {
          caseId: this.row.cameraId,
          streamUrl: this.endUrl,
          durationSceond: this.sliderValue
        }
      }).then(res => {
        if (res.code == 0) {
          this.$message.success('截取成功')
          this.sliderShow = false;
          this.sliderUrl = res.data.data
          this.sliderUrlPlay()
        }
      })
    },
    formatTooltip(val) {

      return val + "s";

      val = '' + val
      if (val.indexOf(".") != -1) {
        val = val.split(".")
        return val[0] + ':' + (val[1] < 10 ? '0' + val[1] : val[1])
      } else {
        return val + ":00";
      }
    },
    closePreview() {
      if (this.rtsp) {
        request({
          url: process.env.STREAM_CLOSE_URL,
          method: 'post',
          data: {
            urls: [this.rtsp]
          }
        }).then(res => {
          this.talkerJob = res.data.data.records.map(item => {
            return {
              label: item.jobName,
              value: item.id
            }
          })
        })
      }
    },
    sliderUrlPlay() {
      this.sliderUrl = '/猫狗大战03.mp4';
      this.$nextTick(() => {
        this.$refs.preview.handlePlay(this.sliderUrl, 0, {});
      })
    },
    dblclick(node, row) {

      if (this.startTime.length == 0||this.endTime.length == 0) {
        this.$message.warning('请选择时间')
        return
      }

      this.node = node
      this.row = row

      if (row.children == 0) {
        console.log("根节点")
        console.log('node', node)
        console.log('row', row)
        console.log(this.playbackTime, 'paytime');
        let parentData = node.parent.data.children
        this.cameraCode = row.cameraCode
        this.startDate = this.convertDateStringToTimestamp(this.startTime)
        this.endDate = this.convertDateStringToTimestamp(this.endTime)
        console.log(this.startTime, 'startTime', this.endTime, 'endtime',this.cameraCode,'cameracode');
        this.sendMesFroIframe(this.cameraCode);
        // this.cameraCode = row.cameraCode
        // request({
        //   url: '/ewaycloud/channel/hik/playbackURLs',
        //   method: 'get',
        //   params: {
        //     cameraIndexCode: this.cameraCode,
        //     beginTime: this.playbackTime[0],
        //     endTime: this.playbackTime[1],
        //   },
        // }).then(res => {
        /* this.playUrl = process.env.STREAM_URL + '/live?url=' + res.data.data.url + '&&&ffmpeg=true';
        //this.playUrl = process.env.STREAM_URL + '/live?url=' + 'rtsp://rtspstream:e06d5bdf15e5f992e39ebb33506c805e@zephyr.rtsp.stream/movie';
        // this.playUrl = '/猫狗大战03.mp4';
        this.$nextTick(() => {
          this.$refs.previews.handlePlay(this.playUrl, 0, row);
        })
        this.endUrl = res.data.data.url + '&&&ffmpeg=true';
        //this.endUrl = 'rtsp://rtspstream:e06d5bdf15e5f992e39ebb33506c805e@zephyr.rtsp.stream/movie'
        this.sliderShow = true */
        // })
      }
    },
    sendMesFroIframe(cameraCode) {
      // 向iframe传值
      const mapFrame = this.$refs['iframe'];
      const iframeWin = mapFrame.contentWindow;
      iframeWin.postMessage(
        {
          action:'cameraCode',
          cameraCode: cameraCode,
          startTime:this.startDate,
          endTime:this.endDate
        },
        '*'
      );
    },
    // 每页数
    sizeChangeHandle(val) {
      this.queryParams.pageSize = val
      this.queryParams.pageIndex = 1
      this.getList()
    },
    // 当前页
    currentChangeHandle(val) {
      this.pageIndex = val
      this.getList()
    },
    init() {
      request({
        url: '/ewaycloud/camera/findCameraDeptTreeList',
        method: 'get',
      }).then(res => {
        this.treeData = res.data.data;
        this.$set(this.treeData[0].children[0].children[0],'status','0')
        this.$set(this.treeData[0].children[0].children[1],'status','1')
        this.$set(this.treeData[0].children[0].children[2],'status','1')
        console.log(this.treeData[0].children[0].children[2]);
        console.log(this.treeData[0].children[0].children[0],'this.treeData[0].children[3].children[0]');
        this.$set(this.treeData[0].children[0].children[3].children[0],'status','0')
      })
    },
    getList() {
      request({
        url: '/ewaycloud/camera/findCamerList',
        method: 'get',
        params: this.queryParams
      }).then(res => {
        this.tableData = res.data.data.records;
        this.totalPage = res.data.data.total;
      })
    },
    nodeClick(data) {
      this.queryParams.deptId = data.id;
      this.getList();
    },
  }
};
</script>
<style lang="scss" scoped>
.user {
  height: 100%;

  &__tree {
    padding-top: 3px;
    padding-right: 20px;
  }

  &__main {
    .el-card__body {
      padding-top: 0;
    }
  }
}

.hover-cursor {
  cursor: pointer;
  /* 鼠标移入单元格时显示小手形状 */
}
.tree_icon_red{
  background-color: red;
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  margin-right: 5px;
}
.tree_icon_green{
  background-color: green;
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  margin-right: 5px;
}
.divSelect:hover {
  cursor: pointer;
}
::v-deep .avue-tree__filter{
  position: sticky;
  top: -0.15rem;
  left: 2rem;
  background-color: #fff;
  z-index: 99;
  width: 100%;
}
::v-deep .el-scrollbar{
  min-height: 200px;
}
::v-deep .el-tree-node__expand-icon{
  font-size: 18px !important;
}
.last-level-node {
      font-size: 16px !important;
    }
.disable-selection {
  user-select: none; /* 标准语法 */
  -webkit-user-select: none; /* Chrome, Safari, Opera */
  -moz-user-select: none; /* Firefox */
  -ms-user-select: none; /* IE10+/Edge */
}
</style>

Preview.vue

<template>
  <div>
    <div
        style="display: flex;justify-content: center;align-items: center;bottom: 0px;top: 215px;width: 100%;height: 600px;">
      <!-- 1*1 -->
      <div
          style="width: 100%;height: 100%;margin-left: 20px;border: 2px solid #779BEF;margin-top: 10px"
          v-if="selected == 0">
        <el-row type="flex" style="min-height: 100%;" justify="center">
          <el-col :span="24">
            <div @click="clickVideo(0)" :class="videoIndex == 0 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video0"></cus-player>
            </div>
          </el-col>
        </el-row>
      </div>
      <!-- 2*2 -->
      <div
          style="width: 100%;height: 100%;margin-left: 20px;border-top: 2px solid #779BEF;border-left: 2px solid #779BEF"
          v-else-if="selected == 1">
        <el-row :gutter="1" type="flex" style="height: 50%;" justify="center">
          <el-col :span="12">
            <div @click="clickVideo(0)" :class="videoIndex == 0 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video0"></cus-player>
            </div>
          </el-col>
          <el-col :span="12">
            <div @click="clickVideo(1)" :class="videoIndex == 1 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video1"></cus-player>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="1" type="flex" style="height: 50%;" class="row-bg" justify="center">
          <el-col :span="12">
            <div @click="clickVideo(2)" :class="videoIndex == 2 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video2"></cus-player>
            </div>
          </el-col>
          <el-col :span="12">
            <div @click="clickVideo(3)" :class="videoIndex == 3 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video3"></cus-player>
            </div>
          </el-col>
        </el-row>
      </div>
      <!-- 3*3 -->
      <div style="height:100%;width:100%;margin-left: 20px;border-top: 2px solid #779BEF;border-left: 2px solid #779BEF"
           v-else-if="selected == 2">
        <el-row :gutter="1" type="flex" style="height: 33.333%;" justify="center">
          <el-col :span="8">
            <div @click="clickVideo(0)" :class="videoIndex == 0 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video0"></cus-player>
            </div>
          </el-col>
          <el-col :span="8">
            <div @click="clickVideo(1)" :class="videoIndex == 1 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video1"></cus-player>
            </div>
          </el-col>
          <el-col :span="8">
            <div @click="clickVideo(2)" :class="videoIndex == 2 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video2"></cus-player>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="1" type="flex" style="height: 33.333%;" justify="center">
          <el-col :span="8">
            <div @click="clickVideo(3)" :class="videoIndex == 3 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video3"></cus-player>
            </div>
          </el-col>
          <el-col :span="8">
            <div @click="clickVideo(4)" :class="videoIndex == 4 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video4"></cus-player>
            </div>
          </el-col>
          <el-col :span="8">
            <div @click="clickVideo(5)" :class="videoIndex == 5 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video5"></cus-player>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="1" type="flex" style="height: 33.333%;" class="row-bg" justify="center">
          <el-col :span="8">
            <div @click="clickVideo(6)" :class="videoIndex == 6 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video6"></cus-player>
            </div>
          </el-col>
          <el-col :span="8">
            <div @click="clickVideo(7)" :class="videoIndex == 7 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video7"></cus-player>
            </div>
          </el-col>
          <el-col :span="8">
            <div @click="clickVideo(8)" :class="videoIndex == 8 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video8"></cus-player>
            </div>
          </el-col>
        </el-row>
      </div>
      <!-- 4*4 -->
      <div style="height:100%;width:100%;margin-left: 20px;border-top: 2px solid #779BEF;border-left: 2px solid #779BEF"
           v-else-if="selected == 3">
        <el-row :gutter="1" type="flex" style="height: 25%;" justify="center">
          <el-col :span="6">
            <div @click="clickVideo(0)" :class="videoIndex == 0 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video0"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(1)" :class="videoIndex == 1 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video1"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(2)" :class="videoIndex == 2 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video2"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(3)" :class="videoIndex == 3 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video3"></cus-player>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="1" type="flex" style="height: 25%;" justify="center">
          <el-col :span="6">
            <div @click="clickVideo(4)" :class="videoIndex == 4 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video4"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(5)" :class="videoIndex == 5 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video5"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(6)" :class="videoIndex == 6 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video6"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(7)" :class="videoIndex == 7 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video7"></cus-player>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="1" type="flex" style="height: 25%;" justify="center">
          <el-col :span="6">
            <div @click="clickVideo(8)" :class="videoIndex == 8 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video8"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(9)" :class="videoIndex == 9 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video9"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(10)" :class="videoIndex == 10 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video10"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(11)" :class="videoIndex == 11 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video11"></cus-player>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="1" type="flex" style="height: 25%;" justify="center">
          <el-col :span="6">
            <div @click="clickVideo(12)" :class="videoIndex == 12 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video12"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(13)" :class="videoIndex == 13 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video13"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(14)" :class="videoIndex == 14 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video14"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(15)" :class="videoIndex == 15 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video15"></cus-player>
            </div>
          </el-col>
        </el-row>
      </div>
    </div>
  </div>
</template>

<script>
import 'xgplayer';
import FlvJsPlayer from 'xgplayer-flv.js';
import CusPlayer from './CusPlayer';
import request from "@/router/axios";

export default {
  components: {
    CusPlayer
  },
  props: {
    playUrl: String,
    endUrl: String,
  },
  data() {
    return {
      selected: 0, //表格类型
      videoIndex: 0, //表格索引
      tableData: [], //列表
      selectValue: '', //当前选择的相机
    };
  },
  created() {
    this.getList();
  },
  methods: {
    selectType(selected) {
      this.selected = selected;
    },
    clickVideo(index) {
      this.videoIndex = index;
    },
    getList() {
    },
    handlePlay(url, videoIndex, row) {
      this.videoIndex = videoIndex
      if (url) {
        let ref = 'video' + this.videoIndex;

        let dynamicDiv = this.$refs[ref];
        dynamicDiv.createPlayer(url, true, this.videoIndex);
      } else {
        this.$message.error('请填写播放地址');
      }
    },

    clickPlayer(index) {
      this.videoIndex = index;
    }
  }
};
</script>

<style scoped>

.video {
  border: 2px solid #779BEF;
  height: 100%;
  width: 100%;
  position: relative;
}


.el-col {
  margin-bottom: 0px !important;
  border: 0px !important;
}
</style>

PreviewS.vue

<template>
  <div>
    <div
        style="display: flex;justify-content: center;align-items: center;bottom: 0px;top: 215px;width: 100%;height: 600px;">
      <!-- 1*1 -->
      <div
          style="width: 100%;height: 100%;margin-left: 20px;border: 2px solid #779BEF;margin-top: 10px"
          v-if="selected == 0">
        <el-row type="flex" style="min-height: 100%;" justify="center">
          <el-col :span="24">
            <div @click="clickVideo(0)" :class="videoIndex == 0 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video0"></cus-player>
            </div>
          </el-col>
        </el-row>
      </div>
      <!-- 2*2 -->
      <div
          style="width: 100%;height: 100%;margin-left: 20px;border-top: 2px solid #779BEF;border-left: 2px solid #779BEF"
          v-else-if="selected == 1">
        <el-row :gutter="1" type="flex" style="height: 50%;" justify="center">
          <el-col :span="12">
            <div @click="clickVideo(0)" :class="videoIndex == 0 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video0"></cus-player>
            </div>
          </el-col>
          <el-col :span="12">
            <div @click="clickVideo(1)" :class="videoIndex == 1 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video1"></cus-player>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="1" type="flex" style="height: 50%;" class="row-bg" justify="center">
          <el-col :span="12">
            <div @click="clickVideo(2)" :class="videoIndex == 2 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video2"></cus-player>
            </div>
          </el-col>
          <el-col :span="12">
            <div @click="clickVideo(3)" :class="videoIndex == 3 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video3"></cus-player>
            </div>
          </el-col>
        </el-row>
      </div>
      <!-- 3*3 -->
      <div style="height:100%;width:100%;margin-left: 20px;border-top: 2px solid #779BEF;border-left: 2px solid #779BEF"
           v-else-if="selected == 2">
        <el-row :gutter="1" type="flex" style="height: 33.333%;" justify="center">
          <el-col :span="8">
            <div @click="clickVideo(0)" :class="videoIndex == 0 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video0"></cus-player>
            </div>
          </el-col>
          <el-col :span="8">
            <div @click="clickVideo(1)" :class="videoIndex == 1 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video1"></cus-player>
            </div>
          </el-col>
          <el-col :span="8">
            <div @click="clickVideo(2)" :class="videoIndex == 2 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video2"></cus-player>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="1" type="flex" style="height: 33.333%;" justify="center">
          <el-col :span="8">
            <div @click="clickVideo(3)" :class="videoIndex == 3 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video3"></cus-player>
            </div>
          </el-col>
          <el-col :span="8">
            <div @click="clickVideo(4)" :class="videoIndex == 4 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video4"></cus-player>
            </div>
          </el-col>
          <el-col :span="8">
            <div @click="clickVideo(5)" :class="videoIndex == 5 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video5"></cus-player>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="1" type="flex" style="height: 33.333%;" class="row-bg" justify="center">
          <el-col :span="8">
            <div @click="clickVideo(6)" :class="videoIndex == 6 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video6"></cus-player>
            </div>
          </el-col>
          <el-col :span="8">
            <div @click="clickVideo(7)" :class="videoIndex == 7 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video7"></cus-player>
            </div>
          </el-col>
          <el-col :span="8">
            <div @click="clickVideo(8)" :class="videoIndex == 8 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video8"></cus-player>
            </div>
          </el-col>
        </el-row>
      </div>
      <!-- 4*4 -->
      <div style="height:100%;width:100%;margin-left: 20px;border-top: 2px solid #779BEF;border-left: 2px solid #779BEF"
           v-else-if="selected == 3">
        <el-row :gutter="1" type="flex" style="height: 25%;" justify="center">
          <el-col :span="6">
            <div @click="clickVideo(0)" :class="videoIndex == 0 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video0"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(1)" :class="videoIndex == 1 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video1"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(2)" :class="videoIndex == 2 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video2"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(3)" :class="videoIndex == 3 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video3"></cus-player>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="1" type="flex" style="height: 25%;" justify="center">
          <el-col :span="6">
            <div @click="clickVideo(4)" :class="videoIndex == 4 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video4"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(5)" :class="videoIndex == 5 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video5"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(6)" :class="videoIndex == 6 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video6"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(7)" :class="videoIndex == 7 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video7"></cus-player>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="1" type="flex" style="height: 25%;" justify="center">
          <el-col :span="6">
            <div @click="clickVideo(8)" :class="videoIndex == 8 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video8"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(9)" :class="videoIndex == 9 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video9"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(10)" :class="videoIndex == 10 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video10"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(11)" :class="videoIndex == 11 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video11"></cus-player>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="1" type="flex" style="height: 25%;" justify="center">
          <el-col :span="6">
            <div @click="clickVideo(12)" :class="videoIndex == 12 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video12"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(13)" :class="videoIndex == 13 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video13"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(14)" :class="videoIndex == 14 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video14"></cus-player>
            </div>
          </el-col>
          <el-col :span="6">
            <div @click="clickVideo(15)" :class="videoIndex == 15 ? 'selectVideo' : 'video'">
              <cus-player @clickPlayer="clickPlayer" ref="video15"></cus-player>
            </div>
          </el-col>
        </el-row>
      </div>
    </div>
  </div>
</template>

<script>
import 'xgplayer';
import FlvJsPlayer from 'xgplayer-flv.js';
import CusPlayer from './CusPlayerS';
import request from "@/router/axios";

export default {
  components: {
    CusPlayer
  },
  props: {
    playUrl: String,
    endUrl: String,
  },
  data() {
    return {
      selected: 0, //表格类型
      videoIndex: 0, //表格索引
      tableData: [], //列表
      selectValue: '', //当前选择的相机
    };
  },
  created() {
    this.getList();
  },
  methods: {
    selectType(selected) {
      this.selected = selected;
    },
    clickVideo(index) {
      this.videoIndex = index;
    },
    getList() {
    },
    handlePlay(url, videoIndex, row) {
      this.videoIndex = videoIndex
      if (url) {
        let ref = 'video' + this.videoIndex;

        let dynamicDiv = this.$refs[ref];
        dynamicDiv.createPlayer(url, true, this.videoIndex);
      } else {
        this.$message.error('请填写播放地址');
      }
    },

    clickPlayer(index) {
      this.videoIndex = index;
    }
  }
};
</script>

<style scoped>

.video {
  border: 2px solid #779BEF;
  height: 100%;
  width: 100%;
  position: relative;
}


.el-col {
  margin-bottom: 0px !important;
  border: 0px !important;
}
</style>

CusPlayerS.vue

<template>
  <div
      style="position: absolute;height: 100%;width: 100%;display: flex;align-items: center;justify-content: center;flex-direction: column">
    <div class="video" v-show="isPlay" :id="elId"></div>
    <div v-show="!isPlay" style="color: #888888;font-size: 25px;">暂无视频源</div>
  </div>
</template>

<script>
import FlvJsPlayer from 'xgplayer-flv.js';
import Player from 'xgplayer';
import {v4} from 'uuid';
import request from "@/router/axios";

export default {
  name: 'CusPlayer',
  components: {},
  data() {
    return {
      title: '',
      isPlay: false,
      player: null,
      elId: '',
      playUrl: String
    };
  },
  created() {
    this.elId = v4(); // 避免key重复
  },
  mounted() {
    var that = this;
    document.addEventListener('visibilitychange', function () {
      // console.log(document.visibilityState);
      // console.log(document.hidden);
      if (document.hidden) {
        console.log("页面隐藏")
      }
    });
  },
  methods: {
    createPlayer(url, hasCloseBtn, index) {
      if (!url) {
        return;
      }
      this.playUrl = url.slice(33);
      if (this.player) {
        this.changeVideo(url);
        return;
      }

      this.isPlay = true;
      this.player = new FlvJsPlayer({
        id: this.elId,
        url: url,
        // fitVideoSize: 'auto',
        fluid: true,
        autoplay: true,
        isLive: true,
        playsinline: false,
        screenShot: true,
        whitelist: [''],
        ignores: ['time'],
        closeVideoClick: true,
        // errorTips: '<span class="app-error">无视频源</span>',
        customConfig: {
          isClickPlayBack: false
        },
        flvOptionalConfig: {
          enableWorker: true,
          enableStashBuffer: true, //启用缓存
          stashInitialSize: 4096, //缓存大小4m
          lazyLoad: false,
          lazyLoadMaxDuration: 40 * 60,
          autoCleanupSourceBuffer: true,
          autoCleanupMaxBackwardDuration: 35 * 60,
          autoCleanupMinBackwardDuration: 30 * 60
        } //flv.js可选配置项 [flv.js配置](https://github.com/bilibili/flv.js/blob/master/docs/api.md#config)
      });

      // 自定义播放器按钮
      // let divStr =
      //   '<i class="btn-hover el-icon-camera button-screen-shot" style="font-size: 23px;margin-right: 10px;pointer-events: auto;"></i>' +
      //   '<i class="btn-hover el-icon-s-tools button-set" style="font-size: 23px;margin-right: 10px;pointer-events: auto;"></i>' +
      //   '<i class="btn-hover el-icon-video-camera-solid button-history" style="font-size: 23px;margin-right: 10px;pointer-events: auto;"></i>';

      let divStr = '<i class="btn-hover el-icon-d-arrow-left button-playback" style="font-size: 23px;pointer-events: auto;"></i>';

      let divClose = '<i @click="closePlayer" class="btn-hover el-icon-close app-close-btn-c"></i>';

      let util = Player.util;
      // let customBtn = util.createDom('div', divStr, {}, 'flex align-center justify-center app-player-button'); //'div', divStr, {}, 'class'
      let customBtn = util.createDom(
          'div',
          divStr,
          {style: 'width: 40px;heigth:40px;position: absolute;right: 155px;top: 7px;'},
          'flex align-center justify-center app-player-button'
      ); //'div', divStr, {}, 'class'
      let closeBtn = util.createDom('div', divClose, {}, 'app-close-btn');
      let xgControls = this.player.root.querySelector('xg-controls');
      let xgError = this.player.root.querySelector('xg-error');
      xgControls.appendChild(customBtn);
      this.player.root.appendChild(closeBtn);

      // let shot = customBtn.querySelector('.button-screen-shot');
      // let set = customBtn.querySelector('.button-set');
      // let history = customBtn.querySelector('.button-history');
      let closeBtnc = closeBtn.querySelector('.app-close-btn-c');
      let playback = customBtn.querySelector('.button-playback');

      this.player.on('play', () => {
      });
      this.player.on('focus', () => {
        if (hasCloseBtn) {
          closeBtn.style.display = 'block';
        }
      });
      this.player.on('ended', () => {
      });
      this.player.on('blur', () => {
        closeBtn.style.display = 'none';
      });

      this.player.on('error', () => {
      });

      if (closeBtnc) {
        closeBtnc.addEventListener('click', () => {
          this.closePlayer();
        });
      }

      // 点击视频时间,设置selectIndex
      this.player.video.addEventListener('click', () => {
        // this.selectIndex = index;
        // this.$parent.setSelectIndex(index);
        this.$emit('clickPlayer', index);
      });

      // if (shot) {
      //   shot.addEventListener('click', () => {
      //     this.screenShot(index);
      //   });
      // }
      // if (set) {
      //   set.addEventListener('click', () => {
      //     this.setPlay(index);
      //   });
      // }
      // if (history) {
      //   history.addEventListener('click', () => {
      //     this.playHistory(index);
      //   });
      // }
    },

    changeVideo(url) {
      this.player.src = url;
      this.playUrl = url.slice(33);
    },

    closePlayer() {
      this.isPlay = false;
      if (this.player) {
        this.player.destroy();
        //关闭视频流
        request({
          url: '/ewaycloud/channel/hik/clearPlayUrls',
          method: 'get',
          params: {
            url: this.playUrl
          },
        }).then(res => {
          console.log(res.data.data.records);
        })
      }
    }
  },

  beforeDestroy() {
    if (this.player) {
      this.player.destroy();
    }
    // clearInterval(this.intvBuffer);
    console.log('销毁了');
  }

  // destroyed() {
  //   if(this.player){
  //     this.player.destroy();
  //   }
  // }
};
</script>
<style>
.btn-hover:hover {
  color: #888888;
  cursor: pointer;
}

.btn-hover {
  color: #ffffff;
}

.video {
  position: relative !important;
  height: 100% !important;
  width: 100% !important;
  padding-top: 0px !important;
}

.app-close-btn {
  position: absolute;
  top: 0px;
  right: 5px;
  z-index: 500;
  display: none;
}

.app-close-btn-c {
  color: #aaffff;
  font-size: 25px;
  pointer-events: auto;
  z-index: 500;
}
</style>

video_demo.vue

<template>
  <div class="videoMain" ref="playWndBox">
    <div id="playWnd" class="playWnd" :style="{
      height: playWndHeight + 'px',
      width: playWndWidth + 'px',
    }"></div>
  </div>
</template>

<script >
export default {
  name: "HikVideo",
  data() {
    return {
      // 视频盒子的高度
      playWndHeight: "",
      // 视频盒子的宽度
      playWndWidth: "",
      oWebControl: null,
      initCount: 0,
      pubKey: "",
      cameraIndexCode: "25603528001310681100",  // 这里面是监控点编号
      objData: {
        //海康初始化数据
        appkey: "22547186",
        ip: "159.61.64.249",
        secret: "sPteT21oUI2CCav79heE",
        port: 443,
        playMode: 0, // 0 预览 1回放
        layout: "1x1", //页面展示的模块数【16】
      },
    }
  },
  mounted() {
    // 首次加载时的到父容器的高度
    this.playWndHeight = this.$refs.playWndBox.clientHeight;
    // 首次加载时的到父容器的宽度
    this.playWndWidth = this.$refs.playWndBox.clientWidth;

    // 初始化播放器插件
    this.$nextTick(() => {
      this.initPlugin();
    });

    // 监听scroll事件,使插件窗口尺寸跟随DIV窗口变化
    window.addEventListener("scroll", () => {
      if (this.oWebControl != null) {
        this.oWebControl.JS_Resize(
          this.$refs.playWndBox.clientWidth,
          this.$refs.playWndBox.clientHeight
        );
        this.setWndCover();
      }
    });

    // 监听resize事件,使插件窗口尺寸跟随DIV窗口变化
    window.addEventListener("resize", (e) => {
      if (this.oWebControl != null) {
        this.oWebControl.JS_Resize(
          this.$refs.playWndBox.clientWidth,
          this.$refs.playWndBox.clientHeight
        );
        this.setWndCover();
      }
    });

  },
  destroyed() {
    if (this.oWebControl != null) {
      // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题
      this.oWebControl.JS_HideWnd();
      // 销毁当前播放的视频
      this.oWebControl.JS_RequestInterface({ funcName: "destroyWnd" });
      // 断开与插件服务连接
      this.oWebControl.JS_Disconnect();
    }
  },
  methods: {
    // 创建播放实例
    initPlugin() {
      let that = this;
      this.oWebControl = null;
      that.oWebControl = new WebControl({
        szPluginContainer: "playWnd", // 指定容器id
        iServicePortStart: 15900, // 指定起止端口号,建议使用该值
        iServicePortEnd: 15909,
        szClassId: "23BF3B0A-2C56-4D97-9C03-0CB103AA8F11", // 用于IE10使用ActiveX的clsid
        cbConnectSuccess: () => {
          // 创建WebControl实例成功
          that.oWebControl
            .JS_StartService("window", {
              // WebControl实例创建成功后需要启动服务
              // 值"./VideoPluginConnect.dll"写死
              dllPath: "./VideoPluginConnect.dll",
            })
            .then(
              function () {
                // 设置消息回调
                that.oWebControl.JS_SetWindowControlCallback({
                  cbIntegrationCallBack: that.cbIntegrationCallBack,
                });
                //JS_CreateWnd创建视频播放窗口,宽高可设定
                that.oWebControl
                  .JS_CreateWnd("playWnd", 2040, 945, { bEmbed: true })
                  //注:2040,945这是我本人项目视频盒子的大小,你们要根据自己视频盒子的大小进行修改,不然初始化插件的时候会有空白闪烁。
                  .then(function () {
                    // 创建播放实例成功后初始化
                    that.init();
                  });
              },
              function () {
                // 启动插件服务失败
              }
            );
        },
        // 创建WebControl实例失败
        cbConnectError: function () {
          that.oWebControl = null;
          // alert('插件未启动,正在尝试启动,请稍候...')
          // that.$message.warning("插件未启动,正在尝试启动,请稍候...");
          // 程序未启动时执行error函数,采用wakeup来启动程序
          window.WebControl.JS_WakeUp("VideoWebPlugin://");
          that.initCount++;
          if (that.initCount < 3) {
            setTimeout(function () {
              that.initPlugin();
            }, 3000);
          } else {


          }
        },
        cbConnectClose: () => {
          // 异常断开:bNormalClose = false
          // JS_Disconnect正常断开:bNormalClose = true
          // console.log("cbConnectClose");
          that.oWebControl = null;
        },
      });
    },
    // 初始化
    init(callback) {
      let that = this;
      that.getPubKey(() => {
        let appkey = that.objData.appkey;                   //综合安防管理平台提供的appkey,必填
        let secret = that.setEncrypt(that.objData.secret); //综合安防管理平台提供的secret,必填
        let ip = that.objData.ip;                           //综合安防管理平台IP地址,必填
        let playMode = that.objData.playMode;               //初始播放模式:0-预览,1-回放
        let port = that.objData.port;                       //综合安防管理平台端口,若启用HTTPS协议,默认443
        let snapDir = "D:\\SnapDir";                        //抓图存储路径
        let videoDir = "D:\\VideoDir";                       //紧急录像或录像剪辑存储路径
        let layout = that.objData.layout;                   //playMode指定模式的布局
        let enableHTTPS = 1;                                //是否启用HTTPS协议与综合安防管理平台交互,这里总是填1
        let encryptedFields = "secret";                     //加密字段,默认加密领域为secret
        let showToolbar = 1;                                //是否显示工具栏,0-不显示,非0-显示
        let showSmart = 1;                                  //是否显示智能信息(如配置移动侦测后画面上的线框),0-不显示,非0-显示
        let buttonIDs = "0,16,256,257,258,259,260,512,513,514,515,516,517,768,769"; //自定义工具条按钮
        // var toolBarButtonIDs = "2049,2304" // 工具栏上自定义按钮
        that.oWebControl
          .JS_RequestInterface({
            funcName: "init",
            argument: JSON.stringify({
              appkey: appkey,                 //API网关提供的appkey
              secret: secret,                 //API网关提供的secret
              ip: ip,                         //API网关IP地址
              playMode: playMode,             //播放模式(决定显示预览还是回放界面)
              port: port,                     //端口
              snapDir: snapDir,               //抓图存储路径
              videoDir: videoDir,             //紧急录像或录像剪辑存储路径
              layout: layout,                 //布局
              enableHTTPS: enableHTTPS,       //是否启用HTTPS协议
              encryptedFields: encryptedFields, //加密字段
              showToolbar: showToolbar,       //是否显示工具栏
              showSmart: showSmart,           //是否显示智能信息
              buttonIDs,                      //自定义工具条按钮
            }),
          })
          .then(function (oData) {
            that.oWebControl.JS_Resize(that.playWndWidth, that.playWndHeight); // 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题
            if (callback) {
              callback();
            }
            // 隐藏
            // that.oWebControl.JS_HideWnd()
          });
      });
    },
    // 获取公钥
    getPubKey(callback) {
      let that = this;
      this.oWebControl
        .JS_RequestInterface({
          funcName: "getRSAPubKey",
          argument: JSON.stringify({
            keyLength: 1024,
          }),
        })
        .then(function (oData) {
          if (oData.responseMsg.data) {
            that.pubKey = oData.responseMsg.data;
            callback();
          }
        });
    },
    // RSA 加密
    setEncrypt(value) {
      let that = this;
      let encrypt = new window.JSEncrypt();
      encrypt.setPublicKey(that.pubKey);
      return encrypt.encrypt(value);
    },
    // 回调的消息
    cbIntegrationCallBack(oData) {
      let { responseMsg: type } = oData;
      if (type === "error") {
      } else {
      }
    },
    // 视频预览功能
    previewVideo(data) {
      let that = this;
      let cameraIndexCode = data;         // 获取输入的监控点编号值,必填
      let streamMode = 0;              // 主子码流标识:0-主码流,1-子码流
      let transMode = 0;              // 传输协议:0-UDP,1-TCP
      let gpuMode = 0;                // 是否启用GPU硬解,0-不启用,1-启用
      let wndId = -1;                 // 播放窗口序号(在2x2以上布局下可指定播放窗口)
      // console.log(cameraIndexCode, "-------cameraIndexCode-");

      that.oWebControl.JS_RequestInterface({
        funcName: "startPreview",
        argument: JSON.stringify({
          cameraIndexCode: cameraIndexCode.trim(), // 监控点编号
          streamMode: streamMode,                 // 主子码流标识
          transMode: transMode,                   // 传输协议
          gpuMode: gpuMode,                       // 是否开启GPU硬解
          wndId: wndId,                           // 可指定播放窗口
        }),
      });
    },
    // 停止全部预览
    stopAllPreview() {
      this.oWebControl.JS_RequestInterface({
        funcName: "stopAllPreview",
      });
    },
    // 格式化时间
    dateFormat(oDate, fmt) {
      let o = {
        "M+": oDate.getMonth() + 1, //月份
        "d+": oDate.getDate(), //日
        "h+": oDate.getHours(), //小时
        "m+": oDate.getMinutes(), //分
        "s+": oDate.getSeconds(), //秒
        "q+": Math.floor((oDate.getMonth() + 3) / 3), //季度
        S: oDate.getMilliseconds(), //毫秒
      };
      if (/(y+)/.test(fmt)) {
        fmt = fmt.replace(
          RegExp.$1,
          (oDate.getFullYear() + "").substr(4 - RegExp.$1.length)
        );
      }
      for (let k in o) {
        if (new RegExp("(" + k + ")").test(fmt)) {
          fmt = fmt.replace(
            RegExp.$1,
            RegExp.$1.length == 1
              ? o[k]
              : ("00" + o[k]).substr(("" + o[k]).length)
          );
        }
      }
      return fmt;
    },

    // 设置窗口裁剪,当因滚动条滚动导致窗口需要被遮住的情况下需要JS_CuttingPartWindow部分窗口
    setWndCover() {
      var iWidth = $(window).width();
      var iHeight = $(window).height();
      var oDivRect = $("#playWnd").get(0).getBoundingClientRect();

      var iCoverLeft = (oDivRect.left < 0) ? Math.abs(oDivRect.left) : 0;
      var iCoverTop = (oDivRect.top < 0) ? Math.abs(oDivRect.top) : 0;
      var iCoverRight = (oDivRect.right - iWidth > 0) ? Math.round(oDivRect.right - iWidth) : 0;
      var iCoverBottom = (oDivRect.bottom - iHeight > 0) ? Math.round(oDivRect.bottom - iHeight) : 0;

      iCoverLeft = (iCoverLeft > 2041) ? 2041 : iCoverLeft;
      iCoverTop = (iCoverTop > 945) ? 945 : iCoverTop;
      iCoverRight = (iCoverRight > 2041) ? 2041 : iCoverRight;
      iCoverBottom = (iCoverBottom > 945) ? 945 : iCoverBottom;

      this.oWebControl.JS_RepairPartWindow(0, 0, 2041, 946);  // 多1个像素点防止还原后边界缺失一个像素条
      if (iCoverLeft != 0) {
        this.oWebControl.JS_CuttingPartWindow(0, 0, iCoverLeft, 946);
      }
      if (iCoverTop != 0) {
        this.oWebControl.JS_CuttingPartWindow(0, 0, 2041, iCoverTop);  // 多剪掉一个像素条,防止出现剪掉一部分窗口后出现一个像素条
      }
      if (iCoverRight != 0) {
        this.oWebControl.JS_CuttingPartWindow(2041 - iCoverRight, 0, iCoverRight, 946);
      }
      if (iCoverBottom != 0) {
        this.oWebControl.JS_CuttingPartWindow(0, 946 - iCoverBottom, 2041, iCoverBottom);
      }
    },
  }

}
</script>

<style lang="less" scoped>
.videoMain {
  width: 50vw;
  height: 50vh;
}
</style>

demo_for_iframe.html

<!doctype html>
<html>
<head>
    <title>Window Demo</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Cache-Control" content="no-cache, must-revalidate" />
    <meta http-equiv="Expires" content="0" />
</head>
<style>
html, body {
    padding: 0;
    margin: 0;
}
.iframe {
  height: 100vh
    /* margin: 100px; */
    /* border: 1px solid blue; */
}
</style>
<body>
	 <!-- 步骤1:src指定待嵌入的子页面,scrolling指定no禁用滚动条 -->
    <iframe id="iframeVideo" class="iframe" src="./demo_embedded_for_iframe.html" scrolling="no" frameborder="0" width="100%"></iframe>
</body>
<script src="jquery-1.12.4.min.js"></script>
<script src="jsencrypt.min.js"></script>
<script src="web-control_1.2.5.min.js"></script>
<!-- <script src="JsonToXml.js"></script>
<script src="jsWebControl-Bridge.js"></script> -->
<script type="text/javascript">
  console.log("进入了:::");
	// 步骤2:嵌入子页面的页面中在iframe的onload事件中向子页面抛以下消息
	var iframeWin = document.getElementById("iframeVideo");
	{
		iframeWin.onload = function(){
      const url = new URL(window.location.href);
      console.log(url,'这是url')
  // 使用URLSearchParams API获取查询参数
  const searchParams = new URLSearchParams(url.search);
  // 获取cameraCode参数
  const videoType = searchParams.get('videoType')
  /* const cameraCode = searchParams.get('cameraCode');
  if (cameraCode) {
    console.log('这里是成功获取 cameraCode:', cameraCode);
    // 在这里处理cameraCode,例如赋值给某个变量或调用相关函数
  } else {
    console.error('失败获取 cameraCode parameter found in the URL.');
  } */
		iframeWin.contentWindow.postMessage({
							  action:'sendTitle',   // 告诉子页面本页面的标题(action自行指定,但需要与子页面中监听的action保持一致
							  msg: '将标题发给子页面',
							  info: document.title,
                // cameraCode:cameraCode,
                videoType:videoType
							}, '\*');
		iframeWin.contentWindow.postMessage({
							  action:'updateInitParam',    // 告诉子页面一些初始值,包括浏览器视窗高度与宽度、iframe偏离文档的位置、iframe相对视窗的位置
							  msg: '更新子页面一些初始值',
							  showSize: {                       // 浏览器视窗高度与宽度
									width: $(window).width(),
									height: $(window).height()
								},
							  iframeOffset: {                   // iframe偏离文档的位置
									left: iframeWin.offsetLeft,
									top: iframeWin.offsetTop
								  },
							  iframeClientPos: {	            // iframe相对视窗的位置
									left: iframeWin.getBoundingClientRect().left,
									right: iframeWin.getBoundingClientRect().right,
									top: iframeWin.getBoundingClientRect().top,
									bottom: iframeWin.getBoundingClientRect().bottom
								}
							}, '\*');   // '\*'表示跨域参数,请结合自身业务合理设置
		}
	}

	// 步骤3:监听嵌入子页面的事件
	window.addEventListener('message', function(e){
		console.log("iframe监听:",e.data);
		if(e && e.data){
			switch (e.data.action){
				case 'updateTitle':        // 本页面收到子页面通知更新标题通知,更新本页面标题
					document.title = e.data.info;
					break;
				case 'updatePos':
					var scrollLeftValue = document.documentElement.scrollLeft;
					var scrollTopValue = document.documentElement.scrollTop;
					iframeWin.contentWindow.postMessage({
							  action:'updatePos',
							  msg: '更新Pos',
							  scrollValue: {          // 滚动条滚动的偏移量
									left: -1 * scrollLeftValue,
									top: -1 * scrollTopValue,
								  }
							}, '\*');   // '\*'表示跨域参数,请结合自身业务合理设置
					break;
          case 'cameraCode':
            let cameraCode = e.data.cameraCode;
            let startTime = e.data.startTime;
            let endTime = e.data.endTime;
            console.log("传递参数:",cameraCode)
            iframeWin.contentWindow.postMessage({
              action:'sendCameraCode',   // 告诉子页面本页面的标题(action自行指定,但需要与子页面中监听的action保持一致
              cameraCode:cameraCode,
              startTime:startTime,
              endTime:endTime
            }, '\*');

            break;
				default:
					break;
			}
		}
	});

	// 步骤4:兼听本页面的resize事件,并将一些状态值发送给嵌入的子页面
	var resizeTimer = null;
	var resizeDate;
	$(window).resize(function () {
		resizeDate = new Date();
		if (resizeTimer === null){
			resizeTimer = setTimeout(checkResizeEndTimer, 100);
		}
	});

	function checkResizeEndTimer(){
		if (new Date() - resizeDate > 100){  // resize结束后再发消息,避免残影问题
			clearTimeout(resizeTimer);
			resizeTimer = null;
			postResizeEvent();
		} else{
			setTimeout(checkResizeEndTimer, 100);
		}
	}

	function postResizeEvent(){
		iframeWin.contentWindow.postMessage({
					action: 'resize',
					msg: 'resize事件',
					showSize: {             // 告诉嵌入的子页面视窗高度与宽度
						width: $(window).width(),
						height: $(window).height()
					},
					iframeClientPos: {	    // iframe相对视窗的位置
						left: iframeWin.getBoundingClientRect().left,
						right: iframeWin.getBoundingClientRect().right,
						top: iframeWin.getBoundingClientRect().top,
						bottom: iframeWin.getBoundingClientRect().bottom
					},
					iframeOffset: {        // iframe偏离文档的位置
						left: iframeWin.offsetLeft,
						top: iframeWin.offsetTop
					  }
				}, '\*');     // '\*'表示跨域参数,请结合自身业务合理设置
	}

	// 步骤5:兼听本页面的scroll事件,并将一些状态值发送给嵌入的子页面
	// 为性能考虑,可以在定时器中处理
	var scrollTimer = null;
	var scrollDate;
	$(window).scroll(function (event) {
		postScrollEvent();
		scrollDate = new Date();
		if (scrollTimer === null){
			scrollTimer = setTimeout(checkScrollEndTimer, 100);
		}
    });

	function checkScrollEndTimer(){
		if (new Date() - scrollDate > 100){  // resize结束后再发消息,避免残影问题
			clearTimeout(scrollTimer);
			scrollTimer = null;
		} else{
			postScrollEvent();
			setTimeout(checkScrollEndTimer, 100);
		}
	}

	function postScrollEvent(){
		// 计算滚动条偏移量
		var scrollLeftValue = document.documentElement.scrollLeft;
		var scrollTopValue = document.documentElement.scrollTop;
		iframeWin.contentWindow.postMessage({
			  action:'scroll',
			  msg: 'scroll事件',
			  scrollValue: {          // 滚动条滚动的偏移量
					left: -1 * scrollLeftValue,
					top: -1 * scrollTopValue,
				  },
				iframeClientPos: {	  // iframe相对视窗的位置
					left: iframeWin.getBoundingClientRect().left,
					right: iframeWin.getBoundingClientRect().right,
					top: iframeWin.getBoundingClientRect().top,
					bottom: iframeWin.getBoundingClientRect().bottom
				},
				showSize: {          // 告诉嵌入的子页面视窗高度与宽度
					width: $(window).width(),    // 视窗宽度
					height: $(window).height()  // 视窗高度
				},
				iframeOffset: {      // iframe偏离文档的位置
					left: iframeWin.offsetLeft,
					top: iframeWin.offsetTop
				  }
			}, '\*');	   // '\*'表示跨域参数,请结合自身业务合理设置
	}

</script>
</html>

index.vue

<template>
  <div>
    <div style="overflow-y: hidden;">
      <el-row style="display: flex">
        <div class="user__tree" style="margin-top: 30px;overflow:auto;height: 72vh;font-size: 18px;width: 280px;">
<!--          <avue-tree :option="treeOption" :data="treeData" style="width: 320px;" class="disable-selection">-->
<!--              <span-->
<!--                class="el-tree-node__label"-->
<!--                slot-scope="{ node, data }"-->
<!--                :class="{ 'last-level-node': !data.children || data.children.length === 0 }"-->
<!--                @dblclick="dblclick(node, data)"-->
<!--                style="display: flex;align-items: center;font-size: 18px;"-->
<!--              >-->
<!--&lt;!&ndash;                <span v-if="data.isOnline===0" class="tree_icon_green"></span>&ndash;&gt;-->
<!--                <span v-if="data.isOnline===1" class="tree_icon_green"></span>-->
<!--                <span>{{ data.name }}</span>-->

<!--              </span>-->
<!--            </avue-tree>-->
          <el-tree
            :data="treeData"
            class="disable-selection"
            :option="treeOption"
            empty-text="加载中,请稍后"
            node-key="areaId"
            :default-expanded-keys="defaultExpandedKeys"
            :props="defaultProps"
          >
          <span
            class="el-tree-node__label"
            slot-scope="{ node, data }"
            :class="{ 'last-level-node': !data.children || data.children.length === 0 }"
            @dblclick="dblclick(node, data)"
            style="display: flex;align-items: center;font-size: 18px;"
          >
            <span v-if="data.isOnline===1" class="tree_icon_green"></span>
            <span>{{ data.name }}</span>
            </span>
          </el-tree>
        </div>

        <el-col :span="10" class="user__main" style="width: 100%;">
          <div style="width: 100%;" v-if="activeName == '实时视频'">
            <iframe style="width: 100%;height: 80vh" scrolling="no"
              :src="`${rtsp_ifarme_url}/in/demo_for_iframe.html?videoType=live`"
              frameborder="0" ref="iframe"></iframe>
              <!-- cameraCode=${cameraCode}& -->
          </div>
        </el-col>
      </el-row>
    </div>
  </div>
</template>

<script>
import { deptRoleList } from "@/api/admin/role";
import { fetchTree } from "@/api/admin/dept";
import { tableOption } from "@/const/crud/admin/user";
import { mapGetters } from "vuex";
import videoDemo from '@/views/video/video_demo.vue'
import Preview from "@/views/video/video_c/Preview.vue";
import request from "@/router/axios";

export default {
  name: "Video",
  components: {
    Preview,
    videoDemo
  },
  //父组件传过来的
  props: {
    activeName: {
      type: String,
      default: ''
    },
  },
  data() {
    return {
      totalPage: 0,
      defaultProps: {
        children: "children",
        label: "name"
      },
      defaultExpandedKeys: [],
      queryParams: {},
      openPreview: false,
      tableData: [], //列表
      playUrl: '', //播放的url
      endUrl: '',//关闭时传的url
      treeOption: {
        nodeKey: "id",
        addBtn: false,
        defaultExpandAll: true,
        menu: false,
        props: {
          label: "name",
          value: "id"
        }
      },
      treeData: [],
      rtsp: '',
      cameraCode: '',
      rtsp_ifarme_url:process.env.rtsp_ifarme_url
    };
  },
  computed: {},
  created() {
    this.init();
  },
  mounted() {
    this.defaultExpandedKeys = '3703';
  },
  deactivated() {
    this.activeName=""
  },
  beforeDestroy() {
    this.activeName=""
  },
  methods: {
    closePreview() {
      if (this.rtsp) {
        request({
          url: process.env.STREAM_CLOSE_URL,
          method: 'post',
          data: {
            urls: [this.rtsp]
          }
        }).then(res => {
          this.talkerJob = res.data.data.records.map(item => {
            return {
              label: item.jobName,
              value: item.id
            }
          })
        })
      }
    },
    dblclick(node, row) {
      console.log("点击:",node,row);
      if (row.children == 0) {
        console.log("根节点")
        console.log('node', node)
        console.log('row', row)

        let parentData = node.parent.data.children
        this.cameraCode = row.cameraCode
        console.log(this.cameraCode, 'code1')

        this.sendMesFroIframe(this.cameraCode);
        // request({
        //   url: '/ewaycloud/channel/hik/tvWallURL',
        //   method: 'get',
        //   params: {
        //     cameraIndexCode: this.cameraCode,
        //   },
        // }).then(res => {
        //   this.playUrl = process.env.STREAM_URL + '/live?url=' + res.data.data.url;
        //   this.openPreview = true
        //   // this.$nextTick(() => {
        //   //   this.$refs.preview.handlePlay(this.playUrl, parentData.indexOf(row), row);
        //   // })
        //   this.endUrl = res.data.data.url
        // })
      }
    },
    sendMesFroIframe(cameraCode) {
      // 向iframe传值
      const mapFrame = this.$refs['iframe'];
      const iframeWin = mapFrame.contentWindow;
      iframeWin.postMessage(
        {
          action:'cameraCode',
          cameraCode: cameraCode,
        },
        '*'
      );
    },
    // 每页数
    sizeChangeHandle(val) {
      this.queryParams.pageSize = val
      this.queryParams.pageIndex = 1
      this.getList()
    },
    // 当前页
    currentChangeHandle(val) {
      this.pageIndex = val
      this.getList()
    },
    init() {
      request({
        url: '/ewaycloud/camera/findCameraDeptTreeList',
        method: 'get',
      }).then(res => {
        this.treeData = res.data.data;
        // this.treeData.forEach((item,index)=>{
        //   item.status = 0
        //   item.forEach((item,index)=>{
        //     item.t
        //   })
        // })

        console.log("树数据:",this.treeData[0].children);
        // this.$set(this.treeData[0].children[0].children[0],'status','0')
        // this.$set(this.treeData[0].children[0].children[1],'status','1')
        // this.$set(this.treeData[0].children[0].children[2],'status','1')
        // console.log(this.treeData[0].children[0].children[2]);
        // console.log(this.treeData[0].children[0].children[0],'this.treeData[0].children[3].children[0]');
        // this.$set(this.treeData[0].children[0].children[3],'status','0')
      })
    },
    getList() {
      request({
        url: '/ewaycloud/camera/findCamerList',
        method: 'get',
        params: this.queryParams
      }).then(res => {
        this.tableData = res.data.data.records;
        this.totalPage = res.data.data.total;
      })
    },
    nodeClick(data) {
      this.queryParams.deptId = data.id;
      this.getList();
    },
  }
};
</script>
<style lang="scss" scoped>
  .last-level-node {
      font-size: 16px !important;
    }
.user {
  height: 100%;

  &__tree {
    padding-top: 3px;
    padding-right: 20px;
  }

  &__main {
    .el-card__body {
      padding-top: 0;
    }
  }
}

::v-deep .avue-view {
  overflow: hidden;
}
::v-deep .el-scrollbar{
  min-height: 200px;
}
.hover-cursor {
  cursor: pointer;
  /* 鼠标移入单元格时显示小手形状 */
}
.tree_icon_red{
  background-color: red;
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  margin-right: 5px;
}
.tree_icon_green{
  background-color: green;
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-right: 5px;
}
.divSelect:hover {
  cursor: pointer;
}
::v-deep .el-tree-node__expand-icon{
  font-size: 18px !important;
}
::v-deep .avue-tree__filter{
  position: sticky;
  top: -0.15rem;
  left: 2rem;
  background-color: #fff;
  z-index: 99;
  width: 100%;
}
.disable-selection {
  user-select: none; /* 标准语法 */
  -webkit-user-select: none; /* Chrome, Safari, Opera */
  -moz-user-select: none; /* Firefox */
  -ms-user-select: none; /* IE10+/Edge */
}
// ::v-deep .avue-tree{
//   padding-top: 50px;
// }
</style>


网站公告

今日签到

点亮在社区的每一天
去签到