这是一次失败的、苦恼的编程经历。
去年用户找到我,说开发一个简单的程序,基本上一两个星期就可以搞定,用于生成Word形式的招标文件。
听说这个需求,只是用一两个星期,我很惊讶,这个程序说简单也的确不难,掌握一些操作Word的API即可,说复杂也挺复杂的,我开发过试题库系统,对这些还是比较熟悉。
最终的需求没有定下来,用户说了开发时间,一两个星期就可以完工,为了不服输,一个星期写好了,程序可以正常使用。
可惜用户没有使用,可能也是因为时间的缘故。
用户说现在都是网页形式,Windows窗口程序使用不方便。
刚好我也在学Web开发,就答应下来,尽快完成。我也想快速掌握这方面的开发技能。
使用的是LayUI+PHP+AXAJ+JSON,数据库开始使用MySQL8,后面换成了MS SQL
Server2014。
这个程序花了整整一个月,都是晚上写,可能是我把需求复杂化了。
现在实在写不下去了,用户的需求也变了,原先是招标文件很长,一般60多页,只是替换里面的一些内容,有列表、单选、多选、手工输入、插入图片、插入Word文件、二选一(选择一个就有内容需要完善)等,刚好Powerbuilder的数据窗口可以有丰富的显示样式,对于内容替换,则是使用书签的方式进行,即书签名称+00XX,这就可以完成多次的替换(XX表示次数)。
现在用户需求变成招标文件只有7页纸,主要是多种样式的文字,还有表格。为了满足这些样式,我设定了多种输入对应:
1、系统函数:自动执行,输出结果;
2、单词:预定义好的词汇;
3、单选列表:选择了输出一个结果;
4、多选列表:选择了输出多种结果,可以分行和不分行输出;
5、二选一:有两种方式,一种是输出单选结果,另外一种是输出方框,前面打不√;
6、手工输入:有简单文字(一行,不弹出输入窗口),有数字、日期、文字(对于数字和日期需要验证,可以限定范围比如最大值和最小值等),多行文字(弹出输入窗口),有长度限制。
写着写着就有了鸡肋的感觉,食之无味,弃之可惜,被这个程序拖住了。
将有用的代码记录下来:
1、在表格的一行里应用radio
主要是使用name和id来区分,name一样,id不一样,这样每行的radio单独起作用。
使用模板语法生成radio
case '二选一':
optionList=d.c18.split('|');
if(d.c19==optionList[0]){
strPSP = "<div class='layui-input-inline'><input type='radio' name='Dc"+rowIDStr+"' id='DcA"+rowIDStr+"' value='"+optionList[0]+"' title='"+optionList[0]+"' lay-filter='Dcradio' checked='checked'> <input type='radio' name='Dc"+rowIDStr+"' id='DcB"+rowIDStr+"' value='"+optionList[1]+"' title='"+optionList[1]+"'></div>";
}else{
strPSP = "<div class='layui-input-inline'><input type='radio' name='Dc"+rowIDStr+"' id='DcA"+rowIDStr+"' value='"+optionList[0]+"' title='"+optionList[0]+"' lay-filter='Dcradio'> <input type='radio' name='Dc"+rowIDStr+"' id='DcB"+rowIDStr+"' value='"+optionList[1]+"' title='"+optionList[1]+"' checked='checked'></div>";
}
return strPSP;
break;
监听操作:
form.on('radio', function(obj){
setTimeout(function(){
if(selectRowNum>=0){
let selectInfo;
let srcSelect=table.cache.projectEditInfo[selectRowNum].c18.split('|');
if(srcSelect[1]==obj.value){
selectInfo=$("#projectEditInfo").next().find("tbody tr[data-index='" +selectRowNum +"'] td[data-field='c19'] div").html();
if(selectInfo=='' || selectInfo==srcSelect[0]){
selectInfo=table.cache.projectEditInfo[selectRowNum].c17;
}
layer.prompt({
formType: 2,
title: '请填写〖'+obj.value+'〗的相关信息',
value: selectInfo,
area: ['500px', '300px']
}, function(value, index){
layer.close(index);
$("#projectEditInfo").next().find("tbody tr[data-index='" +selectRowNum +"'] td[data-field='c19'] div").html(value);
});
}else{
$("#projectEditInfo").next().find("tbody tr[data-index='" +selectRowNum +"'] td[data-field='c19'] div").html(srcSelect[0]);
}
}
},500);
return false;
});
2、在表格中使用下拉列表
使用模板语法生成下拉列表:
case '列表':
selectList=d.c17.split('|');
strPSP="<div class='layui-input-inline' style='width: 300px;'><select lay-filter='DCselect' id='DCselect'+rowIDStr>";
for (var i=0 ; i<selectList.length ; i++ ) {
if(d.c19==selectList[i]){
strPSP = strPSP + "<option value='" + selectList[i] + "' layui-this selected>" + selectList[i] + "</option>" ;
}else{
strPSP = strPSP + "<option value='" + selectList[i] + "'>" + selectList[i] + "</option>" ;
}
}
strPSP=strPSP+"</select></div>";
return strPSP;
break;
监听操作:
form.on('select(DCselect)', function(obj){
if(selectRowNum){
let sTemp='';
let tempList;
tempList=selectRowObj['data']['c17'].split('|');
switch(selectRowObj['data']['c18']){
case '单选':
sTemp=obj.value;
break;
case '全列|方框标识':
for (var i=0 ; i<tempList.length ; i++ ) {
if(obj.value==tempList[i]){
sTemp=sTemp+" √"+tempList[i];
}else{
sTemp=sTemp+" □"+tempList[i];
}
}
break;
case '全列|方框标识|一项一行':
for (var i=0 ; i<tempList.length ; i++ ) {
if(obj.value==tempList[i]){
sTemp=sTemp+" √"+tempList[i]+"\r\n";
}else{
sTemp=sTemp+" □"+tempList[i]+"\r\n";
}
}
break;
}
$("#projectEditInfo").next().find("tbody tr[data-index='" +selectRowNum +"'] td[data-field='c19'] div").html(sTemp);
}
return false;
});
3、在表格中使用手工输入
使用模板语法定义输入样式:
case '手工输入':
let inputType='';
let additionalClass='';
if(d.c04=='数字'){
inputType='number';
}else{
inputType='text';
}
if(d.c04=='日期'){
additionalClass='laydate';
laydateInput.push('Dcinput'+rowIDStr);
}
strPSP="<div class='layui-input-inline' style='width: 300px;'><input type='"+inputType+"' id='Dcinput"+rowIDStr+"' value='"+strContent+"' class='layui-input "+additionalClass+"' lay-filter='DCinput' onkeypress='keyboardInput(event)'></div>";
return strPSP;
break;
监听操作:
function keyboardInput(event){
let inputId=event.srcElement['id'];
if(event.keyCode==13){
let inputVal=document.getElementById(inputId).value;
if(selectRowNum){
layui.$("#projectEditInfo").next().find("tbody tr[data-index='" +selectRowNum +"'] td[data-field='c19'] div").html(inputVal);//改变值
}
}
}
如果双击,判断是多行文本,弹出输入框。
//监听行双击事件
table.on('rowDouble(projectEditInfoFilter)', function (obj) {
selectRowObj=obj;
selectRowNum=obj.tr[0].rowIndex;
if(selectRowObj['data']['c04']=='多行文本'){
let elemID;
let tempRowID=selectRowNum + 1
elemID='Dcinput'+'0'.repeat(4 - tempRowID.toString().length)+tempRowID.toString();
let c19Value=$("#projectEditInfo").next().find("tbody tr[data-index='" +selectRowNum +"'] td[data-field='c19'] div").html();
layer.prompt({
formType: 2,
title: '请填写 〖 '+selectRowObj['data']['c06']+' 〗 的相关信息',
value: c19Value,
area: ['500px', '300px']
}, function(value, index){
document.getElementById(elemID).value=value;
$("#projectEditInfo").next().find("tbody tr[data-index='" +selectRowNum +"'] td[data-field='c19'] div").html(value);
layer.close(index);
});
}
});
4、在表格中使用日期控件
在初始化的时候将日期型的输入控件放到一个数组里,然后一次性完成控件的渲染。
for(let i=0;i<laydateInput.length;i++){
let elemID="#"+laydateInput[i];
let tempRowID=parseInt(laydateInput[i].substr(7,4)) - 1;
let dbDate=initEditData['data'][tempRowID]['c19'];
laydate.render({
elem:elemID,//指定元素
type:'date',
format:'yyyy年MM月dd日',
trigger: 'click',
showBottom:false,
value:dbDate,
done:function(value,date,endDate){
$("#projectEditInfo").next().find("tbody tr[data-index='" +tempRowID +"'] td[data-field='c19'] div").html(value);//改变值
}
});
}
5、表格初始化后要加上下面的代码
$(".layui-table-body, .layui-table-box, .layui-table-cell").css('overflow', 'visible');
form.render('select');
form.render('radio');
6、下载文件
方式有很多,我简单化,隐藏一个a标签,动态更改下载地址,模拟点击。
$.ajax({
url:'XXXX.php',
data:{
"OP":"download",
"Year":selectPorjectRowObj['data']['c01'],
"Project":selectPorjectRowObj['data']['c02'],
"ProjectChild":selectPorjectRowObj['data']['c14']
},
type:'POST',
async:false,
success:function (data) {
document.getElementById('DL').href="http://1.2.3.4/"+data['data'];
document.getElementById('DL').click();
}
});
在开发过程中,被一个存储折腾了很长时间,也是我想复杂了,就是如何记住用户的操作。
开始使用image字段,记录用户操作的html内容,结果折腾了一个星期,各种奇葩问题不断,因为涉及编码和汉字,后面使用模板语法,避免了这些麻烦。
这是个教训,不要总是把简单问题复杂化!!!