MFC實現簡單的計算器

MFC實現的計算器

最近補了一下UI方面的,感覺還好,同時發現算法方面真的還有很多方面要補充的,希望最近一段忙完後開始學習算法,大一錯過了ACM是自己的遺憾,希望接下來一段時間補回來

代碼已上傳:https://github.com/mcc321/UI/tree/master/MFC/test2/test2

其實寫這個計算器最難的倒不是新學習的MFC,感覺MFC其實還是挺簡單的,VS已經做的很完善了,也許是自己學的還很淺,但真的覺得mfc短時間入門其實還是可以做到的。

下面簡單介紹一下MFC

// test2Dlg.h: 頭文件
//

#pragma once
#include <string>
#include <vector>
using namespace std;

// Ctest2Dlg 對話框
class Ctest2Dlg : public CDialogEx
{
// 構造
public:
	Ctest2Dlg(CWnd* pParent = nullptr);	// 標準構造函數

// 對話框數據
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_TEST2_DIALOG };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持


// 實現
protected:
	HICON m_hIcon;
	// 生成的消息映射函數
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	CButton m_modulo;
	CButton m_reset;
	CButton m_delete;
	CButton m_divide;
	CButton m_7;
	CButton m_8;
	CButton m_9;
	CButton m_multiply;
	CButton m_4;
	CButton m_5;
	CButton m_6;
	CButton m_sub;
	CButton m_1;
	CButton m_2;
	CButton m_3;
	CButton m_add;
	CButton m_0;
	CButton m_point;
	CButton m_equal;
	CString m_display;
	CString m_result;
	afx_msg void OnBnClickedButton1();
	afx_msg void OnBnClickedButton2();
	afx_msg void OnBnClickedButton3();
	afx_msg void OnBnClickedButton4();
	afx_msg void OnBnClickedButton5();
	afx_msg void OnBnClickedButton6();
	afx_msg void OnBnClickedButton7();
	afx_msg void OnBnClickedButton8();
	afx_msg void OnBnClickedButton9();
	afx_msg void OnBnClickedButton10();
	afx_msg void OnBnClickedButton11();
	afx_msg void OnBnClickedButton12();
	afx_msg void OnBnClickedButton13();
	afx_msg void OnBnClickedButton14();
	afx_msg void OnBnClickedButton15();
	afx_msg void OnBnClickedButton16();
	afx_msg void OnBnClickedButton18();
	afx_msg void OnBnClickedButton19();
	afx_msg void OnBnClickedButton20();
	afx_msg void OnEnChangeEdit1();
	afx_msg void OnEnChangeEdit2();
	void calculator();
	struct Result split(string);
	afx_msg void OnBnClickedButton17();
};


這裏簡單介紹一下,這些以afx_msg函數都是自動生成的,對應於每個按鍵點擊時的響應事件,當然還有其它事件。CButton定義的變量對應每個按鍵變量,下面自己還定義了兩個處理函數。


// test2Dlg.cpp: 實現文件
//
# include <iostream>
# include "pch.h"
# include "framework.h"
# include "test2.h"
# include "test2Dlg.h"
# include "afxdialogex.h"
# include "stack"
# include "tuple"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif
using namespace std;


// 用於應用程序“關於”菜單項的 CAboutDlg 對話框

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// 對話框數據
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 實現
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)

END_MESSAGE_MAP()


// Ctest2Dlg 對話框



Ctest2Dlg::Ctest2Dlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_TEST2_DIALOG, pParent)
	, m_display(_T(""))
	, m_result(_T(""))
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void Ctest2Dlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_BUTTON1, m_modulo);
	DDX_Control(pDX, IDC_BUTTON2, m_reset);
	DDX_Control(pDX, IDC_BUTTON3, m_delete);
	DDX_Control(pDX, IDC_BUTTON4, m_divide);
	DDX_Control(pDX, IDC_BUTTON5, m_7);
	DDX_Control(pDX, IDC_BUTTON6, m_8);
	DDX_Control(pDX, IDC_BUTTON7, m_9);
	DDX_Control(pDX, IDC_BUTTON8, m_multiply);
	DDX_Control(pDX, IDC_BUTTON9, m_4);
	DDX_Control(pDX, IDC_BUTTON10, m_5);
	DDX_Control(pDX, IDC_BUTTON11, m_6);
	DDX_Control(pDX, IDC_BUTTON12, m_sub);
	DDX_Control(pDX, IDC_BUTTON13, m_1);
	DDX_Control(pDX, IDC_BUTTON14, m_2);
	DDX_Control(pDX, IDC_BUTTON15, m_3);
	DDX_Control(pDX, IDC_BUTTON16, m_add);
	DDX_Control(pDX, IDC_BUTTON18, m_0);
	DDX_Control(pDX, IDC_BUTTON19, m_point);
	DDX_Control(pDX, IDC_BUTTON20, m_equal);
	DDX_Text(pDX, IDC_EDIT1, m_display);
	DDX_Text(pDX, IDC_EDIT2, m_result);
}

BEGIN_MESSAGE_MAP(Ctest2Dlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, &Ctest2Dlg::OnBnClickedButton1)
	ON_BN_CLICKED(IDC_BUTTON2, &Ctest2Dlg::OnBnClickedButton2)
	ON_BN_CLICKED(IDC_BUTTON3, &Ctest2Dlg::OnBnClickedButton3)
	ON_BN_CLICKED(IDC_BUTTON4, &Ctest2Dlg::OnBnClickedButton4)
	ON_BN_CLICKED(IDC_BUTTON5, &Ctest2Dlg::OnBnClickedButton5)
	ON_BN_CLICKED(IDC_BUTTON6, &Ctest2Dlg::OnBnClickedButton6)
	ON_BN_CLICKED(IDC_BUTTON7, &Ctest2Dlg::OnBnClickedButton7)
	ON_BN_CLICKED(IDC_BUTTON8, &Ctest2Dlg::OnBnClickedButton8)
	ON_BN_CLICKED(IDC_BUTTON9, &Ctest2Dlg::OnBnClickedButton9)
	ON_BN_CLICKED(IDC_BUTTON10, &Ctest2Dlg::OnBnClickedButton10)
	ON_BN_CLICKED(IDC_BUTTON11, &Ctest2Dlg::OnBnClickedButton11)
	ON_BN_CLICKED(IDC_BUTTON12, &Ctest2Dlg::OnBnClickedButton12)
	ON_BN_CLICKED(IDC_BUTTON13, &Ctest2Dlg::OnBnClickedButton13)
	ON_BN_CLICKED(IDC_BUTTON14, &Ctest2Dlg::OnBnClickedButton14)
	ON_BN_CLICKED(IDC_BUTTON15, &Ctest2Dlg::OnBnClickedButton15)
	ON_BN_CLICKED(IDC_BUTTON16, &Ctest2Dlg::OnBnClickedButton16)
	ON_BN_CLICKED(IDC_BUTTON18, &Ctest2Dlg::OnBnClickedButton18)
	ON_BN_CLICKED(IDC_BUTTON19, &Ctest2Dlg::OnBnClickedButton19)
	ON_BN_CLICKED(IDC_BUTTON20, &Ctest2Dlg::OnBnClickedButton20)
	ON_EN_CHANGE(IDC_EDIT1, &Ctest2Dlg::OnEnChangeEdit1)
	ON_EN_CHANGE(IDC_EDIT2, &Ctest2Dlg::OnEnChangeEdit2)
	ON_BN_CLICKED(IDC_BUTTON17, &Ctest2Dlg::OnBnClickedButton17)
END_MESSAGE_MAP()


// Ctest2Dlg 消息處理程序

BOOL Ctest2Dlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 將“關於...”菜單項添加到系統菜單中。

	// IDM_ABOUTBOX 必須在系統命令範圍內。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 設置此對話框的圖標。  當應用程序主窗口不是對話框時,框架將自動
	//  執行此操作
	SetIcon(m_hIcon, TRUE);			// 設置大圖標
	SetIcon(m_hIcon, FALSE);		// 設置小圖標

	// TODO: 在此添加額外的初始化代碼

	return TRUE;  // 除非將焦點設置到控件,否則返回 TRUE
}

void Ctest2Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// 如果向對話框添加最小化按鈕,則需要下面的代碼
//  來繪製該圖標。  對於使用文檔/視圖模型的 MFC 應用程序,
//  這將由框架自動完成。

void Ctest2Dlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用於繪製的設備上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使圖標在工作區矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 繪製圖標
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//當用戶拖動最小化窗口時系統調用此函數取得光標
//顯示。
HCURSOR Ctest2Dlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



void Ctest2Dlg::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '%';
	UpdateData(FALSE);
}




void Ctest2Dlg::OnBnClickedButton2()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_result = L"";
	m_display = m_result;
	UpdateData(FALSE);
}



void Ctest2Dlg::OnBnClickedButton3()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	if (!m_display.IsEmpty()) {
		m_display = m_display.Left(m_display.GetLength() - 1);
	}
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton4()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '/';
	UpdateData(TRUE);
}


void Ctest2Dlg::OnBnClickedButton5()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '7';
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton6()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '8';
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton7()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '9';
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton8()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '*';
	UpdateData(FALSE);
}



void Ctest2Dlg::OnBnClickedButton9()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '4';
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton10()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '5';
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton11()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '6';
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton12()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '-';
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton13()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '1';
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton14()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '2';
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton15()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '3';
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton16()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '+';
	UpdateData(FALSE);
}





void Ctest2Dlg::OnBnClickedButton18()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_display += '0';
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton19()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	if (m_result.Find('.') == -1) {
		m_display += '.';
	}
	UpdateData(FALSE);
}


void Ctest2Dlg::OnBnClickedButton20()
{
	// TODO: 在此添加控件通知處理程序代碼
	calculator();
}




void Ctest2Dlg::OnEnChangeEdit1()
{
	// TODO:  如果該控件是 RICHEDIT 控件,它將不
	// 發送此通知,除非重寫 CDialogEx::OnInitDialog()
	// 函數並調用 CRichEditCtrl().SetEventMask(),
	// 同時將 ENM_CHANGE 標誌“或”運算到掩碼中。

	// TODO:  在此添加控件通知處理程序代碼
}


void Ctest2Dlg::OnEnChangeEdit2()
{
	// TODO:  如果該控件是 RICHEDIT 控件,它將不
	// 發送此通知,除非重寫 CDialogEx::OnInitDialog()
	// 函數並調用 CRichEditCtrl().SetEventMask(),
	// 同時將 ENM_CHANGE 標誌“或”運算到掩碼中。

	// TODO:  在此添加控件通知處理程序代碼
}


struct Result {
	vector<char> flag;
	vector<float> str;
};

struct R {
	int a;
	int b;
};


struct R find_s(vector<char> s) {
	struct R r;
	r.a = -1;
	r.b = -1;
	for (int i = 0; i < s.size(); i++) {
		if (s[i] == '*' || s[i] == '/' || s[i] == '%' || s[i] == '+' || s[i] == '-') {
			r.a += 1;
			if (s[i] == '*')
			{
				r.b = 1;
				return r;
			}
			if (s[i] == '/')
			{
				r.b = 2;
				return r;
			}
			if (s[i] == '%')
			{
				r.b = 3;
				return r;
			}
		}
	}
	r.a = 0;
	r.b = 0;
	return r;
}

struct R find_i(vector<char> s) {
	struct R r;
	r.a = -1;
	r.b = -1;
	for (int i = 0; i < s.size(); i++) {
		if (s[i] == '+' || s[i] == '-') {
			r.a += 1;
			if (s[i] == '+')
			{
				r.b = 5;
				return r;
			}
			if (s[i] == '-')
			{
				r.b = 6;
				return r;
			}
		}
	}
	r.a = 0;
	r.b = 4;
	return r;
}

struct Result Ctest2Dlg::split(string s) {
	bool t = 0;
	vector<char> flag;
	string tmp;
	vector<float> str;
	for (int i = 0; i < s.size(); i++) {
		if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/' || s[i] == '%') {
			if (!tmp.size()) {
				if (s[i - 1] == '*' || s[i - 1] == '/' || s[i - 1] == '%')
					t = 1;
				else
					str.push_back(0);
			}
			else {
				if (t)
				{
					str.push_back(-stof(tmp));
					t = 0;
				}
				else {
					str.push_back(stof(tmp));
				}
				tmp = "";
			}
			if (!t)
				flag.push_back(s[i]);
		}
		else {
			tmp = tmp + s[i];
		}
	}
	str.push_back(stof(tmp));
	struct Result result;
	result.flag = flag;
	result.str = str;
	return result;
}



void Ctest2Dlg::calculator()
{
	UpdateData(TRUE);		//取控件中的值
	string str = CT2A(m_display.GetString());
	struct Result result = split(str);
	while (result.flag.size()) {
		while (1) {
			struct R r = find_s(result.flag);
			float n;
			if (r.b == 1)
				n = result.str[r.a] * result.str[r.a + 1];
			if (r.b == 2)
				n = result.str[r.a] / result.str[r.a + 1];
			if (r.b == 3)
				n = (int)result.str[r.a] % (int)result.str[r.a + 1];
			if (r.b == 0)
				break;
			vector<float>::iterator it1 = result.str.begin() + r.a;
			vector<char>::iterator it2 = result.flag.begin() + r.a;
			result.str[r.a] = n;
			result.str.erase(it1 + 1);
			result.flag.erase(it2);
		}

		while (find_s(result.flag).b != 4) {
			struct R r = find_i(result.flag);
			float n;
			if (r.b == 5)
				n = result.str[r.a] + result.str[r.a + 1];
			if (r.b == 6)
				n = result.str[r.a] - result.str[r.a + 1];
			if (r.b == 4)
				break;
			vector<float>::iterator it1 = result.str.begin() + r.a;
			vector<char>::iterator it2 = result.flag.begin() + r.a;
			result.str[r.a] = n;
			result.str.erase(it1 + 1);
			result.flag.erase(it2);
		}
	}
	CA2T szr(to_string(result.str[0]).c_str());
	m_result = (LPCTSTR)szr;
	UpdateData(FALSE);
}



void Ctest2Dlg::OnBnClickedButton17()
{
	// TODO: 在此添加控件通知處理程序代碼
	UpdateData(TRUE);
	m_result = "GL";
	m_display = "Designed by mcc";
	UpdateData(FALSE);
}

其實這裏核心的算法只有一個其它的都是收集相關的按鍵信息,也是唯一的難點,那就是根據獲得的一串字符得到一個數字。

算法介紹

下面詳細介紹一下這個算法

  • 拆分字符串,這裏拆分有一定的規則,考慮到數字對應以及語法的特殊性,這裏做了幾個簡單的處理,如果遇到兩個相連的操作符如:/- %-2 *-2等需要補充不同的項,下面是拆分函數
struct Result {
	vector<char> flag;
	vector<float> str;
};

struct Result Ctest2Dlg::split(string s) {
	bool t = 0;
	vector<char> flag;
	string tmp;
	vector<float> str;
	for (int i = 0; i < s.size(); i++) {
		if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/' || s[i] == '%') {
			if (!tmp.size()) {
				if (s[i - 1] == '*' || s[i - 1] == '/' || s[i - 1] == '%')
					t = 1;
				else
					str.push_back(0);
			}
			else {
				if (t)
				{
					str.push_back(-stof(tmp));
					t = 0;
				}
				else {
					str.push_back(stof(tmp));
				}
				tmp = "";
			}
			if (!t)
				flag.push_back(s[i]);
		}
		else {
			tmp = tmp + s[i];
		}
	}
	str.push_back(stof(tmp));
	struct Result result;
	result.flag = flag;
	result.str = str;
	return result;
}

這裏定義了一個結構體,vector用來記錄符號,vector用來記錄數字,當然代碼沒有經過優化和詳細的設置,有很多錯誤情況不能進行處理,經不起暴力測試,只能處理正常輸入

  • 優先級處理
while (result.flag.size()) {
		while (1) {
			struct R r = find_s(result.flag);
			float n;
			if (r.b == 1)
				n = result.str[r.a] * result.str[r.a + 1];
			if (r.b == 2)
				n = result.str[r.a] / result.str[r.a + 1];
			if (r.b == 3)
				n = (int)result.str[r.a] % (int)result.str[r.a + 1];
			if (r.b == 0)
				break;
			vector<float>::iterator it1 = result.str.begin() + r.a;
			vector<char>::iterator it2 = result.flag.begin() + r.a;
			result.str[r.a] = n;
			result.str.erase(it1 + 1);
			result.flag.erase(it2);
		}

		while (find_s(result.flag).b != 4) {
			struct R r = find_i(result.flag);
			float n;
			if (r.b == 5)
				n = result.str[r.a] + result.str[r.a + 1];
			if (r.b == 6)
				n = result.str[r.a] - result.str[r.a + 1];
			if (r.b == 4)
				break;
			vector<float>::iterator it1 = result.str.begin() + r.a;
			vector<char>::iterator it2 = result.flag.begin() + r.a;
			result.str[r.a] = n;
			result.str.erase(it1 + 1);
			result.flag.erase(it2);
		}
	}

處理步驟是首先探測result.flag中是否存在* / %,優先進行處理,然後在處理+ -,每進行一輪處理便從flag中移除一個符號同時從str移除一個數字和修改一個數字,最後容器會只剩下一個元素,這個元素就是計算結果。

最終效果

wyt
wyt

雜談

今天和家人聊起了關於學習的問題,挺有感觸的,家裏說報個補習班,但自己覺得關於學習真的沒什麼可以勉強的,想學的時候自然會去學,而且樂此不疲,強迫的學習真的很低效,即使是憑藉自己的自制力強迫自己去學,就拿最近班上上課各種複習三級四級的,我的感受是沒用,自己當時的感觸是完全對學到的沒有興趣,只是想拿個證書(當時也沒想到保研什麼的),學完後,草草的考試,也以意外的過了,但事後回憶覺得真的是很浪費,浪費考試費同時也是浪費自己的時間,後來自己遇到網絡問題又重新學習na,np,才慢慢地對網絡有了一些認識。有時候就是覺得興趣到了,自然會主動去學習,不必執着於加分證書什麼的,這些給人的感受是很雞肋的,拿不到覺得很牛,拿到了覺得沒什麼用。有時候就是覺得很多事應該是自己主動嘗試去探索,發現自己感興趣的方面,然後好好學,不一定是一個方面,但一定願意爲它投入時間,且不覺得勉強,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章