使用Python3+PyQT5+Pyserial 实现简单的串口工具方法


Posted in Python onFebruary 13, 2019

练手项目,先上图

使用Python3+PyQT5+Pyserial 实现简单的串口工具方法

先实现一个简单的串口工具,为之后的上位机做准备

代码如下:

github 下载地址

pyserial_demo.py

import sys
import serial
import serial.tools.list_ports
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtCore import QTimer
from ui_demo_1 import Ui_Form


class Pyqt5_Serial(QtWidgets.QWidget, Ui_Form):
  def __init__(self):
    super(Pyqt5_Serial, self).__init__()
    self.setupUi(self)
    self.init()
    self.setWindowTitle("串口小助手")
    self.ser = serial.Serial()
    self.port_check()

    # 接收数据和发送数据数目置零
    self.data_num_received = 0
    self.lineEdit.setText(str(self.data_num_received))
    self.data_num_sended = 0
    self.lineEdit_2.setText(str(self.data_num_sended))

  def init(self):
    # 串口检测按钮
    self.s1__box_1.clicked.connect(self.port_check)

    # 串口信息显示
    self.s1__box_2.currentTextChanged.connect(self.port_imf)

    # 打开串口按钮
    self.open_button.clicked.connect(self.port_open)

    # 关闭串口按钮
    self.close_button.clicked.connect(self.port_close)

    # 发送数据按钮
    self.s3__send_button.clicked.connect(self.data_send)

    # 定时发送数据
    self.timer_send = QTimer()
    self.timer_send.timeout.connect(self.data_send)
    self.timer_send_cb.stateChanged.connect(self.data_send_timer)

    # 定时器接收数据
    self.timer = QTimer(self)
    self.timer.timeout.connect(self.data_receive)

    # 清除发送窗口
    self.s3__clear_button.clicked.connect(self.send_data_clear)

    # 清除接收窗口
    self.s2__clear_button.clicked.connect(self.receive_data_clear)

  # 串口检测
  def port_check(self):
    # 检测所有存在的串口,将信息存储在字典中
    self.Com_Dict = {}
    port_list = list(serial.tools.list_ports.comports())
    self.s1__box_2.clear()
    for port in port_list:
      self.Com_Dict["%s" % port[0]] = "%s" % port[1]
      self.s1__box_2.addItem(port[0])
    if len(self.Com_Dict) == 0:
      self.state_label.setText(" 无串口")

  # 串口信息
  def port_imf(self):
    # 显示选定的串口的详细信息
    imf_s = self.s1__box_2.currentText()
    if imf_s != "":
      self.state_label.setText(self.Com_Dict[self.s1__box_2.currentText()])

  # 打开串口
  def port_open(self):
    self.ser.port = self.s1__box_2.currentText()
    self.ser.baudrate = int(self.s1__box_3.currentText())
    self.ser.bytesize = int(self.s1__box_4.currentText())
    self.ser.stopbits = int(self.s1__box_6.currentText())
    self.ser.parity = self.s1__box_5.currentText()

    try:
      self.ser.open()
    except:
      QMessageBox.critical(self, "Port Error", "此串口不能被打开!")
      return None

    # 打开串口接收定时器,周期为2ms
    self.timer.start(2)

    if self.ser.isOpen():
      self.open_button.setEnabled(False)
      self.close_button.setEnabled(True)
      self.formGroupBox1.setTitle("串口状态(已开启)")

  # 关闭串口
  def port_close(self):
    self.timer.stop()
    self.timer_send.stop()
    try:
      self.ser.close()
    except:
      pass
    self.open_button.setEnabled(True)
    self.close_button.setEnabled(False)
    self.lineEdit_3.setEnabled(True)
    # 接收数据和发送数据数目置零
    self.data_num_received = 0
    self.lineEdit.setText(str(self.data_num_received))
    self.data_num_sended = 0
    self.lineEdit_2.setText(str(self.data_num_sended))
    self.formGroupBox1.setTitle("串口状态(已关闭)")

  # 发送数据
  def data_send(self):
    if self.ser.isOpen():
      input_s = self.s3__send_text.toPlainText()
      if input_s != "":
        # 非空字符串
        if self.hex_send.isChecked():
          # hex发送
          input_s = input_s.strip()
          send_list = []
          while input_s != '':
            try:
              num = int(input_s[0:2], 16)
            except ValueError:
              QMessageBox.critical(self, 'wrong data', '请输入十六进制数据,以空格分开!')
              return None
            input_s = input_s[2:].strip()
            send_list.append(num)
          input_s = bytes(send_list)
        else:
          # ascii发送
          input_s = (input_s + '\r\n').encode('utf-8')

        num = self.ser.write(input_s)
        self.data_num_sended += num
        self.lineEdit_2.setText(str(self.data_num_sended))
    else:
      pass

  # 接收数据
  def data_receive(self):
    try:
      num = self.ser.inWaiting()
    except:
      self.port_close()
      return None
    if num > 0:
      data = self.ser.read(num)
      num = len(data)
      # hex显示
      if self.hex_receive.checkState():
        out_s = ''
        for i in range(0, len(data)):
          out_s = out_s + '{:02X}'.format(data[i]) + ' '
        self.s2__receive_text.insertPlainText(out_s)
      else:
        # 串口接收到的字符串为b'123',要转化成unicode字符串才能输出到窗口中去
        self.s2__receive_text.insertPlainText(data.decode('iso-8859-1'))

      # 统计接收字符的数量
      self.data_num_received += num
      self.lineEdit.setText(str(self.data_num_received))

      # 获取到text光标
      textCursor = self.s2__receive_text.textCursor()
      # 滚动到底部
      textCursor.movePosition(textCursor.End)
      # 设置光标到text中去
      self.s2__receive_text.setTextCursor(textCursor)
    else:
      pass

  # 定时发送数据
  def data_send_timer(self):
    if self.timer_send_cb.isChecked():
      self.timer_send.start(int(self.lineEdit_3.text()))
      self.lineEdit_3.setEnabled(False)
    else:
      self.timer_send.stop()
      self.lineEdit_3.setEnabled(True)

  # 清除显示
  def send_data_clear(self):
    self.s3__send_text.setText("")

  def receive_data_clear(self):
    self.s2__receive_text.setText("")


if __name__ == '__main__':
  app = QtWidgets.QApplication(sys.argv)
  myshow = Pyqt5_Serial()
  myshow.show()
  sys.exit(app.exec_())

ui_demo_1.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'demo_1.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Form(object):
  def setupUi(self, Form):
    Form.setObjectName("Form")
    Form.resize(707, 458)
    self.formGroupBox = QtWidgets.QGroupBox(Form)
    self.formGroupBox.setGeometry(QtCore.QRect(20, 20, 167, 301))
    self.formGroupBox.setObjectName("formGroupBox")
    self.formLayout = QtWidgets.QFormLayout(self.formGroupBox)
    self.formLayout.setContentsMargins(10, 10, 10, 10)
    self.formLayout.setSpacing(10)
    self.formLayout.setObjectName("formLayout")
    self.s1__lb_1 = QtWidgets.QLabel(self.formGroupBox)
    self.s1__lb_1.setObjectName("s1__lb_1")
    self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.s1__lb_1)
    self.s1__box_1 = QtWidgets.QPushButton(self.formGroupBox)
    self.s1__box_1.setAutoRepeatInterval(100)
    self.s1__box_1.setDefault(True)
    self.s1__box_1.setObjectName("s1__box_1")
    self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.s1__box_1)
    self.s1__lb_2 = QtWidgets.QLabel(self.formGroupBox)
    self.s1__lb_2.setObjectName("s1__lb_2")
    self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.s1__lb_2)
    self.s1__box_2 = QtWidgets.QComboBox(self.formGroupBox)
    self.s1__box_2.setObjectName("s1__box_2")
    self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.s1__box_2)
    self.s1__lb_3 = QtWidgets.QLabel(self.formGroupBox)
    self.s1__lb_3.setObjectName("s1__lb_3")
    self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.s1__lb_3)
    self.s1__box_3 = QtWidgets.QComboBox(self.formGroupBox)
    self.s1__box_3.setObjectName("s1__box_3")
    self.s1__box_3.addItem("")
    self.s1__box_3.addItem("")
    self.s1__box_3.addItem("")
    self.s1__box_3.addItem("")
    self.s1__box_3.addItem("")
    self.s1__box_3.addItem("")
    self.s1__box_3.addItem("")
    self.s1__box_3.addItem("")
    self.s1__box_3.addItem("")
    self.s1__box_3.addItem("")
    self.s1__box_3.addItem("")
    self.s1__box_3.addItem("")
    self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.s1__box_3)
    self.s1__lb_4 = QtWidgets.QLabel(self.formGroupBox)
    self.s1__lb_4.setObjectName("s1__lb_4")
    self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.s1__lb_4)
    self.s1__box_4 = QtWidgets.QComboBox(self.formGroupBox)
    self.s1__box_4.setObjectName("s1__box_4")
    self.s1__box_4.addItem("")
    self.s1__box_4.addItem("")
    self.s1__box_4.addItem("")
    self.s1__box_4.addItem("")
    self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.s1__box_4)
    self.s1__lb_5 = QtWidgets.QLabel(self.formGroupBox)
    self.s1__lb_5.setObjectName("s1__lb_5")
    self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.s1__lb_5)
    self.s1__box_5 = QtWidgets.QComboBox(self.formGroupBox)
    self.s1__box_5.setObjectName("s1__box_5")
    self.s1__box_5.addItem("")
    self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.s1__box_5)
    self.open_button = QtWidgets.QPushButton(self.formGroupBox)
    self.open_button.setObjectName("open_button")
    self.formLayout.setWidget(7, QtWidgets.QFormLayout.SpanningRole, self.open_button)
    self.close_button = QtWidgets.QPushButton(self.formGroupBox)
    self.close_button.setObjectName("close_button")
    self.formLayout.setWidget(8, QtWidgets.QFormLayout.SpanningRole, self.close_button)
    self.s1__lb_6 = QtWidgets.QLabel(self.formGroupBox)
    self.s1__lb_6.setObjectName("s1__lb_6")
    self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.s1__lb_6)
    self.s1__box_6 = QtWidgets.QComboBox(self.formGroupBox)
    self.s1__box_6.setObjectName("s1__box_6")
    self.s1__box_6.addItem("")
    self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.s1__box_6)
    self.state_label = QtWidgets.QLabel(self.formGroupBox)
    self.state_label.setText("")
    self.state_label.setTextFormat(QtCore.Qt.AutoText)
    self.state_label.setScaledContents(True)
    self.state_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
    self.state_label.setObjectName("state_label")
    self.formLayout.setWidget(2, QtWidgets.QFormLayout.SpanningRole, self.state_label)
    self.verticalGroupBox = QtWidgets.QGroupBox(Form)
    self.verticalGroupBox.setGeometry(QtCore.QRect(210, 20, 401, 241))
    self.verticalGroupBox.setObjectName("verticalGroupBox")
    self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalGroupBox)
    self.verticalLayout.setContentsMargins(10, 10, 10, 10)
    self.verticalLayout.setObjectName("verticalLayout")
    self.s2__receive_text = QtWidgets.QTextBrowser(self.verticalGroupBox)
    self.s2__receive_text.setObjectName("s2__receive_text")
    self.verticalLayout.addWidget(self.s2__receive_text)
    self.verticalGroupBox_2 = QtWidgets.QGroupBox(Form)
    self.verticalGroupBox_2.setGeometry(QtCore.QRect(210, 280, 401, 101))
    self.verticalGroupBox_2.setObjectName("verticalGroupBox_2")
    self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalGroupBox_2)
    self.verticalLayout_2.setContentsMargins(10, 10, 10, 10)
    self.verticalLayout_2.setObjectName("verticalLayout_2")
    self.s3__send_text = QtWidgets.QTextEdit(self.verticalGroupBox_2)
    self.s3__send_text.setObjectName("s3__send_text")
    self.verticalLayout_2.addWidget(self.s3__send_text)
    self.s3__send_button = QtWidgets.QPushButton(Form)
    self.s3__send_button.setGeometry(QtCore.QRect(620, 310, 61, 31))
    self.s3__send_button.setObjectName("s3__send_button")
    self.s3__clear_button = QtWidgets.QPushButton(Form)
    self.s3__clear_button.setGeometry(QtCore.QRect(620, 350, 61, 31))
    self.s3__clear_button.setObjectName("s3__clear_button")
    self.formGroupBox1 = QtWidgets.QGroupBox(Form)
    self.formGroupBox1.setGeometry(QtCore.QRect(20, 340, 171, 101))
    self.formGroupBox1.setObjectName("formGroupBox1")
    self.formLayout_2 = QtWidgets.QFormLayout(self.formGroupBox1)
    self.formLayout_2.setContentsMargins(10, 10, 10, 10)
    self.formLayout_2.setSpacing(10)
    self.formLayout_2.setObjectName("formLayout_2")
    self.label = QtWidgets.QLabel(self.formGroupBox1)
    self.label.setObjectName("label")
    self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)
    self.label_2 = QtWidgets.QLabel(self.formGroupBox1)
    self.label_2.setObjectName("label_2")
    self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_2)
    self.lineEdit = QtWidgets.QLineEdit(self.formGroupBox1)
    self.lineEdit.setObjectName("lineEdit")
    self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.lineEdit)
    self.lineEdit_2 = QtWidgets.QLineEdit(self.formGroupBox1)
    self.lineEdit_2.setObjectName("lineEdit_2")
    self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.lineEdit_2)
    self.hex_send = QtWidgets.QCheckBox(Form)
    self.hex_send.setGeometry(QtCore.QRect(620, 280, 71, 16))
    self.hex_send.setObjectName("hex_send")
    self.hex_receive = QtWidgets.QCheckBox(Form)
    self.hex_receive.setGeometry(QtCore.QRect(620, 40, 71, 16))
    self.hex_receive.setObjectName("hex_receive")
    self.s2__clear_button = QtWidgets.QPushButton(Form)
    self.s2__clear_button.setGeometry(QtCore.QRect(620, 80, 61, 31))
    self.s2__clear_button.setObjectName("s2__clear_button")
    self.timer_send_cb = QtWidgets.QCheckBox(Form)
    self.timer_send_cb.setGeometry(QtCore.QRect(260, 390, 71, 16))
    self.timer_send_cb.setObjectName("timer_send_cb")
    self.lineEdit_3 = QtWidgets.QLineEdit(Form)
    self.lineEdit_3.setGeometry(QtCore.QRect(350, 390, 61, 20))
    self.lineEdit_3.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
    self.lineEdit_3.setObjectName("lineEdit_3")
    self.dw = QtWidgets.QLabel(Form)
    self.dw.setGeometry(QtCore.QRect(420, 390, 54, 20))
    self.dw.setObjectName("dw")
    self.verticalGroupBox.raise_()
    self.verticalGroupBox_2.raise_()
    self.formGroupBox.raise_()
    self.s3__send_button.raise_()
    self.s3__clear_button.raise_()
    self.formGroupBox.raise_()
    self.hex_send.raise_()
    self.hex_receive.raise_()
    self.s2__clear_button.raise_()
    self.timer_send_cb.raise_()
    self.lineEdit_3.raise_()
    self.dw.raise_()

    self.retranslateUi(Form)
    QtCore.QMetaObject.connectSlotsByName(Form)

  def retranslateUi(self, Form):
    _translate = QtCore.QCoreApplication.translate
    Form.setWindowTitle(_translate("Form", "Form"))
    self.formGroupBox.setTitle(_translate("Form", "串口设置"))
    self.s1__lb_1.setText(_translate("Form", "串口检测:"))
    self.s1__box_1.setText(_translate("Form", "检测串口"))
    self.s1__lb_2.setText(_translate("Form", "串口选择:"))
    self.s1__lb_3.setText(_translate("Form", "波特率:"))
    self.s1__box_3.setItemText(0, _translate("Form", "115200"))
    self.s1__box_3.setItemText(1, _translate("Form", "2400"))
    self.s1__box_3.setItemText(2, _translate("Form", "4800"))
    self.s1__box_3.setItemText(3, _translate("Form", "9600"))
    self.s1__box_3.setItemText(4, _translate("Form", "14400"))
    self.s1__box_3.setItemText(5, _translate("Form", "19200"))
    self.s1__box_3.setItemText(6, _translate("Form", "38400"))
    self.s1__box_3.setItemText(7, _translate("Form", "57600"))
    self.s1__box_3.setItemText(8, _translate("Form", "76800"))
    self.s1__box_3.setItemText(9, _translate("Form", "12800"))
    self.s1__box_3.setItemText(10, _translate("Form", "230400"))
    self.s1__box_3.setItemText(11, _translate("Form", "460800"))
    self.s1__lb_4.setText(_translate("Form", "数据位:"))
    self.s1__box_4.setItemText(0, _translate("Form", "8"))
    self.s1__box_4.setItemText(1, _translate("Form", "7"))
    self.s1__box_4.setItemText(2, _translate("Form", "6"))
    self.s1__box_4.setItemText(3, _translate("Form", "5"))
    self.s1__lb_5.setText(_translate("Form", "校验位:"))
    self.s1__box_5.setItemText(0, _translate("Form", "N"))
    self.open_button.setText(_translate("Form", "打开串口"))
    self.close_button.setText(_translate("Form", "关闭串口"))
    self.s1__lb_6.setText(_translate("Form", "停止位:"))
    self.s1__box_6.setItemText(0, _translate("Form", "1"))
    self.verticalGroupBox.setTitle(_translate("Form", "接受区"))
    self.verticalGroupBox_2.setTitle(_translate("Form", "发送区"))
    self.s3__send_text.setHtml(_translate("Form", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">123456</p></body></html>"))
    self.s3__send_button.setText(_translate("Form", "发送"))
    self.s3__clear_button.setText(_translate("Form", "清除"))
    self.formGroupBox1.setTitle(_translate("Form", "串口状态"))
    self.label.setText(_translate("Form", "已接收:"))
    self.label_2.setText(_translate("Form", "已发送:"))
    self.hex_send.setText(_translate("Form", "Hex发送"))
    self.hex_receive.setText(_translate("Form", "Hex接收"))
    self.s2__clear_button.setText(_translate("Form", "清除"))
    self.timer_send_cb.setText(_translate("Form", "定时发送"))
    self.lineEdit_3.setText(_translate("Form", "1000"))
    self.dw.setText(_translate("Form", "ms/次"))

以上这篇使用Python3+PyQT5+Pyserial 实现简单的串口工具方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python中的闭包实例详解
Aug 29 Python
详解Django框架中用户的登录和退出的实现
Jul 23 Python
老生常谈Python基础之字符编码
Jun 14 Python
关于Django外键赋值问题详解
Aug 13 Python
利用Hyperic调用Python实现进程守护
Jan 02 Python
Python批量查询关键词微信指数实例方法
Jun 27 Python
Django model update的多种用法介绍
Mar 28 Python
python3格式化字符串 f-string的高级用法(推荐)
Mar 04 Python
解决Pycharm双击图标启动不了的问题(JetBrains全家桶通用)
Aug 07 Python
Python中zipfile压缩包模块的使用
May 14 Python
Python 类,对象,数据分类,函数参数传递详解
Sep 25 Python
Python采集股票数据并制作可视化柱状图
Apr 04 Python
PyQt5内嵌浏览器注入JavaScript脚本实现自动化操作的代码实例
Feb 13 #Python
Python实现Event回调机制的方法
Feb 13 #Python
Python socket实现多对多全双工通信的方法
Feb 13 #Python
对python文件读写的缓冲行为详解
Feb 13 #Python
python单线程文件传输的实例(C/S)
Feb 13 #Python
Python 实现文件打包、上传与校验的方法
Feb 13 #Python
使用python3构建文件传输的方法
Feb 13 #Python
You might like
打造计数器DIY三步曲(中)
2006/10/09 PHP
Dedecms常用函数解析
2008/02/01 PHP
PHP MemCached高级缓存配置图文教程
2010/08/05 PHP
PDO防注入原理分析以及使用PDO的注意事项总结
2014/10/23 PHP
PHP 中TP5 Request 请求对象的实例详解
2017/07/31 PHP
php自动加载代码实例详解
2021/02/26 PHP
几款极品的javascript压缩混淆工具
2007/05/16 Javascript
window.open以post方式将内容提交到新窗口
2012/12/26 Javascript
jQuery之过滤元素操作小结
2013/11/30 Javascript
Javascript仿PHP $_GET获取URL中的参数
2014/05/12 Javascript
jQuery中hover方法和toggle方法使用指南
2015/02/27 Javascript
JS实现的数组全排列输出算法
2015/03/19 Javascript
3种js实现string的substring方法
2015/11/09 Javascript
javascript cookie的简单应用
2016/02/24 Javascript
微信小程序 Image API实例详解
2016/09/30 Javascript
Node.js使用cookie保持登录的方法
2018/05/11 Javascript
详解Vue项目中出现Loading chunk {n} failed问题的解决方法
2018/09/14 Javascript
Vue和React组件之间的传值方式详解
2019/01/31 Javascript
基于vue-cli、elementUI的Vue超简单入门小例子(推荐)
2019/04/17 Javascript
如何实现echarts markline标签名显示自己想要的
2020/07/20 Javascript
Javascript中Math.max和Math.max.apply的区别和用法详解
2020/08/24 Javascript
js实现简单的轮播图效果
2020/12/13 Javascript
pandas将DataFrame的列变成行索引的方法
2018/04/10 Python
Python实现的爬取网易动态评论操作示例
2018/06/06 Python
python matlibplot绘制多条曲线图
2021/02/19 Python
Django url,从一个页面调到另个页面的方法
2019/08/21 Python
python import 上级目录的导入
2020/11/03 Python
canvas 绘图时位置偏离的问题解决
2020/09/16 HTML / CSS
一套软件测试笔试题
2014/07/25 面试题
Ajxa常见问题都有哪些
2014/03/26 面试题
请说出这段代码执行后a和b的值分别是多少
2015/03/28 面试题
高中数学教师求职信
2013/10/30 职场文书
促销活动总结报告
2014/04/26 职场文书
红高粱观后感
2015/06/10 职场文书
简历中的自我评价怎么写呢?
2019/04/30 职场文书
Vue接口封装的完整步骤记录
2021/05/14 Vue.js