mirror of
https://gitee.com/layui/layui.git
synced 2025-08-20 09:28:56 +08:00
feat: 升级 2.10 核心 (#2477)
This commit is contained in:
commit
7075fddfeb
21
docs/.layui/detail/demo.md
Normal file
21
docs/.layui/detail/demo.md
Normal file
@ -0,0 +1,21 @@
|
||||
<pre class="layui-code" lay-options="{preview: true, text: {preview: '综合用法'}, layout: ['preview', 'code'], tools: ['full']}">
|
||||
<textarea>
|
||||
{{- d.include("/MOD_NAME/examples/demo.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<h3 id="demo-NAME1" class="ws-anchor ws-bold">示例1</h3>
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], tools: ['full']}">
|
||||
<textarea>
|
||||
{{- d.include("/MOD_NAME/examples/ex1.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<h3 id="demo-NAME2" class="ws-anchor ws-bold">示例2</h3>
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], tools: ['full']}">
|
||||
<textarea>
|
||||
{{- d.include("/MOD_NAME/examples/ex2.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
@ -1,5 +1,3 @@
|
||||
<pre class="layui-code" lay-options="{preview: true, text: {preview: '综合用法'}, layout: ['preview', 'code'], tools: ['full']}">
|
||||
<textarea>
|
||||
AAA
|
||||
|
||||
<!-- import layui -->
|
||||
@ -7,25 +5,6 @@ AAA
|
||||
layui.use(function(){
|
||||
var MOD_NAME = layui.MOD_NAME;
|
||||
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<h3 id="demo-NAME" class="ws-anchor ws-bold">示例标题</h3>
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], tools: ['full']}">
|
||||
<textarea>
|
||||
AAA
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function(){
|
||||
var MOD_NAME = layui.MOD_NAME;
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</textarea>
|
||||
</pre>
|
||||
@ -2,10 +2,10 @@
|
||||
title: 某某组件 MOD_NAME
|
||||
toc: true
|
||||
---
|
||||
|
||||
|
||||
# 某某组件
|
||||
|
||||
> 某某组件 `MOD_NAME`
|
||||
> 某某组件 `MOD_NAME`
|
||||
|
||||
<h2 id="examples" lay-toc="{hot: true}" style="margin-bottom: 0;">示例</h2>
|
||||
|
||||
@ -27,8 +27,8 @@ toc: true
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
|
||||
<h2 id="options" lay-toc="{level: 2, hot: true}">属性</h2>
|
||||
<h3 id="options" lay-toc="{level: 2, hot: true}">属性</h3>
|
||||
|
||||
<div>
|
||||
{{- d.include("/MOD_NAME/detail/options.md") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
34
docs/.layui/prompt.txt
Normal file
34
docs/.layui/prompt.txt
Normal file
@ -0,0 +1,34 @@
|
||||
> 自动生成组件文档提示词
|
||||
|
||||
# 角色
|
||||
你是一位顶级的前端开发专家,能够高效、准确地为 JavaScript 组件代码生成对应的接口文档,并且严格遵循给定的模板规则。
|
||||
|
||||
## 组件
|
||||
本次生成的组件名称为: input (统一简称为 MOD_NAME)
|
||||
|
||||
## 任务
|
||||
按照 `/.layui` 目录给定的模板,为 `/src/modules/MOD_NAME.js` 生成完整的文档,并保存在 `/docs/MOD_NAME/` 目录。文档要采用 `HTML+Markdown+laytpl` 混合编写,其中 laytpl 为视图引擎(类似于 ejs,可为文档引入子模版,如:`{{- d.include("/MOD_NAME/detail/demo.md") }}`)。
|
||||
|
||||
### 文档模板介绍
|
||||
- `index.md`: 组件文档主文件,包含示例、API、属性等完整内容。
|
||||
- `detail/`: 目录存放文档子模板。一般在 index.md 内容过大时,可将内容碎片放置在该目录中,index.md 只需引用即可。
|
||||
- `detail/demo.md`: 组件示例主模板,在 `index.md` 中引入。
|
||||
- `detail/options.md`: 组件 `render()` 方法接受的参数配置项。
|
||||
- `examples/`: 目录存放组件示例文件,在 `detail/demo.md` 中引入。
|
||||
|
||||
### 文档内容要求
|
||||
- `index.md` 主文档内容主要包含以下层级:
|
||||
```markdown
|
||||
## 示例
|
||||
## API
|
||||
### 渲染
|
||||
### 属性
|
||||
## 事件(如果有的话)
|
||||
```
|
||||
其中,API 中列举的所有方法进行介绍,如果方法传入的参数(如 opts)是一个选项,需按照表格的方式展示,如:
|
||||
```markdown
|
||||
| opts | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| 内容 | 内容 | 内容 | 内容 |
|
||||
```
|
||||
- `detail/options.md` 需按照当前已有的 HTML + Markdown 模板混合编写。
|
||||
197
docs/component/detail/options.md
Normal file
197
docs/component/detail/options.md
Normal file
@ -0,0 +1,197 @@
|
||||
<table class="layui-table">
|
||||
<colgroup>
|
||||
<col width="150">
|
||||
<col>
|
||||
<col width="100">
|
||||
<col width="100">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>属性名</th>
|
||||
<th>描述</th>
|
||||
<th>类型</th>
|
||||
<th>默认值</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>name</td>
|
||||
<td>
|
||||
|
||||
组件名称。如 `name:'tabs'`,在使用组件时,即可通过 `layui.tabs` 获得该组件。注:*组件名必须唯一*。
|
||||
|
||||
</td>
|
||||
<td>string</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>config</td>
|
||||
<td>
|
||||
|
||||
定义组件渲染时的默认配置项。
|
||||
|
||||
</td>
|
||||
<td>object</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CONST</td>
|
||||
<td>
|
||||
|
||||
通用常量集,一般存放固定字符,如类名等。如:
|
||||
|
||||
```
|
||||
CONST: {
|
||||
ELEM: 'layui-tabs',
|
||||
}
|
||||
```
|
||||
|
||||
上述常量可通过 `component.CONST.ELEM` 获得。
|
||||
|
||||
</td>
|
||||
<td>object</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>isRenderWithoutElem</td>
|
||||
<td>
|
||||
|
||||
渲染是否无需指定目标元素。即无需设置 `elem` 选项,一般用于渲染即显示的组件类型。
|
||||
|
||||
</td>
|
||||
<td>boolean</td>
|
||||
<td>
|
||||
|
||||
`false`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>isRenderOnEvent</td>
|
||||
<td>
|
||||
|
||||
渲染是否由事件触发。如 `dropdown` 这类通过点击触发的组件,那么应该设置为 `true`;而诸如 `tabs` 这类初始即展示的组件,则应该设置为 `false`。*推荐根据组件类型始终显式设置对应值*。
|
||||
|
||||
</td>
|
||||
<td>boolean</td>
|
||||
<td>
|
||||
|
||||
`true`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>isDeepReload</td>
|
||||
<td>
|
||||
|
||||
组件重载时是否允许深度重载,即对重载时选项进行深度合并。
|
||||
|
||||
</td>
|
||||
<td>boolean</td>
|
||||
<td>
|
||||
|
||||
`false`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" style="text-align: center">
|
||||
|
||||
<div id="options.callback" lay-pid="options" class="ws-anchor">
|
||||
|
||||
[回调函数](#options.callback)
|
||||
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>render</td>
|
||||
<td colspan="3">
|
||||
|
||||
组件渲染的逻辑。
|
||||
|
||||
```js
|
||||
render: function() {
|
||||
// 组件的容器构建、插入等
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
也可以通过原型 `component.Class.prototype.render` 进行定义。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>beforeInit</td>
|
||||
<td colspan="3">
|
||||
|
||||
组件初始化之前的回调函数。
|
||||
|
||||
```js
|
||||
beforeInit: function(options) {
|
||||
console.log(options); // 获得组件初始化前的配置项
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>beforeRender</td>
|
||||
<td colspan="3">
|
||||
|
||||
渲染之前的回调函数。
|
||||
|
||||
```js
|
||||
beforeRender: function(options) {
|
||||
console.log(options); // 获得组件渲染前的配置项
|
||||
}
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>extendsInstance</td>
|
||||
<td colspan="3">
|
||||
|
||||
扩展组件渲染的实例对象的回调函数。如:
|
||||
|
||||
```js
|
||||
extendsInstance: function(that) {
|
||||
return {
|
||||
// 关闭组件
|
||||
close: function() {
|
||||
that.remove(); // 调用组件原型中的 remove 方法
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当组件渲染时,即可通过它返回的对象调用实例方法:
|
||||
|
||||
```js
|
||||
var inst = xxx.render(); // 某组件渲染
|
||||
inst.close(); // 关闭某组件
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>events</td>
|
||||
<td colspan="3">
|
||||
|
||||
定义组件各内部事件。
|
||||
|
||||
```js
|
||||
events: function() {
|
||||
// 亦可包含针对组件的 window, document 等全局事件
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
也可以通过原型 `component.Class.prototype.events` 进行定义。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
289
docs/component/index.md
Normal file
289
docs/component/index.md
Normal file
@ -0,0 +1,289 @@
|
||||
---
|
||||
title: 组件构建器 component
|
||||
toc: true
|
||||
---
|
||||
|
||||
# 组件构建器 <sup>2.10+</sup>
|
||||
|
||||
> 组件构建器 `component` 是 2.10 版本新增的重要模块,旨在为 Layui 2 系列版本逐步构建统一 API 规范的组件。
|
||||
|
||||
<h2 id="api" lay-toc="{hot: true}">API</h2>
|
||||
|
||||
| API | 描述 |
|
||||
| --- | --- |
|
||||
| [layui.component(options)](#create) | 创建组件 |
|
||||
|
||||
<h3 id="create" lay-toc="{level: 2}">创建组件</h3>
|
||||
|
||||
`layui.component(options);`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
|
||||
该方法返回一个对象,包含用于组件对外的基础接口,如:组件渲染、重载、事件操作,及构造函数等等。用法示例:
|
||||
|
||||
```js
|
||||
/**
|
||||
* tabs
|
||||
* 标签页组件
|
||||
*/
|
||||
layui.define('component', function(exports) {
|
||||
// 创建组件
|
||||
var component = layui.component({
|
||||
name: 'tabs', // 组件名称
|
||||
config: { // 组件默认配置项
|
||||
// …
|
||||
},
|
||||
render: function() { // 组件渲染逻辑
|
||||
// …
|
||||
},
|
||||
// 其他选项
|
||||
});
|
||||
|
||||
// 将创建组件时返回的 `component` 对象作为组件的接口输出
|
||||
// 组件将继承通用的基础接口,如 render, reload, set 等方法
|
||||
exports(component.CONST.MOD_NAME, component);
|
||||
});
|
||||
```
|
||||
|
||||
<h3 id="options" lay-toc="{level: 2}">属性配置</h3>
|
||||
|
||||
<div>
|
||||
{{- d.include("/component/detail/options.md") }}
|
||||
</div>
|
||||
|
||||
<h2 id="export" lay-toc="{}">基础接口 🔥</h2>
|
||||
|
||||
通过 `component` 模块创建的组件,均会继承内部定义的「基础对外接口」和「组件渲染的通用选项」。
|
||||
|
||||
| 接口 | 描述 |
|
||||
| --- | --- |
|
||||
| [component.render(options)](#render) | 组件渲染 |
|
||||
| [component.reload(id, options)](#reload) | 组件重载 |
|
||||
| [component.set(options)](#set) | 设置组件渲染时的全局配置项 |
|
||||
| [component.on(\'event(filter)\', callback)](#on) | 组件的自定义事件 |
|
||||
| [component.getThis(id)](#getThis) | 获取指定组件的实例对象 |
|
||||
| component.index | 获得组件的自增索引 |
|
||||
| component.config | 获得组件渲染时的全局配置项。一般通过 `set` 方法设置 |
|
||||
| component.cache | 获得组件的缓存数据集。如组件实例 ID 集 |
|
||||
| [component.CONST](#CONST) | 获得组件的通用常量集。如 `MOD_NAME` 等 |
|
||||
| [component.Class](#Class) | 获得组件的构造函数。一般用于扩展原型方法 |
|
||||
|
||||
> 😊 注:上表中的 `component` 为组件的基础对象,实际使用时,请根据实组件名称进行替换。如 `tabs` 组件,对应的接口则为:`tabs.render(options)` `tabs.on('event(filter)', callback)` 等。
|
||||
|
||||
<h3 id="render" lay-toc="{level: 2}">组件渲染</h3>
|
||||
|
||||
`component.render(options)`
|
||||
|
||||
- 参数 `options` : 组件渲染的配置项。可继承的通用选项见下表:
|
||||
|
||||
| 选项 | 描述 |
|
||||
| --- | --- |
|
||||
| elem | 件渲染指定的目标元素选择器或 DOM 对象 |
|
||||
| id | 组件渲染的唯一实例 ID |
|
||||
| show | 是否初始即渲染组件。通常结合创建组件设定的 `isRenderOnEvent` 选项决定是否启用 |
|
||||
|
||||
更多渲染时的选项则由各组件内部单独定义,具体可查阅各组件对应的文档。
|
||||
|
||||
```js
|
||||
// 以 tabs 组件为例
|
||||
// 渲染
|
||||
tabs.render({
|
||||
elem: '#id',
|
||||
// …
|
||||
});
|
||||
```
|
||||
|
||||
<h3 id="reload" lay-toc="{level: 2}">组件重载</h3>
|
||||
|
||||
`component.reload(id, options)`
|
||||
|
||||
- 参数 `id` : 组件实例 ID。
|
||||
- 参数 `options` : 组件重载的配置项。
|
||||
|
||||
该方法可实现对组件的完整重载。部分组件通常还提供了「仅数据重载」,具体可参考各组件对应的文档。
|
||||
|
||||
```js
|
||||
// 以 tabs 组件为例
|
||||
// 重载 tabs 组件实例
|
||||
tabs.reload('id', {
|
||||
// …
|
||||
})
|
||||
```
|
||||
|
||||
<h3 id="set" lay-toc="{level: 2}">全局设置</h3>
|
||||
|
||||
`component.set(options)`
|
||||
|
||||
- 参数 `options` : 组件渲染的配置项。
|
||||
|
||||
该方法用于全局设置组件渲染时的默认配置项,需在组件渲染之前执行。
|
||||
|
||||
```js
|
||||
// 以 tabs 组件为例
|
||||
// 全局设置。后续所有渲染均会生效,除非对选项进行覆盖
|
||||
tabs.set({
|
||||
trigger: 'mouseenter' // 默认的触发事件
|
||||
// …
|
||||
});
|
||||
|
||||
// 渲染实例 1
|
||||
tabs.render({ id: 'id1'}); // 继承全局设置
|
||||
|
||||
// 渲染实例 2
|
||||
tabs.render({
|
||||
id: 'id2',
|
||||
trigger: 'click' // 覆盖全局的触发事件
|
||||
});
|
||||
```
|
||||
|
||||
<h3 id="on" lay-toc="{level: 2}">事件定义</h3>
|
||||
|
||||
`component.on('event(id)', callback)`
|
||||
|
||||
- 参数 `event(id)` : 是事件的特定结构。 `event` 为事件名,支持的事件见各组件列表。`id` 为组件的实例 ID。
|
||||
- 参数 `callback` : 事件回调函数。返回的参数由各组件内部单独定义。
|
||||
|
||||
具体事件一般由组件内部单独定义,具体可查看各组件对应的文档。
|
||||
|
||||
```js
|
||||
// 以 tabs 组件为例:
|
||||
// 组件渲染成功后的事件
|
||||
tabs.on('afterRender(id)', function(data) {
|
||||
console.log(obj);
|
||||
});
|
||||
```
|
||||
|
||||
<h3 id="getThis" lay-toc="{level: 2}">获取实例</h3>
|
||||
|
||||
`component.getThis(id)`
|
||||
|
||||
- 参数 `id` : 组件的实例 ID。
|
||||
|
||||
该方法可获取组件渲染时对应的实例,以调用组件内部的原型方法,一般用于在外部对组件进行扩展或重构。
|
||||
|
||||
```js
|
||||
// 以 tabs 组件为例
|
||||
var tabInstance = tabs.getThis('id');
|
||||
// 调用内部的标签滚动方法
|
||||
tabInstance.roll();
|
||||
```
|
||||
|
||||
<h3 id="CONST" lay-toc="{level: 2}">基础常量</h3>
|
||||
|
||||
`component.CONST`
|
||||
|
||||
获取组件的通用常量集,一般存放固定字符,如类名等。
|
||||
|
||||
```js
|
||||
// 基础常量如下
|
||||
component.CONST.MOD_NAME; // 组件名称
|
||||
component.CONST.MOD_INDEX; // 组件自增索引
|
||||
component.CONST.CLASS_THIS; // layui-this
|
||||
component.CONST.CLASS_SHOW; // layui-show
|
||||
component.CONST.CLASS_HIDE; // layui-hide
|
||||
component.CONST.CLASS_HIDEV; // layui-hide-v
|
||||
component.CONST.CLASS_DISABLED; // layui-disabled
|
||||
component.CONST.CLASS_NONE; // layui-none
|
||||
|
||||
// 更多常量一般在各组件内部单独定义,以 tabs 组件为例
|
||||
tabs.CONST.ELEM; // layui-tabs
|
||||
```
|
||||
|
||||
<h3 id="extend" lay-toc="{level: 2}">扩展接口</h3>
|
||||
|
||||
除上述「基础接口」外,你也可以对接口进行任意扩展,如:
|
||||
|
||||
```js
|
||||
/**
|
||||
* 定义组件
|
||||
*/
|
||||
layui.define('component', function(exports) {
|
||||
// 创建组件
|
||||
var component = layui.component({
|
||||
name: 'test',
|
||||
// …
|
||||
});
|
||||
// 扩展组件接口
|
||||
layui.$.extend(component, {
|
||||
// 以扩展一个关闭组件面板的接口为例
|
||||
close: function(id) {
|
||||
var that = component.getThis(id);
|
||||
if(!that) return this;
|
||||
that.remove(obj); // 调用原型中的 remove 方法
|
||||
}
|
||||
});
|
||||
// 输出组件接口
|
||||
exports(component.CONST.MOD_NAME, component);
|
||||
});
|
||||
```
|
||||
|
||||
```js
|
||||
/**
|
||||
* 使用组件(以上述定义的 test 组件为例)
|
||||
*/
|
||||
layui.use('test', function() {
|
||||
var test = layui.test;
|
||||
// 渲染组件
|
||||
test.render({
|
||||
elem: '#id',
|
||||
id: 'test-1'
|
||||
});
|
||||
// 关闭组件面板(通常在某个事件中使用)
|
||||
test.close('test-1');
|
||||
});
|
||||
```
|
||||
|
||||
<h3 id="Class" lay-toc="{level: 2}">扩展原型</h3>
|
||||
|
||||
`component.Class`
|
||||
|
||||
当通过 `layui.component()` 方法创建一个新的组件时,通常需借助 `Class` 构造函数扩展组件原型,以灵活实现组件的个性化定制。但一般不推荐重写 `component.js` 原型中已经定义的基础方法,如:`init, reload, cache`
|
||||
|
||||
```
|
||||
/**
|
||||
* 定义组件
|
||||
*/
|
||||
layui.define('component', function(exports) {
|
||||
// 创建组件
|
||||
var component = layui.component({
|
||||
name: '', // 组件名称
|
||||
// …
|
||||
});
|
||||
|
||||
// 获取构造器
|
||||
var Class = component.Class;
|
||||
|
||||
// 扩展原型
|
||||
Class.prototype.xxx = function() {
|
||||
// …
|
||||
};
|
||||
Class.prototype.aaa = function() {
|
||||
// …
|
||||
};
|
||||
|
||||
// 输出组件接口
|
||||
exports(component.CONST.MOD_NAME, component);
|
||||
});
|
||||
```
|
||||
|
||||
通过 `Class` 构造函数也可以对某个组件的原型进行重构,但一般不推荐,因为这可能破坏组件的基础功能。
|
||||
|
||||
```
|
||||
// 以 tabs 组件为例
|
||||
var tabs = layui.tabs;
|
||||
|
||||
// 获得 tabs 组件构造函数
|
||||
var Class = tabs.Class;
|
||||
|
||||
// 重构 tabs 组件内部的 xxx 方法(不推荐)
|
||||
Class.prototype.xxx = function() {
|
||||
// …
|
||||
};
|
||||
```
|
||||
|
||||
您也可以直接参考 tabs 组件源码中关于扩展原型的具体实践。
|
||||
|
||||
## 💖 心语
|
||||
|
||||
Layui 由于早前欠缺统筹性思维,很多组件自成一体,使得无法对组件进行很好的统一管理。随着版本的迭代,我们也一直在努力尝试改善这一问题,但很多时候,为了向下兼容而不得不保留许多旧有的特性。`component` 模块的初衷正是为了确保组件的一致性,如核心逻辑和 API 设计等,其目的也是为了让 2.x 系列版本尽可能地减少些遗憾,让 Layui 在既定的范式中保持前行。
|
||||
|
||||
@ -18,7 +18,7 @@ toc: true
|
||||
<td>用途:用于更简单快速地构建网页界面</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>环境:全部主流 Web 浏览器(IE8 以下除外)</td>
|
||||
<td>环境:<a href="/notes/browser-support.html" target="_blank">详见不同版本的浏览器兼容规划</a> <sup>N</sup></td>
|
||||
<td>特性:原生态开发 / 轻量级模块化 / 外简内丰 / 开箱即用</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@ -2,8 +2,10 @@
|
||||
title: 选项卡组件 tab
|
||||
toc: true
|
||||
---
|
||||
|
||||
# 选项卡组件
|
||||
|
||||
# ~~选项卡组件~~
|
||||
|
||||
> 📣 <span style="color: #ff5722;">升级提示:我们在 2.10 版本中新增了全新的 tabs 标签页组件,用于替代原 `element` 模块中的 `tab` 组件,建议过渡到全新的 tabs 组件,旧的 ~~tab~~ 组件将在后续合适的版本中移除。</span> [前往全新 tabs 组件](../tabs/)
|
||||
|
||||
> 选项卡组件 `tab` 是指可进行标签页切换的一段容器,常广泛应用于 Web 页面。由于为了向下兼容等诸多历史原因,在 2.x 版本中,`tab` 组件属于 `element` 模块的子集。
|
||||
|
||||
@ -159,7 +161,7 @@ tab 组件会在元素加载完毕后,自动对 tab 元素完成一次渲染
|
||||
|
||||
```
|
||||
<div id="test"></div>
|
||||
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function(){
|
||||
@ -284,7 +286,7 @@ layui.use(function(){
|
||||
|
||||
```
|
||||
var element = layui.element;
|
||||
|
||||
|
||||
// tab 切换事件
|
||||
element.on('tab(filter)', function(data){
|
||||
console.log(this); // 当前 tab 标题所在的原始 DOM 元素
|
||||
@ -307,7 +309,7 @@ element.on('tab(filter)', function(data){
|
||||
|
||||
```
|
||||
var element = layui.element;
|
||||
|
||||
|
||||
// tab 切换前的事件
|
||||
element.on('tabBeforeChange(filter)', function(data){
|
||||
console.log(data.elem); // 得到当前的 tab 容器
|
||||
@ -333,7 +335,7 @@ element.on('tabBeforeChange(filter)', function(data){
|
||||
|
||||
```
|
||||
var element = layui.element;
|
||||
|
||||
|
||||
// tab 删除事件
|
||||
element.on('tabDelete(filter)', function(data){
|
||||
console.log(data.index); // 得到被删除的 tab 项的所在下标
|
||||
|
||||
63
docs/tabs/detail/demo.md
Normal file
63
docs/tabs/detail/demo.md
Normal file
@ -0,0 +1,63 @@
|
||||
<h3 lay-toc="{level: 2, id: 'examples', hot: true}" class="layui-hide">动态操作</h3>
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, text: {preview: '动态操作'}, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full'], done: function(obj){
|
||||
obj.render();
|
||||
}}">
|
||||
<textarea>
|
||||
{{- d.include("/tabs/examples/demo.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<h3 id="demo-method" lay-toc="{level: 2}">方法渲染</h3>
|
||||
|
||||
即通过方法设置 tabs 标签,而非默认的自动渲染页面中的 `class="layui-tabs"` 的容器模板。
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], tools: ['full'], codeStyle: 'max-height: 520px;'}">
|
||||
<textarea>
|
||||
{{- d.include("/tabs/examples/method.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<h3 id="demo-card" lay-toc="{level: 2}">卡片风格</h3>
|
||||
|
||||
<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/card.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<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']}">
|
||||
<textarea>
|
||||
{{- d.include("/tabs/examples/trigger.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<h3 id="demo-hash" lay-toc="{level: 2, title: 'HASH 匹配'}">通过 HASH 匹配选中标签</h3>
|
||||
|
||||
切换 tabs 标签项后,地址栏同步 `hash` 值,当页面刷新时,tabs 仍保持对应的切换状态。
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full']}">
|
||||
<textarea>
|
||||
{{- d.include("/tabs/examples/hash.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<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']}">
|
||||
<textarea>
|
||||
{{- d.include("/tabs/examples/nest.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<h3 id="demo-custom" lay-toc="{level: 2, title: '自定义标签'}">给任意元素绑定 tabs 切换功能</h3>
|
||||
|
||||
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], codeStyle: 'max-height: 520px;', tools: ['full']}">
|
||||
<textarea>
|
||||
{{- d.include("/tabs/examples/custom.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
159
docs/tabs/detail/options.md
Normal file
159
docs/tabs/detail/options.md
Normal file
@ -0,0 +1,159 @@
|
||||
<table class="layui-table">
|
||||
<colgroup>
|
||||
<col width="150">
|
||||
<col>
|
||||
<col width="100">
|
||||
<col width="100">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>属性名</th>
|
||||
<th>描述</th>
|
||||
<th>类型</th>
|
||||
<th>默认值</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>elem</td>
|
||||
<td>
|
||||
|
||||
组件渲染指定的目标元素选择器或 DOM 对象
|
||||
|
||||
</td>
|
||||
<td>string/DOM</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr>
|
||||
<td>id</td>
|
||||
<td>
|
||||
|
||||
组件渲染的唯一实例 ID
|
||||
|
||||
</td>
|
||||
<td>string</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>className</td>
|
||||
<td>
|
||||
|
||||
给主容器追加 CSS 类名,以便自定义样式
|
||||
|
||||
</td>
|
||||
<td>string</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>trigger</td>
|
||||
<td>
|
||||
|
||||
标签切换的触发事件
|
||||
|
||||
</td>
|
||||
<td>boolean</td>
|
||||
<td>
|
||||
|
||||
`click`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>headerMode</td>
|
||||
<td>
|
||||
|
||||
标签头部的显示模式。可选值有:
|
||||
|
||||
- `auto` 自动模式,根据标签头部是否溢出自动显示不同模式
|
||||
- `scroll` 始终滚动模式
|
||||
- `normal` 始终常规模式,不渲染头部滚动结构
|
||||
|
||||
</td>
|
||||
<td>string</td>
|
||||
<td>
|
||||
|
||||
`auto`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>index</td>
|
||||
<td>
|
||||
|
||||
初始选中的标签索引或标签 `lay-id` 属性值
|
||||
|
||||
</td>
|
||||
<td>number</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>closable</td>
|
||||
<td>
|
||||
|
||||
是否开启标签项关闭功能
|
||||
|
||||
</td>
|
||||
<td>boolean</td>
|
||||
<td>
|
||||
|
||||
`false`
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>header</td>
|
||||
<td colspan="3">
|
||||
|
||||
设置标签头部列表,通常在「非自动渲染」的场景下使用:
|
||||
|
||||
**1. 方法渲染**
|
||||
|
||||
若 `header` 传入一个数组,且成员值为对象,即为方法渲染时传入的头部列表数据。如:
|
||||
|
||||
```js
|
||||
header: [
|
||||
{ title: 'Tab1' }, // 除 `title` 为必传项外,也可传入其他任意字段。
|
||||
{ title: 'Tab2' }
|
||||
]
|
||||
```
|
||||
|
||||
**2. 任意元素渲染**
|
||||
|
||||
若 `header` 传入一个数组,则成员值为元素选择器,即为绑定标签头部列表元素。如:
|
||||
|
||||
```js
|
||||
header: ['#tabsHeader', '>li'],
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>body</td>
|
||||
<td colspan="3">
|
||||
|
||||
设置标签内容列表,通常在「非自动渲染」的场景下使用:
|
||||
|
||||
**1. 方法渲染**
|
||||
|
||||
若 `body` 传入一个数组,且成员值为对象,即为方法渲染时传入的标签内容列表数据。如:
|
||||
|
||||
```js
|
||||
body: [
|
||||
{ content: 'Tab1' }, // `content` 为必传项
|
||||
{ content: 'Tab2' }
|
||||
]
|
||||
```
|
||||
|
||||
**2. 任意元素渲染**
|
||||
|
||||
若 `body` 传入一个数组,则成员值为元素选择器,即为绑定标签内容列表元素。如:
|
||||
|
||||
```js
|
||||
body: ['#tabsBody', '>div'],
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
44
docs/tabs/examples/beforeChange.md
Normal file
44
docs/tabs/examples/beforeChange.md
Normal file
@ -0,0 +1,44 @@
|
||||
<div class="layui-tabs layui-hide-v" id="demoTabsBeforeChange">
|
||||
<ul class="layui-tabs-header">
|
||||
<li lay-id="aaa">Tab1</li>
|
||||
<li lay-id="bbb">Tab2</li>
|
||||
<li lay-id="ccc">Tab3</li>
|
||||
<li lay-id="ddd">Tab4</li>
|
||||
<li lay-id="eee">Tab5</li>
|
||||
<li lay-id="fff">Tab6</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item">Tab Content-1</div>
|
||||
<div class="layui-tabs-item">Tab Content-2</div>
|
||||
<div class="layui-tabs-item">Tab Content-3</div>
|
||||
<div class="layui-tabs-item">Tab Content-4</div>
|
||||
<div class="layui-tabs-item">Tab Content-5</div>
|
||||
<div class="layui-tabs-item">Tab Content-6</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
本示例演示:切换标签时,弹出确认提示。
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function() {
|
||||
var tabs = layui.tabs;
|
||||
|
||||
// 标签实例 ID
|
||||
var DEMO_TABS_ID = 'demoTabsBeforeChange';
|
||||
|
||||
// tabs 切换前的事件
|
||||
tabs.on(`beforeChange(${DEMO_TABS_ID})`, function(data) {
|
||||
console.log('beforeChange', data);
|
||||
|
||||
// 切换确认提示
|
||||
layer.confirm(`确定从「当前标签」切换到标签「${this.innerText}」吗?`, function(i) {
|
||||
tabs.change(DEMO_TABS_ID, data.to.index, true); // 强制切换
|
||||
layer.close(i); // 关闭确认框
|
||||
});
|
||||
|
||||
// 阻止标签默认关闭
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
44
docs/tabs/examples/beforeClose.md
Normal file
44
docs/tabs/examples/beforeClose.md
Normal file
@ -0,0 +1,44 @@
|
||||
<div class="layui-tabs layui-hide-v" id="demoTabsBeforeClose" lay-options="{closable: true}">
|
||||
<ul class="layui-tabs-header">
|
||||
<li lay-id="aaa" lay-closable="false">Tab1</li>
|
||||
<li lay-id="bbb">Tab2</li>
|
||||
<li lay-id="ccc">Tab3</li>
|
||||
<li lay-id="ddd">Tab4</li>
|
||||
<li lay-id="eee">Tab5</li>
|
||||
<li lay-id="fff">Tab6</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item">Tab Content-1</div>
|
||||
<div class="layui-tabs-item">Tab Content-2</div>
|
||||
<div class="layui-tabs-item">Tab Content-3</div>
|
||||
<div class="layui-tabs-item">Tab Content-4</div>
|
||||
<div class="layui-tabs-item">Tab Content-5</div>
|
||||
<div class="layui-tabs-item">Tab Content-6</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
本示例演示:删除标签之前,弹出确认提示。
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function() {
|
||||
var tabs = layui.tabs;
|
||||
|
||||
// 标签实例 ID
|
||||
var DEMO_TABS_ID = 'demoTabsBeforeClose';
|
||||
|
||||
// tabs 切换前的事件
|
||||
tabs.on(`beforeClose(${DEMO_TABS_ID})`, function(data) {
|
||||
console.log('beforeClose', data);
|
||||
|
||||
// 关闭确认提示
|
||||
layer.confirm(`确定关闭标签「${this.innerText}」吗?`, function(i) {
|
||||
tabs.close(DEMO_TABS_ID, data.index, true); // 强制关闭对应的标签项
|
||||
layer.close(i); // 关闭确认框
|
||||
});
|
||||
|
||||
// 阻止标签默认关闭
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
54
docs/tabs/examples/card.md
Normal file
54
docs/tabs/examples/card.md
Normal file
@ -0,0 +1,54 @@
|
||||
#### 普通卡片
|
||||
|
||||
<div class="layui-tabs layui-tabs-card" lay-options="{index: 1}">
|
||||
<ul class="layui-tabs-header">
|
||||
<li>标题1</li>
|
||||
<li>标题2</li>
|
||||
<li><a href="" target="_blank" class="layui-font-blue">跳转项</a></li>
|
||||
<li class="layui-disabled" lay-unselect>禁选项</li>
|
||||
<li>标题5</li>
|
||||
<li>标题6</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item">内容-1</div>
|
||||
<div class="layui-tabs-item">内容-2</div>
|
||||
<div class="layui-tabs-item">内容-3</div>
|
||||
<div class="layui-tabs-item">内容-4</div>
|
||||
<div class="layui-tabs-item">内容-5</div>
|
||||
<div class="layui-tabs-item">内容-6</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
#### 边框卡片
|
||||
|
||||
<div class="layui-tabs layui-tabs-card layui-panel layui-inline">
|
||||
<ul class="layui-tabs-header layui-bg-tint">
|
||||
<li class="layui-this">标题1</li>
|
||||
<li>标题2</li>
|
||||
<li>标题3</li>
|
||||
<li>标题4</li>
|
||||
<li>标题5</li>
|
||||
<li>标题6</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item layui-show">
|
||||
<div class="layui-form">
|
||||
<select>
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
<option>6</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tabs-item">2</div>
|
||||
<div class="layui-tabs-item">3</div>
|
||||
<div class="layui-tabs-item">4</div>
|
||||
<div class="layui-tabs-item">5</div>
|
||||
<div class="layui-tabs-item">6</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- import layui -->
|
||||
30
docs/tabs/examples/custom.md
Normal file
30
docs/tabs/examples/custom.md
Normal file
@ -0,0 +1,30 @@
|
||||
<div id="demoTabs3">
|
||||
<style>
|
||||
#demoTabsHeader .layui-btn.layui-this{border-color: #eee; color: #000; background: none;}
|
||||
#demoTabsBody .test-item{display: none;}
|
||||
</style>
|
||||
<div class="layui-btn-container" id="demoTabsHeader">
|
||||
<button class="layui-btn layui-this">标题 1</button>
|
||||
<button class="layui-btn">标题 2</button>
|
||||
<button class="layui-btn">标题 3</button>
|
||||
</div>
|
||||
<div class="layui-panel layui-padding-3" id="demoTabsBody">
|
||||
<div class="test-item layui-show">内容 111</div>
|
||||
<div class="test-item">内容 222</div>
|
||||
<div class="test-item">内容 333</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function(){
|
||||
var tabs = layui.tabs;
|
||||
|
||||
// 给任意元素绑定 Tab 功能
|
||||
tabs.render({
|
||||
elem: '#demoTabs3',
|
||||
header: ['#demoTabsHeader', '>button'],
|
||||
body: ['#demoTabsBody', '>.test-item']
|
||||
});
|
||||
});
|
||||
</script>
|
||||
109
docs/tabs/examples/demo.md
Normal file
109
docs/tabs/examples/demo.md
Normal file
@ -0,0 +1,109 @@
|
||||
<div class="layui-tabs layui-hide-v" id="demoTabs1" lay-options="{closable: true, headerMode:'scroll'}">
|
||||
<ul class="layui-tabs-header">
|
||||
<li lay-id="aaa" lay-closable="false">Tab1</li>
|
||||
<li lay-id="bbb">Tab2</li>
|
||||
<li lay-id="ccc">Tab3</li>
|
||||
<li lay-id="ddd">Tab4</li>
|
||||
<li lay-id="eee">Tab5</li>
|
||||
<li lay-id="fff">Tab6</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item">Tab Content-1</div>
|
||||
<div class="layui-tabs-item">Tab Content-2</div>
|
||||
<div class="layui-tabs-item">Tab Content-3</div>
|
||||
<div class="layui-tabs-item">Tab Content-4</div>
|
||||
<div class="layui-tabs-item">Tab Content-5</div>
|
||||
<div class="layui-tabs-item">Tab Content-6</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
🔔 操作提示:在「标签头部」点击鼠标右键,可开启标签操作的更多实用演示
|
||||
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn" onclick="layui.tabs.change('demoTabs1', 'ccc')">切换到:Tab3</button>
|
||||
<button class="layui-btn" onclick="layui.tabs.change('demoTabs1', 1)">切换到:第 2 项</button>
|
||||
<button class="layui-btn" onclick="layui.tabs.close('demoTabs1', 'ddd')">关闭:Tab4</button>
|
||||
<button class="layui-btn" onclick="layui.tabs.close('demoTabs1', 1)">关闭:第 2 项</button>
|
||||
<button class="layui-btn" lay-on="add">添加 Tab</button>
|
||||
</div>
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function() {
|
||||
var $ = layui.$;
|
||||
var tabs = layui.tabs;
|
||||
var util = layui.util;
|
||||
var dropdown = layui.dropdown;
|
||||
|
||||
// 为标签头添加上下文菜单
|
||||
var dropdownInst = dropdown.render({
|
||||
elem: '#demoTabs1 .layui-tabs-header>li',
|
||||
trigger: 'contextmenu',
|
||||
data: [{
|
||||
title: '在右侧新增标签页',
|
||||
action: 'add',
|
||||
mode: 'after'
|
||||
}, {
|
||||
type: '-'
|
||||
}, {
|
||||
title: '关闭',
|
||||
action: 'close',
|
||||
mode: 'this',
|
||||
}, {
|
||||
title: '关闭其他标签页',
|
||||
action: 'close',
|
||||
mode: 'other'
|
||||
}, {
|
||||
title: '关闭右侧标签页',
|
||||
action: 'close',
|
||||
mode: 'right'
|
||||
}, {
|
||||
title: '关闭所有标签页',
|
||||
action: 'close',
|
||||
mode: 'all'
|
||||
}],
|
||||
click: function(data, othis, event) {
|
||||
var index = this.elem.index(); // 获取活动标签索引
|
||||
|
||||
// 新增标签操作
|
||||
if (data.action === 'add') {
|
||||
// 在当前活动标签右侧新增标签页
|
||||
addTabs({
|
||||
mode: data.mode,
|
||||
index: index
|
||||
});
|
||||
} else if(data.action === 'close') { // 关闭标签操作
|
||||
if (data.mode === 'this') {
|
||||
tabs.close('demoTabs1', index); // 关闭当前标签
|
||||
} else {
|
||||
tabs.closeMult('demoTabs1', data.mode, index); // 批量关闭标签
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 新增随机标签
|
||||
var addTabs = function(opts) {
|
||||
var n = Math.random()*1000 | 0; // 演示标记
|
||||
opts = $.extend({
|
||||
title: 'New Tab '+ n, // 此处加 n 仅为演示区分,实际应用不需要
|
||||
content: 'New Tab Content '+ n,
|
||||
id: 'new-'+ n,
|
||||
aaa: 'attr-'+ n, // 自定义属性,其中 aaa 可任意命名
|
||||
done: function(params) {
|
||||
console.log(params);
|
||||
dropdownInst.reload();
|
||||
}
|
||||
}, opts);
|
||||
// 添加标签到最后
|
||||
tabs.add('demoTabs1', opts);
|
||||
}
|
||||
|
||||
|
||||
// 自定义事件
|
||||
util.on({
|
||||
add: function(){
|
||||
addTabs();
|
||||
}
|
||||
});
|
||||
});
|
||||
23
docs/tabs/examples/hash.md
Normal file
23
docs/tabs/examples/hash.md
Normal file
@ -0,0 +1,23 @@
|
||||
<div class="layui-tabs layui-hide-v" id="demoTabs-hash">
|
||||
<ul class="layui-tabs-header">
|
||||
<li lay-id="A1" class="layui-this"><a href="#A1">标题题题题题题1</a></li>
|
||||
<li lay-id="A2"><a href="#A2">标题题题2</a></li>
|
||||
<li lay-id="A3"><a href="#A3">标题3</a></li>
|
||||
<li lay-id="A4"><a href="#A4">标题题题题题题题4</a></li>
|
||||
<li lay-id="A5"><a href="#A5">标题5</a></li>
|
||||
<li lay-id="A6"><a href="#A6">标题6</a></li>
|
||||
<li lay-id="A7"><a href="#A7">标题7</a></li>
|
||||
<li lay-id="A8"><a href="#A8">标题题题题题题题8</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function(){
|
||||
var tabs = layui.tabs;
|
||||
|
||||
// HASH 初始定位
|
||||
var hash = layui.hash();
|
||||
tabs.change('demoTabs-hash', hash.href);
|
||||
});
|
||||
</script>
|
||||
26
docs/tabs/examples/method.md
Normal file
26
docs/tabs/examples/method.md
Normal file
@ -0,0 +1,26 @@
|
||||
<div id="demoTabs2"></div>
|
||||
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function(){
|
||||
var tabs = layui.tabs;
|
||||
|
||||
// 方法渲染
|
||||
tabs.render({
|
||||
elem: '#demoTabs2',
|
||||
header: [
|
||||
{ title: 'Tab1' },
|
||||
{ title: 'Tab2' },
|
||||
{ title: 'Tab3' }
|
||||
],
|
||||
body: [
|
||||
{ content: 'Tab content 1' },
|
||||
{ content: 'Tab content 2' },
|
||||
{ content: 'Tab content 3' }
|
||||
],
|
||||
// index: 1, // 初始选中项
|
||||
// className: 'layui-tabs-card',
|
||||
// closable: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
40
docs/tabs/examples/nest.md
Normal file
40
docs/tabs/examples/nest.md
Normal file
@ -0,0 +1,40 @@
|
||||
<div class="layui-tabs layui-tabs-card" lay-options="{headerMode:'normal'}">
|
||||
<ul class="layui-tabs-header">
|
||||
<li class="layui-this">标题1</li>
|
||||
<li>标题2</li>
|
||||
<li>标题3</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body" style="padding: 16px;">
|
||||
<div class="layui-tabs-item layui-show">
|
||||
<div class="layui-tabs" lay-options="{headerMode:'normal'}">
|
||||
<ul class="layui-tabs-header">
|
||||
<li class="layui-this">标题 1-1</li>
|
||||
<li>标题 1-2</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item layui-show">1-1</div>
|
||||
<div class="layui-tabs-item">1-2</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tabs-item">
|
||||
<div class="layui-tabs" lay-options="{headerMode:'normal'}">
|
||||
<ul class="layui-tabs-header">
|
||||
<li class="layui-this">标题 2-1</li>
|
||||
<li>标题 2-2</li>
|
||||
<li>标题 2-3</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item layui-show">2-1</div>
|
||||
<div class="layui-tabs-item">2-2</div>
|
||||
<div class="layui-tabs-item">2-3</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tabs-item">3</div>
|
||||
<div class="layui-tabs-item">4</div>
|
||||
<div class="layui-tabs-item">5</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- import layui -->
|
||||
43
docs/tabs/examples/trigger.md
Normal file
43
docs/tabs/examples/trigger.md
Normal file
@ -0,0 +1,43 @@
|
||||
#### mouseenter 触发
|
||||
|
||||
<div class="layui-tabs layui-tabs-card layui-panel" lay-options="{trigger: 'mouseenter'}">
|
||||
<ul class="layui-tabs-header layui-bg-tint">
|
||||
<li class="layui-this">标题1</li>
|
||||
<li>标题2</li>
|
||||
<li>标题3</li>
|
||||
<li>标题4</li>
|
||||
<li>标题5</li>
|
||||
<li>标题6</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item layui-show">内容-1</div>
|
||||
<div class="layui-tabs-item">内容-2</div>
|
||||
<div class="layui-tabs-item">内容-3</div>
|
||||
<div class="layui-tabs-item">内容-4</div>
|
||||
<div class="layui-tabs-item">内容-5</div>
|
||||
<div class="layui-tabs-item">内容-6</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
#### mousedown 触发
|
||||
|
||||
<div class="layui-tabs layui-tabs-card layui-panel" lay-options="{trigger: 'mousedown'}">
|
||||
<ul class="layui-tabs-header layui-bg-tint">
|
||||
<li class="layui-this">标题1</li>
|
||||
<li>标题2</li>
|
||||
<li>标题3</li>
|
||||
<li>标题4</li>
|
||||
<li>标题5</li>
|
||||
<li>标题6</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item layui-show">内容-1</div>
|
||||
<div class="layui-tabs-item">内容-2</div>
|
||||
<div class="layui-tabs-item">内容-3</div>
|
||||
<div class="layui-tabs-item">内容-4</div>
|
||||
<div class="layui-tabs-item">内容-5</div>
|
||||
<div class="layui-tabs-item">内容-6</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- import layui -->
|
||||
350
docs/tabs/index.md
Normal file
350
docs/tabs/index.md
Normal file
@ -0,0 +1,350 @@
|
||||
---
|
||||
title: 标签页组件 tabs
|
||||
toc: true
|
||||
---
|
||||
|
||||
# 标签页组件 <sup>2.10+</sup>
|
||||
|
||||
> `tabs` 是 2.10 版本新增的加强型组件,可替代原 `element` 模块中的 `tab` 组件。tabs 广泛应用于 Web 页面。
|
||||
|
||||
<h2 id="examples" lay-toc="{anchor: null}" style="margin-bottom: 0;">示例</h2>
|
||||
|
||||
<div class="ws-docs-showcase"></div>
|
||||
|
||||
<div>
|
||||
{{- d.include("/tabs/detail/demo.md") }}
|
||||
</div>
|
||||
|
||||
<h2 id="api" lay-toc="{hot: true}">API</h2>
|
||||
|
||||
| API | 描述 |
|
||||
| --- | --- |
|
||||
| var tabs = layui.tabs | 获得 `tabs` 模块。|
|
||||
| [基础接口](../component/#export) | 该组件由 `component` 构建,因此继承其提供的基础接口。|
|
||||
| [tabs.render(options)](#render) | tabs 组件渲染,核心方法。|
|
||||
| [tabs.add(id, opts)](#add) | 新增一个标签项。|
|
||||
| [tabs.close(id, index, force)](#close) | 关闭指定的标签项。|
|
||||
| [tabs.closeMult(id, mode, index)](#closeMult) | 批量关闭标签项。|
|
||||
| [tabs.change(id, index, force)](#change) | 切换到指定的标签项。|
|
||||
| [tabs.data(id)](#data) | 获取当前标签页相关数据。|
|
||||
| [tabs.getHeaderItem(id, index)](#getHeaderItem) | 获取指定的标签头部项。|
|
||||
| [tabs.getBodyItem(id, index)](#getBodyItem) | 获取指定的标签内容项。|
|
||||
| [tabs.refresh(id)](#refresh) | 刷新标签视图。 |
|
||||
|
||||
<h3 id="render" lay-toc="{level: 2}">渲染</h3>
|
||||
|
||||
`tabs.render(options)`
|
||||
|
||||
- 参数 `options` : 基础属性配置项。[#详见属性](#options)
|
||||
|
||||
组件支持以下三种渲染方式:
|
||||
|
||||
#### 1. 自动渲染
|
||||
|
||||
tabs 组件会在元素加载完毕后,自动对 `class="layui-tabs"` 目标元素完成一次渲染,若无法找到默认的目标元素(如:动态插入的标签元素的场景),则可通过该方法完成对标签页的初始化渲染。
|
||||
|
||||
```js
|
||||
// 对 class="layui-tabs" 所在标签进行初始化渲染
|
||||
tabs.render();
|
||||
```
|
||||
|
||||
#### 2. 方法渲染
|
||||
|
||||
通过方法动态渲染一个 tabs 组件,无需在 HTML 中书写标签页的 HTML 结构。
|
||||
|
||||
```js
|
||||
<div id="test"></div>
|
||||
<!-- import layui -->
|
||||
<script>
|
||||
layui.use(function(){
|
||||
var tabs = layui.tabs;
|
||||
tabs.render({
|
||||
elem: '#test',
|
||||
header: [
|
||||
{ title: 'Tab1' },
|
||||
{ title: 'Tab2' }
|
||||
],
|
||||
body: [
|
||||
{ content: 'Tab content 1' },
|
||||
{ content: 'Tab content 2' }
|
||||
],
|
||||
index: 0, // 初始选中标签索引
|
||||
})
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
#### 3. 为任意元素渲染 tabs 功能
|
||||
|
||||
当 `header` 和 `body` 参数传入元素选择器时,可为任意元素绑定标签切换功能。
|
||||
|
||||
```js
|
||||
// 给任意元素绑定 Tab 功能
|
||||
tabs.render({
|
||||
elem: '#demoTabs3', // 目标主容器选择器
|
||||
header: ['#demoTabsHeader', '>button'], // 标签头部主元素选择器、标签头部列表选择器
|
||||
body: ['#demoTabsBody', '>.test-item'] // 标签内容主元素选择器、标签内容列表选择器
|
||||
});
|
||||
```
|
||||
|
||||
具体用法可直接参考上述示例:[给任意元素绑定 tabs 切换功能](#demo-custom)
|
||||
|
||||
|
||||
<h3 id="options" lay-toc="{level: 2}">属性</h3>
|
||||
|
||||
<div>
|
||||
{{- d.include("/tabs/detail/options.md") }}
|
||||
</div>
|
||||
|
||||
<h3 id="add" class="ws-anchor ws-bold">新增标签</h3>
|
||||
|
||||
`tabs.add(id, opts)`
|
||||
|
||||
- 参数 `id` : 组件的实例 ID
|
||||
- 参数 `opts` : 标签配置项。可选项详见下表
|
||||
|
||||
| opts | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| title | 标签标题。必填项 | string | - |
|
||||
| content | 标签内容。必填项 | string | - |
|
||||
| id | 标签的 `lay-id` 属性值 | string | - |
|
||||
| index | 活动标签的索引或 `lay-id` 属性值,默认取当前选中标签的索引 | number | - |
|
||||
| mode | 标签的插入方式。支持以下可选值:<ul><li>`append` 插入标签到最后</li> <li>`prepend` 插入标签到最前</li> <li>`before` 在活动标签前插入</li> <li>`after` 在活动标签后插入</li></ul> | string | `append` |
|
||||
| closable | 标签是否可关闭。初始值取决于 `options.closable` | boolean | `false` |
|
||||
| headerItem | 自定义标签头部元素,如 `headerItem: '<li></li>'` | string | - |
|
||||
| bodyItem | 自定义标签内容元素,如 `bodyItem: '<div></div>'` | string | - |
|
||||
| done | 标签添加成功后执行的回调函数 | Function | - |
|
||||
|
||||
该方法用于给对应的 tabs 实例新增一个标签
|
||||
|
||||
```js
|
||||
tabs.add('test', {
|
||||
title: 'New Tab 1',
|
||||
content: 'New Tab Content 1',
|
||||
});
|
||||
```
|
||||
|
||||
<h3 id="close" class="ws-anchor ws-bold">关闭标签</h3>
|
||||
|
||||
`tabs.close(id, index, force)`
|
||||
|
||||
- 参数 `id` : 组件的实例 ID
|
||||
- 参数 `index` : 标签索引或标签的 `lay-id` 属性值
|
||||
- 参数 `force` : 是否强制关闭。若设置 `true` 将忽略 `beforeClose` 事件行为。默认 `false`
|
||||
|
||||
该方法用于关闭指定的标签项。
|
||||
|
||||
```js
|
||||
tabs.close('test', 3); // 关闭索引为 3 的标签
|
||||
tabs.close('test', 3, true); // 强制关闭索引为 3 的标签
|
||||
tabs.close('test', 'abc'); // 关闭 lay-id="abc" 的标签
|
||||
```
|
||||
|
||||
<h3 id="closeMult" class="ws-anchor ws-bold">批量关闭标签</h3>
|
||||
|
||||
`tabs.closeMult(id, mode, index)`
|
||||
|
||||
- 参数 `id` : 组件的实例 ID
|
||||
- 参数 `mode` : 关闭方式。支持以下可选值:
|
||||
|
||||
| mode | 描述 |
|
||||
| --- | --- |
|
||||
| other | 关闭除当前标签外的所有标签 |
|
||||
| right | 关闭当前标签及右侧标签 |
|
||||
| all | 关闭所有标签 |
|
||||
|
||||
- 参数 `index` : 活动标签的索引或 `lay-id` 属性值,默认取当前选中标签的索引。一般用于标签右键事件。
|
||||
|
||||
该方法用于批量关闭标签。
|
||||
|
||||
```js
|
||||
tabs.closeMult(id, 'other'); // 关闭除当前标签外的所有标签
|
||||
tabs.closeMult(id, 'other', 3); // 关闭除索引为 3 的标签外的所有标签
|
||||
tabs.closeMult(id, 'right'); // 关闭当前标签及右侧标签
|
||||
tabs.closeMult(id, 'right', 3); // 关闭索引为 3 的标签的右侧所有标签
|
||||
tabs.closeMult(id, 'all'); // 关闭所有标签
|
||||
```
|
||||
|
||||
<h3 id="change" class="ws-anchor ws-bold">切换标签</h3>
|
||||
|
||||
`tabs.change(id, index, force)`
|
||||
|
||||
- 参数 `id` : 组件的实例 ID
|
||||
- 参数 `index` : 标签索引或标签的 `lay-id` 属性值
|
||||
- 参数 `force` : 是否强制切换。若设置 `true` 将忽略 `beforeChange` 事件行为。默认 false
|
||||
|
||||
该方法用于切换到指定的标签项。
|
||||
|
||||
```js
|
||||
tabs.change('test', 3); // 切换到索引为 3 的标签
|
||||
tabs.change('test', 3, true); // 强制切换到索引为 3 的标签
|
||||
tabs.change('test', 'abc'); // 切换到 lay-id="abc" 的标签
|
||||
tabs.change('test', 'abc', true); // 强制切换到 lay-id="abc" 的标签
|
||||
```
|
||||
|
||||
<h3 id="data" class="ws-anchor ws-bold">获取标签相关数据</h3>
|
||||
|
||||
`tabs.data(id)`
|
||||
|
||||
- 参数 `id` : 组件的实例 ID
|
||||
|
||||
该方法用于获取标签相关数据。
|
||||
|
||||
```js
|
||||
var data = tabs.data('test');
|
||||
console.log(data);
|
||||
```
|
||||
|
||||
返回的 `data` 包含以下字段:
|
||||
|
||||
```js
|
||||
{
|
||||
options, // 标签配置信息
|
||||
container, // 标签容器的相关元素
|
||||
thisHeaderItem, // 当前标签头部项
|
||||
thisBodyItem, // 当前标签内容项
|
||||
index, // 当前标签索引
|
||||
length, // 当前标签数
|
||||
}
|
||||
```
|
||||
|
||||
<h3 id="getHeaderItem" class="ws-anchor ws-bold">获取标签头部项</h3>
|
||||
|
||||
`tabs.getHeaderItem(id, index)`
|
||||
|
||||
- 参数 `id` : 组件的实例 ID
|
||||
- 参数 `index` : 标签索引或标签的 `lay-id` 属性值
|
||||
|
||||
该方法用于获取标签头部项元素。
|
||||
|
||||
```js
|
||||
var headerItem = tabs.getHeaderItem('test', 3); // 获取索引为 3 的标签头部项元素
|
||||
```
|
||||
|
||||
<h3 id="getBodyItem" class="ws-anchor ws-bold">获取标签内容项</h3>
|
||||
|
||||
`tabs.getBodyItem(id, index)`
|
||||
|
||||
- 参数 `id` : 组件的实例 ID
|
||||
- 参数 `index` : 标签索引或标签的 `lay-id` 属性值
|
||||
|
||||
该方法用于获取标签内容项元素。
|
||||
|
||||
```js
|
||||
var bodyItem = tabs.getBodyItem('test', 3); // 获取索引为 3 的标签内容项元素
|
||||
```
|
||||
|
||||
<h3 id="refresh" class="ws-anchor ws-bold">刷新标签视图</h3>
|
||||
|
||||
`tabs.refresh(id)`
|
||||
|
||||
- 参数 `id` : 组件的实例 ID
|
||||
|
||||
该方法用于刷新标签视图,如标签头部的滚动结构等,一般通过非 API 方式对标签进行修改的场景中使用。
|
||||
|
||||
```js
|
||||
tabs.refresh('test'); // 刷新标签视图
|
||||
```
|
||||
|
||||
|
||||
<h2 id="on" lay-toc="{hot: true}">事件</h2>
|
||||
|
||||
`tabs.on('event(id)', callback)`
|
||||
|
||||
- 参数介绍详见 `component` 组件的[事件定义](../component/#on)。以下是组件提供的 `event` 事件列表
|
||||
|
||||
| event | 描述 |
|
||||
| --- | --- |
|
||||
| [afterRender](#on-afterRender) | 标签渲染后的事件 |
|
||||
| [beforeChange](#on-beforeChange) | 标签切换前的事件 |
|
||||
| [afterChange](#on-afterChange) | 标签切换后的事件 |
|
||||
| [beforeClose](#on-beforeClose) | 标签关闭前的事件 |
|
||||
| [afterClose](#on-afterClose) | 标签关闭后的事件 |
|
||||
|
||||
|
||||
<h3 id="on-afterRender" class="ws-anchor ws-bold">标签渲染后的事件</h3>
|
||||
|
||||
`tabs.on('afterRender(id)', callback)`
|
||||
|
||||
标签渲染成功后触发。
|
||||
|
||||
```js
|
||||
tabs.on('afterRender(testID)', function(data){
|
||||
console.log(data); // 标签相关数据
|
||||
});
|
||||
```
|
||||
|
||||
<h3 id="on-beforeChange" class="ws-anchor ws-bold">标签切换前的事件</h3>
|
||||
|
||||
`tabs.on('beforeChange(id)', callback)`
|
||||
|
||||
标签在切换前触发,通过在事件中 `return false` 可阻止默认标签切换行为。通常和 `tabs.change()` 方法搭配使用。
|
||||
|
||||
```js
|
||||
// tabs 切换前的事件
|
||||
tabs.on(`beforeChange(testID)`, function(data) {
|
||||
console.log(data); // 标签相关数据
|
||||
console.log(data.from.index); // 切换前的选中标签索引
|
||||
console.log(data.from.headerItem); // 切换前的选中标签头部项
|
||||
console.log(data.to.index); // 切换后的选中标签索引
|
||||
console.log(data.to.headerItem); // 切换后的选中标签头部项
|
||||
|
||||
// 阻止标签默认关闭
|
||||
return false;
|
||||
});
|
||||
```
|
||||
|
||||
示例演示:
|
||||
|
||||
<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/beforeChange.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<h3 id="on-afterChange" class="ws-anchor ws-bold">标签切换后的事件</h3>
|
||||
|
||||
`tabs.on('afterChange(id)', callback)`
|
||||
|
||||
标签成功切换后触发。
|
||||
|
||||
```js
|
||||
// tabs 切换后的事件
|
||||
tabs.on('afterChange(testID)', function(data) {
|
||||
console.log(data);
|
||||
});
|
||||
```
|
||||
|
||||
<h3 id="on-beforeClose" class="ws-anchor ws-bold">标签关闭前的事件</h3>
|
||||
|
||||
`tabs.on('beforeClose(id)', callback)`
|
||||
|
||||
标签在切换前触发,通过在事件中 `return false` 可阻止默认标签切换行为。通常和 `tabs.close()` 方法搭配使用。
|
||||
|
||||
<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/beforeClose.md") }}
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<h3 id="on-afterClose" class="ws-anchor ws-bold">标签关闭后的事件</h3>
|
||||
|
||||
`tabs.on('afterClose(id)', callback)`
|
||||
|
||||
标签被成功关闭后触发。
|
||||
|
||||
```js
|
||||
// tabs 关闭后的事件
|
||||
tabs.on('afterClose(testID)', function(data) {
|
||||
console.log(data);
|
||||
});
|
||||
```
|
||||
|
||||
## 💖 心语
|
||||
|
||||
tabs 是通过 component 重构的首个组件,它来自于最早试图发布的 Layui 3.0(后因为 3.0 技术路线的变化,而整理放至 2.10+ 版本中),目的是将 element 模块中的 tab 组件进行解耦,增强其可扩展性。为了给开发者必要的时间缓冲,我们会将旧 tab 组件仍然保留在后续的若干版本中,但会在合适的时机对旧 tab 组件进行剔除,建议开发者尽量提前过渡到当前新的 tabs 组件。
|
||||
|
||||
289
examples/tabs.html
Normal file
289
examples/tabs.html
Normal file
@ -0,0 +1,289 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<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">
|
||||
</head>
|
||||
<body>
|
||||
<div class="layui-container layui-padding-3 layui-text">
|
||||
<h2>动态操作</h2>
|
||||
<div class="layui-tabs layui-hide-v" id="demoTabs1" lay-options="{closable: true, headerMode:'scroll'}">
|
||||
<ul class="layui-tabs-header">
|
||||
<li lay-id="aaa" lay-closable="false">Tab1</li>
|
||||
<li lay-id="bbb">Tab2</li>
|
||||
<li lay-id="ccc">Tab3</li>
|
||||
<li lay-id="ddd">Tab4</li>
|
||||
<li lay-id="eee">Tab5</li>
|
||||
<li lay-id="fff">Tab6</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item">Tab Content-1</div>
|
||||
<div class="layui-tabs-item">Tab Content-2</div>
|
||||
<div class="layui-tabs-item">Tab Content-3</div>
|
||||
<div class="layui-tabs-item">Tab Content-4</div>
|
||||
<div class="layui-tabs-item">Tab Content-5</div>
|
||||
<div class="layui-tabs-item">Tab Content-6</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn" onclick="layui.tabs.change('demoTabs1', 'ccc')">切换到:Tab3</button>
|
||||
<button class="layui-btn" onclick="layui.tabs.change('demoTabs1', 1)">切换到:第 2 项</button>
|
||||
<button class="layui-btn" onclick="layui.tabs.close('demoTabs1', 'ddd')">关闭:Tab4</button>
|
||||
<button class="layui-btn" onclick="layui.tabs.close('demoTabs1', 1)">关闭:第 2 项</button>
|
||||
<button class="layui-btn" lay-on="add">添加 Tab</button>
|
||||
</div>
|
||||
|
||||
<h2>方法渲染</h2>
|
||||
<div id="demoTabs2"></div>
|
||||
|
||||
<h2>卡片风格</h2>
|
||||
<div class="layui-tabs layui-tabs-card" lay-options="{index: 1}">
|
||||
<ul class="layui-tabs-header">
|
||||
<li>标题1</li>
|
||||
<li>标题2</li>
|
||||
<li><a href="" target="_blank" class="layui-font-blue">跳转项</a></li>
|
||||
<li class="layui-disabled" lay-unselect>禁选项</li>
|
||||
<li>标题5</li>
|
||||
<li>标题6</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item">内容-1</div>
|
||||
<div class="layui-tabs-item">内容-2</div>
|
||||
<div class="layui-tabs-item">内容-3</div>
|
||||
<div class="layui-tabs-item">内容-4</div>
|
||||
<div class="layui-tabs-item">内容-5</div>
|
||||
<div class="layui-tabs-item">内容-6</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>卡片 + 边框</h3>
|
||||
<div class="layui-tabs layui-tabs-card layui-panel layui-inline">
|
||||
<ul class="layui-tabs-header layui-bg-tint">
|
||||
<li class="layui-this">标题1</li>
|
||||
<li>标题2</li>
|
||||
<li>标题3</li>
|
||||
<li>标题4</li>
|
||||
<li>标题5</li>
|
||||
<li>标题6</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item layui-show">
|
||||
<div class="layui-form">
|
||||
<select>
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
<option>6</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tabs-item">2</div>
|
||||
<div class="layui-tabs-item">3</div>
|
||||
<div class="layui-tabs-item">4</div>
|
||||
<div class="layui-tabs-item">5</div>
|
||||
<div class="layui-tabs-item">6</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>标签 HASH 定位</h2>
|
||||
<div class="layui-tabs layui-hide-v" id="demoTabs-hash">
|
||||
<ul class="layui-tabs-header">
|
||||
<li lay-id="A1" class="layui-this"><a href="#A1">标题题题题题题1</a></li>
|
||||
<li lay-id="A2"><a href="#A2">标题题题2</a></li>
|
||||
<li lay-id="A3"><a href="#A3">标题3</a></li>
|
||||
<li lay-id="A4"><a href="#A4">标题题题题题题题4</a></li>
|
||||
<li lay-id="A5"><a href="#A5">标题5</a></li>
|
||||
<li lay-id="A6"><a href="#A6">标题6</a></li>
|
||||
<li lay-id="A7"><a href="#A7">标题7</a></li>
|
||||
<li lay-id="A8"><a href="#A8">标题题题题题题题8</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2>标签嵌套</h2>
|
||||
<div class="layui-tabs layui-tabs-card" lay-options="{headerMode:'normal'}">
|
||||
<ul class="layui-tabs-header">
|
||||
<li class="layui-this">标题1</li>
|
||||
<li>标题2</li>
|
||||
<li>标题3</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body" style="padding: 16px;">
|
||||
<div class="layui-tabs-item layui-show">
|
||||
<div class="layui-tabs" lay-options="{headerMode:'normal'}">
|
||||
<ul class="layui-tabs-header">
|
||||
<li class="layui-this">标题 1-1</li>
|
||||
<li>标题 1-2</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item layui-show">1-1</div>
|
||||
<div class="layui-tabs-item">1-2</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tabs-item">
|
||||
<div class="layui-tabs" lay-options="{headerMode:'normal'}">
|
||||
<ul class="layui-tabs-header">
|
||||
<li class="layui-this">标题 2-1</li>
|
||||
<li>标题 2-2</li>
|
||||
<li>标题 2-3</li>
|
||||
</ul>
|
||||
<div class="layui-tabs-body">
|
||||
<div class="layui-tabs-item layui-show">2-1</div>
|
||||
<div class="layui-tabs-item">2-2</div>
|
||||
<div class="layui-tabs-item">2-3</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tabs-item">3</div>
|
||||
<div class="layui-tabs-item">4</div>
|
||||
<div class="layui-tabs-item">5</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>给任意元素绑定 tabs 切换功能</h2>
|
||||
<div id="demoTabs3">
|
||||
<style>
|
||||
#demoTabsHeader .layui-btn.layui-this{border-color: #eee; color: #000; background: none;}
|
||||
#demoTabsBody .test-item{display: none;}
|
||||
</style>
|
||||
<div class="layui-btn-container" id="demoTabsHeader">
|
||||
<button class="layui-btn layui-this">标题 1</button>
|
||||
<button class="layui-btn">标题 2</button>
|
||||
<button class="layui-btn">标题 3</button>
|
||||
</div>
|
||||
<div class="layui-panel layui-padding-3" id="demoTabsBody">
|
||||
<div class="test-item layui-show">内容 111</div>
|
||||
<div class="test-item">内容 222</div>
|
||||
<div class="test-item">内容 333</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="../src/layui.js"></script>
|
||||
<script>
|
||||
layui.use(function() {
|
||||
var tabs = layui.tabs
|
||||
var util = layui.util;
|
||||
var layer = layui.layer;
|
||||
var dropdown = layui.dropdown;
|
||||
|
||||
// 自定义事件
|
||||
util.on({
|
||||
add: function(){
|
||||
var n = Math.random()*1000 | 0; // 演示标记
|
||||
|
||||
//添加标签
|
||||
tabs.add('demoTabs1', {
|
||||
title: 'New Tab '+ n, // 此处加 n 仅为演示区分,实际应用不需要
|
||||
content: 'New Tab Content '+ n,
|
||||
id: 'new-'+ n,
|
||||
aaa: 'attr-'+ n, // 自定义属性,其中 aaa 可任意命名
|
||||
// mode: 'curr',
|
||||
done: function(params) {
|
||||
console.log(params);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// tabs 关闭前的事件
|
||||
tabs.on('beforeClose(demoTabs1)', function(data) {
|
||||
console.log('beforeClose', data);
|
||||
|
||||
// 关闭确认提示
|
||||
layer.confirm(`确定关闭标签「${this.innerText}」吗?`, function(i) {
|
||||
tabs.close('demoTabs1', data.index, true); // 强制关闭对应的标签项
|
||||
layer.close(i); // 关闭确认框
|
||||
});
|
||||
return false; // 阻止标签默认关闭
|
||||
});
|
||||
|
||||
// tabs 关闭后的事件
|
||||
tabs.on('afterClose(demoTabs1)', function(data) {
|
||||
console.log('afterClose', data);
|
||||
});
|
||||
|
||||
// tabs 切换前的事件
|
||||
tabs.on('beforeChange(demoTabs1)', function(data) {
|
||||
console.log('beforeChange', data);
|
||||
|
||||
// 切换确认提示
|
||||
/*layer.confirm(`确定从「当前标签」切换到标签「${this.innerText}」吗?`, function(i) {
|
||||
tabs.change('demoTabs1', data.to.index, true); // 强制切换
|
||||
layer.close(i); // 关闭确认框
|
||||
});
|
||||
return false; // 阻止标签默认关闭*/
|
||||
});
|
||||
|
||||
// tabs 切换后的事件
|
||||
tabs.on('afterChange(demoTabs1)', function(data) {
|
||||
console.log('afterChange', data);
|
||||
});
|
||||
|
||||
// 为标签头添加上下文菜单
|
||||
dropdown.render({
|
||||
elem: '#demoTabs1 .layui-tabs-header>li',
|
||||
trigger: 'contextmenu',
|
||||
data: [{
|
||||
title: '关闭',
|
||||
type: 'this'
|
||||
}, {
|
||||
title: '关闭其他标签页',
|
||||
type: 'other'
|
||||
}, {
|
||||
title: '关闭右侧标签页',
|
||||
type: 'right'
|
||||
}, {
|
||||
type: '-'
|
||||
}, {
|
||||
title: '关闭所有标签页',
|
||||
type: 'all'
|
||||
}],
|
||||
click: function(data, othis, event) {
|
||||
var index = this.elem.index();
|
||||
if (data.type === 'this') {
|
||||
tabs.close('demoTabs1', index); // 关闭当前标签
|
||||
} else {
|
||||
tabs.closeMult('demoTabs1', data.type, index); // 批量关闭标签
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 方法渲染
|
||||
tabs.render({
|
||||
elem: '#demoTabs2',
|
||||
header: [
|
||||
{ title: 'Tab1' },
|
||||
{ title: 'Tab2' },
|
||||
{ title: 'Tab3' }
|
||||
],
|
||||
body: [
|
||||
{ content: 'Tab content 1' },
|
||||
{ content: 'Tab content 2' },
|
||||
{ content: 'Tab content 3' }
|
||||
],
|
||||
// index: 1, //初始选中项
|
||||
// className: 'layui-tabs-card',
|
||||
// closable: true
|
||||
});
|
||||
|
||||
|
||||
// HASH 初始定位
|
||||
var hash = layui.hash();
|
||||
tabs.change('demoTabs-hash', hash.href);
|
||||
|
||||
// 给任意元素绑定 Tab 功能
|
||||
tabs.render({
|
||||
elem: '#demoTabs3',
|
||||
header: ['#demoTabsHeader', '>button'],
|
||||
body: ['#demoTabsBody', '>.test-item']
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -20,24 +20,23 @@ pre{white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; w
|
||||
/** 初始化全局标签 **/
|
||||
body{line-height: 1.6; color: #333; color: rgba(0,0,0,.85); font: 14px 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:hover{color: #777;}
|
||||
a cite{font-style: normal; *cursor:pointer;}
|
||||
a{color: #333; text-decoration: none;}
|
||||
a cite{font-style: normal;}
|
||||
|
||||
/** 基础通用 **/
|
||||
.layui-border-box, .layui-border-box *{box-sizing: border-box;}
|
||||
/* 消除第三方ui可能造成的冲突 */.layui-box, .layui-box *{box-sizing: content-box;}
|
||||
.layui-clear{clear: both; *zoom: 1;}
|
||||
.layui-clear:after{content:'\20'; clear:both; *zoom:1; display:block; height:0;}
|
||||
.layui-clear{clear: both;}
|
||||
.layui-clear:after{content:'\20'; clear:both; display:block; height:0;}
|
||||
.layui-clear-space{word-spacing: -5px;}
|
||||
.layui-inline{position: relative; display: inline-block; *display:inline; *zoom:1; vertical-align: middle;}
|
||||
.layui-inline{position: relative; display: inline-block; vertical-align: middle;}
|
||||
/* 三角形 */.layui-edge{position: relative; display: inline-block; vertical-align: middle; width: 0; height: 0; border-width: 6px; border-style: dashed; border-color: transparent; overflow: hidden;}
|
||||
.layui-edge-top{top: -4px; border-bottom-color: #999; border-bottom-style: solid;}
|
||||
.layui-edge-right{border-left-color: #999; border-left-style: solid;}
|
||||
.layui-edge-bottom{top: 2px; border-top-color: #999; border-top-style: solid;}
|
||||
.layui-edge-left{border-right-color: #999; border-right-style: solid;}
|
||||
/* 单行溢出省略 */.layui-elip{text-overflow: ellipsis; overflow: hidden; white-space: nowrap;}
|
||||
/* 屏蔽选中 */.layui-unselect,.layui-icon, .layui-disabled{-moz-user-select: none; -webkit-user-select: none; -ms-user-select: none;}
|
||||
/* 屏蔽选中 */.layui-unselect,.layui-icon, .layui-disabled{user-select: none;}
|
||||
/* 禁用 */.layui-disabled,.layui-disabled:hover{color: #d2d2d2 !important; cursor: not-allowed !important;}
|
||||
/* 纯圆角 */.layui-circle{border-radius: 100%;}
|
||||
.layui-show{display: block !important;}
|
||||
@ -515,14 +514,18 @@ a cite{font-style: normal; *cursor:pointer;}
|
||||
.layui-col-space32>*{padding: 16px;}
|
||||
|
||||
|
||||
/* 内边距 */
|
||||
/*
|
||||
* 内边距
|
||||
*/
|
||||
.layui-padding-1{padding: 4px !important;}
|
||||
.layui-padding-2{padding: 8px !important;}
|
||||
.layui-padding-3{padding: 16px !important;}
|
||||
.layui-padding-4{padding: 32px !important;}
|
||||
.layui-padding-5{padding: 48px !important;}
|
||||
|
||||
/* 外边距 */
|
||||
/*
|
||||
* 外边距
|
||||
*/
|
||||
.layui-margin-1{margin: 4px !important;}
|
||||
.layui-margin-2{margin: 8px !important;}
|
||||
.layui-margin-3{margin: 16px !important;}
|
||||
@ -538,7 +541,7 @@ a cite{font-style: normal; *cursor:pointer;}
|
||||
.layui-input,
|
||||
.layui-select,
|
||||
.layui-textarea,
|
||||
.layui-upload-button{outline: none; -webkit-appearance: none; transition: all .3s; -webkit-transition: all .3s; box-sizing: border-box;}
|
||||
.layui-upload-button{outline: none; appearance: none; -webkit-appearance: none; transition: all .3s; -webkit-transition: all .3s; box-sizing: border-box;}
|
||||
|
||||
/* 引用 */
|
||||
.layui-elem-quote{margin-bottom: 10px; padding: 15px; line-height: 1.8; border-left: 5px solid #16b777; border-radius: 0 2px 2px 0; background-color: #fafafa;}
|
||||
@ -670,8 +673,8 @@ hr.layui-border-black{border-width: 0 0 1px;}
|
||||
.layui-text ol ul > li{list-style-type: disc;}
|
||||
.layui-text ul li > p:first-child,
|
||||
.layui-text ol li > p:first-child{margin-top: 0; margin-bottom: 0;}
|
||||
.layui-text a:not(.layui-btn){color: #01AAED;}
|
||||
.layui-text a:not(.layui-btn):hover{text-decoration: underline;}
|
||||
.layui-text :where(a:not(.layui-btn)){color: #01AAED;}
|
||||
.layui-text :where(a:not(.layui-btn):hover){text-decoration: underline;}
|
||||
.layui-text blockquote:not(.layui-elem-quote){margin: 15px 0; padding: 5px 15px; border-left: 5px solid #eee;}
|
||||
.layui-text pre > code:not(.layui-code){display: block; padding: 15px; font-family: "Courier New",Consolas,"Lucida Console", monospace;}
|
||||
|
||||
@ -709,7 +712,7 @@ hr.layui-border-black{border-width: 0 0 1px;}
|
||||
* 按钮
|
||||
*/
|
||||
|
||||
.layui-btn{display: inline-block; vertical-align: middle; height: 38px; line-height: 38px; border: 1px solid transparent; padding: 0 18px; background-color: #16baaa; color: #fff; white-space: nowrap; text-align: center; font-size: 14px; border-radius: 2px; cursor: pointer; -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none;}
|
||||
.layui-btn{display: inline-block; vertical-align: middle; height: 38px; line-height: 38px; border: 1px solid transparent; padding: 0 18px; background-color: #16baaa; color: #fff; white-space: nowrap; text-align: center; font-size: 14px; border-radius: 2px; cursor: pointer; user-select: none;}
|
||||
.layui-btn:hover{opacity: 0.8; filter:alpha(opacity=80); color: #fff;}
|
||||
.layui-btn:active{opacity: 1; filter:alpha(opacity=100);}
|
||||
.layui-btn+.layui-btn{margin-left: 10px;}
|
||||
@ -763,8 +766,8 @@ hr.layui-border-black{border-width: 0 0 1px;}
|
||||
.layui-form input[type=radio]{display: none;}
|
||||
.layui-form *[lay-ignore]{display: initial;}
|
||||
|
||||
.layui-form-item{position: relative; margin-bottom: 15px; clear: both; *zoom: 1;}
|
||||
.layui-form-item:after{content:'\20'; clear: both; *zoom: 1; display: block; height:0;}
|
||||
.layui-form-item{position: relative; margin-bottom: 15px; clear: both;}
|
||||
.layui-form-item:after{content:'\20'; clear: both; display: block; height:0;}
|
||||
.layui-form-label{position: relative; float: left; display: block; padding: 9px 15px; width: 80px; font-weight: 400; line-height: 20px; text-align: right;}
|
||||
.layui-form-label-col{display: block; float: none; padding: 9px 0; line-height: 20px; text-align: left;}
|
||||
.layui-form-item .layui-inline{margin-bottom: 5px; margin-right: 10px;}
|
||||
@ -837,7 +840,7 @@ hr.layui-border-black{border-width: 0 0 1px;}
|
||||
.layui-input-wrap .layui-input-number .layui-icon:hover{font-weight: 700;}
|
||||
.layui-input-wrap .layui-input[type="number"]::-webkit-outer-spin-button,
|
||||
.layui-input-wrap .layui-input[type="number"]::-webkit-inner-spin-button{-webkit-appearance: none !important;}
|
||||
.layui-input-wrap .layui-input[type="number"]{-moz-appearance: textfield;}
|
||||
.layui-input-wrap .layui-input[type="number"]{-moz-appearance: textfield; -webkit-appearance: textfield; appearance: textfield;}
|
||||
.layui-input-wrap .layui-input.layui-input-number-out-of-range,
|
||||
.layui-input-wrap .layui-input.layui-input-number-invalid{color:#ff5722;}
|
||||
|
||||
@ -872,7 +875,6 @@ hr.layui-border-black{border-width: 0 0 1px;}
|
||||
|
||||
/* 复选框 */
|
||||
.layui-form-checkbox{position: relative; display: inline-block; vertical-align: middle; height: 30px; line-height: 30px; margin-right: 10px; padding-right: 30px; background-color: #fff; cursor: pointer; font-size: 0; -webkit-transition: .1s linear; transition: .1s linear; box-sizing: border-box;}
|
||||
.layui-form-checkbox:hover{}
|
||||
.layui-form-checkbox > *{display: inline-block; vertical-align: middle;}
|
||||
.layui-form-checkbox > div{padding: 0 11px; font-size: 14px; border-radius: 2px 0 0 2px; background-color: #d2d2d2; color: #fff; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;}
|
||||
.layui-form-checkbox > div > .layui-icon{line-height: normal}
|
||||
@ -965,7 +967,7 @@ hr.layui-border-black{border-width: 0 0 1px;}
|
||||
}
|
||||
|
||||
/** 分页 **/
|
||||
.layui-laypage{display: inline-block; *display: inline; *zoom: 1; vertical-align: middle; margin: 10px 0; font-size: 0;}
|
||||
.layui-laypage{display: inline-block; vertical-align: middle; margin: 10px 0; font-size: 0;}
|
||||
.layui-laypage>a:first-child,
|
||||
.layui-laypage>a:first-child em{border-radius: 2px 0 0 2px;}
|
||||
.layui-laypage>a:last-child,
|
||||
@ -978,7 +980,7 @@ hr.layui-border-black{border-width: 0 0 1px;}
|
||||
.layui-laypage button,
|
||||
.layui-laypage select{border: 1px solid #eee;}
|
||||
.layui-laypage a,
|
||||
.layui-laypage span{display: inline-block; *display: inline; *zoom: 1; vertical-align: middle; padding: 0 15px; height: 28px; line-height: 28px; margin: 0 -1px 5px 0; background-color: #fff; color: #333; font-size: 12px;}
|
||||
.layui-laypage span{display: inline-block; vertical-align: middle; padding: 0 15px; height: 28px; line-height: 28px; margin: 0 -1px 5px 0; background-color: #fff; color: #333; font-size: 12px;}
|
||||
.layui-laypage a[data-page]{color: #333;}
|
||||
.layui-laypage a{text-decoration: none !important; cursor: pointer;}
|
||||
.layui-laypage a:hover{color: #16baaa;}
|
||||
@ -1019,12 +1021,6 @@ hr.layui-border-black{border-width: 0 0 1px;}
|
||||
.layui-table tr{transition: all .3s; -webkit-transition: all .3s;}
|
||||
.layui-table th{text-align: left; font-weight: 600;}
|
||||
|
||||
.layui-table thead tr,
|
||||
.layui-table-header,
|
||||
.layui-table-tool,
|
||||
.layui-table-total,
|
||||
.layui-table-total tr,
|
||||
.layui-table-patch{}
|
||||
.layui-table-mend{background-color: #fff;}
|
||||
.layui-table-hover,
|
||||
.layui-table-click,
|
||||
@ -1292,18 +1288,55 @@ body .layui-table-tips .layui-layer-content{background: none; padding: 0; box-sh
|
||||
/* 下拉菜单 */
|
||||
.layui-dropdown{position: absolute; left: -999999px; top: -999999px; z-index: 77777777; margin: 5px 0; min-width: 100px;}
|
||||
.layui-dropdown:before{content:""; position: absolute; width: 100%; height: 6px; left: 0; top: -6px;}
|
||||
.layui-dropdown-shade{top: 0; left: 0; width: 100%; height: 100%; _height: expression(document.body.offsetHeight+"px"); position: fixed; _position: absolute; pointer-events: auto;}
|
||||
.layui-dropdown-shade{top: 0; left: 0; width: 100%; height: 100%; position: fixed; pointer-events: auto;}
|
||||
|
||||
|
||||
/* Tabs 标签页 */
|
||||
.layui-tabs{position: relative;}
|
||||
.layui-tabs *{box-sizing: border-box;}
|
||||
.layui-tabs.layui-hide-v{overflow: hidden;}
|
||||
.layui-tabs-header{position: relative; left: 0; height: 40px; padding: 0 !important; white-space: nowrap; font-size: 0; transition: all .16s; -webkit-transition: all .16s;}
|
||||
.layui-tabs-header:after,
|
||||
.layui-tabs-scroll:after{content: ""; position: absolute; left: 0; bottom: 0; z-index: 0; width: 100%; border-bottom: 1px solid #eee;}
|
||||
.layui-tabs-header li{position: relative; display: inline-block; vertical-align: middle; line-height: 40px; margin: 0 !important; padding: 0 16px; text-align: center; cursor: pointer; font-size: 14px; transition: all .16s; -webkit-transition: all .16s;}
|
||||
.layui-tabs-header li:first-child{margin-left: 0;}
|
||||
.layui-tabs-header li a{display: block; padding: 0 16px; margin: 0 -16px; color: inherit;}
|
||||
.layui-tabs-header li a:hover{text-decoration: none;}
|
||||
.layui-tabs-header li:hover,
|
||||
.layui-tabs-header .layui-this{color: #16baaa;}
|
||||
.layui-tabs-header .layui-this:after{content: ""; position: absolute; left:0; top: 0; z-index: 1; width: 100%; height: 100%; border-bottom: 3px solid #16baaa; box-sizing: border-box; pointer-events: none;}
|
||||
.layui-tabs-header .layui-badge,
|
||||
.layui-tabs-header .layui-badge-dot{left: 5px; top: -1px;}
|
||||
|
||||
.layui-tabs-scroll{position: relative; overflow: hidden; padding: 0 40px;}
|
||||
.layui-tabs-scroll .layui-tabs-header:after{display: none; content: none; border: 0;}
|
||||
.layui-tabs-bar .layui-icon{position: absolute; left: 0; top: 0; z-index: 3; width: 40px; height: 100%; line-height: 40px; border: 1px solid #eee; text-align: center; cursor: pointer; box-sizing: border-box; background-color: #fff; box-shadow: 2px 0 5px 0 rgb(0 0 0 / 6%);}
|
||||
.layui-tabs-bar .layui-icon-next{left: auto; right: 0; box-shadow: -2px 0 5px 0 rgb(0 0 0 / 6%);}
|
||||
|
||||
.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-body{padding: 16px 0;}
|
||||
.layui-tabs-item{display: none;}
|
||||
|
||||
/* tabs 卡片风格 */
|
||||
.layui-tabs-card>.layui-tabs-header .layui-this{background-color: #fff;}
|
||||
.layui-tabs-card>.layui-tabs-header .layui-this:after{border: 1px solid #eee; border-bottom-color: #fff; border-radius: 2px 2px 0 0;}
|
||||
.layui-tabs-card>.layui-tabs-header li:first-child.layui-this:after{margin-left: -1px;}
|
||||
.layui-tabs-card>.layui-tabs-header li:last-child.layui-this:after{margin-right: -1px;}
|
||||
.layui-tabs-card.layui-panel>.layui-tabs-header .layui-this:after{border-top: 0; border-radius: 0;}
|
||||
.layui-tabs-card.layui-panel>.layui-tabs-body{padding: 16px;}
|
||||
|
||||
|
||||
/** 导航菜单 **/
|
||||
.layui-nav{position: relative; padding: 0 15px; background-color: #2f363c; color: #fff; border-radius: 2px; font-size: 0; box-sizing: border-box;}
|
||||
.layui-nav *{font-size: 14px;}
|
||||
.layui-nav .layui-nav-item{position: relative; display: inline-block; *display: inline; *zoom: 1; margin-top: 0; list-style: none; vertical-align: middle; line-height: 60px;}
|
||||
.layui-nav .layui-nav-item{position: relative; display: inline-block; margin-top: 0; list-style: none; vertical-align: middle; line-height: 60px;}
|
||||
.layui-nav .layui-nav-item a{display: block; padding: 0 20px; color: #fff; color: rgba(255,255,255,.7); transition: all .3s; -webkit-transition: all .3s;}
|
||||
.layui-nav-bar,
|
||||
.layui-nav .layui-this:after{content: ""; position: absolute; left: 0; top: 0; width: 0; height: 3px; background-color: #16b777; transition: all .2s; -webkit-transition: all .2s; pointer-events: none;}
|
||||
.layui-nav-bar{z-index: 1000;}
|
||||
.layui-nav[lay-bar="disabled"] .layui-nav-bar{display: none;}
|
||||
.layui-nav[lay-bar="disabled"].layui-this:after{}
|
||||
.layui-nav .layui-this a,
|
||||
.layui-nav .layui-nav-item a:hover{color: #fff; text-decoration: none;}
|
||||
.layui-nav .layui-this:after{top: auto; bottom: 0; width: 100%;}
|
||||
@ -1381,7 +1414,7 @@ body .layui-table-tips .layui-layer-content{background: none; padding: 0; box-sh
|
||||
.layui-tab[overflow]>.layui-tab-title{overflow: hidden;}
|
||||
.layui-tab .layui-tab-title{position: relative; left: 0; height: 40px; white-space: nowrap; font-size: 0; transition: all .2s; -webkit-transition: all .2s;}
|
||||
.layui-tab .layui-tab-title:after{content: ""; border-bottom-color: #eee; border-bottom-width: 1px; border-style: none none solid; bottom: 0; left: 0; right: auto; top: auto; pointer-events: none; position: absolute; width: 100%; z-index: 8;}
|
||||
.layui-tab .layui-tab-title li{display: inline-block; *display: inline; *zoom: 1; vertical-align: middle; font-size: 14px; transition: all .2s; -webkit-transition: all .2s;}
|
||||
.layui-tab .layui-tab-title li{display: inline-block; vertical-align: middle; font-size: 14px; transition: all .2s; -webkit-transition: all .2s;}
|
||||
.layui-tab .layui-tab-title li{position: relative; line-height: 40px; min-width: 65px; margin: 0; padding: 0 15px; text-align: center; cursor: pointer;}
|
||||
.layui-tab .layui-tab-title li a{display: block; padding: 0 15px; margin: 0 -15px;}
|
||||
.layui-tab-title .layui-this{color: #000;}
|
||||
|
||||
11
src/layui.js
11
src/layui.js
@ -62,6 +62,7 @@
|
||||
tree: 'tree', // 树结构
|
||||
table: 'table', // 表格
|
||||
treeTable: 'treeTable', // 树表
|
||||
tabs: 'tabs', // 标签页
|
||||
element: 'element', // 常用元素操作
|
||||
rate: 'rate', // 评分组件
|
||||
colorpicker: 'colorpicker', // 颜色选择器
|
||||
@ -71,6 +72,7 @@
|
||||
util: 'util', // 工具块
|
||||
code: 'code', // 代码修饰器
|
||||
jquery: 'jquery', // DOM 库(第三方)
|
||||
component: 'component', // 组件构建器
|
||||
|
||||
all: 'all',
|
||||
'layui.all': 'layui.all' // 聚合标识(功能性的,非真实模块)
|
||||
@ -401,13 +403,14 @@
|
||||
var data = {
|
||||
path: [],
|
||||
search: {},
|
||||
hash: (hash.match(/[^#](#.*$)/) || [])[1] || ''
|
||||
hash: (hash.match(/[^#](#.*$)/) || [])[1] || '',
|
||||
href: ''
|
||||
};
|
||||
|
||||
if(!/^#\//.test(hash)) return data; // 禁止非路由规范
|
||||
if (!/^#/.test(hash)) return data; // 禁止非路由规范
|
||||
|
||||
hash = hash.replace(/^#\//, '');
|
||||
data.href = '/' + hash;
|
||||
hash = hash.replace(/^#/, '');
|
||||
data.href = hash;
|
||||
hash = hash.replace(/([^#])(#.*$)/, '$1').split('/') || [];
|
||||
|
||||
// 提取 Hash 结构
|
||||
|
||||
@ -3,12 +3,13 @@
|
||||
* Code 预览组件
|
||||
*/
|
||||
|
||||
layui.define(['lay', 'util', 'element', 'form'], function(exports){
|
||||
layui.define(['lay', 'util', 'element', 'tabs', 'form'], function(exports){
|
||||
"use strict";
|
||||
|
||||
var $ = layui.$;
|
||||
var util = layui.util;
|
||||
var element = layui.element;
|
||||
var tabs = layui.tabs;
|
||||
var form = layui.form;
|
||||
var layer = layui.layer;
|
||||
var hint = layui.hint();
|
||||
@ -403,6 +404,9 @@ layui.define(['lay', 'util', 'element', 'form'], function(exports){
|
||||
render: function(){
|
||||
form.render(thisItemBody.find('.layui-form'));
|
||||
element.render();
|
||||
tabs.render({
|
||||
elem: ['.'+ CONST.ELEM_PREVIEW, '.layui-tabs'].join(' ')
|
||||
});
|
||||
}
|
||||
});
|
||||
},3);
|
||||
|
||||
238
src/modules/component.js
Normal file
238
src/modules/component.js
Normal file
@ -0,0 +1,238 @@
|
||||
/**
|
||||
* component
|
||||
* Layui 2 组件构建器
|
||||
*/
|
||||
|
||||
layui.define(['jquery', 'lay'], function(exports) {
|
||||
"use strict";
|
||||
|
||||
var $ = layui.$;
|
||||
var lay = layui.lay;
|
||||
|
||||
// export
|
||||
exports('component', function(settings) {
|
||||
// 默认设置
|
||||
settings = $.extend(true, {
|
||||
isRenderWithoutElem: false, // 渲染是否无需指定目标元素
|
||||
isRenderOnEvent: true, // 渲染是否仅由事件触发。--- 推荐根据组件类型始终显式设置对应值
|
||||
isDeepReload: false // 是否默认为深度重载
|
||||
}, settings);
|
||||
|
||||
// 组件名
|
||||
var MOD_NAME = settings.name;
|
||||
var MOD_INDEX = 'layui_'+ MOD_NAME +'_index'; // 组件索引名
|
||||
var MOD_ID = 'lay-' + MOD_NAME + '-id'; // 用于记录组件实例 id 的属性名
|
||||
|
||||
// 组件基础对外接口
|
||||
var component = {
|
||||
config: {}, // 全局配置项,一般通过 component.set() 设置
|
||||
index: layui[MOD_NAME] ? (layui[MOD_NAME].index + 10000) : 0, // 组件索引
|
||||
|
||||
// 通用常量集,一般存放固定字符,如类名等
|
||||
CONST: $.extend(true, {
|
||||
MOD_NAME: MOD_NAME,
|
||||
MOD_INDEX: MOD_INDEX,
|
||||
|
||||
CLASS_THIS: 'layui-this',
|
||||
CLASS_SHOW: 'layui-show',
|
||||
CLASS_HIDE: 'layui-hide',
|
||||
CLASS_HIDEV: 'layui-hide-v',
|
||||
CLASS_DISABLED: 'layui-disabled',
|
||||
CLASS_NONE: 'layui-none'
|
||||
}, settings.CONST),
|
||||
|
||||
// 设置全局项
|
||||
set: function(options) {
|
||||
var that = this;
|
||||
$.extend(true, that.config, options);
|
||||
return that;
|
||||
},
|
||||
|
||||
// 事件
|
||||
on: function(events, callback) {
|
||||
return layui.onevent.call(this, MOD_NAME, events, callback);
|
||||
}
|
||||
};
|
||||
|
||||
// 操作当前实例
|
||||
var instance = function() {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var id = options.id;
|
||||
|
||||
// 实例对象
|
||||
var inst = {
|
||||
config: options,
|
||||
id: id,
|
||||
|
||||
// 重置实例
|
||||
reload: function(options) {
|
||||
that.reload.call(that, options);
|
||||
}
|
||||
};
|
||||
|
||||
// 扩展实例对象的回调
|
||||
if (typeof settings.extendsInstance === 'function') {
|
||||
$.extend(true, inst, settings.extendsInstance.call(that));
|
||||
}
|
||||
|
||||
// 返回实例对象
|
||||
return inst;
|
||||
};
|
||||
|
||||
// 构造器
|
||||
var Class = function(options) {
|
||||
var that = this;
|
||||
that.index = ++component.index; // 每创建一个实例,下标自增
|
||||
|
||||
// 扩展配置项:传入选项 -> 全局选项 -> 默认选项 = 当前选项
|
||||
that.config = $.extend(true, {}, that.config, component.config, options);
|
||||
|
||||
// 初始化之前的回调
|
||||
if (typeof settings.beforeInit === 'function') {
|
||||
settings.beforeInit.call(that, that.config);
|
||||
}
|
||||
|
||||
// 初始化
|
||||
that.init();
|
||||
};
|
||||
|
||||
// 默认配置
|
||||
Class.prototype.config = settings.config;
|
||||
|
||||
// 重载实例
|
||||
Class.prototype.reload = function(options, type) {
|
||||
var that = this;
|
||||
$.extend(settings.isDeepReload, that.config, options);
|
||||
that.init(true, type);
|
||||
};
|
||||
|
||||
// 初始化准备(若由事件触发渲染,则必经此步)
|
||||
Class.prototype.init = function(rerender, type){
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var elem = $(options.elem);
|
||||
|
||||
// 若 elem 非唯一,则拆分为多个实例
|
||||
if (elem.length > 1) {
|
||||
layui.each(elem, function() {
|
||||
component.render($.extend({}, options, {
|
||||
elem: this
|
||||
}));
|
||||
});
|
||||
return that;
|
||||
}
|
||||
|
||||
// 合并 lay-options 属性上的配置信息
|
||||
$.extend(true, options, lay.options(elem[0]));
|
||||
|
||||
// 若重复执行 render,则视为 reload 处理
|
||||
if (!rerender && elem.attr(MOD_ID)) {
|
||||
var newThat = instance.getThis(elem.attr(MOD_ID));
|
||||
if (!newThat) return;
|
||||
return newThat.reload(options, type);
|
||||
}
|
||||
|
||||
options.elem = $(options.elem);
|
||||
|
||||
// 初始化 id 属性 - 优先取 options.id > 元素 id > 自增索引
|
||||
options.id = lay.hasOwn(options, 'id') ? options.id : (
|
||||
elem.attr('id') || that.index
|
||||
);
|
||||
|
||||
// 记录当前实例对象
|
||||
instance.that[options.id] = that;
|
||||
|
||||
// 渲染之前的回调
|
||||
if (typeof settings.beforeRender === 'function') {
|
||||
settings.beforeRender.call(that, options);
|
||||
}
|
||||
|
||||
// 执行渲染
|
||||
var render = function() {
|
||||
component.cache.id[options.id] = null; // 记录所有实例 id,用于批量操作(如 resize)
|
||||
elem.attr(MOD_ID, options.id); // 目标元素已渲染过的标记
|
||||
that.render(rerender); // 渲染核心
|
||||
};
|
||||
|
||||
// 若绑定元素不存在
|
||||
if (!elem[0]) {
|
||||
return settings.isRenderWithoutElem ? render() : null; // 渲染是否无需指定目标元素
|
||||
};
|
||||
|
||||
// 执行渲染 - 是否初始即渲染组件
|
||||
if((settings.isRenderOnEvent && options.show) || !settings.isRenderOnEvent) {
|
||||
render();
|
||||
}
|
||||
|
||||
// 事件
|
||||
typeof settings.events === 'function' && that.events();
|
||||
};
|
||||
|
||||
// 组件必传项
|
||||
Class.prototype.render = settings.render; // 渲染
|
||||
Class.prototype.events = settings.events; // 事件
|
||||
|
||||
// 元素操作缓存
|
||||
Class.prototype.cache = function(key, value) {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var elem = options.elem;
|
||||
|
||||
if (!elem) return;
|
||||
|
||||
var CACHE_NAME = 'lay_'+ MOD_NAME + '_cache';
|
||||
var cache = elem.data(CACHE_NAME) || {};
|
||||
|
||||
if (value === undefined) return cache[key];
|
||||
|
||||
cache[key] = value;
|
||||
elem.data(CACHE_NAME, cache);
|
||||
};
|
||||
|
||||
// 缓存所有实例对象
|
||||
instance.that = {};
|
||||
|
||||
// 获取当前实例对象
|
||||
instance.getThis = component.getThis = function(id) {
|
||||
if (id === undefined) {
|
||||
throw new Error('ID argument required');
|
||||
}
|
||||
return instance.that[id];
|
||||
};
|
||||
|
||||
// 组件缓存
|
||||
component.cache = {
|
||||
id: {}
|
||||
};
|
||||
|
||||
// 用于扩展原型
|
||||
component.Class = Class;
|
||||
|
||||
/**
|
||||
* 组件完整重载
|
||||
* @param {string} id - 实例 id
|
||||
* @param {Object} options - 配置项
|
||||
* @returns
|
||||
*/
|
||||
component.reload = function(id, options) {
|
||||
var that = instance.getThis(id);
|
||||
if (!that) return;
|
||||
|
||||
that.reload(options);
|
||||
return instance.call(that);
|
||||
};
|
||||
|
||||
/**
|
||||
* 组件渲染
|
||||
* @param {Object} options - 配置项
|
||||
* @returns
|
||||
*/
|
||||
component.render = function(options) {
|
||||
var inst = new Class(options);
|
||||
return instance.call(inst);
|
||||
};
|
||||
|
||||
return component;
|
||||
});
|
||||
});
|
||||
9724
src/modules/jquery.js
vendored
9724
src/modules/jquery.js
vendored
File diff suppressed because it is too large
Load Diff
792
src/modules/tabs.js
Normal file
792
src/modules/tabs.js
Normal file
@ -0,0 +1,792 @@
|
||||
/**
|
||||
* tabs
|
||||
* 标签页组件
|
||||
*/
|
||||
|
||||
layui.define('component', function(exports) {
|
||||
'use strict';
|
||||
|
||||
var $ = layui.$;
|
||||
|
||||
// 创建组件
|
||||
var component = layui.component({
|
||||
name: 'tabs', // 组件名
|
||||
|
||||
// 默认配置
|
||||
config: {
|
||||
elem: '.layui-tabs',
|
||||
trigger: 'click', // 标签切换的触发事件
|
||||
headerMode: 'auto' // 标签头部的显示模式 auto | scroll | normal
|
||||
},
|
||||
|
||||
CONST: {
|
||||
ELEM: 'layui-tabs',
|
||||
HEADER: 'layui-tabs-header',
|
||||
CLOSE: 'layui-tabs-close',
|
||||
BODY: 'layui-tabs-body',
|
||||
ITEM: 'layui-tabs-item',
|
||||
CARD: 'layui-tabs-card'
|
||||
},
|
||||
|
||||
isRenderOnEvent: false,
|
||||
|
||||
// 渲染
|
||||
render: function() {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
|
||||
// 标签页元素项
|
||||
that.headerElem = ['.'+ component.CONST.HEADER + ':eq(0)', '>li'];
|
||||
that.bodyElem = ['.'+ component.CONST.BODY + ':eq(0)', '>.'+ component.CONST.ITEM];
|
||||
|
||||
// 获取标签容器中的 header body 相关元素
|
||||
that.getContainer = function() {
|
||||
var elem = that.documentElem || options.elem;
|
||||
return {
|
||||
header: {
|
||||
elem: elem.find(that.headerElem[0]),
|
||||
items: elem.find(that.headerElem.join(''))
|
||||
},
|
||||
body: {
|
||||
elem: elem.find(that.bodyElem[0]),
|
||||
items: elem.find(that.bodyElem.join(''))
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 若 header 选项类型为数组
|
||||
if (layui.type(options.header) === 'array') {
|
||||
if (options.header.length === 0) return;
|
||||
|
||||
// 给任意元素绑定 tabs 切换功能
|
||||
if (typeof options.header[0] === 'string') {
|
||||
that.headerElem = options.header.concat();
|
||||
that.documentElem = $(document);
|
||||
} else { // 方法传值渲染
|
||||
that.elemView = $('<div class="layui-tabs"></div>');
|
||||
if (options.className) that.elemView.addClass(options.className);
|
||||
|
||||
var headerElem = $('<ul class="layui-tabs-header"></ul>');
|
||||
var bodyElem = $('<div class="layui-tabs-body"></div>');
|
||||
|
||||
// 生成标签项
|
||||
layui.each(options.header, function(i, item){
|
||||
var elemHeaderItem = that.renderHeaderItem(item);
|
||||
headerElem.append(elemHeaderItem);
|
||||
});
|
||||
layui.each(options.body, function(i, item){
|
||||
var elemBodyItem = that.renderBodyItem(item);
|
||||
bodyElem.append(elemBodyItem);
|
||||
});
|
||||
|
||||
that.elemView.append(headerElem).append(bodyElem);
|
||||
options.elem.html(that.elemView);
|
||||
}
|
||||
} else {
|
||||
that.renderClose(); // 初始化标签关闭结构
|
||||
}
|
||||
|
||||
// 若 body 选项类型为数组
|
||||
if (layui.type(options.body) === 'array') {
|
||||
if (typeof options.body[0] === 'string') {
|
||||
that.documentElem = $(document);
|
||||
that.bodyElem = options.body.concat();
|
||||
}
|
||||
}
|
||||
|
||||
// 初始选中项
|
||||
var data = that.data();
|
||||
if ('index' in options && data.index != options.index) {
|
||||
that.change(that.findHeaderItem(options.index), true);
|
||||
} else if (data.index === -1) { // 初始选中项为空时,默认选中第一个
|
||||
that.change(that.findHeaderItem(0), true);
|
||||
}
|
||||
|
||||
// 初始化滚动结构
|
||||
that.roll('auto');
|
||||
|
||||
// 清除隐藏占位
|
||||
if (options.elem.hasClass(component.CONST.CLASS_HIDEV)) {
|
||||
options.elem.removeClass(component.CONST.CLASS_HIDEV);
|
||||
}
|
||||
|
||||
// 回调
|
||||
typeof options.afterRender === 'function' && options.afterRender(data);
|
||||
|
||||
// 渲染成功后的事件
|
||||
layui.event.call(
|
||||
options.elem[0],
|
||||
component.CONST.MOD_NAME,
|
||||
'afterRender('+ options.id +')',
|
||||
data
|
||||
);
|
||||
},
|
||||
|
||||
// 事件
|
||||
events: function() {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var container = that.getContainer();
|
||||
var MOD_NAME = component.CONST.MOD_NAME;
|
||||
var TRIGGER_NAMESPACE = '.lay_'+ MOD_NAME + '_trigger';
|
||||
var delegatedElement = that.documentElem ? container.header.elem : options.elem;
|
||||
|
||||
// 标签头部事件
|
||||
var trigger = options.trigger + TRIGGER_NAMESPACE;
|
||||
var elemHeaderItem = that.documentElem ? that.headerElem[1] : that.headerElem.join('');
|
||||
delegatedElement.off(trigger).on(trigger, elemHeaderItem, function() {
|
||||
that.change($(this));
|
||||
});
|
||||
|
||||
// 窗口 resize 事件
|
||||
if (!inner.onresize) {
|
||||
var timer;
|
||||
$(window).on('resize', function() {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function(){
|
||||
layui.each(component.cache.id, function(key) {
|
||||
var that = component.getThis(key);
|
||||
if(!that) return;
|
||||
that.roll('init');
|
||||
});
|
||||
}, 50);
|
||||
});
|
||||
inner.onresize = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 内部变量集
|
||||
var inner = {};
|
||||
|
||||
/**
|
||||
* 扩展组件原型方法
|
||||
*/
|
||||
|
||||
var Class = component.Class;
|
||||
|
||||
/**
|
||||
* 增加标签
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.title - 标签标题
|
||||
* @param {string} opts.content - 标签内容
|
||||
* @param {string} opts.id - 标签的 lay-id 属性值
|
||||
* @param {string} [opts.index] - 活动标签索引,默认取当前选中标签的索引
|
||||
* @param {('append'|'prepend'|'after'|'before')} [opts.mode='append'] - 标签插入方式
|
||||
* @param {string} [opts.closable] - 标签是否可关闭。初始值取决于 options.closable
|
||||
* @param {string} [opts.headerItem] - 自定义标签头部元素
|
||||
* @param {string} [opts.bodyItem] - 自定义标签内容元素
|
||||
* @param {Function} [opts.done] - 标签添加成功后执行的回调函数
|
||||
*/
|
||||
Class.prototype.add = function(opts) {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var container = that.getContainer();
|
||||
var newHeaderItem = that.renderHeaderItem(opts);
|
||||
var newBodyItem = that.renderBodyItem(opts);
|
||||
|
||||
// 插入方式
|
||||
if (/(before|after)/.test(opts.mode)) { // 在活动标签前后插入
|
||||
var data = that.data();
|
||||
var hasOwnIndex = opts.hasOwnProperty('index');
|
||||
var headerItem = hasOwnIndex ? that.findHeaderItem(opts.index) : data.thisHeaderItem;
|
||||
var bodyItem = hasOwnIndex ? that.findBodyItem(opts.index) : data.thisHeaderItem;
|
||||
headerItem[opts.mode](newHeaderItem);
|
||||
bodyItem[opts.mode](newBodyItem);
|
||||
} else { // 在标签最前后插入
|
||||
var mode = ({
|
||||
prepend: 'prepend', // 插入标签到最前
|
||||
append: 'append' // 插入标签到最后
|
||||
})[opts.mode || 'append'] || 'append';
|
||||
container.header.elem[mode](newHeaderItem);
|
||||
container.body.elem[mode](newBodyItem);
|
||||
}
|
||||
|
||||
// 将插入项切换为当前标签
|
||||
that.change(newHeaderItem, true);
|
||||
|
||||
// 回调
|
||||
var params = that.data();
|
||||
typeof opts.done === 'function' && opts.done(params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭指定标签
|
||||
* @param {Object} thisHeaderItem - 当前标签头部项元素
|
||||
* @param {boolean} force - 是否强制删除
|
||||
*/
|
||||
Class.prototype.close = function(thisHeaderItem, force) {
|
||||
if(!thisHeaderItem || !thisHeaderItem[0]) return;
|
||||
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var index = thisHeaderItem.index();
|
||||
|
||||
if (!thisHeaderItem[0]) return;
|
||||
|
||||
// 标签是否不可关闭
|
||||
if (thisHeaderItem.attr('lay-closable') === 'false') {
|
||||
return;
|
||||
}
|
||||
|
||||
// 当前标签相关数据
|
||||
var params = that.data();
|
||||
|
||||
// 标签关闭前的事件。若非强制关闭,可则根据事件的返回结果决定是否关闭
|
||||
if (!force) {
|
||||
var closable = layui.event.call(
|
||||
thisHeaderItem[0],
|
||||
component.CONST.MOD_NAME,
|
||||
'beforeClose('+ options.id +')',
|
||||
$.extend(params, {
|
||||
index: thisHeaderItem.index()
|
||||
})
|
||||
);
|
||||
|
||||
// 是否阻止关闭
|
||||
if (closable === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果关闭的是当前标签,则更换当前标签索引
|
||||
if (thisHeaderItem.hasClass(component.CONST.CLASS_THIS)) {
|
||||
if (thisHeaderItem.next()[0]) {
|
||||
that.change(thisHeaderItem.next(), true);
|
||||
} else if(thisHeaderItem.prev()[0]) {
|
||||
that.change(thisHeaderItem.prev(), true);
|
||||
}
|
||||
}
|
||||
|
||||
// 移除元素
|
||||
thisHeaderItem.remove();
|
||||
that.findBodyItem(index).remove();
|
||||
|
||||
that.roll('auto', index);
|
||||
|
||||
// 获取当前标签相关数据
|
||||
var params = that.data();
|
||||
|
||||
// 标签关闭后的事件
|
||||
layui.event.call(
|
||||
params.thisHeaderItem[0],
|
||||
component.CONST.MOD_NAME,
|
||||
'afterClose('+ options.id +')',
|
||||
params
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量关闭标签
|
||||
* @see tabs.close
|
||||
*/
|
||||
Class.prototype.closeMult = function(mode, index) {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var container = that.getContainer();
|
||||
var data = that.data();
|
||||
var headers = container.header.items;
|
||||
var bodys = container.body.items;
|
||||
var DISABLED_CLOSE_SELECTOR = '[lay-closable="false"]'; // 不可关闭标签选择器
|
||||
var FILTER = ':not('+ DISABLED_CLOSE_SELECTOR +')'; // 不可关闭标签过滤器
|
||||
|
||||
index = index === undefined ? data.index : index;
|
||||
|
||||
// 将标签头 lay-closable 属性值同步到 body 项
|
||||
headers.each(function(i) {
|
||||
var othis = $(this);
|
||||
var closableAttr = othis.attr('lay-closable');
|
||||
if (closableAttr) {
|
||||
bodys.eq(i).attr('lay-closable', closableAttr);
|
||||
}
|
||||
});
|
||||
|
||||
// 若当前选中标签也允许关闭,则尝试寻找不可关闭的标签并将其选中
|
||||
if (data.thisHeaderItem.attr('lay-closable') !== 'false') {
|
||||
if(mode === 'all' || !mode){
|
||||
var nextHeader = headers.filter(':gt('+ data.index +')'+ DISABLED_CLOSE_SELECTOR).eq(0);
|
||||
var prevHeader = $(headers.filter(':lt('+ data.index +')'+ DISABLED_CLOSE_SELECTOR).get().reverse()).eq(0);
|
||||
if (nextHeader[0]) {
|
||||
that.change(nextHeader, true);
|
||||
} else if(prevHeader[0]) {
|
||||
that.change(prevHeader, true);
|
||||
}
|
||||
} else if(index !== data.index) { // 自动切换到活动标签(功能可取消)
|
||||
that.change(that.findHeaderItem(index), true);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行批量关闭标签
|
||||
if (mode === 'other') { // 关闭其他标签
|
||||
headers.eq(index).siblings(FILTER).remove();
|
||||
bodys.eq(index).siblings(FILTER).remove();
|
||||
} else if(mode === 'right') { // 关闭右侧标签
|
||||
headers.filter(':gt('+ index +')'+ FILTER).remove();
|
||||
bodys.filter(':gt('+ index +')'+ FILTER).remove();
|
||||
} else { // 关闭所有标签
|
||||
headers.filter(FILTER).remove();
|
||||
bodys.filter(FILTER).remove();
|
||||
}
|
||||
|
||||
that.roll('auto');
|
||||
|
||||
// 回调
|
||||
var params = that.data();
|
||||
|
||||
// 标签关闭后的事件
|
||||
layui.event.call(
|
||||
params.thisHeaderItem[0],
|
||||
component.CONST.MOD_NAME,
|
||||
'afterClose('+ options.id +')',
|
||||
params
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 切换标签
|
||||
* @param {Object} thisHeaderItem - 当前标签头部项元素
|
||||
* @param {boolean} [force=false] - 是否强制切换
|
||||
* @returns
|
||||
*/
|
||||
Class.prototype.change = function(thisHeaderItem, force) {
|
||||
if (!thisHeaderItem || !thisHeaderItem[0]) return;
|
||||
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var index = thisHeaderItem.index();
|
||||
var thatA = thisHeaderItem.find('a');
|
||||
// 是否存在跳转链接
|
||||
var isLink = typeof thatA.attr('href') === 'string' && thatA.attr('target') === '_blank';
|
||||
// 是否不允许选中
|
||||
var unselect = typeof thisHeaderItem.attr('lay-unselect') === 'string';
|
||||
|
||||
// 不满足切换的条件
|
||||
if (isLink || unselect) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 当前标签相关数据
|
||||
var params = that.data();
|
||||
|
||||
// 标签关闭前的事件。若非强制关闭,可则根据事件的返回结果决定是否关闭
|
||||
if (!force) {
|
||||
var enable = layui.event.call(
|
||||
thisHeaderItem[0],
|
||||
component.CONST.MOD_NAME,
|
||||
'beforeChange('+ options.id +')',
|
||||
$.extend(params, {
|
||||
from: {
|
||||
index: params.index,
|
||||
headerItem: params.thisHeaderItem
|
||||
},
|
||||
to: {
|
||||
index: thisHeaderItem.index(),
|
||||
headerItem: thisHeaderItem
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// 是否阻止切换
|
||||
if (enable === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行标签头部切换
|
||||
thisHeaderItem.addClass(component.CONST.CLASS_THIS).siblings()
|
||||
.removeClass(component.CONST.CLASS_THIS);
|
||||
|
||||
// 执行标签内容切换
|
||||
that.findBodyItem(index).addClass(component.CONST.CLASS_SHOW)
|
||||
.siblings().removeClass(component.CONST.CLASS_SHOW);
|
||||
|
||||
that.roll('auto', index);
|
||||
|
||||
// 重新获取标签相关数据
|
||||
var params = that.data();
|
||||
|
||||
// 标签切换后的事件
|
||||
layui.event.call(
|
||||
params.thisHeaderItem[0],
|
||||
component.CONST.MOD_NAME,
|
||||
'afterChange('+ options.id +')',
|
||||
params
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 渲染标签头部项
|
||||
* @param {Object} opts - 标签项配置信息
|
||||
*/
|
||||
Class.prototype.renderHeaderItem = function(opts) {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var headerItem = $(opts.headerItem || options.headerItem || '<li></li>');
|
||||
|
||||
headerItem.html(opts.title || 'New Tab');
|
||||
|
||||
// 追加属性
|
||||
layui.each(opts, function(key, value){
|
||||
if(/^(title|content|mode|done)$/.test(key)) return;
|
||||
headerItem.attr('lay-'+ key, value);
|
||||
});
|
||||
|
||||
// 追加标签关闭元素
|
||||
that.appendClose(headerItem, opts);
|
||||
|
||||
return headerItem;
|
||||
};
|
||||
|
||||
/**
|
||||
* 渲染标签内容项
|
||||
* @param {Object} opts - 标签项配置信息
|
||||
*/
|
||||
Class.prototype.renderBodyItem = function(opts) {
|
||||
var that = this
|
||||
var options = that.config
|
||||
var bodyItem = $(opts.bodyItem || options.bodyItem || '<div class="'+ component.CONST.ITEM +'"></div>');
|
||||
|
||||
bodyItem.html(opts.content || '');
|
||||
return bodyItem;
|
||||
};
|
||||
|
||||
/**
|
||||
* 给某一个标签项追加可关闭元素
|
||||
* @param {Object} headerItem - 标签项元素
|
||||
* @param {Object} opts - 标签项配置信息
|
||||
*/
|
||||
Class.prototype.appendClose = function(headerItem, opts) {
|
||||
var that = this
|
||||
var options = that.config;
|
||||
|
||||
if(!options.closable) return;
|
||||
|
||||
opts = opts || {};
|
||||
|
||||
// 不可关闭项
|
||||
if (opts.closable === 'false' || headerItem.attr('lay-closable') === 'false') {
|
||||
return;
|
||||
}
|
||||
|
||||
// 可关闭项追加关闭按钮
|
||||
if (!headerItem.find('.'+ component.CONST.CLOSE)[0]) {
|
||||
var close = $('<i class="layui-icon layui-icon-close layui-unselect '+ component.CONST.CLOSE +'"></i>');
|
||||
close.on('click', function(){
|
||||
that.close($(this).parent());
|
||||
return false;
|
||||
});
|
||||
headerItem.append(close);
|
||||
}
|
||||
};
|
||||
|
||||
// 渲染标签可关闭元素
|
||||
Class.prototype.renderClose = function() {
|
||||
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() {
|
||||
$(this).find('.'+ component.CONST.CLOSE).remove();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 标签头滚动
|
||||
* @param {('auto'|'prev'|'next'|'init')} [mode='next'] - 滚动方式
|
||||
* @param {number} index - 标签索引。默认取当前选中标签的索引值
|
||||
* @returns
|
||||
*/
|
||||
Class.prototype.roll = function(mode, index) {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var container = that.getContainer();
|
||||
var headerElem = container.header.elem;
|
||||
var headerItems = container.header.items;
|
||||
var scrollWidth = headerElem.prop('scrollWidth'); // 实际总长度
|
||||
var outerWidth = Math.ceil(headerElem.outerWidth()); // 可视区域的长度
|
||||
var tabsLeft = headerElem.data('left') || 0;
|
||||
var scrollMode = options.headerMode === 'scroll'; // 标签头部是否始终保持滚动模式
|
||||
|
||||
// 让选中标签始终保持在可视区域
|
||||
var rollToVisibleArea = function() {
|
||||
index = isNaN(index) ? that.data().index : index;
|
||||
|
||||
var thisItemElem = headerItems.eq(index);
|
||||
if (!thisItemElem[0]) return;
|
||||
|
||||
// 当前标签的相对水平坐标值
|
||||
var thisLeft = Math.ceil(thisItemElem.position().left);
|
||||
var padding = 1; // 让边界额外保持一定间距
|
||||
|
||||
// 当选中标签溢出在可视区域「左侧」时
|
||||
var countWidth = thisLeft - (thisItemElem.prev().outerWidth() || 0); // 始终空出上一个标签
|
||||
if (countWidth > 0) countWidth = countWidth - padding;
|
||||
|
||||
// 左侧临界值
|
||||
if (tabsLeft + countWidth < 0) {
|
||||
tabsLeft = countWidth >= 0 ? countWidth : 0; // 标签的复原位移不能超出 0
|
||||
return headerElem.css('left', -tabsLeft).data('left', -tabsLeft);
|
||||
}
|
||||
|
||||
// 当选中标签溢出在可视区域「右侧」时,
|
||||
var countWidth = thisLeft + thisItemElem.outerWidth()
|
||||
+ (thisItemElem.next().outerWidth() || 0) + padding; // 始终空出下一个标签
|
||||
|
||||
// 右侧临界值
|
||||
if (tabsLeft + countWidth - outerWidth > 0) {
|
||||
tabsLeft = countWidth - outerWidth;
|
||||
headerElem.css('left', -tabsLeft).data('left', -tabsLeft);
|
||||
}
|
||||
};
|
||||
|
||||
// css 类名
|
||||
var CLASS_SCROLL = 'layui-tabs-scroll';
|
||||
var CLASS_BAR = 'layui-tabs-bar';
|
||||
var CLASS_BAR_ICON = ['layui-icon-prev', 'layui-icon-next'];
|
||||
|
||||
// 滚动结构
|
||||
var rollElem = {
|
||||
elem: $('<div class="'+ CLASS_SCROLL +' layui-unselect"></div>'),
|
||||
bar: $([
|
||||
'<div class="'+ CLASS_BAR +'">',
|
||||
'<i class="layui-icon '+ CLASS_BAR_ICON[0] +'" lay-mode="prev"></i>',
|
||||
'<i class="layui-icon '+ CLASS_BAR_ICON[1] +'" lay-mode="next"></i>',
|
||||
'</div>'
|
||||
].join(''))
|
||||
};
|
||||
|
||||
// 不渲染头部滚动结构
|
||||
if (options.headerMode === 'normal') return;
|
||||
|
||||
// 是否渲染滚动结构
|
||||
var elemScroll = headerElem.parent('.'+ CLASS_SCROLL);
|
||||
if (scrollMode || (!scrollMode && scrollWidth > outerWidth)) {
|
||||
if (!elemScroll[0]) {
|
||||
if (options.elem.hasClass(component.CONST.CARD)) {
|
||||
rollElem.elem.addClass(component.CONST.CARD);
|
||||
}
|
||||
headerElem.wrap(rollElem.elem);
|
||||
headerElem.after(rollElem.bar);
|
||||
|
||||
// 点击左右箭头
|
||||
rollElem.bar.children().on('click', function(){
|
||||
var othis = $(this);
|
||||
var mode = othis.attr('lay-mode');
|
||||
if ($(this).hasClass(component.CONST.CLASS_DISABLED)) return;
|
||||
mode && that.roll(mode);
|
||||
});
|
||||
}
|
||||
} else if(!scrollMode) {
|
||||
if (elemScroll[0]) {
|
||||
elemScroll.find('.'+ CLASS_BAR).remove();
|
||||
headerElem.unwrap().css('left', 0).data('left', 0);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化滚动模式
|
||||
if (mode === 'init') return;
|
||||
|
||||
// 重新获取
|
||||
scrollWidth = headerElem.prop('scrollWidth') // 实际总长度
|
||||
outerWidth = headerElem.outerWidth() // 可视区域的长度
|
||||
elemScroll = headerElem.parent('.'+ CLASS_SCROLL);
|
||||
|
||||
// 左箭头(往右滚动)
|
||||
if (mode === 'prev') {
|
||||
// 当前的 left 减去可视宽度,用于与上一轮的页签比较
|
||||
var prevLeft = -tabsLeft - outerWidth;
|
||||
if(prevLeft < 0) prevLeft = 0;
|
||||
headerItems.each(function(i, item){
|
||||
var li = $(item);
|
||||
var left = Math.ceil(li.position().left);
|
||||
|
||||
if (left >= prevLeft) {
|
||||
headerElem.css('left', -left).data('left', -left);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else if(mode === 'auto') { // 自动识别滚动
|
||||
rollToVisibleArea();
|
||||
} else { // 右箭头(往左滚动) 默认 next
|
||||
headerItems.each(function(i, item){
|
||||
var li = $(item);
|
||||
var left = Math.ceil(li.position().left);
|
||||
|
||||
if (left + li.outerWidth() >= outerWidth - tabsLeft) {
|
||||
headerElem.css('left', -left).data('left', -left);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 同步箭头状态
|
||||
tabsLeft = headerElem.data('left') || 0;
|
||||
|
||||
// 左
|
||||
elemScroll.find('.'+ CLASS_BAR_ICON[0])[
|
||||
tabsLeft < 0 ? 'removeClass' : 'addClass'
|
||||
](component.CONST.CLASS_DISABLED);
|
||||
// 右
|
||||
elemScroll.find('.'+ CLASS_BAR_ICON[1])[
|
||||
parseFloat(tabsLeft + scrollWidth) - outerWidth > 0
|
||||
? 'removeClass'
|
||||
: 'addClass'
|
||||
](component.CONST.CLASS_DISABLED);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据 id 或 index 获取相关标签头部项
|
||||
* @param {number|string} index - 标签索引或 id
|
||||
*/
|
||||
Class.prototype.findHeaderItem = function(index) {
|
||||
if(!(
|
||||
typeof index === 'number'
|
||||
|| (typeof index === 'string' && index)
|
||||
)) return;
|
||||
var headerItems = this.getContainer().header.items;
|
||||
var item = headerItems.filter('[lay-id="'+ index +'"]');
|
||||
return item[0] ? item : headerItems.eq(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据 index 获取相关标签内容项
|
||||
* @param {number} index - 标签索引
|
||||
*/
|
||||
Class.prototype.findBodyItem = function(index) {
|
||||
return this.getContainer().body.items.eq(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* 返回给回调的公共信息
|
||||
* @returns
|
||||
*/
|
||||
Class.prototype.data = function() {
|
||||
var that = this;
|
||||
var options = that.config;
|
||||
var container = that.getContainer();
|
||||
var thisHeaderItem = container.header.items.filter('.'+ component.CONST.CLASS_THIS);
|
||||
var index = thisHeaderItem.index();
|
||||
|
||||
return {
|
||||
options: options, // 标签配置信息
|
||||
container: container, // 标签容器的相关元素
|
||||
thisHeaderItem: thisHeaderItem, // 当前标签头部项
|
||||
thisBodyItem: that.findBodyItem(index), // 当前标签内容项
|
||||
index: index, // 当前标签索引
|
||||
length: container.header.items.length // 当前标签数
|
||||
}
|
||||
};
|
||||
|
||||
// 扩展组件接口
|
||||
$.extend(component, {
|
||||
/**
|
||||
* 添加标签
|
||||
* @param {string} id - 渲染时的实例 ID
|
||||
* @param {Object} opts - 添加标签的配置项,详见 Class.prototype.add
|
||||
*/
|
||||
add: function(id, opts) {
|
||||
var that = component.getThis(id);
|
||||
if(!that) return;
|
||||
that.add(opts);
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭标签
|
||||
* @param {string} id - 渲染时的实例 ID
|
||||
* @param {number} index - 标签索引
|
||||
* @param {boolean} [force=false] - 是否强制关闭
|
||||
*/
|
||||
close: function(id, index, force) {
|
||||
var that = component.getThis(id);
|
||||
if(!that) return;
|
||||
if(index === undefined) index = that.data().index; // index 若不传,则表示关闭当前标签
|
||||
that.close(that.findHeaderItem(index), force);
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭多个标签
|
||||
* @param {string} id - 渲染时的实例 ID
|
||||
* @param {('other'|'right'|'all')} [mode="all"] - 关闭方式
|
||||
* @param {number} index - 活动标签的索引,默认取当前选中标签的索引。一般用于标签右键事件
|
||||
*/
|
||||
closeMult: function(id, mode, index, force) {
|
||||
var that = component.getThis(id);
|
||||
if(!that) return;
|
||||
that.closeMult(mode, index, force);
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换标签
|
||||
* @param {string} id - 渲染时的实例 ID
|
||||
* @param {number} index - 标签索引
|
||||
*/
|
||||
change: function(id, index, force) {
|
||||
var that = component.getThis(id);
|
||||
if(!that) return;
|
||||
that.change(that.findHeaderItem(index), force);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取标签信息
|
||||
* @param {string} id - 渲染时的实例 ID
|
||||
*/
|
||||
data: function(id) {
|
||||
var that = component.getThis(id);
|
||||
return that ? that.data() : {};
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取标签指定头部项
|
||||
* @param {string} id - 渲染时的实例 ID
|
||||
* @param {number} index - 标签索引
|
||||
* @returns
|
||||
*/
|
||||
getHeaderItem: function(id, index) {
|
||||
var that = component.getThis(id);
|
||||
if(!that) return;
|
||||
return that.findHeaderItem(index);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取标签指定内容项
|
||||
* @param {string} id - 渲染时的实例 ID
|
||||
* @param {number} index - 标签索引
|
||||
* @returns
|
||||
*/
|
||||
getBodyItem: function(id, index) {
|
||||
var that = component.getThis(id);
|
||||
if(!that) return;
|
||||
return that.findBodyItem(index);
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新标签视图结构
|
||||
* @param {string} id - 渲染时的实例 ID
|
||||
*/
|
||||
refresh: function(id) {
|
||||
var that = component.getThis(id);
|
||||
if (!that) return;
|
||||
that.roll('auto');
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化渲染
|
||||
$(function() {
|
||||
component.render();
|
||||
});
|
||||
|
||||
exports(component.CONST.MOD_NAME, component);
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user