大祥子的博客日记 - 大前端
type: channel
link: https://blog.fxnws.com/category/web/
description: 大前端
items: websocket封装带心跳和重连机制（vue3+ts+vite）
items: PresentationAPI使用教程(Vue3&html)
items: vue3使用vue-clipboard3复制到剪切板
items: 编译asar文件与electron反编译
items: Vue使用screenfull全屏插件
items: 小程序防抖节流方法封装
items: Vue3使用setup语法糖
items: vue3使用pdfh5插件
items: 前端转PDF，通过前端edge库去实现（officeNet源码见附件）
items: Vue3请求封装

websocket封装带心跳和重连机制（vue3+ts+vite）
type: item
date: 2022-08-18T17:11:00+08:00
description: import { mitts } from "./tool"; /* * @Author: lzx * @Date: 2022-05-25 15:42:37 * @LastEditors: lzx * @LastEditTime: 2022-08-18 15:01:38 * @Description: Fuck Bug * @FilePath: \talk_pc\src\utils\socket.ts */ let socketUrl: any = ""; // socket地址 let websocket: any = null; // websocket 实例 let heartTime: any = null; // 心跳定时器实例 let socketHeart: number = 0; // 心跳次数 let HeartTimeOut: number = 3000; // 心跳超时时间 let socketError: number = 0; // 错误次数 // 初始化socket const initWebSocket = (url: any) => { socketUrl = url; // 初始化 websocket websocket = new WebSocket(url); websocketonopen(); websocketonmessage(); sendSocketHeart() }; // socket 连接成功 const websocketonopen = () => { websocket.onopen = function (e: any) { console.log("连接 websocket 成功", e); resetHeart(); }; }; // socket 连接失败 const websocketonerror = () => { websocket.onerror = function (e: any) { console.log("连接 websocket 失败", e); }; }; // socket 断开链接 const websocketclose = () => { websocket.onclose = function (e: any) { console.log("断开连接", e); }; }; // socket 接收数据 const websocketonmessage = () => { websocket.onmessage = function (e: any) { let msg = JSON.parse(e.data); if (msg.type === 'heartbeat') { resetHeart() console.log("心跳"); } // console.log("收到socket消息", JSON.parse(e.data)); test(msg) // 测试数据 }; }; // socket 发送数据 const sendMsg = (data: any) => { websocket.send(data); }; // socket 错误 const websocketError = () => { websocket.onerror = function (e: any) { console.log("socket 错误", e); }; }; // socket 重置心跳 const resetHeart = () => { socketHeart = 0; socketError = 0; clearInterval(heartTime); sendSocketHeart(); }; // socket心跳发送 const sendSocketHeart = () => { heartTime = setInterval(() => { if (socketHeart <= 2) { console.log("心跳发送：", socketHeart); websocket.send( JSON.stringify({ content: "", requestId: "aa9872be-d5b9-478e-aba4-50527cd3ef32", type: "heartbeat" }) ); socketHeart = socketHeart + 1; } else { reconnect() } }, HeartTimeOut); }; // socket重连 const reconnect = () => { if (socketError <= 2) { clearInterval(heartTi…

PresentationAPI使用教程(Vue3&html)
type: item
date: 2022-07-08T18:19:00+08:00
description: W3C在2020年12月份出台了Presentation API规范，这个api接口允许用户自由控制投放在其他屏幕上，并和投放窗口通信，查看W3C规范。演示地址：投屏演示，详细demo（Vue3/HTML）见文章末尾{alert type="error"} 特别注意：需要注意必须为https环境{/alert} 1、创建一个PresentationRequest对象，该对象将包含我们要在辅助附加显示器上显示的地址。const presentationRequest = new PresentationRequest('receiver.html'); 2、做控制及监听事件/* * @Author: lzx * @Date: 2022-07-08 15:34:16 * @LastEditors: lzx * @LastEditTime: 2022-07-08 17:14:31 * @Description: Fuck Bug * @FilePath: \test\js\index.js */ // 创建个投屏对象 const presentationRequest = new PresentationRequest(['https://blog.fxnws.com']); // 投屏网址 // const presentationRequest = new PresentationRequest(['test.html']);//投屏页面 // Make this presentation the default one when using the "Cast" browser menu. navigator.presentation.defaultRequest = presentationRequest; let presentationConnection; //监视连接是否可用 presentationRequest.addEventListener('connectionavailable', function (event) { presentationConnection = event.connection; presentationConnection.addEventListener('close', function () { console.log('> Connection closed.'); }); presentationConnection.addEventListener('terminate', function () { console.log('> Connection terminated.'); }); presentationConnection.addEventListener('message', function (event) { console.log('> ' + event.data); }); }); //监视可用的显示器 presentationRequest.getAvailability() .then(availability => { console.log('Available presentation displays: ' + availability.value); availability.addEventListener('change', function () { console.log('> Available presentation displays: ' + availability.value); }); }) .catch(error => { console.log('Presentation availability not supported, ' + error.name + ': ' + error.message); }); // 开启投屏 function startProjectionScreen () { presentationRequest.start() .then(connection => { console.log('Connected to ' + connection.url + ', id: ' + connection.id); }) .catch(error => { console.log(error); }); } //发送消息 function sendMsg () { const message = '发送消息测试'; const lang = document.body.lang || 'en-US'; console.log('Sending "' + message + '…

vue3使用vue-clipboard3复制到剪切板
type: item
date: 2022-06-21T11:56:00+08:00
description: 使用vue-clipboard3实现复制到剪切板，首先引入：npm install --save vue-clipboard3（详见）使用教程 点我复制

编译asar文件与electron反编译
type: item
date: 2022-06-15T14:27:00+08:00
description: 这两天遇到了个软件，想反编一下，整理各种不太会，最后想着会不会是electron开发的，就突发奇想把软件.exe改成zip打开看看，果不其然看到了electron图标，看了下是用electron-quick-start写的（electron-quick-start）目录结构如下：打开resources目录可以看到app.asar（这个文件就是项目源码的归档文件，asar是一种归档格式）既然找到了app.asar，那就把它解压出来看看吧，具体操作如下： 1、npm install -g asar全局安装asar插件 2、使用asar命令解压asar extract app.asar（源文件路径） ./（解压路径） 3、解压后的样式，可以看到解压出来了个app文件夹，里面就是代码了，可以对着代码为所欲为了，逆向出来的package.json文件里是没有devDependencies的，而electron是开发环境依赖，所以要自己手动添加npm add electron --save-dev，然后加个启动方式script："start" : " electron . "需要执行npm install就执行下，然后执行npm start跑起来即可。（也可以创建一个空项目，将上面从app.asar中逆向处理的源代码拷贝过去） 4、改写下代码就可以重新打包了，打包前需要先为项目添加一些打包选项，编辑项目根目录下的package.json文件，为其添加build选项："build": { "appId": "程序包名", // 程序包名 "copyright": "版权相关信息", // 版权相关信息 "productName": "项目名字", // 项目名字 "dmg": { "background": "背景图片的路径", // 背景图片的路径 "window": { // 启动后窗口的大小 "width": 500, "height": 300 } }, "win": { // 打包后的文件会有个图标，这个图标的路径，不指定的话默认的不好看 "icon": "图标" } // 注意：图标、appId什么的要对应好，然后在项目的根路径下执行打包命令 5、如何重新打包app.asar，执行asar pack ./（路径） app.asar（路径&名称）虽然能重新打包个app.asar文件，但是无法文件夹打包成为exe文件（我暂时未找到好方法）结语：如果能直接文件夹打包exe的话，直接替换app.asar文件就很nice了

Vue使用screenfull全屏插件
type: item
date: 2022-05-12T16:19:00+08:00
description: 1、下载screenfull插件npm install screenfull@5 --save，注意Vue3中使用需要下载低版本的，否则会报错误2、引入，安装教程引入会发现，node_modules/screenfull下找不到index.js文件会报错如上，具体使用如下{mtitle title="具体使用"/} 测试全屏 import ScreenFull from 'screenfull/dist/screenfull' test() { if (!ScreenFull.isEnabled) { alert('不支持全屏') return false; } ScreenFull.toggle(); } 3、webApi提供的全屏API详见（MDN全屏API）

小程序防抖节流方法封装
type: item
date: 2022-02-24T11:43:00+08:00
description: /* * @Author: lzx * @Date: 2022-05-06 13:33:43 * @LastEditors: lzx * @LastEditTime: 2022-05-06 13:57:06 * @Description: Fuck Bug * @FilePath: \reconsitution_talk_vant\utils\tool.js */ /*函数节流*/ const throttle = (fn, interval) => { let enterTime = 0; // 触发的时间 let gapTime = interval || 500; // 间隔时间，如果interval不传，则默认300ms return function () { var context = this; var backTime = new Date(); // 第一次函数return即触发的时间 if (backTime - enterTime > gapTime) { fn.call(context, arguments); enterTime = backTime; // 赋值给第一次触发的时间，这样就保存了第二次触发的时间 } }; } /*函数防抖*/ const debounce = (fn, interval) => { let timer; let gapTime = interval || 500; // 间隔时间，如果interval不传，则默认500ms return function () { clearTimeout(timer); var context = this; var args = arguments; // 保存此处的arguments，因为setTimeout是全局的，arguments不是防抖函数需要的。 timer = setTimeout(function () { fn.call(context, args); }, gapTime); }; } export default { throttle, debounce }; // 说明 // 页面引入：import tool from '../../utils/tool' // 调用示例： // 防抖 // test1: tool.debounce(function () { // console.log('防抖') // }, 5000) // 节流 // test2: tool.throttle(function () { // console.log('节流') // }, 5000)

Vue3使用setup语法糖
type: item
date: 2022-02-10T22:55:00+08:00
description: 1、语法糖与非语法糖代码写法对比{mtitle title="普通写法非语法糖"/} {mtitle title="setup语法糖写法"/}/** * setup语法糖写法 */ 2、如何在setup语法糖中使用props和配置TS使用（首先需要在 {mtitle title="setup语法糖使用props"/}/** *setup语法糖使用props */ 3、如何在setup语法糖中使用emit和配置TS使用（首先需要在 {mtitle title="子组件调用在setup语法糖下"/}/** * 子组件调用在setup语法糖下 */ 3、使用defineExpose（使用 ⽗组件 <child ref="child" />

vue3使用pdfh5插件
type: item
date: 2022-01-27T11:14:00+08:00
description: // 下载引入 npm i pdfh5 -S 或者 npm install pdfh5 需要用到的页面引入 import Pdfh5 from "pdfh5" import"pdfh5/css/pdfh5.css" 模板 创建实例对象 setup (props, { emit }) { // 获取dom let pdfBox = ref(null) // 传参 const state = reactive({ pdfh5: null }) // 挂载 onMounted(() => { if (router.currentRoute.value.query) { state.pdfh5 = new Pdfh5(pdfBox.value, { // 此处使用ref获取避免不刷新 pdfurl: 'http://192.168.4.67:17000/sign/' + router.currentRoute.value.query.url }); //监听完成事件 state.pdfh5.on("complete", function (status, msg, time) { console.log("状态：" + status + "，信息：" + msg + "，耗时：" + time + "毫秒，总页数：" + this.totalNum) }) } }) onUnmounted(() => { state.pdfh5 = null }) }

前端转PDF，通过前端edge库去实现（officeNet源码见附件）
type: item
date: 2021-12-16T11:03:00+08:00
description: 通过前端edge库去实现（officeNet源码见附件）1、方案系统环境： 需要安装office，试过office2019 和 2016，其他版本尚未测试； 需要安装vs2010的office运行时（office安装完毕之后应该已经包含运行时环境，但实际情况不能保证每一台安装都没问题）（最好下载安装个微软运行库） 运行时微软官方下载地址（或者直接下载微软运行库合集）： 微软运行库 微软运行库 需要.netframework4.5及其以上（因为edge支持的.net的类库版本要求不得低于4.5，否则类库语法编译不通过，类库代码编写必须按照edge的格式要求定义返回值方法名入参格式等）2、方案前端环境： 1.package.json dependencies 追加 "electron-edge-js": "^14.16.1" （和electron 13.6.0版本匹配） 2.vue.config.js 外部拓展 externals 追加 "electron-edge-js"，打包的时候否则遗漏报错 3.officeNet.dll 提供的中间类库，放到前端项目根目录下调用 const edge = require('electron-edge-js')3、officeNet功能：{mtitle title="执行word转换pdf：前端代码调用示例（backgrund.js中测试）"/}var sdkPath = path.resolve("./officeNet.dll") // 三个参数名称和参数的值写死 const translate = edgefunc({ assemblyFile: sdkPath, typeName:"officeNet.OfficeTrans", translate: "Invoke" }) // 这两个参数名称写死，一个是输入文件绝对地址，一个是输出文件绝对地址，类库取这两个字段 var input = { inputFilePath: "E:\office-dll\0222\1.docx", outputFilePath: "E:\office-dll\0222\1.pdf" } translate(input, function(err,result) { if (err) throw err; console.log("traslate doc -> pdf: " + result); // 成功返回1 失败返回2 });{mtitle title="判断当前系统是否安装office"/}// 三个参数名称和参数的值写死 const officeVersionCheck =edge.func({ assemblyFile: sdkPath, typeName:"officeNet.OfficeCheck", translate: "Invoke" }) // 入参为{}对象（预留） officeVersionCheck({}, function(err,result) { if (err) throw err; console.log("office word version check：" + result); // 返回1 环境正常，返回2，office版本低于2010，返回3，系统无office });该方法通过注册表去获取word信息，不能绝对确认office在系统中安装，无法检测office安装是否存在问题；简单举例：注册表异常 或者 注册表残留 都会导致返回的值不准确（该方法只能作为一个参考）备注： 1.不能保证所有版本的word文件都能转换为pdf，可能存在转换失败的情况，代码注意做好不能转换的处理 2.仅限于windows环境使用 3.虽然edge内可以写c#代码，c#中可以写node代码，禁止这样写，代码互侵太严重 4.edge官方地址https://github.com/tjanczuk/edge#how-to-marshal-data-between-c-and-nodejs 5.自行熟悉edge的语法和代码约束和方法定义要求，edge性能不高（node和c#两个运行环境的胶水层），但是应用于当前pc场景本地转换文件功能足够{hide}officeNet.zip{/hide}

Vue3请求封装
type: item
date: 2021-11-17T10:03:00+08:00
description: {mtitle title="使用教程"/}配合使用 Vue3接口封装（配合请求封装）{mtitle title="新建一个request.js文件"/}import axios from 'axios' import {getUserInfo} from '@/utils/auth' import errorCode from '@/utils/errorCode' let msgBox: any = null; // 创建axios实例 const service = axios.create({ baseURL: process.env.VUE_APP_API, // 配置的地址 timeout: 30000, // 超时 headers: { 'content-type': 'application/json;charset=utf-8' } }); // request拦截器 // @ts-ignore service.interceptors.request.use(config => { if (getUserInfo() && getUserInfo().token) { // @ts-ignore config.headers.token = getUserInfo().token.toString() // 自定义token } // get请求映射params参数 if (config.method === 'get' && config.params) { let url = config.url + '?'; for (const propName of Object.keys(config.params)) { // @ts-ignore const value = config.params[propName]; let part = encodeURIComponent(propName) + "="; if (value !== null && typeof (value) !== "undefined") { if (typeof value === 'object') { for (const key of Object.keys(value)) { let params = propName + '[' + key + ']'; let subPart = encodeURIComponent(params) + "="; url += subPart + encodeURIComponent(value[key]) + "&"; } } else { url += part + encodeURIComponent(value) + "&"; } } } url = url.slice(0, -1); config.params = {}; config.url = url; } return config }, error => { return Promise.reject(error) }) // 响应拦截器 service.interceptors.response.use(res => { // 未设置状态码则默认成功状态 const code = res.data.code || 200; // 获取错误信息 // @ts-ignore const msg = errorCode[code] || res.data.message || errorCode['default']; if (code === 403) { if (msgBox) return false; // msgBox = ElMessage.error("登录状态已过期，请重新登录！") setTimeout(() => { location.href = '/'; }, 1500) } else if (code === 400) { // ElMessage.error(msg); return res.data; } else if (code !== 200) { // ElMessage.error(msg); return res.data; } else { return res.data } }, error => { // ElMessage.error("服务器连接错误，请联系管理员！") // ElMessage.error(errorCode['default']); return null } ) export default service {mtitle title="新建一个errorCode.js文件"/}// 用于定义一些错误码封装 export default {…