mirror of
https://gitee.com/layui/layui.git
synced 2025-06-28 13:34:27 +08:00
feat: 重构 laytpl,增强对更多复杂模板结构的解析能力 (#2577)
* feat: 重构 laytpl,增强对更多复杂模板结构的解析能力 * Squashed commit of the following: commit6884f80378
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 14:45:58 2025 +0800 release: v2.10.1 commit8d643ad6dc
Merge:5521e48c
213fe5a2
Author: corededitor <107152508+corededitor@users.noreply.github.com> Date: Wed Mar 19 14:24:50 2025 +0800 feat: Merge pull request #2566 from layui/feat/component feat: 优化 component, tabs 若干功能 commit213fe5a209
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 14:15:39 2025 +0800 docs: 添加 component 文档中实验性选项标记 commit5521e48c05
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 14:10:46 2025 +0800 fix: 修复 `body` 初始 `line-height` 无效的问题 (#2569) commit8c7cf0f606
Author: 青崖 <33601030+bxjt123@users.noreply.github.com> Date: Wed Mar 19 14:10:08 2025 +0800 优化 checkbox 标签风格选中且禁用时的显示 (#2563) commit23b21254d4
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 14:04:44 2025 +0800 docs: Squashed commit of the following: commit95a0503f41
Merge:e6eb86ba
87ba4c43
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 14:03:13 2025 +0800 Merge branch 'main' into 2.x commite6eb86bacb
Author: morning-star <26325820+Sight-wcg@users.noreply.github.com> Date: Wed Mar 19 14:02:05 2025 +0800 docs(slider): 修正错别字 (#2578) commit46f7a9783e
Merge:df1fc4f4
c204590a
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Fri Mar 14 19:07:50 2025 +0800 Merge branch 'main' into 2.x commitdf1fc4f419
Author: itletu <itletu@163.com> Date: Mon Mar 10 13:54:06 2025 +0800 docs: 更正 class 公共类文档错误 (#2544) | layui-border-box | 设置元素及其所有子元素均为 `box-sizing: border-box` 模型的容器 | commit87ba4c4394
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 13:41:33 2025 +0800 docs(version): 优化 2.9.x 锚点 commita0f533f0fd
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 13:37:29 2025 +0800 docs: 修复 tabs 文档示例异常问题 commit0f0584e2ed
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 13:35:00 2025 +0800 docs: 修复 tabs 文档中自定义事件示例重新点击 Preview 失效的问题 commit172957d243
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Wed Mar 19 13:29:09 2025 +0800 docs: 优化文档中的用词细节 (#2571) commit094be4ddcc
Author: letianpailove <113023596+letianpailove@users.noreply.github.com> Date: Wed Mar 19 13:28:46 2025 +0800 fix: 更正 class 公共类文档错误 (#2562) commit53ded26cb9
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Sun Mar 16 00:15:25 2025 +0800 fix: 优化 tabs 重载时未按照传入的 closable 正确渲染可关闭状态 commitbd892bf87e
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Sun Mar 16 00:15:17 2025 +0800 feat(component): 新增 cache 原型方法,用于元素缓存操作 commit6ccc5a453d
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Sun Mar 16 00:14:18 2025 +0800 fix(component): 优化元素 lay-options 属性上的配置在重载时的优先级 commit79b0a56f50
Author: 贤心 <3277200+sentsim@users.noreply.github.com> Date: Sun Mar 16 00:13:55 2025 +0800 fix(component): 修复 reload 时传入的选项未正确合并的问题 * refactor(laytpl): 优化代码细节 * docs: 重写 laytpl 模块文档
This commit is contained in:
parent
b4fbb28a04
commit
169f6ff9b8
2
dist/css/layui.css
vendored
2
dist/css/layui.css
vendored
File diff suppressed because one or more lines are too long
2
dist/css/layui.css.map
vendored
2
dist/css/layui.css.map
vendored
File diff suppressed because one or more lines are too long
2
dist/layui.js
vendored
2
dist/layui.js
vendored
File diff suppressed because one or more lines are too long
2
dist/layui.js.map
vendored
2
dist/layui.js.map
vendored
File diff suppressed because one or more lines are too long
@ -25,7 +25,7 @@ toc: true
|
||||
|
||||
`MOD_NAME.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
<h3 id="options" lay-toc="{level: 2, hot: true}">属性</h3>
|
||||
|
||||
|
@ -48,7 +48,7 @@ layui.use(function(){
|
||||
- 在元素外层设置 `class="layui-carousel"` 来定义一个轮播容器
|
||||
- 在元素内层设置属性 `carousel-item` 用来定义条目容器
|
||||
|
||||
<h3 id="demo-config" lay-toc="{level: 2, hot: true}">属性配置预览</h3>
|
||||
<h3 id="demo-config" lay-toc="{level: 2, hot: true}">可选项预览</h3>
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, codeStyle: 'height: 515px;', layout: ['preview', 'code'], tools: ['full'], done: function(obj){
|
||||
obj.render();
|
||||
@ -215,4 +215,4 @@ layui.use(function(){
|
||||
});
|
||||
</script>
|
||||
</textarea>
|
||||
</pre>
|
||||
</pre>
|
||||
|
@ -33,7 +33,7 @@ div[carousel-item]>*:nth-child(2n+1){background-color: #16baaa;}
|
||||
|
||||
`carousel.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法返回一个实例对象,包含操作当前实例的相关方法成员。
|
||||
|
||||
@ -46,7 +46,7 @@ console.log(inst); // 得到当前实例对象
|
||||
|
||||
`inst.reload(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
通过渲染返回的实例对象,可获得重载方法,用于实现对实例的属性重载。
|
||||
|
||||
|
@ -12,7 +12,7 @@ toc: true
|
||||
|
||||
`var codeInst = layui.code(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)。
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)。
|
||||
|
||||
其中 `codeInst` <sup>2.8.17+</sup> 即实例返回的对象,包含对当前实例进行重载等方法成员,如:
|
||||
|
||||
|
@ -27,7 +27,7 @@ toc: true
|
||||
|
||||
`colorpicker.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
<br>注 <sup>2.7+</sup> : 除 `elem` 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中。
|
||||
|
||||
```
|
||||
|
@ -53,7 +53,7 @@ CONST: {
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>isRenderWithoutElem</td>
|
||||
<td>isRenderWithoutElem <br><sup>实验性</sup></td>
|
||||
<td>
|
||||
|
||||
渲染是否无需指定目标元素。即无需设置 `elem` 选项,一般用于渲染即显示的组件类型。
|
||||
@ -67,7 +67,7 @@ CONST: {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>isRenderOnEvent</td>
|
||||
<td>isRenderOnEvent <br><sup>实验性</sup></td>
|
||||
<td>
|
||||
|
||||
渲染是否由事件触发。如 `dropdown` 这类通过点击触发的组件,那么应该设置为 `true`;而诸如 `tabs` 这类初始即展示的组件,则应该设置为 `false`。*推荐根据组件类型始终显式设置对应值*。
|
||||
@ -81,7 +81,7 @@ CONST: {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>isDeepReload</td>
|
||||
<td>isDeepReload <br><sup>实验性</sup></td>
|
||||
<td>
|
||||
|
||||
组件重载时是否允许深度重载,即对重载时选项进行深度合并。
|
||||
|
@ -17,7 +17,7 @@ toc: true
|
||||
|
||||
`layui.component(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法返回一个对象,包含用于组件对外的基础接口,如:组件渲染、重载、事件操作,及构造函数等等。用法示例:
|
||||
|
||||
@ -45,7 +45,7 @@ layui.define('component', function(exports) {
|
||||
});
|
||||
```
|
||||
|
||||
<h3 id="options" lay-toc="{level: 2}">属性配置</h3>
|
||||
<h3 id="options" lay-toc="{level: 2}">属性选项</h3>
|
||||
|
||||
<div>
|
||||
{{- d.include("/component/detail/options.md") }}
|
||||
@ -78,7 +78,7 @@ layui.define('component', function(exports) {
|
||||
|
||||
| 选项 | 描述 |
|
||||
| --- | --- |
|
||||
| elem | 件渲染指定的目标元素选择器或 DOM 对象 |
|
||||
| elem | 组件渲染指定的目标元素选择器或 DOM 对象 |
|
||||
| id | 组件渲染的唯一实例 ID |
|
||||
| show | 是否初始即渲染组件。通常结合创建组件设定的 `isRenderOnEvent` 选项决定是否启用 |
|
||||
|
||||
|
@ -30,7 +30,7 @@ toc: true
|
||||
|
||||
`dropdown.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
<br>注 <sup>2.8+</sup> : 除 `elem` 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中。
|
||||
|
||||
```
|
||||
@ -111,7 +111,7 @@ dropdown.render({
|
||||
`dropdown.reload(id, options);`
|
||||
|
||||
- 参数 `id` : 组件渲染时定义的 `id` 属性值
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法用于对下拉菜单进行完整重载,所有属性均可参与到重载中。
|
||||
|
||||
@ -177,4 +177,4 @@ dropdown.render({
|
||||
});
|
||||
// 打开对应的组件面板
|
||||
dropdown.open('test');
|
||||
```
|
||||
```
|
||||
|
@ -24,10 +24,10 @@ toc: true
|
||||
|
||||
`util.fixbar(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
<h2 id="options" lay-toc="{level: 2, hot: true}">属性</h2>
|
||||
|
||||
<div>
|
||||
{{- d.include("/fixbar/detail/options.md") }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -33,7 +33,7 @@ toc: true
|
||||
|
||||
`flow.load(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
信息流是分页的另一种表现形式,新加载的内容不替换原有的内容,而是随着滚动条滚动而追加显示。[#详见示例](#examples)
|
||||
|
||||
@ -47,7 +47,7 @@ toc: true
|
||||
|
||||
`flow.lazyimg(options);`
|
||||
|
||||
- 参数 `options` : 属性配置项。可选项见下表。
|
||||
- 参数 `options` : 属性选项。可选项见下表。
|
||||
|
||||
| 属性名 | 描述 |
|
||||
| --- | --- |
|
||||
|
@ -448,7 +448,7 @@ form.on('select(test)', function(data){
|
||||
|
||||
`form.set(options);`
|
||||
|
||||
- 参数 `options` : 全局属性配置项。详见下表:
|
||||
- 参数 `options` : 全局属性选项。详见下表:
|
||||
|
||||
| 属性名 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
|
@ -42,7 +42,7 @@ toc: true
|
||||
|
||||
`laydate.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
<br>注 <sup>2.8+</sup> : 除 `elem` 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中。
|
||||
|
||||
```
|
||||
@ -77,7 +77,7 @@ layui.use(function(){
|
||||
`laydate.hint(id, opts);`
|
||||
|
||||
- 参数 `id` : 组件渲染时定义的 `id` 属性值
|
||||
- 参数 `opts` : 该方法支持的属性可选项,详见下表
|
||||
- 参数 `opts` : 该方法支持的属性选项,详见下表
|
||||
|
||||
| opts | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
|
@ -56,7 +56,7 @@ toc: true
|
||||
|
||||
`layer.open(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
打开弹层的核心方法,其他不同类型的弹出方法均为该方法的二次封装。
|
||||
|
||||
@ -79,7 +79,7 @@ var index = layer.open({
|
||||
`layer.alert(content, options, yes);`
|
||||
|
||||
- 参数 `content` : 弹出内容
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
- 参数 `yes` : 点击确定后的回调函数
|
||||
|
||||
该方法用于弹出 `dialog` 类型信息框(`type: 0`),参数自动向左补位。
|
||||
@ -106,7 +106,7 @@ layer.alert('不开启图标', function(index){
|
||||
`layer.confirm(content, options, yes, cancel);`
|
||||
|
||||
- 参数 `content` : 弹出内容
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
- 参数 `yes` : 点击确定后的回调函数
|
||||
- 参数 `cancel` : 点击第二个按钮(默认「取消」)后的回调函数
|
||||
|
||||
@ -133,7 +133,7 @@ layer.confirm('确定吗?', function(index){
|
||||
`layer.msg(content, options, end);`
|
||||
|
||||
- 参数 `content` : 弹出内容
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
- 参数 `end` : 提示框关闭后的回调函数
|
||||
|
||||
该方法用于弹出 `dialog` 类型提示框(`type: 0`),默认 `3` 秒后自动关闭。参数自动向左补位。
|
||||
@ -162,7 +162,7 @@ layer.msg('提示框', {
|
||||
`layer.load(icon, options);`
|
||||
|
||||
- 参数 `icon` : 加载图标风格,支持 `0-2` 可选值
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法用于弹出 `load` 类型加载层(`type: 3`)。
|
||||
|
||||
@ -184,7 +184,7 @@ layer.close(index);
|
||||
|
||||
- 参数 `content` : 弹出内容
|
||||
- 参数 `elem` : 吸附的目标元素选择器或对象
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法用于弹出 `tips` 类型贴士层(`type: 4`),默认 `3` 秒后自动关闭。
|
||||
|
||||
@ -206,7 +206,7 @@ layer.tips('显示在目标元素上方', '#id', {
|
||||
|
||||
`layer.prompt(options, yes);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。除了支持 [基础属性](#options) 之外,还支持下表私有属性:
|
||||
- 参数 `options` : 基础属性选项。除了支持 [基础属性](#options) 之外,还支持下表私有属性:
|
||||
|
||||
| 私有属性 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
@ -243,7 +243,7 @@ layer.prompt({
|
||||
|
||||
`layer.photos(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。除了支持 [基础属性](#options) 之外,还支持下表私有属性:
|
||||
- 参数 `options` : 基础属性选项。除了支持 [基础属性](#options) 之外,还支持下表私有属性:
|
||||
|
||||
| 私有属性 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
@ -312,7 +312,7 @@ layui.use(function(){
|
||||
|
||||
`layer.tab(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。除了支持 [基础属性](#options) 之外,还支持下表私有属性:
|
||||
- 参数 `options` : 基础属性选项。除了支持 [基础属性](#options) 之外,还支持下表私有属性:
|
||||
|
||||
| 私有属性 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
@ -405,7 +405,7 @@ layer.closeLast(['dialog', 'page']); // 关闭最近一次打开的信息框或
|
||||
|
||||
`layer.config(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法用于全局设置弹层的默认基础属性。
|
||||
|
||||
|
@ -24,7 +24,7 @@ toc: true
|
||||
|
||||
`laypage.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
<h2 id="options" lay-toc="{level: 2, hot: true}">属性</h2>
|
||||
|
||||
@ -34,4 +34,4 @@ toc: true
|
||||
|
||||
## 小贴士
|
||||
|
||||
laypage 组件只负责分页本身的逻辑,具体的数据请求及对应的视图渲染需要另外去完成。laypage 不仅能应用在一般的异步分页上,还可直接对一段已知数据进行分页展现,如 table 组件的分页就是采用的 laypage。
|
||||
laypage 组件只负责分页本身的逻辑,具体的数据请求及对应的视图渲染需要另外去完成。laypage 不仅能应用在一般的异步分页上,还可直接对一段已知数据进行分页展现,如 table 组件的分页就是采用的 laypage。
|
||||
|
86
docs/laytpl/detail/delimiter.md
Normal file
86
docs/laytpl/detail/delimiter.md
Normal file
@ -0,0 +1,86 @@
|
||||
<table class="layui-table">
|
||||
<colgroup>
|
||||
<col width="150">
|
||||
<col>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>标签</th>
|
||||
<th>描述</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{{!
|
||||
<tr>
|
||||
<td>{{= }}</td>
|
||||
<td>
|
||||
|
||||
转义输出。若字段存在 HTML,将进行转义。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{- }} <sup>2.8+</sup></td>
|
||||
<td>
|
||||
|
||||
原文输出。即不对 HTML 字符进行转义,但需做好 XSS 防护。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{# }} <sup>旧</sup></td>
|
||||
<td>
|
||||
|
||||
**旧版本风格**(`< 2.11`)「Scriptlet 标签」。一般用于流程控制,如:
|
||||
|
||||
```js
|
||||
{{#if (d.title) { }}
|
||||
标题:{{= d.title }}
|
||||
{{#} else { }}
|
||||
默认标题
|
||||
{{#} }}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ }} <sup>新</sup><sup>2.11+</sup></td>
|
||||
<td>
|
||||
|
||||
**新版本风格**「Scriptlet 标签」。一般用于流程控制,如:
|
||||
|
||||
```js
|
||||
{{ if (d.title) { }}
|
||||
标题:{{= d.title }}
|
||||
{{ } else { }}
|
||||
默认标题
|
||||
{{ } }}
|
||||
```
|
||||
|
||||
需设置 `tagStyle: 'modern'` 后生效,否则模板会报错。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{# }} <sup>新</sup><sup>2.11+</sup></td>
|
||||
<td>
|
||||
|
||||
**新版本风格**「注释标签」。即仅在模板中显示,不在视图中输出。
|
||||
|
||||
需设置 `tagStyle: 'modern'` 后生效,否则会被视为旧版本的 Scriptlet 标签。
|
||||
|
||||
</td>
|
||||
</tr>!}}
|
||||
<tr>
|
||||
<td>{{!{{! !}}!}}</td>
|
||||
<td>
|
||||
|
||||
忽略标签。即该区域中的标签不会被解析,一般用于输出原始标签。如:
|
||||
|
||||
```js
|
||||
{{! {{! 这里面的 {{= escape }} 等模板标签不会被解析 !}} !}}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
@ -1,59 +1,59 @@
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview'], copy: false, tools: ['full'], addTools: null}">
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview'], codeStyle: 'max-height: 520px;', copy: false, tools: ['full'], addTools: null}">
|
||||
<textarea>
|
||||
{{!
|
||||
<style>
|
||||
.laytpl-demo{border: 1px solid #eee;}
|
||||
.laytpl-demo:first-child{border-right: none;}
|
||||
.laytpl-demo>textarea{position: relative; display: block; width:100%; height: 300px; padding: 11px; border: 0; box-sizing: border-box; resize: none; background-color: #fff; font-family: Courier New; font-size: 13px;}
|
||||
.laytpl-demo>div:first-child{height: 32px; line-height: 32px; padding: 6px 11px; border-bottom: 1px solid #eee; background-color: #F8F9FA;}
|
||||
.laytpl-demo{border: 1px solid #eee;}
|
||||
.laytpl-demo:first-child{border-right: none;}
|
||||
.laytpl-demo>textarea{position: relative; display: block; width:100%; height: 320px; padding: 11px; border: 0; box-sizing: border-box; resize: none; background-color: #fff; font-family: Courier New; font-size: 13px;}
|
||||
.laytpl-demo>div:first-child{height: 32px; line-height: 32px; padding: 6px 11px; border-bottom: 1px solid #eee; background-color: #F8F9FA;}
|
||||
.laytpl-demo .layui-tabs{top: -1px;}
|
||||
|
||||
#ID-tpl-view-body {
|
||||
max-height: 320px; overflow: auto; clear: both;
|
||||
}
|
||||
#ID-tpl-view-body > div {
|
||||
display: none;
|
||||
}
|
||||
.laytpl-demo pre {
|
||||
margin: 0; padding: 16px; background-color: #1F1F1F; color: #F8F9FA; font-family: 'Courier New',Consolas, monospace;
|
||||
}
|
||||
</style>
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs6 laytpl-demo">
|
||||
<div>模板</div>
|
||||
<textarea id="ID-tpl-src">
|
||||
<h3>{{= d.title }}</h3>
|
||||
<ul>
|
||||
{{# layui.each(d.list, function(index, item){ }}
|
||||
<li>
|
||||
<span>{{= item.modname }}</span>
|
||||
<span>{{= item.alias }}:</span>
|
||||
<span>{{= item.site || '' }}</span>
|
||||
</li>
|
||||
{{# }); }}
|
||||
|
||||
{{# if(d.list.length === 0){ }}
|
||||
无数据
|
||||
{{# } }}
|
||||
</ul>
|
||||
</textarea>
|
||||
<div>
|
||||
<div style="cursor: pointer;" id="ID-tpl-src-title">
|
||||
<strong>模板(旧版本)</strong>
|
||||
<i class="layui-icon layui-icon-down layui-font-12"></i>
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="ID-tpl-src"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="layui-col-xs6 laytpl-demo">
|
||||
<div>数据</div>
|
||||
<div><strong>数据</strong></div>
|
||||
<textarea id="ID-tpl-data">
|
||||
{
|
||||
"title": "Layui 常用模块",
|
||||
"title": "Layui 常用组件",
|
||||
"desc": "<a style=\"color:blue;\">一段带 HTML 内容的描述</a>",
|
||||
"list": [
|
||||
{
|
||||
"modname": "弹层",
|
||||
"alias": "layer",
|
||||
"site": "layer.domain.com"
|
||||
"title": "弹层",
|
||||
"name": "layer"
|
||||
},
|
||||
{
|
||||
"modname": "表单",
|
||||
"alias": "form"
|
||||
"title": "表单",
|
||||
"name": "form"
|
||||
},
|
||||
{
|
||||
"modname": "表格",
|
||||
"alias": "table"
|
||||
"title": "表格",
|
||||
"name": "table"
|
||||
},
|
||||
{
|
||||
"modname": "日期",
|
||||
"alias": "laydate"
|
||||
"title": "日期选择器",
|
||||
"name": "laydate"
|
||||
},
|
||||
{
|
||||
"modname": "上传",
|
||||
"alias": "upload"
|
||||
"title": "标签页",
|
||||
"name": "tabs"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -61,75 +61,198 @@
|
||||
</div>
|
||||
<div class="layui-col-xs12 laytpl-demo" style="border-top: none;">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs6">视图</div>
|
||||
<div class="layui-col-xs6" style="text-align: right">
|
||||
<span id="ID-tpl-viewtime"></span>
|
||||
<div class="layui-col-xs6 layui-tabs" id="ID-tpl-view-header">
|
||||
<ul class="layui-tabs-header">
|
||||
<li><strong>渲染结果</strong></li>
|
||||
<li><strong>源码</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-padding-sm" id="ID-tpl-view" style="max-height: 300px; padding: 16px; overflow: auto;">…</div>
|
||||
<div class="layui-col-xs6" style="text-align: right">
|
||||
<span class="layui-badge" id="ID-tpl-view-time"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ID-tpl-view-body">
|
||||
<div class="layui-show layui-padding-3 layui-text" id="ID-tpl-view"></div>
|
||||
<div><pre id="ID-tpl-view-code" ></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-clear"></div>
|
||||
|
||||
<!-- import layui -->
|
||||
<!-- 新版本模板 -->
|
||||
<script type="text/html" id="ID-tpl-template-modern">
|
||||
<p>转义输出:{{= d.desc }}</p>
|
||||
<p>原文输出:{{- d.desc }}</p>
|
||||
|
||||
{{#注释标签 - 仅在模板中显示,不在视图中输出 }}
|
||||
|
||||
<p>{{! 忽略标签,可显示原始标签:
|
||||
{{ let a = 0; }} {{= escape }} {{- source }} {{#comments }} {{! ignore !}}
|
||||
!}}</p>
|
||||
|
||||
{{#标签空主体测试 }}
|
||||
{{}} {{ }} {{ }} {{= }} {{=}} {{= }}
|
||||
|
||||
<p>
|
||||
条件语句:
|
||||
{{= d.list.length ? d.title : '' }}
|
||||
{{ if(d.title){}} - #AAAA{{='A'}}{{ } }}
|
||||
</p>
|
||||
<p>循环语句:</p>
|
||||
<ul>
|
||||
{{ d.list.forEach(function(item) { }}
|
||||
<li>
|
||||
<span>{{= item.title }}</span>
|
||||
<span>{{= item.name }}</span>
|
||||
</li>
|
||||
{{ }); }}
|
||||
</ul>
|
||||
{{ if (d.list.length === 0) { }}
|
||||
无数据
|
||||
{{} }}
|
||||
</script>
|
||||
|
||||
<!-- 旧版本模板 -->
|
||||
<script type="text/html" id="ID-tpl-template-legacy">
|
||||
<p>转义输出:{{= d.desc }}</p>
|
||||
<p>原文输出:{{- d.desc }}</p>
|
||||
<p>
|
||||
条件语句:
|
||||
{{= d.list.length ? d.title : '' }}
|
||||
{{#if(d.title){}} - #AAAA{{='A'}}{{#}}}
|
||||
</p>
|
||||
<p>循环语句:</p>
|
||||
<ul>
|
||||
{{#d.list.forEach(function(item) { }}
|
||||
<li>
|
||||
<span>{{= item.title }}</span>
|
||||
<span>{{= item.name }}</span>
|
||||
</li>
|
||||
{{#}); }}
|
||||
</ul>
|
||||
{{#if (d.list.length === 0) { }}
|
||||
无数据
|
||||
{{#} }}
|
||||
</script>
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function(){
|
||||
layui.use(['laytpl', 'util', 'tabs', 'dropdown'], function() {
|
||||
var laytpl = layui.laytpl;
|
||||
var util = layui.util;
|
||||
var tabs = layui.tabs;
|
||||
var dropdown = layui.dropdown;
|
||||
var $ = layui.$;
|
||||
|
||||
// 默认设置
|
||||
laytpl.config({
|
||||
// tagStyle: 'modern' // 初始化标签风格
|
||||
});
|
||||
|
||||
// 获取模板和数据
|
||||
var get = function(type){
|
||||
var getData = function(type) {
|
||||
return {
|
||||
template: $('#ID-tpl-src').val(), // 获取模板
|
||||
data: function(){ // 获取数据
|
||||
try {
|
||||
return JSON.parse($('#ID-tpl-data').val());
|
||||
} catch(e){
|
||||
} catch(e) {
|
||||
$('#ID-tpl-view').html(e);
|
||||
}
|
||||
}()
|
||||
};
|
||||
};
|
||||
|
||||
var data = get();
|
||||
|
||||
// 耗时计算
|
||||
var startTime = new Date().getTime(), timer = function(startTime, title){
|
||||
var endTime = new Date().getTime();
|
||||
$('#ID-tpl-viewtime').html((title || '模板解析耗时:')+ (endTime - startTime) + 'ms');
|
||||
|
||||
// 视图渲染
|
||||
var renderView = function(html, startTime) {
|
||||
timer(startTime);
|
||||
$('#ID-tpl-view').html(html);
|
||||
$('#ID-tpl-view-code').html(util.escape(html));
|
||||
};
|
||||
|
||||
// 渲染模板
|
||||
var thisTpl = laytpl(data.template);
|
||||
// 生成模板
|
||||
var createTemplate = function(opts) {
|
||||
opts = $.extend({
|
||||
tagStyle: 'legacy'
|
||||
}, opts);
|
||||
|
||||
// 执行渲染
|
||||
thisTpl.render(data.data, function(view){
|
||||
timer(startTime);
|
||||
$('#ID-tpl-view').html(view);
|
||||
// 初始化模板
|
||||
var elem = $('#ID-tpl-template-'+ opts.tagStyle);
|
||||
$('#ID-tpl-src').val(elem.html().replace(/^\s+/g, ''));
|
||||
|
||||
return opts;
|
||||
};
|
||||
var tplConfig = createTemplate();
|
||||
var data = getData();
|
||||
|
||||
// 耗时计算
|
||||
var timer = function(startTime, title) {
|
||||
var endTime = new Date();
|
||||
$('#ID-tpl-view-time').html((title || '本次渲染总耗时:')+ (endTime - startTime) + 'ms');
|
||||
};
|
||||
var startTime = new Date();
|
||||
|
||||
// 创建一个模板实例
|
||||
var templateInst = laytpl(data.template, {
|
||||
condense: false, // 不处理连续空白符,即保留模板原始结构
|
||||
tagStyle: tplConfig.tagStyle
|
||||
});
|
||||
|
||||
|
||||
// 初始渲染
|
||||
templateInst.render(data.data, function(html) {
|
||||
renderView(html, startTime);
|
||||
});
|
||||
|
||||
// 编辑
|
||||
$('.laytpl-demo textarea').on('input propertychange', function(){
|
||||
var data = get();
|
||||
if(!data.data) return;
|
||||
|
||||
// 计算模板渲染耗时
|
||||
var startTime = new Date().getTime();
|
||||
|
||||
// 若模板有变化,则重新解析模板;若模板没变,数据有变化,则从模板缓存中直接渲染(效率大增)
|
||||
if(this.id === 'ID-tpl-src'){
|
||||
thisTpl.parse(data.template, data.data); // 解析模板
|
||||
$('.laytpl-demo textarea').on('input', function() {
|
||||
var data = getData();
|
||||
var startTime = new Date();
|
||||
|
||||
// 若模板有变化,则重新编译模板
|
||||
if (this.id === 'ID-tpl-src') {
|
||||
templateInst.compile(data.template);
|
||||
}
|
||||
|
||||
// 执行渲染
|
||||
thisTpl.render(data.data, function(view){
|
||||
timer(startTime);
|
||||
$('#ID-tpl-view').html(view);
|
||||
|
||||
// 若模板没变,数据有变化,则从模板缓存中直接渲染数据(效率大增)
|
||||
templateInst.render(data.data, function(html) {
|
||||
renderView(html, startTime);
|
||||
});
|
||||
});
|
||||
|
||||
// 视图结果 tabs
|
||||
tabs.render({
|
||||
elem: '#ID-tpl-view-header',
|
||||
body: ['#ID-tpl-view-body', '>div']
|
||||
});
|
||||
|
||||
// 切换模板
|
||||
dropdown.render({
|
||||
elem: '#ID-tpl-src-title',
|
||||
data: [{
|
||||
title: '新版本模板',
|
||||
tagStyle: 'modern'
|
||||
}, {
|
||||
title: '旧版本模板',
|
||||
tagStyle: 'legacy'
|
||||
}],
|
||||
click: function(obj){
|
||||
createTemplate({
|
||||
tagStyle: obj.tagStyle
|
||||
});
|
||||
this.elem.children('strong').html(obj.title);
|
||||
|
||||
// 同步设置标签风格
|
||||
templateInst.config.tagStyle = obj.tagStyle;
|
||||
|
||||
var data = getData();
|
||||
var startTime = new Date();
|
||||
|
||||
// 重新渲染
|
||||
templateInst.compile(data.template).render(data.data, function(html) {
|
||||
renderView(html, startTime);
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
!}}
|
||||
</textarea>
|
||||
</script>!}}</textarea>
|
||||
</pre>
|
||||
|
@ -7,72 +7,85 @@
|
||||
<tr>
|
||||
<th>标签</th>
|
||||
<th>描述</th>
|
||||
</tr>
|
||||
<th>类型</th>
|
||||
<th>默认值</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{{!
|
||||
<tr>
|
||||
<td>{{= }}</td>
|
||||
<td>open</td>
|
||||
<td>
|
||||
|
||||
转义输出。若字段存在 HTML,将进行转义。
|
||||
|
||||
```
|
||||
<h2>{{= d.title }}</h2>
|
||||
```
|
||||
用于设置起始界定符
|
||||
|
||||
</td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
|
||||
`{{`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{- }} <sup>2.8+</sup></td>
|
||||
<td>close</td>
|
||||
<td>
|
||||
|
||||
原始输出。若字段存在 HTML,将正常渲染。
|
||||
|
||||
```
|
||||
<div>{{- d.content }}</div>
|
||||
```
|
||||
用于设置结束界定符
|
||||
|
||||
该语句一般在需要正常渲染 HTML 时用到,但若字段存在 script 等标签,为防止 xss 问题,可采用 `{{= }}` 进行转义输出。
|
||||
</td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
|
||||
> ### 注意
|
||||
> 由于 `2.6.11` 版本对 laytpl 语句进行了重要调整,原 `{{ }}` 语法即等同 `{{- }}`,升级版本时,请进行相应调整。可参考:https://gitee.com/layui/layui/issues/I5AXSP
|
||||
`}}`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{# }}</td>
|
||||
<td>cache <sup>2.11+</sup></td>
|
||||
<td>
|
||||
|
||||
JavaScript 语句。一般用于逻辑处理。
|
||||
|
||||
```
|
||||
<div>
|
||||
{{#
|
||||
var fn = function(){
|
||||
return '2017-08-18';
|
||||
};
|
||||
}}
|
||||
{{# if(true){ }}
|
||||
开始日期:{{= fn() }}
|
||||
{{# } else { }}
|
||||
已截止
|
||||
{{# } }}
|
||||
</div>
|
||||
```
|
||||
是否开启模板缓存,以便下次渲染时不重新编译模板
|
||||
|
||||
</td>
|
||||
</tr>!}}
|
||||
<tr>
|
||||
<td>{{!{{! !}}!}}</td>
|
||||
<td>boolean</td>
|
||||
<td>
|
||||
|
||||
对一段指定的模板区域进行过滤,即不解析该区域的模板。
|
||||
|
||||
```
|
||||
{{! {{! 这里面的模板不会被解析 !}} !}}
|
||||
```
|
||||
`true`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tr>
|
||||
<td>condense <sup>2.11+</sup></td>
|
||||
<td>
|
||||
|
||||
是否压缩模板空白符,如:将多个连续的空白符压缩为单个空格
|
||||
|
||||
</td>
|
||||
<td>boolean</td>
|
||||
<td>
|
||||
|
||||
`true`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>tagStyle<br><sup>2.11+</sup></td>
|
||||
<td>
|
||||
|
||||
设置标签风格。可选值:
|
||||
|
||||
- `legacy`: 采用 `< 2.11` 旧版本的标签风格
|
||||
- `modern`: 采用 `2.11+` 新版本的标签风格
|
||||
|
||||
为了保持向下兼容,默认仍然采用旧版本的标签风格,但在后续版本可能会将默认值设置为 `modern`,因此,**实际使用时,建议显式设置该选项值,以免升级时产生不兼容的问题**。
|
||||
|
||||
</td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
|
||||
`legacy`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>!}}
|
||||
</table>
|
||||
|
@ -5,11 +5,11 @@ toc: true
|
||||
|
||||
# 模板引擎
|
||||
|
||||
> `laytpl` 是 Layui 的一款轻量 JavaScript 模板引擎,在字符解析上有着比较出色的表现。
|
||||
> `laytpl` 是 Layui 内置的 JavaScript 模板引擎,采用原生控制流,在模板解析上有着比较出色的表现。
|
||||
|
||||
<h2 id="test" lay-toc="{hot: true}" style="margin-bottom: 0;">在线测试</h2>
|
||||
|
||||
在以下*模板*或*数据*中进行编辑,下方*视图*将呈现对应结果。
|
||||
对文本框中的*模板*或*数据*进行编辑,下方将呈现对应的*渲染结果*。注:自 <sup>2.11+</sup> 版本开始,你可以设置 `tagStyle: 'modern'` 让模板采用新的标签风格。为了保持向下兼容,默认仍然采用旧版本的标签风格。
|
||||
|
||||
<div>
|
||||
{{- d.include("/laytpl/detail/demo.md") }}
|
||||
@ -20,135 +20,256 @@ toc: true
|
||||
| API | 描述 |
|
||||
| --- | --- |
|
||||
| var laytpl = layui.laytpl | 获得 `laytpl` 模块。 |
|
||||
| [laytpl(str, options).render(data, callback)](#render) | laytpl 组件渲染,核心方法。 |
|
||||
| [laytpl.config(options)](#config) | 配置 laytpl 全局属性 |
|
||||
| [var templateInst = laytpl(template, options)](#laytpl) | 创建模板实例。 |
|
||||
| [laytpl.config(options)](#config) | 设置基础选项默认值 |
|
||||
| [laytpl.extendVars(variables)](#variables) <sup>2.11+</sup> | 扩展模板内部变量 |
|
||||
|
||||
<h2 id="render" lay-toc="{level: 2, hot: true, title: '解析和渲染'}">模板解析和渲染</h2>
|
||||
<h3 id="laytpl" lay-toc="{level: 2, hot: true}">创建模板实例</h3>
|
||||
|
||||
`laytpl(str, options).render(data, callback);`
|
||||
`var templateInst = laytpl(template, options)`
|
||||
|
||||
- 参数 `str` : 模板原始字符
|
||||
- 参数 `options` <sup>2.8+</sup> : 当前模板实例的属性配置项。可选项详见:[#属性配置](#config)
|
||||
- 参数 `data` : 模板数据
|
||||
- 参数 `callback` : 模板渲染完毕的回调函数,并返回渲染后的字符
|
||||
- 参数 `template` : 原始模板字符
|
||||
- 参数 `options` <sup>2.8+</sup> : 当前模板实例的选项。详见下述:[#基础选项](#options)
|
||||
|
||||
该方法返回一个模板编译器实例,用于对模板进行数据渲染等操作,实例对象的成员有:
|
||||
|
||||
| 实例成员 | 描述 |
|
||||
| --- | --- |
|
||||
| templateInst.render(data, callback) | 给模板实例进行数据渲染,返回渲染后的 HTML 字符 |
|
||||
| templateInst.compile(template) <sup>2.11+</sup> | 编译新的模板,会强制清除旧模板缓存 |
|
||||
| templateInst.config <sup>2.11+</sup> | 获取当前模板实例的配置选项 |
|
||||
|
||||
通过将模板编译与渲染两个环节分开,我们可以在模板仅编译一次的情况下,对其渲染不同的数据,如:
|
||||
|
||||
{{!
|
||||
```js
|
||||
var laytpl = layui.laytpl;
|
||||
|
||||
```
|
||||
layui.use('laytpl', function(){
|
||||
var laytpl = layui.laytpl;
|
||||
// 创建模板实例
|
||||
var templateInst = laytpl('{{= d.name }}是一名{{= d.role }}');
|
||||
|
||||
// 直接解析字符
|
||||
laytpl('{{= d.name }}是一名前端工程师').render({
|
||||
name: '张三'
|
||||
}, function(str){
|
||||
console.log(str); // 张三是一名前端工程师
|
||||
});
|
||||
// 数据渲染 1
|
||||
templateInst.render({
|
||||
name: '张三',
|
||||
role: '全栈开发者'
|
||||
}, function(html) {
|
||||
console.log(html); // 张三是一名全栈开发者
|
||||
});
|
||||
|
||||
// 同步写法
|
||||
var str = laytpl('{{= d.name }}是一名前端工程师').render({
|
||||
name: '张三'
|
||||
});
|
||||
console.log(str); // 张三是一名前端工程师
|
||||
// 数据渲染 2
|
||||
var html = templateInst.render({
|
||||
name: '王五',
|
||||
role: '架构师'
|
||||
});
|
||||
```
|
||||
!}}
|
||||
|
||||
若模板字符较大,可存放在页面某个标签中,如:
|
||||
若每次需要对不同的模板进行编译和数据渲染,你也可以使用链式写法,如:
|
||||
|
||||
{{!
|
||||
```js
|
||||
laytpl('{{= d.name }}是一名{{= d.role }}').render({
|
||||
name: '张三',
|
||||
role: '全栈开发者'
|
||||
}, function(html) {
|
||||
console.log(html); // 张三是一名全栈开发者
|
||||
});
|
||||
```
|
||||
<script id="TPL" type="text/html">
|
||||
!}}
|
||||
|
||||
若模板字符较大,你可以将模板存放在页面某个标签中,如:
|
||||
|
||||
{{!
|
||||
```js
|
||||
<script id="ID-demo-tpl" type="text/html">
|
||||
<h3>{{= d.name }}</h3>
|
||||
<p>性别:{{= d.sex ? '男' : '女' }}</p>
|
||||
<p>角色:{{= d.role }}</p>
|
||||
</script>
|
||||
|
||||
<div id="view"></div>
|
||||
<div id="ID-demo-view"></div>
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function(){
|
||||
var laytpl = layui.laytpl;
|
||||
|
||||
// 渲染
|
||||
var data = {
|
||||
name: '张三',
|
||||
sex: 1
|
||||
var template = document.getElementById('ID-demo-tpl').innerHTML; // 获取模板字符
|
||||
var target = document.getElementById('ID-demo-view'); // 输出结果的目标元素
|
||||
var data = { // 数据
|
||||
"name": "张三",
|
||||
"role": "全栈开发者"
|
||||
};
|
||||
var getTpl = document.getElementById('TPL').innerHTML; // 获取模板字符
|
||||
var elemView = document.getElementById('view'); // 视图对象
|
||||
// 渲染并输出结果
|
||||
laytpl(getTpl).render(data, function(str){
|
||||
elemView.innerHTML = str;
|
||||
laytpl(template).render(data, function(html) {
|
||||
target.innerHTML = html;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
!}}
|
||||
|
||||
在实际使用时,若模板通用,而数据不同,为减少模板解析的开销,可将语句分开书写,如。
|
||||
实际使用时,若模板通用,而数据不同,为了避免对模板进行不必要的重复编译,推荐将创建模板实例与数据渲染分开书写。
|
||||
|
||||
```
|
||||
var compile = laytpl(str); // 模板解析
|
||||
compile.render(data, callback); // 模板渲染
|
||||
```
|
||||
<h3 id="options" lay-toc="{level: 2, hot: true}">基础选项</h3>
|
||||
|
||||
<h2 id="grammar" lay-toc="{level: 2, hot: true}">标签语法</h2>
|
||||
创建模板实例时,你还可以对其设置一些选项,如:
|
||||
|
||||
{{!
|
||||
```js
|
||||
// 创建模板实例
|
||||
var templateInst = laytpl(`
|
||||
{{ let role = d.role || '全栈开发者'; }}
|
||||
{{= d.name }}是一名{{= role }}
|
||||
`, {
|
||||
tagStyle: 'modern' // 采用新版本的标签风格
|
||||
});
|
||||
var html = templateInst.render({ name: '张三' });
|
||||
```
|
||||
!}}
|
||||
|
||||
支持设置的完整选项如下:
|
||||
|
||||
<div>
|
||||
{{- d.include("/laytpl/detail/options.md") }}
|
||||
</div>
|
||||
|
||||
> ### 注意
|
||||
> 开发者在使用模板语法时,需确保模板中的 JS 语句不来自于页面用户输入,即必须在页面开发者自身的可控范围内,否则请避免使用该模板引擎。
|
||||
<h3 id="delimiter" lay-toc="{level: 2, hot: true}">标签规则</h3>
|
||||
|
||||
<h2 id="config" lay-toc="{level: 2}">属性配置</h2>
|
||||
<div>
|
||||
{{- d.include("/laytpl/detail/delimiter.md") }}
|
||||
</div>
|
||||
|
||||
#### ⚡ 请注意:
|
||||
> *开发者在使用模板标签时,需确保模板中待输出的内容在开发者自身的可控范围内,尤其对于用户输入的字符要做好 XSS 防护,否则请避免使用该模板引擎,以免产生 XSS 安全隐患*。
|
||||
|
||||
<h3 id="include" lay-toc="{level: 2, hot: true}">导入子模板 <sup>2.11+</sup></h3>
|
||||
|
||||
{{!
|
||||
laytpl 支持在模板中通过添加 `{{- include(id, data) }}` 语句引入子模板。`include` 语句参数解释:
|
||||
|
||||
- `id` : 子模板 ID
|
||||
- `data` : 向子模版传入的数据
|
||||
|
||||
为了引入的子模板不被转义,因此这里应该使用 `{{- }}`,即对子模板进行原文输出。示例:
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['code', 'preview'], codeStyle: 'max-height: 520px;', tools: ['full']}">
|
||||
<textarea>
|
||||
<script id="ID-demo-tpl-header" type="text/html">
|
||||
<div>头部公共模板</div>
|
||||
</script>
|
||||
<script id="ID-demo-tpl-list" type="text/html">
|
||||
<ul>
|
||||
{{ d.items.forEach(function(item, index) { }}
|
||||
<li>
|
||||
<span>{{= item.title }}</span>
|
||||
{{ if(item.children) { }}
|
||||
{{- include('ID-demo-tpl-list', { items: item.children }) }}
|
||||
{{ } }}
|
||||
</li>
|
||||
{{ }); }}
|
||||
</ul>
|
||||
</script>
|
||||
<script id="ID-demo-tpl-main" type="text/html">
|
||||
{{- include('ID-demo-tpl-header') }}
|
||||
<h3>循环输出:</h3>
|
||||
{{- include('ID-demo-tpl-list', { items: d.items }) }}
|
||||
</script>
|
||||
<div id="ID-demo-view"></div>
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function() {
|
||||
var laytpl = layui.laytpl;
|
||||
|
||||
var template = document.getElementById('ID-demo-tpl-main').innerHTML; // 获取模板字符
|
||||
var target = document.getElementById('ID-demo-view'); // 输出结果的目标元素
|
||||
var data = {
|
||||
items: [{"title": "list 1", "children": [{"title": "list 1-1", "children": [{"title": "list 1-1-1"}]}, {"title": "list 1-2"}]},{"title": "list 2", "children": [{"title": "list 2-1"}]},{"title": "list 3"}]
|
||||
};
|
||||
|
||||
// 创建模板实例
|
||||
var templateInst = laytpl(template, {
|
||||
tagStyle: 'modern' // 采用新版本的标签风格
|
||||
});
|
||||
|
||||
// 渲染并输出结果
|
||||
templateInst.render(data, function(html) {
|
||||
target.innerHTML = html;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
!}}
|
||||
|
||||
若在 Node.js 环境,可通过 `laytpl.extendVars()` 方法重置 `include` 语句实现模板文件的导入。
|
||||
|
||||
<h3 id="config" lay-toc="{level: 2}">设置选项默认值</h3>
|
||||
|
||||
`laytpl.config(options);`
|
||||
|
||||
- 参数 `options` : 属性配置项。可选项详见下表
|
||||
- 参数 `options`: 基础选项
|
||||
|
||||
| 属性 | 描述 |
|
||||
| --- | --- |
|
||||
| open | 标签符前缀 |
|
||||
| close | 标签符后缀 |
|
||||
你可以设置任意选项的默认值,如:
|
||||
|
||||
### 全局配置
|
||||
|
||||
若模板默认的标签符与其他模板存在冲突,可通过该方法重新设置标签符,如:
|
||||
|
||||
```
|
||||
{{!
|
||||
```js
|
||||
laytpl.config({
|
||||
open: '<%',
|
||||
close: '%>'
|
||||
open: '<%', // 自定义起始界定符
|
||||
close: '%>', // 自定义起始界定符
|
||||
tagStyle: 'modern' // 采用新版本的标签风格
|
||||
});
|
||||
|
||||
// 模板语法将默认采用上述定义的标签符书写
|
||||
laytpl(`
|
||||
<%# var job = ["前端工程师"]; %>
|
||||
<%= d.name %>是一名<%= job[d.type] %>。
|
||||
`).render({
|
||||
// 创建模板实例
|
||||
var templateInst = laytpl(`
|
||||
<% var roles = ["前端工程师","全栈工程师","架构师"]; %>
|
||||
<%= d.name %>是一名<%= roles[d.role] %>
|
||||
`);
|
||||
// 渲染
|
||||
templateInst.render({
|
||||
name: '张三',
|
||||
type: 0
|
||||
role: 1
|
||||
}, function(string){
|
||||
console.log(string); // 张三是一名前端工程师。
|
||||
console.log(string); // 张三是一名全栈工程师
|
||||
});
|
||||
```
|
||||
!}}
|
||||
|
||||
### 局部配置 <sup>2.8+</sup>
|
||||
<h3 id="variables" lay-toc="{level: 2}">扩展模板内变量</h3>
|
||||
|
||||
若不想受到上述全局配置的影响,可在 `laytpl(str, options)` 方法的第二个参数中设置当前模板的局部属性,如:
|
||||
`laytpl.extendVars(variables)`
|
||||
|
||||
```
|
||||
laytpl('<%= d.name %>是一名前端工程师', {
|
||||
open: '<%',
|
||||
close: '%>'
|
||||
}).render({name: '张三'}, function(string){
|
||||
console.log(string); // 张三是一名前端工程师。
|
||||
- 参数 `variables` : 扩展的变量列表,变量值通常是一个函数
|
||||
|
||||
事实上 laytpl 内置了一些模板内部方法,如 `_escape, include`。你可以对它们进行重构,或扩展更多内部变量,如:
|
||||
|
||||
{{!
|
||||
```js
|
||||
// 扩展模板内部变量
|
||||
laytpl.extendVars({
|
||||
// 重构 include 方法,实现引入模板文件
|
||||
include: function(filename, data) {
|
||||
// …
|
||||
},
|
||||
// 添加 toDataString 方法
|
||||
toDataString: function(date) {
|
||||
date = date || new Date();
|
||||
return new Date(date).toLocaleDateString();
|
||||
}
|
||||
});
|
||||
|
||||
// 在模板中使用扩展的变量
|
||||
var templateInst = laytpl('日期:{{= toDataString(d.time) }}');
|
||||
templateInst.render({ time: 1742745600000 }, function(html) {
|
||||
console.log(html);
|
||||
});
|
||||
```
|
||||
!}}
|
||||
|
||||
## 💖 心语
|
||||
|
||||
## 贴士
|
||||
我们在 `2.11` 版本对 laytpl 完成了重要重构,使其能够具备应对更多复杂模板结构的解析能力。同时,为了与业界常用的 JavaScript 模板引擎 ejs 对齐,我们新增了与 ejs 相同的标签规则,这意味着同一套模板可以在 laytpl 和 ejs 中任意切换。
|
||||
|
||||
> Layui table 等组件的动态模板功能,均采用 laytpl 驱动。 laytpl 亦可承载单页面应用开发中的视图模板。
|
||||
作为 Layui 为数不多的一个纯功能型的模块,laytpl 承载了一些重要组件的功能支撑,如 table, dropdown 等,使得它们也能够自定义动态模板,增强了组件的可定制化。当然,laytpl 也可以作为前端单页面应用及 Express 等 Web 框架的视图引擎。
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
title: 面板 panel,card,collapse
|
||||
toc: true
|
||||
---
|
||||
|
||||
|
||||
# 面板
|
||||
|
||||
> 面板是一个包含普通面板(panel)、卡片面板(card)、折叠面板(collapse)的集合
|
||||
@ -44,7 +44,7 @@ toc: true
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
@ -127,7 +127,7 @@ toc: true
|
||||
|
||||
<!-- import layui -->
|
||||
</textarea>
|
||||
</pre>
|
||||
</pre>
|
||||
|
||||
<h3 id="collapse-tree" lay-toc="{level: 2}">折叠面板嵌套</h3>
|
||||
|
||||
@ -141,33 +141,33 @@ toc: true
|
||||
<div class="layui-colla-item">
|
||||
<div class="layui-colla-title">文学家</div>
|
||||
<div class="layui-colla-content layui-show">
|
||||
|
||||
|
||||
<div class="layui-collapse" lay-accordion>
|
||||
<div class="layui-colla-item">
|
||||
<div class="layui-colla-title">唐代</div>
|
||||
<div class="layui-colla-content layui-show">
|
||||
|
||||
|
||||
<div class="layui-collapse" lay-accordion>
|
||||
<div class="layui-colla-item">
|
||||
<div class="layui-colla-title">杜甫</div>
|
||||
<div class="layui-colla-content layui-show">
|
||||
一代诗圣
|
||||
唐代著名诗人,与李白齐名
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-colla-item">
|
||||
<div class="layui-colla-title">李白</div>
|
||||
<div class="layui-colla-content">
|
||||
<p>一代诗仙</p>
|
||||
<p>唐代著名诗人,与杜甫齐名</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-colla-item">
|
||||
<div class="layui-colla-title">王勃</div>
|
||||
<div class="layui-colla-content">
|
||||
<p>千古绝唱《滕王阁序》</p>
|
||||
<p>著有千古名篇《滕王阁序》</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-colla-item">
|
||||
@ -227,7 +227,7 @@ toc: true
|
||||
`element.on('collapse(filter)', callback)`
|
||||
|
||||
- 参数 `collapse(filter)` 是一个特定结构。
|
||||
- `collapse` 为折叠面板点击事件固定值;
|
||||
- `collapse` 为折叠面板点击事件固定值;
|
||||
- `filter` 为导航容器属性 `lay-filter` 对应的值。
|
||||
- 参数 `callback` 为事件执行时的回调函数,并返回一个 `object` 类型的参数。
|
||||
|
||||
@ -276,7 +276,7 @@ layui.use(function(){
|
||||
console.log(data.show); // 得到当前面板的展开状态,true or false
|
||||
console.log(data.title); // 得到当前点击面板的标题区域对象
|
||||
console.log(data.content); // 得到当前点击面板的内容区域对象
|
||||
|
||||
|
||||
// 显示状态,仅用于演示
|
||||
layer.msg('展开状态:'+ data.show);
|
||||
});
|
||||
|
@ -26,7 +26,7 @@ toc: true
|
||||
|
||||
`rate.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
<br>注 <sup>2.8+</sup> : 除 `elem` 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中。
|
||||
|
||||
```
|
||||
@ -54,4 +54,4 @@ layui.use(function(){
|
||||
|
||||
<div>
|
||||
{{- d.include("/rate/detail/options.md") }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -47,7 +47,7 @@
|
||||
滑块初始值。
|
||||
|
||||
- 默认可直接设置数值,如: `value: 50`
|
||||
- 若滑块开启 `range: true` 区间选择,则值为数组,异表示开始和结尾的区间,如: `value: [30, 60]`
|
||||
- 若滑块开启 `range: true` 区间选择,则值为数组,亦表示开始和结尾的区间,如: `value: [30, 60]`
|
||||
|
||||
</td>
|
||||
<td>number<br>array</td>
|
||||
|
@ -24,13 +24,13 @@ toc: true
|
||||
| var slider = layui.slider | 获得 `slider` 模块。 |
|
||||
| [var inst = slider.render(options)](#render) | slider 组件渲染,核心方法。 |
|
||||
| [inst.setValue(value)](#setValue) | 设置滑块值 |
|
||||
| inst.config | 获得当前实例的属性配置项 |
|
||||
| inst.config | 获得当前实例的属性选项 |
|
||||
|
||||
<h2 id="render" lay-toc="{level: 2}">渲染</h2>
|
||||
|
||||
`slider.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
<br>注 <sup>2.8+</sup> : 除 `elem` 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中。
|
||||
|
||||
```
|
||||
@ -90,4 +90,4 @@ ins1.setValue(60, 1) // 设置结尾值
|
||||
|
||||
<div>
|
||||
{{- d.include("/slider/detail/options.md") }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -185,7 +185,7 @@ layui.use(function(){
|
||||
`element.tabAdd(filter, options);`
|
||||
|
||||
- 参数 `filter` : tab 容器(`class="layui-tab"`)的 `lay-filter` 属性值
|
||||
- 参数 `options` : 添加 tab 时的属性可选项,见下表:
|
||||
- 参数 `options` : 添加 tab 时的属性选项,见下表:
|
||||
|
||||
| options | 描述 | 类型 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
@ -225,7 +225,7 @@ layui.use(function(){
|
||||
|
||||
`element.tab(options);`
|
||||
|
||||
- 参数 `options` : 属性可选项,见下表:
|
||||
- 参数 `options` : 属性选项,见下表:
|
||||
|
||||
| options | 描述 | 类型 |
|
||||
| --- | --- | --- |
|
||||
|
@ -155,7 +155,7 @@ table.render({
|
||||
| LAY_DISABLED | 当前行是否禁止选择 | 可读可写 |
|
||||
| LAY_INDEX | 当前行下标。每页重新从零开始计算 | 只读 |
|
||||
| LAY_NUM | 当前行序号 | 只读 |
|
||||
| LAY_COL | 当前列的表头属性配置项 | 只读 |
|
||||
| LAY_COL | 当前列的表头属性选项 | 只读 |
|
||||
|
||||
示例一: 在返回的数据中设置特定字段:
|
||||
|
||||
|
@ -527,7 +527,7 @@ initSort: {
|
||||
```
|
||||
table.render({
|
||||
before: function(options){
|
||||
console.log(options); // 当前实例属性配置项
|
||||
console.log(options); // 当前实例属性选项
|
||||
options.where.abc = 123; // 修改或额外追加 where 属性
|
||||
},
|
||||
// … // 其它属性
|
||||
|
@ -277,7 +277,7 @@ layui.use(['table', 'dropdown'], function(){
|
||||
console.log(obj);
|
||||
if(event === 'email-tips'){
|
||||
layer.alert(layui.util.escape(JSON.stringify(obj.col)), {
|
||||
title: '当前列属性配置项'
|
||||
title: '当前列属性选项'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -29,7 +29,7 @@ layui.use(['table', 'dropdown', 'util'], function(){
|
||||
var dataCache = obj.dataCache; // 得到当前行缓存数据,包含特定字段 --- 2.8.8+
|
||||
var index = obj.index; // 得到当前行索引
|
||||
var tr = obj.tr; // 得到当前行 <tr> 元素的 jQuery 对象
|
||||
var options = obj.config; // 获取当前表格基础属性配置项
|
||||
var options = obj.config; // 获取当前表格基础属性选项
|
||||
var e = obj.e; // 当前的 jQuery 事件对象 --- 2.9.14+
|
||||
console.log('rowContextmenu', obj); // 查看返回对象的所有成员
|
||||
|
||||
|
@ -42,7 +42,7 @@ toc: true
|
||||
|
||||
<h3 id="set" class="ws-anchor ws-bold">全局设置</h3>
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法主要用于初始化设置属性默认值。实际应用时,必须先设置该方法,再执行渲染、重载等操作。
|
||||
|
||||
@ -74,7 +74,7 @@ table 提供了以下三种渲染模式,在实际使用时,一般按情况
|
||||
|
||||
`table.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法返回当前实例对象,包含可操作当前表格的一些成员方法。
|
||||
|
||||
@ -119,7 +119,7 @@ layui.use(function(){
|
||||
</table>
|
||||
```
|
||||
|
||||
> 2.8 之前版本通过 `lay-data="{}"` 定义属性配置项;<br>
|
||||
> 2.8 之前版本通过 `lay-data="{}"` 定义属性选项;<br>
|
||||
> 2.8+ 版本推荐采用 `lay-options`,但同时兼容 `lay-data`。
|
||||
|
||||
|
||||
@ -128,7 +128,7 @@ layui.use(function(){
|
||||
`table.init(filter, options);`
|
||||
|
||||
- 参数 `filter` : `<table>` 元素对应的 `lay-filter` 属性值
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法用于将已输出在页面中的静态表格内容转换为动态 table 组件。[#参考相关示例](#demo-init)
|
||||
|
||||
@ -220,7 +220,7 @@ table 的属性众多,我们大致分为以下几种:
|
||||
`table.reload(id, options, deep);`
|
||||
|
||||
- 参数 `id` : table 渲染时的 `id` 属性值
|
||||
- 参数 `options` : 为基础属性配置项
|
||||
- 参数 `options` : 为基础属性选项
|
||||
- 参数 `deep` <sup>2.6+</sup> : 是否采用深度重载(即重载时始终携带初始时及上一次重载时的参数),默认 false。<div style="margin-top:5px;"><button type="button" class="layui-btn layui-btn-sm layui-btn-primary" lay-layer="{content: '#DOCS-table-reload-comp'}">2.6 之前版本的 <code>table.reload()</code> 方法兼容性说明</button></div>
|
||||
|
||||
<div style="display: none;" id="DOCS-table-reload-comp">
|
||||
@ -482,7 +482,7 @@ table.resize('test');
|
||||
`table.exportFile(id, data, opts);`
|
||||
- 参数 `id` : table 渲染时的 `id` **或** 要导出的数据表头(当 `id` 为 `array` 类型时)。
|
||||
- 参数 `data` : 要导出的自定义数据,参数可选。
|
||||
- 参数 `opts` <sup>2.7+</sup>: 导出数据时的属性可选项,支持: `type,title`。
|
||||
- 参数 `opts` <sup>2.7+</sup>: 导出数据时的属性选项,支持: `type,title`。
|
||||
|
||||
该方法用于外部导出对应 table 的数据和任意自定义数据。
|
||||
|
||||
@ -513,7 +513,7 @@ table.exportFile(['名字','性别','年龄'], [
|
||||
`table.getOptions(id);`
|
||||
- 参数 `id` : table 渲染时的 `id` 属性值
|
||||
|
||||
该方法用于外部获取对应 table 实例的属性配置项。
|
||||
该方法用于外部获取对应 table 实例的属性选项。
|
||||
|
||||
```
|
||||
// 渲染
|
||||
@ -622,7 +622,7 @@ layui.use(function(){
|
||||
|
||||
// 头部工具栏事件
|
||||
table.on('toolbar(test)', function(obj){
|
||||
var options = obj.config; // 获取当前表格属性配置项
|
||||
var options = obj.config; // 获取当前表格属性选项
|
||||
var checkStatus = table.checkStatus(options.id); // 获取选中行相关数据
|
||||
console.log(obj); // 查看对象所有成员
|
||||
|
||||
@ -697,8 +697,8 @@ table.render({
|
||||
|
||||
// 表头自定义元素工具事件
|
||||
table.on('colTool(test)', function(obj){
|
||||
var col = obj.col; // 获取当前列属性配置项
|
||||
var options = obj.config; // 获取当前表格基础属性配置项
|
||||
var col = obj.col; // 获取当前列属性选项
|
||||
var options = obj.config; // 获取当前表格基础属性选项
|
||||
var layEvent = obj.event; // 获得自定义元素对应的 lay-event 属性值
|
||||
console.log(obj); // 查看对象所有成员
|
||||
});
|
||||
@ -721,8 +721,8 @@ table.render({
|
||||
|
||||
// 列拖拽宽度后的事件
|
||||
table.on('colResized(test)', function(obj){
|
||||
var col = obj.col; // 获取当前列属性配置项
|
||||
var options = obj.config; // 获取当前表格基础属性配置项
|
||||
var col = obj.col; // 获取当前列属性选项
|
||||
var options = obj.config; // 获取当前表格基础属性选项
|
||||
console.log(obj); // 查看对象所有成员
|
||||
});
|
||||
```
|
||||
@ -744,8 +744,8 @@ table.render({
|
||||
|
||||
// 列筛选(显示或隐藏)后的事件
|
||||
table.on('colToggled(test)', function(obj){
|
||||
var col = obj.col; // 获取当前列属性配置项
|
||||
var options = obj.config; // 获取当前表格基础属性配置项
|
||||
var col = obj.col; // 获取当前列属性选项
|
||||
var options = obj.config; // 获取当前表格基础属性选项
|
||||
console.log(obj); // 查看对象所有成员
|
||||
});
|
||||
```
|
||||
@ -774,7 +774,7 @@ table.on('row(test)', function(obj) {
|
||||
var dataCache = obj.dataCache; // 得到当前行缓存数据,包含特定字段 --- 2.8.8+
|
||||
var index = obj.index; // 得到当前行索引
|
||||
var tr = obj.tr; // 得到当前行 <tr> 元素的 jQuery 对象
|
||||
var options = obj.config; // 获取当前表格基础属性配置项
|
||||
var options = obj.config; // 获取当前表格基础属性选项
|
||||
var e = obj.e; // 当前的 jQuery 事件对象 --- 2.9.14+
|
||||
|
||||
console.log('onrow', obj); // 查看返回对象的所有成员
|
||||
@ -887,7 +887,7 @@ layui.use(function(){
|
||||
var index = obj.index; // 得到当前行索引
|
||||
var layEvent = obj.event; // 获得元素对应的 lay-event 属性值
|
||||
var tr = obj.tr; // 得到当前行 <tr> 元素的 jQuery 对象
|
||||
var options = obj.config; // 获取当前表格基础属性配置项
|
||||
var options = obj.config; // 获取当前表格基础属性选项
|
||||
var col = obj.getCol(); // 得到当前列的表头配置属性 -- v2.8.3 新增
|
||||
console.log(obj); // 查看对象所有成员
|
||||
|
||||
|
@ -30,7 +30,9 @@
|
||||
|
||||
<h3 id="demo-trigger" lay-toc="{level: 2}">自定义事件</h3>
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full']}">
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full'], done: function(obj){
|
||||
obj.render();
|
||||
}}">
|
||||
<textarea>
|
||||
{{- d.include("/tabs/examples/trigger.md") }}
|
||||
</textarea>
|
||||
@ -40,7 +42,9 @@
|
||||
|
||||
切换 tabs 标签项后,地址栏同步 `hash` 值,当页面刷新时,tabs 仍保持对应的切换状态。
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full']}">
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full'], done: function(obj){
|
||||
obj.render();
|
||||
}}">
|
||||
<textarea>
|
||||
{{- d.include("/tabs/examples/hash.md") }}
|
||||
</textarea>
|
||||
@ -48,7 +52,9 @@
|
||||
|
||||
<h3 id="demo-nest" lay-toc="{level: 2}">标签嵌套</h3>
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full']}">
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full'], done: function(obj){
|
||||
obj.render();
|
||||
}}">
|
||||
<textarea>
|
||||
{{- d.include("/tabs/examples/nest.md") }}
|
||||
</textarea>
|
||||
|
@ -35,7 +35,7 @@ toc: true
|
||||
|
||||
`tabs.render(options)`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
组件支持以下三种渲染方式:
|
||||
|
||||
|
@ -27,7 +27,7 @@ toc: true
|
||||
|
||||
`transfer.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
<h3 id="options" lay-toc="{level: 2, hot: true}">属性</h3>
|
||||
|
||||
@ -46,7 +46,7 @@ toc: true
|
||||
`transfer.reload(id, options);`
|
||||
|
||||
- 参数 `id` : 对应渲染时定义的 `id` 属性值
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
```
|
||||
var transfer = layui.transfer;
|
||||
@ -89,7 +89,7 @@ var getData = transfer.getData('test');
|
||||
|
||||
`transfer.set(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法对所有的 `transfer` 实例有效,设置的属性优先级低于 `transfer.render(options)`
|
||||
|
||||
@ -99,4 +99,4 @@ transfer.set({
|
||||
height: 'auto', // 所有穿梭框默认高度为自动
|
||||
// …
|
||||
});
|
||||
```
|
||||
```
|
||||
|
@ -27,7 +27,7 @@ toc: true
|
||||
|
||||
`tree.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
<h3 id="options" lay-toc="{level: 2, hot: true}">属性</h3>
|
||||
|
||||
@ -109,7 +109,7 @@ tree.setChecked('test', [1, 3]); // 批量勾选 id 为 1,3 的节点
|
||||
`tree.reload(id, idArr);`
|
||||
|
||||
- 参数 `id` : 对应 tree 渲染时定义的 id 属性值
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
```
|
||||
var tree = layui.tree;
|
||||
@ -125,4 +125,4 @@ tree.render({
|
||||
tree.reload('test', { // options
|
||||
data: []
|
||||
});
|
||||
```
|
||||
```
|
||||
|
@ -124,7 +124,7 @@ treeTable.render({
|
||||
|
||||
| 属性 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| enable | 是否开启异步加载模式。只有开启时 `async` 的其他属性配置才有效。 **注意:** 异步加载子节点不应跟 `simpleData` 同时开启,可以是 `url+simpleData` 的方式,获取完整的简单数据进行转换。若开启异步加载模式,即表示按需异步加载子节点。 | boolean | `false` |
|
||||
| enable | 是否开启异步加载模式。只有开启时 `async` 的其他属性选项才有效。 **注意:** 异步加载子节点不应跟 `simpleData` 同时开启,可以是 `url+simpleData` 的方式,获取完整的简单数据进行转换。若开启异步加载模式,即表示按需异步加载子节点。 | boolean | `false` |
|
||||
| url | 异步加载的接口,可以根据需要设置与顶层接口不同的接口,若相同可不设置该属性 | string | - |
|
||||
| [format](#options.tree.async.format) | 用于处理异步子节点数据的回调函数,该属性优先级高于 `async.url` 属性。用法详见下文。 | function | - |
|
||||
| type | 请求的接口类型,设置可缺省同上 | string | - |
|
||||
@ -146,7 +146,7 @@ treeTable.render({
|
||||
enable: true,
|
||||
async: {
|
||||
format: function(trData, options, callback){
|
||||
// trData 为行数据、options 为 treeTable 属性配置项
|
||||
// trData 为行数据、options 为 treeTable 属性选项
|
||||
// callbacck 为子节点的渲染函数
|
||||
// 可利用该函数对子节点数据进行异步请求或其他格式化处理
|
||||
var nodeList = [
|
||||
|
@ -47,7 +47,7 @@ toc: true
|
||||
|
||||
`treeTable.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该组件渲染的使用方式与 `table` 组件完全相同。
|
||||
|
||||
@ -69,7 +69,7 @@ toc: true
|
||||
| 仅数据重载 | treeTable.reloadData(id, options) |
|
||||
|
||||
- 参数 `id` : treeTable 渲染时的 id 属性值
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
使用方式与 `table` 组件完全相同,具体用法可参考:[table 重载](../table/#reload)
|
||||
|
||||
@ -144,7 +144,7 @@ console.log(obj);
|
||||
|
||||
- 参数 `id` : treeTable 渲染时的 `id` 属性值
|
||||
- 参数 `filter` : 过滤函数
|
||||
- 参数 `opts` : 该方法的属性可选项,详见下表:
|
||||
- 参数 `opts` : 该方法的属性选项,详见下表:
|
||||
|
||||
| 属性名 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
|
@ -27,13 +27,13 @@ toc: true
|
||||
| [var inst = upload.render(options)](#render) | upload 组件渲染,核心方法。 |
|
||||
| [inst.upload()](#upload) | 对当前实例提交上传 |
|
||||
| [inst.reload(options)](#reload) | 对当前实例进行重载 |
|
||||
| inst.config | 获得当前实例的属性配置项 |
|
||||
| inst.config | 获得当前实例的属性选项 |
|
||||
|
||||
<h3 id="render" lay-toc="{level: 2, hot: true}">渲染</h3>
|
||||
|
||||
`upload.render(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
<br>注 : 除 `elem` 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中。
|
||||
|
||||
```
|
||||
@ -94,7 +94,7 @@ var inst = upload.render({
|
||||
|
||||
`inst.reload(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
- 参数 `options` : 基础属性选项。[#详见属性](#options)
|
||||
|
||||
该方法用于对当前的上传实例进行完整重载,所有属性均可参与到重载中。
|
||||
|
||||
|
@ -34,7 +34,7 @@ toc: true
|
||||
|
||||
`util.countdown(options);`
|
||||
|
||||
- 参数 `options` <sup>2.8.9+</sup>: 属性配置项。可选项详见下表:
|
||||
- 参数 `options` <sup>2.8.9+</sup>: 属性选项。可选项详见下表:
|
||||
|
||||
| 属性 | 描述 |
|
||||
| --- | --- |
|
||||
@ -101,7 +101,7 @@ var result = util.timeAgo(1672531200000); // 2023-01-01 00:00:00
|
||||
|
||||
- 参数 `time` : 毫秒数或日期对象
|
||||
- 参数 `format` : 日期字符格式。默认格式:`yyyy-MM-dd HH:mm:ss` 。可自定义,如: `yyyy年MM月dd日`
|
||||
- 参数 `options` <sup>2.8.13+</sup> : 该方法的属性可选项,详见下表:
|
||||
- 参数 `options` <sup>2.8.13+</sup> : 该方法的属性选项,详见下表:
|
||||
|
||||
| 属性名 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
@ -179,7 +179,7 @@ var str2 = util.unescape('<div>123</div>'); // 返回: <div>123</d
|
||||
|
||||
`util.openWin(options);`
|
||||
|
||||
- 参数 `options` : 属性配置项。可选项详见下表
|
||||
- 参数 `options` : 属性选项。可选项详见下表
|
||||
|
||||
| 属性 | 描述 |
|
||||
| --- | --- |
|
||||
|
@ -5,10 +5,26 @@ toc: true
|
||||
|
||||
# 更新日志
|
||||
|
||||
> 导读:📑 [Layui 2.8+ 《升级指南》](/notes/2.8/upgrade-guide.html) · 📑 [Layui 新版文档站上线初衷](/notes/2.8/news.html)
|
||||
> 导读:📑 [Layui 2.x 系列版本主要升级变化](/notes/share/2x-major-upgrade-changes.html) · 📑 [Layui 2.8+ 《升级指南》](/notes/2.8/upgrade-guide.html) · 📑 [Layui 新版文档站上线初衷](/notes/2.8/news.html)
|
||||
|
||||
<h2 id="2.10+" lay-toc="{title: '2.10+'}"></h2>
|
||||
|
||||
<h2 id="v2.10.1" class="ws-anchor">
|
||||
v2.10.1
|
||||
<span class="layui-badge-rim">2025-03-19</span>
|
||||
</h2>
|
||||
|
||||
- #### component
|
||||
- 修复 `reload` 时传入的选项未正确合并的问题 #2566 @sentsim
|
||||
- 优化 `lay-options` 属性上的配置在重载时的优先级 #2566 @sentsim
|
||||
- #### 其他
|
||||
- 优化 tabs `reload` 未按照 `closable` 正确渲染可关闭状态的问题 #2566 @sentsim
|
||||
- 优化 form 的 `checkbox` 标签风格选中且禁用时的显示 #2563 @bxjt123
|
||||
- 修复 body 初始 `line-height` 无效的问题 #2569 @sentsim
|
||||
|
||||
### 下载: [layui-v2.10.1.zip](https://gitee.com/layui/layui/attach_files/2100525/download)
|
||||
|
||||
---
|
||||
|
||||
<h2 id="v2.10.0" class="ws-anchor">
|
||||
v2.10.0
|
||||
@ -49,6 +65,10 @@ toc: true
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
<h2 id="2.9.x" lay-toc="{title: '2.9.x'}"></h2>
|
||||
|
||||
<h2 id="v2.9.25" class="ws-anchor">
|
||||
v2.9.25
|
||||
<span class="layui-badge-rim">2025-03-13</span>
|
||||
@ -62,8 +82,6 @@ toc: true
|
||||
|
||||
---
|
||||
|
||||
<h2 id="2.9.x" lay-toc="{title: '2.9.x'}"></h2>
|
||||
|
||||
<h2 id="v2.9.24" class="ws-anchor">
|
||||
v2.9.24
|
||||
<span class="layui-badge-rim">2025-03-07</span>
|
||||
|
@ -1,74 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>视图模板引擎 - layui</title>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>模板引擎 - Layui</title>
|
||||
<link rel="stylesheet" href="../src/css/layui.css">
|
||||
<style>
|
||||
.laytpl-demo{border: 1px solid #eee;}
|
||||
.laytpl-demo:first-child{border-right: none;}
|
||||
.laytpl-demo>textarea{position: relative; display: block; width:100%; height: 300px; padding: 11px; border: 0; box-sizing: border-box; resize: none; background-color: #fff; font-family: Courier New; font-size: 13px;}
|
||||
.laytpl-demo>div:first-child{height: 32px; line-height: 32px; padding: 6px 11px; border-bottom: 1px solid #eee; background-color: #F8F9FA;}
|
||||
.laytpl-demo .layui-tabs{top: -1px;}
|
||||
|
||||
<link rel="stylesheet" href="../src/css/layui.css">
|
||||
<style>
|
||||
.laytpl-demo{border: 1px solid #EBEBEB;}
|
||||
.laytpl-demo>textarea{position: relative; display: block; width:100%; height: 300px; padding: 11px; border: 0; box-sizing: border-box; resize: none; background-color: #fff; font-family: Courier New; font-size: 13px;}
|
||||
.laytpl-demo>div:first-child{height: 32px; line-height: 32px; padding: 6px 11px; border-bottom: 1px solid #EBEBEB; background-color: #F8F9FA;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs6 laytpl-demo">
|
||||
<div>模板</div>
|
||||
<textarea id="demoTPL1"><h1>{{ d.title }}</h1>
|
||||
|
||||
<p>转义输出(HTML):{{ d.desc }}</p>
|
||||
<p>转义输出(HTML):{{= d.desc }}</p>
|
||||
<p>原始输出(HTML):{{- d.desc }}</p>
|
||||
{{#}}
|
||||
|
||||
<div class="layui-section">
|
||||
<hr>
|
||||
<ul>
|
||||
{{# var str = "a b c";
|
||||
layui.each(d.items, function(index, item){ }}
|
||||
<li class="{{ index > 0 ? 'list' : '' }}">
|
||||
<strong>{{ item.title }}</strong>
|
||||
{{# if(item.content){ }}
|
||||
<span>{{ item.content }}</span>
|
||||
{{# } }}
|
||||
<span>{{ item.time || '' }}</span>
|
||||
{{ str }}
|
||||
|
||||
|
||||
</li>
|
||||
{{# }); if(d.items.length === 0){ }}
|
||||
无数据
|
||||
{{# } }}
|
||||
</ul>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ d.content || '' }}
|
||||
{{ }} {{}}
|
||||
{{ }}
|
||||
|
||||
\a
|
||||
'12'"""""
|
||||
"哈''哈"
|
||||
</div>
|
||||
|
||||
<p>渲染时间:{{ layui.util.toDateString(new Date()) }}</p></textarea>
|
||||
</div>
|
||||
|
||||
<div class="layui-col-xs6 laytpl-demo">
|
||||
#ID-tpl-view-body {
|
||||
height: calc(100vh - 430px); overflow: auto; clear: both;
|
||||
}
|
||||
#ID-tpl-view-body > div {
|
||||
display: none;
|
||||
}
|
||||
.laytpl-demo pre {
|
||||
padding: 16px; background-color: #20222A; color: #F8F9FA; font-family: 'Courier New',Consolas, monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="layui-padding-3">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs3">数据</div>
|
||||
<div class="layui-col-xs9" style="text-align: right">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-primary" lay-on="createData">生成数据</button>
|
||||
<div class="layui-col-xs6 laytpl-demo">
|
||||
<div>
|
||||
<a href="javascript:;" id="ID-tpl-src-title">
|
||||
<cite><strong>模板</strong></cite>
|
||||
<i class="layui-icon layui-icon-down layui-font-12"></i>
|
||||
</a>
|
||||
</div>
|
||||
<textarea id="ID-tpl-src"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="demoData1">
|
||||
<div class="layui-col-xs6 laytpl-demo">
|
||||
<div class="layui-row">
|
||||
<div><strong>数据</strong></div>
|
||||
</div>
|
||||
<textarea id="ID-tpl-data">
|
||||
{
|
||||
"title": "标题",
|
||||
"desc": "<a href=\"\" style=\"color:blue;\">一段描述</a>",
|
||||
@ -91,156 +62,316 @@
|
||||
{"title": "list 3"}
|
||||
]
|
||||
}</textarea>
|
||||
</div>
|
||||
<div class="layui-col-xs12 laytpl-demo">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs4">视图</div>
|
||||
<div class="layui-col-xs4" style="text-align: center">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-primary" lay-on="test1">性能测试</button>
|
||||
</div>
|
||||
<div class="layui-col-xs4" style="text-align: right">
|
||||
<span id="demoViewTime"></span>
|
||||
<div class="layui-col-xs12 laytpl-demo" style="border-top: none;">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs4 layui-tabs" id="ID-tpl-view-header">
|
||||
<ul class="layui-tabs-header">
|
||||
<li><strong>渲染结果</strong></li>
|
||||
<li><strong>源码</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="layui-col-xs4" style="text-align: center">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-border" lay-on="test">性能测试</button>
|
||||
</div>
|
||||
<div class="layui-col-xs4" style="text-align: right">
|
||||
<span class="layui-badge" id="ID-tpl-view-time"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ID-tpl-view-body">
|
||||
<div class="layui-show layui-padding-3 layui-text" id="ID-tpl-view"></div>
|
||||
<div><pre id="ID-tpl-view-code"></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-padding-sm" id="demoView1"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script type="type/html" template id="demoTplCommon">
|
||||
公共模板 - {{ d.title }}
|
||||
</script>
|
||||
|
||||
<script type="type/html" template id="demoTplList">
|
||||
{{# if(d.items && d.items.length > 0){ }}
|
||||
<ul>
|
||||
{{# layui.each(d.items, function(index, item){ }}
|
||||
<li><strong>{{ item.title }}</strong>{{ laytpl.include('demoTplList', {items: item.child}) }}</li>
|
||||
{{# }); }}
|
||||
</ul>
|
||||
{{# } }}
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<script type="type/html" template id="laytplTestTpl">
|
||||
{{# for(var i = 0; i < d.items.length; i++){ }}
|
||||
第{{= d.items[i].index }}个,Name: {{- d.items[i].name }} Number: {{= d.items[i].number }}
|
||||
{{# } }}
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="ID-tpl-template-modern">
|
||||
<h2>
|
||||
{{= d.title }} - {{= d.title ? '#' : '' }}
|
||||
{{ if(true){ }}AAAA{{='A'}}{{ } }}
|
||||
</h2>
|
||||
{{- include('ID-tpl-template-common', {title: '头部'}) }}
|
||||
|
||||
<hr>
|
||||
|
||||
<p>转义输出:{{= d.desc }}</p>
|
||||
<p>原文输出:{{- d.desc }}</p>
|
||||
|
||||
{{# 这是一段注释。仅在模板中显示,不在视图中输出 }}
|
||||
|
||||
{{!
|
||||
这是一段不进行模板解析的区域,可显示原始标签:
|
||||
{{ let a = 0; }}、{{= escape }}、{{- source }}、{{# comments }}、{{! ignore !}}
|
||||
!}}
|
||||
|
||||
{{# 空主体测试 }}
|
||||
{{}} {{ }} {{ }} {{= }} {{=}} {{= }}
|
||||
|
||||
<div>
|
||||
{{- !0 ? '<br>循环输出:' : '' }}
|
||||
<hr>
|
||||
<ul>
|
||||
{{
|
||||
var str = "一级列表 a \\ b c";
|
||||
d.items.forEach(function(value, index) {
|
||||
}}
|
||||
<li class="{{= index > 0 ? 'list' : '' }}">
|
||||
<strong>{{= value.title }}</strong>
|
||||
{{ if(value.content){ }}
|
||||
<span>{{= value.content }}</span>
|
||||
{{ } }}
|
||||
<span>{{= value.time || '' }}</span>
|
||||
{{= str }}
|
||||
{{- include('ID-tpl-template-list', { items: value.child }) }}
|
||||
</li>
|
||||
{{ }); }}
|
||||
{{ if(d.items.length === 0){ }}
|
||||
无数据
|
||||
{{ } }}
|
||||
</ul>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<script src="../src/layui.js"></script>
|
||||
<script>
|
||||
layui.use(['laytpl', 'util'], function(){
|
||||
var laytpl = layui.laytpl;
|
||||
var util = layui.util;
|
||||
var $ = layui.$;
|
||||
|
||||
// 获取模板和数据
|
||||
var get = function(type){
|
||||
return {
|
||||
template: $('#demoTPL1').val(), // 获取模板
|
||||
data: function(){ // 获取数据
|
||||
try {
|
||||
return JSON.parse($('#demoData1').val());
|
||||
} catch(e){
|
||||
$('#demoView1').html(e);
|
||||
}
|
||||
}()
|
||||
};
|
||||
};
|
||||
|
||||
var data = get();
|
||||
|
||||
// 耗时计算
|
||||
var startTime = new Date().getTime(), timer = function(startTime, title){
|
||||
var endTime = new Date().getTime();
|
||||
$('#demoViewTime').html((title || '模板解析耗时:')+ (endTime - startTime) + 'ms');
|
||||
};
|
||||
<div>
|
||||
{{= d.content || '' }}
|
||||
\反斜杠 | '单引号' "双引号" ""''"" | "左双右单' | '左单右双"
|
||||
</div>
|
||||
|
||||
// 全局设置
|
||||
/*laytpl.config({
|
||||
open: '<%',
|
||||
close: '%>'
|
||||
});*/
|
||||
<p>渲染时间:{{= layui.util.toDateString(new Date()) }}</p>
|
||||
</script>
|
||||
|
||||
// 渲染模板
|
||||
var thisTpl = laytpl(data.template);
|
||||
<script type="text/html" id="ID-tpl-template-common">
|
||||
公共模板 - {{= d.title }}
|
||||
</script>
|
||||
|
||||
// 执行渲染
|
||||
thisTpl.render(data.data, function(view){
|
||||
timer(startTime);
|
||||
$('#demoView1').html(view);
|
||||
});
|
||||
|
||||
// 编辑
|
||||
$('.laytpl-demo textarea').on('input', function(){
|
||||
var data = get();
|
||||
if(!data.data) return;
|
||||
|
||||
// 计算模板渲染耗时
|
||||
var startTime = new Date().getTime();
|
||||
|
||||
// 若模板有变化,则重新解析模板;若模板没变,数据有变化,则从模板缓存中直接渲染(效率大增)
|
||||
if(this.id === 'demoTPL1'){
|
||||
thisTpl.parse(data.template, data.data); // 解析模板
|
||||
}
|
||||
|
||||
// 执行渲染
|
||||
thisTpl.render(data.data, function(view){
|
||||
timer(startTime);
|
||||
$('#demoView1').html(view);
|
||||
});
|
||||
});
|
||||
|
||||
// 事件
|
||||
util.on({
|
||||
// 性能测试
|
||||
test1: function(){
|
||||
var dataLen = 1000 // 数据量
|
||||
var renderTimes = 1000; // 渲染次数
|
||||
|
||||
// 初始化数据
|
||||
var data = {
|
||||
title: '性能测试',
|
||||
items: function(items){
|
||||
for(var i = 0; i < dataLen; i++){
|
||||
items.push({
|
||||
index: i
|
||||
,name: '<strong style="color: red;">张三</strong>'
|
||||
,number: 100+i
|
||||
<script type="text/html" id="ID-tpl-template-list">
|
||||
{{ if(d.items && d.items.length > 0){ }}
|
||||
<ul>
|
||||
{{ layui.each(d.items, function(index, item){ }}
|
||||
<li>
|
||||
<strong>{{= item.title }}</strong>
|
||||
{{- include('ID-tpl-template-list', {items: item.child}) }}
|
||||
</li>
|
||||
{{ }); }}
|
||||
</ul>
|
||||
{{ } }}
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="ID-tpl-template-test">
|
||||
{{ for (var i = 0; i < d.items.length; i++) { }}
|
||||
第 {{= d.items[i].index }} 个,Name: {{- d.items[i].name }} Number: {{= d.items[i].number }}
|
||||
{{ } }}
|
||||
</script>
|
||||
|
||||
<!-- 旧版本模板 -->
|
||||
<script type="text/html" id="ID-tpl-template-legacy">
|
||||
<h2>
|
||||
{{= d.title }} - {{= d.title ? '#' : '' }}
|
||||
{{# if(true){ }}AAAA{{='A'}}{{# } }}
|
||||
</h2>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>转义输出:{{ d.desc }}</p>
|
||||
<p>转义输出:{{= d.desc }}</p>
|
||||
<p>原文输出:{{- d.desc }}</p>
|
||||
|
||||
{{}} {{ }} {{ }} {{= }} {{=}} {{= }}
|
||||
|
||||
<div>
|
||||
<ul>
|
||||
{{# var str = "a b c"; }}
|
||||
{{# layui.each(d.items, function(index, item) { }}
|
||||
<li class="{{= index > 0 ? 'list' : '' }}">
|
||||
<strong>{{= item.title }}</strong>
|
||||
{{# if(item.content){ }}
|
||||
<span>{{= item.content }}</span>
|
||||
{{# } }}
|
||||
<span>{{= item.time || '' }}</span>
|
||||
{{ str }}
|
||||
</li>
|
||||
{{# }); }}
|
||||
{{# if (d.items.length === 0) { }}
|
||||
无数据
|
||||
{{# } }}
|
||||
</ul>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{= d.content || '' }}
|
||||
\反斜杠 | '单引号' "双引号" ""''"" | "左双右单' | '左单右双"
|
||||
</div>
|
||||
|
||||
<p>渲染时间:{{ layui.util.toDateString(new Date()) }}</p>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<script src="../src/layui.js"></script>
|
||||
<script>
|
||||
layui.use(['laytpl', 'util', 'tabs', 'dropdown'], function() {
|
||||
var laytpl = layui.laytpl;
|
||||
var util = layui.util;
|
||||
var tabs = layui.tabs;
|
||||
var dropdown = layui.dropdown;
|
||||
var $ = layui.$;
|
||||
|
||||
// 默认设置
|
||||
laytpl.config({
|
||||
tagStyle: 'modern' // 初始采用新版标签风格
|
||||
})
|
||||
|
||||
// 获取模板和数据
|
||||
var getData = function(type) {
|
||||
return {
|
||||
template: $('#ID-tpl-src').val(), // 获取模板
|
||||
data: function(){ // 获取数据
|
||||
try {
|
||||
return JSON.parse($('#ID-tpl-data').val());
|
||||
} catch(e) {
|
||||
$('#ID-tpl-view').html(e);
|
||||
}
|
||||
}()
|
||||
};
|
||||
};
|
||||
|
||||
// 视图渲染
|
||||
var renderView = function(html, startTime) {
|
||||
timer(startTime);
|
||||
$('#ID-tpl-view').html(html);
|
||||
$('#ID-tpl-view-code').html(util.escape(html));
|
||||
};
|
||||
|
||||
// 生成模板
|
||||
var createTemplate = function(opts) {
|
||||
opts = $.extend({
|
||||
tagStyle: 'modern'
|
||||
}, opts);
|
||||
|
||||
// 初始化模板
|
||||
var elem = $('#ID-tpl-template-'+ opts.tagStyle);
|
||||
$('#ID-tpl-src').val(elem.html());
|
||||
|
||||
return opts;
|
||||
};
|
||||
var tplConfig = createTemplate();
|
||||
var data = getData();
|
||||
|
||||
// 耗时计算
|
||||
var timer = function(startTime, title) {
|
||||
var endTime = new Date();
|
||||
$('#ID-tpl-view-time').html((title || '模板解析耗时:')+ (endTime - startTime) + 'ms');
|
||||
};
|
||||
var startTime = new Date();
|
||||
|
||||
// 创建一个模板实例
|
||||
var templateInst = laytpl(data.template, {
|
||||
condense: false, // 不处理连续空白符,即保留模板原始结构
|
||||
tagStyle: tplConfig.tagStyle
|
||||
});
|
||||
|
||||
// 初始渲染
|
||||
templateInst.render(data.data, function(html) {
|
||||
renderView(html, startTime);
|
||||
});
|
||||
|
||||
// 编辑
|
||||
$('.laytpl-demo textarea').on('input', function() {
|
||||
var data = getData();
|
||||
var startTime = new Date();
|
||||
|
||||
// 若模板有变化,则重新编译模板
|
||||
if (this.id === 'ID-tpl-src') {
|
||||
templateInst.compile(data.template);
|
||||
}
|
||||
|
||||
// 若模板没变,数据有变化,则从模板缓存中直接渲染数据(效率大增)
|
||||
templateInst.render(data.data, function(html) {
|
||||
renderView(html, startTime);
|
||||
});
|
||||
});
|
||||
|
||||
// 事件
|
||||
util.on({
|
||||
// 性能测试
|
||||
test: function() {
|
||||
var dataLen = 1000 // 数据量
|
||||
var renderTimes = 1000; // 渲染次数
|
||||
|
||||
// 初始化数据
|
||||
var data = {
|
||||
title: '性能测试',
|
||||
items: function(items) {
|
||||
for (var i = 0; i < dataLen; i++) {
|
||||
items.push({
|
||||
index: i,
|
||||
name: '<strong style="color: red;">张三</strong>',
|
||||
number: 100+i
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}([])
|
||||
};
|
||||
|
||||
// 模板
|
||||
var startTime = new Date();
|
||||
for (var j = 0; j < renderTimes; j++) {
|
||||
var template = document.getElementById('ID-tpl-template-test').innerHTML;
|
||||
var html = laytpl(template).render(data);
|
||||
}
|
||||
renderView(html, startTime);
|
||||
}
|
||||
});
|
||||
|
||||
// 局部自定义标签符
|
||||
laytpl(`
|
||||
<% var job = ["前端工程师"]; %>
|
||||
<%= d.name %>是一名<%= job[d.index] %>。
|
||||
`, {
|
||||
open: '<%',
|
||||
close: '%>'
|
||||
}).render({
|
||||
name: '张三',
|
||||
index: 0
|
||||
}, function(str) {
|
||||
console.log(str); // 张三是一名前端工程师。
|
||||
});
|
||||
|
||||
// 视图结果 tabs
|
||||
tabs.render({
|
||||
elem: '#ID-tpl-view-header',
|
||||
body: ['#ID-tpl-view-body', '>div']
|
||||
});
|
||||
|
||||
// 切换模板
|
||||
dropdown.render({
|
||||
elem: '#ID-tpl-src-title',
|
||||
data: [{
|
||||
title: '新版本模板',
|
||||
tagStyle: 'modern'
|
||||
}, {
|
||||
title: '旧版本模板',
|
||||
tagStyle: 'legacy'
|
||||
}],
|
||||
click: function(obj){
|
||||
createTemplate({
|
||||
tagStyle: obj.tagStyle
|
||||
});
|
||||
this.elem.children('cite').html(obj.title);
|
||||
|
||||
// 同步设置标签风格
|
||||
templateInst.config.tagStyle = obj.tagStyle;
|
||||
|
||||
var data = getData();
|
||||
var startTime = new Date();
|
||||
|
||||
// 重新渲染
|
||||
templateInst.compile(data.template).render(data.data, function(html) {
|
||||
renderView(html, startTime);
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}([])
|
||||
};
|
||||
|
||||
// 模板
|
||||
var startTime = new Date();
|
||||
for(var j = 0; j < renderTimes; j++){
|
||||
var template = document.getElementById('laytplTestTpl').innerHTML;
|
||||
var html = laytpl(template).render(data);
|
||||
}
|
||||
timer(startTime, '本次测试耗时:');
|
||||
$('#demoView1').html(html);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// 自定义标签符
|
||||
laytpl(`
|
||||
<%# var job = ["前端工程师"]; %>
|
||||
<%= d.name %>是一名<%= job[d.type] %>。
|
||||
`, {
|
||||
open: '<%',
|
||||
close: '%>'
|
||||
}).render({
|
||||
name: '张三',
|
||||
type: 0
|
||||
}, function(str){
|
||||
console.log(str); // 张三是一名前端工程师。
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "layui",
|
||||
"version": "2.10.0",
|
||||
"version": "2.10.1",
|
||||
"description": "Classic modular Front-End UI library",
|
||||
"keywords": [
|
||||
"layui",
|
||||
|
@ -18,7 +18,7 @@ input,button,textarea,select,optgroup,option{font-family: inherit; font-size: in
|
||||
pre{white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word;}
|
||||
|
||||
/** 初始化全局标签 **/
|
||||
body{line-height: 1.6; color: #333; color: rgba(0,0,0,.85); font: 14px Helvetica Neue,Helvetica,PingFang SC,Tahoma,Arial,sans-serif;}
|
||||
body{line-height: 1.6; color: rgba(0,0,0,.85); font-size: 14px; font-family: Helvetica Neue,Helvetica,PingFang SC,Tahoma,Arial,sans-serif;}
|
||||
hr{height: 0; line-height: 0; margin: 10px 0; padding: 0; border: none; border-bottom: 1px solid #eee; clear: both; overflow: hidden; background: none;}
|
||||
a{color: #333; text-decoration: none;}
|
||||
a cite{font-style: normal;}
|
||||
@ -930,6 +930,8 @@ hr.layui-border-black{border-width: 0 0 1px;}
|
||||
.layui-checkbox-disabled > div{color: #c2c2c2!important;}
|
||||
.layui-checkbox-disabled > i{border-color: #eee !important;}
|
||||
.layui-checkbox-disabled:hover > i{color: #fff !important;}
|
||||
.layui-form-checkbox[lay-skin="tag"].layui-form-checked.layui-checkbox-disabled > i{color:#c2c2c2;}
|
||||
.layui-form-checkbox[lay-skin="tag"].layui-form-checked.layui-checkbox-disabled:hover > i{color: #c2c2c2!important;}
|
||||
|
||||
/* 单选框 */
|
||||
.layui-form-radio{display: inline-block; vertical-align: middle; line-height: 28px; margin: 6px 10px 0 0; padding-right: 10px; cursor: pointer; font-size: 0;}
|
||||
@ -1317,6 +1319,7 @@ body .layui-table-tips .layui-layer-content{background: none; padding: 0; box-sh
|
||||
|
||||
.layui-tabs-header li .layui-tabs-close{position: relative; display: inline-block; width: 16px; height: 16px; line-height: 18px; margin-left: 8px; top: 0px; text-align: center; font-size: 12px; color: #959595; border-radius: 50%; font-weight: 700; transition: all .16s; -webkit-transition: all .16s;}
|
||||
.layui-tabs-header li .layui-tabs-close:hover{ background-color: #ff5722; color: #fff;}
|
||||
.layui-tabs-header li[lay-closable="false"] .layui-tabs-close{display: none;}
|
||||
|
||||
.layui-tabs-body{padding: 16px 0;}
|
||||
.layui-tabs-item{display: none;}
|
||||
|
@ -20,7 +20,6 @@ layui.define(['jquery', 'lay'], function(exports) {
|
||||
|
||||
// 组件名
|
||||
var MOD_NAME = settings.name;
|
||||
var MOD_INDEX = 'layui_'+ MOD_NAME +'_index'; // 组件索引名
|
||||
var MOD_ID = 'lay-' + MOD_NAME + '-id'; // 用于记录组件实例 id 的属性名
|
||||
|
||||
// 组件基础对外接口
|
||||
@ -31,7 +30,7 @@ layui.define(['jquery', 'lay'], function(exports) {
|
||||
// 通用常量集,一般存放固定字符,如类名等
|
||||
CONST: $.extend(true, {
|
||||
MOD_NAME: MOD_NAME,
|
||||
MOD_INDEX: MOD_INDEX,
|
||||
MOD_ID: MOD_ID,
|
||||
|
||||
CLASS_THIS: 'layui-this',
|
||||
CLASS_SHOW: 'layui-show',
|
||||
@ -103,7 +102,7 @@ layui.define(['jquery', 'lay'], function(exports) {
|
||||
// 重载实例
|
||||
Class.prototype.reload = function(options, type) {
|
||||
var that = this;
|
||||
$.extend(settings.isDeepReload, that.config, options);
|
||||
that.config = $.extend(settings.isDeepReload, {}, that.config, options);
|
||||
that.init(true, type);
|
||||
};
|
||||
|
||||
@ -124,7 +123,13 @@ layui.define(['jquery', 'lay'], function(exports) {
|
||||
}
|
||||
|
||||
// 合并 lay-options 属性上的配置信息
|
||||
$.extend(true, options, lay.options(elem[0]));
|
||||
var layOptions = lay.options(elem[0]);
|
||||
if (rerender) {
|
||||
// 若重载渲染,则重载传入的 options 配置优先
|
||||
options = that.config = $.extend(layOptions, options);
|
||||
} else {
|
||||
$.extend(options, layOptions); // 若首次渲染,则 lay-options 配置优先
|
||||
}
|
||||
|
||||
// 若重复执行 render,则视为 reload 处理
|
||||
if (!rerender && elem.attr(MOD_ID)) {
|
||||
@ -173,21 +178,39 @@ layui.define(['jquery', 'lay'], function(exports) {
|
||||
Class.prototype.render = settings.render; // 渲染
|
||||
Class.prototype.events = settings.events; // 事件
|
||||
|
||||
// 元素操作缓存
|
||||
Class.prototype.cache = function(key, value) {
|
||||
/**
|
||||
* 元素缓存操作
|
||||
* @param {string} key - 缓存键
|
||||
* @param {*} value - 缓存值
|
||||
* @param {boolean} remove - 是否删除缓存
|
||||
* @returns {*} - 若 value 未传,则返回缓存值
|
||||
*/
|
||||
Class.prototype.cache = function(key, value, remove) {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var elem = options.elem;
|
||||
|
||||
var MOD_CACHE_NAME = MOD_ID + '-cache';
|
||||
if (!elem) return;
|
||||
|
||||
var CACHE_NAME = 'lay_'+ MOD_NAME + '_cache';
|
||||
var cache = elem.data(CACHE_NAME) || {};
|
||||
var cache = elem.data(MOD_CACHE_NAME) || {};
|
||||
|
||||
if (value === undefined) return cache[key];
|
||||
// value 未传则获取缓存值
|
||||
if (value === undefined) {
|
||||
return cache[key];
|
||||
}
|
||||
|
||||
cache[key] = value;
|
||||
elem.data(CACHE_NAME, cache);
|
||||
if (remove) {
|
||||
delete cache[key]; // 删除缓存
|
||||
} else {
|
||||
cache[key] = value; // 设置缓存
|
||||
}
|
||||
|
||||
elem.data(MOD_CACHE_NAME, cache);
|
||||
};
|
||||
|
||||
// 清除缓存
|
||||
Class.prototype.removeCache = function(key) {
|
||||
this.cache(key, null, true);
|
||||
};
|
||||
|
||||
// 缓存所有实例对象
|
||||
|
@ -1,162 +1,475 @@
|
||||
/**
|
||||
* laytpl 轻量模板引擎
|
||||
* laytpl
|
||||
* 轻量级通用模板引擎
|
||||
*/
|
||||
|
||||
layui.define(function(exports){
|
||||
"use strict";
|
||||
(function(global) {
|
||||
'use strict';
|
||||
|
||||
// 默认属性
|
||||
var config = {
|
||||
open: '{{', // 标签符前缀
|
||||
close: '}}' // 标签符后缀
|
||||
};
|
||||
var MOD_NAME = 'laytpl';
|
||||
|
||||
// 模板工具
|
||||
var tool = {
|
||||
escape: function(html){
|
||||
var exp = /[<"'>]|&(?=#[a-zA-Z0-9]+)/g;
|
||||
if(html === undefined || html === null) return '';
|
||||
|
||||
html += '';
|
||||
if(!exp.test(html)) return html;
|
||||
|
||||
return html.replace(/&(?!#?[a-zA-Z0-9]+;)/g, '&')
|
||||
.replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/'/g, ''').replace(/"/g, '"');
|
||||
}
|
||||
};
|
||||
|
||||
// 内部方法
|
||||
var inner = {
|
||||
exp: function(str){
|
||||
return new RegExp(str, 'g');
|
||||
},
|
||||
// 错误提示
|
||||
error: function(e, source){
|
||||
var error = 'Laytpl Error: ';
|
||||
typeof console === 'object' && console.error(error + e + '\n'+ (source || ''));
|
||||
return error + e;
|
||||
}
|
||||
};
|
||||
|
||||
// constructor
|
||||
var Class = function(template, options){
|
||||
// 实例接口
|
||||
var thisModule = function() {
|
||||
var that = this;
|
||||
that.config = that.config || {};
|
||||
that.template = template;
|
||||
var options = that.config;
|
||||
return {
|
||||
config: options,
|
||||
|
||||
// 简单属性合并
|
||||
var extend = function(obj){
|
||||
for(var i in obj){
|
||||
that.config[i] = obj[i];
|
||||
/**
|
||||
* 渲染模板
|
||||
* @param {Object} data - 模板数据
|
||||
* @param {Function} callback - 回调函数
|
||||
* @returns {string} 渲染后的模板
|
||||
*/
|
||||
render: function(data, callback) {
|
||||
options.data = data
|
||||
var html = that.render();
|
||||
|
||||
// 如果传入目标元素选择器,则直接将模板渲染到目标元素中
|
||||
if (options.target) {
|
||||
var elem = document.querySelector(options.target);
|
||||
if (elem) {
|
||||
elem.innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
return typeof callback === 'function'
|
||||
? (callback(html), this)
|
||||
: html;
|
||||
},
|
||||
|
||||
/**
|
||||
* 编译新的模板
|
||||
* @param {string} template - 模板
|
||||
* @returns {this}
|
||||
*/
|
||||
compile: function(template) {
|
||||
options.template = template;
|
||||
delete that.compilerCache; // 清除模板缓存
|
||||
// that.compile(template);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 模板编译错误事件
|
||||
* @param {Function} callback
|
||||
* @returns {this}
|
||||
*/
|
||||
error: function(callback) {
|
||||
callback && (options.error = callback);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 以下为兼容旧版本相关方法
|
||||
*/
|
||||
|
||||
// 解析并渲染模板
|
||||
parse: function(template, data) {
|
||||
return this.compile(template).render(data);
|
||||
}
|
||||
};
|
||||
|
||||
extend(config);
|
||||
extend(options);
|
||||
};
|
||||
|
||||
// 标签正则
|
||||
Class.prototype.tagExp = function(type, _, __){
|
||||
var options = this.config;
|
||||
var types = [
|
||||
'#([\\s\\S])+?', // js 语句
|
||||
'([^{#}])*?' // 普通字段
|
||||
][type || 0];
|
||||
|
||||
return inner.exp((_||'') + options.open + types + options.close + (__||''));
|
||||
// 模板内部变量
|
||||
var vars = {
|
||||
// 字符转义
|
||||
escape: function(html) {
|
||||
var exp = /[<"'>]|&(?=#[a-zA-Z0-9]+)/g;
|
||||
if (html === undefined || html === null) return '';
|
||||
html = ''+ html;
|
||||
if (!exp.test(html)) return html;
|
||||
return html.replace(exp, function(str) {
|
||||
return '&#'+ str.charCodeAt(0) + ';';
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 模版解析
|
||||
Class.prototype.parse = function(template, data){
|
||||
// 组件工具类方法
|
||||
var tools = {
|
||||
regex: function(str, mod) {
|
||||
return new RegExp(str, mod || 'g');
|
||||
},
|
||||
|
||||
/**
|
||||
* 错误提示
|
||||
* @param {string} e - 原始错误信息
|
||||
* @param {Object} opts - 自定义选项
|
||||
* @param {Function} error - 错误回调
|
||||
* @returns {string} - 错误提示
|
||||
*/
|
||||
error: function(e, opts, error) {
|
||||
opts = opts || {};
|
||||
opts = Object.assign({
|
||||
debug: '',
|
||||
message: 'Laytpl '+ (opts.type || '') +'Error: ' + e
|
||||
}, opts);
|
||||
|
||||
// 向控制台输出错误信息
|
||||
typeof console === 'object' && console.error(opts.message, '\n', opts.debug, '\n', opts);
|
||||
typeof error === 'function' && error(opts); // 执行错误回调
|
||||
return opts.message; // 向视图返回错误提示
|
||||
}
|
||||
};
|
||||
|
||||
// 默认配置
|
||||
var config = {
|
||||
open: '{{', // 起始界定符
|
||||
close: '}}', // 结束界定符
|
||||
cache: true, // 是否开启模板缓存,以便下次渲染时不重新编译模板
|
||||
condense: true, // 是否压缩模板空白符,如:将多个连续的空白符压缩为单个空格
|
||||
tagStyle: '' // 标签风格。默认采用 < 2.11 的风格,设置 modern 则采用 2.11+ 风格
|
||||
};
|
||||
|
||||
// 构造器
|
||||
var Class = function(template, options) {
|
||||
var that = this;
|
||||
|
||||
// 选项合并
|
||||
options = that.config = Object.assign({
|
||||
template: template
|
||||
}, config, options);
|
||||
|
||||
// 当前实例的模板内工具
|
||||
that.vars = Object.assign({
|
||||
/**
|
||||
* 引用外部模板。若在 Node.js 环境,可通过重置该方法实现模板文件导入
|
||||
* @param {string} id - 模板 ID
|
||||
* @param {Object} data - 模板数据
|
||||
* @returns {string} 模板渲染后内容
|
||||
*/
|
||||
include: function(id, data) {
|
||||
var elem = document.getElementById(id);
|
||||
var template = elem ? elem.innerHTML : '';
|
||||
return template ? that.render(template, data) : '';
|
||||
}
|
||||
}, vars);
|
||||
|
||||
// 编译模板
|
||||
that.compile(options.template);
|
||||
};
|
||||
|
||||
/**
|
||||
* 渲染
|
||||
* @param {Object} template - 模板
|
||||
* @param {Object} data - 数据
|
||||
* @returns {string} 渲染后的模板内容
|
||||
*/
|
||||
Class.prototype.render = function(template, data) {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
|
||||
// 获得模板渲染函数
|
||||
var compiler = template ? that.compile(template) : (
|
||||
that.compilerCache || that.compile(options.template)
|
||||
);
|
||||
|
||||
// 获取渲染后的字符
|
||||
var html = function() {
|
||||
data = data || options.data || {};
|
||||
try {
|
||||
return compiler(data);
|
||||
} catch(e) {
|
||||
template = template || options.template;
|
||||
return tools.error(e, {
|
||||
debug: that.checkErrorArea(template, data),
|
||||
template: template,
|
||||
type: 'Render'
|
||||
}, options.error);
|
||||
}
|
||||
}();
|
||||
|
||||
// 缓存编译器
|
||||
if (options.cache && !template) {
|
||||
that.compilerCache = compiler;
|
||||
}
|
||||
|
||||
return html; // 返回渲染后的字符
|
||||
};
|
||||
|
||||
/**
|
||||
* 编译模板
|
||||
* @param {string} template - 原始模板
|
||||
* @returns {Function} 模板编译器,用于后续数据渲染
|
||||
*/
|
||||
Class.prototype.compile = function(template) {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var source = template;
|
||||
var jss = inner.exp('^'+ options.open +'#', '');
|
||||
var jsse = inner.exp(options.close +'$', '');
|
||||
var openDelimiter = options.open;
|
||||
var closeDelimiter = options.close;
|
||||
var condense = options.condense;
|
||||
var regex = tools.regex;
|
||||
const placeholder = '\u2028'; // Unicode 行分隔符
|
||||
|
||||
// 模板必须为 string 类型
|
||||
if(typeof template !== 'string') return template;
|
||||
// console.log('compile');
|
||||
|
||||
// 正则解析
|
||||
template = template.replace(/\s+|\r|\t|\n/g, ' ')
|
||||
.replace(inner.exp(options.open +'#'), options.open +'# ')
|
||||
.replace(inner.exp(options.close +'}'), '} '+ options.close).replace(/\\/g, '\\\\')
|
||||
|
||||
// 不匹配指定区域的内容
|
||||
.replace(inner.exp(options.open + '!(.+?)!' + options.close), function(str){
|
||||
str = str.replace(inner.exp('^'+ options.open + '!'), '')
|
||||
.replace(inner.exp('!'+ options.close), '')
|
||||
.replace(inner.exp(options.open + '|' + options.close), function(tag){
|
||||
return tag.replace(/(.)/g, '\\$1')
|
||||
});
|
||||
return str
|
||||
})
|
||||
|
||||
// 匹配 JS 语法
|
||||
.replace(/(?="|')/g, '\\').replace(that.tagExp(), function(str){
|
||||
str = str.replace(jss, '').replace(jsse, '');
|
||||
return '";' + str.replace(/\\(.)/g, '$1') + ';view+="';
|
||||
})
|
||||
|
||||
// 匹配普通输出语句
|
||||
.replace(that.tagExp(1), function(str){
|
||||
var start = '"+laytpl.escape(';
|
||||
if(str.replace(/\s/g, '') === options.open + options.close){
|
||||
// 模板必须为 string 类型,且不能为空
|
||||
if (typeof template !== 'string' || !template) {
|
||||
return function() {
|
||||
return '';
|
||||
}
|
||||
str = str.replace(inner.exp(options.open + '|' + options.close), '');
|
||||
if(/^=/.test(str)){
|
||||
str = str.replace(/^=/, '');
|
||||
} else if(/^-/.test(str)){
|
||||
str = str.replace(/^-/, '');
|
||||
start = '"+(';
|
||||
}
|
||||
return start + str.replace(/\\(.)/g, '$1') + ')+"';
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
template = '"use strict";var view = "' + template + '";return view;';
|
||||
/**
|
||||
* 完整标签正则
|
||||
* @param {string[]} cores - 标签内部核心表达式,含:前置、主体、后置
|
||||
* @param {Object} sides - 标签两侧外部表达式
|
||||
* @returns {RegExp}
|
||||
*/
|
||||
var tagRegex = function(cores, sides) {
|
||||
var arr = [
|
||||
'(?:'+ openDelimiter + (cores[0] || '') +'\\s*)', // 界定符前置
|
||||
'('+ (cores[1] || '[\\s\\S]') +'*?)', // 标签主体
|
||||
'(?:\\s*'+ (cores[2] || '') + closeDelimiter +')' // 界定符后置
|
||||
];
|
||||
sides = sides || {};
|
||||
sides.before && arr.unshift(sides.before); // 标签前面的表达式
|
||||
sides.after && arr.push(sides.after); // 标签后面的表达式
|
||||
return regex(arr.join(''));
|
||||
};
|
||||
|
||||
// 匹配非输出类型标签两侧的换行符和空白符,避免渲染后占用一行
|
||||
var sidesRegex = condense ? ['', ''] : ['(?:(?:\\n)*\\s*)', '(?:\\s*?)'];
|
||||
var delimSides = {
|
||||
before: sidesRegex[0],
|
||||
after: sidesRegex[1]
|
||||
};
|
||||
|
||||
/**
|
||||
* 清理多余符号
|
||||
* @param {string} body - 标签主体字符
|
||||
* @param {boolean} nowrap - 是否强制不换行
|
||||
* @returns {string} 清理后的字符
|
||||
*/
|
||||
var clear = function(body, nowrap) {
|
||||
if (!condense) {
|
||||
// 还原语句中的 Unicode 行分隔符
|
||||
body = body.replace(regex(placeholder), nowrap ? '' : '\n');
|
||||
}
|
||||
body = body.replace(/\\(\\|")/g, '$1'); // 去除多余反斜杠
|
||||
return body;
|
||||
};
|
||||
|
||||
// 纠正标签结构
|
||||
var correct = function(tpl) {
|
||||
return tpl.replace(regex('([}\\]])'+ closeDelimiter), '$1 '+ closeDelimiter);
|
||||
};
|
||||
|
||||
// 模板解析
|
||||
var parse = that.parse = function(tpl) {
|
||||
tpl = tpl || '';
|
||||
if (!tpl) return tpl;
|
||||
|
||||
// 压缩连续空白符
|
||||
if (condense) {
|
||||
tpl = tpl.replace(/\t/g, ' ').replace(/\s+/g, ' ');
|
||||
}
|
||||
|
||||
// 初始整理
|
||||
tpl = correct(tpl) // 纠正标签
|
||||
.replace(/(?=\\|")/g, '\\') // 转义反斜杠和双引号
|
||||
.replace(/\r?\n/g, condense ? '' : placeholder); // 整理换行符
|
||||
|
||||
// 忽略标签 - 即区域中的内容不进行标签解析
|
||||
tpl = tpl.replace(tagRegex(['!', '', '!'], delimSides), function(str, body) {
|
||||
body = body.replace(regex(openDelimiter + '|' + closeDelimiter), function(tag) {
|
||||
return tag.replace(/(?=.)/g, '\\');
|
||||
});
|
||||
return body;
|
||||
});
|
||||
|
||||
// 模板字符拼接
|
||||
var strConcatenation = function(body) {
|
||||
// 通过对 20k+ 行的模板进行编译测试, 发现 Chrome `+=` 性能竟优于 `push`
|
||||
// 1k 次循环 + 1k 行数据量进行模板编译+渲染,发现 `+=` 性能仍然优于 `push`
|
||||
// 考虑可能是 V8 做了 Ropes 结构优化? 或跟模板采用「静态拼接」的实现有关(可能性更大)
|
||||
return ['";', body, '__laytpl__+="'].join('\n');
|
||||
// return ['");', body, '__laytpl__.push("'].join('\n');
|
||||
};
|
||||
|
||||
// 解析输出标签
|
||||
var output = function(str, delimiter, body) {
|
||||
var _escape;
|
||||
|
||||
if (!body) return '';
|
||||
body = clear(body, true);
|
||||
|
||||
// 输出方式
|
||||
if (delimiter === '-') { // 原文输出,即不对 HTML 原文进行转义
|
||||
_escape = '';
|
||||
} else { // 转义输出
|
||||
_escape = '_escape';
|
||||
}
|
||||
|
||||
return body ? strConcatenation(
|
||||
'__laytpl__+='+ _escape +'('+ body +');'
|
||||
// '__laytpl__.push('+ _escape +'('+ body +'));'
|
||||
) : '';
|
||||
};
|
||||
|
||||
// 解析 Scriptlet
|
||||
var statement = function(str, body) {
|
||||
if (!body) return '';
|
||||
body = clear(body);
|
||||
return strConcatenation(body);
|
||||
};
|
||||
|
||||
// 标签风格
|
||||
if (options.tagStyle === 'modern') { // 2.11+ 版本风格
|
||||
// 注释标签 - 仅在模板中显示,不进行解析,也不在视图中输出
|
||||
tpl = tpl.replace(tagRegex(['#'], delimSides), '');
|
||||
// 输出标签
|
||||
tpl = tpl.replace(tagRegex(['(=|-)']), output);
|
||||
// Scriptlet 标签
|
||||
tpl = tpl.replace(tagRegex([], delimSides), statement);
|
||||
} else { // < 2.11 版本风格
|
||||
// Scriptlet 标签
|
||||
tpl = tpl.replace(tagRegex(['#'], delimSides), statement);
|
||||
// 输出标签
|
||||
tpl = tpl.replace(tagRegex(['(=|-)*']), output);
|
||||
}
|
||||
|
||||
// 恢复换行符
|
||||
if (!condense) {
|
||||
tpl = tpl.replace(regex(placeholder), '\\n');
|
||||
}
|
||||
|
||||
return tpl;
|
||||
};
|
||||
|
||||
// 创建模板编译器
|
||||
var createCompiler = that.createCompiler = function(template) {
|
||||
var codeBuilder = [
|
||||
'function(d){',
|
||||
'"use strict";',
|
||||
'var __laytpl__="",'+
|
||||
function() { // 内部变量
|
||||
// 内部方法
|
||||
var arr = [];
|
||||
for (var key in that.vars) {
|
||||
arr.push(((key === 'escape' ? '_' : '') + key) +'=laytpl.'+ key);
|
||||
}
|
||||
return arr.join(',');
|
||||
}() + ';',
|
||||
'__laytpl__="'+ parse(template) +'";',
|
||||
'return __laytpl__;',
|
||||
// '__laytpl__.push("'+ parse(template) +'");',
|
||||
// 'return __laytpl__.join("");',
|
||||
'};'
|
||||
].join('\n');
|
||||
// console.log(codeBuilder);
|
||||
|
||||
try {
|
||||
/**
|
||||
* 请注意: 开发者在使用模板语法时,需确保模板中的 JS 语句不来自于页面用户输入。
|
||||
* 即模板中的 JS 语句必须在页面开发者自身的可控范围内,否则请避免使用该模板解析。
|
||||
*/
|
||||
that.cache = template = new Function('d, laytpl', template);
|
||||
return template(data, tool);
|
||||
return new Function('laytpl', 'return '+ codeBuilder)(that.vars);
|
||||
};
|
||||
|
||||
try {
|
||||
return createCompiler(template); // 返回编译器
|
||||
} catch(e) {
|
||||
delete that.cache;
|
||||
return inner.error(e, source);
|
||||
delete that.compilerCache;
|
||||
return function() {
|
||||
return tools.error(e, {
|
||||
debug: that.checkErrorArea(source),
|
||||
template: source,
|
||||
type: 'Compile'
|
||||
}, options.error);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 数据渲染
|
||||
Class.prototype.render = function(data, callback){
|
||||
data = data || {};
|
||||
|
||||
/**
|
||||
* 校验出错区域
|
||||
* @param {string} source - 原始模板
|
||||
* @param {Object} data - 数据
|
||||
* @returns {string} 出错区域的模板碎片
|
||||
*/
|
||||
Class.prototype.checkErrorArea = function(source, data) {
|
||||
var that = this;
|
||||
var result = that.cache ? that.cache(data, tool) : that.parse(that.template, data);
|
||||
var srcs = source.split(/\n/g);
|
||||
var validLine = -1; // 有效行
|
||||
|
||||
// 返回渲染结果
|
||||
typeof callback === 'function' && callback(result);
|
||||
return result;
|
||||
};
|
||||
|
||||
// 创建实例
|
||||
var laytpl = function(template, options){
|
||||
return new Class(template, options);
|
||||
};
|
||||
|
||||
// 配置全局属性
|
||||
laytpl.config = function(options){
|
||||
options = options || {};
|
||||
for(var i in options){
|
||||
config[i] = options[i];
|
||||
// 逐行查找
|
||||
var i = 0;
|
||||
var str = '';
|
||||
var len = srcs.length;
|
||||
for (; i < len; i++) {
|
||||
str += srcs[i];
|
||||
try {
|
||||
data
|
||||
? that.createCompiler(str)(data)
|
||||
: new Function('"use strict";_laytpl__="'+ that.parse(str) +'";');
|
||||
validLine = i;
|
||||
} catch(e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 呈现模板出错大致区域
|
||||
var errorArea = function(errLine) {
|
||||
var arr = [];
|
||||
var addLine = 3; // 错误行上下延伸的行数
|
||||
var i = 0;
|
||||
var len = srcs.length;
|
||||
|
||||
if (errLine < 0) errLine = 0;
|
||||
if (errLine > len - 1) errLine = len - 1;
|
||||
|
||||
i = errLine - addLine;
|
||||
if (i < 0) i = 0;
|
||||
|
||||
for (; i < len; i++) {
|
||||
arr.push((i == errLine ? '? ' : ' ') +(i + 1)+ '| '+ srcs[i]);
|
||||
if (i >= errLine + addLine) break;
|
||||
}
|
||||
|
||||
return '\n'+ arr.join('\n');
|
||||
};
|
||||
|
||||
return errorArea(validLine + 1); // 有效行的下一行即为出错行
|
||||
};
|
||||
|
||||
laytpl.v = '2.0.0';
|
||||
/**
|
||||
* 创建实例
|
||||
* @param {string} template - 模板
|
||||
* @param {Object} options - 选项
|
||||
* @returns
|
||||
*/
|
||||
var laytpl = function(template, options) {
|
||||
var inst = new Class(template, options);
|
||||
return thisModule.call(inst);
|
||||
};
|
||||
|
||||
// export
|
||||
exports('laytpl', laytpl);
|
||||
});
|
||||
/**
|
||||
* 扩展模板内部变量
|
||||
* @param {Object} variables - 扩展内部变量,变量值通常为函数
|
||||
*/
|
||||
laytpl.extendVars = function(variables) {
|
||||
Object.assign(vars, variables);
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置默认配置
|
||||
* @param {Object} options - 选项
|
||||
*/
|
||||
laytpl.config = laytpl.set = function(options) {
|
||||
Object.assign(config, options);
|
||||
};
|
||||
|
||||
// 输出接口
|
||||
typeof module === 'object' && typeof exports === 'object'
|
||||
? module.exports = laytpl // CommonJS
|
||||
: ( // 浏览器
|
||||
typeof layui === 'object' ? layui.define(function(exports) { // Layui
|
||||
exports(MOD_NAME, laytpl);
|
||||
}) : (
|
||||
typeof define === 'function' && define.amd ? define(function() { // RequireJS
|
||||
return laytpl;
|
||||
}) : global.laytpl = laytpl // 单独引入
|
||||
)
|
||||
);
|
||||
})(this);
|
@ -459,7 +459,7 @@ layui.define('component', function(exports) {
|
||||
var that = this
|
||||
var options = that.config;
|
||||
|
||||
if(!options.closable) return;
|
||||
if (!options.closable) return;
|
||||
|
||||
opts = opts || {};
|
||||
|
||||
@ -484,17 +484,13 @@ layui.define('component', function(exports) {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var container = that.getContainer();
|
||||
var hasDel = that.cache('close');
|
||||
|
||||
// 是否开启关闭
|
||||
if (options.closable) {
|
||||
if (!hasDel) {
|
||||
container.header.items.each(function(){
|
||||
that.appendClose($(this));
|
||||
});
|
||||
that.cache('close', true);
|
||||
}
|
||||
} else if(hasDel) {
|
||||
container.header.items.each(function() {
|
||||
that.appendClose($(this));
|
||||
});
|
||||
} else {
|
||||
container.header.items.each(function() {
|
||||
$(this).find('.'+ component.CONST.CLOSE).remove();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user