UNPKG

dnp-client

Version:

数据基础设施轻量连接器,帮助用户管理身份凭证、区域节点、业务节点,完成身份集成。

954 lines (830 loc) 25.4 kB
# DNP 统一身份登录 SDK 统一身份登录 SDK - 基于自定义协议调用桌面应用程序进行身份认证 ## 功能特性 - 🔐 统一身份认证 - 🖥️ 桌面应用程序集成 - 📱 自定义协议支持 - ⏱️ 智能轮询机制 - 🔄 自动重试机制 - 💪 TypeScript 支持 - 🌐 多环境支持(浏览器、Electron) <!-- ## 📚 文档链接 - [📋 更新日志](CHANGELOG.md) - 查看版本更新历史 - [🔧 Webpack 兼容性说明](WEBPACK_COMPATIBILITY.md) - 解决构建兼容性问题 - [📦 发布指南](PUBLISH.md) - 项目发布说明 --> ## 安装 ```bash npm install dnp-client ``` 或者使用 yarn: ```bash yarn add dnp-client ``` ## 快速开始 ### 1. 统一身份登录 ```javascript import { createDnpLogin } from "dnp-client"; const dnpLogin = createDnpLogin({ options: { baseUrl: `http://your-api-server.com`, // 服务端地址 type: 1, // 1-区域,2-业务 }, callbacks: { onLoading: (loading) => { console.log("登录状态:", loading); }, onSuccess: (token) => { console.log("登录成功,Token:", token); // 处理登录成功逻辑 if (token) { // emit("login", token); } }, onError: (error) => { console.error("登录失败:", error); }, onMessage: (message, type) => { console.log(`消息[${type}]:`, message); }, }, }); // 点击登录按钮,开始登录 const handleLogin = () => { dnp.login(); }; ``` ### 2. 身份注册 ```javascript import { createDnpRegister } from "dnp-client"; const dnpRegister = createDnpRegister({ callbacks: { onLoading: (loading) => { console.log("注册状态:", loading); }, onSuccess: (certData) => { console.log("注册成功,证书数据:", certData); // 处理注册成功逻辑 certData的值是一个JSON字符串:"{\"csrJson\":\"-----BEGIN "}" try { const { csrJson } = JSON.parse(certData); // emit("register", csrJson); } catch (error) { console.error("解析证书数据失败:", error); } }, onError: (error) => { console.error("注册失败:", error); }, onMessage: (message, type) => { console.log(`消息[${type}]:`, message); }, }, }); const handleRegister = () => { const subject = [ { name: "countryName", value: "CN" }, { name: "stateOrProvinceName", value: "江苏" }, { name: "localityName", value: "南京" }, { name: "organizationName", value: "数据中心4" }, { name: "organizationalUnitName", value: "研发部" }, { name: "commonName", value: "zaq12wsx3edc4rfv5t" }, ]; dnpRegister.registerIdentity({ subject: JSON.stringify(subject) }); }; ``` ### 3. 身份凭证导入 ```javascript import { createDnpImportVc } from "dnp-client"; const dnpImportVc = createDnpImportVc({ callbacks: { onLoading: (loading) => { console.log("导入状态:", loading); }, onSuccess: (result) => { console.log("导入成功:", result); }, onError: (error) => { console.error("导入失败:", error); }, onMessage: (message, type) => { console.log(`消息[${type}]:`, message); }, }, }); const handleImport = () => { const crt = `-----BEGIN CERTIFICATE----- MIIDNDCCAhygAwIBAgIUborye/q2vMhxsjmmqZpx+eEXxykwDQYJKoZIhvcNAQEF BQAwJTEKMAgGA1UEAwwBYTEKMAgGA1UECgwBYTELMAkGA1UEBhMCQ04wHhcNMjUw NzEzMDY1MzAwWhcNMjkwNzA1MDY1MDAwWjBkMQswCQYDVQQGEwJDTjEPMA0GA1UE CAwG5YyX5LqsMQ8wDQYDVQQHDAbljJfkuqwxGzAZBgNVBAoMEuenkeaKgOaciemZ kOWFrOWPuDEWMBQGA1UEAwwNd3d3LmJhaWR1LmNvbTCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAKtsbM7tL/gJmpwBH2WT1pmat6qWAWuWlLdiW4WiX7MZ O8VHvMgmcnhFmJ5BRZ0VUZbHF2yWw/WoOCSMktFhqJ2NhjAYRxxTvd3sGbDncPEn 0/2VSfjtjuTp4JrQe+mR6Ki1KNTuNQTBLOb1lCImPhUt1PmMqr4MpPuJlqWCs86+ doTgtWZftP2SxeinkD+HAuQ4ohH47sINTWZgkjFI9Ctu05dGR3T/UdraELgs0u41 c/ANmrQX0KLEhDOSIYgRnnApFT21Jp+I0pfuVVretLyPinK351HfvSpiSRi9oHSp CjK3Q70cNG7iyieqYmksWBo0dge9uvi7ufB+19viJI0CAwEAAaMdMBswCwYDVR0R BAQwAoIAMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEFBQADggEBAJkHSavndKwY g7J0BFvGYxQQjNmWtN19zgehnNKQBRRSgVve710AjsiIhsnfF0JWY+bOMmTg3055 wp/8U9vMEsKYj7BOci8jMvI+7mcid6XgOPOgCV6t0mXQ5o7bWxVGNerVtNuPmcuY 5JCcnq9kGlzPXNsH7zc3T9HNtgn2Mjw8+3mLZElIApEz3XV4dtw1afRzuoM+bH04 Arz7S/A6RwVSa3twaihggmhLIGzxJyq5k60aeK35If5KNouPn5E12uUHAgjDs67+ asd9tWOH8zKR161kQ0xybhZfJPiUAJ3NWWqJFV+V4V7Z4KfzZ9oCLs+ObHCqCGky dZOehfiSF2A= -----END CERTIFICATE-----`; const certData = { crt, // 证书内容 certType: "1", // 凭证类型 1-接入主体身份凭证 2-接入连接器身份凭证 3-业务节点身份凭证 }; dnpImportVc.importCert(certData); }; ``` ## Vue.js 集成示例 ### 登录组件 ```vue <template> <div> <button @click="handleLogin" :disabled="loading"> {{ loading ? "登录中..." : "统一身份登录" }} </button> <div v-if="message">{{ message }}</div> </div> </template> <script> import { createDnpLogin } from "dnp-client"; export default { data() { return { loading: false, message: "", dnpLogin: null, }; }, created() { this.dnpLogin = createDnpLogin({ options: { baseUrl: process.env.VUE_APP_API_BASE_URL || `${window.location.origin}/api/v1`, }, callbacks: { onLoading: (loading) => { this.loading = loading; }, onSuccess: (token) => { this.message = "登录成功!"; localStorage.setItem("authToken", token); this.$router.push("/dashboard"); }, onError: (error) => { this.message = `登录失败: ${error.message}`; }, onMessage: (message, type) => { this.message = message; }, }, }); }, methods: { handleLogin() { this.dnpLogin.login(); }, }, beforeDestroy() { if (this.dnpLogin) { this.dnpLogin.destroy(); } }, }; </script> ``` ### 注册组件 ```vue <template> <div> <button @click="handleRegister" :disabled="loading"> {{ loading ? "注册中..." : "注册数字身份" }} </button> <div v-if="message">{{ message }}</div> </div> </template> <script> import { createDnpRegister } from "dnp-client"; export default { data() { return { loading: false, message: "", dnpRegister: null, }; }, created() { this.dnpRegister = createDnpRegister({ callbacks: { onLoading: (loading) => { this.loading = loading; }, onSuccess: (certData) => { this.message = "注册成功!"; console.log("证书数据:", certData); // 处理注册成功逻辑 - 解析证书数据 try { const { csrJson } = JSON.parse(certData); console.log("解析后的CSR:", csrJson); // 可以将CSR数据保存到本地存储或发送到后端 localStorage.setItem("csrData", csrJson); } catch (error) { console.error("解析证书数据失败:", error); } }, onError: (error) => { this.message = `注册失败: ${error.message}`; }, onMessage: (message, type) => { this.message = message; }, }, }); }, methods: { handleRegister() { // 构建主体信息 const subject = [ { name: "countryName", value: "CN" }, { name: "stateOrProvinceName", value: "北京" }, { name: "localityName", value: "海淀" }, { name: "organizationName", value: "科技公司" }, { name: "organizationalUnitName", value: "研发部" }, { name: "commonName", value: "www.example.com" }, ]; this.dnpRegister.registerIdentity({ subject: JSON.stringify(subject), }); }, }, beforeDestroy() { if (this.dnpRegister) { this.dnpRegister.destroy(); } }, }; </script> ``` ### 凭证导入组件 ```vue <template> <div> <div class="cert-form"> <h3>身份凭证导入</h3> <div class="form-group"> <label>证书内容:</label> <textarea v-model="certContent" placeholder="请粘贴证书内容(PEM格式)" rows="10" cols="60" ></textarea> </div> <div class="form-group"> <label>凭证类型:</label> <select v-model="certType"> <option value="1">接入主体身份凭证</option> <option value="2">接入连接器身份凭证</option> <option value="3">业务节点身份凭证</option> </select> </div> <button @click="handleImport" :disabled="loading || !certContent"> {{ loading ? "导入中..." : "导入凭证" }} </button> <button @click="useSampleCert" :disabled="loading">使用示例证书</button> </div> <div v-if="message" class="message">{{ message }}</div> </div> </template> <script> import { createDnpImportVc } from "dnp-client"; export default { data() { return { loading: false, message: "", certContent: "", certType: "1", dnpImportVc: null, }; }, created() { this.dnpImportVc = createDnpImportVc({ callbacks: { onLoading: (loading) => { this.loading = loading; }, onSuccess: (result) => { this.message = "凭证导入成功!"; console.log("导入结果:", result); // 处理导入成功逻辑 localStorage.setItem("importedCert", JSON.stringify(result)); }, onError: (error) => { this.message = `导入失败: ${error.message}`; }, onMessage: (message, type) => { this.message = message; }, }, }); }, methods: { handleImport() { if (!this.certContent.trim()) { this.message = "请输入证书内容"; return; } const certData = { crt: this.certContent, // 证书内容 certType: this.certType, // 凭证类型 }; this.dnpImportVc.importCert(certData); }, useSampleCert() { // 使用示例证书 this.certContent = `-----BEGIN CERTIFICATE----- MIIDNDCCAhygAwIBAgIUborye/q2vMhxsjmmqZpx+eEXxykwDQYJKoZIhvcNAQEF BQAwJTEKMAgGA1UEAwwBYTEKMAgGA1UECgwBYTELMAkGA1UEBhMCQ04wHhcNMjUw NzEzMDY1MzAwWhcNMjkwNzA1MDY1MDAwWjBkMQswCQYDVQQGEwJDTjEPMA0GA1UE CAwG5YyX5LqsMQ8wDQYDVQQHDAbljJfkuqwxGzAZBgNVBAoMEuenkeaKgOaciemZ kOWFrOWPuDEWMBQGA1UEAwwNd3d3LmJhaWR1LmNvbTCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAKtsbM7tL/gJmpwBH2WT1pmat6qWAWuWlLdiW4WiX7MZ O8VHvMgmcnhFmJ5BRZ0VUZbHF2yWw/WoOCSMktFhqJ2NhjAYRxxTvd3sGbDncPEn 0/2VSfjtjuTp4JrQe+mR6Ki1KNTuNQTBLOb1lCImPhUt1PmMqr4MpPuJlqWCs86+ doTgtWZftP2SxeinkD+HAuQ4ohH47sINTWZgkjFI9Ctu05dGR3T/UdraELgs0u41 c/ANmrQX0KLEhDOSIYgRnnApFT21Jp+I0pfuVVretLyPinK351HfvSpiSRi9oHSp CjK3Q70cNG7iyieqYmksWBo0dge9uvi7ufB+19viJI0CAwEAAaMdMBswCwYDVR0R BAQwAoIAMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEFBQADggEBAJkHSavndKwY g7J0BFvGYxQQjNmWtN19zgehnNKQBRRSgVve710AjsiIhsnfF0JWY+bOMmTg3055 wp/8U9vMEsKYj7BOci8jMvI+7mcid6XgOPOgCV6t0mXQ5o7bWxVGNerVtNuPmcuY 5JCcnq9kGlzPXNsH7zc3T9HNtgn2Mjw8+3mLZElIApEz3XV4dtw1afRzuoM+bH04 Arz7S/A6RwVSa3twaihggmhLIGzxJyq5k60aeK35If5KNouPn5E12uUHAgjDs67+ asd9tWOH8zKR161kQ0xybhZfJPiUAJ3NWWqJFV+V4V7Z4KfzZ9oCLs+ObHCqCGky dZOehfiSF2A= -----END CERTIFICATE-----`; this.message = "已填入示例证书"; }, }, beforeDestroy() { if (this.dnpImportVc) { this.dnpImportVc.destroy(); } }, }; </script> <style scoped> .cert-form { max-width: 600px; margin: 20px 0; } .form-group { margin-bottom: 15px; } .form-group label { display: block; margin-bottom: 5px; font-weight: bold; } .form-group textarea { width: 100%; border: 1px solid #ddd; border-radius: 4px; padding: 8px; font-family: monospace; font-size: 12px; resize: vertical; } .form-group select { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; } button { margin-right: 10px; padding: 8px 16px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } button:disabled { background-color: #ccc; cursor: not-allowed; } .message { margin-top: 15px; padding: 10px; border-radius: 4px; background-color: #f8f9fa; border: 1px solid #dee2e6; } </style> ``` ## React 集成示例 ### 登录组件 ```jsx import React, { useState, useEffect, useRef } from "react"; import { createDnpLogin } from "dnp-client"; function LoginComponent() { const [loading, setLoading] = useState(false); const [message, setMessage] = useState(""); const dnpLoginRef = useRef(null); useEffect(() => { dnpLoginRef.current = createDnpLogin({ options: { baseUrl: process.env.REACT_APP_API_BASE_URL || `${window.location.origin}/api/v1`, }, callbacks: { onLoading: setLoading, onSuccess: (token) => { setMessage("登录成功!"); localStorage.setItem("authToken", token); // 跳转到主页或处理登录成功逻辑 }, onError: (error) => { setMessage(`登录失败: ${error.message}`); }, onMessage: (message) => { setMessage(message); }, }, }); return () => { if (dnpLoginRef.current) { dnpLoginRef.current.destroy(); } }; }, []); const handleLogin = () => { dnpLoginRef.current?.login(); }; return ( <div> <button onClick={handleLogin} disabled={loading}> {loading ? "登录中..." : "统一身份登录"} </button> {message && <div>{message}</div>} </div> ); } export default LoginComponent; ``` ### 注册组件 ```jsx import React, { useState, useEffect, useRef } from "react"; import { createDnpRegister } from "dnp-client"; function RegisterComponent() { const [loading, setLoading] = useState(false); const [message, setMessage] = useState(""); const dnpRegisterRef = useRef(null); useEffect(() => { dnpRegisterRef.current = createDnpRegister({ callbacks: { onLoading: setLoading, onSuccess: (certData) => { setMessage("注册成功!"); console.log("证书数据:", certData); // 处理注册成功逻辑 - 解析证书数据 try { const { csrJson } = JSON.parse(certData); console.log("解析后的CSR:", csrJson); // 可以将CSR数据保存到本地存储或发送到后端 localStorage.setItem("csrData", csrJson); } catch (error) { console.error("解析证书数据失败:", error); } }, onError: (error) => { setMessage(`注册失败: ${error.message}`); }, onMessage: (message) => { setMessage(message); }, }, }); return () => { if (dnpRegisterRef.current) { dnpRegisterRef.current.destroy(); } }; }, []); const handleRegister = () => { // 构建主体信息 const subject = [ { name: "countryName", value: "CN" }, { name: "stateOrProvinceName", value: "北京" }, { name: "localityName", value: "海淀" }, { name: "organizationName", value: "科技公司" }, { name: "organizationalUnitName", value: "研发部" }, { name: "commonName", value: "www.example.com" }, ]; dnpRegisterRef.current?.registerIdentity({ subject: JSON.stringify(subject), }); }; return ( <div> <button onClick={handleRegister} disabled={loading}> {loading ? "注册中..." : "注册数字身份"} </button> {message && <div>{message}</div>} </div> ); } export default RegisterComponent; ``` ### 凭证导入组件 ```jsx import React, { useState, useEffect, useRef } from "react"; import { createDnpImportVc } from "dnp-client"; function ImportVcComponent() { const [loading, setLoading] = useState(false); const [message, setMessage] = useState(""); const [certContent, setCertContent] = useState(""); const [certType, setCertType] = useState("1"); const dnpImportVcRef = useRef(null); useEffect(() => { dnpImportVcRef.current = createDnpImportVc({ callbacks: { onLoading: setLoading, onSuccess: (result) => { setMessage("凭证导入成功!"); console.log("导入结果:", result); // 处理导入成功逻辑 localStorage.setItem("importedCert", JSON.stringify(result)); }, onError: (error) => { setMessage(`导入失败: ${error.message}`); }, onMessage: (message, type) => { setMessage(message); }, }, }); return () => { if (dnpImportVcRef.current) { dnpImportVcRef.current.destroy(); } }; }, []); const handleImport = () => { if (!certContent.trim()) { setMessage("请输入证书内容"); return; } const certData = { crt: certContent, // 证书内容 certType: certType, // 凭证类型 }; dnpImportVcRef.current?.importCert(certData); }; const useSampleCert = () => { // 使用示例证书 const sampleCert = `-----BEGIN CERTIFICATE----- MIIDNDCCAhygAwIBAgIUborye/q2vMhxsjmmqZpx+eEXxykwDQYJKoZIhvcNAQEF BQAwJTEKMAgGA1UEAwwBYTEKMAgGA1UECgwBYTELMAkGA1UEBhMCQ04wHhcNMjUw NzEzMDY1MzAwWhcNMjkwNzA1MDY1MDAwWjBkMQswCQYDVQQGEwJDTjEPMA0GA1UE CAwG5YyX5LqsMQ8wDQYDVQQHDAbljJfkuqwxGzAZBgNVBAoMEuenkeaKgOaciemZ kOWFrOWPuDEWMBQGA1UEAwwNd3d3LmJhaWR1LmNvbTCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAKtsbM7tL/gJmpwBH2WT1pmat6qWAWuWlLdiW4WiX7MZ O8VHvMgmcnhFmJ5BRZ0VUZbHF2yWw/WoOCSMktFhqJ2NhjAYRxxTvd3sGbDncPEn 0/2VSfjtjuTp4JrQe+mR6Ki1KNTuNQTBLOb1lCImPhUt1PmMqr4MpPuJlqWCs86+ doTgtWZftP2SxeinkD+HAuQ4ohH47sINTWZgkjFI9Ctu05dGR3T/UdraELgs0u41 c/ANmrQX0KLEhDOSIYgRnnApFT21Jp+I0pfuVVretLyPinK351HfvSpiSRi9oHSp CjK3Q70cNG7iyieqYmksWBo0dge9uvi7ufB+19viJI0CAwEAAaMdMBswCwYDVR0R BAQwAoIAMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEFBQADggEBAJkHSavndKwY g7J0BFvGYxQQjNmWtN19zgehnNKQBRRSgVve710AjsiIhsnfF0JWY+bOMmTg3055 wp/8U9vMEsKYj7BOci8jMvI+7mcid6XgOPOgCV6t0mXQ5o7bWxVGNerVtNuPmcuY 5JCcnq9kGlzPXNsH7zc3T9HNtgn2Mjw8+3mLZElIApEz3XV4dtw1afRzuoM+bH04 Arz7S/A6RwVSa3twaihggmhLIGzxJyq5k60aeK35If5KNouPn5E12uUHAgjDs67+ asd9tWOH8zKR161kQ0xybhZfJPiUAJ3NWWqJFV+V4V7Z4KfzZ9oCLs+ObHCqCGky dZOehfiSF2A= -----END CERTIFICATE-----`; setCertContent(sampleCert); setMessage("已填入示例证书"); }; return ( <div> <div style={{ maxWidth: "600px", margin: "20px 0" }}> <h3>身份凭证导入</h3> <div style={{ marginBottom: "15px" }}> <label style={{ display: "block", marginBottom: "5px", fontWeight: "bold", }} > 证书内容: </label> <textarea value={certContent} onChange={(e) => setCertContent(e.target.value)} placeholder="请粘贴证书内容(PEM格式)" rows="10" cols="60" style={{ width: "100%", border: "1px solid #ddd", borderRadius: "4px", padding: "8px", fontFamily: "monospace", fontSize: "12px", resize: "vertical", }} /> </div> <div style={{ marginBottom: "15px" }}> <label style={{ display: "block", marginBottom: "5px", fontWeight: "bold", }} > 凭证类型: </label> <select value={certType} onChange={(e) => setCertType(e.target.value)} style={{ width: "100%", padding: "8px", border: "1px solid #ddd", borderRadius: "4px", }} > <option value="1">接入主体身份凭证</option> <option value="2">接入连接器身份凭证</option> <option value="3">业务节点身份凭证</option> </select> </div> <button onClick={handleImport} disabled={loading || !certContent} style={{ marginRight: "10px", padding: "8px 16px", backgroundColor: loading || !certContent ? "#ccc" : "#007bff", color: "white", border: "none", borderRadius: "4px", cursor: loading || !certContent ? "not-allowed" : "pointer", }} > {loading ? "导入中..." : "导入凭证"} </button> <button onClick={useSampleCert} disabled={loading} style={{ padding: "8px 16px", backgroundColor: loading ? "#ccc" : "#28a745", color: "white", border: "none", borderRadius: "4px", cursor: loading ? "not-allowed" : "pointer", }} > 使用示例证书 </button> </div> {message && ( <div style={{ marginTop: "15px", padding: "10px", borderRadius: "4px", backgroundColor: "#f8f9fa", border: "1px solid #dee2e6", }} > {message} </div> )} </div> ); } export default ImportVcComponent; ``` ## 配置选项 ### DnpLoginOptions | 参数 | 类型 | 默认值 | 说明 | | --------- | -------- | ------------------------ | ---------------------------- | | `baseUrl` | `string` | `window.location.origin` | 域名配置(用于桌面应用调用) | | `type` | `number` | `1` | 类型参数 1-区域,2-业务 | ### DnpLoginCallbacks | 参数 | 类型 | 说明 | | ----------- | ------------------------------------------ | ---------------------- | | `onLoading` | `(loading: boolean) => void` | 加载状态变化回调 | | `onSuccess` | `(data: any) => void` | 成功回调,返回相应数据 | | `onError` | `(error: Error) => void` | 错误回调 | | `onMessage` | `(message: string, type?: string) => void` | 消息回调 | ### 身份凭证数据结构 ```javascript const certData = { crt: "string", // 证书内容-----BEGIN CERTIFICATE-----\nXXX-----END CERTIFICATE----- certType: "1", // 凭证类型 1-接入主体身份凭证 2-接入连接器身份凭证 3-业务节点身份凭证 }; ``` ## API 方法 ### 登录相关 #### `login()` 开始统一身份登录流程。 ```javascript await dnpLogin.login(); ``` ### 注册相关 #### `registerIdentity(params)` 注册身份(1-接入主体身份凭证 2-接入连接器身份凭证 3-业务节点身份凭证)。 ```javascript // 注册 await dnpRegister.registerIdentity({ subject: "[{ name: 'countryName', value: 'CN' }]", }); ``` 参数说明: - `subject` (必填): 主体信息 ### 凭证导入相关 #### `importCert(certData)` 导入身份凭证。 ```javascript await dnpImportVc.importCert(certData); ``` ## 工厂函数 ### `createDnpLogin(config)` 创建登录实例。 ```javascript import { createDnpLogin } from "dnp-client"; const dnpLogin = createDnpLogin(config); ``` ### `createDnpRegister(config)` 创建注册实例。 ```javascript import { createDnpRegister } from "dnp-client"; const dnpRegister = createDnpRegister(config); ``` ### `createDnpImportVc(config)` 创建凭证导入实例。 ```javascript import { createDnpImportVc } from "dnp-client"; const dnpImportVc = createDnpImportVc(config); ``` ## 注意事项 1. **桌面应用依赖**:此 SDK 依赖于桌面应用程序,需要确保目标设备已安装相应的桌面客户端 2. **自定义协议**:自定义协议 `dnp://` 需要在桌面应用程序中正确注册 3. **服务器配置**:确保 DNP 服务器正确配置并运行在指定端口(默认 3521) 4. **资源清理**:建议在组件销毁时调用 `destroy()` 方法以释放资源 5. **错误处理**:建议实现完整的错误处理机制,包括网络错误、超时等情况 ## 错误处理 ### 常见错误情况 - 网络连接失败 - 服务器响应错误 - 桌面应用程序未安装 - 自定义协议未注册 - 轮询超时 ### 错误处理建议 ```javascript const dnpLogin = createDnpLogin({ callbacks: { onError: (error) => { console.error("DNP错误:", error); // 根据错误类型进行处理 if (error.message.includes("fetch")) { // 网络错误 alert("网络连接失败,请检查网络设置"); } else if (error.message.includes("timeout")) { // 超时错误 alert("操作超时,请重试"); } else { // 其他错误 alert("操作失败,请重试"); } }, onMessage: (message, type) => { if (type === "error") { console.error("DNP消息:", message); } }, }, }); ``` ## 浏览器兼容性 - Chrome 60+ - Firefox 55+ - Safari 12+ - Edge 79+ - 支持现代浏览器的自定义协议处理 ## 许可证 MIT License ## 贡献 欢迎提交 Issue 和 Pull Request 来改进这个项目。