数据库后台
# 成品演示
# 需求分析
这里想构建一个可以展示在页面上的数据库的管理器,可通过前端文本框来实现增删查
全部放在一个页面里面
- 侧边栏为查询部分
- 主体部分为表格展示的数据库,添加和删除都可以直接在表格内进行
- 删除:每一行旁边都有一个删除键
- 添加:最后一行填写完每一列信息后,对应一个添加键
# 思路设计
# 前端
整体被分为三个部分:标题栏、侧边查询栏、主体数据操作,可以使用三个 <div>
分隔开,标题栏置顶,然后下面两个水平布局
查询栏是一组输入框结合一个查询按键,主体数据操作是一个表格,列为操作表列数加一(塞入一个操作按键),行为操作表行数加一(塞入一组添加信息)
两组文本框都需要能将 html
中用户在查询栏中输入的信息传递给 cgi
,可以合并为一个大表单,对于每一个 submit
按键都能实现响应,但也会出现一个问题就是响应了话另外两个操作可能会受到影响,但是这里是一个文本框,另外两个操作在不写内容或者内容不全的情况下是不会响应的
# 后端
首先 cgi
需要是一个连接了数据库的 cpp
实现的,用这个 cpp
可以对数据库进行增删查,这个之前的文章中有详解编码,就不赘述了,传送门 ,且这个对应的之前写好的一个 SqlManager
类,可以直接拿过来
cgi
将从 html
那接收到信息来控制 sql
语句的 WHERE
定位,将提取出来的表传递给 html
进行展示,故需要将 SqlManager::OutputMysql
的返回值变成 std::vector<std::vector<std::string> >
类型,以便我们的传输
# 前后交互
好了难点来了,我们通过 html
接受信息传递给 cgi
,然后 cgi
根据信息处理数据后再传递回给 html
,cgi
传递给 html
这件事似乎有点难做,故想到让 cgi
传递给 cgi
就省了很多事情 参考之前的讲述
且我们可以直接使用 cgi
来循环打印表格,就很方便了
# 程序设计
# 固定内容
首先是在正常的 html
上编写固定的样式,以便预览,这里样式完全按照自己的喜好来的,读者们也可以编写自己喜欢的页面,这里就不讲细致了,把骨架放出来
<body>
<form>
<div><h1>学生信息收集</h1></div>
<!--搜索栏-->
<div>
<div>
姓名:<input type='text'/></br>
学号:<input type='text'/></br>
性别:<input type='text'/></br>
年龄:<input type='text'/></br>
系别:<input type='text'/></br>
</div>
<div>
<input type='submit' value='搜索'/>
</div>
</div>
<!--主体-->
<div>
<h2>学生信息表:</h2>
<table>
<tr>
<th>学号</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>系别</th>
<th>操作</th>
</tr>
<tr>
<td>123456</td>
<td>Chivas_Regal</td>
<td>男</td>
<td>20</td>
<td>计算机系</td>
<!-- 删除按钮 -->
<td><button type="submit">-</button></td>
</tr>
<!-- 添加信息 -->
<tr>
<td><input type="text"/></td>
<td><input type="text"/></td>
<td><input type="text"/></td>
<td><input type="text"/></td>
<td><input type="text"/></td>
<td><button type="submit">+</button></td>
</tr>
</table>
</div>
</form>
</body></html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
加上我喜欢的样式和微调布局后长这样:
分析一下知道在 <table>
往上都是固定的,直接构建 main.cpp
的函数 Header()
用作输出这些,同时 </table>
下面的用作 Ender()
// main.cpp
void Header () {
std::string header = "<body>\n"
"<form>\n"
" <div><h1>学生信息收集</h1></div>\n"
" <!--搜索栏-->\n"
" <div>\n"
" <div>\n"
" 姓名:<input type='text'/></br>\n"
" 学号:<input type='text'/></br>\n"
" 性别:<input type='text'/></br>\n"
" 年龄:<input type='text'/></br>\n"
" 系别:<input type='text'/></br>\n"
" </div>\n"
" <div>\n"
" <input type='submit' value='搜索'/>\n"
" </div>\n"
" </div>\n"
" <!--主体-->\n"
" <div>\n"
" <h2>学生信息表:</h2>\n"; // CLion 复制粘贴一下就好了,真方便 ...
std::cout << header;
}
int Ender () {
std::cout << "</form></body></html>";
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 查询与展示
我们展示的表和我们的查询是有关的,当查询为空的时候对应的 sql
不应该带上 WHERE
定位,故写好几个文本框的 name
为 sno,sname,ssex,sage,sdept
后,我们在代码最前面先收集好看看上一层有没有传递过来的数据,然后构建两个函数用作查询总表和查询定位表
查询中我们在修改 SqlManager::OutputMysql
返回 std::vector<std::vector<std::string> >
后,直接拿着这个二维数组去输出对应的表格内容
同时每一行末尾要加一个删除按钮,我们对这些按钮也设置一个 value
和 name
,就按它所在第几行来设置,方便我们在后面删除操作中接取信息直接定位要删第几行的信息
且在最后一行加入几个输入框和一个添加的按钮
int main () {
cgicc::Cgicc formData;
cgicc::form_iterator fit_name = formData.getElement("sname");
cgicc::form_iterator fit_no = formData.getElement("sno");
cgicc::form_iterator fit_sex = formData.getElement("ssex");
cgicc::form_iterator fit_age = formData.getElement("sage");
cgicc::form_iterator fit_dept = formData.getElement("sdept");
...
std::cout << " <table>\n";
// 看看有无内容
if (fit_name->isEmpty() && fit_no->isEmpty() && fit_sex->isEmpty() && fit_age->isEmpty() && fit_dept->isEmpty()) {
showAll(sqm);
} else {
showQuery(sqm, fit_name, fit_no, fit_sex, fit_age, fit_dept);
}
std::cout << "<tr>\n";
// 添加的信息和按钮
std::cout << " <td><input type='text' name='add_no'/></td>\n";
std::cout << " <td><input type='text' name='add_name'/></td>\n";
std::cout << " <td><input type='text' name='add_sex'/></td>\n";
std::cout << " <td><input type='text' name='add_age'/></td>\n";
std::cout << " <td><input type='text' name='add_dept'/></td>\n";
std::cout << " <td><input type='submit' value='+'/></td>\n";
std::cout << "</tr>\n";
std::cout << " </table>\n";
}
// 定位查询
void showQuery (SqlManager &sqm, cgicc::form_iterator fit_name, cgicc::form_iterator fit_no, cgicc::form_iterator fit_sex, cgicc::form_iterator fit_age, cgicc::form_iterator fit_dept) {
// 搭建带 WHERE 定位的 sql 语句
std::string sql = "SELECT * FROM STUDENT WHERE";
if (!fit_no->isEmpty()) sql += " Sno=\"" + **fit_no + "\" AND";
if (!fit_name->isEmpty()) sql += " Sname=\"" + **fit_name + "\" AND";
if (!fit_sex->isEmpty()) sql += " Ssex=\"" + **fit_sex + "\" AND";
if (!fit_age->isEmpty()) sql += " Sage=" + **fit_age + " AND";
if (!fit_dept->isEmpty()) sql += " Sdept=\"" + **fit_dept + "\" AND";
sql.pop_back(); sql.pop_back(); sql.pop_back(); // 删掉最后的 "AND"
sqm.QueryMysql(sql);
std::vector<std::vector<std::string> > output = sqm.OutputMysql(); // 返回表
int lineidx = 1; // 行号
// 打印整张表
for (const std::vector<std::string> &line : output) {
std::cout << " <tr>\n";
if (line[0] == "Sno") { // 列名信息
std::cout << " <th>学号</th>\n";
std::cout << " <th>姓名</th>\n";
std::cout << " <th>性别</th>\n";
std::cout << " <th>年龄</th>\n";
std::cout << " <th>系别</th>\n";
std::cout << " <th>操作</th>\n";
} else {
for (const std::string &curcol : line) {
std::cout << " <td>" << curcol << "</td>\n";
}
// name=delbtn(1/2/3/...), value=(1/2/3/...)
std::cout << " <td><button type=\"submit\" name=\"delbtn" << std::to_string(lineidx) << "\" value=\"" << std::to_string(lineidx) << "\">-</button></td>\n";
lineidx ++;
}
std::cout << " </tr>\n";
}
}
// 本函数为打印全表,和上面的定位查询思路基本一致
void showAll (SqlManager &sqm) {
sqm.QueryMysql("SELECT * FROM STUDENT");
std::vector<std::vector<std::string> > output = sqm.OutputMysql();
int lineidx = 1;
for (const std::vector<std::string> &line : output) {
std::cout << " <tr>\n";
if (line[0] == "Sno") {
std::cout << " <th>学号</th>\n";
std::cout << " <th>姓名</th>\n";
std::cout << " <th>性别</th>\n";
std::cout << " <th>年龄</th>\n";
std::cout << " <th>系别</th>\n";
std::cout << " <th>操作</th>\n";
} else {
for (const std::string &curcol : line) {
std::cout << " <td>" << curcol << "</td>\n";
}
std::cout << " <td><button type=\"submit\" name=\"delbtn" << std::to_string(lineidx) << "\" value=\"" << std::to_string(lineidx) << "\">-</button></td>\n";
lineidx ++;
}
std::cout << " </tr>\n";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# 添加与删除
对于添加,获取完在末尾输入好的整行数据后,直接将其填进 STUDENT
即可
void addRow (SqlManager &sqm, cgicc::form_iterator fit_sno, cgicc::form_iterator fit_sname, cgicc::form_iterator fit_sex, cgicc::form_iterator fit_age, cgicc::form_iterator fit_dept) {
// 缺一不可
if (fit_sno->isEmpty()) return;
if (fit_sname->isEmpty()) return;
if (fit_sex->isEmpty()) return;
if (fit_age->isEmpty()) return;
if (fit_dept->isEmpty()) return;
// 装入之前写好的 StudentInfo 类中
StudentInfo stu(**fit_sno, **fit_sname, **fit_sex, std::stoi(**fit_age), **fit_dept);
// 插入
sqm.InsertMysql(stu);
}
2
3
4
5
6
7
8
9
10
11
12
对于删除,我们应当定位一下是哪一个删除按钮响应了,去获取对应行的信息然后精确删除
sqm.QueryMysql("SELECT * FROM STUDENT");
for (int i = 1; i <= sqm.numRow(); i ++) { // 遍历所有的行
if (!formData.getElement("delbtn" + std::to_string(i))->isEmpty()) { // 找到了删除按钮响应了的行
delRow(sqm, i);
break;
}
}
void delRow (SqlManager &sqm, int lineidx) {
std::vector<std::vector<std::string> > table = sqm.OutputMysql();
// 获取这一行的学生信息
StudentInfo stu(table[lineidx][0], table[lineidx][1], table[lineidx][2], std::stoi(table[lineidx][3]), table[lineidx][4]);
// 精确删除
sqm.DeleteMysql(stu);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
注意这个之后就应该输出整张表,所以我们要放在打印表之前进行,同时要和上面的 name{sno,sname,...}
用同一个 formData
这一部分的整体代码为:
int main () {
// header 的制作
...
cgicc::form_iterator fit_addsno = formData.getElement("add_no");
cgicc::form_iterator fit_addsname = formData.getElement("add_name");
cgicc::form_iterator fit_addsex = formData.getElement("add_sex");
cgicc::form_iterator fit_addage = formData.getElement("add_age");
cgicc::form_iterator fit_adddept = formData.getElement("add_dept");
SqlManager sqm;
sqm.ConnectMysql();
sqm.QueryMysql("SELECT * FROM STUDENT");
for (int i = 1; i <= sqm.numRow(); i ++) {
if (!formData.getElement("delbtn" + std::to_string(i))->isEmpty()) {
delRow(sqm, i);
break;
}
}
addRow(sqm, fit_addsno, fit_addsname, fit_addsex, fit_addage, fit_adddept);
...
// table 的制作和 ender
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 整体结构
- 表单(将所有数据传递给下一个自己) {
- 接收上一个自己传递过来的数据
- 用接收的数据 增添/删除
- 用接收的数据 查询并制作表格
- }