layui用table.render加载数据 用table.reload重载里面的数据---解决table.render重新加载闪动的问题_itfallrain的博客-CSDN博客

mikel阅读(635)

来源: layui用table.render加载数据 用table.reload重载里面的数据—解决table.render重新加载闪动的问题_itfallrain的博客-CSDN博客

今天在用layui 展示数据的时候,首先想到了table.render这个插件进行数据的展示,因为数据要实时刷新,说到实时刷新,你最低要三秒刷新一次表格的数据吧!!!一开始写了个定时把table.render放到定时函数里面,三秒执行一次函数,那么问题来了,虽然效果是实现了,但这是重新加载表格啊,三秒闪一次,别说是用户了,我都看不下去了,闪的眼疼,就想有没有只让数据重新加载,表格不动。终于功夫不负有心人!!!
tablePlug感觉就是救命稻草,就是解决这个问题的,这也是贤心人性补充吧
下面教你怎么用
1:首先奉上下载这个插件的地址
https://gitee.com/sun_zoro/layuiTablePlug
2:然后找到“下载”文件夹下把tablePlug文件夹复制到你项目的合适位置
3:敲黑板!!!把你项目一开始引入layui modules文件夹下的table.js给替换了(或者你手动修改源码),替换成你从git上下载的那个table.js(这个可能后续不需要修改,现在贤心还没整合!!!)
然后就是引入了

layui.config({base:’static/tablePlug/’}).use([‘table’,’tablePlug’], function(){
var table = layui.table;
var $ = layer.$;
var tablePlug=layui.tablePlug;
tablePlug.smartReload.enable(true);//处理不闪动的关键代码
table.render({
elem: ‘#testone’
, method : ‘POST’
,contentType: ‘application/json’
,url:UrlSchool.oneWen()
,id:”testoneTable”
,smartReloadModel:true
,cellMinWidth: 80 //全局定义常规单元格的最小宽度,layui 2.2.1 新增
,cols: [
[
{field:’deviceName’, title:’设备名称’, width:100}
,{field:’valveStatusName’, title:’阀门状态’, width:120, edit: ‘text’}
]
]
});
// var t1 = window.setInterval(hahahah,3000);
function hahahah(){
// 表格重载
table.reload(‘testoneTable’,{
url:UrlSchool.oneWen()
});
}
});

重点:需要重新加载数据要在table.render中加入 ,smartReloadModel:true(开启)
友情提示:table.reload这个不需要任何改动,组件会根据你reload里面的参数去判断是重新请求数据还是重载!!!
写的应该很详细了,觉得有问题的可以直接参考贤心原文解释!!!
最后的最后给你个贤心博文地址,如果遇到问题这里面有解决办法
https://fly.layui.com/jie/43423/
从这里往下看!!!

————————————————
版权声明:本文为CSDN博主「itfallrain」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38215042/article/details/102705606

 EditPlus 格式化HTML JS CSS 教程及edtools分享链接_editplus html代码格式化_Seleey的博客-CSDN博客

mikel阅读(383)

 

来源: (1条消息) EditPlus 格式化HTML JS CSS 教程及edtools分享链接_editplus html代码格式化_Seleey的博客-CSDN博客

首先你得安装一个EditPlus,然后下载你想格式化的xxxformatter.js文件,也就是网上说的EDTOOLS工具包集合,包括html,css,js格式化工具文件。
我的edtools分享链接:
https://pan.baidu.com/s/12fUx8XhkeWsaqucAgLH9Jg 提取码: dm3h

1.:在tools栏—>打开Preferences窗口,点击User tools.

2.按照上图进行,设置。点击Group Name填入”前端开发工具”,
然后Add Tool,填入“html格式化”,填入Command的值
cscript /nologo “D:\software\edTools\htmlFormatter.js”
后面的就是文件工具路径

然后选择Action,

选择标出来的Action的值(第四个选项),
最后操作6,7就可以了。
继续tools

完成了吗,没有,还会出现错误,哈哈,

解决方法:创建reg文件 内容如下 点击运行:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\.js]
@=”JSFile”
“Content Type”=”text/plain”
“PerceivedType”=”text”

[HKEY_CLASSES_ROOT\.js\OpenWithProgids]
“VisualStudio.js.b2b43195″=””
1
2
3
4
5
6
7
8
9
添加注册表成功后,重试一下,就我上面图一样格式化正常了,
尼玛,又乱码了。
解决方法:
1.出现乱码后按Ctrl+Z,回退到乱码前的正常显示状态

2.先把本文档按ANSI编码的先保存一遍(此时不会乱码,此步骤是保证中文不乱码正常显示)

(Editplus的编码设置步骤:

英文版在上边的工具栏找到Document——File Encoding——Convert Encoding——ANSI,

中文版:文件——文件编码——转换编码——选ANSI)

3.再按快捷键让edtools格式化代码(此时不会乱码)

4.再把文档按UTF-8的编码保存一遍
————————————————
版权声明:本文为CSDN博主「Seleey」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44094872/article/details/101988341

 转载editplus格式化js,xml等____附输入错误: 没有文件扩展“.js”的脚本引擎解决_jsformatter.js_大脑袋硕鼠的博客-CSDN博客

mikel阅读(392)

来源: (1条消息) 转载editplus格式化js,xml等____附输入错误: 没有文件扩展“.js”的脚本引擎解决_jsformatter.js_大脑袋硕鼠的博客-CSDN博客

本人一般用editplus写一些小的测试代码或者来研究学习别人的代码,但经常会遇到这些问题:下载过来的HTML/CSS代码混乱,JS代码被压缩,或者是我们想把我们的代码做一下压缩混淆以供发布时使用。当然,对于代码的格式化和代码压缩等,我们都可以使用专有的工具,或者使用一些在线的工作来做,既然EditPlus可以扩展插件,那我们何不利用这一功能来把这些工具集成到EP中呢?

其实很早我都有这些想法,一直没有深究其做法,EP的用户工具组,我一般也是挂些帮助手册之类的供开发时参考。今天在BlueIdea看到有人发了一篇名为“让Editplus自动格式化css和js”的文章,看完后觉得写的很好,我也突然来了灵感,为什么不把前端开发常用工具都集成进去呢?

说做就做,我在网上找了一些相关的工具代码,按照作者的方式开始改造(作者是使用“本地cScript调用JS+EP的文本过滤器”的方式来实现的)。

 

     下面来说下集成的方法(以下以EditPlus3中文版本为例):

      1、下载工具包:edtools.rar ,解压后放到磁盘的一个目录,如D:/edTools。

      2、打开ED,打开“工具”-“用户工具组”,在弹出的对象框中,在“组和工具项目”下拉框中选择一个工具组,点击“组名称”,为该组工具设定一个名称,如“前端开发工具”,如下图所示:

 

 

3、下面开始加入工具,这里主要以JSFormat为例讲解,其它类似。
点击“添加”–“应用程序”,在新建的项中,菜单文本写上名称,如”jsFormat”,在命令里面输入:

 

 

  1. cscript /nologo “D:/edTools/jsFormatter.js”

 

 

后面引号中的内容要修改你磁盘上对应的文件的路径。
在下面的几个选项中,选择“运行为文本过滤”。如下图所示:

 

 

4、其它几个的安装方式与jsFormat的安装类似,这里不再重复。点击ED ,工具你会发现:jsFormat   Ctrl+1,打开一个js点击后你会发现奥秘。

 

 

下载地址: http://download.csdn.net/source/1907530

转自:CSDN:http://blog.csdn.net/misswuyang/article/details/5018143

然后crtl+1格式化错误”输入错误: 没有文件扩展“.js”的脚本引擎。”是因为你js格式文件被别的软件关联了

你可以

如系统中安装了ULTRAEDIT或者E钻加密软件等,就需要去掉关联;

或者如下解决方法:

在运行中输入“regedit”进入注册表,

只需要把[HKEY_CLASSES_ROOT\.js] 项下的那个默认值改成 “JSFile” 就可以正常运行JS 文件了。

ThinkPHP3.2.3扩展之生成PDF文件(MPDF) - 博文 - 手册网

mikel阅读(559)

来源: ThinkPHP3.2.3扩展之生成PDF文件(MPDF) – 博文 – 手册网

先安照路径放好如图。

2016-03-14_144416.gif

下面是使用方法

  1. public function pdf(){
  2.     //引入类库
  3.     Vendor(‘mpdf.mpdf’);
  4.     //设置中文编码
  5.     $mpdf=new \mPDF(‘zh-cn’,‘A4’, 0, ‘宋体’, 0, 0);
  6.     //html内容
  7.     $html=‘<h1><a name=”top”></a>一个PDF文件</h1>’;
  8.     $mpdf->WriteHTML($html);
  9.     $mpdf->Output();
  10.     exit;
  11. }

在浏览器中输入

  1. xxx/index.php?s=/Home/Index/pdf    //换成你的url路径

效果如下

2016-03-14_144911.gif

下面是高级一些的使用方法:

  1. public function pdf(){
  2.         //引入类库
  3.         Vendor(‘mpdf.mpdf’);
  4.         //设置中文编码
  5.         $mpdf=new \mPDF(‘zh-cn’,‘A4’, 0, ‘宋体’, 0, 0);
  6.         $mpdf->SetWatermarkText(‘中国水印’,0.1);
  7.         $strContent = ‘我是带水印的PDF文件’;
  8.         $mpdf->showWatermarkText = true;
  9.         $mpdf->SetHTMLHeader( ‘头部’ );
  10.         $mpdf->SetHTMLFooter( ‘底部’ );
  11.         //$stylesheet =file_get_contents(‘themes/wei/css/bootstrap.min.css’);
  12.         //$mpdf->WriteHTML($stylesheet, 1);
  13.         $mpdf->WriteHTML($strContent);
  14.         //保存ss.pdf文件
  15.         $mpdf->Output(‘ss.pdf’);
  16.         //直接浏览器输出pdf
  17.         $mpdf->Output(‘tmp.pdf’,true);
  18.         $mpdf->Output(‘tmp.pdf’,‘d’);
  19.         $mpdf->Output();
  20.         exit;
  21. }

2016-03-14_150552.gif

mpdf下载可以到官网下载http://www.mpdf1.com 或者在下面的百度云里下载

tcpdf开发文档(中文翻译版) - zh7314 - 博客园

mikel阅读(564)

来源: tcpdf开发文档(中文翻译版) – zh7314 – 博客园

2017年5月3日15:06:15

这个是英文翻译版,我看过作者的文档其实不太友善或者不方便阅读,不如wiki方便

后面补充一些,结构性文档翻译

这是一部官方网站文档,剩余大部分都是开发的时候和网络总结来的

项目官网:https://tcpdf.org/

github:https://github.com/tecnickcom/TCPDF

都没比较完整的api文档,所以最后的demo需要总去总结,好吧

(发现所有例子全部翻译消耗时间过于长,只把其中不同的部分分离出来,也方便读者使用)

 

2018年1月24日09:27:20

发现一个比较大的问题,不算bug,但是是一个比较蛋疼的 问题,当你需要直接将word或者excel的东西,直接获取出来,贴到编辑器,在拿出来,替换内容标签做pdf的时候,

tcpdf需要的是必须闭合的标签,要可以100%转换成xml,不然就报错 Undefined index: startcolumn in tcpdf.php (line 19456)

 

2018年1月29日17:43:18

因为新需要,合同页面变得很复杂,需要支持后端编辑合同模板,又得兼容合同编辑之后,在生成合同

解决办法,找前端直接使用table布局或则div+css,直接把页面写出来在存起来,不然就会报错,虽然麻烦一点,而且也不是那么灵活,但是如果在不随意改变合同模板样式的架构的情况下,还是可以使用的

笔者现在已经尝试了dompdf 但是目前看来这个的中文支持不好,css也好像支持不好,

如果你需要弄复杂页面的合同,请参看 :http://www.cnblogs.com/zx-admin/p/8352003.html

mpdf目前对table布局和div+css布局支持唯一个支持的比较好的插件

 

新增composer 使用tcpdf

composer require tecnickcom/tcpdf
//引入tcpdf
use TCPDF;

我使用laravel是5.4,但是服务上php是5.6但是laravel 这个组件 doctrine/inflector 1.2.x-dev requires php ^7.0

需要的是7,又不好直接服务器上php7,thinkphp最新版的话就直接上7,5.6版本不会出现类似的问题,好吧,我还是觉得tp好使

如果把laravl的计划任务模块,移植到tp上就爽了,有时间在弄这个

 

 

 

Fonts设置字体

注意:以下信息仅对旧的TCPDF库有效。 新的tc-lib-pdf库使用能够即时转换字体的新的tc-lib-pdf-字体库。

TCPDF支持TrueTypeUnicode(UTF-8 Unicode),OpenTypeUnicode,TrueType,OpenType,Type1,CID-0和Core(标准)字体。
有两种使用新字体的方法:将其嵌入PDF(有或没有子集)。 当未嵌入字体时,将在系统中进行搜索。 优点是PDF文件较轻; 另一方面,如果不可用,则使用替换字体。 因此,最好确保在客户端系统上安装所需的字体。 如果该文件要被大量观众查看,建议嵌入。

TCPDF支持字体子集,以减少大型unicode字体文件的文档大小。 如果您将整个字体嵌入到PDF中,则另一端的人即使没有您的字体也可以进行更改。 如果您对字体进行子集,则PDF的文件大小会更小,但是接收PDF的人需要具有相同的字体才能更改PDF。 有关启用/禁用字体子集的选项,请参见SetFont()和AddFont()方法的源代码文档。

可以不嵌入的字体只是标准的核心字体和CID-0字体。

PDF Core(标准)字体是:

  • courier : Courier
  • courierB : Courier Bold
  • courierBI : Courier Bold Italic
  • courierI : Courier Italic
  • helvetica : Helvetica
  • helveticaB : Helvetica Bold
  • helveticaBI : Helvetica Bold Italic
  • helveticaI : Helvetica Italic
  • symbol : Symbol
  • times : Times New Roman
  • timesB : Times New Roman Bold
  • timesBI : Times New Roman Bold Italic
  • timesI : Times New Roman Italic
  • zapfdingbats : Zapf Dingbats

转换TCPDF的字体

使用addTTFfont()方法,您可以直接从TrueType,OpenType或Type1字体创建一个TCPDF字体。
注意:’fonts’文件夹必须由webserver可写。

实例:

$fontname = $pdf->addTTFfont(‘/path-to-font/DejaVuSans.ttf’, ‘TrueTypeUnicode’, “, 32);

检查addTTFfont()的源代码文档以获取更多信息。

设置字体

在配置文件(config / tcpdf_config.php)上将K_PATH_FONTS常量设置为TCPDF字体路径。
在TCPDF类构造函数的第四个参数上,如果使用Unicode字体(true)或旧字体(false),则必须指定。

要在脚本中设置字体,只需调用SetFont()方法即可。 在打印文本或生成的文档无效之前,必须至少调用此方法一次。 该方法可以在创建第一个页面之前调用,字体从页面到页面保留:

SetFont(string family[,string style[,string size]])
  • family : 字体属性 它可以是字体名称或标准系列之一(不区分大小写):
    • Courier (fixed-width)
    • Helvetica or Arial (synonymous; sans serif)
    • Times (serif)
    • Symbol (symbolic)
    • ZapfDingbats (symbolic)

    也可以传递一个空字符串。 在这种情况下,现有的家庭被保留。

  • style : 字体样式。 可能的值是(不区分大小写):
    • empty string: regular
    • B: bold
    • I: italic
    • U: underline

    或任何组合。 默认值为常规值。

  • size:字体大小分。 默认值为当前大小。 如果文档开头没有指定大小,则取值为12。
  • fontfile : 字体定义文件。 默认情况下,名称是从家庭和样式构建的,小写没有空格。
  • subset :如果true只嵌入字体的一个子集(仅存储与所使用字符相关的信息); 如果假嵌入完整字体; 如果’default’使用setFontSubsetting()设置的默认值。 此选项仅对TrueTypeUnicode字体有效。 如果要启用用户更改文档,请将此参数设置为false。 如果您对字体进行分类,那么接收PDF的人将需要使用相同的字体才能更改PDF。 PDF的文件大小也会较小,因为您仅嵌入字体的一部分。

Example:

$pdf->SetFont(‘times’, ‘BI’, 20, “, ‘false’);

Performances(性能)

  • 安装和配置PHP操作码cacher,如XCache;
  • 编辑php.ini文件并增加脚本可能消耗的最大内存量(memory_limit);
  • 编辑php.ini文件并增加每个脚本的最大执行时间(max_execution_time);
  • 编辑config / tcpdf_config.php文件:手动设置$ _SERVER [‘DOCUMENT_ROOT’],K_PATH_MAIN和K_PATH_URL常量,并删除自动计算部分;
  • 如果您不使用泰语,请编辑config / tcpdf_config.php文件并将K_THAI_TOPCHARS常量设置为false;
  • 如果您不需要扩展字符,请编辑config / tcpdf_config.php文件,并将默认字体设置为核心字体;
  • 如果不需要UTF-8 Unicode,则将TCPID构造函数上的unicodefalse�������参数设置为�����,将 encoding参数设置为“ISO-8859-1”或其他字符映射。
  • 默认情况下,TCPDF允许字体子集减少嵌入式Unicode TTF字体的大小,这个过程非常慢,需要大量内存,可以使用setFontSubsetting(false)方法关闭;
  • 尽可能使用核心字体而不是嵌入字体;
  • 如果不严格要求,避免使用HTML语法(writeHTML和writeHTMLCell方法)
  • 拆分较小的大块HTML块;
  • 如果不严格要求,避免使用翻译;
  • 更改后重新启动网络服务器。

 

api使用分析实例

 

 

//去掉默认的页头页脚。比如那个横线
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);

 

//设置一个单元格,可以按比例缩放单元格大小
$pdf->Cell(0, 0, 'TEST CELL STRETCH: no stretch', 1, 1, 'C', 0, '', 0);

 

 

复制代码
//设置一个单元格,可以按比例缩放单元格大小
$pdf->Cell(0, 0, 'TEST CELL STRETCH: no stretch', 1, 1, 'C', 0, '', 0);


// 设置背景填充色
$pdf->SetFillColor(220, 255, 220);

/*
*此方法允许以换行符打印文本。
*它们可以是自动的(一旦文本到达单元格的右边界)或显式(通过\ n字符)。 输出所需的多个单元格,一个低于另一个。<br />
*文本可以对齐,居中或对齐。 单元格块可以框架并绘制背景
 */
//设置一个text文本块
$pdf->MultiCell(55, 5, '[LEFT] '.$txt, 1, 'L', 1, 0, '', '', true);
复制代码

 

 

 个人demo实例

注意pdf不是完全支持html标签,所以如果你的写入pdf的是html内容请使用txt或者qq对话框,过滤掉多数的html,

不然写入pdf就会异常,但是是openXML就不会有这个问题,写入word就没任何问题

 

复制代码
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
            $pdf->setPrintHeader(false);    //页面头部横线取消
            $pdf->setPrintFooter(false); //页面底部更显取消
            $pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);//自动分页
            $pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);//设置页面margin
            $pdf->SetFont('stsongstdlight', '', 12);//设置字体,注意在循环里面一定要把new都一起放在循环里面,不然会报错,没有设置字体,因为这个需要上下文来读取配置
            $pdf->AddPage();//增加一个页面
            $pdf->setCellPaddings(0, 0, 0, 0);//设置一个单元格的padding
            //追加用户亲笔签名到文件底部
            $identify = $v['identify'];
            $contract = M("contract")->where(array("identify" => "$identify"))->find();
            $file = M("file")->where(array("file_id" => $contract['contract_sign_pic_file_id']))->find();
            $sign_pic_url = app_standard_path_new($file['file_path']);

            //tcpdf支持远程图片,所以不用麻烦

            $pdf->writeHTML($contract_content, $ln = true, $fill = false, $reseth = false, $cell = false, $align = '1');//这个如果里面有远程图片,不能直接获取,需要在代码本地才行,把

            $pdf->writeHTML('<b>用户签名</b><br>', $ln = true, $fill = false, $reseth = false, $cell = false, $align = '1');

            $pdf->Image($sign_pic_url, '', '', '40%', '40%', '', '', 'T', false, 300, '', false, false, 1, false, false, false);
//这个可以获得远程图片地址,但是注意它一定是可以在公网可以访问或者授权的
//如果怕图片太大也是可以安比例缩小,放大的

// i 输出到浏览器,D下来php://output S保存
contractdata=������������=pdf->Output($file_name, ‘S’);

file_put_contents(tmpname,�������,contract_data);//所以写入你想写入的地方的文件

 


复制代码

 

新增实例,吧图片章浮动到文字上面demo

2018年1月17日11:34:27

 

例子的官方地址  https://tcpdf.org/examples/

 

Example 001 : first example with default Header and Footer  第一个例子设置默认页眉和页脚的

 

复制代码
<?php

// Include the main TCPDF library (search for installation path).
require_once('./tcpdf/tcpdf.php');

// create new PDF document
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);

// set document information
$pdf->SetCreator(PDF_CREATOR); //设置创建者
$pdf->SetAuthor('Nicola Asuni'); //设置作者
$pdf->SetTitle('TCPDF Example 001'); //设置文件的title
$pdf->SetSubject('TCPDF Tutorial'); //设置主题
$pdf->SetKeywords('TCPDF, PDF, example, test, guide'); //设置关键词
// set default header data
$pdf->SetHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE . ' 001', PDF_HEADER_STRING, array(0, 64, 255), array(0, 64, 128)); //设置头部,比如header_logo,header_title,header_string及其属性
$pdf->setFooterData(array(0, 64, 0), array(0, 64, 128));

// set header and footer fonts
$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN)); //设置页头字体
$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA)); //设置页尾字体
// set default monospaced font
$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED); //设置默认等宽字体
// set margins
$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); //设置margins 参考css的margins
$pdf->SetHeaderMargin(PDF_MARGIN_HEADER); //设置页头margins
$pdf->SetFooterMargin(PDF_MARGIN_FOOTER); //设置页脚margins
// set auto page breaks
$pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); //设置自动分页
// set image scale factor
$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); //设置调整图像自适应比例
// set some language-dependent strings (optional) 设置一些与语言相关的字符串
if (@file_exists(dirname(__FILE__) . '/lang/eng.php')) {
    require_once(dirname(__FILE__) . '/lang/eng.php');
    $pdf->setLanguageArray($l);
}

// ---------------------------------------------------------
// set default font subsetting mode
$pdf->setFontSubsetting(true); //设置默认字体子集模式
// Set font
// dejavusans is a UTF-8 Unicode font, if you only need to
// print standard ASCII chars, you can use core fonts like
// helvetica or times to reduce file size.
$pdf->SetFont('dejavusans', '', 14, '', true); //设置字体
// Add a page
// This method has several options, check the source code documentation for more information.
$pdf->AddPage(); //增加一个页面
// set text shadow effect  设置文字阴影效果
$pdf->setTextShadow(array('enabled' => true, 'depth_w' => 0.2, 'depth_h' => 0.2, 'color' => array(196, 196, 196), 'opacity' => 1, 'blend_mode' => 'Normal'));

// Set some content to print
$html = <<<EOD
<h1>Welcome to <a href="http://www.tcpdf.org" style="text-decoration:none;background-color:#CC0000;color:black;">&nbsp;<span style="color:black;">TC</span><span style="color:white;">PDF</span>&nbsp;</a>!</h1>
<i>This is the first example of TCPDF library.</i>
<p>This text is printed using the <i>writeHTMLCell()</i> method but you can also use: <i>Multicell(), writeHTML(), Write(), Cell() and Text()</i>.</p>
<p>Please check the source code documentation and other examples for further information.</p>
<p style="color:#CC0000;">TO IMPROVE AND EXPAND TCPDF I NEED YOUR SUPPORT, PLEASE <a href="http://sourceforge.net/donate/index.php?group_id=128076">MAKE A DONATION!</a></p>
EOD;

/*
 * 
  此方法允许以换行符打印文本。
  它们可以是自动的(一旦文本到达单元格的右边界)或显式(通过\ n字符)。 输出所需的许多单元,一个低于另一个。
    文本可以对齐,居中或对齐。 单元格块可以框架并绘制背景。
 */

// Print text using writeHTMLCell() 
$pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true); //使用writeHTMLCell打印文本
// ---------------------------------------------------------
// Close and output PDF document
// This method has several options, check the source code documentation for more information.
$pdf->Output('example_001.pdf', 'I'); //I输出在浏览器上

//============================================================+
// END OF FILE
//============================================================+
复制代码

 

Example 002 : without Header and Footer  取消页眉和页脚

 

Example 003 : custom Header and Footer  自定义页眉和页脚

Example 004 : text Stretching with Cell()

Example 005 : Multicell()

Example 006 : WriteHTML()

Example 007 : independent columns with WriteHTMLCell()

Example 008 : external UTF-8 Unicode text file

Example 009 : Image()

Example 010 : text on multiple columns

Example 011 : table with primitive methods

Example 012 : graphic methods

Example 013 : graphic transformations

Example 014 : forms and JavaScript

Example 015 : index with Bookmarks()

Example 016 : document encryption

Example 017 : independent columns with MultiCell()

Example 018 : Persian and Arabic language on RTL document

Example 019 : alternative config file

Example 020 : complex alignment with Multicell()

Example 021 : writeHTML() text flow

Example 022 : CMYK colors

Example 023 : page groups

Example 024 : object visibility with setVisibility() and layers with startLayer()

Example 025 : object transparency with SetAlpha()

Example 026 : text clipping

Example 027 : 1D barcodes

Example 028 : multiple page formats

Example 029 : Set PDF viewer display preferences with setViewerPreferences()

Example 030 : colour gradients

Example 031 : pie chart graphic

Example 032 : EPS/AI vectorial image with ImageEPS()

Example 033 : mixed font types (TrueType Unicode, core, CID-0)

Example 034 : clipping masks

Example 035 : border styles with SetLineStyle()

Example 036 : PDF text annotations

Example 037 : spot colors

Example 038 : unembedded CID-0 CJK font

Example 039 : HTML text justification

Example 040 : booklet mode (double-sided pages)

Example 041 : file attachment

Example 042 : image with transparency (alpha channel)

Example 043 : disk caching

Example 044 : move, copy and delete pages

Example 045 : table of contents

Example 046 : text hyphenation

Example 047 : transactions and UNDO

Example 048 : HTML tables with header and rowspan

Example 049 : call TCPDF methods in HTML

Example 050 : 2D barcodes (QR-Code, Datamatrix ECC200 and PDF417)

Example 051 : image as a page background

Example 052 : digital signature certification

Example 053 : JavaScript functions

Example 054 : XHTML form

Example 055 : core fonts dump

Example 056 : crop marks and registration marks

Example 057 : vertical alignment and metrics on Cell()

Example 058 : SVG vectorial image with ImageSVG()

Example 059 : table of contents with HTML templates

Example 060 : advanced page settings

Example 061 : XHTML + CSS

Example 062 : XObject templates

Example 063 : text stretching and spacing (tracking/kerning)

Example 064 : no-write page regions

Example 065 : PDF/A-1b (ISO 19005-1:2005) document

Dapper简明教程 - 简书

mikel阅读(842)

来源: Dapper简明教程 – 简书

Dapper是一款轻量级的ORM框架,有关Dapper优缺点的文章网上一大堆,这里小编就不再赘述啦。下面直接进入正题:

使用前准备

  • 添加对Dapper的引用

在使用Dapper之前,我们要首先添加对Dapper的引用,这里小编使用NuGet来添加引用。因为小编使用的是MySQL数据库,所以也要在项目中添加对MySQL.Data的引用。

Dapper是一款ORM框架,用于数据表和实体模型间的映射,所以在使用前我们还需要创建数据表和实体模型。

  • 创建数据表

CREATE TABLE `t_schools` (
`Id`  int(11) NOT NULL AUTO_INCREMENT ,
`Name`  varchar(20)  NOT NULL ,
`Address`  varchar(50)  NOT NULL ,
PRIMARY KEY (`Id`)
)

CREATE TABLE `t_students` (
`Id`  int(11) NOT NULL AUTO_INCREMENT ,
`Name`  varchar(15) NOT NULL ,
`Number`  varchar(15)  NOT NULL ,
`SchoolId`  int(11) NOT NULL ,
`Gender`  enum('男','女','保密') NOT NULL ,
PRIMARY KEY (`Id`)
)
  • 创建模型

class School
{
    /*
      若属性名和数据库字段不一致(不区分大小写)则查询不出数据,如果使用EF则可以通过Column特性
      建立属性和数据表字段之间的映射关系,Dapper则不行
    */
    //[Column("Name")]
    public string Title { set; get; }
    public string Address { set; get; }
}

class Student
{
    public string Name { set; get; }
    public string Number { set; get; }
    public int SchoolId { set; get; }
}

Dapper的基本用法

const string _connectionString = "Database=Dapper;Data Source=127.0.0.1;User Id=root;Password=root;pooling=false;CharSet=utf8;port=3306;";
using (IDbConnection dbConnection = new MySqlConnection(_connectionString))
{
    dbConnection.Open();
    //通过匿名类型插入单条数据
    dbConnection.Execute("insert into t_schools(Name,Address) values(@Name,@Address)", new { Name = "西南大学", Address = "重庆市北碚区天生路2号" });
    //批量插入数据
    List<School> schools = new List<School>()
    {
      new School() {Address="China·BeiJing",Title="清华大学" },
      new School() {Address="杭州",Title="浙江大学" },
      new School() {Address="不知道,US?",Title="哈弗大学" }
    };
    //在执行参数化的SQL时,SQL中的参数(如@title可以和数据表中的字段不一致,但要和实体类型的属性Title相对应)
    dbConnection.Execute("insert into t_schools(Address,Name) values(@address,@title);", schools);
    //通过匿名类型批量插入数据
    dbConnection.Execute("insert into t_schools(Address,Name) values(@address,@name)", 
    new[] {
      new {Address="杨浦区四平路1239号",Name="同济大学"},
      new {Address="英国",Name="剑桥"},
      new {Address="美国·硅谷",Name="斯坦福大学"}
    });
}

使用Dapper进行查询操作

默认情况下Dapper会将查询到的整个数据集放到内存中,可以在Query方法中通过参数buffered来设置是否将查询结果存放到内存中

  • 查询结果映射到强类型

var schools = dbConnection.Query<School>("select * from t_schools where Name=@name", new { Name = "西南大学" });
foreach (var school in schools)
{
    Console.WriteLine(school.Address);
}
查询变量

有上图我们可以看到,因为School类中的Title属性在数据库中没有与之对应的字段,所以Title的值为null。

查询结果
  • 查询结果映射到匿名类型

在上面的查询中,我们将查询结果映射到了自定义的类型上。除了将查询结果映射到强类型之外,Dapper中还提供了匿名查询方式。

//查询结果result是匿名类型
var result = dbConnection.Query("select * from t_schools limit 0,3");
var resultList = result.AsList();
foreach (var l in resultList)
{
    Console.WriteLine(l.Name);
}
查询结果
  • in

var result = dbConnection.Query<Student>("select * from t_students where SchoolId in @schoolId", new { schoolId = new int[] { 2, 3 } });
foreach (var r in result)
{
    var ps = r.GetType().GetProperties();
    foreach (var p in ps)
    {
        Console.Write(p.Name + "=" + p.GetValue(r) + " ");
    }
    Console.WriteLine();
}
查询结果
  • between

var result = dbConnection.Query<School>("select Name,Address from t_schools where Id between @start and @end", new { start = 2, end = 4 });
foreach (var r in result)
{
    var ps = r.GetType().GetProperties();
    foreach (var p in ps)
    {
        Console.Write(p.Name + "=" + p.GetValue(r) + " ");
    }
    Console.WriteLine();
}
查询结果
  • join

使用join查询时需要用到Query方法中的splitOn参数,话说这个参数让小编纠结了很久才弄明白。关于splitOn参数的说明,可参考stackoverflow上的一篇文章Correct use of Multimapping in Dapper

var result = dbConnection.Query<Student, School, string>("select s.Name,sc.Address from t_students s,t_schools sc where s.SchoolId=sc.Id and binary sc.Address like '%BeiJing%'",
    (s, sc) =>
    {
        return s.Name + " " + sc.Address;
    }, 
    /*
    还有一点需要特别注意,泛型参数的顺序必须和SQL语句查询数据表的顺序一致,
    即Student对应t_students表的查询结果s.Name,否则Query方法的查询结果
    可能为null(这点也是困扰小编很久......)
    */
    splitOn: "Address"
    );
foreach (var r in result)
{
    Console.WriteLine(r);
}
查询结果

Dapper执行多条SQL语句

string sql = "select Address from t_schools;select SchoolId from t_students;select Name from t_students";
using (var multipleReader = dbConnection.QueryMultiple(sql))
{
    //一次执行N条SQL语句则最多只能调用N次Read方法,否则抛异常:the reader has been disposed.
    //Dapper读取查询结果数据的顺序必须要和查询语句中的查询顺序一致,否则可能读取不到数据
    var schoolList = multipleReader.Read<School>();
    foreach (var s in schoolList)
    {
        Console.Write(s.Address + " ");
    }
    Console.WriteLine();
    var studentSchools = multipleReader.Read<Student>();
    foreach (var s in studentSchools)
    {
        Console.Write(s.SchoolId + " ");
    }
    Console.WriteLine();
    var studentNames = multipleReader.Read<Student>();
    foreach (var s in studentNames)
    {
        Console.Write(s.Name + " ");
    }
}
查询结果

事务

使用Dapper执行事务倒是没有什么需要特别说明的。

using (IDbTransaction tran = dbConnection.BeginTransaction())
{
    try
    {
        dbConnection.Execute("delete from t_schools where Id=3", transaction: tran);
        throw new Exception();
        tran.Commit();
    }
    catch
    {
        tran.Rollback();
    }
}

存储过程

首先先创建一个存储过程

DROP PROCEDURE IF EXISTS `GetSchoolName`;
CREATE PROCEDURE `GetSchoolName`(in schoolId int,out scname varchar(20))
BEGIN
select `Name` into scname from t_schools where Id=schoolId;
select scname; 
END;

然后在程序中调用存储过程

//在程序中调用存储过程时,存储过程名要小写,传递的参数名要和存储过程中的参数名一致(不区分大小写)
//连接字符串中的数据库名也要小写,否则抛异常:在数据库***中找不到存储过程×××
var parameters = new DynamicParameters();
parameters.Add("@scname", dbType: DbType.String, direction: ParameterDirection.Output);
parameters.Add("schoolid", 6, direction: ParameterDirection.Input);
var result = dbConnection.Query("getschoolname", parameters, commandType: CommandType.StoredProcedure);
执行结果

从上图可以看出,返回值类型是Dapper中定义的DapperRow类型。

结语

  • Dapper是一个轻量级的ORM框架,它是通过扩展IDbConnection接口来实现一系列的功能的。相比EF、NHibernate,它的功能较为简单。
  • Dapper在执行查询语句时会缓存SQL语句的相关信息,这样就保证了Dapper拥有较高的性能(原文:Dapper caches information about every query it runs, this allow it to materialize objects quickly and process parameters quickly)。

以上内容是小编自己的一个学习总结,写出来一是作为自己的学习笔记,二是和广大网友分享。文中若有错误之处,还望各位读者能够指正。

参考文章:

Dapper
Dapper快速学习
Dapper中的一些复杂操作和inner join应该注意的坑
Dapper异常汇总
Correct use of Multimapping in Dapper

16人点赞

作者:雪飞鸿
链接:https://www.jianshu.com/p/b6cd7778567e
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 Dapper官方文档(一)【介绍】_风神修罗使的博客-CSDN博客

mikel阅读(318)

来源: (4条消息) Dapper官方文档(一)【介绍】_风神修罗使的博客-CSDN博客

什么是Dapper

Dapper是一个简单的.NET对象映射器,在速度方面具有”King of Micro ORM“的头衔,几乎与使用原始的ADO.NET数据读取器一样快。ORM是一个对象关系映射器,它负责数据库和编程语言之间的映射。

Dapper通过扩展IDbConnection提供一些有用的扩展方法去查询您的数据库。

Dapper是如何工作的

它可以分为三个步骤:

  • 创建一个IDbConnection接口对象;
  • 编写一个查询SQL来执行CRUD操作;
  • 将查询SQL作为Execute方法的参数传递。

安装

Dapper通过NuGet安装:https://www.nuget.org/packages/Dapper

PM> Install-Package Dapper
  • 1

要求

Dapper可以与任何数据库提供者一起工作,因为没有数据库特定的实现。

方法

Dapper会用以下几个方法扩展您的IDbConnection接口:

  • Execute
  • Query
  • QueryFirst
  • QueryFirstOrDefault
  • QuerySingle
  • QuerySingleOrDefault
  • QueryMultiple
string sqlInvoices = "SELECT * FROM Invoice;";
string sqlInvoice = "SELECT * FROM Invoice WHERE InvoiceID = @InvoiceID;";
string sp = "EXEC Invoice_Insert";

using (var connection = My.ConnectionFactory())
{
        // 执行普通SQL
    var invoices = connection.Query<Invoice>(sqlInvoices).ToList();
    // 执行带参数的SQL
    var invoice = connection.QueryFirstOrDefault(sqlInvoice, new {InvoiceID = 1});
    // 执行存储过程 
    var affectedRows = connection.Execute(sp, new { Param1 = "Single_Insert_1" }, commandType: CommandType.StoredProcedure);
}

参数

执行和查询方法可以用以下几种不同的方式使用参数:

  • 匿名类型
  • 动态类型
  • 列表类型
  • 字符串类型
// Anonymous
var affectedRows = connection.Execute(sql,
                    new {Kind = InvoiceKind.WebInvoice, Code = "Single_Insert_1"},
                    commandType: CommandType.StoredProcedure);

// Dynamic
DynamicParameters parameter = new DynamicParameters();

parameter.Add("@Kind", InvoiceKind.WebInvoice, DbType.Int32, ParameterDirection.Input);
parameter.Add("@Code", "Many_Insert_0", DbType.String, ParameterDirection.Input);
parameter.Add("@RowCount", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);

connection.Execute(sql,
    new {Kind = InvoiceKind.WebInvoice, Code = "Single_Insert_1"},
    commandType: CommandType.StoredProcedure);

// List
connection.Query<Invoice>(sql, new {Kind = new[] {InvoiceKind.StoreInvoice, InvoiceKind.WebInvoice}}).ToList();

// String
connection.Query<Invoice>(sql, new {Code = new DbString {Value = "Invoice_1", IsFixedLength = false, Length = 9, IsAnsi = true}}).ToList();

结果

查询方法返回的结果可以映射到以下几种类型:

  • 匿名类型
  • 强类型
  • 多映射
  • 多结果
  • 多类型
string sql = "SELECT * FROM Invoice;";

using (var connection = My.ConnectionFactory())
{
    connection.Open();

    var anonymousList = connection.Query(sql).ToList();
    var invoices = connection.Query<Invoice>(sql).ToList();
}

工具

  • 异步
  • 缓冲
  • 事务
  • 存储过程
// Async
connection.QueryAsync<Invoice>(sql)

// Buffered
connection.Query<Invoice>(sql, buffered: false)

// Transaction
using (var transaction = connection.BeginTransaction())
{
    var affectedRows = connection.Execute(sql,
        new {Kind = InvoiceKind.WebInvoice, Code = "Single_Insert_1"},
        commandType: CommandType.StoredProcedure,
        transaction: transaction);

    transaction.Commit();
}

// Stored Procedure
var affectedRows = connection.Execute(sql,
    new {Kind = InvoiceKind.WebInvoice, Code = "Single_Insert_1"},
    commandType: CommandType.StoredProcedure);

钉钉 基于vue开发h5微应用,免登录获取用户信息_钉钉微应用免登vue实现_sheen~向阳的博客-CSDN博客

mikel阅读(821)

来源: (1条消息) 钉钉 基于vue开发h5微应用,免登录获取用户信息_钉钉微应用免登vue实现_sheen~向阳的博客-CSDN博客

 

需求
在钉钉里面内嵌一个自定义的h5应用,点击微应用获取到用户信息,实现微应用免登录访问

准备工作,查看钉钉开放平台文档开发H5微应用
免登流程 文档位置
钉钉API总览
vue + 钉钉

先安装 npm install dingtalk-jsapi –save
vue根据钉钉企业id获取微应用免登授权码code

import * as dd from ‘dingtalk-jsapi’

/* eslint-disable */
export function GetCode(callback) {
const corpId = ‘xxxxxxxxx’ //钉钉企业id
if (dd.env.platform !== ‘notInDingTalk’) {
dd.ready(() => {
dd.runtime.permission.requestAuthCode({
corpId: corpId,
onSuccess: (info) => {
callback(info.code)
},
onFail: (err) => {
alert(‘fail’)
alert(JSON.stringify(err))
}
})
})
}
}

使用
import { GetCode } from ‘@/views/dingTalk/Dingding.js’

GetCode((code: any) => {
console.log(code, 123)
})

获取用户信息【建议用户信息保存在前端缓存中(dd.setStorage)或者cookie中,避免每次进入应用都调用钉钉接口进行免登。】

async loadData() {
const userInfos = this.$cookie.get(‘userinfos’)
if (!userInfos) {
await GetCode((code: any) => {
this.serviceType = code
})
// 获取企业内部应用的access_token,
let access_token = await axios
.get(
‘/gettoken?appkey=xxxx&appsecret=xxxx’
)
.then((response: { data: any }) => {
return response.data.access_token
})
.catch((err: Error) => {})
// 通过免登码和access_token获取用户信息
let userId = await axios
.get(
`/user/getuserinfo?access_token=${access_token}&code=${this.serviceType}`
)
.then((res: { data: any }) => {
return res.data.userid
})
.catch((err: Error) => {})
// 通过userId和access_token获取用户详情
axios
.get(`/user/get?access_token=${access_token}&userid=${userId}`)
.then((res: { data: any }) => {
this.message = JSON.stringify(res.data)
this.$cookie.set(‘userinfos’, JSON.stringify(res.data), {
expires: 7
})
})
.catch((err: Error) => {})
} else {
this.message = ‘获取缓存在cookie中的数据’ + userInfos
}
}

vue.config.js 配置

devServer: {
proxy: {
‘/’: {
target: ‘https://oapi.dingtalk.com/’,
autoRewrite: true,
cookieDomainRewrite: {
‘*’: ”
},
pathRewrite: {
[‘^’ + ‘/’]: ”
}
}
}
}
————————————————
版权声明:本文为CSDN博主「sheen~向阳」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_40079913/article/details/120198601

 

Vue 响应式原理模拟以及最小版本的 Vue的模拟 - 编程大世界

mikel阅读(470)

来源: Vue 响应式原理模拟以及最小版本的 Vue的模拟 – 编程大世界

在模拟最小的vue之前,先复习一下,发布订阅模式和观察者模式

对两种模式有了了解之后,对Vue2.0和Vue3.0的数据响应式核心原理

1.Vue2.0和Vue3.0的数据响应式核心原理

(1).  Vue2.0是采用Object.defineProperty的方式,对数据进行get,set方法设置的, 具体可以详见Object.defineProperty的介绍

浏览器兼容 IE8 以上(不兼容 IE8)
<script>
    // 模拟 Vue 中的 data 选项
    let data = {
      msg: 'hello'
    }

    // 模拟 Vue 的实例
    let vm = {}

    // 数据劫持:当访问或者设置 vm 中的成员的时候,做一些干预操作
    Object.defineProperty(vm, 'msg', {
      // 可枚举(可遍历)
      enumerable: true,
      // 可配置(可以使用 delete 删除,可以通过 defineProperty 重新定义)
      configurable: true,
      // 当获取值的时候执行
      get () {
        console.log('get: ', data.msg)
        return data.msg
      },
      // 当设置值的时候执行
      set (newValue) {
        console.log('set: ', newValue)
        if (newValue === data.msg) {
          return
        }
        data.msg = newValue
        // 数据更改,更新 DOM 的值
        document.querySelector('#app').textContent = data.msg
      }
    })

    // 测试
    vm.msg = 'Hello World'
    console.log(vm.msg)
  </script>

如果,vm里的属性是对象如何处理,可以,对其遍历,在进行Object.defineProperty

<script>
    // 模拟 Vue 中的 data 选项
    let data = {
      msg: 'hello',
      count: 10,
      person: {name: 'zhangsan'}
    }

    // 模拟 Vue 的实例
    let vm = {}

    proxyData(data)

    function proxyData(data) {
      // 遍历 data 对象的所有属性
      Object.keys(data).forEach(key => {
        // 把 data 中的属性,转换成 vm 的 setter/setter
        Object.defineProperty(vm, key, {
          enumerable: true,
          configurable: true,
          get () {
            console.log('get: ', key, data[key])
            return data[key]
          },
          set (newValue) {
            console.log('set: ', key, newValue)
            if (newValue === data[key]) {
              return
            }
            data[key] = newValue
            // 数据更改,更新 DOM 的值
            document.querySelector('#app').textContent = data[key]
          }
        })
      })
    }

    // 测试
    vm.msg = 'Hello World'
    console.log(vm.msg)
  </script>

(2). Vue3.x是采用proxy代理的方式实现, 直接监听对象,而非属性。ES 6中新增,IE 不支持,性能由浏览器优化,具体可以详见MDN – Proxy

<script>
    // 模拟 Vue 中的 data 选项
    let data = {
      msg: 'hello',
      count: 0
    }

    // 模拟 Vue 实例
    let vm = new Proxy(data, {
      // 执行代理行为的函数
      // 当访问 vm 的成员会执行
      get (target, key) {
        console.log('get, key: ', key, target[key])
        return target[key]
      },
      // 当设置 vm 的成员会执行
      set (target, key, newValue) {
        console.log('set, key: ', key, newValue)
        if (target[key] === newValue) {
          return
        }
        target[key] = newValue
        document.querySelector('#app').textContent = target[key]
      }
    })

    // 测试
    vm.msg = 'Hello World'
    console.log(vm.msg)
  </script>

2.Vue 响应式原理模拟

看图,整体分析

 Vue
  • 把 data 中的成员注入到 Vue 实例,并且把 data 中的成员转成 getter/setter
Observer
  • 能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知 Dep
Compiler
  • 解析每个元素中的指令/插值表达式,并替换成相应的数据
Dep
  • 添加观察者(watcher),当数据变化通知所有观察者
Watcher
  • 数据变化更新视图

 (1) Vue

功能
  • 负责接收初始化的参数(选项)
  • 负责把 data 中的属性注入到 Vue 实例,转换成 getter/setter
  • 负责调用 observer 监听 data 中所有属性的变化
  • 负责调用 compiler 解析指令/插值表达式
class Vue {
    constructor (options) {
        //1.通过属性保存选项的数据
        this.$options = options || {}
        this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el
        this.$data = options.data || {}
        //2.把data中的成员转换成getter和setter方法,注入到vue实例中
        this._proxyData(this.$data)
        //3.调用observer对象,监听数据变化
        new Observer(this.$data)
        //4.调用compiler对象, 解析指令和差值表达式
        new Compiler(this)
    }

    _proxyData (data) {
        //遍历data中的所有属性
        Object.keys(data).forEach( key => {
            //把data的属性注入到vue实例中
            Object.defineProperty(this, key, {
                enumerable: true,
                configurable: true,
                get () {
                    return data[key]
                },
                set (newValue) {
                    if (newValue === data[key]) {
                        return 
                    }
                    data[key] = newValue
                }

            })
        })

    }

}

(2)Observer

功能
  • 负责把 data 选项中的属性转换成响应式数据
  • data 中的某个属性也是对象,把该属性转换成响应式数据
  • 数据变化发送通知
class Observer {
    constructor (data) {
        this.walk(data)
    }
    //1.
    walk (data) {
        //1.判断data是不是对象
        if (!data || typeof data !== 'object') {
            return
        }
        //遍历data对象里的所有属性
        Object.keys(data).forEach( key => {
            this.definedReactive(data, key, data[key])
        })
    }

    definedReactive (obj, key, value) {
        let that = this
        //负责收集依赖(观察者), 并发送通知
        let dep = new Dep()

        this.walk(value)//如果data里的属性是对象,对象里面的属性也得是响应式的,所以得判断一下
        
        Object.defineProperty (obj, key, {
            enumerable: true,
            configurable: true,
            get () {
                //收集依赖
                Dep.target && dep.addSubs(Dep.target)
                return value
                // return obj[key]//这么写会引起堆栈溢出
            },
            set (newValue) {
                if (newValue === value) {
                    return 
                }
                
                value = newValue
                that.walk(newValue)//如果赋值为对象,对象里面的属性得是响应式数据

                //数据变换 ,发送通知给watcher的update ,在渲染视图里的数据
                dep.notify()
            }    
                
        }) 
    }

}

(3).Compiler

功能
  • 负责编译模板,解析指令/插值表达式
  • 负责页面的首次渲染
  • 当数据变化后重新渲染视图
class Compiler {

    constructor (vm) {//传个vue实例
        this.el = vm.$el
        this.vm = vm
        this.compile(this.el)
    }

    //编译模板, 处理文本节点和元素节点
    compile (el) {

        let childNodes = el.childNodes //获取子节点  伪数组
        console.dir(el.childNodes)
        Array.from(childNodes).forEach( node => {
            if (this.isTextNode(node)) { //是文本节点
                this.compileText(node)
            } else if (this.isElementNode(node)) {//是元素节点
                this.compileElement(node)
            }

            if (node.childNodes && node.childNodes.length) { //子节点里面还有节点,递归遍历获取
                this.compile(node)
            }
        })
    }

    //编译元素节点, 处理指令
    compileElement (node) {
        //console.log(node.attributes)

        Array.from(node.attributes).forEach( attr => {

            //判断是不是指令
            let attrName = attr.name //<div v-text="msg"></div> 里的v-text
            if (this.isDirective(attrName)) {
                //v-text --> text
                attrName = attrName.substr(2)
                let key = attr.value   //<div v-text="msg"></div> 里的msg
                this.update(node , key, attrName) 
            }
        })
    }

    update (node, key, attrName) {
        let updateFn = this[attrName + 'Updater']
        updateFn && updateFn.call(this, node, this.vm[key], key)//call方法改变this指向
    }
    //处理v-text 命令
    textUpdater (node, value, key) {
        node.textContent = value
        new Watcher(this.vm, key, (newValue) => {
            node.textContent = newValue
        })
    }
    //v-model
    modelUpdater (node, value, key) {
        node.value = value
        new Watcher(this.vm, key, (newValue) => {
            node.value = newValue
        })

        //双向绑定,视图改变,数据也会更新
        node.addEventListener('input', () => {
            this.vm[key] = node.value
        })
    }

    //编译文本节点,处理差值表达式
    compileText (node) {
        //console.dir(node)
        // {{  msg   }}
        let reg = /\{\{(.+?)\}\}/
        let value = node.textContent //里面的内容, 也可以是nodeValue
        if (reg.test(value)) {
            let key = RegExp.$1.trim()  //匹配到的第一个
            node.textContent = value.replace(reg, this.vm[key])

            //创建watcher对象, 当数据改变更新视图
            new Watcher(this.vm, key, (newValue) => {
                node.textContent = newValue
            })
        }
    }

    //判断元素属性是否是指令
    isDirective (attrName) {
        return attrName.startsWith('v-')
    }

    //判断节点是否是文本节点
    isTextNode (node) {
        return node.nodeType === 3
    }

    //判断节点是否是元素节点
    isElementNode (node) {
        return node.nodeType === 1
    }
}

(4).Dep(Dependency)

功能

  • 收集依赖,添加观察者(watcher)
  • 通知所有观察者
class Dep {

    constructor () {
        //收集观察者
        this.subs = []
    }

    //添加观察者
    addSubs (watcher) {
        if (watcher && watcher.update) {
            this.subs.push(watcher)
        }
    }
    //数据变换,就调watcher的update方法
    notify () {
        this.subs.forEach(watcher => {
            watcher.update()
        });
    }
}

(5).Watcher

功能

  • 当数据变化触发依赖, dep 通知所有的 Watcher 实例更新视图
  • 自身实例化的时候往 dep 对象中添加自己
class Watcher {
    constructor (vm, key, callback) {
        this.vm = vm
        //data中的属性名
        this.key = key
        this.callback = callback
        //将watcher对象记录在Dep的静态属性target
        Dep.target = this
        //触发get方法,触发get里的addsubs方法,添加watcher
        this.oldValue = vm[key]
        Dep.target = null
    }

    //当数据变化的时候,更新视图
    update () {
        let newValue = this.vm[this.key]
        if (this.oldValue === newValue) {
            return
        }
        this.callback(newValue)
    }
}

总结:

Vue

  • 记录传入的选项,设置 $data/$el
  • 把 data 的成员注入到 Vue 实例
  • 负责调用 Observer 实现数据响应式处理(数据劫持)
  • 负责调用 Compiler 编译指令/插值表达式等
Observer
  • 数据劫持
  • 负责把 data 中的成员转换成 getter/setter
  • 负责把多层属性转换成 getter/setter
  • 如果给属性赋值为新对象,把新对象的成员设置为 getter/setter
  • 添加 Dep 和 Watcher 的依赖关系
  • 数据变化发送通知
Compiler
  • 负责编译模板,解析指令/插值表达式
  • 负责页面的首次渲染过程
  • 当数据变化后重新渲染
Dep
  • 收集依赖,添加订阅者(watcher)
  • 通知所有订阅者
Watcher
  • 自身实例化的时候往dep对象中添加自己
  • 当数据变化dep通知所有的 Watcher 实例更新视图

Windows 取证之EVTX日志 - 合天网安实验室 - 博客园

mikel阅读(600)

来源: Windows 取证之EVTX日志 – 合天网安实验室 – 博客园

0x0、概述

evtx文件是微软从 Windows NT 6.0(Windows Vista 和 Server 2008) 开始采用的一种全新的日志文件格式。在此之前的格式是 evt 。evtxWindows事件查看器创建,包含Windows记录的事件列表,以专有的二进制XML格式保存。

0x1、EVTX文件结构

evtx文件主要由三部分组成:

  • file header (文件头)

  • chunks (数据块)

  • trailing empty values (尾部填充空值)

File Header(文件头):

文件头长度为4KB(4096bytes),其结构如下:

 

偏移 长度(Bytes) 描述
0x00 8 “ElfFile\x00” 标志位/签名
0x08 8 第一个区块编号(存在时间最久的区块编号)
0x10 8 当前区块编号(块的编号从0开始)
0x18 8 下一条事件记录的ID
0x20 4 128 文件头有效部分的大小
0x24 2 1 次要版本
0x26 2 3 主要版本
0x28 2 4096 文件头的大小
0x2A 2 区块的数量
0x2C 76 未知 (空值)
0x78 4 文件标志
0x7C 4 文件头前 120 bytes 的CRC32校验和
0x80 3968 未知 (空值)

 

我们可以使用Hex编辑器打开一个evtx文件查看一下:

image-20210720163636907

Chunk(块):

每个块的大小是 65536 bytes(64KB),主要由三部分组成:

  • chunk header 块头

  • array of event records 事件记录组

  • unused space 未使用的空间

chunk头长度为512bytes,其结构如下:

 

偏移 长度(Bytes) 描述
0x00 8 “ElfChnk\x00” 标志位/签名
0x08 8 基于日志编号的第一条日志记录的ID
0x10 8 基于日志编号的最后一条日志记录的ID
0x18 8 基于文件编号的第一条日志记录的ID
0x20 8 基于文件编号的最后一条日志记录的ID
0x28 4 128 chunk头大小
0x2C 4 最后一条日志记录的偏移量(相对于块头的起始偏移量)
0x30 4 下一条日志记录的偏移量(相对于块头的起始偏移量)
0x34 4 事件记录数据的 CRC32 校验和
0x38 64 Unknown (空值)
0x78 4 Unknown (flags?)
0x7C 4 块头CRC32校验和(块头前120个字节和128至512字节的数据的CRC32校验和)

 

image-20210721123805851

Event record(事件记录):

事件记录的长度非固定长度,其结构如下:

 

偏移 长度(Bytes) 描述
0x00 4 “\x2a\x2a\x00\x00” 标志位/签名
0x04 4 事件记录的长度
0x08 8 记录ID
0x10 8 日志记录的写入时间(FILETIME)
0x18 不确定 基于二进制XML编码的信息
不确定 4 记录长度(副本)

 

image-20210721131110932

image-20210721131135650

由上面的信息,可知evtx日志文件包含一个4KB的文件头加后面一定数量的64KB大小的块,一个块中记录一定数量(大约100条)的事件记录。每个块是独立的,不受其他块影响。不会出现一条事件记录的数据存在于两个块中。每条记录包含一个基于二进制XML编码的信息。每条事件记录包含其创建时间与事件 ID(可以用于确定事件的种类),因此可以反映某个特定的时间发生的特定的操作,取证人员可以根据日志文件来发现犯罪的过程。

evtx日志文件大概的结构如下所示:

image-20210721132346354

在windows事件查看器中查看:

image-20210721140401309

0x2、EVTX文件的存储

Windows事件日志文件保存在%SystemRoot%\System32\Winevt\Logs路径中。

image-20210721141141511

常见日志文件主要有三个,分别是:System.evtx 、Application.evtx 和Security.evtx。分别是系统日志、应用程序日志和安全日志。

  • System.evtx

    记录操作系统自身组件产生的日志事件,比如驱动、系统组件和应用软件的崩溃以及数据丢失错误等等。

  • Application.evtx

    记录应用程序或系统程序运行方面的日志事件,比如数据库程序可以在应用程序日志中记录文件错误,应用的崩溃记录等。

  • Security.evtx

    记录系统的安全审计日志事件,比如登录事件、对象访问、进程追踪、特权调用、帐号管理、策略变更等。Security.evtx也是取证中最常用到的。

默认情况下,当一个evtx文件的记录满了,日志服务会覆盖最开始的记录,从头开始写入新的记录。也就是相当于一个循环记录的缓存文件。

image-20210721142106536

0x3、Evtx日志分析

Windows 用 Event ID来标识事件的不同含义,拿Security日志来说,一些常见的Event ID 如下:

 

事件ID 描述
4608 Windows 启动
4609 Windows 关机
4616 系统时间发生更改
4624 用户成功登录到计算机
4625 登录失败。使用未知用户名或密码错误的已知用户名尝试登录。
4634 用户注销完成
4647 用户启动了注销过程
4648 用户在以其他用户身份登录时,使用显式凭据成功登录到计算机
4703 令牌权限调整
4704 分配了用户权限
4720 已创建用户账户
4725 账户被禁用
4768 请求Kerberos身份验证票证(TGT)
4769 请求Kerberos服务票证
4770 已续订Kerberos服务票证
4779 用户在未注销的情况下断开了终端服务器会话

 

1、通过Windows事件查看器分析日志

通过Windows事件查看器可以查看当前主机的事件日志,也可以打开保存的 evtx文件。

image-20210721160559304

可以通过点击、筛选、查找等多种方式查看事件日志

image-20210721160753865

筛选器提供了丰富的筛选方式:

image-20210721161013112

2、通过工具分析Evtx

Log Parser

Log Parser(是微软公司自己开发的日志分析工具,它功能强大,使用简单,可以分析基于文本的日志文件、XML 文件、CSV(逗号分隔符)文件,以及操作系统的事件日志、注册表、文件系统、Active Directory。它使用类似 SQL 语句一样查询分析这些数据,还可以把分析结果以图表的形式展现出来。

Log Parser下载地址:https://www.microsoft.com/en-us/download/details.aspx?id=24659

使用方法:

logparser -i:输入文件的格式 -o:输出文件的格式 "查询语句 和文件路径"

例子:

查询登录成功的事件:

LogParser.exe -i:EVT -o:DATAGRID  "SELECT *  FROM E:\Security.evtx where EventID=4624"

image-20210721161849128

还有其他的语法,具体可以查看其帮助信息

>LogParser.exe

Microsoft (R) Log Parser Version 2.2.10
Copyright (C) 2004 Microsoft Corporation. All rights reserved.

Usage:   LogParser [-i:<input_format>] [-o:<output_format>] <SQL query> |
                  file:<query_filename>[?param1=value1+...]
                  [<input_format_options>] [<output_format_options>]
                  [-q[:ON|OFF]] [-e:<max_errors>] [-iw[:ON|OFF]]
                  [-stats[:ON|OFF]] [-saveDefaults] [-queryInfo]

        LogParser -c -i:<input_format> -o:<output_format> <from_entity>
                  <into_entity> [<where_clause>] [<input_format_options>]
                  [<output_format_options>] [-multiSite[:ON|OFF]]
                  [-q[:ON|OFF]] [-e:<max_errors>] [-iw[:ON|OFF]]
                  [-stats[:ON|OFF]] [-queryInfo]

-i:<input_format>   :  one of IISW3C, NCSA, IIS, IISODBC, BIN, IISMSID,
                       HTTPERR, URLSCAN, CSV, TSV, W3C, XML, EVT, ETW,
                       NETMON, REG, ADS, TEXTLINE, TEXTWORD, FS, COM (if
                       omitted, will guess from the FROM clause)
-o:<output_format>  :  one of CSV, TSV, XML, DATAGRID, CHART, SYSLOG,
                       NEUROVIEW, NAT, W3C, IIS, SQL, TPL, NULL (if omitted,
                       will guess from the INTO clause)
-q[:ON|OFF]         :  quiet mode; default is OFF
-e:<max_errors>     :  max # of parse errors before aborting; default is -1
                       (ignore all)
-iw[:ON|OFF]        :  ignore warnings; default is OFF
-stats[:ON|OFF]     :  display statistics after executing query; default is
                       ON
-c                  :  use built-in conversion query
-multiSite[:ON|OFF] :  send BIN conversion output to multiple files
                       depending on the SiteID value; default is OFF
-saveDefaults       :  save specified options as default values
-restoreDefaults    :  restore factory defaults
-queryInfo          :  display query processing information (does not
                       execute the query)


Examples:
LogParser "SELECT date, REVERSEDNS(c-ip) AS Client, COUNT(*) FROM file.log
           WHERE sc-status<>200 GROUP BY date, Client" -e:10
LogParser file:myQuery.sql?myInput=C:\temp\ex*.log+myOutput=results.csv
LogParser -c -i:BIN -o:W3C file1.log file2.log "ComputerName IS NOT NULL"

Help:
-h GRAMMAR                  : SQL Language Grammar
-h FUNCTIONS [ <function> ] : Functions Syntax
-h EXAMPLES                 : Example queries and commands
-h -i:<input_format>        : Help on <input_format>
-h -o:<output_format>       : Help on <output_format>
-h -c                       : Conversion help

Log Parser Studio

logparser的GUI版本。

下载地址:https://techcommunity.microsoft.com/t5/exchange-team-blog/log-parser-studio-2-0-is-now-available/ba-p/593266

其界面如下:

image

Event Log Explorer

Event Log Explorer 是一个非常好用的Windows 日志分析工具,下载地址:https://eventlogxp.com/

image-20210721163315625

LogParser Lizard

LogParser Lizard 是一个功能丰富的Windows 日志分析软件,可以通过类似SQL查询语句对日志筛选查询进行分析。

下载地址:https://lizard-labs.com/log_parser_lizard.aspx

image-20210721163556793

image-20210721163647101

Evtx Explorer/EvtxECmd

具有标准化CSV、XML和json输出的事件日志(Evtx)解析器!

下载地址:https://ericzimmerman.github.io/#!index.md

使用方法:

EvtxECmd.exe -f 日志文件 --xml 输出路径

image-20210721165111773

解析的xml文件结构如下:

image-20210721165205013

0x4、Evtx取证实战

题目来源:Cynet应急响应挑战赛

描述:GOT Ltd 的人力资源主管King-Slayer认为他的电脑上有可疑活动。

2020 年 2 月 8 日,15:00 左右,他发现桌面上出现了一个带有 kiwi标志的文件。据他描述,该文件首次出现在他的桌面后不久就突然消失了。那天晚些时候,他开始收到消息告诉他需要重新激活 Windows Defender。他激活了 Windows Defender,几个小时后又收到了同样的消息。

他决定将这件事告诉他在 IT 部门的朋友——ChrisChris立即将此事报告给了 GOT 的网络安全部门。

该公司的 CISO 立即打电话求助我们,GOT有限公司总部设在瑞士,CISO 向我们发送了来自 King-Slayer的 PC 和域控制器的所有事件日志文件。他希望我们查出异常:

提示:

  • 用户帐户 (KingSlayer) 是他电脑上的本地管理员。

  • 域名 -> GOT.Com

  • DC 服务器名称 -> WIN-IL7M7CC6UVU

  • Jaime(King Slayer)的主机名->DESKTOP-HUB666E(172.16.44.135)

提交攻击者使用的域用户帐户(King-Slayer除外)以及他使用此用户帐户访问的主机的IP地址。

我们拿到的文件包括DC服务的日志和主机日志文件:

image-20210721171750067

给出的文件还有一个提示就是PassTheHash ,表明攻击者使用了该技术。

传递哈希是一种黑客技术,它允许攻击者使用用户密码的基础NTLM或LanMan哈希对远程服务器或服务进行身份验证,而不是像通常情况下那样要求使用关联的明文密码。它取代了仅窃取哈希值并使用该哈希值进行身份验证而窃取明文密码的需要。–via 维基百科

通过日志交叉比对和筛选查找,我们确定了在2020-2-9 21:59左右,有异常登录行为

注意:Windows EVTX 的FILETIME 是 UTC时间,注意转化为瑞士当地时间。

image-20210721173442458

image-20210721173517788

我们发现用户Daenerys在2020年2月9日21:59 (当地时间15:59)通过SMB协议登录到WIN-IL7M7CC6UVU(域控制器),而且使用了PSExec.exe 利用Deanerys用户登录了域控服务器。攻击者可能使用了Mimikatz拿到了Daenerys用户的哈希,然后用于横向移动渗透到DC。