release v2.8.9

release v2.8.9
pull/1304/head v2.8.9
贤心 2023-07-03 08:27:44 +08:00 committed by GitHub
commit 40277a7964
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 414 additions and 166 deletions

2
dist/css/layui.css vendored

File diff suppressed because one or more lines are too long

2
dist/layui.js vendored

File diff suppressed because one or more lines are too long

View File

@ -48,13 +48,13 @@
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">范围</label>
<label class="layui-form-label">数字输入框</label>
<div class="layui-input-inline" style="width: 100px;">
<input type="text" name="price_min" placeholder="" autocomplete="off" class="layui-input">
<input type="number" name="price_min" placeholder="" autocomplete="off" class="layui-input" min="0" step="1" lay-affix="number">
</div>
<div class="layui-form-mid">-</div>
<div class="layui-input-inline" style="width: 100px;">
<input type="text" name="price_max" placeholder="" autocomplete="off" class="layui-input">
<input type="number" name="price_max" placeholder="" autocomplete="off" class="layui-input" min="0" step="1" lay-affix="number">
</div>
</div>
</div>

View File

@ -100,31 +100,51 @@ toc: true
<h2 id="affix" lay-toc="{hot: true}">动态点缀 <sup>2.8+</sup></h2>
该功能允许对输入框内容进行相关动态操作。通过对输入框元素设置 `lay-affix` 属性来开启动态点缀,且输入框元素必须放置在一个具有输入框点缀结构的容器中(上文有讲解)如:
该功能允许对输入框内容进行相关动态操作。通过对输入框元素设置 `lay-affix` 属性来开启动态点缀。
```
input 放在前后置容器中:
<div class="layui-input-group">
<input type="text" lay-affix="clear">
</div>
input 放在前后缀容器中:
<div class="layui-input-wrap">
<input type="text" lay-affix="clear">
</div>
```
`lay-affix` 属性支持设置内置属性值和自定义值
| 值 | 描述 |
| 点缀 | 属性值 |
| --- | --- |
| `lay-affix="eye"` | 密码框显隐 |
| `lay-affix="clear"` | 内容清除 |
| `lay-affix="自定义图标值"` | 值对应图标类`layui-icon-`后面的名称([#详见图标列表](../icon/#list))。<br>可通过「[点缀事件](#affix-event)」完成自定义操作 |
| [数字输入框](#affix-number) <sup>2.8.9+</sup> | `lay-affix="number"` |
| [密码框显隐](#affix-eye) | `lay-affix="eye"` |
| [内容清除](#affix-clear) | `lay-affix="clear"` |
| [自定义动态点缀](#affix-custom) | `lay-affix="customName"` |
<h3 id="affix-number" lay-toc="{level: 2, hot: true}" class="ws-bold">
数字输入框 <sup>2.8.9+</sup>
</h3>
一般搭配 `<input type="number">` 使用,用于替代原生输入框,可继承 `step,min,max` 等原生属性。
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], done: function(obj){
obj.render();
}}">
<textarea>
<div class="layui-form">
<input type="number" lay-affix="number" placeholder="" value="0" class="layui-input">
<hr class="ws-space-16">
<div class="layui-row layui-col-space16">
<div class="layui-col-xs6">
<input type="number" lay-affix="number" placeholder="设置 step 为 0.01" step="0.01" class="layui-input">
</div>
<div class="layui-col-xs6">
<input type="number" lay-affix="number" placeholder="设置 step,min,max" step="10" min="0" max="100" class="layui-input">
</div>
<div class="layui-col-xs12">
<input type="number" lay-affix="number" disabled placeholder="禁用状态" value="0" class="layui-input">
</div>
</div>
</div>
<!-- import layui -->
</textarea>
</pre>
<h3 id="affix-eye" lay-toc="{level: 2}" class="ws-bold">密码显隐</h3>
一般搭配 `<input type="password">` 使用,用于控制输入框内容的显示和隐藏。
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], done: function(obj){
obj.render();
}}">
@ -141,6 +161,8 @@ input 放在前后缀容器中:
<h3 id="affix-clear" lay-toc="{level: 2}" class="ws-bold">内容清除</h3>
一般搭配 `<input type="text">` 使用,用于清除输入框的内容。
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], done: function(obj){
obj.render();
}}">
@ -169,7 +191,7 @@ input 放在前后缀容器中:
<h3 id="affix-custom" lay-toc="{level: 2}" class="ws-bold">自定义动态点缀</h3>
我们还可以对 `lay-affix` 属性设置任意图标值,从而实现自定义动态点缀功能。
我们还可以对 `lay-affix` 属性设置任意图标值,从而实现自定义动态点缀功能。 其中 `lay-affix="customName"` 值对应图标类`layui-icon-`后面的名称([#详见图标列表](../icon/#list))。且可通过「[点缀事件](#affix-event)」完成自定义操作。
<pre class="layui-code" lay-options="{preview: true, layout: ['preview', 'code'], done: function(obj){
obj.render();
@ -195,6 +217,7 @@ input 放在前后缀容器中:
form.on('input-affix(filter)', function(data){
var elem = data.elem; // 获取输入框 DOM 对象
var affix = data.affix; // 获取输入框 lay-affix 属性值
console.log(this); // 当前触发点缀事件的图标元素
});
```

View File

@ -149,6 +149,6 @@ layui.use(['element', 'layer', 'util'], function(){
> 小贴士:以上是一个基础的框体布局方案,若要实现诸如 `iframe` 跳转、侧边菜单收缩等功能,还需按照实际的业务需求自主实现。当然,也可以采用社区已有的 AdminUI 主题方案layuiAdmin 等。
<h2 id="adminui" lay-toc="">Admin UI</h2>
<h2 id="adminui" lay-toc="{}">Admin UI</h2>
<a href="https://dev.layuion.com/themes/layuiadmin/" target="_blank">layuiAdmin</a> 是一套用于开发通用型管理系统的纯静态的 `HTML` 网页界面主题,基于开源的 Layui Web 组件库制作而成,没有任何后端程序及数据库存储等服务端代码。开发者使用该网页模板,可省去前端的大量工作,从而更高效地开发 Web 应用系统。

View File

@ -136,7 +136,7 @@ treeTable.render({
```
treeTable.render({
elem: '',
treee: {
tree: {
enable: true,
async: {
format: function(trData, options, callback){

View File

@ -265,6 +265,33 @@ acceptMime: 'image/jpeg, image/png` // 只筛选 jpg,png 格式图片
`true`
</td>
</tr>
<tr>
<td>
[text](#options.text) <sup>2.8.9+</sup>
</td>
<td colspan="4">
<div id="options.text" lay-pid="options" class="ws-anchor">
自定义内部各类场景下的提示文本
</div>
```
text: { // 自定义提示文本
"data-format-error": "", // 数据格式错误的提示
"check-error": "", // 文件格式校验失败的提示
"error": "", // 上传失败的提示
"limit-number": null, // 限制 number 属性的提示。若设置,需为函数写法
"limit-size": null, // 限制 size 属性的提示。若设置,需为函数写法
"cross-domain": "", // IE 下跨域的提示
}
```
</td>
</tr>
<tr>

View File

@ -1,4 +1,4 @@
<pre class="layui-code" lay-options="{preview: true, codeStyle: 'height: 535px;', layout: ['preview', 'code'], tools: ['full']}">
<pre class="layui-code" lay-options="{preview: true, codeStyle: 'height: 535px;', layout: ['preview', 'code'], allowParse: true, tools: ['full']}">
<textarea>
<h3 class="ws-anchor ws-bold">倒计时</h3>
@ -6,11 +6,8 @@
<div class="layui-inline">
<input type="text" readonly class="layui-input" id="test1" value="2099-12-31 00:00:00">
</div>
<span class="layui-word-aux layui-font-green" id="test2"></span>
<blockquote class="layui-elem-quote" style="margin-top: 10px;">
<div id="test2"></div>
</blockquote>
<h3 class="ws-anchor ws-bold">某个时间在多久前</h3>
请选择要计算的日期:
@ -51,26 +48,29 @@ layui.use(function(){
var $ = layui.$;
// 倒计时
var thisTimer;
var setCountdown = function(value){
var endTime = new Date(value); // 结束日期
var serverTime = new Date(); // 假设为当前服务器时间,这里采用的是本地时间,实际使用一般是取服务端的
clearTimeout(thisTimer);
util.countdown(endTime, serverTime, function(date, serverTime, timer){
var str = date[0] + ' 天 ' + date[1] + ' 时 ' + date[2] + ' 分 ' + date[3] + ' 秒';
lay('#test2').html('距离上述日期还有:'+ str);
thisTimer = timer;
});
};
setCountdown('2099-12-31 00:00:00');
var countdown = util.countdown({
date: '2099-12-31 00:00:00', // 目标时间值
now: new Date(), // 当前时间,一般为服务器时间,此处以本地时间为例
ready: function(){ // 初始操作
clearTimeout(util.countdown.timer); // 清除旧定时器,防止多次渲染时重复执行。实际使用时不常用
},
clock: function(obj, inst){ // 计时中
var str = [obj.d,'天',obj.h,'时',obj.m,'分',obj.s,'秒'].join(' ');
lay('#test2').html(str);
util.countdown.timer = inst.timer; // 记录当前定时器,以便在重复渲染时清除。实际使用时不常用
},
done: function(obj, inst){ // 计时完成
layer.msg('Time is up');
}
});
// 重置倒计时
laydate.render({
elem: '#test1',
type: 'datetime',
done: function(value, date){
setCountdown(value);
done: function(value){
countdown.reload({
date: value
});
}
});
@ -125,4 +125,4 @@ layui.use(function(){
});
</script>
</textarea>
</pre>
</pre>

View File

@ -20,8 +20,8 @@ toc: true
| API | 描述 |
| --- | --- |
| var util = layui.util | 获得 `util` 模块。 |
| util.fixbar(options) | `fixbar` 固定条组件,用法:[#详见](../fixbar/) |
| [util.countdown(endTime, serverTime, callback)](#countdown) | 倒计时 |
| [util.fixbar(options)](../fixbar/) | 固定条组件 |
| [util.countdown(options)](#countdown) | 倒计时组件 |
| [util.timeAgo(time, onlyDate)](#timeAgo) | 某个时间在多久前 |
| [util.toDateString(time, format)](#toDateString) | 将毫秒数或日期对象转换成日期格式字符 |
| [util.digit(num, length)](#digit) | 数字前置补零 |
@ -32,26 +32,45 @@ toc: true
<h3 id="countdown" class="ws-anchor ws-bold">倒计时</h3>
`util.countdown(endTime, serverTime, callback);`
`util.countdown(options);`
- 参数 `endTime` : 结束时间毫秒数或 `Date` 对象
- 参数 `serverTime` : 服务器当前时间毫秒数 或 `Date` 对象
- 参数 `callback` : 倒计时回调函数。若倒计时正在运行,则每一秒都会执行一次。并且返回以下三个参数。
- `date` 包含天/时/分/秒的对象
- `serverTime` 当前服务器时间毫秒数或 `Date` 对象
- `timer` 计时器返回的索引,用于 `clearTimeout`
- 参数 `options` <sup>2.8.9+</sup>: 属性配置项。可选项详见下表:
该方法并不负责视图的呈现,而仅返回倒计时数值。 相关用法见:[#示例](#examples)
| 属性 | 描述 |
| --- | --- |
| date | 目标时间值。值可以为毫秒数或 `Date` 对象 |
| now | 当前时间值,一般为当前服务器时间。值可以为毫秒数或 `Date` 对象 |
| ready | 倒计时初始时的回调函数。 |
| clock | 倒计时计时中的回调函数,每秒触发一次,直到计时完成。 |
| done | 倒计时计时完成的回调函数,即到达目标时间值时触发 |
- 注: <sup>2.8.9</sup> 之前的版本写法为:`util.countdown(date, now, clock);`
该方法返回的实例对象成员如下 <sup>2.8.9+</sup>
```js
var countdown = util.countdown(options);
countdown.clear(); // 清除当前倒计时
countdown.reload(options); // 重载当前倒计时。
countdown.timer; // 当前倒计时计时器 ID
```
相关用法可参考:[#示例](#examples)
```js
layui.use('util', function(){
var util = layui.util;
// 示例
var endTime = new Date(2099,1,1).getTime() // 假设为结束日期
var serverTime = new Date().getTime(); // 这里采用的是本地时间,实际使用一般是取服务端时间
util.countdown(endTime, serverTime, function(date, serverTime, timer){
console.log(date, serverTime, timer)
util.countdown({
date: '2099-1-1', // 目标时间值
now: new Date(), // 当前时间,一般为服务器时间,此处以本地时间为例
clock: function(obj, countdown){ // 计时中
console.log(obj); // 得到当前计时器的「天、时、分、秒」值
console.log(countdown); // 得到当前实例对象
},
done: function(obj, countdown){ // 计时完成
console.log('time is up');
}
});
});
```

View File

@ -12,6 +12,29 @@ toc: true
> 导读:📑 [Layui 2.8 《升级指南》](/notes/2.8/upgrade-guide.html) · 📑 [Layui 新版文档站上线初衷](/notes/2.8/news.html)
<h2 id="2.8.9" class="ws-anchor">
2.8.9
<span class="layui-badge-rim">2023-07-03</span>
</h2>
- #### form
- 新增 `input` 数字输入框组件,通过动态点缀 `lay-affix="number"` 属性开启
- 优化 `input,textarea` 禁用状态时的样式 # I7GN5Z
- #### table
- 优化 点击单元格出现编辑框时,不触发行事件
- #### treeTable
- 修复 选中和取消选中时,父节点和子节点的选中背景色未能正确同步的问题 # I7FUD6
- #### upload
- 新增 `text` 属性,用于自定义内部各类场景下的提示文本
- #### util
- 重构 `countdown` 倒计时组件,采用 `options` 参数写法,但仍对旧版兼容
- 新增 `countdown``date,now,clock,done` 等属性
- 新增 `countdown``clear,reload` 等实例方法,用于清除和重置倒计时等操作
### 下载: [layui-v2.8.9.zip](https://gitee.com/layui/layui/attach_files/1454465/download)
---
<h2 id="2.8.8" class="ws-anchor">
2.8.8
<span class="layui-badge-rim">2023-06-20</span>

View File

@ -23,7 +23,7 @@
<div class="layui-input-split layui-input-prefix">
前置信息
</div>
<input type="text" name="arr[]" required lay-verify="required" placeholder="请输入" autocomplete="off" class="layui-input">
<input type="text" name="arr[]" required lay-verify="required" placeholder="请输入" autocomplete="off" class="layui-input" lay-affix="clear">
<div class="layui-input-split layui-input-suffix">
后置信息
</div>
@ -109,6 +109,26 @@
</select>
</div>
</div>
<div class="layui-col-md3">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-date"></i>
</div>
<input type="text" name="arr[]" required placeholder="前缀和后缀+动态点缀" autocomplete="off" class="layui-input" lay-affix="clear">
<div class="layui-input-suffix">
<i class="layui-icon layui-icon-down"></i>
</div>
</div>
</div>
<div class="layui-col-md3">
<input type="number" step="0.01" min="0" max="10" name="arr[]" required placeholder="数字输入框" lay-affix="number" autocomplete="off" class="layui-input">
</div>
<div class="layui-col-md3">
<input type="number" step="0.01" min="0" max="10" name="arr[]" required placeholder="系统自带数字输入框" autocomplete="off" class="layui-input">
</div>
<div class="layui-col-md3">
<input type="number" disabled name="arr[]" required placeholder="数字输入框 禁用状态" lay-affix="number" autocomplete="off" class="layui-input">
</div>
<div class="layui-col-md12">
<div class="layui-input-wrap">
<textarea class="layui-textarea" lay-affix="clear"></textarea>

View File

@ -55,7 +55,7 @@
</script>
<script type="text/html" id="cityTpl">
<select id="demoCity1" lay-ignore>
<select id="demoCity1" lay-unrow lay-ignore>
<option value="浙江杭州">浙江杭州</option>
<option value="江西南昌">江西南昌</option>
<option value="湖北武汉">湖北武汉</option>

View File

@ -24,6 +24,7 @@ layui.use(['treeTable'], function(){
url: './json/treeTable/demo-1.json',
maxHeight: 'full-32',
cols: [[
{type: 'checkbox', fixed: 'left'},
{type: 'numbers', fixed: 'left'},
{field: 'id', title: 'ID', width: 145, sort: true, fixed: 'left', totalRow: '合计:'},
{field: 'name', title: '用户名', width: 180, fixed: 'left'},

View File

@ -1,6 +1,6 @@
{
"name": "layui",
"version": "2.8.8",
"version": "2.8.9",
"description": "Classic modular Front-End UI library",
"main": "dist/layui.js",
"license": "MIT",

View File

@ -722,6 +722,7 @@ a cite{font-style: normal; *cursor:pointer;}
.layui-input:hover, .layui-textarea:hover{border-color: #d2d2d2 !important;}
.layui-input:focus, .layui-textarea:focus{border-color: #16b777 !important; box-shadow: 0 0 0 3px rgba(22,183,119,0.08);}
.layui-textarea{position: relative; min-height: 100px; height: auto; line-height: 20px; padding: 6px 10px; resize: vertical;}
.layui-input[disabled], .layui-textarea[disabled]{background-color: #fafafa;}
.layui-select{padding: 0 10px;}
.layui-form select,
.layui-form input[type=checkbox],
@ -780,6 +781,7 @@ a cite{font-style: normal; *cursor:pointer;}
.layui-input-wrap .layui-input-prefix,
.layui-input-wrap .layui-input-suffix,
.layui-input-wrap .layui-input-split{pointer-events: none;}
.layui-input-wrap .layui-input:hover + .layui-input-split{border-color: #d2d2d2;}
.layui-input-wrap .layui-input:focus + .layui-input-split{border-color: #16b777;}
.layui-input-wrap .layui-input-prefix.layui-input-split{border-width: 0; border-right-width: 1px;}
@ -790,6 +792,17 @@ a cite{font-style: normal; *cursor:pointer;}
.layui-input-affix .layui-icon-clear{color: rgba(0,0,0,.3);}
.layui-input-affix .layui-icon:hover{color: rgba(0,0,0,.6);}
/* 数字输入框动态点缀 */
.layui-input-number{width: 24px; padding: 0;}
.layui-input-number .layui-icon{position: absolute; right: 0; width: 100%; height: 50%; line-height: normal; font-size: 12px;}
.layui-input-number .layui-icon:before{position: absolute; left: 50%; top: 50%; margin-top: -6px; margin-left: -6px;}
.layui-input-number .layui-icon:first-child{top: 0; border-bottom: 1px solid #eee;}
.layui-input-number .layui-icon:last-child{bottom: 0;}
.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;}
/* 下拉选择 */
@ -822,6 +835,7 @@ a cite{font-style: normal; *cursor:pointer;}
.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}
.layui-form-checkbox:hover > div{background-color: #c2c2c2;}
.layui-form-checkbox > i{position: absolute; right: 0; top: 0; width: 30px; height: 100%; border: 1px solid #d2d2d2; border-left: none; border-radius: 0 2px 2px 0; color: #fff; color: rgba(255,255,255,0); font-size: 20px; text-align: center; box-sizing: border-box;}
.layui-form-checkbox:hover > i{border-color: #c2c2c2; color: #c2c2c2;}
@ -1509,12 +1523,12 @@ body .layui-util-face .layui-layer-content{padding:0; background-color:#fff; co
.layui-slider-wrap-btn.layui-disabled:hover{transform: scale(1) !important;}
.layui-slider-tips{position: absolute; top: -42px; z-index: 77777777; white-space:nowrap; display: none; -webkit-transform: translateX(-50%); transform: translateX(-50%); color: #FFF; background: #000; border-radius: 3px; height: 25px; line-height: 25px; padding: 0 10px;}
.layui-slider-tips:after{content: ""; position: absolute; bottom: -12px; left: 50%; margin-left: -6px; width: 0; height: 0; border-width: 6px; border-style: solid; border-color: #000 transparent transparent transparent;}
.layui-slider-input{width: 70px; height: 32px; border: 1px solid #eee; border-radius: 3px; font-size: 16px; line-height: 32px; position: absolute; right: 0; top: -14px;}
.layui-slider-input{width: 70px; height: 32px; border: 1px solid #eee; border-radius: 3px; font-size: 16px; line-height: 32px; position: absolute; right: 0; top: -14px; box-sizing: border-box;}
.layui-slider-input-btn{position: absolute; top: 0; right: 0; width: 20px; height: 100%; border-left: 1px solid #eee;}
.layui-slider-input-btn i{cursor: pointer; position: absolute; right: 0; bottom: 0; width: 20px; height: 50%; font-size: 12px; line-height: 16px; text-align: center; color: #999;}
.layui-slider-input-btn i:first-child{top: 0; border-bottom: 1px solid #eee;}
.layui-slider-input-txt{height: 100%; font-size: 14px;}
.layui-slider-input-txt input{height: 100%; border: none;}
.layui-slider-input-txt input{height: 100%; border: none; padding-right: 21px;}
.layui-slider-input-btn i:hover{color: #16baaa;}
/*垂直滑块*/
.layui-slider-vertical{width: 4px; margin-left: 33px;}

View File

@ -16,7 +16,7 @@
};
var Layui = function(){
this.v = '2.8.8'; // Layui 版本号
this.v = '2.8.9'; // Layui 版本号
};
// 识别预先可能定义的指定全局对象

View File

@ -90,6 +90,14 @@ layui.define(['lay', 'util', 'element', 'form'], function(exports){
othis.data('code', codes);
// code
var html = finalCode = codes.join('');
// 外部重新解析 code
if(typeof options.codeParse === 'function'){
html = finalCode = options.codeParse(html);
}
// 工具栏
var tools = {
copy: {
@ -231,7 +239,7 @@ layui.define(['lay', 'util', 'element', 'form'], function(exports){
var run = function(thisItemBody){
var iframe = thisItemBody.children('iframe')[0];
if(isIframePreview && iframe){
iframe.srcdoc = codes.join('');
iframe.srcdoc = finalCode;
} else {
thisItemBody.html(codes.join(''));
}
@ -297,13 +305,7 @@ layui.define(['lay', 'util', 'element', 'form'], function(exports){
}
// code
var html = finalCode = codes.join('');
// 外部重新解析 code
if(typeof options.codeParse === 'function'){
html = finalCode = options.codeParse(html);
}
// 转义 HTML 标签
if(options.encode) html = util.escape(html); // 编码

View File

@ -162,6 +162,7 @@ layui.define(['lay', 'layer', 'util'], function(exports){
elemForm.find('input[lay-affix],textarea[lay-affix]').each(function(){
var othis = $(this);
var affix = othis.attr('lay-affix');
var CLASS_WRAP = 'layui-input-wrap';
var CLASS_SUFFIX = 'layui-input-suffix';
var CLASS_AFFIX = 'layui-input-affix';
var disabled = othis.is('[disabled]') || othis.is('[readonly]');
@ -179,17 +180,32 @@ layui.define(['lay', 'layer', 'util'], function(exports){
value: affix
}), opts, lay.options(othis[0]));
var elemAffix = $('<div class="'+ CLASS_AFFIX +'">');
var elemIcon = $('<i class="layui-icon layui-icon-'+ opts.value + (
opts.disabled ? (' '+ DISABLED) : ''
) +'"></i>');
var value = layui.isArray(opts.value) ? opts.value : [opts.value];
var elemIcon = $(function(){
var arr = [];
layui.each(value, function(i, item){
arr.push('<i class="layui-icon layui-icon-'+ item + (
opts.disabled ? (' '+ DISABLED) : ''
) +'"></i>');
});
return arr.join('');
}());
elemAffix.append(elemIcon);
elemAffix.append(elemIcon); // 插入图标元素
// 追加 className
if(opts.split) elemAffix.addClass('layui-input-split');
if(opts.className) elemAffix.addClass(opts.className);
// 移除旧的元素
var hasElemAffix = othis.next('.'+ CLASS_AFFIX);
if(hasElemAffix[0]) hasElemAffix.remove();
// 是否在规定的容器中
if(!othis.parent().hasClass(CLASS_WRAP)){
othis.wrap('<div class="'+ CLASS_WRAP +'"></div>');
}
// 是否已经存在后缀元素
var hasElemSuffix = othis.next('.'+ CLASS_SUFFIX);
if(hasElemSuffix[0]){
@ -256,6 +272,36 @@ layui.define(['lay', 'layer', 'util'], function(exports){
},
show: 'auto', // 根据输入框值是否存在来显示或隐藏点缀图标
disabled: disabled // 跟随输入框禁用状态
},
number: { // 数字输入框
value: ['up', 'down'],
split: true,
className: 'layui-input-number',
disabled: disabled, // 跟随输入框禁用状态
click: function(elem){
var index = $(this).index();
var value = elem.val();
var step = Number(elem.attr('step')) || 1; // 加减的数字间隔
var min = Number(elem.attr('min'));
var max = Number(elem.attr('max'));
if(isNaN(value)) return; // 若非数字,则不作处理
value = Number(value);
value = index ? value - step : value + step;
// min max
if(value < min) value = min;
if(value > max) value = max;
// 小数点后保留位数
var fixed = function(step){
return (step.match(/(?<=\.)[\d]+$/) || [''])[0].length;
}(step.toString());
if(fixed) value = value.toFixed(fixed);
elem.val(value);
}
}
};

View File

@ -179,7 +179,7 @@ layui.define(['jquery', 'lay'], function(exports){
//插入输入框
if(options.input && !options.range){
var elemInput = $('<div class="layui-slider-input layui-input"><div class="layui-slider-input-txt"><input type="text" class="layui-input"></div><div class="layui-slider-input-btn"><i class="layui-icon layui-icon-up"></i><i class="layui-icon layui-icon-down"></i></div></div>');
var elemInput = $('<div class="layui-slider-input"><div class="layui-slider-input-txt"><input type="text" class="layui-input"></div><div class="layui-slider-input-btn"><i class="layui-icon layui-icon-up"></i><i class="layui-icon layui-icon-down"></i></div></div>');
othis.css("position","relative");
othis.append(elemInput);
othis.find('.' + SLIDER_INPUT_TXT).children('input').val(options.value);

View File

@ -2314,9 +2314,9 @@ layui.define(['lay', 'laytpl', 'laypage', 'form', 'util'], function(exports){
// 显示编辑表单
if(editType){
var input = $(function(){
var inputElem = '<input class="layui-input '+ ELEM_EDIT +'">';
var inputElem = '<input class="layui-input '+ ELEM_EDIT +'" lay-unrow>';
if(editType === 'textarea') {
inputElem = '<textarea class="layui-input ' + ELEM_EDIT + '"></textarea>';
inputElem = '<textarea class="layui-input ' + ELEM_EDIT + '" lay-unrow></textarea>';
}
return inputElem;
}());

View File

@ -68,6 +68,7 @@ layui.define(['table'], function (exports) {
var ELEM_FIXED = '.layui-table-fixed';
var ELEM_FIXL = '.layui-table-fixed-l';
var ELEM_FIXR = '.layui-table-fixed-r';
var ELEM_CHECKED = 'layui-table-checked';
var TABLE_TREE = 'layui-table-tree';
var LAY_DATA_INDEX = 'LAY_DATA_INDEX';
@ -1511,8 +1512,15 @@ layui.define(['table'], function (exports) {
if (dataP) {
var trsP = that.updateParentCheckStatus(dataP, layui.type(checked) === 'boolean' ? checked : null);
layui.each(trsP, function (indexP, itemP) {
form.render(tableView.find('tr[lay-data-index="' + itemP[LAY_DATA_INDEX] + '"] input[name="layTableCheckbox"]:not(:disabled)').prop({
checked: itemP[checkName],
var checkboxElem = tableView.find('tr[lay-data-index="' + itemP[LAY_DATA_INDEX] + '"] input[name="layTableCheckbox"]:not(:disabled)');
var checked = itemP[checkName];
// 标记父节点行背景色
checkboxElem.closest('tr')[checked ? 'addClass' : 'removeClass'](ELEM_CHECKED);
// 设置原始复选框 checked 属性值并渲染
form.render(checkboxElem.prop({
checked: checked,
indeterminate: itemP[LAY_CHECKBOX_HALF]
}))
})
@ -1639,11 +1647,15 @@ layui.define(['table'], function (exports) {
// that.updateStatus(null, statusChecked); // 取消其他的选中状态
that.updateStatus(null, function (d) {
if (d[checkName]) {
var radioElem = tableView.find('tr[lay-data-index="' + d[LAY_DATA_INDEX] + '"] input[type="radio"][lay-type="layTableRadio"]');
d[checkName] = false;
form.render(tableView.find('tr[lay-data-index="' + d[LAY_DATA_INDEX] + '"] input[type="radio"][lay-type="layTableRadio"]').prop('checked', false));
radioElem.closest('tr').removeClass(ELEM_CHECKED); // 取消当前选中行背景色
form.render(radioElem.prop('checked', false));
}
}); // 取消其他的选中状态
trData[checkName] = checked;
trElem[checked ? 'addClass' : 'removeClass'](ELEM_CHECKED); // 标记当前选中行背景色
trElem.siblings().removeClass(ELEM_CHECKED); // 取消其他行背景色
form.render(trElem.find('input[type="radio"][lay-type="layTableRadio"]').prop('checked', checked));
} else {
var isParentKey = options.tree.customName.isParent;
@ -1660,9 +1672,11 @@ layui.define(['table'], function (exports) {
}
var trs = that.updateStatus(trData ? [trData] : table.cache[tableId], checkedStatusFn);
form.render(tableView.find(trs.map(function (value) {
var checkboxElem = tableView.find(trs.map(function (value) {
return 'tr[lay-data-index="' + value[LAY_DATA_INDEX] + '"] input[name="layTableCheckbox"]:not(:disabled)';
}).join(',')).prop({checked: checked, indeterminate: false}));
}).join(','));
checkboxElem.closest('tr')[checked ? 'addClass' : 'removeClass'](ELEM_CHECKED); // 标记当前选中行背景色
form.render(checkboxElem.prop({checked: checked, indeterminate: false}));
// }
var trDataP;
// 更新父节点以及更上层节点的状态

View File

@ -53,7 +53,6 @@ layui.define(['lay','layer'], function(exports){
var ELEM_CHOOSE = 'layui-upload-choose';
var ELEM_DRAG = 'layui-upload-drag';
// 构造器
var Class = function(options){
var that = this;
@ -76,7 +75,15 @@ layui.define(['lay','layer'], function(exports){
drag: true, // 是否允许拖拽上传
size: 0, // 文件限制大小,默认不限制
number: 0, // 允许同时上传的文件数,默认不限制
multiple: false // 是否允许多文件上传不支持ie8-9
multiple: false, // 是否允许多文件上传,不支持 ie8-9
text: { // 自定义提示文本
"cross-domain": "Cross-domain requests are not supported", // 跨域
"data-format-error": "Please return JSON data format", // 数据格式错误
"check-error": "", // 文件格式校验失败
"error": "", // 上传失败
"limit-number": null, // 限制 number 属性的提示 --- function
"limit-size": null // 限制 size 属性的提示 --- function
}
};
// 初始渲染
@ -152,8 +159,8 @@ layui.define(['lay','layer'], function(exports){
//异常提示
Class.prototype.msg = function(content){
return layer.msg(content, {
icon: 2
,shift: 6
icon: 2,
shift: 6
});
};
@ -182,6 +189,7 @@ layui.define(['lay','layer'], function(exports){
Class.prototype.upload = function(files, type){
var that = this;
var options = that.config;
var text = options.text || {};
var elemFile = that.elemFile[0];
// 获取文件队列
@ -237,7 +245,7 @@ layui.define(['lay','layer'], function(exports){
},
error: function(e){ // 异常回调
options.unified ? (failed += that.fileLength) : failed++;
that.msg([
that.msg(text['error'] || [
'Upload failed, please try again.',
'status: '+ (e.status || '') +' - '+ (e.statusText || 'error')
].join('<br>'));
@ -284,14 +292,14 @@ layui.define(['lay','layer'], function(exports){
that.elemFile.parent().submit();
//获取响应信息
// 获取响应信息
clearInterval(Class.timer);
Class.timer = setInterval(function() {
var res, iframeBody = iframe.contents().find('body');
try {
res = iframeBody.text();
} catch(e) {
that.msg('Cross-domain requests are not supported');
that.msg(text['cross-domain']);
clearInterval(Class.timer);
error();
}
@ -314,7 +322,7 @@ layui.define(['lay','layer'], function(exports){
res = JSON.parse(res);
} catch(e){
res = {};
return that.msg('Please return JSON data format');
return that.msg(text['data-format-error']);
}
}
}
@ -377,7 +385,7 @@ layui.define(['lay','layer'], function(exports){
// 上传前的回调 - 如果回调函数明确返回 false则停止上传
if(options.before && (options.before(args) === false)) return;
// IE兼容处理
// IE 兼容处理
if(device.ie){
return device.ie > 9 ? ajaxSend() : iframeSend();
}
@ -393,37 +401,37 @@ layui.define(['lay','layer'], function(exports){
audio: '音频'
})[options.accept] || '文件';
//校验文件格式
// 校验文件格式
value = value.length === 0
? ((elemFile.value.match(/[^\/\\]+\..+/g)||[]) || '')
: value;
if(value.length === 0) return;
//根据文件类型校验
// 根据文件类型校验
switch(options.accept){
case 'file': //一般文件
case 'file': // 一般文件
layui.each(value, function(i, item){
if(exts && !RegExp('.\\.('+ exts +')$', 'i').test(escape(item))){
return check = true;
}
});
break;
case 'video': //视频文件
case 'video': // 视频文件
layui.each(value, function(i, item){
if(!RegExp('.\\.('+ (exts || 'avi|mp4|wma|rmvb|rm|flash|3gp|flv') +')$', 'i').test(escape(item))){
return check = true;
}
});
break;
case 'audio': //音频文件
case 'audio': // 音频文件
layui.each(value, function(i, item){
if(!RegExp('.\\.('+ (exts || 'mp3|wav|mid') +')$', 'i').test(escape(item))){
return check = true;
}
});
break;
default: //图片文件
default: // 图片文件
layui.each(value, function(i, item){
if(!RegExp('.\\.('+ (exts || 'jpg|png|gif|bmp|jpeg|svg') +')$', 'i').test(escape(item))){
return check = true;
@ -432,13 +440,13 @@ layui.define(['lay','layer'], function(exports){
break;
}
//校验失败提示
// 校验失败提示
if(check){
that.msg('选择的'+ typeName +'中包含不支持的格式');
that.msg(text['check-error'] || ('选择的'+ typeName +'中包含不支持的格式'));
return elemFile.value = '';
}
//选择文件的回调
// 选择文件的回调
if(type === 'choose' || options.auto){
options.choose && options.choose(args);
if(type === 'choose'){
@ -446,7 +454,7 @@ layui.define(['lay','layer'], function(exports){
}
}
//检验文件数量
// 检验文件数量
that.fileLength = function(){
var length = 0;
var items = getFiles();
@ -457,10 +465,12 @@ layui.define(['lay','layer'], function(exports){
}();
if(options.number && that.fileLength > options.number){
return that.msg(
return that.msg(typeof text['limit-number'] === 'function'
? text['limit-number'](options, that.fileLength)
: (
'同时最多只能上传: '+ options.number + ' 个文件'
+'<br>您当前已经选择了: '+ that.fileLength +' 个文件'
);
));
}
// 检验文件大小
@ -475,7 +485,9 @@ layui.define(['lay','layer'], function(exports){
limitSize = size;
}
});
if(limitSize) return that.msg('文件大小不能超过 '+ limitSize);
if(limitSize) return that.msg(typeof text['limit-size'] === 'function'
? text['limit-size'](options, limitSize)
: '文件大小不能超过 '+ limitSize);
}
send();

View File

@ -131,48 +131,95 @@ layui.define('jquery', function(exports){
setTopBar();
}, 100);
});
}
},
//倒计时
,countdown: function(endTime, serverTime, callback){
var that = this
,type = typeof serverTime === 'function'
,end = new Date(endTime).getTime()
,now = new Date((!serverTime || type) ? new Date().getTime() : serverTime).getTime()
,count = end - now
,time = [
Math.floor(count/(1000*60*60*24)) //天
,Math.floor(count/(1000*60*60)) % 24 //时
,Math.floor(count/(1000*60)) % 60 //分
,Math.floor(count/1000) % 60 //秒
];
// 倒计时
countdown: function(options){
var that = this;
// 默认可选项
options = $.extend(true, {
date: new Date(),
now: new Date()
}, options);
// 兼容旧版参数
var args = arguments;
if(args.length > 1){
options.date = new Date(args[0]);
options.now = new Date(args[1]);
options.clock = args[2];
}
// 实例对象
var inst = {
options: options,
clear: function(){ // 清除计时器
clearTimeout(inst.timer);
},
reload: function(opts){ // 重置倒计时
this.clear();
$.extend(true, this.options, {
now: new Date()
}, opts);
count();
}
};
typeof options.ready === 'function' && options.ready();
// 计算倒计时
var count = (function fn(){
var date = new Date(options.date);
var now = new Date(options.now);
var countTime = function(time){
return time > 0 ? time : 0;
}(date.getTime() - now.getTime());
var result = {
d: Math.floor(countTime/(1000*60*60*24)), // 天
h: Math.floor(countTime/(1000*60*60)) % 24, // 时
m: Math.floor(countTime/(1000*60)) % 60, // 分
s: Math.floor(countTime/1000) % 60 // 秒
};
var next = function(){
now.setTime(now.getTime() + 1000);
options.now = now;
count();
};
// 兼容旧版返回值
if(args.length > 1) result = [result.d,result.h,result.m,result.s]
// 计时 - 以秒间隔
inst.timer = setTimeout(next, 1000);
typeof options.clock === 'function' && options.clock(result, inst);
// 计时完成
if(countTime <= 0){
clearTimeout(inst.timer);
typeof options.done === 'function' && options.done(result, inst);
};
return fn;
})();
if(type) callback = serverTime;
var timer = setTimeout(function(){
that.countdown(endTime, now + 1000, callback);
}, 1000);
callback && callback(count > 0 ? time : [0,0,0,0], serverTime, timer);
if(count <= 0) clearTimeout(timer);
return timer;
}
return inst;
},
//某个时间在当前时间的多久前
,timeAgo: function(time, onlyDate){
var that = this
,arr = [[], []]
,stamp = new Date().getTime() - new Date(time).getTime();
// 某个时间在当前时间的多久前
timeAgo: function(time, onlyDate){
var that = this;
var arr = [[], []];
var stamp = new Date().getTime() - new Date(time).getTime();
//返回具体日期
// 返回具体日期
if(stamp > 1000*60*60*24*31){
stamp = new Date(time);
arr[0][0] = that.digit(stamp.getFullYear(), 4);
arr[0][1] = that.digit(stamp.getMonth() + 1);
arr[0][2] = that.digit(stamp.getDate());
//是否输出时间
// 是否输出时间
if(!onlyDate){
arr[1][0] = that.digit(stamp.getHours());
arr[1][1] = that.digit(stamp.getMinutes());
@ -181,22 +228,22 @@ layui.define('jquery', function(exports){
return arr[0].join('-') + ' ' + arr[1].join(':');
}
//30天以内返回“多久前”
// 30 天以内,返回「多久前」
if(stamp >= 1000*60*60*24){
return ((stamp/1000/60/60/24)|0) + ' 天前';
} else if(stamp >= 1000*60*60){
return ((stamp/1000/60/60)|0) + ' 小时前';
} else if(stamp >= 1000*60*3){ //3分钟以内为刚刚
} else if(stamp >= 1000*60*3){ // 3 分钟以内为:刚刚
return ((stamp/1000/60)|0) + ' 分钟前';
} else if(stamp < 0){
return '未来';
} else {
return '刚刚';
}
}
},
//数字前置补零
,digit: function(num, length){
// 数字前置补零
digit: function(num, length){
var str = '';
num = String(num);
length = length || 2;
@ -204,10 +251,10 @@ layui.define('jquery', function(exports){
str += '0';
}
return num < Math.pow(10, length) ? str + (num|0) : num;
}
},
//转化为日期格式字符
,toDateString: function(time, format){
// 转化为日期格式字符
toDateString: function(time, format){
//若 null 或空字符,则返回空字符
if(time === null || time === '') return '';
@ -236,10 +283,10 @@ layui.define('jquery', function(exports){
.replace(/HH/g, hms[0])
.replace(/mm/g, hms[1])
.replace(/ss/g, hms[2]);
}
},
//转义 html
,escape: function(html){
// 转义 html
escape: function(html){
var exp = /[<"'>]|&(?=#[a-zA-Z0-9]+)/g;
if(html === undefined || html === null) return '';
@ -249,20 +296,20 @@ layui.define('jquery', function(exports){
return html.replace(/&(?!#?[a-zA-Z0-9]+;)/g, '&amp;')
.replace(/</g, '&lt;').replace(/>/g, '&gt;')
.replace(/'/g, '&#39;').replace(/"/g, '&quot;');
}
},
//还原转义的 html
,unescape: function(html){
// 还原转义的 html
unescape: function(html){
if(html === undefined || html === null) html = '';
html += '';
return html.replace(/\&amp;/g, '&')
.replace(/\&lt;/g, '<').replace(/\&gt;/g, '>')
.replace(/\&#39;/g, '\'').replace(/\&quot;/g, '"');
}
},
// 打开新窗口
,openWin: function(options){
openWin: function(options){
var win;
options = options || {};
win = options.window || window.open((options.url || ''), options.target, options.specs);
@ -270,10 +317,10 @@ layui.define('jquery', function(exports){
win.document.open('text/html', 'replace');
win.document.write(options.content || '');
win.document.close();
}
},
//让指定的元素保持在可视区域
,toVisibleArea: function(options){
// 让指定的元素保持在可视区域
toVisibleArea: function(options){
options = $.extend({
margin: 160 //触发动作的边界值
,duration: 200 //动画持续毫秒数
@ -298,10 +345,10 @@ layui.define('jquery', function(exports){
obj[SCROLL_NAME] = thisOffset - size/2 + scrollValue
scrollElem.animate(obj, options.duration);
}
}
},
//批量事件
,event: function(attr, obj, eventType){
event: function(attr, obj, eventType){
var _body = $('body');
eventType = eventType || 'click';