td的cellIndex属性被style.display改变

本文深入探讨了在IE7浏览器中,当应用style.display='none'时,td元素的cellIndex属性发生改变的问题,揭示了其背后的原理及解决策略。

IE7下面td的cellIndex属性,居然会随着style.display='none'的设置而改变,真是太恶心了

转载于:https://www.cnblogs.com/hz-blog/p/4897754.html

// JScript 檔 // 主调用函数是 setday(this,[object])和setday(this),[object]是控件输出的控件名,举两个例子: // 一、<input name=txt><input type=button value=setday onclick="setday(this,document.all.txt)"> // 二、<input onfocus="setday(this)"> var bMoveable = true; var strFrame; document.writeln('<iframe id=endDateLayer frameborder=0 width=162 height=211 style="position: absolute; z-index: 9998; display: none"></iframe>'); strFrame = '<style>'; strFrame += 'INPUT.button{BORDER-RIGHT: #63A3E9 1px solid;BORDER-TOP: #63A3E9 1px solid;BORDER-LEFT: #63A3E9 1px solid;'; strFrame += 'BORDER-BOTTOM: #63A3E9 1px solid;BACKGROUND-COLOR: #63A3E9;font-family:宋体;}'; strFrame += 'TD{FONT-SIZE: 9pt;font-family:宋体;}'; strFrame += '</style>'; strFrame += '<scr' + 'ipt>'; strFrame += 'var datelayerx,datelayery;'; strFrame += 'var bDrag;'; strFrame += 'function document.onmousemove()'; strFrame += '{if(bDrag && window.event.button==1)'; strFrame += ' {var DateLayer=parent.document.all.endDateLayer.style;'; strFrame += ' DateLayer.posLeft += window.event.clientX-datelayerx;'; strFrame += ' DateLayer.posTop += window.event.clientY-datelayery;}}'; strFrame += 'function DragStart()'; strFrame += '{var DateLayer=parent.document.all.endDateLayer.style;'; strFrame += ' datelayerx=window.event.clientX;'; strFrame += ' datelayery=window.event.clientY;'; strFrame += ' bDrag=true;}'; strFrame += 'function DragEnd(){'; strFrame += ' bDrag=false;}'; strFrame += '</scr' + 'ipt>'; strFrame += '<div style="z-index:9999;position: absolute; left:0; top:0;" onselectstart="return false">'; strFrame += '<span id=tmpSelectYearLayer style="z-index: 9999;position: absolute;top: 3; left: 19;display: none"></span>'; strFrame += '<span id=tmpSelectMonthLayer style="z-index: 9999;position: absolute;top: 3; left: 78;display: none"></span>'; strFrame += '<span id=tmpSelectHourLayer style="z-index: 9999;position: absolute;top: 188; left: 35px;display: none"></span>'; strFrame += '<span id=tmpSelectMinuteLayer style="z-index:9999;position:absolute;top: 188; left: 77px;display: none"></span>'; strFrame += '<span id=tmpSelectSecondLayer style="z-index:9999;position:absolute;top: 188; left: 119px;display: none"></span>'; strFrame += '<table border=1 cellspacing=0 cellpadding=0 width=142 height=160 bordercolor=#63A3E9 bgcolor=#63A3E9 >'; strFrame += ' <tr><td width=142 height=23 bgcolor=#FFFFFF>'; strFrame += ' <table border=0 cellspacing=1 cellpadding=0 width=158 height=23>'; strFrame += ' <tr align=center >'; strFrame += ' <td width=16 align=center bgcolor=#63A3E9 style="font-size:12px;cursor: hand;color: #ffffff"; display: none'; strFrame += ' onclick="parent.meizzPrevM()" title="向前翻 1 月" ><b ><</b></td>'; strFrame += ' <td width=60 align="center" bgcolor="#63A3E9" style="font-size:12px;cursor:hand" '; strFrame += ' onmouseover="style.backgroundColor=\'#aaccf3\'"'; strFrame += ' onmouseout="style.backgroundColor=\'#63A3E9\'" '; strFrame += ' onclick="parent.tmpSelectYearInnerHTML(this.innerText.substring(0,4))" '; strFrame += ' title="點擊這裡選擇年份"><span id=meizzYearHead></span></td>'; strFrame += ' <td width=48 align="center" style="font-size:12px;font-color: #ffffff;cursor:hand" '; strFrame += ' bgcolor="#63A3E9" onmouseover="style.backgroundColor=\'#aaccf3\'" '; strFrame += ' onmouseout="style.backgroundColor=\'#63A3E9\'" '; strFrame += ' onclick="parent.tmpSelectMonthInnerHTML(this.innerText.length==3?this.innerText.substring(0,1):this.innerText.substring(0,2))"'; strFrame += ' title="點擊這裡選擇月份"><span id=meizzMonthHead ></span></td>'; strFrame += ' <td width=16 bgcolor=#63A3E9 align=center style="font-size:12px;cursor: hand;color: #ffffff" '; strFrame += ' onclick="parent.meizzNextM()" title="向后翻 1 月" ><b >></b></td>'; strFrame += ' </tr>'; strFrame += ' </table></td></tr>'; strFrame += ' <tr><td width=142 height=18 >'; strFrame += ' <table border=0 cellspacing=0 cellpadding=2 bgcolor=#63A3E9 ' + (bMoveable ? 'onmousedown="DragStart()" onmouseup="DragEnd()"' : ''); strFrame += ' BORDERCOLORLIGHT=#63A3E9 BORDERCOLORDARK=#FFFFFF width=140 height=20 style="cursor:' + (bMoveable ? 'move' : 'default') + '">'; strFrame += ' <tr><td style="font-size:12px;color:#ffffff" width=20> 日</td>'; strFrame += '<td style="font-size:12px;color:#FFFFFF" > 一</td><td style="font-size:12px;color:#FFFFFF"> 二</td>'; strFrame += '<td style="font-size:12px;color:#FFFFFF" > 三</td><td style="font-size:12px;color:#FFFFFF" > 四</td>'; strFrame += '<td style="font-size:12px;color:#FFFFFF" > 五</td><td style="font-size:12px;color:#FFFFFF" > 六</td></tr>'; strFrame += '</table></td></tr>'; strFrame += ' <tr ><td width=142 height=120 >'; strFrame += ' <table border=1 cellspacing=2 cellpadding=2 BORDERCOLORLIGHT=#63A3E9 BORDERCOLORDARK=#FFFFFF bgcolor=#fff8ec width=140 height=120 >'; var n = 0; for (j = 0; j < 5; j++) { strFrame += ' <tr align=center >'; for (i = 0; i < 7; i++) { strFrame += '<td width=20 height=20 id=meizzDay' + n + ' style="font-size:12px" onclick=parent.meizzDayClick(this.innerText,0)></td>'; n++; } strFrame += '</tr>'; } strFrame += ' <tr align=center >'; for (i = 35; i < 37; i++) strFrame += '<td width=20 height=20 id=meizzDay' + i + ' style="font-size:12px" onclick="parent.meizzDayClick(this.innerText,0)"></td>'; strFrame += ' <td colspan=5 align=right style="color:#1478eb"><span onclick="parent.setNull()" style="font-size:12px;cursor: hand"'; strFrame += ' onmouseover="style.color=\'#ff0000\'" onmouseout="style.color=\'#1478eb\'" title="将日期置空">置空</span> <span onclick="parent.meizzToday()" style="font-size:12px;cursor: hand"'; strFrame += ' onmouseover="style.color=\'#ff0000\'" onmouseout="style.color=\'#1478eb\'" title="当前日期时间">當前</span> <span style="cursor:hand" id=evaAllOK onmouseover="style.color=\'#ff0000\'" onmouseout="style.color=\'#1478eb\'" onclick="parent.closeLayer()" title="關閉日曆">確定 </span></td></tr>'; strFrame += ' </table></td></tr><tr visible="false"><td >'; strFrame += ' <table border=0 cellspacing=1 cellpadding=0 width=100% bgcolor=#FFFFFF height=22 >'; strFrame += ' <tr bgcolor="#63A3E9"><td id=bUseTimeLayer width=30 style="cursor:hand" title="點擊這裡啟用/禁用時間"'; strFrame += ' onmouseover="style.backgroundColor=\'#aaccf3\'" align=center onmouseout="style.backgroundColor=\'#63A3E9\'"'; strFrame += ' onclick="parent.UseTime(this)">'; strFrame += ' <span></span></td>'; strFrame += ' <td style="cursor:hand" onclick="parent.tmpSelectHourInnerHTML(this.innerText.length==3?this.innerText.substring(0,1):this.innerText.substring(0,2))"'; strFrame += ' onmouseover="style.backgroundColor=\'#aaccf3\'" onmouseout="style.backgroundColor=\'#63A3E9\'"'; strFrame += ' title="點擊這裡選擇時間" align=center width=42>'; strFrame += ' <span id=meizzHourHead></span></td>'; strFrame += ' <td style="cursor:hand" onclick="parent.tmpSelectMinuteInnerHTML(this.innerText.length==3?this.innerText.substring(0,1):this.innerText.substring(0,2))"'; strFrame += ' onmouseover="style.backgroundColor=\'#aaccf3\'" onmouseout="style.backgroundColor=\'#63A3E9\'"'; strFrame += ' title="點擊這裡選擇時間" align=center width=42>'; strFrame += ' <span id=meizzMinuteHead></span></td>'; strFrame += ' <td style="cursor:hand" onclick="parent.tmpSelectSecondInnerHTML(this.innerText.length==3?this.innerText.substring(0,1):this.innerText.substring(0,2))"'; strFrame += ' onmouseover="style.backgroundColor=\'#aaccf3\'" onmouseout="style.backgroundColor=\'#63A3E9\'"'; strFrame += ' title="點擊這裡選擇時間" align=center width=42>'; strFrame += ' <span id=meizzSecondHead></span></td>'; strFrame += ' </tr></table></td></tr></table></div>'; window.frames.endDateLayer.document.writeln(strFrame); window.frames.endDateLayer.document.close(); //解决ie进度条不结束的问题 //==================================================== WEB 页面显示部分 ====================================================== var outObject; var outButton; //点击的按钮 var outDate = ""; //存放对象的日期 var bUseTime = false; //是否使用时间 var odatelayer = window.frames.endDateLayer.document.all; //存放日历对象 var odatelayer = window.endDateLayer.document.all; //odatelayer.bUseTimeLayer.innerText="NO"; bImgSwitch(); odatelayer.bUseTimeLayer.innerHTML = bImg; function setday(tt, obj) //主调函数 { if (arguments.length > 2) { alert("对不起!传入本控件的参数太多!"); return; } if (arguments.length == 0) { alert("对不起!您没有传回本控件任何参数!"); return; } var dads = document.all.endDateLayer.style; var th = tt; var ttop = tt.offsetTop; //TT控件的定位点高 var thei = tt.clientHeight; //TT控件本身的高 var tleft = tt.offsetLeft; //TT控件的定位点宽 var ttyp = tt.type; //TT控件的类型 while (tt = tt.offsetParent) { ttop += tt.offsetTop; tleft += tt.offsetLeft; } dads.top = (ttyp == "image") ? ttop + thei : ttop + thei + 6; dads.left = tleft; outObject = (arguments.length == 1) ? th : obj; outButton = (arguments.length == 1) ? null : th; //设定外部点击的按钮 //根据当前输入框的日期显示日历的年月 var reg = /^(\d+)-(\d{1,2})-(\d{1,2})/; //不含时间 var r = outObject.value.match(reg); if (r != null) { r[2] = r[2] - 1; var d = new Date(r[1], r[2], r[3]); if (d.getFullYear() == r[1] && d.getMonth() == r[2] && d.getDate() == r[3]) { outDate = d; parent.meizzTheYear = r[1]; parent.meizzTheMonth = r[2]; parent.meizzTheDate = r[3]; } else { outDate = ""; } meizzSetDay(r[1], r[2] + 1); } else { outDate = ""; meizzSetDay(new Date().getFullYear(), new Date().getMonth() + 1); } dads.display = ''; //判断初始化时是否使用时间,非严格验证 //if (outObject.value.length>10) //{ bUseTime = true; bImgSwitch(); odatelayer.bUseTimeLayer.innerHTML = bImg; meizzWriteHead(meizzTheYear, meizzTheMonth); //} //else //{ // bUseTime=false; // bImgSwitch(); // odatelayer.bUseTimeLayer.innerHTML=bImg; // meizzWriteHead(meizzTheYear,meizzTheMonth); //} try { event.returnValue = false; } catch (e) { //此处排除错误,错误原因暂未找到。 } } var MonHead = new Array(12); //定义阳历中每个月的最大天数 MonHead[0] = 31; MonHead[1] = 28; MonHead[2] = 31; MonHead[3] = 30; MonHead[4] = 31; MonHead[5] = 30; MonHead[6] = 31; MonHead[7] = 31; MonHead[8] = 30; MonHead[9] = 31; MonHead[10] = 30; MonHead[11] = 31; var meizzTheYear = new Date().getFullYear(); //定义年的变量的初始值 var meizzTheMonth = new Date().getMonth() + 1; //定义月的变量的初始值 var meizzTheDate = new Date().getDate(); //定义日的变量的初始值 var meizzTheHour = new Date().getHours(); //定义小时变量的初始值 var meizzTheMinute = new Date().getMinutes(); //定义分钟变量的初始值 var meizzTheSecond = new Date().getSeconds(); //定义秒变量的初始值 var meizzWDay = new Array(37); //定义写日期的数组 function document.onclick() //任意点击时关闭该控件 //ie6的情况可以由下面的切换焦点处理代替 { with (window.event) { if (srcElement != outObject && srcElement != outButton) closeLayer(); } } function document.onkeyup() //按Esc键关闭,切换焦点关闭 { if (window.event.keyCode == 27) { if (outObject) outObject.blur(); closeLayer(); } else if (document.activeElement) { if (document.activeElement != outObject && document.activeElement != outButton) { closeLayer(); } } } function meizzWriteHead(yy, mm, ss) //往 head 中写入当前的年与月 { odatelayer.meizzYearHead.innerText = yy + " 年"; odatelayer.meizzMonthHead.innerText = format(mm) + " 月"; //插入当前小时、分 odatelayer.meizzHourHead.innerText = bUseTime ? (meizzTheHour + " 时") : ""; odatelayer.meizzMinuteHead.innerText = bUseTime ? (meizzTheMinute + " 分") : ""; odatelayer.meizzSecondHead.innerText = bUseTime ? (meizzTheSecond + " 秒") : ""; } function tmpSelectYearInnerHTML(strYear) //年份的下拉框 { if (strYear.match(/\D/) != null) { alert("年份输入参数不是数字!"); return; } var m = (strYear) ? strYear : new Date().getFullYear(); if (m < 1000 || m > 9999) { alert("年份值不在 1000 到 9999 之间!"); return; } var n = m - 50; if (n < 1000) n = 1000; if (n + 101 > 9999) n = 9974; var s = " <select name=tmpSelectYear style='font-size: 12px' " s += "onblur='document.all.tmpSelectYearLayer.style.display=\"none\"' " s += "onchange='document.all.tmpSelectYearLayer.style.display=\"none\";" s += "parent.meizzTheYear = this.value; parent.meizzSetDay(parent.meizzTheYear,parent.meizzTheMonth)'>\r\n"; var selectInnerHTML = s; for (var i = n; i < n + 101; i++) { if (i == m) { selectInnerHTML += "<option value='" + i + "' selected>" + i + "年" + "</option>\r\n"; } else { selectInnerHTML += "<option value='" + i + "'>" + i + "年" + "</option>\r\n"; } } selectInnerHTML += "</select>"; odatelayer.tmpSelectYearLayer.style.display = ""; odatelayer.tmpSelectYearLayer.innerHTML = selectInnerHTML; odatelayer.tmpSelectYear.focus(); } function tmpSelectMonthInnerHTML(strMonth) //月份的下拉框 { if (strMonth.match(/\D/) != null) { alert("月份输入参数不是数字!"); return; } var m = (strMonth) ? strMonth : new Date().getMonth() + 1; var s = " <select name=tmpSelectMonth style='font-size: 12px' " s += "onblur='document.all.tmpSelectMonthLayer.style.display=\"none\"' " s += "onchange='document.all.tmpSelectMonthLayer.style.display=\"none\";" s += "parent.meizzTheMonth = this.value; parent.meizzSetDay(parent.meizzTheYear,parent.meizzTheMonth)'>\r\n"; var selectInnerHTML = s; for (var i = 1; i < 13; i++) { if (i == m) { selectInnerHTML += "<option value='" + i + "' selected>" + i + "月" + "</option>\r\n"; } else { selectInnerHTML += "<option value='" + i + "'>" + i + "月" + "</option>\r\n"; } } selectInnerHTML += "</select>"; odatelayer.tmpSelectMonthLayer.style.display = ""; odatelayer.tmpSelectMonthLayer.innerHTML = selectInnerHTML; odatelayer.tmpSelectMonth.focus(); } /**//***** 增加 小时、分钟 ***/ function tmpSelectHourInnerHTML(strHour) //小时的下拉框 { if (!bUseTime) { return; } if (strHour.match(/\D/) != null) { alert("小时输入参数不是数字!"); return; } var m = (strHour) ? strHour : new Date().getHours(); var s = "<select name=tmpSelectHour style='font-size: 12px' " s += "onblur='document.all.tmpSelectHourLayer.style.display=\"none\"' " s += "onchange='document.all.tmpSelectHourLayer.style.display=\"none\";" s += "parent.meizzTheHour = this.value; parent.evaSetTime(parent.meizzTheHour,parent.meizzTheMinute);'>\r\n"; var selectInnerHTML = s; for (var i = 0; i < 24; i++) { if (i == m) { selectInnerHTML += "<option value='" + i + "' selected>" + i + "</option>\r\n"; } else { selectInnerHTML += "<option value='" + i + "'>" + i + "</option>\r\n"; } } selectInnerHTML += "</select>"; odatelayer.tmpSelectHourLayer.style.display = ""; odatelayer.tmpSelectHourLayer.innerHTML = selectInnerHTML; odatelayer.tmpSelectHour.focus(); } function tmpSelectMinuteInnerHTML(strMinute) //分钟的下拉框 { if (!bUseTime) { return; } if (strMinute.match(/\D/) != null) { alert("分钟输入参数不是数字!"); return; } var m = (strMinute) ? strMinute : new Date().getMinutes(); var s = "<select name=tmpSelectMinute style='font-size: 12px' " s += "onblur='document.all.tmpSelectMinuteLayer.style.display=\"none\"' " s += "onchange='document.all.tmpSelectMinuteLayer.style.display=\"none\";" s += "parent.meizzTheMinute = this.value; parent.evaSetTime(parent.meizzTheHour,parent.meizzTheMinute);'>\r\n"; var selectInnerHTML = s; for (var i = 0; i < 60; i++) { // if (i == m) { selectInnerHTML += "<option value='"+i+"' selected>"+i+"</option>\r\n"; } // else { selectInnerHTML += "<option value='"+i+"'>"+i+"</option>\r\n"; } if (i == m) { if (i < 10) selectInnerHTML += "<option value='0" + i + "' selected>0" + i + "</option>\r\n"; else selectInnerHTML += "<option value='" + i + "' selected>" + i + "</option>\r\n"; } else { if (i < 10) selectInnerHTML += "<option value='0" + i + "'>0" + i + "</option>\r\n"; else selectInnerHTML += "<option value='" + i + "'>" + i + "</option>\r\n"; } } selectInnerHTML += "</select>"; odatelayer.tmpSelectMinuteLayer.style.display = ""; odatelayer.tmpSelectMinuteLayer.innerHTML = selectInnerHTML; odatelayer.tmpSelectMinute.focus(); } function tmpSelectSecondInnerHTML(strSecond) //秒的下拉框 { if (!bUseTime) { return; } if (strSecond.match(/\D/) != null) { alert("分钟输入参数不是数字!"); return; } var m = (strSecond) ? strSecond : new Date().getMinutes(); var s = "<select name=tmpSelectSecond style='font-size: 12px' " s += "onblur='document.all.tmpSelectSecondLayer.style.display=\"none\"' " s += "onchange='document.all.tmpSelectSecondLayer.style.display=\"none\";" s += "parent.meizzTheSecond = this.value; parent.evaSetTime(parent.meizzTheHour,parent.meizzTheMinute,parent.meizzTheSecond);'>\r\n"; var selectInnerHTML = s; for (var i = 0; i < 60; i++) { // if (i == m) { selectInnerHTML += "<option value='"+i+"' selected>"+i+"</option>\r\n"; } // else { selectInnerHTML += "<option value='"+i+"'>"+i+"</option>\r\n"; } if (i == m) { if (i < 10) selectInnerHTML += "<option value='0" + i + "' selected>0" + i + "</option>\r\n"; else selectInnerHTML += "<option value='" + i + "' selected>" + i + "</option>\r\n"; } else { if (i < 10) selectInnerHTML += "<option value='0" + i + "'>0" + i + "</option>\r\n"; else selectInnerHTML += "<option value='" + i + "'>" + i + "</option>\r\n"; } } selectInnerHTML += "</select>"; odatelayer.tmpSelectSecondLayer.style.display = ""; odatelayer.tmpSelectSecondLayer.innerHTML = selectInnerHTML; odatelayer.tmpSelectSecond.focus(); } function closeLayer() //这个层的关闭 { var o = document.getElementById("endDateLayer"); if (o != null) { o.style.display = "none"; } } function showLayer() //这个层的关闭 { document.all.endDateLayer.style.display = ""; } function IsPinYear(year) //判断是否闰平年 { if (0 == year % 4 && ((year % 100 != 0) || (year % 400 == 0))) return true; else return false; } function GetMonthCount(year, month) //闰年二月为29天 { var c = MonHead[month - 1]; if ((month == 2) && IsPinYear(year)) c++; return c; } function GetDOW(day, month, year) //求某天的星期几 { var dt = new Date(year, month - 1, day).getDay() / 7; return dt; } function meizzPrevY() //往前翻 Year { if (meizzTheYear > 999 && meizzTheYear < 10000) { meizzTheYear--; } else { alert("年份超出范围(1000-9999)!"); } meizzSetDay(meizzTheYear, meizzTheMonth); } function meizzNextY() //往后翻 Year { if (meizzTheYear > 999 && meizzTheYear < 10000) { meizzTheYear++; } else { alert("年份超出范围(1000-9999)!"); } meizzSetDay(meizzTheYear, meizzTheMonth); } function setNull() { outObject.value = ''; closeLayer(); } function meizzToday() //Today Button { parent.meizzTheYear = new Date().getFullYear(); parent.meizzTheMonth = new Date().getMonth() + 1; parent.meizzTheDate = new Date().getDate(); parent.meizzTheHour = new Date().getHours(); parent.meizzTheMinute = new Date().getMinutes(); parent.meizzTheSecond = new Date().getSeconds(); var meizzTheSecond = new Date().getSeconds(); if (meizzTheMonth < 10 && meizzTheMonth.length < 2) //格式化成两位数字 { parent.meizzTheMonth = "0" + parent.meizzTheMonth; } if (parent.meizzTheDate < 10 && parent.meizzTheDate.length < 2) //格式化成两位数字 { parent.meizzTheDate = "0" + parent.meizzTheDate; } //meizzSetDay(meizzTheYear,meizzTheMonth); if (outObject) { if (bUseTime) { outObject.value = parent.meizzTheYear + "/" + format(parent.meizzTheMonth) + "/" + format(parent.meizzTheDate) + " " + format(parent.meizzTheHour) + ":" + format(parent.meizzTheMinute) //+ ":" + format(parent.meizzTheSecond); //注:在这里你可以输出改成你想要的格式 } else { outObject.value = parent.meizzTheYear + "-" + format(parent.meizzTheMonth) + "-" + format(parent.meizzTheDate); //注:在这里你可以输出改成你想要的格式 } } closeLayer(); } function meizzPrevM() //往前翻月份 { if (meizzTheMonth > 1) { meizzTheMonth-- } else { meizzTheYear--; meizzTheMonth = 12; } meizzSetDay(meizzTheYear, meizzTheMonth); } function meizzNextM() //往后翻月份 { if (meizzTheMonth == 12) { meizzTheYear++; meizzTheMonth = 1 } else { meizzTheMonth++ } meizzSetDay(meizzTheYear, meizzTheMonth); } // TODO: 整理代码 function meizzSetDay(yy, mm) //主要的写程序********** { meizzWriteHead(yy, mm); //设置当前年月的公共变量为传入值 meizzTheYear = yy; meizzTheMonth = mm; for (var i = 0; i < 37; i++) { meizzWDay[i] = "" }; //将显示框的内容全部清空 var day1 = 1, day2 = 1, firstday = new Date(yy, mm - 1, 1).getDay(); //某月第一天的星期几 for (i = 0; i < firstday; i++) meizzWDay[i] = GetMonthCount(mm == 1 ? yy - 1 : yy, mm == 1 ? 12 : mm - 1) - firstday + i + 1 //上个月的最后几天 for (i = firstday; day1 < GetMonthCount(yy, mm) + 1; i++) { meizzWDay[i] = day1; day1++; } for (i = firstday + GetMonthCount(yy, mm); i < 37; i++) { meizzWDay[i] = day2; day2++; } for (i = 0; i < 37; i++) { var da = eval("odatelayer.meizzDay" + i) //书写新的一个月的日期星期排列 if (meizzWDay[i] != "") { //初始化边框 da.borderColorLight = "#63A3E9"; da.borderColorDark = "#63A3E9"; da.style.color = "#1478eb"; if (i < firstday) //上个月的部分 { da.innerHTML = "<b><font color=#BCBABC>" + meizzWDay[i] + "</font></b>"; da.title = (mm == 1 ? 12 : mm - 1) + "月" + meizzWDay[i] + "日"; da.onclick = Function("meizzDayClick(this.innerText,-1)"); if (!outDate) da.style.backgroundColor = ((mm == 1 ? yy - 1 : yy) == new Date().getFullYear() && (mm == 1 ? 12 : mm - 1) == new Date().getMonth() + 1 && meizzWDay[i] == new Date().getDate()) ? "#5CEFA0" : "#f5f5f5"; else { da.style.backgroundColor = ((mm == 1 ? yy - 1 : yy) == outDate.getFullYear() && (mm == 1 ? 12 : mm - 1) == outDate.getMonth() + 1 && meizzWDay[i] == outDate.getDate()) ? "#84C1FF" : (((mm == 1 ? yy - 1 : yy) == new Date().getFullYear() && (mm == 1 ? 12 : mm - 1) == new Date().getMonth() + 1 && meizzWDay[i] == new Date().getDate()) ? "#5CEFA0" : "#f5f5f5"); //将选中的日期显示为凹下去 if ((mm == 1 ? yy - 1 : yy) == outDate.getFullYear() && (mm == 1 ? 12 : mm - 1) == outDate.getMonth() + 1 && meizzWDay[i] == outDate.getDate()) { da.borderColorLight = "#FFFFFF"; da.borderColorDark = "#63A3E9"; } } } else if (i >= firstday + GetMonthCount(yy, mm)) //下个月的部分 { da.innerHTML = "<b><font color=#BCBABC>" + meizzWDay[i] + "</font></b>"; da.title = (mm == 12 ? 1 : mm + 1) + "月" + meizzWDay[i] + "日"; da.onclick = Function("meizzDayClick(this.innerText,1)"); if (!outDate) da.style.backgroundColor = ((mm == 12 ? yy + 1 : yy) == new Date().getFullYear() && (mm == 12 ? 1 : mm + 1) == new Date().getMonth() + 1 && meizzWDay[i] == new Date().getDate()) ? "#5CEFA0" : "#f5f5f5"; else { da.style.backgroundColor = ((mm == 12 ? yy + 1 : yy) == outDate.getFullYear() && (mm == 12 ? 1 : mm + 1) == outDate.getMonth() + 1 && meizzWDay[i] == outDate.getDate()) ? "#84C1FF" : (((mm == 12 ? yy + 1 : yy) == new Date().getFullYear() && (mm == 12 ? 1 : mm + 1) == new Date().getMonth() + 1 && meizzWDay[i] == new Date().getDate()) ? "#5CEFA0" : "#f5f5f5"); //将选中的日期显示为凹下去 if ((mm == 12 ? yy + 1 : yy) == outDate.getFullYear() && (mm == 12 ? 1 : mm + 1) == outDate.getMonth() + 1 && meizzWDay[i] == outDate.getDate()) { da.borderColorLight = "#FFFFFF"; da.borderColorDark = "#63A3E9"; } } } else //本月的部分 { da.innerHTML = "<b>" + meizzWDay[i] + "</b>"; da.title = mm + "月" + meizzWDay[i] + "日"; da.onclick = Function("meizzDayClick(this.innerText,0)"); //给td赋予onclick事件的处理 //如果是当前选择的日期,则显示亮蓝色的背景;如果是当前日期,则显示暗黄色背景 if (!outDate) da.style.backgroundColor = (yy == new Date().getFullYear() && mm == new Date().getMonth() + 1 && meizzWDay[i] == new Date().getDate()) ? "#5CEFA0" : "#f5f5f5"; else { da.style.backgroundColor = (yy == outDate.getFullYear() && mm == outDate.getMonth() + 1 && meizzWDay[i] == outDate.getDate()) ? "#84C1FF" : ((yy == new Date().getFullYear() && mm == new Date().getMonth() + 1 && meizzWDay[i] == new Date().getDate()) ? "#5CEFA0" : "#f5f5f5"); //将选中的日期显示为凹下去 if (yy == outDate.getFullYear() && mm == outDate.getMonth() + 1 && meizzWDay[i] == outDate.getDate()) { da.borderColorLight = "#FFFFFF"; da.borderColorDark = "#63A3E9"; } } } da.style.cursor = "hand" } else { da.innerHTML = ""; da.style.backgroundColor = ""; da.style.cursor = "default"; } } } function meizzDayClick(n, ex) //点击显示框选取日期,主输入函数************* { parent.meizzTheDate = n; var yy = meizzTheYear; var mm = parseInt(meizzTheMonth) + ex; //ex表示偏移量,用于选择上个月份和下个月份的日期 var hh = meizzTheHour; var mi = meizzTheMinute; var se = meizzTheSecond; //判断月份,并进行对应的处理 if (mm < 1) { yy--; mm = 12 + mm; } else if (mm > 12) { yy++; mm = mm - 12; } if (mm < 10) { mm = "0" + mm; } if (hh < 10) { hh = "0" + hh; } //时 // if (mi<10) {mi="0" + mi;} //分 if (mi < 10) { mi = "" + mi; } //分 if (se < 10) { se = "0" + se; } //秒 if (outObject) { if (!n) { //outObject.value=""; return; } if (n < 10) { n = "0" + n; } WriteDateTo(yy, mm, n, hh, mi, se); closeLayer(); if (bUseTime) { try { outButton.click(); } catch (e) { setday(outObject); } } } else { closeLayer(); alert("您所要输出的控件对象并不存在!"); } } function format(n) //格式化数字为两位字符表示 { var m = new String(); var tmp = new String(n); if (n < 10 && tmp.length < 2) { m = "0" + n; } else { m = n; } return m; } function evaSetTime() //设置用户选择的小时、分钟 { odatelayer.meizzHourHead.innerText = meizzTheHour + " 时"; odatelayer.meizzMinuteHead.innerText = meizzTheMinute + " 分"; odatelayer.meizzSecondHead.innerText = meizzTheSecond + " 秒"; WriteDateTo(meizzTheYear, meizzTheMonth, meizzTheDate, meizzTheHour, meizzTheMinute, meizzTheSecond) } function evaSetTimeNothing() //设置时间控件为空 { odatelayer.meizzHourHead.innerText = ""; odatelayer.meizzMinuteHead.innerText = ""; odatelayer.meizzSecondHead.innerText = ""; WriteDateTo(meizzTheYear, meizzTheMonth, meizzTheDate, meizzTheHour, meizzTheMinute, meizzTheSecond) } function evaSetTimeNow() //设置时间控件为当前时间 { odatelayer.meizzHourHead.innerText = new Date().getHours() + " 时"; odatelayer.meizzMinuteHead.innerText = new Date().getMinutes() + " 分"; odatelayer.meizzSecondHead.innerText = new Date().getSeconds() + " 秒"; meizzTheHour = new Date().getHours(); meizzTheMinute = new Date().getMinutes(); meizzTheSecond = new Date().getSeconds(); WriteDateTo(meizzTheYear, meizzTheMonth, meizzTheDate, meizzTheHour, meizzTheMinute, meizzTheSecond) } function UseTime(ctl) { bUseTime = !bUseTime; if (bUseTime) { bImgSwitch(); ctl.innerHTML = bImg; evaSetTime(); //显示时间,用户原来选择的时间 //evaSetTimeNow(); //显示当前时间 } else { bImgSwitch(); ctl.innerHTML = bImg; evaSetTimeNothing(); } } function WriteDateTo(yy, mm, n, hh, mi, se) { if (bUseTime) { //outObject.value= yy + "/" + format(mm) + "/" + format(n) + " " + format(hh) + ":" + format(mi) + ":" + format(se); //注:在这里你可以输出改成你想要的格式 outObject.value = yy + "/" + format(mm) + "/" + format(n) + " " + format(hh) + ":" + format(mi); } else { //outObject.value= format(mm) + "/" + format(n)+"/"+yy; outObject.value = yy + "/" + format(mm) + "/" + format(n); //注:在这里你可以输出改成你想要的格式 } } function bImgSwitch() { if (bUseTime) { bImg = "開啟"; } else { bImg = "確定"; } } 这是日期控件的内容
最新发布
09-11
// 监听来自父窗口的消息 window.addEventListener('message', function(event) { if (event.source === window.parent && event.data === 'DataManagerReady') { initializeDataManager(); } }); // 初始化数据管理器 function initializeDataManager() { const dataManager = window.parent.dataManager; if (dataManager) { // 注册数据刷新回调 dataManager.registerCallback('all', function(operation, entity, data) { updatePageData(dataManager); }); // 初始页面数据更新 updatePageData(dataManager); // 设置搜索功能 setupSearch(dataManager); } else { console.error('DataManager not available'); } } // 更新页面数据 function updatePageData(dataManager) { updateStats(dataManager); updateTable(dataManager); document.getElementById('lastUpdate').textContent = new Date().toLocaleTimeString(); } // 更新统计卡片数据 function updateStats(dataManager) { const data = dataManager.data; document.getElementById('orderCount').textContent = data.dingdans?.length || 0; document.getElementById('productCount').textContent = data.chanpins?.length || 0; document.getElementById('materialCount').textContent = data.bancais?.length || 0; const totalStock = data.kucuns?.reduce((sum, kucun) => sum + kucun.shuliang, 0) || 0; document.getElementById('totalStock').textContent = totalStock; } // 更新结果表格 function updateTable(dataManager) { const tableBody = document.getElementById('resultBody'); tableBody.innerHTML = ''; const data = dataManager.data; const results = []; let resultCount = 0; // 处理订单产品数据 if (data.dingdan_chanpins) { data.dingdan_chanpins.forEach(dc => { if (!dc.dingdan || !dc.chanpin) return; if (dc.chanpin.chanpin_zujians) { dc.chanpin.chanpin_zujians.forEach(cz => { if (!cz.zujian || !cz.bancai) return; const bancai = cz.bancai; const materialInfo = getMaterialInfo(bancai); const stockQuantity = bancai.kucun?.shuliang || 0; results.push({ orderNumber: dc.dingdan.number, productInfo: dc.chanpin.bianhao, productQuantity: dc.shuliang, component: cz.zujian.name, material: materialInfo, materialPerComponent: cz.one_howmany, materialOrderQuantity: dc.shuliang * cz.one_howmany, stockQuantity: stockQuantity, operation: `<button class="btn btn-sm btn-outline-primary" onclick="showMaterialDetail(${bancai.id})">详情</button>` }); resultCount++; }); } }); } // 处理直接订单组件数据 if (data.dingdan_chanpin_zujians) { data.dingdan_chanpin_zujians.forEach(dcz => { if (!dcz.dingdan || !dcz.chanpin_zujian || !dcz.chanpin_zujian.zujian || !dcz.bancai) return; const bancai = dcz.bancai; const materialInfo = getMaterialInfo(bancai); const stockQuantity = bancai.kucun?.shuliang || 0; results.push({ orderNumber: dcz.dingdan.number, productInfo: dcz.chanpin_zujian.chanpin?.bianhao || '独立组件', productQuantity: dcz.shuliang, component: dcz.chanpin_zujian.zujian.name, material: materialInfo, materialPerComponent: dcz.chanpin_zujian.one_howmany, materialOrderQuantity: dcz.shuliang * dcz.chanpin_zujian.one_howmany, stockQuantity: stockQuantity, operation: `<button class="btn btn-sm btn-outline-primary" onclick="showMaterialDetail(${bancai.id})">详情</button>` }); resultCount++; }); } // 填充表格 if (resultCount > 0) { document.getElementById('noResults').style.display = 'none'; results.forEach(row => { const tr = document.createElement('tr'); tr.innerHTML = ` <td>${row.orderNumber}</td> <td>${row.productInfo}</td> <td>${row.productQuantity}</td> <td>${row.component}</td> <td>${row.material}</td> <td>${row.materialPerComponent}</td> <td>${row.materialOrderQuantity}</td> <td>${row.stockQuantity}</td> <td>${row.operation}</td> `; tableBody.appendChild(tr); }); } else { document.getElementById('noResults').style.display = 'flex'; } document.getElementById('resultCount').textContent = resultCount; } // 获取板材详细信息函数(添加油状态) function getMaterialInfo(bancai) { if (!bancai) return '未知板材'; // 获取材质名称 const caizhiName = bancai.caizhi?.name || '未知材质'; // 获取木皮信息(添加油状态) const formatMupi = (mupi) => { if (!mupi) return null; return `${mupi.name}${mupi.you ? '(油)' : ''}`; }; const mupi1Str = formatMupi(bancai.mupi1) || '无'; const mupi2Str = formatMupi(bancai.mupi2) || '无'; const mupiInfo = mupi1Str + (mupi2Str !== '无' ? `/${mupi2Str}` : ''); // 厚度转换为字符串 const thickness = bancai.houdu ? `${bancai.houdu.toFixed(1)}mm` : '未知厚度'; return `${caizhiName} - ${mupiInfo} (${thickness})`; } // 设置搜索功能(添加油状态搜索) function setupSearch(dataManager) { // 订单搜索 $('#orderSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 0); }); // 产品搜索 $('#productSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 1); }); // 板材材质搜索 $('#materialSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 4); }); // 木皮搜索(添加油状态搜索) $('#woodSearch').on('input', function() { const searchTerm = $(this).val().toLowerCase(); // 支持中文和数字搜索 const searchKeywords = { '有油': '有油', '无油': '无油', 'you': '有油', 'wu': '无油', '1': '有油', '0': '无油' }; const actualTerm = searchKeywords[searchTerm] || searchTerm; filterTable(actualTerm, 4); }); // 厚度搜索(字符串匹配) $('#thicknessSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 4); }); // 库存状态搜索 $('#stockStatusBtn').click(function() { const minStock = parseInt($('#minStock').val()) || 0; const maxStock = parseInt($('#maxStock').val()) || Number.MAX_SAFE_INTEGER; filterByStock(minStock, maxStock); }); } // 表格过滤函数(支持油状态搜索) function filterTable(searchTerm, columnIndex) { const rows = $('#resultBody tr'); let visibleCount = 0; rows.each(function() { const cellText = $(this).find(`td:eq(${columnIndex})`).text().toLowerCase(); let isMatch = false; // 特殊处理油状态搜索 if (searchTerm === '有油' || searchTerm === '无油') { isMatch = cellText.includes(searchTerm); } // 处理厚度字符串匹配 else if (columnIndex === 4 && !isNaN(parseFloat(searchTerm))) { // 提取厚度数字部分进行匹配 const thicknessMatch = cellText.match(/$(\d+\.?\d*)mm$/); if (thicknessMatch) { const thicknessValue = parseFloat(thicknessMatch[1]); const searchValue = parseFloat(searchTerm); // 允许小数点误差 isMatch = Math.abs(thicknessValue - searchValue) < 0.1; } } // 常规文本匹配 else { isMatch = cellText.includes(searchTerm); } $(this).toggle(isMatch); if (isMatch && searchTerm) { $(this).addClass('highlight'); } else { $(this).removeClass('highlight'); } if (isMatch) visibleCount++; }); document.getElementById('resultCount').textContent = visibleCount; document.getElementById('noResults').style.display = visibleCount > 0 ? 'none' : 'flex'; } // 按库存量过滤 function filterByStock(minStock, maxStock) { const rows = $('#resultBody tr'); let visibleCount = 0; rows.each(function() { const stockCell = $(this).find('td:eq(7)').text(); const stockValue = parseInt(stockCell) || 0; const isMatch = stockValue >= minStock && stockValue <= maxStock; $(this).toggle(isMatch); if (isMatch) visibleCount++; }); document.getElementById('resultCount').textContent = visibleCount; document.getElementById('noResults').style.display = visibleCount > 0 ? 'none' : 'flex'; } // 显示板材详情(添加油状态显示) function showMaterialDetail(bancaiId) { const dataManager = window.parent.dataManager; if (!dataManager) return; const bancais = dataManager.data.bancais || []; const bancai = bancais.find(b => b.id === bancaiId); if (!bancai) { alert('未找到板材信息'); return; } // 格式化木皮信息(添加油状态) const formatMupiDetail = (mupi) => { if (!mupi) return '无'; return `${mupi.name} (${mupi.you ? '有油' : '无油'})`; }; // 构建详情信息 const detailHtml = ` <div class="material-detail"> <h4>板材详情 (ID: ${bancai.id})</h4> <p><strong>材质:</strong> ${bancai.caizhi?.name || '未知'}</p> <p><strong>厚度:</strong> ${bancai.houdu ? bancai.houdu.toFixed(1) + 'mm' : '未知'}</p> <p><strong>木皮1:</strong> ${formatMupiDetail(bancai.mupi1)}</p> <p><strong>木皮2:</strong> ${formatMupiDetail(bancai.mupi2)}</p> <p><strong>库存数量:</strong> ${bancai.kucun?.shuliang || 0}</p> <div class="related-orders"> <h5>相关订单:</h5> <ul> ${getRelatedOrders(bancai).map(order => `<li>订单 ${order.number} (ID: ${order.id})</li>` ).join('') || '<li>无相关订单</li>'} </ul> </div> </div> `; // 显示详情(实际项目中可用模态框) const detailWindow = window.open('', '_blank', 'width=600,height=400'); detailWindow.document.write(` <!DOCTYPE html> <html> <head> <title>板材详情</title> <style> body { font-family: Arial, sans-serif; padding: 20px; } .material-detail { max-width: 500px; } h4 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; } p { margin: 10px 0; } .related-orders { margin-top: 20px; } ul { list-style-type: none; padding: 0; } li { padding: 5px 0; border-bottom: 1px solid #eee; } </style> </head> <body> ${detailHtml} </body> </html> `); detailWindow.document.close(); } // 获取板材相关订单 function getRelatedOrders(bancai) { const dataManager = window.parent.dataManager; if (!dataManager) return []; const orders = []; const dingdan_chanpin_zujians = dataManager.data.dingdan_chanpin_zujians || []; // 查找直接订单组件关联 dingdan_chanpin_zujians.forEach(dcz => { if (dcz.bancai?.id === bancai.id && dcz.dingdan && !orders.some(o => o.id === dcz.dingdan.id)) { orders.push(dcz.dingdan); } }); // 查找产品组件关联 const chanpin_zujians = dataManager.data.chanpin_zujians || []; chanpin_zujians.forEach(cz => { if (cz.bancai?.id === bancai.id) { const dingdan_chanpins = dataManager.data.dingdan_chanpins || []; dingdan_chanpins.forEach(dc => { if (dc.chanpin?.id === cz.chanpin?.id && dc.dingdan && !orders.some(o => o.id === dc.dingdan.id)) { orders.push(dc.dingdan); } }); } }); return orders; } // 页面加载时尝试初始化 if (window.parent) { window.parent.postMessage('RequestDataManager', '*'); } // 添加全局函数供按钮调用 window.showMaterialDetail = showMaterialDetail; 厚度搜索没有用,加入每列排序
06-20
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>工资条生成工具</title> <!-- 引入SheetJS库用于处理Excel文件 --> <script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script> <!-- 关键修复:使用支持样式的xlsx-style库替换基础版xlsx库 --> <script src="https://unpkg.com/xlsx-style@0.15.6/dist/xlsx.full.min.js"></script> <style> * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: "Microsoft YaHei", "SimHei", Arial, sans-serif; width: 100%; min-height: 100vh; padding: 20px; font-size: 14px; color: #333; line-height: 1.5; } h1 { text-align: center; margin-bottom: 25px; font-size: 24px; color: #2c3e50; } h2 { font-size: 18px; margin: 15px 0; color: #3498db; } .file-upload { border: 2px dashed #95a5a6; padding: 30px 20px; text-align: center; margin-bottom: 30px; cursor: pointer; border-radius: 6px; transition: all 0.3s ease; background-color: #f8f9fa; } .file-upload:hover, .file-upload.active { border-color: #3498db; background-color: #f1f7fc; } .file-upload p { font-size: 16px; color: #7f8c8d; } #fileInput { display: none; } .controls { display: flex; justify-content: center; margin: 20px 0; } .btn { background-color: #3498db; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background-color 0.3s ease; display: flex; align-items: center; gap: 8px; } .btn:hover { background-color: #2980b9; } .btn:disabled { background-color: #bdc3c7; cursor: not-allowed; } #dataContainer { width: 100%; overflow-x: auto; margin: 0 auto; } table { width: 100%; min-width: 900px; border-collapse: collapse; table-layout: fixed; } th, td { border: 1px solid #ddd; padding: 12px 10px; text-align: left; word-wrap: break-word; word-break: break-all; height: 45px; } /* 数值列右对齐,方便查看 */ td.numeric { text-align: right; } /* 工资条表头样式 */ .salary-slip-header th { background-color: #ecf0f1; color: #2c3e50; font-weight: bold; position: sticky; top: 0; z-index: 2; } /* 合计行样式 */ .total-row td { background-color: #f8f9fa; font-weight: bold; } /* 签字行样式 */ .signature-row td { background-color: #f9f9f9; } tr:hover { background-color: #f1f7fc; } .message { color: #7f8c8d; text-align: center; padding: 50px 0; font-size: 16px; } .error { color: #e74c3c; } /* 无效数值样式 */ .invalid-value { background-color: #fff3cd; color: #856404; } .date-info { color: #2c3e50; font-size: 15px; margin: 15px 0; padding: 15px 12px; background-color: #f1f9f7; border-left: 3px solid #27ae60; border-radius: 4px; line-height: 1.8; min-height: 45px; } /* 响应式调整 */ @media (max-width: 1200px) { body { padding: 15px; font-size: 13px; } th, td { padding: 10px 8px; height: 40px; } .date-info { padding: 12px 10px; min-height: 40px; } } @media (max-width: 768px) { body { padding: 10px; font-size: 12px; } h1 { font-size: 20px; margin-bottom: 15px; } h2 { font-size: 16px; } .file-upload { padding: 20px 10px; margin-bottom: 20px; } th, td { padding: 8px 6px; height: 36px; } .date-info { padding: 10px 8px; min-height: 36px; } } </style> </head> <body> <h1>工资条生成工具</h1> <!-- 文件上传区域 --> <div class="file-upload" id="dropArea"> <p>点击或拖放工资表Excel文件到这里(支持多次上传更新)</p> <input type="file" id="fileInput" accept=".xlsx, .xls"> </div> <!-- 提取的信息 --> <div id="infoContainer" style="display: none;"> <div class="date-info"> <p><strong>公司名称:</strong><span id="companyName"></span></p> <p><strong>工资月份:</strong><span id="salaryMonth"></span></p> <p><strong>上次更新:</strong><span id="lastUpdated"></span></p> </div> </div> <!-- 控制按钮区域 --> <div class="controls"> <button id="exportBtn" class="btn" disabled> 导出工资条 </button> </div> <!-- 数据展示区域 --> <div id="dataContainer" style="display: none;"> <h2>工资条数据</h2> <table id="dataTable"> <tbody id="dataTableBody"> <!-- 提取的数据将在这里显示,每条数据带独立表头 --> </tbody> </table> </div> <!-- 消息区域 --> <div id="message" class="message">请上传符合格式要求的工资表Excel文件(.xlsx, .xls)</div> <script> // 获取DOM元素 const dropArea = document.getElementById('dropArea'); const fileInput = document.getElementById('fileInput'); const dataContainer = document.getElementById('dataContainer'); const dataTable = document.getElementById('dataTable'); const dataTableBody = document.getElementById('dataTableBody'); const message = document.getElementById('message'); const infoContainer = document.getElementById('infoContainer'); const companyName = document.getElementById('companyName'); const salaryMonth = document.getElementById('salaryMonth'); const lastUpdated = document.getElementById('lastUpdated'); const exportBtn = document.getElementById('exportBtn'); // 存储处理后的数据用于导出 let processedDataForExport = null; let currentDate = ''; // 定义预期的表头结构 const expectedHeaders = [ '序号', '部门', '姓名', '出勤', '基本工资', '岗位工资', '绩效', '浮动津贴', '扣除', '应发工资', '基本养老(个人)', '失业(个人)', '基本医疗(个人)', '大病(个人)', '公积金(个人)', '个税', '实发工资', '签字' ]; // 定义哪些列是数值类型(索引) const numericColumns = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; // 点击上传区域触发文件选择 dropArea.addEventListener('click', () => fileInput.click()); // 文件选择变化时处理 fileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) { handleFile(e.target.files[0]); // 重置input值,允许重复选择同一文件 fileInput.value = ''; } }); // 拖放功能增强 dropArea.addEventListener('dragover', (e) => { e.preventDefault(); dropArea.classList.add('active'); }); dropArea.addEventListener('dragleave', () => { dropArea.classList.remove('active'); }); dropArea.addEventListener('drop', (e) => { e.preventDefault(); dropArea.classList.remove('active'); if (e.dataTransfer.files.length > 0) { handleFile(e.dataTransfer.files[0]); } }); // 导出按钮点击事件 exportBtn.addEventListener('click', exportToExcel); // 处理Excel文件 - 支持多次上传更新 function handleFile(file) { // 检查文件类型 if (!file.name.match(/\.(xlsx|xls)$/)) { showMessage('请上传Excel文件(.xlsx, .xls)', true); return; } showMessage(`正在解析文件: ${file.name}...`); const reader = new FileReader(); reader.onload = function(e) { try { // 读取并解析Excel文件 const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, { type: 'array' }); // 获取第一个工作表 const firstSheetName = workbook.SheetNames[1]; const worksheet = workbook.Sheets[firstSheetName]; // 转换为JSON const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); if (jsonData.length < 2) { showMessage('文件内容不符合要求,至少需要包含标题行和表头行', true); return; } // 处理数据并更新表格 processData(jsonData); // 更新最后上传时间 updateLastModifiedTime(); } catch (error) { showMessage('解析文件失败: ' + error.message, true); console.error(error); } }; reader.readAsArrayBuffer(file); } // 更新最后修改时间 function updateLastModifiedTime() { const now = new Date(); const formattedTime = now.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }); lastUpdated.textContent = formattedTime; infoContainer.style.display = 'block'; } // 处理数据,提取公司名称和日期并规范化表头 function processData(rawData) { // 提取标题行信息(第一行数据) let title = rawData[0][0] || ''; // 提取公司名称 const companyMatch = title.match(/^(.*?)\s*\d{4}/); const company = companyMatch ? companyMatch[1] : '未知公司'; // 提取日期 const datePattern = /(\d{4})\D*(\d{1,2})\D*月/; const dateMatch = title.match(datePattern); currentDate = dateMatch ? dateMatch[0] : ''; // 更新公司名称和工资月份信息 companyName.textContent = company; salaryMonth.textContent = currentDate; // 提取特定行列数据 const extractedData = extractSpecificData(rawData); // 处理数值数据 const processedData = processNumericData(extractedData); // 保存处理后的数据用于导出 processedDataForExport = processedData; // 启用导出按钮 exportBtn.disabled = false; // 显示处理后的数据(每条数据带独立表头) displayData(processedData, currentDate); } // 提取特定行列数据 function extractSpecificData(rawData) { // 转换Excel列字母为索引(A=0, B=1, C=2...) const columnMap = { 'A': 0, 'B': 1, 'C': 2, 'O': 14, 'Q': 16, 'R': 17, 'S': 18, 'T': 19, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25, 'AA': 26, 'AB': 27, 'AC': 28, 'AE': 30, 'AG': 32, 'AH': 33 }; // 需要提取的列索引 const columnsToExtract = Object.values(columnMap); // 找到最后一行有数据的行索引 let lastDataRow = -1; for (let i = rawData.length - 1; i >= 0; i--) { const row = rawData[i]; if (row && row.some(cell => cell !== undefined && cell !== null && cell !== '')) { lastDataRow = i; break; } } // 计算行范围 const startRow = 5; // 第6行(0-based索引) const endRow = lastDataRow !== -1 ? lastDataRow - 6 : -1; // 验证行范围有效性 if (lastDataRow === -1) { console.warn('未找到有数据的行'); return []; } if (startRow >= endRow || endRow < 0) { console.warn(`数据行范围无效,开始行(${startRow+1}) >= 结束行(${endRow+1})`); showMessage(`数据行范围无效,开始行(${startRow+1}) >= 结束行(${endRow+1})`, true); return []; } // 提取数据到二维数组 const result = []; for (let i = startRow; i < endRow; i++) { const rowData = rawData[i] || []; const extractedRow = []; columnsToExtract.forEach(colIndex => { extractedRow.push(rowData[colIndex] !== undefined ? rowData[colIndex] : ''); }); result.push(extractedRow); } return result; } // 处理数值数据:校验并保留2位小数(四舍五入),空值不标红 function processNumericData(data) { return data.map(row => { return row.map((cell, index) => { // 检查当前列是否为数值列 if (numericColumns.includes(index)) { // 处理空值情况 if (cell === '' || cell === null || cell === undefined) { return { value: '', valid: true, // 空值视为有效 rawValue: 0 // 用于计算合计的原始数值 }; } // 尝试转换为数值 const num = parseFloat(cell); // 校验数值有效性 if (isNaN(num)) { return { value: cell, valid: false, rawValue: 0 }; } else { const roundedValue = Math.round(num * 100) / 100; return { value: roundedValue, valid: true, rawValue: roundedValue // 存储用于计算合计的数值 }; } } // 非数值列,直接返回原始值 return { value: cell, valid: true, rawValue: 0 }; }); }); } // 计算各列的总和 function calculateTotals(processedData) { const totals = new Array(expectedHeaders.length).fill(0); processedData.forEach(row => { row.forEach((cell, index) => { // 只对数值列进行求和 if (numericColumns.includes(index)) { totals[index] += cell.rawValue; } }); }); // 保留两位小数 return totals.map(total => Math.round(total * 100) / 100); } // 显示解析后的数据 - 每条数据前都添加表头,无间隔,最后添加合计行和签字行 function displayData(processedData, date) { // 清空表格 dataTableBody.innerHTML = ''; // 如果没有提取到数据,显示提示 if (processedData.length === 0) { const emptyRow = document.createElement('tr'); const emptyCell = document.createElement('td'); emptyCell.colSpan = expectedHeaders.length; emptyCell.textContent = '没有提取到符合条件的数据'; emptyRow.appendChild(emptyCell); dataTableBody.appendChild(emptyRow); } else { // 为每条数据添加表头(无间隔) processedData.forEach((row) => { // 添加表头行(每条数据前都添加) const headerRow = document.createElement('tr'); headerRow.className = 'salary-slip-header'; expectedHeaders.forEach((header) => { if (header === '应发工资' || header === '实发工资') { header += `(${date})`; } const th = document.createElement('th'); th.textContent = header; headerRow.appendChild(th); }); dataTableBody.appendChild(headerRow); // 添加数据行 const dataRow = document.createElement('tr'); row.forEach((cell, cellIndex) => { const td = document.createElement('td'); td.textContent = cell.value; // 数值列右对齐 if (numericColumns.includes(cellIndex)) { td.classList.add('numeric'); } // 标记无效数值 if (!cell.valid) { td.classList.add('invalid-value'); } dataRow.appendChild(td); }); dataTableBody.appendChild(dataRow); }); // 计算各列总和 const totals = calculateTotals(processedData); // 添加合计行(倒数第二行) const totalRow = document.createElement('tr'); totalRow.className = 'total-row'; // 前3个单元格合并,内容为"合计" const mergedCell = document.createElement('td'); mergedCell.colSpan = 3; mergedCell.textContent = '合计'; mergedCell.style.textAlign = 'center'; totalRow.appendChild(mergedCell); // 第4个单元格为空 const emptyCell = document.createElement('td'); totalRow.appendChild(emptyCell); // 5-17单元格为各列总和(索引4到16) for (let i = 4; i < expectedHeaders.length; i++) { const td = document.createElement('td'); td.textContent = totals[i]; if (i == expectedHeaders.length - 1) { td.textContent = ''; } if (numericColumns.includes(i)) { td.classList.add('numeric'); } totalRow.appendChild(td); } dataTableBody.appendChild(totalRow); // 添加签字行(最后一行) const signatureRow = document.createElement('tr'); signatureRow.className = 'signature-row'; // 制表 const makerCell = document.createElement('td'); makerCell.colSpan = 4; makerCell.textContent = '制表:'; signatureRow.appendChild(makerCell); // 财务 const financeCell = document.createElement('td'); financeCell.colSpan = 4; financeCell.textContent = '财务:'; signatureRow.appendChild(financeCell); // 审核 const auditCell = document.createElement('td'); auditCell.colSpan = 5; auditCell.textContent = '审核:'; signatureRow.appendChild(auditCell); // 审批 const approveCell = document.createElement('td'); approveCell.colSpan = 5; approveCell.textContent = '审批:'; signatureRow.appendChild(approveCell); dataTableBody.appendChild(signatureRow); } // 显示数据区域,隐藏消息 dataContainer.style.display = 'block'; message.style.display = 'none'; } // 导出数据到Excel function exportToExcel() { if (!processedDataForExport || processedDataForExport.length === 0) { showMessage('没有可导出的数据', true); return; } // 准备导出数据 const exportData = []; // 添加每条记录的表头和数据 processedDataForExport.forEach(row => { // 添加表头行 const headerRow = expectedHeaders.map(header => { if (header === '应发工资' || header === '实发工资') { return `${header}(${currentDate})`; } return header; }); exportData.push(headerRow); // 添加数据行 const dataRow = row.map(cell => cell.value); exportData.push(dataRow); }); // 计算合计 const totals = calculateTotals(processedDataForExport); // 准备合计行 const totalRow = []; // 前3个单元格合并为"合计" totalRow.push("合计"); totalRow.push(null); // 第二个单元格(合并后不需要) totalRow.push(null); // 第三个单元格(合并后不需要) totalRow.push(""); // 第四个单元格为空 // 添加5-17列的合计值 for (let i = 4; i < expectedHeaders.length - 1; i++) { totalRow.push(totals[i]); } exportData.push(totalRow); // 准备签字行 - 只在第一个单元格设置值,其他为空 const signatureRow = new Array(expectedHeaders.length).fill(null); signatureRow[0] = "制表: 财务: 审核: 审批:"; exportData.push(signatureRow); // 创建工作簿和工作表 const ws = XLSX.utils.aoa_to_sheet(exportData); // 设置单元格合并 if (exportData.length > 0) { const totalRowIndex = exportData.length - 2; // 合计行索引 const signatureRowIndex = exportData.length - 1; // 签字行索引 ws['!merges'] = [ // 合计行前3列合并 { s: { r: totalRowIndex, c: 0 }, e: { r: totalRowIndex, c: 2 } }, // 签字行A-R列合并为一个单元格 { s: { r: signatureRowIndex, c: 0 }, e: { r: signatureRowIndex, c: 17 } // 合并到第18列(R列) } ]; } // 设置固定行高 const range = XLSX.utils.decode_range(ws['!ref']); ws['!rows'] = []; // 初始化行配置数组 for (let R = 0; R <= range.e.r; R++) { // 为每一行设置固定高度20 ws['!rows'][R] = { hpt: 30 }; // hpt单位是1/20点,所以20像素需要乘以20 } // 使用自定义列宽,从第一列开始依次应用 const customWidths = [3.92, 4.8, 5, 4.05, 7.43, 7.68, 3.55, 4.93, 4.18, 12.05, 6.93, 6.93, 7.3, 6.93, 7.55, 5.68, 12.18, 9.43 ]; // 为每个表头应用对应的自定义宽度 ws['!cols'] = customWidths.map(width => ({ // 保留一位小数并转换为数字,确保宽度值正确 wch: parseFloat(width.toFixed(2)) })); // 定义包含自动换行的单元格样式 const cellStyle = { font: { sz: 10 // 小一号字体 }, alignment: { wrapText: true // 核心配置:启用自动换行 } }; // 应用样式到所有单元格 for (const cellAddress in ws) { // 跳过非单元格属性(如!cols、!rows等) if (cellAddress.startsWith('!')) continue; if (ws[cellAddress]) { // 合并已有样式与自动换行样式 ws[cellAddress].s = { ...ws[cellAddress].s, ...cellStyle }; } else { // 为空白单元格设置样式 ws[cellAddress] = { s: cellStyle }; } } // 创建工作簿并添加工作表 const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "工资条"); // 生成文件名(包含公司名称和日期) const company = companyName.textContent || "未知公司"; const fileName = `${company}_${currentDate || new Date().toLocaleDateString()}_工资条.xlsx`; // 导出文件 XLSX.writeFile(wb, fileName); } // 显示消息 function showMessage(text, isError = false) { message.textContent = text; message.className = isError ? 'message error' : 'message'; message.style.display = 'block'; dataContainer.style.display = 'none'; infoContainer.style.display = 'none'; } </script> </body> </html> 希望导出后有内容部分加边框,字体为宋体10号字,自动换行,对其方式上下左右居中,奇数行加粗
08-07
<!DOCTYPE html> <html> <head> <title>完整版数独游戏</title> <style> /* 棋盘样式 */ table { border-collapse: collapse; margin: 20px auto; } td { width: 40px; height: 40px; border: 1px solid #ccc; text-align: center; font-size: 20px; cursor: pointer; transition: background 0.3s; } .thick-right { border-right: 3px solid #333; } .thick-bottom { border-bottom: 3px solid #333; } .conflict { background-color: #ffcccc !important; } .error { background-color: #ff9999 !important; } .initial { background-color: #f0f0f0; } .selected { background-color: #e6f3ff; } /* 输入面板 */ #input-panel { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 20px; background: white; box-shadow: 0 0 10px rgba(0,0,0,0.1); border-radius: 8px; } /* 控制按钮 */ .controls { text-align: center; margin-top: 20px; } button { padding: 10px 20px; margin: 0 5px; cursor: pointer; background: #4CAF50; color: white; border: none; border-radius: 4px; } </style> </head> <body> <table id="sudoku-grid"></table> <div class="controls"> <button onclick="checkSolution()">检查答案</button> <button onclick="showSolution()">显示答案</button> <button onclick="newGame()">新游戏</button> </div> <div id="input-panel" style="display: none;"> <input type="number" id="number-input" min="1" max="9" autofocus> <button onclick="confirmInput()">确认</button> <button onclick="cancelInput()">取消</button> </div> <script> // 游戏状态管理 let currentBoard = Array(9).fill().map(() => Array(9).fill(0)); let initialBoard = Array(9).fill().map(() => Array(9).fill(0)); let selectedCell = null; // 初始化棋盘 function initializeBoard() { const grid = document.getElementById('sudoku-grid'); grid.innerHTML = ''; for (let row = 0; row < 9; row++) { const tr = document.createElement('tr'); for (let col = 0; col < 9; col++) { const td = document.createElement('td'); td.addEventListener('click', () => handleCellClick(row, col)); // 添加粗边框样式 if (col % 3 === 2 && col !== 8) td.classList.add('thick-right'); if (row % 3 === 2 && row !== 8) td.classList.add('thick-bottom'); tr.appendChild(td); } grid.appendChild(tr); } } // 单元格点击处理 function handleCellClick(row, col) { if (initialBoard[row][col] !== 0) return; // 清除之前选中的样式 document.querySelectorAll('.selected').forEach(cell => { cell.classList.remove('selected'); }); selectedCell = { row, col }; document.querySelector(`tr:nth-child(${row + 1}) td:nth-child(${col + 1})`) .classList.add('selected'); // 显示输入面板 document.getElementById('input-panel').style.display = 'block'; document.getElementById('number-input').focus(); } // 输入处理 function confirmInput() { const input = document.getElementById('number-input'); const value = parseInt(input.value); const { row, col } = selectedCell; const cell = document.querySelector(`tr:nth-child(${row + 1}) td:nth-child(${col + 1})`); if (isNaN(value) || value < 1 || value > 9) { alert('请输入1-9之间的数字'); return; } currentBoard[row][col] = value; cell.textContent = value; cell.classList.remove('conflict'); // 实时验证 if (!isValid(currentBoard, row, col, value)) { cell.classList.add('conflict'); } cancelInput(); } function cancelInput() { document.getElementById('input-panel').style.display = 'none'; document.getElementById('number-input').value = ''; selectedCell = null; } // 核心算法 function solveSudoku(board) { const emptyCell = findEmptyCell(board); if (!emptyCell) return true; const [row, col] = emptyCell; for (let num = 1; num <= 9; num++) { if (isValid(board, row, col, num)) { board[row][col] = num; if (solveSudoku(board)) return true; board[row][col] = 0; } } return false; } function findEmptyCell(board) { for (let row = 0; row < 9; row++) { for (let col = 0; col < 9; col++) { if (board[row][col] === 0) return [row, col]; } } return null; } function isValid(board, row, col, num) { // 行检查 for (let c = 0; c < 9; c++) { if (board[row][c] === num && c !== col) return false; } // 列检查 for (let r = 0; r < 9; r++) { if (board[r][col] === num && r !== row) return false; } // 宫格检查 const startRow = Math.floor(row / 3) * 3; const startCol = Math.floor(col / 3) * 3; for (let r = startRow; r < startRow + 3; r++) { for (let c = startCol; c < startCol + 3; c++) { if (board[r][c] === num && r !== row && c !== col) return false; } } return true; } // 游戏功能 function checkSolution() { let hasError = false; let hasEmpty = false; const cells = document.querySelectorAll('td'); cells.forEach(cell => { cell.classList.remove('error', 'conflict'); const row = cell.parentElement.rowIndex; const col = cell.cellIndex; const value = currentBoard[row][col]; if (value === 0) { hasEmpty = true; cell.classList.add('error'); hasError = true; } else if (!isValid(currentBoard, row, col, value)) { cell.classList.add('conflict'); hasError = true; } }); if (!hasError && !hasEmpty) { alert('恭喜!解答正确!'); return true; } alert(hasError ? '存在错误或冲突!' : '尚未完成所有单元格!'); return false; } function showSolution() { const solution = JSON.parse(JSON.stringify(currentBoard)); if (!solveSudoku(solution)) { alert('此数独无解!'); return; } const cells = document.querySelectorAll('td'); cells.forEach((cell, index) => { const row = Math.floor(index / 9); const col = index % 9; if (initialBoard[row][col] === 0) { currentBoard[row][col] = solution[row][col]; cell.textContent = solution[row][col]; cell.classList.remove('conflict'); } }); checkSolution(); } // 新游戏初始化 function newGame() { // 简单示例数独(可替换为随机生成) const sample = [ [5,3,0,0,7,0,0,0,0], [6,0,0,1,9,5,0,0,0], [0,9,8,0,0,0,0,6,0], [8,0,0,0,6,0,0,0,3], [4,0,0,8,0,3,0,0,1], [7,0,0,0,2,0,0,0,6], [0,6,0,0,0,0,2,8,0], [0,0,0,4,1,9,0,0,5], [0,0,0,0,8,0,0,7,9] ]; currentBoard = JSON.parse(JSON.stringify(sample)); initialBoard = JSON.parse(JSON.stringify(sample)); const cells = document.querySelectorAll('td'); cells.forEach((cell, index) => { const row = Math.floor(index / 9); const col = index % 9; cell.textContent = sample[row][col] || ''; cell.className = ''; if (sample[row][col] !== 0) { cell.classList.add('initial'); } if (col % 3 === 2 && col !== 8) cell.classList.add('thick-right'); if (row % 3 === 2 && row !== 8) cell.classList.add('thick-bottom'); }); } // 启动游戏 initializeBoard(); newGame(); </script> </body> </html> 这个就是之前版本的带编辑后 可以点击检视答案的,我只是让把显示动画的效果加到里面
05-29
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>万得数据库浏览器</title> <style> * { box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { margin: 0; padding: 20px; background-color: #f5f7fa; color: #333; } .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 10px; box-shadow: 0 0 20px rgba(0,0,0,0.1); padding: 25px; } h1 { color: #2c3e50; text-align: center; margin-bottom: 30px; } .search-box { display: flex; margin-bottom: 20px; position: relative; } #search-input { flex: 1; padding: 12px 20px; font-size: 16px; border: 2px solid #3498db; border-radius: 50px; outline: none; transition: all 0.3s; } #search-input:focus { border-color: #2980b9; box-shadow: 0 0 10px rgba(52, 152, 219, 0.3); } #search-button { position: absolute; right: 5px; top: 5px; background: #3498db; color: white; border: none; padding: 8px 20px; border-radius: 50px; cursor: pointer; font-weight: bold; transition: background 0.3s; } #search-button:hover { background: #2980b9; } .results-container { display: none; margin-top: 30px; } .table-card { background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); margin-bottom: 20px; overflow: hidden; border: 1px solid #e0e6ed; } .table-header { background: #3498db; color: white; padding: 12px 20px; display: flex; justify-content: space-between; align-items: center; } .table-name { font-weight: bold; font-size: 18px; } .export-btn { background: #27ae60; color: white; border: none; padding: 6px 15px; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background 0.3s; } .export-btn:hover { background: #219653; } .columns-table { width: 100%; border-collapse: collapse; } .columns-table th { background: #f8f9fa; text-align: left; padding: 12px 15px; border-bottom: 2px solid #e0e6ed; } .columns-table td { padding: 10px 15px; border-bottom: 1px solid #e0e6ed; } .columns-table tr:nth-child(even) { background-color: #f8f9fa; } .no-results { text-align: center; padding: 30px; color: #7f8c8d; font-style: italic; } .loading { display: none; text-align: center; padding: 20px; } .loading-spinner { border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; width: 30px; height: 30px; animation: spin 1s linear infinite; margin: 0 auto; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .error-message { color: #e74c3c; text-align: center; padding: 20px; display: none; } .export-dictionary-btn { background: linear-gradient(to right, #8e44ad, #9b59b6); color: white; border: none; padding: 12px 30px; border-radius: 50px; cursor: pointer; font-size: 16px; font-weight: bold; box-shadow: 0 4px 10px rgba(0,0,0,0.1); transition: all 0.3s ease; display: inline-block; margin: 20px auto; } .export-dictionary-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 15px rgba(0,0,0,0.2); background: linear-gradient(to right, #9b59b6, #8e44ad); } </style> </head> <body> <div class="container"> <h1>万得数据库浏览器</h1> <div class="search-box"> <input type="text" id="search-input" placeholder="输入表名、字段名或注释进行搜索..."> <button id="search-button">搜索</button> </div> <div style="text-align: center;"> <button id="export-dictionary" class="export-dictionary-btn"> <i class="fas fa-book"></i> 导出数据库字典 </button> </div> <div class="loading"> <div class="loading-spinner"></div> <p>正在搜索,请稍候...</p> </div> <div class="error-message" id="error-message"></div> <div class="results-container" id="results-container"> <!-- 搜索结果将在这里动态显示 --> </div> </div> <script> // 在<script>标签内添加 document.getElementById('export-dictionary').addEventListener('click', function() { // 显示加载提示 const loadingElement = document.querySelector('.loading'); loadingElement.style.display = 'block'; loadingElement.querySelector('p').textContent = '正在生成数据库字典,请稍候...'; // 发起导出请求 window.location.href = '/qwind/export/dictionary'; // 2秒后隐藏加载提示(实际导出时间可能更长) setTimeout(() => { loadingElement.style.display = 'none'; }, 2000); }); document.addEventListener('DOMContentLoaded', function() { const searchInput = document.getElementById('search-input'); const searchButton = document.getElementById('search-button'); const resultsContainer = document.getElementById('results-container'); const loadingElement = document.querySelector('.loading'); const errorMessage = document.getElementById('error-message'); // 搜索按钮点击事件 searchButton.addEventListener('click', performSearch); // 回车键搜索 searchInput.addEventListener('keyup', function(event) { if (event.key === 'Enter') { performSearch(); } }); function performSearch() { const searchTerm = searchInput.value.trim(); if (!searchTerm) { resultsContainer.style.display = 'none'; errorMessage.style.display = 'block'; errorMessage.textContent = '请输入搜索内容'; return; } // 显示加载动画 loadingElement.style.display = 'block'; resultsContainer.style.display = 'none'; errorMessage.style.display = 'none'; // 发送AJAX请求 fetch(`/qwind/search?q=${encodeURIComponent(searchTerm)}`) .then(response => { if (!response.ok) { throw new Error('搜索失败'); } return response.json(); }) .then(data => { loadingElement.style.display = 'none'; if ('error' in data) { errorMessage.style.display = 'block'; errorMessage.textContent = `错误: ${data.error}`; return; } // 处理搜索结果 displayResults(data); }) .catch(error => { loadingElement.style.display = 'none'; errorMessage.style.display = 'block'; errorMessage.textContent = `请求失败: ${error.message}`; }); } function displayResults(data) { resultsContainer.innerHTML = ''; if (Object.keys(data).length === 0) { resultsContainer.innerHTML = ` <div class="no-results"> <p>没有找到匹配的表或字段</p> </div> `; resultsContainer.style.display = 'block'; return; } for (const [tableName, columns] of Object.entries(data)) { const tableCard = document.createElement('div'); tableCard.className = 'table-card'; tableCard.innerHTML = ` <div class="table-header"> <div class="table-name">${tableName}</div> <button class="export-btn" data-table="${tableName}">导出数据</button> </div> <table class="columns-table"> <thead> <tr> <th width="40%">字段名</th> <th width="60%">注释/字段名</th> </tr> </thead> <tbody> ${columns.map(col => ` <tr> <td>${col.name}</td> <td>${col.comment}</td> </tr> `).join('')} </tbody> </table> `; resultsContainer.appendChild(tableCard); } // 添加导出按钮事件 document.querySelectorAll('.export-btn').forEach(btn => { btn.addEventListener('click', function() { const tableName = this.getAttribute('data-table'); window.location.href = `/qwind/export/${tableName}`; }); }); resultsContainer.style.display = 'block'; } }); </script> </body> </html> from flask import Flask, render_template, request, send_file, jsonify import cx_Oracle import csv import io import os from flask import send_file from openpyxl import Workbook import io import pandas as pd app = Flask(__name__) # Oracle数据库配置 ORACLE_USER = os.getenv('ORACLE_USER', 'az_wasp') ORACLE_PASSWORD = os.getenv('ORACLE_PASSWORD', 'cmf+2011') ORACLE_DSN = os.getenv('ORACLE_DSN', '192.168.125.66/PDB_CMWADEV') def get_db_connection(): return cx_Oracle.connect(ORACLE_USER, ORACLE_PASSWORD, ORACLE_DSN) @app.route('/qwind') def index(): """显示搜索页面""" return render_template('index.html') @app.route('/qwind/search') def search_tables(): """根据查询条件搜索表和字段""" search_term = request.args.get('q', '').strip().upper() try: with get_db_connection() as conn: cursor = conn.cursor() # 查询匹配的表和字段 query = """ SELECT t.table_name, c.column_name, NVL(com.comments, c.column_name) AS column_comment FROM all_tables@wd_dblink_20230908 t JOIN all_tab_columns@wd_dblink_20230908 c ON t.table_name = c.table_name LEFT JOIN all_col_comments@wd_dblink_20230908 com ON c.table_name = com.table_name AND c.column_name = com.column_name WHERE (t.owner = 'WIND_FILESYNC' and c.owner = 'WIND_FILESYNC' and com.owner = 'WIND_FILESYNC') and t.table_name LIKE :1 OR c.column_name LIKE :1 OR com.comments LIKE :1 ORDER BY t.table_name, c.column_id """ # 添加通配符进行模糊匹配 search_pattern = f'%{search_term}%' cursor.execute(query, [search_pattern]) # 组织结果数据 results = {} for row in cursor: table_name, column_name, column_comment = row if table_name not in results: results[table_name] = [] results[table_name].append({ 'name': column_name, 'comment': column_comment }) return jsonify(results) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/qwind/export/<table_name>') def export_table(table_name): """导出表数据为CSV""" try: with get_db_connection() as conn: cursor = conn.cursor() # 获取字段注释作为列名 col_query = """ SELECT col.column_name, NVL(com.comments, col.column_name) AS column_comment FROM all_tab_columns@wd_dblink_20230908 col LEFT JOIN all_col_comments@wd_dblink_20230908 com ON col.table_name = com.table_name AND col.column_name = com.column_name WHERE col.table_name = :1 and (col.owner = 'WIND_FILESYNC' and com.owner = 'WIND_FILESYNC') ORDER BY col.column_id """ cursor.execute(col_query, [table_name.upper()]) columns = cursor.fetchall() headers = [col[1] for col in columns] col_names = [col[0] for col in columns] # 构建数据查询(限制10万行) data_query = f""" SELECT {', '.join(col_names)} FROM wind_filesync.{table_name}@wd_dblink_20230908 WHERE ROWNUM <= 100000 """ print(data_query) cursor.execute(data_query) # 创建CSV内存文件 output = io.StringIO() writer = csv.writer(output) writer.writerow(headers) # 写入表头(字段注释) writer.writerows(cursor) # 写入数据 # 返回CSV文件 output.seek(0) return send_file( io.BytesIO(output.getvalue().encode()), mimetype='text/csv', as_attachment=True, attachment_filename=f'{table_name}_data.csv' ) except Exception as e: return f"Export failed: {str(e)}", 500 # 添加新路由 @app.route('/qwind/export/dictionary') def export_dictionary(): """导出数据库表和字段字典为Excel文件""" try: with get_db_connection() as conn: cursor = conn.cursor() # 执行提供的SQL查询 sql = """ SELECT t.table_name AS 表名, t2.comments AS 表中文名, t.column_name AS 字段名, t.comments AS 字段中文名, t4.data_type AS 字段类型, t4.data_length AS 字段长度, t3.num_rows AS "行数量(近似值)" FROM ALL_COL_COMMENTS@WD_DBLINK_20230908 t LEFT JOIN (SELECT * FROM ALL_TAB_COMMENTS@WD_DBLINK_20230908 WHERE owner = 'WIND_FILESYNC') t2 ON t.table_name = t2.table_name LEFT JOIN (SELECT * FROM ALL_TABLES@WD_DBLINK_20230908 t WHERE t.owner = 'WIND_FILESYNC') t3 ON t.table_name = t3.table_name LEFT JOIN (SELECT * FROM all_tab_columns@WD_DBLINK_20230908 WHERE owner = 'WIND_FILESYNC') t4 ON t.table_name = t4.table_name AND t.column_name = t4.column_name WHERE t.owner = 'WIND_FILESYNC' AND T.TABLE_NAME NOT LIKE '%TB_OBJECT%' AND T.TABLE_NAME NOT LIKE '%WINDHASH%' AND T.TABLE_NAME NOT LIKE '%TEST%' AND T.TABLE_NAME NOT LIKE '%==%' """ cursor.execute(sql) # 创建Excel工作簿 wb = Workbook() ws = wb.active ws.title = "数据库字典" # 添加表头 headers = [desc[0] for desc in cursor.description] for col_num, header in enumerate(headers, 1): ws.cell(row=1, column=col_num, value=header) # 添加数据 for row_num, row_data in enumerate(cursor, 2): for col_num, cell_value in enumerate(row_data, 1): ws.cell(row=row_num, column=col_num, value=cell_value) # 调整列宽 for col in ws.columns: max_length = 0 column = col[0].column_letter for cell in col: try: if len(str(cell.value)) > max_length: max_length = len(str(cell.value)) except: pass adjusted_width = (max_length + 2) * 1.2 ws.column_dimensions[column].width = adjusted_width # 保存到内存 virtual_workbook = io.BytesIO() wb.save(virtual_workbook) virtual_workbook.seek(0) return send_file( virtual_workbook, mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', as_attachment=True, attachment_filename='数据库字典.xlsx' ) except Exception as e: return f"导出字典失败: {str(e)}", 500 if __name__ == '__main__': #app.config['APPLICATION_ROOT'] = '/qwind' app.run(host='0.0.0.0', port=9093, debug=True) 我的前后端代码都放到了后端服务器,需要通过前端服务器的nginx进行访问,帮我配置nginx
06-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值