前言
这是《数据挖掘》课程的作业,虽然叫做数据挖掘,前半学期还是在学数据库的知识。这次大作业算是一次检验吧。其实我觉得,数据库的部分并不多,难到我的都是呈现部分…可能是资历还太浅,总觉得数据库保存数据就好了,无需主键外键级联什么的约束关系,具体的实现部分放在PHP中足够了。
这次最自豪的地方在于所有的工具仅限于Chrome浏览器和Sublime Text 2编辑器,没有用任何网页排版软件比如Dreamweaver。感谢Chrome,有了这个神器,配合Sublime Text 2 我才能迅速的修改并调试Javascript代码,具体的调试方法我决定另写一篇博客。
先来看看最终效果吧,已经搬上SAE了,相关的数据库连接什么的也改了改,提醒大家,在connect了之后要立刻mysql_select_db(),真心不能含糊...
链接地址:小型自选商场综合管理系统
最初做了一个版本的,用了表格排版,做了还没一半实在是觉得太丑,还不如之前的绩效考核系统,正巧在网上看到一个侧边栏动态效果,就把这个侧边栏拿下来,自定义了一下,在main里边加入自己的内容,做成了这个网站,用了div来排版,主体配色还是原来的侧边栏,新增加的配色尽量配合了原有的。
实现过程
HTML & CSS
这次全部采用了div排版,配合css设定样式,需要小幅修改的地方,用Chrome实时的修改调整,再把调整好的内容复制到代码里,这样就不用反复的在Sublime里边修改了。
Javascript
Javascript的部分我觉得写重复了好多,由于第一次应用AJAX技术,现学现卖,有些不确定的地方不太敢自作主张,还是利用了教程里的代码,这就使得代码重复的部分确实很多,每一个页面都需要AJAX技术和后台程序通讯,每一个页面的Javascript函数都是重复写的,虽然每个页面都有不同的需求和不同的显示格式,我还是觉得应该有一个很好的方法组织好这些代码。此刻我更加迫切的觉得应该认认真真的看看《代码整洁之道》了。
具体一点来说,有这么几个主要的部分:
全局快捷键
全局快捷键的设置是考虑到用户在输入商品编号、数量之后,再去点击按钮,使用起来不方便,所以想要加一个全局快捷键,当用户输入完成后,按下回车,即可调用对应的函数提交表单。
代码如下:
其中sendItem()是被调用的函数,每当用户在网页上按下一个键,就会检查一次是否是回车,如果是,则调用函数。
网页的动态响应部分
收银台实时计算找零
因为考虑到找零的内容无需在后台完成,也不用存入数据库,因此完全可以在前台显示,并不做存储。在前台完成还有一个优点,运行速度不受网速的限制,立刻就能完成。
代码如下:
HTML部分:
其中和是占位符,给它一个id,我们就能在Javascript里边对它的内容进行操作了。totalMoney是AJAX返回的当前总价,result是找零的结果。
这个是减法运算式的那条横线,用了一个div附加上阴影,就有了一根线,再设定好宽度就好了。
Javascript部分:
function showResult (cash)//计算找零
{
if (cash.length == 0 || cash == 0)//如果
{
document.getElementById("result").innerHTML = "";
return ;
}
var result = cash - document.getElementById("totalMoney").innerHTML;
if (result<0)
{
document.getElementById("result").innerHTML = "不足";
}
else
{
document.getElementById("result").innerHTML = result;
}
}
调整页面元素的内容和属性
这里的应用主要在表格的隐藏和显示,另一个应用在于利用占位符动态的填充信息。
表格的显示是考虑到,在默认的情况下,表格头不显示,当有查询结果的时候,显示表格头并在下边添加新的行,显示查询结果。主要的实现方法就是在HTML中设定这一部分的div属性为style="display:none;font-size: 18px”,当这个display更改为block的时候就会显示里边的内容。
HTML代码如下:
Javascript相关代码如下:
//显示列表(通过设置display属性)
document.getElementById("List").style.display = ‘block';
动态生成表格添加新的行
这部分用于动态的生成新的行,用于收银台实时显示当前订单的所有项目。主要是将AJAX返回的数据格式化显示到表格里,这里主要用tbody作占位,给它id之后对它操作,以收银台为例,HTML的代码上边就有,
Javascript代码如下(截取相关部分):
//动态建立表格
var newItem = afterSplit[1].split(",");
var table = document.getElementById("itemList");
var newRow = table.insertRow(table.rows.length);//通过获得当前行数把新的行插到最下边
for (var i=0; i < newItem.length ;i++)
{
newRow.insertCell(i).innerHTML= newItem[i];
}
split是分词函数,它会把字符串用参数里的符号分割,将各部分返回为数组,可以多次分割。
AJAX发送和接收部分
AJAX真心是琢磨了一会儿的,但上手了之后又会感叹AJAX的牛X之处,利用原有的技术,进行挖掘,实现从未有过的功能,异步Javascript和XML..你值得拥有。
Javascript部分:
function sendItem()
{
var addDeal = 0;
if(!document.getElementById("itemID").value)
{
return ;
}
xmlHttp = GetXmlHttpObject();
if (xmlHttp == null)
{
alert("不好意思~你的浏览器不支持AJAX技术诶...");
return ;
}
if(isfirst == 1)
{
addDeal = 1;
isfirst = 0;
}
var url = "../itemInsert.php";//定义要发送到服务器的 URL(文件名)
url = url + "?addDeal=" + addDeal;
url = url + "&itemID=" + document.getElementById("itemID").value;//把商品编号添加到这个 URL
url = url + "&itemNum=" + document.getElementById("itemNum").value;//把商品数量添加到 URL
url = url + "&sid=" + Math.random();//添加一个随机数,以防服务器使用缓存文件
xmlHttp.onreadystatechange = responseItem;
//调用 GetXmlHttpObject 函数来创建 XMLHTTP 对象,
//并在事件被触发时告知该对象执行名为 stateChanged 的函数
xmlHttp.open("GET", url, true);//用给定的 URL 来打开打开这个 XMLHTTP 对象
xmlHttp.send(null);//向服务器发送 HTTP 请求
clearInput();//清除输入框
document.getElementById('itemID').focus();//重新定位焦点
}
//每当 XMLHTTP 对象的状态发生改变,则执行该函数。
//在状态变成 4 (或 "complete")时,执行内容。
function responseItem()
{
if (xmlHttp.readyState == 4 || xmlHttp.readyState == "complete")
{
var result = xmlHttp.responseText;
if(result == "IDfail")
{
alert("您输入的 商品编号 有误,请重新输入");
return ;
}
if(result == "Insertfail")
{
alert("数据库插入错误");
return ;
}
if(result == "notEnough")
{
alert("货源不足");
return ;
}
if(result == "updateFail")
{
alert("数据库更新错误");
return ;
}
var afterSplit = result.split(";");
document.getElementById("totalMoney").innerHTML = afterSplit[0];
//显示列表(通过设置display属性)
document.getElementById("List").style.display = 'block';
//动态建立表格
var newItem = afterSplit[1].split(",");
var table = document.getElementById("itemList");
var newRow = table.insertRow(table.rows.length);//通过获得当前行数把新的行插到最下边
for (var i=0; i < newItem.length ;i++)
{
newRow.insertCell(i).innerHTML= newItem[i];
}
}
}
后台PHP部分:
>>>>>>>>>>>>>>>>收银台后台程序<<<<<<<<<<<<<<<
//----------------------------------------------
require_once "database.php";
$itemID=$_GET["itemID"];
$itemNum=$_GET["itemNum"];
$addDeal=$_GET["addDeal"];
$salesmanNo = 1;
$con = initialise();
$lastest = query("select dealID from ItemOut order by saleTime desc");//分辨是否新订单
$lastest = mysql_fetch_array($lastest);
if($lastest[0])
{
if($addDeal == 1)
{
$dealID = $lastest[0] + 1;
}
else
{
$dealID = $lastest[0];
}
}
else
{
$dealID = 1;
}
//检查库存并修改
$storeNum = query("select itemNum from Item where itemID='".$itemID."'");
$storeNum = mysql_fetch_array($storeNum);
$result = $storeNum[0] - $itemNum;
if($result >= 0)
{
if(!query("update Item set itemNum=".$result." where itemID='".$itemID."'"))
{
echo "updateFail";
return ;
}
}
else
{
echo "notEnough";
return ;
}
$itemPrice = mysql_query("select itemPrice from Item where itemID='". $itemID ."'");
$itemPrice = mysql_fetch_array($itemPrice);
if($itemPrice[0])
{
$totalPrice = $itemPrice[0] * $itemNum;
//检查insert语句
//echo "insert into ItemOut (dealID,itemID,itemNum,totalPrice,saleTime) values (". $dealID .",'". $itemID ."',". $itemNum .",". $totalPrice .",'". date('Y-m-d') ."')";
if(query("insert into ItemOut (dealID,itemID,itemNum,totalPrice,saleTime,salesmanNo) values (". $dealID .",'". $itemID ."',". $itemNum .",". $totalPrice .",'". date("Y-m-d H:i:s") ."',".$salesmanNo.")"))
{
//如果添加成功了,先返回总价
$sum = query("select sum(totalPrice) from ItemOut where dealID=".$dealID);
$sum = mysql_fetch_array($sum);
$response = $sum[0] .";";
//然后返回新的商品信息
//dealID对用户无意义
$response = $response ."". $itemID;//itemID
$name = query("select itemName from Item where itemID=". $itemID);//取出商品名
mysql_query('set names utf8');
$name = mysql_fetch_array($name);
$response = $response .",". $name[0];//itemName
$response = $response .",". $itemNum;//itemNum
$response = $response .",". date("Y-m-d H:i:s");//saleTime
$response = $response .",". $totalPrice;//totalPrice
$name = query("select salesmanName from Employee where salesmanNo=". $salesmanNo);//取出商品名
mysql_query('set names utf8');
$name = mysql_fetch_array($name);
$response = $response .",". $name[0];//salesmanNo
$response = $response .";" ;//每行的分割
echo $response;
}
else
{
echo "Insertfail";
}
}
else
echo "IDfail";
?>
PHP & MySQL
PHP主要是当做后台的服务端,接收前端发来的内容,操作MySQL数据库,格式化返回的数据并返回给前端。
另外销售清单页面用php操作数据库直接生成了网页,这里没有用到新的技术。
后台程序的代码上边就有。
SQL语句还是用的很低级...没有用到join之类的。现在网站还很小,无所谓。将来网站如果访问量上去了,这样写SQL查询语句消耗的资源很多,如果要计费的话,成本会很高。
遇到的问题与解决办法
AJAX技术
AJAX技术因为以前没有接触过,这次现学现卖,遇到问题了还不知道解决起来还是挺费时间的。
发送和接收数据包遇到的问题
这里主要要注意的地方,在于前端Javascript调用response函数的时候,只有函数名没有括号,像这样:
xmlHttp.onreadystatechange = responseStore;
之前不知道,加了括号之后怎么都没法调用后边的函数,用Chrome查错之后才发现问题,在对比了N遍教程代码之后才发现…原来是多了一对括号。
编码解码的问题
前端往后端发内容的时候用了GET方式,后端发回来一整个字符串,需要重新分割成各个部分,一开始想要用JSON编码,后来发现数据量很小,用字符串再分割也可以。具体的例子上边也有。
前端动态响应部分
动态响应部分主要是根据后台发来的数据,格式化之后添加到现有的网页里,主要是动态添加新的行。原本想要实现新的行添加到表头之后,始终无法实现,后来想想,HTML是静态的,已经生成的内容无法更改,是无法将新的行插入到最前边的,只能在后边添加。
订单号保存
这个问题一开始并没有尝试解决,而是留到最后才做的,最初的想法是在前端记录订单号,当点击“结账”之后,订单号自动增加1,订单号发送到后台存储,后来发现,前端的网页关闭再打开之后,就又回到1号订单了,会和之前的记录重复,这样计算总价的时候会有问题。
最后这一部分的实现结合了前端和后端。前端定义全局变量isFirst,初始值是1,增加一个判断,如果isFirst是1,则告诉后台,这是个新订单,同时将isFirst置0,这样第二次就不会进这一部分了,在结账按钮对应的函数里,把isFirst再次置1,这样就相当于,传递的数据负责告诉后台,这是不是一个新订单,如果是,则dealID加1,如果不是,则按照日期寻找最新的订单号,用这个订单号继续存储。
反思
总得来讲,这次的网站要比之前的绩效考核系统好看,有动画,按钮也好看一些,采用了CSS样式表描述网页。学了一些新的技术并且应用进去,学到了不少。
在运用SAE的时候试了试SVN,发现确实,版本控制是个应当学习的东西,有了这个还真方便…
之所以没有用jQuery是因为考虑到AJAX已经是个新技术了,jQuery需要学习的内容还很多,选择器什么的都和原本的操作不太一样,因此就没有学习这个库,不过听说这个库实现AJAX技术很方便,也找到了些例子,接下来有空的话要学习一下这个类库。
SQL语句还是太低端,效率很低,显得很没水平。
没有考虑到安全方面的问题,没有相关的处理函数,这个可以慢慢加进去。
近期项目预告
1.微信公众平台,已经将接口设置好了,用了SAE服务器,响应迅速,利用微信的例子代码实现了自动回复功能,目前在研究微信的API,研究其他网站的API,希望能添加一些功能进来。
2.iOS开发,目前在跟最新的斯坦福iOS教程,认真的做笔记,我想有空的时候把笔记搬到博客里来,让大家不看视频也能学到重点。
3.Alfred这个吧...搁着吧..
最主要的是下周四要检查操作系统大实验了……我还只做了一丁点…不幸福..