2023年2月

做过很多的Web项目,大多数在打印页面内容的时候,采用的都是通过Javascript调用系统内置的打印方法进行打印,也就是调用PrintControl.ExecWB(?,?)实现直接打印和打印预览功能。打印的效果及控制性虽然不是很好,但是也能勉强使用,应付一般的打印还是可以的了。

代码如下所示:



代码

//
调用PrintControl.ExecWB(?,?)实现直接打印和打印预览功能。(直接用系统提供的print()方法打印无法隐藏某些区域)

//

preview:是否显示预览。null/false:不显示,true:显示


function
printPage(preview)
{

try

{

var
content
=
window.document.body.innerHTML;

var
oricontent
=
content;

while
(content.indexOf(
"
{$printhide}
"
)
>=
0
) content
=
content.replace(
"
{$printhide}
"
,
"
style='display:none'
"
);

if
(content.indexOf(
"
ID=\
"
PrintControl\
""
)
<
0
) content
=
content
+
"
<OBJECT ID=\
"
PrintControl\
"
WIDTH=0 HEIGHT=0 CLASSID=\
"
CLSID:8856F961
-
340A
-
11D0
-
A96B
-
00C04FD705A2\
"
></OBJECT>
"
;
window.document.body.innerHTML

=
content;

//
PrintControl.ExecWB(7,1)打印预览,(1,1)打开,(4,1)另存为,(17,1)全选,(10,1)属性,(6,1)打印,(6,6)直接打印,(8,1)页面设置



if
(preview
==
null
||
preview
==
false
) PrintControl.ExecWB(
6
,
1
);

else
PrintControl.ExecWB(
7
,
1
);
//
OLECMDID_PRINT=7; OLECMDEXECOPT_DONTPROMPTUSER=6/OLECMDEXECOPT_PROMPTUSER=1


window.document.body.innerHTML
=
oricontent;
}

catch
(ex){ alert(
"
执行Javascript脚本出错。
"
); }
}


function
printConten(preview, html)
{

try

{

var
content
=
html;

var
oricontent
=
window.document.body.innerHTML;

while
(content.indexOf(
"
{$printhide}
"
)
>=
0
) content
=
content.replace(
"
{$printhide}
"
,
"
style='display:none'
"
);

if
(content.indexOf(
"
ID=\
"
PrintControl\
""
)
<
0
) content
=
content
+
"
<OBJECT ID=\
"
PrintControl\
"
WIDTH=0 HEIGHT=0 CLASSID=\
"
CLSID:8856F961
-
340A
-
11D0
-
A96B
-
00C04FD705A2\
"
></OBJECT>
"
;

window.document.body.innerHTML

=
content;

//
PrintControl.ExecWB(7,1)打印预览,(1,1)打开,(4,1)另存为,(17,1)全选,(10,1)属性,(6,1)打印,(6,6)直接打印,(8,1)页面设置



if
(preview
==
null
||
preview
==
false
) PrintControl.ExecWB(
6
,
1
);

else
PrintControl.ExecWB(
7
,
1
);
//
OLECMDID_PRINT=7; OLECMDEXECOPT_DONTPROMPTUSER=6/OLECMDEXECOPT_PROMPTUSER=1


window.document.body.innerHTML
=
oricontent;
}

catch
(ex){ alert(
"
执行Javascript脚本出错。
"
); }
}

上面两个函数放在一个Js文件中,在页面内容中通过应用该脚本文件并调用进一步封装的函数即可打印指定部分的内容:


<
script language
=
"
javascript
"
>


function
Print(preview) {

var
text
=
document.getElementById(
"
content
"
).innerHTML;
printConten(preview, text);
}

打印的效果大致如下图所示,如果打印的页面在框架页面中,那么需要选定“仅打印选定框架”的选项。

采用此种方法,不需要安装任何控件,具有很好的兼容优势,不过出来的报表内容,好像控制起来会比较麻烦一些,特别对于一些报表方面的打印,需要输出复杂的内容是,也有一定的缺陷,但总体来说,也是一个较好的选择。

后来在需要做一些证件套打方面的工作,这个控件就做不到了,因此需要一种方法或者控件,能够较好处理套打方面的事情。

无意间,发现一个比较好的打印控件,支持各种格式的打印,还有我关心的证件套打功能,功能强大,使用也很简单的,非常值得推荐。

控件的相关地址:

控件下载主页:
http://mt.runon.cn/index.html

控件博客介绍:
http://blog.sina.com.cn/s/articlelist_1340389911_0_1.html

应用这个控件,普通报表的打印效果如下所示:

上面两个报表的打印其实都差不多,都是打印部分的HTML内容,不过后者看起来要好一点,而且提供很完善的报表功能设置。

代码大致如下所示。



代码


<
script
language
="javascript"
>


function
Print(preview) {

var
text
=
document.getElementById(
"
content
"
).innerHTML;
printConten(preview, text);
}

</
script
>



<
script
language
="javascript"
src
="http://www.cnblogs.com/Scripts/CheckActivX.js"
></
script
>


<
object
id
="LODOP"
classid
="clsid:2105C259-1E0C-4534-8141-A753534CB4CA"
width
=0
height
=0
>

</
object
>


<
script
language
="javascript"
>


var
LODOP
=
document.getElementById(
"
LODOP
"
);
//
这行语句是为了符合DTD规范


CheckLodop();

</
script
>


<
script
language
="javascript"
type
="text/javascript"
>



function
Preview() {
//
打印预览


CreateLicenseData();
LODOP.SET_SHOW_MODE(

"
PREVIEW_IN_BROWSE
"
,
1
);
LODOP.PREVIEW();
};

function
Setup() {
//
打印维护 给用户调整位置


CreateLicenseData();
LODOP.PRINT_SETUP();
};

function
Design() {
//
打印设计 开发人员设置内容和位置


CreateLicenseData();
LODOP.PRINT_DESIGN();
};


function
CreateLicenseData() {
LODOP.PRINT_INIT(

"
查询报表
"
);
LODOP.ADD_PRINT_HTM(

20
,
40
,
610
,
900
, document.all(
"
content
"
).innerHTML);
LODOP.PREVIEW();
}

</
script
>

很多时候,我们也没的内容,都是通过CSS来控制美观的,所以有时候,我们打印部分HTML,没有这些样式的话,那么出来的Table格式和字体,可能都会发生变化,不太好看。那么就需要进行HTML的样式设置。

如果给打印内容设置了样式,那么出来的界面效果就好很多了。

设置样式的代码如下所示。



代码


<
script language
=
"
javascript
"
type
=
"
text/javascript
"
>


function Preview() {

//
打印预览


CreateLicenseData();
LODOP.SET_SHOW_MODE(

"
PREVIEW_IN_BROWSE
"
,
1
);
LODOP.PREVIEW();
};

function CreateLicenseData() {
LODOP.PRINT_INIT(

"
申请处理单
"
);
var strBodyStyle

=

"
<link type='text/css' rel='stylesheet' href='http://www.cnblogs.com/Themes/Default/style.css' /><style><!--table { border:1;background-color: #CBCBCC } td {background-color:#FFFFFE;border: 1; } th { background-color:#F1F1F3;padding-left:5px;border:1}--></style>
"
;
var strFormHtml

=
strBodyStyle
+

"
<body>
"

+
document.getElementById(
"
content
"
).innerHTML
+

"
</body>
"
;
LODOP.ADD_PRINT_HTM(

20
,
40
,
610
,
900
, strFormHtml);
LODOP.PREVIEW();
}

</
script
>

下一篇继续介绍下证件套打的打印功能。

由于以前未接触过套打,一直觉得套打是一个比较神秘和麻烦的事情,因为打印机的位置总是需要调整的,你总不能硬编码吧?但是如果位置可调,有需要直观一些来处理,那就比较麻烦了。

在前面介绍过《
Web打印的解决方案之普通报表打印
》的一片文章中提到过那个打印控件Lodop,做起套打来感觉还是挺方便的,至少位置调整界面不需要自己弄,位置嘛,也提供了自动保存的功能,不需要理会。

一般的套打,包含了几部分操作:打印预览、打印维护、打印设计。

打印预览和打印维护是面向终端用户的,打印维护是指内容不能修改删除、但位置可以调整,给不同的打印机不同的尺寸打印提供调整位置的可能性。

打印设计是面向开发人员的,开始需要通过这个功能来设计好套打的界面,就是根据套打证件的背景图片,大致摆放好各个内容的位置。

大致的实现代码如下所示:

<
script
language
="javascript"
>


var
LODOP
=
document.getElementById(
"
LODOP
"
);
//
这行语句是为了符合DTD规范


CheckLodop();

</
script
>


<
script
language
="javascript"
type
="text/javascript"
>



function
Preview2() {
CreateDataBill();
LODOP.PREVIEW();
};

function
Setup2() {
CreateDataBill();
LODOP.PRINT_SETUP();
};

function
Design2() {
CreateDataBill();
LODOP.PRINT_DESIGN();

};

function
RealPrint() {
CreateDataBill();

if
(LODOP.PRINTA())
alert(

"
已发出实际打印命令!
"
);

else

alert(

"
放弃打印!
"
);
};


function
CreateDataBill() {
LODOP.SET_PRINT_PAPER(

10
,
10
,
762
,
533
,
"
打印控件功能演示_Lodop功能_移动公司发票套打
"
);
LODOP.ADD_PRINT_TEXT(

126
,
150
,
100
,
20
,
"
郭德刚
"
);
LODOP.SET_PRINT_STYLEA(

1
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

151
,
150
,
100
,
20
,
"
13954885177
"
);
LODOP.SET_PRINT_STYLEA(

2
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

125
,
584
,
99
,
20
,
"
发票打印(第1次)
"
);
LODOP.SET_PRINT_STYLEA(

3
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

465
,
140
,
198
,
20
,
"
陆百柒拾捌元叁角零分
"
);
LODOP.SET_PRINT_STYLEA(

4
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

465
,
599
,
70
,
20
,
"
678.30
"
);
LODOP.SET_PRINT_STYLEA(

5
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

496
,
408
,
59
,
20
,
"
H112063
"
);
LODOP.SET_PRINT_STYLEA(

6
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

191
,
58
,
100
,
20
,
"
国内漫游通话
"
);
LODOP.SET_PRINT_STYLEA(

7
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

191
,
217
,
100
,
20
,
"
584.00
"
);
LODOP.SET_PRINT_STYLEA(

8
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

222
,
58
,
100
,
20
,
"
增值业务费
"
);
LODOP.SET_PRINT_STYLEA(

9
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

222
,
217
,
100
,
20
,
"
48.30
"
);
LODOP.SET_PRINT_STYLEA(

10
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

251
,
58
,
100
,
20
,
"
代收费
"
);
LODOP.SET_PRINT_STYLEA(

11
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

251
,
217
,
100
,
20
,
"
50.00
"
);
LODOP.SET_PRINT_STYLEA(

12
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

280
,
58
,
100
,
20
,
"
优惠费
"
);
LODOP.SET_PRINT_STYLEA(

13
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

280
,
217
,
100
,
20
,
"
4.00
"
);
LODOP.SET_PRINT_STYLEA(

14
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

98
,
101
,
150
,
20
,
"
101081005747319387
"
);
LODOP.SET_PRINT_STYLEA(

15
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

97
,
307
,
150
,
20
,
"
2008年10月19日 10:28:38
"
);
LODOP.SET_PRINT_STYLEA(

16
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

152
,
584
,
103
,
20
,
"
138860016786
"
);
LODOP.SET_PRINT_STYLEA(

17
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

95
,
571
,
112
,
20
,
"
06775516
"
);
LODOP.SET_PRINT_STYLEA(

18
,
"
FontName
"
,
"
System
"
);
LODOP.SET_PRINT_STYLEA(

18
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

496
,
135
,
183
,
20
,
"
2008年09月(20080901-20080930)
"
);
LODOP.SET_PRINT_STYLEA(

19
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

496
,
572
,
112
,
20
,
"
-王府井中心店营
"
);
LODOP.SET_PRINT_STYLEA(

20
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

311
,
217
,
100
,
20
,
"
678.30
"
);
LODOP.SET_PRINT_STYLEA(

21
,
"
FontColor
"
,
16711680
);
LODOP.ADD_PRINT_TEXT(

311
,
58
,
100
,
20
,
"
费用合计
"
);
LODOP.SET_PRINT_STYLEA(

22
,
"
FontColor
"
,
16711680
);

LODOP.ADD_PRINT_SETUP_BKIMG(

"
<img src=Report/证件背景.jpg' />
"
);
LODOP.SET_SHOW_MODE (

"
BKIMG_IN_PREVIEW
"
,
1
);
}

</
script
>

其中大部分内容还是比较好理解的,不同的功能调用不同的函数Preview为预览、Setup为维护、Design为设计。

在套打的时候,注意需要通过下面代码来设置显示背景图片(打印的时候,是不打印背景的)。

LODOP.ADD_PRINT_SETUP_BKIMG("
<
img
src
=Report/证件背景.jpg'
/>
");
LODOP.SET_SHOW_MODE ("BKIMG_IN_PREVIEW",1); //打印预览时是否包含背景图

另外,由于报表的内容都是写在HTML页面中的,因此在动态设置内容的时候,一可以用脚本来读取界面元素作为数据源,二可以通过后台代码自动生成脚本代码,输出到前台页面中,看具体的需要了。

套打的预览界面大致如下图所示,其中蓝色部分是需要打印的内容,背景图片是一个发票的样板。

下面代码是通过脚本获取界面元素来取得相应的内容的,这种情况适合于界面上可能会修改一些打印的内容的情景。

LODOP.ADD_PRINT_TEXT(95, 695, 250, 52, document.getElementById("txtCompanyName").innerText); //业户名称
LODOP.SET_PRINT_STYLEA(1, "FontSize", 14);
LODOP.SET_PRINT_STYLEA(1, "Bold", 1);

var licenseDate = document.getElementById("txtLicenseDate").value;//证件有效期
var licenseYear = "";
var licenseMonth = "";
var licenseDay = "";
if (licenseDate != null && licenseDate != "") {
licenseYear = licenseDate.split("-")[0];
licenseMonth = licenseDate.split("-")[1];
licenseDay = (licenseDate.split("-")[2]).substr(0, 2);
}

LODOP.ADD_PRINT_TEXT(396, 190, 46, 22, validateYear); //有效期 结束 年
LODOP.SET_PRINT_STYLEA(6, "FontSize", 11);
LODOP.SET_PRINT_STYLEA(6, "Bold", 1);
LODOP.ADD_PRINT_TEXT(396, 253, 30, 22, validateMonth); //有效期 结束 月
LODOP.SET_PRINT_STYLEA(7, "FontSize", 11);
LODOP.SET_PRINT_STYLEA(7, "Bold", 1);
LODOP.ADD_PRINT_TEXT(396, 304, 32, 22, validateDay); //有效期 结束 日
LODOP.SET_PRINT_STYLEA(8, "FontSize", 11);
LODOP.SET_PRINT_STYLEA(8, "Bold", 1);

最后附上它的设计界面,其中生成代码功能可以生成用于静态HTML中的内容布局显示,做一定的修改调整就可以用在动态页面中了。非常有用的一个功能。

前段时间,有幸参加一次高级软件架构师的培训,授课老师介绍了两个很好玩的界面原型设计工具:GUIDesignStudio 和 Mockups For Desktop,现分享一下,截图说明,洗洗眼球,权当娱乐。

以前在做界面原型设计的时候(不多,但有时候要做的),印象中多数用Visio或者一些UML工具来大致描述一下,效果及交互性较差,不知您是否做过这方面的工作,不管有没有,我们来了解下这两个比较有意思的东西吧。
GUIDesignStudio 运行后,随便画上几个控件,界面如下:


运行设计好的内容后,界面如下所示:

Mockups For Desktop 运行后,涂鸦设计后的界面如下:


运行后的界面:



再看看网上其它人的一个设计图:


一般人做界面原型设计的时候,可能会存在下面几种设计方式:

纸质
:很多人比较推崇纸质原型设计,就是用笔和纸进行产品原型描绘(白板也常常起到类似的作用),不过我认为这只是产品经理进行原型 构思阶段使用的最佳方式,不过这才是原型设计的第一步,构思和框架基本确定之后,就需要将这个"纸上谈兵"的框架转移到更形象直观的电子文档上,便于后续的研讨、设计、开发和备案。

WORD
:这是原型设计时常用的一种方式,在WORD文档建立一块画布,用文本框、图片、控件等等组合起来形成一个原型设计方案。WORD文档门槛低,使用方便,功能效果丰富,如果一个熟练者甚至可以达到一个很好的类似实际页面的表现力,我的同事做出来的原型连设计师都夸奖它好比PS设计图一般(不过原型设计不讲求美观,不推荐花费过多精力去修饰)。但是WORD文档的WEB控件不是太好用,交互性也较弱。

VISIO
:这也是常用的原型设计工具,它的操作比WORD更加方便快捷,可以进行快速原型设计,但表现力弱一些,毕竟它不是专门的网页原型设计工具。

Photoshop
:也有人使用,不过用PS进行原型设计,费时费力,改动很不方便,容易降低效率,PM还是不要抢了UI设计师的饭碗。

Dreamweaver
:这是网页设计工具,但是对于功能复杂并且交互性很强的产品,可以通过DW去设计简单的HTML交互稿,这样更有说服力。

如果是设计原型,采用专门的原型设计工具,应该事半功倍的,当然原型设计工具,好用的应该还有不少,除了GUI Design Studio和Mockups For Desktop外,Axure Rp 好像也是一个不错的原型设计工具。在此放上一个Axure Rp的软件截图,感兴趣的可以也去了解一下。

下面我们先看看这两款软件的介绍内容:

GUI Design Studio
是一个给应用软件设计图形用户界面(GUIs)的专业工具。 它是一个不需要软件开发和编码的完整的设计工具。屏幕上的一切都通过图形方式创建,你可以设计整个应用程序或单个窗体,对话框和组件。并组合它们来创建更多的设计,和典型界面。我们也可以将它们链接在一起做为一个故事板,然后通过模拟器来运行,形成交互原型。 GUI Design Studio 将会支持所有基于微软 Windows 平台的软件环境。这个意味着,你可以先自由的设计,设计完后再选择实现工具。 按照这种方法,你可以快速聚焦到应用程序设计中,而不会被实现细节干扰。GUI Design Studio 可以被用于,任何你需要画程序界面,或着想展示这些界面是如何流转和配合的时候,例如: ·设计整个应用程序 ·文档化产品创意 ·创建项目建议 ·需求捕捉 ·创建模拟界面 ·给开发者的产品详细规格说明 ·注解现有产品的可用性 ·给现有产品提供建议 ·构建用户手册时候的临时屏幕截图等。

Balsamiq mockup
主要是做界面原型设计,是一款免费的带有手绘涂鸦风格的原型设计软件,这也是他独特的地方—可以手绘,当然也有丰富的各种控件元素,可以帮助你设计桌面应用软件,Web 2.0 站点,RIA富网络应用程序, Web站点和Web应用软件。
这个软件是由意大利人Peldi开发的,本来是他自己用它来做设计,满足自己的需求。而在经济寒冷的2008年,从1,322位付费用户那却获得了162,302美元的收入(其中12月份就有39,000美元);可见其受欢迎的程度。
虽然是由个人设计的,但功能却一点不弱于其他大牌的原型设计工具:操作方面,拖拽,控件分组,甚至元素之间的对齐都做得很贴心;预制了很多界面元素,从简单的输入框,下拉框,浏览器主要元素,到经常用得到的导航条,日历,表格,到复杂的Tag Cloud,Cover Flow, 地图,WYSWYG的格式工具栏等,有了这些不用从头画起,往往比用白板都快;下面是它的一些特性介绍,参考了解一下:

  • 易操作
    :从 Balsamiq Mockups 自带的元素里可以很方便地拖拽,效果图轻易形成;元素对齐很贴心;
  • 可偷懒
    :根据 Balsamiq Mockups 提供的 Wiki 风格的代码规则,画图时可以「偷懒」,输入文本符号则能生成图标。因此相比其它繁琐的软件操作,Balsamiq Mockups 也能更快地完成画图任务。
  • 控件足
    :Balsamiq Mockups 软件包括 50 多个控件, 70 多个图标。基本自带了所有常用的小控件,并在导航处进行分类;图标设计赏心悦目。要是 Balsamiq Mockups 允许用户导入自定义的控件就更好了,当然现有的也足够了 -:)
  • 新风格
    :让人眼前一亮的涂鸦风格,很能还原手绘效果;
  • 可中文
    :在菜单栏 View 里将 Use System Fonts 勾上,就能完美支持中文输入(注:非
    Balsamiq Mockups 中文版
    );
  • 其它点
    :Balsamiq Mockups 使用 xml 记录,方便移植、二次利用;可导出为 png 格式图片。

另外还有跨平台与多版本两个优点:

  • 跨平台
    :Balsamiq Mockups 基于 Air ,因此能同时在 Windows、Mac OS 及 Linux 下使用;
  • 多版本
    :包括桌面版本,以及集成于 Confluence、JIRA、XWiki、FogBugz 中的版本;

我们在界面设计的时候,不管是Web的还是Winform的程序,为了方便用户对各种数据进行操作,提高用户的操作体验,都是一个永恒不变的话题,需要尽可能地提高。本文抛砖引玉,介绍本人在Web和Winform中使用拼音简码以及智能提示的具体例子,对这个话题进行探讨。

在下面的Web界面中,我们可以通过拼音首字母或者部分中文内容,来模糊搜索(类似Google搜索的智能提示),一个方便用户搜索,第二个有效利用Ajax技术来提高用户的体验。


用户可以输入中文,一样有智能提示。



在Winform界面中,同样也可以做到智能提示,由于Winform中的响应速度比较快,我们可以根据输入的首字母或者部分中文快速更新列表内容即可,如下图所示。


其实以上两个,都是需要一个Sql函数,就是把中文转换为首字母的函数,以便能够快速搜索内容,下面我列出SqlServer和Oracle的转换首字母的函数。以飨读者。

SqlServer的汉字转拼音码的函数:



代码

--
--

Definition for user-defined function f_GetPy :

--

GO


create

function

[
dbo
]
.f_GetPy(
@str

nvarchar
(
4000
))

returns

nvarchar
(
4000
)

as


begin


declare

@strlen

int
,
@re

nvarchar
(
4000
)

declare

@t

table
(chr
nchar
(
1
)   collate   Chinese_PRC_CI_AS,letter
nchar
(
1
))

insert

into

@t
(chr,letter)

select

'

'
,
'
A
'

union

all

select

'

'
,
'
B
'

union

all


select

'

'
,
'
C
'

union

all

select

'

'
,
'
D
'

union

all


select

'

'
,
'
E
'

union

all

select

'

'
,
'
F
'

union

all


select

'

'
,
'
G
'

union

all

select

'

'
,
'
H
'

union

all


select

'

'
,
'
J
'

union

all

select

'

'
,
'
K
'

union

all


select

'

'
,
'
L
'

union

all

select

'

'
,
'
M
'

union

all


select

'

'
,
'
N
'

union

all

select

'

'
,
'
O
'

union

all


select

'

'
,
'
P
'

union

all

select

'

'
,
'
Q
'

union

all


select

'

'
,
'
R
'

union

all

select

'

'
,
'
S
'

union

all


select

'

'
,
'
T
'

union

all

select

'

'
,
'
W
'

union

all


select

'

'
,
'
X
'

union

all

select

'

'
,
'
Y
'

union

all


select

'

'
,
'
Z
'


select

@strlen
=
len
(
@str
),
@re
=

'

'


while

@strlen
>

0


begin


select

top

1

@re
=
letter
+
@re
,
@strlen
=
@strlen
-
1


from

@t
a
where
chr
<=
substring
(
@str
,
@strlen
,
1
)

order

by
chr
desc


if

@@rowcount
=
0


select

@re
=
substring
(
@str
,
@strlen
,
1
)
+
@re
,
@strlen
=
@strlen
-
1


end


return
(
@re
)

end

Oracle的汉字转拼音首字母的函数:



代码

CREATE

OR

REPLACE

FUNCTION
F_PINYIN(P_NAME
IN

VARCHAR2
)
RETURN

VARCHAR2

AS

V_COMPARE

VARCHAR2
(
100
);
V_RETURN

VARCHAR2
(
4000
);


FUNCTION
F_NLSSORT(P_WORD
IN

VARCHAR2
)
RETURN

VARCHAR2

AS


BEGIN


RETURN
NLSSORT(P_WORD,
'
NLS_SORT=SCHINESE_PINYIN_M
'
);

END
;

BEGIN



FOR
I
IN

1
..NVL(LENGTH(P_NAME),
0
) LOOP
V_COMPARE :

=
F_NLSSORT(SUBSTR(P_NAME, I,
1
));

IF
V_COMPARE
>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
A
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'
簿
'
)
THEN

V_RETURN :

=
V_RETURN
||

'
B
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
C
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
D
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
E
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
F
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
G
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
H
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
J
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
K
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
L
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
M
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
N
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
O
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
P
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
Q
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
R
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
S
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
T
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
W
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
X
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
Y
'
;
ELSIF V_COMPARE

>=
F_NLSSORT(
'

'
)
AND
V_COMPARE
<=
F_NLSSORT(
'

'
)
THEN

V_RETURN :

=
V_RETURN
||

'
Z
'
;

END

IF
;

END
LOOP;

RETURN
V_RETURN;

END
;

使用代码大概如下所示:

使用例子1:



代码


///

<summary>


///
根据商品名称获取商品列表

///

</summary>


///

<param name="goodsType">
商品类型
</param>


///

<returns></returns>



public
List
<
GoodsInfo
>
FindByName(
string
goodsName)
{

string
sql
=

string
.Format(
"
Name like '%{0}%' or dbo.f_GetPy(Name) like '{0}%'
"
, goodsName);

return

this
.Find(sql);
}

使用例子2(基于Ajax的Web智能提示):

数据库访问层的代码如下所示:



代码


///

<summary>


///
获取公司名称

///

</summary>


///

<param name="topCount"></param>


///

<param name="name"></param>


///

<returns></returns>



public
List
<
string
>
GetTopCompanyName(
int
topCount,
string
name)
{

string
sql
=

string
.Format(
@"
Select * from (Select Company_Name from tb_enterprise where Company_Name like '%{1}%' or F_PINYIN(Company_Name) like '%{1}%' )
WHERE ROWNUM <= {0} ORDER BY ROWNUM ASC

"
, topCount, name);
DataTable dt

=
SqlTable(sql);
List

<
string
>
list
=

new
List
<
string
>
();

foreach
(DataRow row
in
dt.Rows)
{
list.Add(row[

0
].ToString());
}

return
list;
}


Web前台部分页面如下所示:



代码

<
td
align
="left"
style
="background-color: #F1F6FF; width: 200px;"
>


<
asp:TextBox
ID
="txtCompanyName"
runat
="server"
Width
="200"
></
asp:TextBox
>


<
cc1:AutoCompleteExtraExtender
ID
="AutoCompleteExtraExtender1"
runat
="server"
ServiceMethod
="GetCompanyNameList"

TargetControlID

="txtCompanyName"
AsyncPostback
="false"
UseContextKey
="True"
AutoPostback
="true"

MinimumPrefixLength

="2"
CompletionInterval
="10"
OnItemSelected
="AutoCompleteExtraExtender1_ItemSelected"
>


</
cc1:AutoCompleteExtraExtender
>


</
td
>

Web后台页面的代码如下所示:



代码

[System.Web.Services.WebMethodAttribute(), System.Web.Script.Services.ScriptMethodAttribute()]

public

static

string
[][] GetCompanyNameList(
string
prefixText,
int
count,
string
contextKey)
{

//
获取自动完成的选项数据


List
<
string
[]
>
list
=

new
List
<
string
[]
>
();
List

<
string
>
nameList
=
BLLFactory
<
Enterprise
>
.Instance.GetTopCompanyName(count, prefixText.ToUpper());

for
(
int
i
=

0
; i
<
nameList.Count; i
++
)
{

string
[] Respuesta
=

new

string
[
2
];
Respuesta[

0
]
=
nameList[i];
Respuesta[

1
]
=
i.ToString();
list.Add(Respuesta);
}


return
list.ToArray();
}


protected

void
AutoCompleteExtraExtender1_ItemSelected(
object
sender, EventArgs e)
{

//
TextBox txtCompanyName = FindControl(this.AutoCompleteExtraExtender1.TargetControlID) as TextBox;

//
string companyName = txtCompanyName.Text;


//
根据用户选项更新显示相关内容


BindData();
}

这样就可以,在界面上输入几个简单的英文字符或者中文名称,就会有智能提示的列表出现,选择其中一个可以显示相关的信息了。

由于在Web开发中,为了实现Ajax的智能提示效果,需要一个特殊的类库,地址如下所示:
https://files.cnblogs.com/wuhuacong/AutoCompleteExtra.rar

前面发表过两篇随笔:《
Socket开发探秘--基类及公共类的定义
》和《
Socket开发探秘--数据封包和拆包
》,介绍了Socket方面的开发。本文继续探讨使用Json格式来作为Socket收发协议方面的技术问题。

前面说到,
收到的Socket数据经过粗略的解析后,就是PreData类型的数据,这个是通用的数据格式,我们需要进一步处理才能转化为所能认识的数据对象(实体类对象),同样,我们发送数据的时候,内容部分肯定是按照一定协议规则串联起来的数据,那么我们就需要把实体转化为发送的数据格式。综上所述,我们通过实体类,必须实现数据的发送和读取的转换。

由于数据的封包拆包是一个繁琐的过程,代码重复性比较多,而且也容易出错。前面介绍过
设计一个基类,我们把所有对数据的拆包和封包,利用反射机制,减少我们的代码量,提高代码的优雅性。
但是后来有人建议,可能使用Json格式的数据内容可能更好,确实,如果是采用以|分割符号的内容,有一个缺点,就是数据内容比较难懂(有时候我们还是需要分析数据包的),Json会更易读一些。
另外,使用Json可以脱离字段顺序的关系,可以向后兼容一些历史的协议,例如首次定义的协议有字段A、B,后来服务器升级,升级增加支持C、D,旧的客户端可以和新的客户端并存,增加了兼容性。
因此我在此基础上优化一下代码,使其支持Json格式的数据发送,其实由于之前的代码封装的还算比较好,因此修改为Json格式的协议内容,只需要修改BaseEntity中几行代码即可实现,下面贴出修改代码的前后对比(注释掉的代码是原来的代码):




代码


public

class
BaseEntity
{

protected

string
HeaderKey;


public
BaseEntity()
{
}


///

<summary>


///
转换Socket接收到的信息为对象信息

///

</summary>


///

<param name="data">
Socket接收到的信息
</param>



public
BaseEntity(
string
data)
{

#region
普通按顺序构造的代码


//
string[] dataArray = null;

//
dataArray = NetStringUtil.UnPack(data);

//
if (dataArray != null && dataArray.Length > 0)

//
{

//
int i = 0;

//
FieldInfo[] fieldArray = ReflectionUtil.GetFields(this);

//
if (fieldArray == null || dataArray.Length != fieldArray.Length)

//
{

//
throw new ArgumentException("收到的信息和字段信息不一致");

//
}


//
if (fieldArray != null)

//
{

//
foreach (FieldInfo info in fieldArray)

//
{

//
string strValue = dataArray[i++];

//
ReflectionUtil.SetField(this, info.Name, strValue);

//
}

//
}

//
}



#endregion



//
Json格式转换后的内容,肯定是小于或者等于实体类的内容

//
因为对象要兼容历史的Json内容,通过反射以最小的成员来赋值


BaseEntity obj
=
JsonTools.JsonToObject(data,
this
.GetType())
as
BaseEntity;

if
(obj
!=

null
)
{
FieldInfo[] fieldArray

=
ReflectionUtil.GetFields(obj);

foreach
(FieldInfo info
in
fieldArray)
{

object
value
=
ReflectionUtil.GetField(obj, info.Name);
ReflectionUtil.SetField(

this
, info.Name, value);
}
}
}


///

<summary>


///
转换对象为Socket发送格式的字符串

///

</summary>


///

<returns></returns>



public

override

string
ToString()
{

string
data
=

""
;


#region
普通按顺序构造的代码


//
FieldInfo[] fieldArray = ReflectionUtil.GetFields(this);

//
StringBuilder sb = new StringBuilder();

//
if (fieldArray != null)

//
{

//
foreach (FieldInfo info in fieldArray)

//
{

//
sb.Append(ReflectionUtil.GetField(this, info.Name));

//
sb.Append("|");

//
}

//
}

//
data = sb.ToString().Trim('|');



#endregion



#region
按Json格式构造的代码


data

=
JsonTools.ObjectToJson(
this
);


#endregion




if
(
string
.IsNullOrEmpty(HeaderKey))
{

throw

new
ArgumentNullException(
"
DataTypeKey
"
,
"
实体类未指定协议类型
"
);
}
data

=
NetStringUtil.PackSend(HeaderKey, data);

return
data;
}
}

JsonTools是一个Json的辅助类,负责Json内容的解析的,由于我的项目是采用C#2.0的,因此Json操作采用了Newtonsoft.Json.dll类库,如果是C#3.5的,采用系统内置类库就可以了。




代码


///

<summary>


///
Json处理类

///

</summary>



public

class
JsonTools
{

///

<summary>


///
从一个对象信息生成Json串

///

</summary>


///

<param name="obj"></param>


///

<returns></returns>



public

static

string
ObjectToJson(
object
obj)
{

return
JavaScriptConvert.SerializeObject(obj);
}


///

<summary>


///
从一个Json串生成对象信息

///

</summary>


///

<param name="jsonString"></param>


///

<param name="objType"></param>


///

<returns></returns>



public

static

object
JsonToObject(
string
jsonString, Type objType)
{

return
JavaScriptConvert.DeserializeObject(jsonString, objType);
}

}

这样就可以实现Json格式内容的发送和接受了。


使用测试客户端对数据进行测试,并调用ToString()生成接受到的数据内容,查看具体的内容,得到的效果如下所示。