textool.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /**
  2. * Layui 文本输入工具组件
  3. *
  4. * @author iTanken
  5. * @since 20200310
  6. */
  7. layui.define(['jquery'], function (exports) {
  8. var $ = layui.$, baseClassName = 'layext-text-tool', extClassName = 'layext-textool-pane',
  9. style, alignRight = true, alignClass, nodes = [], tools = {
  10. "hide": null, "count": null, "copy": null, "reset": null, "clear": null,
  11. hideIndex: -1, countIndex: -1, copyIndex: -1, resetIndex: -1, clearIndex: -1,
  12. hideName: "hide", countName: "count", copyName: "copy", resetName: "reset", clearName: "clear",
  13. hideClass: "layext-textool-minmax", countClass: "layext-textool-count", maxClass: "layext-textool-max",
  14. copyClass: "layext-textool-copy", resetClass: "layext-textool-reset", clearClass: "layext-textool-clear",
  15. copyTextId: baseClassName + '-copy-text', lengthClass: 'layext-textool-length',
  16. lengthOverClass: baseClassName + '-length-over', laytips: 'layext-textool-laytips'
  17. }, defaultOptions = {
  18. // 根据元素 id 值单独渲染,为空默认根据 class='layext-text-tool' 批量渲染
  19. eleId: null,
  20. // 批量设置输入框最大长度,可结合 eleId 单独设置最大长度
  21. maxlength: -1,
  22. // 初始化回调,无参
  23. initEnd: $.noop,
  24. // 显示回调,参数为当前输入框和工具条面板的 jQuery 对象
  25. showEnd: $.noop,
  26. // 隐藏回调,参数为当前输入框和工具条面板的 jQuery 对象
  27. hideEnd: $.noop,
  28. // 初始化展开,默认展开,否则收起
  29. initShow: true,
  30. // 工具条是否位于输入框内部,默认位于外部
  31. inner: false,
  32. // 工具条对齐方向,默认右对齐,可选左对齐 'left'
  33. align: 'right',
  34. // 启用指定工具模块,默认依次为字数统计、复制内容、重置内容、清空内容,按数组顺序显示
  35. tools: ['count', 'copy', 'reset', 'clear'],
  36. // 工具按钮提示类型,默认为 'title' 属性,可选 'laytips',使用 layer 组件的吸附提示, 其他值不显示提示
  37. tipType: 'title',
  38. // 吸附提示背景颜色
  39. tipColor: '#01AAED',
  40. // 工具条字体颜色
  41. color: '#666666',
  42. // 工具条背景颜色
  43. bgColor: '#FFFFFF',
  44. // 工具条边框颜色
  45. borderColor: '#E6E6E6',
  46. // 工具条附加样式类名
  47. className: '',
  48. // z-index
  49. zIndex: 19891014
  50. }, Class = function (custom) {
  51. var _this = this;
  52. _this.tipsAttr = null;
  53. _this.selector = null;
  54. _this.init(_this, custom || {});
  55. };
  56. /** 初始化 */
  57. Class.prototype.init = function (_this, custom) {
  58. _this.options = $.extend({}, defaultOptions, custom);
  59. _this.selector = $.trim(_this.options.eleId) === '' ? '.' + baseClassName : '#' + _this.options.eleId;
  60. _this.initStyle(_this);
  61. _this.initPrototype();
  62. $(_this.selector).each(function (i, n) {
  63. var $this = $(this), maxlength = _this.options.maxlength;
  64. !isNaN(maxlength) && maxlength > -1 && $this.attr('maxlength', maxlength);
  65. _this.addTextool(_this, $this);
  66. });
  67. _this.initTips(_this);
  68. typeof _this.options.initEnd === 'function' && _this.options.initEnd();
  69. };
  70. /** 初始化扩展样式 */
  71. Class.prototype.initStyle = function (_this) {
  72. _this.options.zIndex = isNaN(_this.options.zIndex) ? 0 : _this.options.zIndex || 0;
  73. style = ['<style type="text/css">',
  74. '#', tools.copyTextId, ' { width: 0; height: 0; position: absolute; top: -190000px; }',
  75. _this.selector, ' { position: relative; z-index: ', _this.options.zIndex + (_this.options.inner ? 0 : 1), '; }',
  76. _this.selector, ' + .', extClassName, ' { position: relative; z-index: ', _this.options.zIndex, '; margin-top: -2px; display: block; outline: none; }',
  77. _this.selector, ' + .', extClassName, ' * { color: ', (_this.options.color || '#666666'), '; }',
  78. _this.selector, ' + .', extClassName, ' a > i { font-size: 12px!important; }',
  79. _this.selector, ' + .', extClassName, ' a { padding: 0 3px; cursor: pointer; }',
  80. _this.selector, ' + .', extClassName, ' a:active { background-color: #E2E2E2; opacity: 0.8; }',
  81. _this.selector, ' + .', extClassName, ' .', tools.lengthClass, ' * { font-family: Consolas, sans-serif; }',
  82. _this.selector, ' + .', extClassName, ' .', tools.lengthClass, ' { display: inline-block; border-width: 0 1px; }', /* border: 1px solid #F6F6F6; */
  83. _this.selector, ' + .', extClassName, ' .', tools.lengthClass, ' * { font-family: Consolas, sans-serif; }',
  84. _this.selector, ' + .', extClassName, ' .', tools.lengthOverClass, ' { color: #FF5722; }',
  85. _this.selector, ' + .', extClassName, ' .', tools.countClass, ', ', _this.selector, ' + .', extClassName, ' .', tools.maxClass, ' { display: inline-block; min-width: 26px; height: 16px; line-height: 18px; }',
  86. _this.selector, ' + .', extClassName, ' > .layui-badge { overflow: hidden; border-color: ', (_this.options.borderColor || '#E6E6E6'), '; background-color: ', (_this.options.bgColor || '#FFFFFF'), '; }',
  87. _this.selector, ' + .', extClassName, '-r.', extClassName, '-inner > .layui-badge { border-right: 0 none; border-radius: 15px 0 17px; margin-right: 1px; }',
  88. _this.selector, ' + .', extClassName, '-l.', extClassName, '-inner > .layui-badge { border-left: 0 none; border-radius: 0 15px 0; margin-left: 1px; }',
  89. _this.selector, ' + .', extClassName, '-inner > .layui-badge { top: -18px; border-bottom: 0 none; opacity: 0.8; }',
  90. _this.selector, ' + .', extClassName, '-inner > .layui-badge:hover { opacity: 1; }',
  91. _this.selector, ' + .', extClassName, ' .', tools.maxClass, ' { opacity: 0.9; }',
  92. _this.selector, ' + .', extClassName, '-r { text-align: right; }',
  93. _this.selector, ' + .', extClassName, '-l { text-align: left; }',
  94. _this.selector, ' + .', extClassName, '-inner { height: 0; }',
  95. '</style>'].join('');
  96. $('head link:last')[0] && $('head link:last').after(style) || $('head').append(style);
  97. };
  98. /** 初始化默认方法,处理 JS 兼容问题 */
  99. Class.prototype.initPrototype = function () {
  100. // 获取数组元素下标
  101. !Array.prototype.indexOf && (Array.prototype.indexOf = function (array, value) {
  102. array = array || [];
  103. for (var i = array.length; i--;) {
  104. if (array[i] == value) {
  105. return i;
  106. }
  107. }
  108. return -1;
  109. });
  110. };
  111. /** 添加文本工具 */
  112. Class.prototype.addTextool = function (_this, $target) {
  113. var $extPane = $target.next('.' + extClassName);
  114. // 若已存在,则移除元素,支持重复渲染
  115. $extPane && $extPane.length && $extPane.remove();
  116. // 添加元素
  117. $target.after(_this.getToolsNode(_this, $target));
  118. $extPane = $target.next('.' + extClassName);
  119. _this.setEvent(_this, $target, $extPane);
  120. $extPane.fadeIn(200, function() {
  121. !_this.options.initShow && $extPane.find('.' + tools.hideClass).trigger('click');
  122. });
  123. };
  124. /** 复制文本 */
  125. Class.prototype.copyText = function (_this, $target) {
  126. if (!$target) {
  127. return false;
  128. }
  129. !$('#' + tools.copyTextId).length && $('body').append('<textarea id=' + tools.copyTextId + ' readonly="readonly"></textarea>');
  130. var $copy = $('#' + tools.copyTextId), value = $target.val();
  131. $copy.val(value === '' ? ' ' : value).select();
  132. document.execCommand('copy');
  133. _this.showTip(_this, $target, '已复制!');
  134. };
  135. /** 设置内容长度 */
  136. Class.prototype.setValLength = function (_this, $target) {
  137. var $length = $target.next('.' + extClassName).find('.' + tools.countClass);
  138. $length.text($target.val().length);
  139. if ($target.val().length > $target.attr('maxlength')) {
  140. $length.addClass(tools.lengthOverClass);
  141. } else if ($length.hasClass(tools.lengthOverClass)) {
  142. $length.removeClass(tools.lengthOverClass);
  143. }
  144. }
  145. /** 设置工具条事件 */
  146. Class.prototype.setEvent = function (_this, $target, $extPane) {
  147. _this.setValLength(_this, $target);
  148. var initValue = $target.val();
  149. // 文本工具条按钮点击事件
  150. $extPane.on('click', 'a', function (e) {
  151. var $this = $(this), $icon = $this.children('i.layui-icon');
  152. if ($this.hasClass(tools.hideClass)) {
  153. // 收起展开按钮事件
  154. $this.nextAll().toggle('fast');
  155. $this.prevAll().toggle('fast');
  156. if ($icon.hasClass('layui-icon-more')) {
  157. $icon.removeClass('layui-icon-more').addClass('layui-icon-more-vertical');
  158. $this.attr(_this.tipsAttr, '展开');
  159. typeof _this.options.hideEnd === 'function' && _this.options.hideEnd($target, $extPane);
  160. } else {
  161. $icon.removeClass('layui-icon-more-vertical').addClass('layui-icon-more');
  162. $this.attr(_this.tipsAttr, '收起');
  163. typeof _this.options.showEnd === 'function' && _this.options.showEnd($target, $extPane);
  164. }
  165. _this.tipsAttr === tools.laytips && _this.showTip(_this, $this, $this.attr(_this.tipsAttr));
  166. }
  167. if ($this.hasClass(tools.copyClass)) {
  168. // 复制按钮事件
  169. _this.copyText(_this, $target);
  170. }
  171. if ($this.hasClass(tools.resetClass)) {
  172. // 重置按钮事件
  173. $target.val(initValue);
  174. _this.setValLength(_this, $target);
  175. }
  176. if ($this.hasClass(tools.clearClass)) {
  177. // 清空按钮事件
  178. $target.val('');
  179. _this.setValLength(_this, $target);
  180. }
  181. layui.stope(e);
  182. return false;
  183. });
  184. // 字数统计事件
  185. $target.on('keyup input', function (e) {
  186. _this.setValLength(_this, $target);
  187. layui.stope(e);
  188. return false;
  189. });
  190. };
  191. /** 获取工具条节点 */
  192. Class.prototype.getToolsNode = function (_this, $target) {
  193. if (!$target) return false;
  194. // 总是显示收起展开按钮
  195. tools.hide = ['<a href="javascript:;"', _this.getTips(_this, '收起'), 'class="', tools.hideClass, '"><i class="layui-icon layui-icon-more"></i></a>'].join('');
  196. // 至少显示一个工具模块
  197. _this.options.tools = _this.options.tools || [tools.countName];
  198. // 字数统计
  199. tools.countIndex = _this.options.tools.indexOf(tools.countName);
  200. if (tools.countIndex > -1) {
  201. var maxlength = $target.attr('maxlength') || -1;
  202. tools.count = ['<span class="', tools.lengthClass, '"><b class="', tools.countClass, '"', _this.getTips(_this, '当前字数'), '>0</b>',
  203. (maxlength < 0 ? '' : ['/<span class="', tools.maxClass, '"', _this.getTips(_this, '最大字数'), '>', maxlength, '</span>'].join('')), '</span>'].join('');
  204. }
  205. // 复制内容
  206. tools.copyIndex = _this.options.tools.indexOf(tools.copyName);
  207. if (tools.copyIndex > -1) {
  208. tools.copy = ['<a href="javascript:;" class="', tools.copyClass, '"', _this.getTips(_this, '复制'),
  209. '><i class="layui-icon layui-icon-file"></i></a>'].join('');
  210. }
  211. // 重置内容
  212. tools.resetIndex = _this.options.tools.indexOf(tools.resetName);
  213. if (tools.resetIndex > -1) {
  214. tools.reset = ['<a href="javascript:;" class="', tools.resetClass, '"', _this.getTips(_this, '重置'),
  215. '><i class="layui-icon layui-icon-refresh-1"></i></a>'].join('');
  216. }
  217. // 清空内容
  218. tools.clearIndex = _this.options.tools.indexOf(tools.clearName);
  219. if (tools.clearIndex > -1) {
  220. tools.clear = ['<a href="javascript:;" class="', tools.clearClass, '"', _this.getTips(_this, '清空'),
  221. '><i class="layui-icon layui-icon-close"></i></a>'].join('');
  222. }
  223. if (_this.options.align === 'left') {
  224. // 居左对齐
  225. alignRight = false;
  226. alignClass = extClassName + '-l';
  227. } else {
  228. // 居右对其
  229. alignRight = true;
  230. alignClass = extClassName + '-r';
  231. }
  232. // 处理工具条节点
  233. nodes = ['<span class="layui-unselect ', extClassName, ' ', alignClass, ' ', (_this.options.inner ? extClassName + '-inner ' : ''),
  234. $.trim(_this.options.className), ' layui-anim layui-anim-fadein"><span class="layui-badge layui-badge-rim">'];
  235. !alignRight && nodes.push(tools.hide);
  236. for (var i = 0; i < _this.options.tools.length; i++) {
  237. nodes.push(tools[_this.options.tools[i]] || '');
  238. }
  239. alignRight && nodes.push(tools.hide);
  240. nodes.push('</span></span>');
  241. return nodes.join('');
  242. };
  243. /** 获取提示信息属性 */
  244. Class.prototype.getTips = function (_this, msg) {
  245. switch (_this.options.tipType) {
  246. case 'title':
  247. _this.tipsAttr = 'title';
  248. break;
  249. case 'laytips':
  250. _this.tipsAttr = tools.laytips;
  251. break;
  252. default:
  253. return '';
  254. }
  255. return [' ', _this.tipsAttr, '=', msg, ' '].join('');
  256. };
  257. /** 初始化吸附提示 */
  258. Class.prototype.initTips = function (_this) {
  259. $('[' + tools.laytips + ']').each(function (i, n) {
  260. var $target = $(n);
  261. if ($.trim($target.attr(_this.tipsAttr)) !== '') {
  262. $target.hover(function () {
  263. _this.showTip(_this, $target, $target.attr(_this.tipsAttr));
  264. }, _this.hideTip);
  265. }
  266. });
  267. };
  268. /** 显示吸附提示 */
  269. Class.prototype.showTip = function (_this, $target, msg) {
  270. _this.hideTip();
  271. layui.layer.tips(msg, $target, { tips: [1, _this.options.tipColor || '#01AAED'], time: 2e3, anim: 5, zIndex: (_this.options.zIndex || 0) + 2 });
  272. };
  273. /** 隐藏吸附提示 */
  274. Class.prototype.hideTip = function () {
  275. layui.layer.closeAll('tips');
  276. };
  277. exports('textool', {
  278. /** 初始化入口方法 */
  279. init: function (custom) {
  280. return new Class(custom);
  281. }
  282. });
  283. });