懒羊羊
2024-03-06 93feb80ee51b6b8e99bf780689f5f7e9aa02427e
提交 | 用户 | 时间
93feb8 1 <template>
2   <div>
3     <el-row
4       type="flex"
5       class="row-bg"
6       justify="center"
7       v-show="portsList.length === 0"
8     >
9       <el-col :span="7">
10         <div style="margin-top: 400px">
11           <span style="display: block">
12             仅支持Chrome 89+或者Edge 89+浏览器(安全上下文(HTTPS)中可用)
13           </span>
14           <el-button type="primary" @click="obtainAuthorization">授权</el-button>
15         </div>
16       </el-col>
17     </el-row>
18     <el-form
19       v-show="portsList.length > 0"
20       ref="form1"
21       :model="form1"
22       label-width="100px">
23       <el-row>
24         <el-col :span="6"
25         ><div>
26           <el-form-item label="串口">
27             <el-select
28               v-model="form1.port"
29               filterable
30               placeholder="请选择串口"
31               :disabled="isDisable"
32             >
33               <el-option
34                 v-for="item in portsList"
35                 :key="item.value"
36                 :label="item.label"
37                 :value="item.value"
38               >
39               </el-option>
40             </el-select>
41           </el-form-item>
42           <el-form-item label="波特率">
43             <el-autocomplete
44               popper-class="my-autocomplete"
45               v-model="form1.baudRate"
46               :fetch-suggestions="querySearch"
47               placeholder="请输入波特率"
48               :disabled="isDisable"
49             >
50               <i class="el-icon-edit el-input__icon" slot="suffix"> </i>
51               <template slot-scope="{ item }">
52                 <div class="name">{{ item.value }}</div>
53                 <span class="addr">{{ item.address }}</span>
54               </template>
55             </el-autocomplete>
56           </el-form-item>
57           <el-form-item label="数据位">
58             <el-select
59               v-model="form1.dataBits"
60               placeholder="请选择数据位"
61               :disabled="isDisable"
62             >
63               <el-option label="7" value="7"></el-option>
64               <el-option label="8" value="8"></el-option>
65             </el-select>
66           </el-form-item>
67           <el-form-item label="停止位">
68             <el-select
69               v-model="form1.stopBits"
70               placeholder="请选择停止位"
71               :disabled="isDisable"
72             >
73               <el-option label="1" value="1"></el-option>
74               <el-option label="2" value="2"></el-option>
75             </el-select>
76           </el-form-item>
77
78           <el-form-item label="校验位">
79             <el-select
80               v-model="form1.parity"
81               placeholder="请选择校验位"
82               :disabled="isDisable"
83             >
84               <el-option label="None" value="none"></el-option>
85               <el-option label="Even" value="even"></el-option>
86               <el-option label="Odd" value="odd"></el-option>
87             </el-select>
88           </el-form-item>
89
90           <el-form-item label="流控制">
91             <el-select
92               v-model="form1.flowControl"
93               placeholder="请选择流控制"
94               :disabled="isDisable"
95             >
96               <el-option label="None" value="none"></el-option>
97               <el-option label="HardWare" value="hardware"></el-option>
98             </el-select>
99           </el-form-item>
100           <el-form-item label="显示历史">
101             <el-switch
102               v-model="form1.isShowHistory"
103               @change="loadHistory"
104             ></el-switch>
105             <el-button
106               type="danger"
107               icon="el-icon-delete"
108               circle
109               title="清空历史"
110               @click="clearHistory"
111             ></el-button>
112           </el-form-item>
113           <el-form-item label="发送区设置" v-show="isShowSendArea">
114             <el-form-item label="发送格式">
115               <el-radio-group v-model="form1.type">
116                 <el-radio label="1">ASCII</el-radio>
117                 <el-radio label="2">HEX</el-radio>
118               </el-radio-group>
119             </el-form-item>
120             <el-form-item label="发送信息">
121               <el-input type="textarea" v-model="form1.sendMsg"></el-input>
122             </el-form-item>
123             <el-button type="primary" @click="sendCommon">发送</el-button>
124           </el-form-item>
125
126           <el-form-item>
127             <el-button :type="btnType" @click="connectBtn">{{
128                 btnText
129               }}</el-button>
130             <el-button type="info" @click="obtainAuthorization"
131             >新增授权</el-button
132             >
133           </el-form-item>
134         </div>
135         </el-col>
136         <el-col :span="10"
137         ><div>
138           <el-form-item label="条码信息">
139             <el-input
140               type="textarea"
141               v-model="form1.desc"
142               disabled
143               :autosize="{ minRows: 21, maxRows: 25 }"
144             ></el-input>
145           </el-form-item></div
146         ></el-col>
147       </el-row>
148     </el-form>
149   </div>
150 </template>
151
152 <script>
153 import MySerialPort from "@/utils/MySerialPort";
154 import USBDevice from "@/utils/usb.json";
155 export default {
156   data() {
157     return {
158       input: "",
159       keepReading: true,
160       form1: {
161         baudRate: "9600",
162         dataBits: "8",
163         stopBits: "1",
164         parity: "none",
165         flowControl: "none",
166         desc: "",
167         type: "1",
168         isShowHistory: false,
169       },
170       btnType: "primary",
171       btnText: "连接串口",
172       restaurants: [],
173       portsList: [],
174       isShowSendArea: false,
175       readType: 1,
176     };
177   },
178   mounted() {
179     if ("serial" in navigator) {
180       this.myserialport = new MySerialPort();
181       this.getPorts();
182       navigator.serial.addEventListener("connect", (e) => {
183         this.$message.success("设备已连接");
184         this.getPorts();
185       });
186       navigator.serial.addEventListener("disconnect", (e) => {
187         this.$message.error("设备已断开");
188       });
189       this.restaurants = this.loadAll();
190     } else {
191       this.$message.error(
192         "当前为HTTP模式或者浏览器版本过低,不支持网页连接串口"
193       );
194     }
195   },
196   computed: {
197     isDisable() {
198       return this.btnType === "danger";
199     },
200   },
201   methods: {
202     //接受数据的回调
203     callBack(value) {
204       if (this.form1.isShowHistory) this.form1.desc = this.readLi().join("");
205       else {
206         if (value.length > 0)
207           this.form1.desc = this.myserialport.hex2atostr(value);
208       }
209     },
210     clearHistory() {
211       this.form1.desc = "";
212       this.myserialport.state.readValue = [];
213     },
214     loadHistory() {
215       if (this.form1.isShowHistory) this.form1.desc = this.readLi().join("");
216       else {
217         let temp = this.readLi();
218         if (temp.length > 0) this.form1.desc = temp[temp.length - 1].join("");
219       }
220     },
221     readLi() {
222       let readType = this.readType;
223       return this.myserialport.state.readValue.map((items, index) => {
224         const item = items.value;
225         const type = items.type; // 1接收,2发送
226         let body = [];
227         if (item !== undefined) {
228           let strArr = [];
229           for (let hex of Array.from(item)) {
230             strArr.push(hex.toString(16).toLocaleUpperCase());
231           }
232           if (strArr.includes("D") && strArr.includes("A")) {
233             if (strArr.indexOf("A") - strArr.indexOf("D") === 1) {
234               strArr.splice(strArr.indexOf("D"), 1);
235               strArr.splice(strArr.indexOf("A"), 1, <br key={0} />);
236             }
237           }
238           strArr = strArr.map((item) => {
239             if (typeof item === "string") {
240               if (readType === 1) {
241                 return this.myserialport.hex2a(parseInt(item, 16));
242               } else if (readType === 2) {
243                 return item + " ";
244               }
245             }
246             return item;
247           });
248           if (typeof strArr[strArr.length - 1] === "string") {
249             strArr.push("\r\n");
250           }
251           body.push(strArr.join(""));
252         }
253         return body;
254       });
255     },
256     //连接
257     async connectBtn() {
258       if (this.btnType === "primary") {
259         try {
260           this.myserialport.state.baudRate = this.form1.baudRate;
261           this.myserialport.state.dataBits = this.form1.dataBits;
262           this.myserialport.state.stopBits = this.form1.stopBits;
263           this.myserialport.state.parity = this.form1.parity;
264           this.myserialport.state.flowControl = this.form1.flowControl;
265           await this.myserialport.openPort(this.form1.port, true, this.callBack);
266         } catch (error) {
267           this.$message.error("串口连接失败!请检查串口是否已被占用");
268         }
269         if (this.myserialport.state.isOpen) {
270           this.$message.success("串口连接成功");
271           this.btnType = "danger";
272           this.btnText = "关闭串口";
273         }
274       } else {
275         this.myserialport.openPort(this.form1.port, false, this.callBack);
276         this.$message.success("串口关闭成功");
277         this.btnType = "primary";
278         this.btnText = "连接串口";
279       }
280     },
281     //授权
282     async obtainAuthorization() {
283       if ("serial" in navigator) {
284         console.log("The Web Serial API is supported.");
285         if (!this.myserialport) this.myserialport = new MySerialPort();
286         try {
287           await this.myserialport.handleRequestPort();
288           this.$message.success("串口授权成功");
289           this.getPortInfo(this.myserialport.state.ports);
290         } catch (error) {
291           this.$message.warning("未选择新串口授权!");
292         }
293       } else {
294         this.$message.error(
295           "当前为HTTP模式或者浏览器版本过低,不支持网页连接串口"
296         );
297       }
298     },
299     //串口列表初始化
300     getPortInfo(portList) {
301       this.portsList = [];
302       portList.map((port, index) => {
303         const { usbProductId, usbVendorId } = port.getInfo();
304         if (usbProductId === undefined || usbVendorId === undefined) {
305           this.portsList.push({ label: "未知设备" + index, value: index });
306         } else {
307           const usbVendor = USBDevice.filter(
308             (item) => parseInt(item.vendor, 16) === usbVendorId
309           );
310           let usbProduct = [];
311           if (usbVendor.length === 1) {
312             usbProduct = usbVendor[0].devices.filter(
313               (item) => parseInt(item.devid, 16) === usbProductId
314             );
315           }
316           this.portsList.push({ label: usbProduct[0].devname, value: index });
317         }
318       });
319     },
320     // 发送
321     async sendCommon() {
322       if (this.myserialport.state.isOpen) {
323         if (this.form1.sendMsg.length !== 0) {
324           const writeType = this.form1.type;
325           let value = this.form1.sendMsg;
326           let arr = [];
327           if (writeType === 1) {
328             // ASCII
329             for (let i = 0; i < value.length; i++) {
330               arr.push(this.myserialport.a2hex(value[i]));
331             }
332           } else if (writeType === 2) {
333             // HEX
334             if (/^[0-9A-Fa-f]+$/.test(value) && value.length % 2 === 0) {
335               for (let i = 0; i < value.length; i = i + 2) {
336                 arr.push(parseInt(value.substring(i, i + 2), 16));
337               }
338             } else {
339               this.$message.error("格式错误");
340               return;
341             }
342           }
343           this.myserialport.writeText(arr);
344         } else {
345           this.$message.warning("请输入发送的信息");
346         }
347       } else {
348         this.$message.warning("串口处于关闭状态,请连接串口");
349       }
350     },
351     async getPorts() {
352       await this.myserialport.getPorts();
353       this.getPortInfo(this.myserialport.state.ports);
354     },
355     querySearch(queryString, cb) {
356       var restaurants = this.restaurants;
357       var results = queryString
358         ? restaurants.filter(this.createFilter(queryString))
359         : restaurants;
360       // 调用 callback 返回建议列表的数据
361       cb(results);
362     },
363     createFilter(queryString) {
364       return (restaurant) => {
365         return (
366           restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) ===
367           0
368         );
369       };
370     },
371     loadAll() {
372       return [
373         { value: "110" },
374         { value: "300" },
375         { value: "600" },
376         { value: "1200" },
377         { value: "2400" },
378         { value: "4800" },
379         { value: "7200" },
380         { value: "9600" },
381         { value: "14400" },
382         { value: "19200" },
383         { value: "28800" },
384         { value: "38400" },
385         { value: "56000" },
386         { value: "57600" },
387         { value: "76800" },
388         { value: "115200" },
389         { value: "230400" },
390         { value: "460800" },
391       ];
392     },
393   },
394 };
395 </script>
396
397 <style scoped>
398 /* ::v-deep .el-textarea__inner {
399   height: 320px !important;
400   width: 80% !important;
401 } */
402 </style>