Qt自绘日历控件

引言

qt本身是有自带的日历控件QCalendarWidget ,但是实际使用对于特殊的展示方式很难满足,而且调整样式也比较麻烦,索性就自己实现了日历。效果如下:

在这里插入图片描述

核心实现

主体控件为QTableWidget,不同的背景颜色、边框以及显示当前日期的圆点等都是通过代理QStyledItemDelegate实现的。翻动日历通过上方的按钮以及滚轮事件实现。

代码

#include <QMainWindow>
#include <QStyledItemDelegate>
#include <QTableWidget>
#include <QDateTime>
#include <QLabel>
#include <QPushButton>
#include <QComboBox>
#include <QDateTimeEdit>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QStackedLayout>
#include <QLineEdit>
#include <QString>

// 提供绘图函数
class DrawBaseDelegage : public QStyledItemDelegate
{
    Q_OBJECT
public:
    DrawBaseDelegage(QObject* parent);

protected:
    void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;

protected:
    void drawBackgroud(QPainter* painter, const QRect& rect, const QColor& color) const;
    void drawBorder(QPainter* painter, const QRect& rect, const QColor& color) const;
    void drawText(QPainter* painter, const QRect& rect, const QColor& color, const QString text, const Qt::AlignmentFlag align = Qt::AlignCenter) const;
    void drawPoint(QPainter* painter, const QRect& rect, const QColor& color) const;
};

class CalendarDelegage : public DrawBaseDelegage
{
    Q_OBJECT
public:
    CalendarDelegage(QAbstractItemView* parent);

protected:
    void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
    virtual bool eventFilter(QObject* obj, QEvent* ev);

private:
    bool colorByRole(const QModelIndex& modelIndex, int role, QColor& tmpColor, double alpha = 0) const;

private:
    QAbstractItemView* m_parentView;
    QModelIndex m_hoverIndex;
};

class CalendarTable : public QTableWidget
{
   Q_OBJECT
public:
    CalendarTable(QWidget* parent, int year = QDate::currentDate().year(), int month = QDate::currentDate().month());

    enum CalendarRoleType{
        CRT_DATE = Qt::UserRole,
        CRT_BACKGROUND_COLOR,
        CRT_BORDER_COLOR,
        CRT_HOVER_BORDER_COLOR,
        CRT_FONT_COLOR,
        CRT_POINT_COLOR,
        CRT_ALPHA,// 整体不透明度
    };

    enum CalendarSelectionMode{
        CSM_NO_SELECTION,
        CSM_SINGLE_SELECTION,
        CSM_MULTI_SELECTION,
    };

public:
    bool setYearMonth(int year, int month);
    void setFristDayOnWeek(int fristDayOnWeek);
    void setSelMode(CalendarSelectionMode selectionMode);
    void addSelectedDate(QDate date);
    void clearSelectedDate();
    void setMaxDate(QDate date);
    void setMinDate(QDate date);

    int year(){return m_year;}
    int month(){return m_month;}
    int fristDayOnWeek(){return m_fristDayOnWeek;}
    CalendarSelectionMode selectionMode(){return m_selMode;}
    QList<QDate> selectedDate(){return m_selDate;}
    QDate maxDate(){return m_maxDate;}
    QDate minDate(){return m_minDate;}

protected:
    virtual bool eventFilter(QObject* obj, QEvent* ev);

signals:
    void sig_refresh(int year, int month);
    void sig_selectionChanged();
    void sig_selectionAdded(const QDate &date);
    void sig_maxDateChanged(const QDate &date);
    void sig_minDateChanged(const QDate &date);

private slots:
    void slot_itemClicked(QTableWidgetItem* item);

private:
    void refreshCalendar();
    void refreshCalendarHeader();
    void refreshSelection();
    bool appendSelection(QDate date);
    bool removeSelection(QDate date);

private:
    int m_year;
    int m_month;
    int m_fristDayOnWeek;
    QDate m_maxDate;
    QDate m_minDate;

    CalendarDelegage* m_delegage;
    QList<QDate> m_selDate;
    CalendarSelectionMode m_selMode;
};

class CalendarWidget : public QWidget
{
   Q_OBJECT
public:
    CalendarWidget(QWidget* parent, int year = QDate::currentDate().year(), int month = QDate::currentDate().month());

public:
    void addWidgetBottom(QWidget* widget){m_mainLayout->addWidget(widget);}
    void addLayoutBottom(QLayout* layout){m_mainLayout->addLayout(layout);}
    void addSelectedDate(QDate date){m_calendar->addSelectedDate(date);}
    void clearSelectedDate(){m_calendar->clearSelectedDate();}

    bool setYearMonth(int year, int month){return m_calendar->setYearMonth(year, month);}
    void setSelMode(CalendarTable::CalendarSelectionMode selectionMode){m_calendar->setSelMode(selectionMode);}
    void setMaxDate(QDate date){m_calendar->setMaxDate(date);}
    void setMinDate(QDate date){m_calendar->setMinDate(date);}
    void setTitle(QString title);

    QDate maxDate(){return m_calendar->maxDate();}
    QDate minDate(){return m_calendar->minDate();}
    QList<QDate> selectedDate(){return m_calendar->selectedDate();}
    int year(){return m_calendar->year();}
    int month(){return m_calendar->month();}

private slots:
    void slot_calendarRefresh(int year, int month);
    void slot_calendarMaxDateChanged(const QDate &date);
    void slot_calendarMinDateChanged(const QDate &date);
    void slot_preMonth();
    void slot_nextMonth();
    void slot_preYear();
    void slot_nextYear();

signals:
    void sig_calendarSelectionChanged();
    void sig_calendarSelectionAdded(const QDate &date);
    void sig_calendarRefresh(int year, int month);

private:
    void refreshNextEnable();
    void refreshPreEnable();

private:
    QLabel* m_title;
    QLabel* m_displayLabel;
    QPushButton* m_preMonthBtn;
    QPushButton* m_nextMonthBtn;
    QPushButton* m_preYearBtn;
    QPushButton* m_nextYearBtn;
    QString m_dateStr;
    CalendarTable* m_calendar;

    QVBoxLayout* m_mainLayout;
};

DrawBaseDelegage作为代理的基类提供了绘制函数,绘制背景、边框、文字等。

CalendarDelegage为实际的代理,根据角色值进行相关绘制。

CalendarTable为日历主体,没有按钮等其他控件,是继承QTableWidget,实现了日历展示以及多选功能,对外提供设置当前展示月接口。refreshCalendar函数是展示内容的实现。

CalendarWidget为组合控件,融合了按钮、标题、日历,作为一个最常用的日历单元。

#include <QPainter>
#include <QHeaderView>
#include <QWheelEvent>
#include <QMoveEvent>

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

}

void DrawBaseDelegage::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyledItemDelegate::paint(painter, option, index);
}

void DrawBaseDelegage::drawBackgroud(QPainter *painter, const QRect &rect, const QColor &color) const
{
    painter->fillRect(rect, color);
}

void DrawBaseDelegage::drawBorder(QPainter *painter, const QRect &rect, const QColor &color) const
{
    painter->save();

    painter->setPen(color);
    painter->drawRect(rect.x(), rect.y(), rect.width()-1, rect.height()-1);

    painter->restore();
}

void DrawBaseDelegage::drawText(QPainter *painter, const QRect &rect, const QColor &color, const QString text, const Qt::AlignmentFlag align) const
{
    painter->save();

    painter->setPen(color);
    painter->drawText(rect, align, text);

    painter->restore();
}

void DrawBaseDelegage::drawPoint(QPainter *painter, const QRect &rect, const QColor &color) const
{
    painter->save();

    painter->setPen(color);
    painter->setBrush(color);
    painter->setRenderHint(QPainter::Antialiasing, true);// 反锯齿
    painter->drawEllipse(rect);

    painter->restore();
}

CalendarDelegage::CalendarDelegage(QAbstractItemView *parent)
    : DrawBaseDelegage(parent)
    , m_parentView(parent)
{
    m_parentView->viewport()->installEventFilter(this);
    m_parentView->viewport()->setAttribute(Qt::WA_Hover, true);
}

void CalendarDelegage::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    double colorAlpha = index.data(CalendarTable::CRT_ALPHA).toDouble() ? index.data(CalendarTable::CRT_ALPHA).toDouble() : 1;
    if (!(QStyle::State_Enabled & option.state)) {
        colorAlpha *= 0.5;
    }

    QColor tmpColor;
    // 绘制背景
    if (colorByRole(index, CalendarTable::CRT_BACKGROUND_COLOR, tmpColor, colorAlpha)) {
        drawBackgroud(painter, option.rect, tmpColor);
    }

    // 绘制边框
    if (colorByRole(index, CalendarTable::CRT_BORDER_COLOR, tmpColor, colorAlpha)) {
        drawBorder(painter, option.rect, tmpColor);
    }

    // 绘制文字
    if (colorByRole(index, CalendarTable::CRT_FONT_COLOR, tmpColor, colorAlpha)) {
        drawText(painter, option.rect, tmpColor, index.data().toString());
    }

    // 绘制文字下方点
    if (colorByRole(index, CalendarTable::CRT_POINT_COLOR, tmpColor, colorAlpha)) {
        int radius = 1;
        QRect tmpRect(option.rect.x() + option.rect.width() / 2 - radius, option.rect.y() + option.rect.height() * 6 / 7 - radius, 2 * radius, 2 * radius);
        drawPoint(painter, tmpRect, tmpColor);
    }

    // 绘制鼠标悬浮边框
    if (colorByRole(index, CalendarTable::CRT_HOVER_BORDER_COLOR, tmpColor, colorAlpha)) {
        if (index == m_hoverIndex && (QStyle::State_Enabled & option.state))
            drawBorder(painter, option.rect, tmpColor);
    }

    //QStyledItemDelegate::paint(painter, option, index);
}

bool CalendarDelegage::colorByRole(const QModelIndex& modelIndex, int role, QColor& tmpColor, double alpha) const
{
    QVariant var = modelIndex.data(role);
    if (var.canConvert<QColor>()) {
        tmpColor = var.value<QColor>();
        if (alpha)
            tmpColor.setAlphaF(alpha);
        return true;
    }
    return false;
}

bool CalendarDelegage::eventFilter(QObject *obj, QEvent *ev)
{
    if(obj == m_parentView->viewport()){
        if(QEvent::HoverMove == ev->type()){
            QPoint pos = m_parentView->viewport()->mapFromGlobal(QCursor::pos());
            m_hoverIndex = m_parentView->indexAt(pos);
        }else if(QEvent::Leave == ev->type()){
            m_hoverIndex = QModelIndex();
        }
    }

    return DrawBaseDelegage::eventFilter(obj, ev);
}

CalendarTable::CalendarTable(QWidget *parent, int year, int month)
    : QTableWidget(parent)
    , m_year(year)
    , m_month(month)
    , m_fristDayOnWeek(7)
    , m_maxDate(QDate(year+6,1,1).addDays(-1))
    , m_minDate(QDate(year-6,1,1))
    , m_selMode(CSM_SINGLE_SELECTION)
{
    QString tableStyle = "QTableWidget#calendar_table { gridline-color: rgb(250,250,250); border:0px; background-color: transparent; }";
    QString headerStyle = "QHeaderView#calendar_tableHeader { font: 14px; background-color: transparent; } QHeaderView#calendar_tableHeader::section { background: transparent;border-right: 1px solid transparent;}";

    this->setObjectName("calendar_table");
    this->horizontalHeader()->setObjectName("calendar_tableHeader");
    this->setStyleSheet(tableStyle);
    this->horizontalHeader()->setStyleSheet(headerStyle);

    this->setRowCount(6);
    this->setColumnCount(7);
    this->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    this->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    this->verticalHeader()->hide();
    this->setSelectionMode(QAbstractItemView::NoSelection);
    this->setEditTriggers(QAbstractItemView::NoEditTriggers);

    this->installEventFilter(this);

    m_delegage = new CalendarDelegage(this);
    this->setItemDelegate(m_delegage);

    refreshCalendar();
    refreshCalendarHeader();

    connect(this, &QTableWidget::itemClicked, this, &CalendarTable::slot_itemClicked);
}

bool CalendarTable::setYearMonth(int year, int month)
{
    year += (month - 1 + 12)/12 - 1;
    month = (month - 1 + 12)%12 + 1;

    if(year > m_maxDate.year() || year < m_minDate.year()){
        return false;
    }
    else if(QDate(year, month, 1) > QDate(m_maxDate.year(),m_maxDate.month(), 1)){
        month = m_maxDate.month();
    }
    else if(QDate(year, month, 1) < QDate(m_minDate.year(),m_minDate.month(), 1)){
        month = m_minDate.month();
    }

    if(m_year == year && m_month == month)
        return false;

    m_year = year;
    m_month = month;

    refreshCalendar();
    return true;
}

void CalendarTable::setFristDayOnWeek(int fristDayOnWeek)
{
    m_fristDayOnWeek = fristDayOnWeek;
    refreshCalendarHeader();
}

void CalendarTable::setSelMode(CalendarTable::CalendarSelectionMode selectionMode)
{
    m_selMode = selectionMode;
    clearSelectedDate();
}

void CalendarTable::addSelectedDate(QDate date)
{
    if(appendSelection(date))
        refreshSelection();
}

void CalendarTable::clearSelectedDate()
{
    m_selDate.clear();
    emit sig_selectionChanged();
    refreshSelection();
}

void CalendarTable::setMaxDate(QDate date)
{
    if(m_maxDate == date)
        return;
    m_maxDate = date;

    if(QDate(m_year, m_month, m_maxDate.day()) > m_maxDate){
        setYearMonth(m_maxDate.year(), m_maxDate.month());
    }

    emit sig_maxDateChanged(date);
}

void CalendarTable::setMinDate(QDate date)
{
    if(m_minDate == date)
        return;
    m_minDate = date;

    if(QDate(m_year, m_month, m_minDate.day()) < m_minDate){
        setYearMonth(m_minDate.year(), m_minDate.month());
    }

    emit sig_minDateChanged(date);
}

bool CalendarTable::eventFilter(QObject *obj, QEvent *ev)
{
    if(obj == this){
        if (ev->type() == QEvent::Wheel && isEnabled()) {
            auto wheelEv = static_cast<QWheelEvent*>(ev);
            if(wheelEv->delta() > 0){// 当滚轮远离使用者时
                setYearMonth(m_year, m_month-1);
            }else{
                setYearMonth(m_year, m_month+1);
            }
            ev->accept();
            return true;
        }
    }
    return QWidget::eventFilter(obj, ev);
}

void CalendarTable::slot_itemClicked(QTableWidgetItem *item)
{
    if(!item->data(CRT_DATE).canConvert<QDate>())
        return;

    QDate tmpDate = item->data(CRT_DATE).value<QDate>();
    if (m_selDate.contains(tmpDate)) {
        if (!removeSelection(tmpDate))
            return;
    }
    else {
        if (!appendSelection(tmpDate))
            return;
    }

    if(tmpDate.month() != m_month){
        setYearMonth(tmpDate.year(), tmpDate.month());
    }else{
        refreshSelection();
    }
}

void CalendarTable::refreshCalendar()
{
    // 计算日历第一天
    QDate curDate;
    curDate.setDate(m_year, m_month, 1);
    int fillDays = curDate.dayOfWeek()-m_fristDayOnWeek;
    if(fillDays>0){
        curDate = curDate.addDays(-fillDays);   //开始日期
    }else{
        curDate = curDate.addDays(-fillDays-7);
    }

    // 日历填充
    int maxItemCount = this->columnCount()*this->rowCount();
    for (int var = 0; var < maxItemCount; ++var){
        QTableWidgetItem *item = new QTableWidgetItem();
        item->setData(Qt::DisplayRole, curDate.day());
        item->setData(CRT_DATE, curDate);
        item->setTextAlignment(Qt::AlignCenter);
        item->setData(CRT_FONT_COLOR, QColor(93,93,93));
        item->setData(CRT_HOVER_BORDER_COLOR, QColor(0,186,218));


        // 非本月字体显示灰色
        if(curDate.month() != m_month){
            item->setData(CRT_ALPHA, 0.2);
        }

        this->setItem(var/this->columnCount(),var%this->columnCount(), item);
        curDate = curDate.addDays(1);
    }

    refreshSelection();

    emit sig_refresh(m_year, m_month);
}

void CalendarTable::refreshCalendarHeader()
{
    QStringList strList = QString("Mon,Tue,Wed,Thu,Fri,Sat,Sun").split(",");
    QStringList tmpList;
    for(int i=0; i<this->columnCount(); i++){
        tmpList << strList.at((i+m_fristDayOnWeek-1)%7);
    }
    this->setHorizontalHeaderLabels(tmpList);
}

void CalendarTable::refreshSelection()
{
    for(int row=0; row<this->rowCount(); row++){
        for(int col=0; col<this->columnCount(); col++){
            auto item = this->item(row, col);
            if(m_selDate.contains(this->item(row, col)->data(CRT_DATE).value<QDate>())){
                // 已选中背景显示蓝色
                this->item(row, col)->setData(CRT_BACKGROUND_COLOR, QColor(0,186,218));
            }else{
                item->setData(CRT_BACKGROUND_COLOR, QVariant());
            }

            if(item->data(CRT_DATE).value<QDate>() == QDate::currentDate()){
                if(item->data(CRT_BACKGROUND_COLOR).canConvert<QColor>()){
                    item->setData(CRT_POINT_COLOR, QColor(Qt::white));
                }else{
                    item->setData(CRT_POINT_COLOR, QColor(0,186,218));
                }
            }
        }
    }

    update();
}

bool CalendarTable::appendSelection(QDate date)
{
    if (m_selDate.contains(date))
        return false;

    if(date > m_maxDate || date < m_minDate)
        return false;

    switch(m_selMode){
    case CSM_SINGLE_SELECTION:
        m_selDate.clear();
        m_selDate.append(date);
        break;
    case CSM_MULTI_SELECTION:
        m_selDate.append(date);
        break;
    default:
        return false;
    }

    emit sig_selectionChanged();
    emit sig_selectionAdded(date);
    return true;
}

bool CalendarTable::removeSelection(QDate date)
{
    if (!m_selDate.contains(date))
        return false;

    // 单选则保证一定有一个选中
    if (m_selMode == CSM_SINGLE_SELECTION && m_selDate.count() <= 1) {
        return false;
    }


    m_selDate.removeAll(date);
    return true;
}

CalendarWidget::CalendarWidget(QWidget *parent, int year, int month)
    : QWidget(parent)
    , m_dateStr("%1 year %2 month")
{
    // 构造
    m_calendar = new CalendarTable(this, year, month);
    m_title = new QLabel("title", this);

    m_displayLabel = new QLabel(m_dateStr.arg(m_calendar->year()).arg(m_calendar->month()));
    m_displayLabel->setAlignment(Qt::AlignCenter);

    m_preYearBtn = new QPushButton("<<", this);
    m_preMonthBtn = new QPushButton("<", this);
    m_nextMonthBtn = new QPushButton(">", this);
    m_nextYearBtn = new QPushButton(">>", this);

    m_preYearBtn->adjustSize();
    m_preMonthBtn->adjustSize();
    m_nextMonthBtn->adjustSize();
    m_nextYearBtn->adjustSize();

    refreshNextEnable();
    refreshPreEnable();

    // 信号槽关联
    connect(m_calendar, &CalendarTable::sig_refresh, this, &CalendarWidget::slot_calendarRefresh);
    connect(m_calendar, &CalendarTable::sig_refresh, this, &CalendarWidget::sig_calendarRefresh);
    connect(m_calendar, &CalendarTable::sig_maxDateChanged, this, &CalendarWidget::slot_calendarMaxDateChanged);
    connect(m_calendar, &CalendarTable::sig_minDateChanged, this, &CalendarWidget::slot_calendarMinDateChanged);
    connect(m_calendar, &CalendarTable::sig_selectionChanged, this, &CalendarWidget::sig_calendarSelectionChanged);
    connect(m_calendar, &CalendarTable::sig_selectionAdded, this, &CalendarWidget::sig_calendarSelectionAdded);
    connect(m_preMonthBtn, &QPushButton::clicked, this, &CalendarWidget::slot_preMonth);
    connect(m_nextMonthBtn, &QPushButton::clicked, this, &CalendarWidget::slot_nextMonth);
    connect(m_preYearBtn, &QPushButton::clicked, this, &CalendarWidget::slot_preYear);
    connect(m_nextYearBtn, &QPushButton::clicked, this, &CalendarWidget::slot_nextYear);

    // 布局
    auto titleLayout = new QHBoxLayout;
    titleLayout->addWidget(m_title);
    titleLayout->addStretch();

    auto btnLayout = new QHBoxLayout;
    btnLayout->addWidget(m_preYearBtn);
    btnLayout->addWidget(m_preMonthBtn);
    btnLayout->addWidget(m_displayLabel);
    btnLayout->addWidget(m_nextMonthBtn);
    btnLayout->addWidget(m_nextYearBtn);

    auto backgroundWidget = new QFrame(this);
    backgroundWidget->setObjectName("calendar_background");
    QString backgrounStyle = "QFrame#calendar_background{border: 1px solid rgb(0,171,201);background: rgb(250,250,250);}";
    backgroundWidget->setStyleSheet(backgrounStyle);

    m_mainLayout = new QVBoxLayout(backgroundWidget);
    m_mainLayout->addLayout(titleLayout);
    m_mainLayout->addLayout(btnLayout);
    m_mainLayout->addWidget(m_calendar);

    auto mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget(backgroundWidget);
}

void CalendarWidget::setTitle(QString title)
{
    if (m_title->isHidden())
        m_title->show();
    m_title->setText(title);
}

void CalendarWidget::slot_calendarRefresh(int year, int month)
{
    m_displayLabel->setText(m_dateStr.arg(year).arg(month));

    refreshNextEnable();
    refreshPreEnable();
}

void CalendarWidget::slot_calendarMaxDateChanged(const QDate &date)
{
    Q_UNUSED(date);
    refreshNextEnable();
}

void CalendarWidget::slot_calendarMinDateChanged(const QDate &date)
{
    Q_UNUSED(date);
    refreshPreEnable();
}

void CalendarWidget::slot_preMonth()
{
    m_calendar->setYearMonth(m_calendar->year(), m_calendar->month()-1);
}

void CalendarWidget::slot_nextMonth()
{
    m_calendar->setYearMonth(m_calendar->year(), m_calendar->month()+1);
}

void CalendarWidget::slot_preYear()
{
    m_calendar->setYearMonth(m_calendar->year()-1, m_calendar->month());
}

void CalendarWidget::slot_nextYear()
{
    m_calendar->setYearMonth(m_calendar->year()+1, m_calendar->month());
}

void CalendarWidget::refreshNextEnable()
{
    m_nextYearBtn->setEnabled(m_calendar->year() < m_calendar->maxDate().year());
    m_nextMonthBtn->setEnabled(QDate(m_calendar->year(), m_calendar->month(), 1) < QDate(m_calendar->maxDate().year(), m_calendar->maxDate().month(), 1));
}

void CalendarWidget::refreshPreEnable()
{
    m_preYearBtn->setEnabled(m_calendar->year() > m_calendar->minDate().year());
    m_preMonthBtn->setEnabled(QDate(m_calendar->year(), m_calendar->month(), m_calendar->minDate().day()) > QDate(m_calendar->minDate().year(), m_calendar->minDate().month(), 1));
}

cpp的内容具体就看代码吧,不详细说了

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    auto calendar = new CalendarWidget(this);
    calendar->setSelMode(CalendarTable::CSM_MULTI_SELECTION);
    this->setCentralWidget(calendar);
}

MainWindow::~MainWindow()
{
    delete ui;
}

MainWindow里面设置了将CalendarWidget设为多选,也就是上面gif里面展示的效果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Arui丶

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值