《 QT5.9 c++ 开发指南》玩转数据库 QSqITableModel实例

Qt SQL 支持的数据库

       QtSQL 提供 了一些常见数据库的驱动,包括网络型数据库,如 Oracle 、 MS SQL Server 等 ,
也包括简单的单机型数据库 ,如 SQLite , Qt SQL 提供的数据库驱动


       
       

QtSQL 模块的主要类

界面布局:

菜单布局:

界面布局部分先介绍这么多,下面来看程序部分:

首先要在工程项目中添加

QT       += sql

先让我们看看主程序:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include    <QMainWindow>

#include    <QLabel>
#include    <QString>

#include    <QtSql>
#include    <QDataWidgetMapper>

#include    "qwcomboboxdelegate.h"


namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    QSqlDatabase  DB;//数据库连接

    QSqlTableModel  *tabModel;  //数据模型

    QItemSelectionModel *theSelection; //选择模型

    QDataWidgetMapper   *dataMapper; //数据映射

    QWComboBoxDelegate   delegateSex; //自定义数据代理,性别
    QWComboBoxDelegate   delegateDepart; //自定义数据代理,部门

    void    openTable();//打开数据表
    void    getFieldNames();//获取字段名称,填充“排序字段”的comboBox
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
//    void on_primeInsert(int row, QSqlRecord &record);//插入记录时,用于设置缺省字段数据

//
    void on_currentChanged(const QModelIndex &current, const QModelIndex &previous);

// QTableView的SelectionModel的行发生了变化,进行处理
    void on_currentRowChanged(const QModelIndex &current, const QModelIndex &previous);


///
    void on_actOpenDB_triggered();


    void on_actRecAppend_triggered();

    void on_actRecInsert_triggered();

    void on_actRevert_triggered();

    void on_actSubmit_triggered();

    void on_actRecDelete_triggered();

    void on_actPhoto_triggered();

    void on_actPhotoClear_triggered();

    void on_radioBtnAscend_clicked();

    void on_radioBtnDescend_clicked();

//    void on_radioButton_3_clicked();

    void on_radioBtnMan_clicked();

    void on_radioBtnWoman_clicked();

    void on_radioBtnBoth_clicked();

    void on_comboFields_currentIndexChanged(int index);

    void on_actScan_triggered();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

对于MainWindow 类中定义了 几个私有变量。

  • QSqlDatabase  DB , 用于加载数据库驱动和建立与数据库之间的连接。
  • QSqlTableModel  *tabModel ,用于指定某一个数据表 ,作为数据表的数据模型 。
  • QltemSelectionModel  *theSelection,作为 tabMode l 的选择模型,提供 currentChanged () 、currentRowChanged ()等信号 ,在 tabModel 选择的字段发生变化、当前记录发生变化时发射信号,以便程序进行响应 。 例如在 currentChanged()信号发射时,检查 tabModel 是否有数据被修改,从而更新界面上 “保存”和“取消” 两个按钮的使能状态 
  • QDataWidgetMapper *dataMapper 用于实现界面组件与 tabMode l 的字段之间 的映射 。
  • QWComboBoxDelegate 是自定义的基于 QComboBox 的代理类 

定义了两个槽函数 , 功能如下。

  • on_ currentChanged()用于检查数据表内容是否有修改,从而更新“保存”和“取消”两个按钮的使能状态 。
  • on_ currentRowChanged()用于在当前记录发生变化时,从新的当前记录里提取 Photo 字段的内容,并作为界面上的 QLabel 类型的 dblabPhoto 组件的 pixmap 显示出来 。
     

先看构造函数吧:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    this->setCentralWidget(ui->splitter);

    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
    ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableView->setAlternatingRowColors(true);

}

打开数据表:

void MainWindow::on_actOpenDB_triggered()
{//打开数据表
    QString aFile=QFileDialog::getOpenFileName(this,"选择数据库文件","",
                             "SQL Lite数据库(*.db *.db3)");
    if (aFile.isEmpty())  //选择SQL Lite数据库文件
       return;

//打开数据库
    DB=QSqlDatabase::addDatabase("QSQLITE"); //添加 SQL LITE数据库驱动
    DB.setDatabaseName(aFile); //设置数据库名称
    if (!DB.open())   //打开数据库
    {
        QMessageBox::warning(this, "错误", "打开数据库失败",
                                 QMessageBox::Ok,QMessageBox::NoButton);
        return;
    }

//打开数据表
    openTable();
}

本实例中需要打开 demodb.db3 数据库 (例子在最后)


       函数 openTable()主要是创建 QSqITableModel 类型的私有变量 tabModel ,指定需要打开的数据
表为 employee,设置与界面显示组件的关联等

void MainWindow::openTable()
{//打开数据表
    tabModel=new QSqlTableModel(this,DB);//数据表
    tabModel->setTable("employee"); //设置数据表
    tabModel->setEditStrategy(QSqlTableModel::OnManualSubmit);//数据保存方式,OnManualSubmit , OnRowChange
    tabModel->setSort(tabModel->fieldIndex("empNo"),Qt::AscendingOrder); //排序
    if (!(tabModel->select()))//查询数据
    {
       QMessageBox::critical(this, "错误信息",
              "打开数据表错误,错误信息\n"+tabModel->lastError().text(),
                 QMessageBox::Ok,QMessageBox::NoButton);
       return;
    }

//字段显示名
    tabModel->setHeaderData(tabModel->fieldIndex("empNo"),Qt::Horizontal,"工号");
    tabModel->setHeaderData(tabModel->fieldIndex("Name"),Qt::Horizontal,"姓名");
    tabModel->setHeaderData(tabModel->fieldIndex("Gender"),Qt::Horizontal,"性别");
    tabModel->setHeaderData(tabModel->fieldIndex("Height"),Qt::Horizontal,"身高");
    tabModel->setHeaderData(tabModel->fieldIndex("Birthday"),Qt::Horizontal,"出生日期");
    tabModel->setHeaderData(tabModel->fieldIndex("Mobile"),Qt::Horizontal,"手机");
    tabModel->setHeaderData(tabModel->fieldIndex("Province"),Qt::Horizontal,"省份");
    tabModel->setHeaderData(tabModel->fieldIndex("City"),Qt::Horizontal,"城市");
    tabModel->setHeaderData(tabModel->fieldIndex("Department"),Qt::Horizontal,"部门");
    tabModel->setHeaderData(tabModel->fieldIndex("Education"),Qt::Horizontal,"学历");
    tabModel->setHeaderData(tabModel->fieldIndex("Salary"),Qt::Horizontal,"工资");

    tabModel->setHeaderData(tabModel->fieldIndex("Memo"),Qt::Horizontal,"备注"); //这两个字段不再tableView中显示
    tabModel->setHeaderData(tabModel->fieldIndex("Photo"),Qt::Horizontal,"照片");

    theSelection=new QItemSelectionModel(tabModel);//关联选择模型
//theSelection当前项变化时触发currentChanged信号
    connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
            this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));
//选择行变化时
    connect(theSelection,SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
            this,SLOT(on_currentRowChanged(QModelIndex,QModelIndex)));

    ui->tableView->setModel(tabModel);//设置数据模型
    ui->tableView->setSelectionModel(theSelection); //设置选择模型
    ui->tableView->setColumnHidden(tabModel->fieldIndex("Memo"),true);//隐藏列
    ui->tableView->setColumnHidden(tabModel->fieldIndex("Photo"),true);//隐藏列

//tableView上为“性别”和“部门”两个字段设置自定义代理组件
    QStringList strList;
    strList<<"男"<<"女";
    bool isEditable=false;
    delegateSex.setItems(strList,isEditable);
    ui->tableView->setItemDelegateForColumn(
       tabModel->fieldIndex("Gender"),&delegateSex); //Combbox选择型

    strList.clear();
    strList<<"销售部"<<"技术部"<<"生产部"<<"行政部";
    isEditable=true;
    delegateDepart.setItems(strList,isEditable);
    ui->tableView->setItemDelegateForColumn(tabModel->fieldIndex("Department"),&delegateDepart); //Combbox选择型

//创建界面组件与数据模型的字段之间的数据映射
    dataMapper= new QDataWidgetMapper();
    dataMapper->setModel(tabModel);//设置数据模型
    dataMapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);//

//    dataMapper->setItemDelegate(new QSqlRelationalDelegate(this)); //含有外键的
//界面组件与tabModel的具体字段之间的联系
    dataMapper->addMapping(ui->dbSpinEmpNo,tabModel->fieldIndex("empNo"));
    dataMapper->addMapping(ui->dbEditName,tabModel->fieldIndex("Name"));
    dataMapper->addMapping(ui->dbComboSex,tabModel->fieldIndex("Gender"));

    dataMapper->addMapping(ui->dbSpinHeight,tabModel->fieldIndex("Height"));
    dataMapper->addMapping(ui->dbEditBirth,tabModel->fieldIndex("Birthday"));
    dataMapper->addMapping(ui->dbEditMobile,tabModel->fieldIndex("Mobile"));

    dataMapper->addMapping(ui->dbComboProvince,tabModel->fieldIndex("Province"));
    dataMapper->addMapping(ui->dbEditCity,tabModel->fieldIndex("City"));
    dataMapper->addMapping(ui->dbComboDep,tabModel->fieldIndex("Department"));

    dataMapper->addMapping(ui->dbComboEdu,tabModel->fieldIndex("Education"));
    dataMapper->addMapping(ui->dbSpinSalary,tabModel->fieldIndex("Salary"));

    dataMapper->addMapping(ui->dbEditMemo,tabModel->fieldIndex("Memo"));

//    dataMapper->addMapping(ui->dbPhoto,tabModel->fieldIndex("Photo")); //图片无法直接映射

    dataMapper->toFirst();//移动到首记录

    getFieldNames();//获取字段名称列表,填充ui->groupBoxSort组件

//更新actions和界面组件的使能状态
    ui->actOpenDB->setEnabled(false);

    ui->actRecAppend->setEnabled(true);
    ui->actRecInsert->setEnabled(true);
    ui->actRecDelete->setEnabled(true);
    ui->actScan->setEnabled(true);

    ui->groupBoxSort->setEnabled(true);
    ui->groupBoxFilter->setEnabled(true);
}

剩下的部分: 

对应的知识点可以看帮助手册:

取值 Qt::AscendingOrder表示升序 , Qt: :DescendingOrder 表示降序。

QSqITableModel: :setEditStrategy()函数用于设置编辑策略:

  • QSqlTableModel: : OnFieldChange,字段值变化时立即更新到数据库 。
  • QSqlTableModel:: OnRowChange,当前行(就是记录)变换时更新到数据库 。
  • QSqlTableModel:: OnManualSubmit,所有修改暂时缓存,手动调用 submitAll()保存所有修改 , 或调用 revertAll ()函数取消所有未保存修改。

QSqlTableModel::setHeaderData()函数用于设置每个字段 的表头数据 , 主要是设置显示标题。如果不进行表头设置,在 TableView 里显示时将显示字段名作为表头。为此,将每个字段的显示设置为相应的中文标题。

选择模型及其信号的作用

theSelection=new QitemSelectionModel(tabModel);

    选择模型的作用是当用户在 TableView 组件上操作时 , 获取当前选择的行、列信息,并且在选择的单元格变化时发射 currentChanged()信号,在当前行变化时发射 currentR.owChanged()信号。
 

void MainWindow::on_currentChanged(const QModelIndex &current, const QModelIndex &previous)
{//更新actPost和actCancel 的状态
    Q_UNUSED(current);
    Q_UNUSED(previous);
    ui->actSubmit->setEnabled(tabModel->isDirty()); //有未保存修改时可用
    ui->actRevert->setEnabled(tabModel->isDirty());

//    if (current.isValid())
//        ui->actRecDelete->setEnabled(true);
//    else
    //        ui->actRecDelete->setEnabled(false);
}
void MainWindow::on_currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
{
    Q_UNUSED(previous);
// 行切换时的状态控制
    ui->actRecDelete->setEnabled(current.isValid());
    ui->actPhoto->setEnabled(current.isValid());
    ui->actPhotoClear->setEnabled(current.isValid());

    if (!current.isValid())
    {
        ui->dbLabPhoto->clear(); //清除图片显示
        return;
    }

    dataMapper->setCurrentIndex(current.row()); //更细数据映射的行号

//    QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
    int curRecNo=current.row();//获取行号
    QSqlRecord  curRec=tabModel->record(curRecNo); //获取当前记录

    if (curRec.isNull("Photo"))  //图片字段内容为空
       ui->dbLabPhoto->clear();
    else
    {
        QByteArray data=curRec.value("Photo").toByteArray();
        QPixmap pic;
        pic.loadFromData(data);
        ui->dbLabPhoto->setPixmap(pic.scaledToWidth(ui->dbLabPhoto->size().width()));
    }
}

表示记录的类 QSqlRecord

       QSqlRecord 类记录了数据表 的字段信息和 一条记录的数据内容, QSqlTableModel 有两种参数的函数 record()可以返回一条记录。

      QSq!Record QSqlTableModel::record(int row),返 回行号为 row 的记录,包括记录的字段定义和数据 。

表示字段的类 QSqlField

 QWComboBoxDelegate 类的设计:

#ifndef QWCOMBOBOXDELEGATE_H
#define QWCOMBOBOXDELEGATE_H

#include    <QStyledItemDelegate>
#include    <QComboBox>

class QWComboBoxDelegate : public QStyledItemDelegate
{
    Q_OBJECT

private:
    QStringList m_ItemList;//选择列表
    bool    m_isEdit; //是否可编辑

public:
    QWComboBoxDelegate(QObject *parent=0);

    void    setItems(QStringList items, bool isEdit);//初始化设置列表内容,是否可编辑
//    void    clearItems();
//自定义代理组件必须继承以下4个函数
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const Q_DECL_OVERRIDE;

    void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;
    void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const Q_DECL_OVERRIDE;
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
                              const QModelIndex &index) const Q_DECL_OVERRIDE;
};

#endif // QWCOMBOBOXDELEGATE_H

对应的cpp:

#include "qwcomboboxdelegate.h"

#include    <QComboBox>

QWComboBoxDelegate::QWComboBoxDelegate(QObject *parent):QStyledItemDelegate(parent)
{

}

void QWComboBoxDelegate::setItems(QStringList items, bool isEdit)
{
    m_ItemList=items;
    m_isEdit=isEdit;
}

QWidget *QWComboBoxDelegate::createEditor(QWidget *parent,
       const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(option);
    Q_UNUSED(index);

    QComboBox *editor = new QComboBox(parent);
    for (int i=0;i<m_ItemList.count();i++)   //从字符串列表初始下拉列表
        editor->addItem(m_ItemList.at(i));

    editor->setEditable(m_isEdit); //是否可编辑
    return editor;
}

void QWComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    QString str = index.model()->data(index, Qt::EditRole).toString();

    QComboBox *comboBox = static_cast<QComboBox*>(editor);
    comboBox->setCurrentText(str);
}

void QWComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    QComboBox *comboBox = static_cast<QComboBox*>(editor);
    QString str = comboBox->currentText();
    model->setData(index, str, Qt::EditRole);
}

void QWComboBoxDelegate::updateEditorGeometry(QWidget *editor,
                const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(index);
    editor->setGeometry(option.rect);
}

数据映射

       QDataWidgetMapper 用于建立界面组件与数据模型之间的 映射,可 以将界面的 QLineEdit 、QCombobox 等组件与数据模型的一个字段关联起来。

获取数据表的所有字段名称

      自定义函数 getfieldNames()获取数据表的所有字段名称,并填充到界面上的“排序宇段”下拉列表框里 , 用于选择排序字段 。 getFieldNames()的实现代码如下:

void MainWindow::getFieldNames()
{ //获取所有字段名称
    QSqlRecord  emptyRec=tabModel->record();//获取空记录,只有字段名
    for (int i=0;i<emptyRec.count();i++)
        ui->comboFields->addItem(emptyRec.fieldName(i));
}

添加、插入与删除记录

添加: 

void MainWindow::on_actRecAppend_triggered()
{//添加记录
    tabModel->insertRow(tabModel->rowCount(),QModelIndex()); //在末尾添加一个记录

    QModelIndex curIndex=tabModel->index(tabModel->rowCount()-1,1);//创建最后一行的ModelIndex
    theSelection->clearSelection();//清空选择项
    theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);//设置刚插入的行为当前选择行

    int currow=curIndex.row(); //获得当前行
    tabModel->setData(tabModel->index(currow,0),2000+tabModel->rowCount()); //自动生成编号
    tabModel->setData(tabModel->index(currow,2),"男");
// 插入行时设置缺省值,需要在primeInsert()信号里去处理
}

插入: 

void MainWindow::on_actRecInsert_triggered()
{//插入记录
    QModelIndex curIndex=ui->tableView->currentIndex();

    tabModel->insertRow(curIndex.row(),QModelIndex());

    theSelection->clearSelection();//清除已有选择
    theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);

//    ui->actPost->setEnabled(true);
//    ui->actCancel->setEnabled(true);
}

删除: 

void MainWindow::on_actRecDelete_triggered()
{//删除当前记录

    QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
    tabModel->removeRow(curIndex.row()); //删除最后一行

}

保存与取消修改

void MainWindow::on_actRevert_triggered()
{//取消修改
    tabModel->revertAll();
    ui->actSubmit->setEnabled(false);
    ui->actRevert->setEnabled(false);
}

void MainWindow::on_actSubmit_triggered()
{//保存修改
    bool res=tabModel->submitAll();

    if (!res)
        QMessageBox::information(this, "消息", "数据保存错误,错误信息\n"+tabModel->lastError().text(),
                                 QMessageBox::Ok,QMessageBox::NoButton);
    else
    {
        ui->actSubmit->setEnabled(false);
        ui->actRevert->setEnabled(false);
    }
}

      QSqlTableModel : :submitAll () 函数用于将数据表所有未提交的修改保存到数据库, revertAll ()函数取消所有修改。
调用 submitAll ()函数保存数据时如果失败 , 可以通过 lastError()函数获取错误的具体信息,例如 , Name 是必填字段 , 添加记录时若没有填写 Name 字段 的 内容就提交 , 就会出现错误信息提示对话框。
     

设置和清除照片

       employee 数据表的 Photo 字段是 BLOB 字段,用于存储图片文件。 Photo 宇段内容的显示己经在自定义槽函数 on_cutTentRowChanged()里实现了,就是在当前记录变化时提取 Photo 字段的内容 , 井显示为 dbLabPhoto 的 pixmap 。

void MainWindow::on_actPhoto_triggered()
{
//设置照片
    QString aFile=QFileDialog::getOpenFileName(this,"选择图片文件","","照片(*.jpg)");
    if (aFile.isEmpty())
       return;

    QByteArray data;
    QFile* file=new QFile(aFile); //fileName为二进制数据文件名
    file->open(QIODevice::ReadOnly);
    data = file->readAll();
    file->close();

//    QVariant photo(data); //

//    QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
    int curRecNo=theSelection->currentIndex().row();
    QSqlRecord  curRec=tabModel->record(curRecNo); //获取当前记录
    curRec.setValue("Photo",data); //设置字段数据
    tabModel->setRecord(curRecNo,curRec);
//    tabModel->submit(); //并不能保存到数据库

    QPixmap pic;
    pic.load(aFile);  //在界面上显示
    ui->dbLabPhoto->setPixmap(pic.scaledToWidth(ui->dbLabPhoto->width()));
}

     代码功能是用文件对话框选择一个图片文件 , 读取文件内容到 QByteArray 类型的变量 data里 , 获取当前记录到变量 curRec 后,用 QSqlRecord 的 setValue()函数为 Photo 字段设置数据为 data,然后用 setRecord()函数更新当前记录。

void MainWindow::on_actPhotoClear_triggered()
{
//    QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
    int curRecNo=theSelection->currentIndex().row();
    QSqlRecord  curRec=tabModel->record(curRecNo); //获取当前记录

    curRec.setNull("Photo");//设置为空值
    tabModel->setRecord(curRecNo,curRec);
//    tabModel->submit(); //并不能保存到数据库

    ui->dbLabPhoto->clear();
}

这段函数为清除图片。

数据记录的遍历

void MainWindow::on_actScan_triggered()
{//涨工资,记录遍历
    if (tabModel->rowCount()==0)
        return;

//    QSqlRecord  aRec;   //这种方式速度慢,因为需要提取记录,并且记录含有BLOB字段
//    float   salary;
    for (int i=0;i<tabModel->rowCount();i++)
    {
        QSqlRecord aRec=tabModel->record(i); //获取当前记录
        float salary=aRec.value("Salary").toFloat();
        salary=salary*1.1;
        aRec.setValue("Salary",salary);
        tabModel->setRecord(i,aRec);
    }

// 索引方式刷新记录,速度一样
//    float   salary;
//    for (int i=0;i<tabModel->rowCount();i++)
//    {
//        salary=tabModel->data(tabModel->index(i,10)).toFloat();
//        salary=salary*1.1;
//        tabModel->setData(tabModel->index(i,10),salary);
//    }

    if (tabModel->submitAll())
        QMessageBox::information(this, "消息", "涨工资计算完毕",
                             QMessageBox::Ok,QMessageBox::NoButton);
}

记录排序与记录过滤

void MainWindow::on_radioBtnAscend_clicked()
{//升序
    tabModel->setSort(ui->comboFields->currentIndex(),Qt::AscendingOrder);
    tabModel->select();
}

void MainWindow::on_radioBtnDescend_clicked()
{//降序
    tabModel->setSort(ui->comboFields->currentIndex(),Qt::DescendingOrder);
    tabModel->select();
}

void MainWindow::on_radioBtnMan_clicked()
{
    tabModel->setFilter(" Gender='男' ");
//    tabModel->select();
}

void MainWindow::on_radioBtnWoman_clicked()
{
    tabModel->setFilter(" Gender='女' ");
//    tabModel->select();
}

void MainWindow::on_radioBtnBoth_clicked()
{
    tabModel->setFilter("");
//    tabModel->setFilter(" Gender='男' or Gender='女' ");
//    tabModel->setTable("employee");//重新设置tableName并打开,会取消所有初始设置
//    tabModel->select();
}

void MainWindow::on_comboFields_currentIndexChanged(int index)
{//选择字段进行排序
    if (ui->radioBtnAscend->isChecked())
        tabModel->setSort(index,Qt::AscendingOrder);
    else
        tabModel->setSort(index,Qt::DescendingOrder);

    tabModel->select();
}

       好了已上就是本章的内容了,有关数据库的类容,如果不会的话,那就去学呀,哈哈,可以的,本例子放在了下面了,喜欢的同学可以下载下来:

 链接:本程序的例子  提取码:isz6 
 

 

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

比卡丘不皮

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值