selectpage.js 98 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485
  1. /**
  2. * @summary SelectPage
  3. * @desc 基于jQuery及使用Bootstrap环境开发的,下拉列表带输入快速查找及结果分页展示的多功能选择器
  4. * @file selectpage.js
  5. * @version 2.7
  6. * @author TerryZeng
  7. * @contact https://terryz.github.io/
  8. * @license MIT License
  9. *
  10. * 插件的部分功能使用、借鉴了
  11. * jQuery Plugin: jquery.ajax-combobox
  12. * 作者:Yuusaku Miyazaki <toumin.m7@gmail.com>(宮崎 雄策)
  13. *
  14. * 插件依赖:
  15. * jQuery1.x
  16. * font-awesome(图标库)
  17. *
  18. * 基本功能:
  19. * 可实时搜索的下拉列表
  20. * 对待选择的下拉项目可进行分页
  21. * 可使用键盘快捷分页操作
  22. * 使用标签的方式支持下拉项目多选
  23. *
  24. * 修改记录:
  25. * 2016.04.20
  26. * 增加参数autoSelectFirst(是否自动选择列表中的第一项内容)
  27. * 解决下拉分页有初始化内容,并删除部分关键字时显示的结果集列表不足一页时,分页栏没有被生成的问题
  28. * 增加参数autoFillResult(是否自动填充内容)
  29. * 增加参数noResultClean(是否清空无匹配结果的输入关键字)
  30. * 2016.06.29
  31. * 修复分页栏鼠标点击时跳转的页数不正常的问题
  32. * 2016.08.04
  33. * 修复因宽度变化导致下拉触发按钮位置在出现下拉列表后发生偏移,原因是原控件的触发按钮是在输入框外部扩展,现已移入输入框内部
  34. * 2016.08.10
  35. * 弹出下拉列表时,若有已选中的项目,则将已选中的项目进行高亮,否则对第一行进行高亮
  36. * 下拉列表展开时,鼠标点击列表区域外,若当前列表已有选中项目,则直接隐藏列表;若当前列表没有默认选中项目,则使用当前高亮项目的内容进行设置
  37. * 2016.08.12
  38. * 解决控件对于原始input设置的样式(bootstrap原生提供的宽度样式),宽度显示不正常的问题
  39. * 增加若设置了formatItem格式函数,则进行匹配的数据源从showField改为formatItem后的结果进行匹配
  40. * 2016.10
  41. * 增加光标进入输入框时,打开下拉列表的功能
  42. * 2017.01.16
  43. * 解决输入关键进行搜索并有匹配结果时,失去焦点后,没有自动选中第一项
  44. * 2017.01.19
  45. * 取消在输入状态时,判断到输入框里内容为空时,隐藏下拉列表的操作
  46. * 在展示下拉列表时,判断默认与输入框左对齐的列表是否会超出屏幕边界,是则右对齐,否则默认左对齐
  47. * 2017.01.20
  48. * 增加下拉列表展示之前判断列表的面板是否会超出底部区域,若超出则将列表向上对齐展示
  49. * 处理下拉列表显示一次操作显示多次的问题
  50. * 增加控件已有选中值时,在显示下拉列表时,直接跳转到该项目所在的页
  51. * 修复一些点击输入框出现下拉列表的Bug
  52. * 2017.03.24
  53. * 解决下拉分页插件向上浮动时位置不正确的问题
  54. * 解决下拉分页插件在已有选中值时,再次点击输入框后,打开的列表分页栏翻页功能无效的问题
  55. * 2017.04.21
  56. * 解决打开noResultClean参数,没有匹配输入关键词的项目时,列表不隐藏的问题
  57. * 2017.04.24
  58. * 解决控件在设置disabled="disabled"禁用状态时,点击向下的三角尖也可以打开列表的问题
  59. * 增加控件在已有选中项目时,直接删除输入框中的内容,作为清空控件内容的功能
  60. * 修改失去焦点的范围从document.body到document
  61. * 优化键盘输入捕捉的方式
  62. * 原列表有多页的情况下,再输入关键字,没有匹配到任何项目时,分页条的下一页,最后一页为可点击的样式,且分页信息的内容也不正确
  63. * 2017.05
  64. * 代码重构
  65. * 增加多项选择东西,并以标签(Tag)的形式展现在输入框中
  66. * 修正插件外框宽度问题
  67. * 修改选中事件回调的入参从key,value改为选中行的原始数据对象,以便更灵活的数据处理
  68. * 2017.06
  69. * 修复部分样式问题
  70. * 2017.06.13
  71. * 改名SelectPage
  72. * 2017.06.24
  73. * 增加Bootstrap3样式支持
  74. * 2017.08.08
  75. * 修复界面上排版内容较少时,列表会向上展示开的问题
  76. * 增加多选模式下的控制按钮区域,功能:“全选本页”、“取消本页”、“清空全部”
  77. * 修复最大宽度下超出父容器的宽度问题
  78. * 修复ajax模式报错的问题
  79. * 增加eAjaxSuccess请求成功后的数据处理回调
  80. * 2017.08.13(v2.0)
  81. * 代码重构
  82. * 修改默认样式,使用更简洁的风格
  83. * 增加maxSelectLimit参数,设置多选模式下最大选择个数限制
  84. * 增加eTagRemove回调函数,在多选模式下,移除标签时触发的回调
  85. * 优化错误信息展示的交互方式
  86. * 增加初始化选中项目时(多选模式),允许设置多个内容,例如:data-init="1,2,3,4"
  87. * 修复键盘操作分页部分情况下会失效的问题
  88. * 增加selectToCloseList参数,用于设置在多选模式下,选择项目后不关闭列表
  89. * 修复selectToCloseList:false状态下,键盘操作会失去焦点,操作不连贯的问题
  90. * 增加$.fn.selectPageClear的API,用于清空控件所有已选中的项目
  91. * 增加$.fn.selectPageText的API,用于获得已选择的项目文本内容
  92. * 增加$.fn.selectPageData的API,用于动态修改插件数据源
  93. * 增加$.fn.SelectedRefresh的API,用于在使用.val()的方式修改了插件的选中项目后,刷新显示在输入框中的文本内容
  94. * 优化控件内部对象缓存机制
  95. * 去除快速使用脚本b.selectpage.js
  96. * 初始化入口从原来的$('').bSelectPage({})修改为$('').selectPage({})
  97. * 重新调整参数名称
  98. * 修正Bootstrap3下控件宽度、高度应用的BUG
  99. * 2017.08.19(v2.2)
  100. * 增加为原始输入框的value属性设置初始化值,以初始化插件选中项目
  101. * 修复多选模式下关闭标签出错的问题
  102. * 修复输入查询关键字后失去焦点,再次获得焦点时,插件没有根据已存在的关键进行过滤
  103. * 增加inputDelay配置项目,设置ajax数据源模式下,延迟输入查询的时间,避免在单位时间内连续输入发起的连续ajax查询,单位:秒,默认值:0.5
  104. * 修正对数字类型的列进行排序时,仍然以字符串的方式进行排序
  105. * 2017.08.23(v2.3)
  106. * 修复在查询关键字状态下,分页数据没有被更新,导致分页按钮功能不正常问题
  107. * 清理整理内部对象
  108. * 修复多选模式下,若设置了最大选中项目个数,点击“全选本页”按钮时,仅选中指定的最大数量
  109. * 增加selectpage.base.css兼容无UI框架的方案,但建议要至少使用normalize.css
  110. * 2017.08.26(v2.4)
  111. * 增加pagination参数,指定稿件是否使用分页加载数据,以及显示分页栏
  112. * 增加listSize参数,指定了不使用分页的列表,显示的高度,单位为个(选项个数),默认显示10个项目的高度
  113. * 设置selectOnly:true的情况下,输入框为只读模式,不允许输入查询过滤
  114. * 修复多选模式下及设置了最大选中项目时,选中了项目再次点击“全选本页”按钮会在已选择的基础上增加最大选中项目个数的项目
  115. * 调整下拉列表样式及位置
  116. * 增加单选模式下,选中项目后,自动显示清空按钮
  117. * 修复多选模式下,移除本页和清除所有两个按钮点击后,回调出错的问题
  118. * 增加搜索无结果时显示提示信息
  119. * 2017.09.07(v2.5)
  120. * 修复多选模式下,初始化项目的显示文本没有使用formatItem回调进行格式化
  121. * 修复ajax数据源模式下,输入查询关键字时,翻页始终为第一页的问题
  122. * 2017.09.07(v2.6)
  123. * 修复单选模式下初始化项目的显示文本没有使用formatItem回调格式化的问题
  124. * 修复单选模式存在初始化项目时,再打开下拉列表时,仅显示匹配的项目一条数据的问题
  125. * 修复多选模式下,动态修改选中值selectPageRefresh功能无效
  126. * 2017.09.12(v2.7)
  127. * 增加eClear回调,单选模式下,清除按钮的功能回调
  128. * 单选,多选模式下,输入框禁用或只读状态,不显示清除按钮
  129. * 2017.09.23(v2.8)
  130. * 调整部分样式
  131. * 修复可视区域高度较小时,列表始终会向上展开的问题
  132. * 分离键盘事件处理,对键盘输入精准控制
  133. * 优化区域外点击处理
  134. * 优化数据展示渲染效率
  135. * 优化列表位置定位的准确性
  136. */
  137. ;
  138. (function (factory) {
  139. if (typeof define === "function" && define.amd) {
  140. // AMD模式
  141. define(["jquery"], factory);
  142. } else {
  143. // 全局模式
  144. factory(jQuery);
  145. }
  146. }(function ($) {
  147. "use strict";
  148. /**
  149. * @desc 默认参数集
  150. */
  151. var defaults = {
  152. /**
  153. * @desc 数据源(String:Ajax查询的URL|Object:JSON格式的数据源)
  154. * @type {string|Object}
  155. * @example
  156. * string:服务端请求的URL地址
  157. * Object:JSON格式数组,推荐格式:[{a:1,b:2,c:3},{...}]
  158. */
  159. data: undefined,
  160. /**
  161. * @desc 插件显示语言 ('ja', 'en', 'es', 'pt-br'等)
  162. * @type string 默认'cn'
  163. */
  164. lang: 'cn',
  165. /**
  166. * @desc 是否为多选模式(标签模式)
  167. * @type boolean 默认值false
  168. */
  169. multiple: false,
  170. /**
  171. * @desc 是否分页
  172. * @type boolean 默认值 true
  173. */
  174. pagination: true,
  175. /**
  176. * @desc 关闭分页的状态下,列表显示的项目个数,其它的项目以滚动条滚动方式展现
  177. * @type number 默认值 10
  178. */
  179. listSize: 10,
  180. /**
  181. * @desc 是否启用多选模式的控制按钮区域
  182. * 仅multiple: true模式下可用
  183. * @type boolean 默认值true
  184. */
  185. multipleControlbar: true,
  186. /**
  187. * @desc 多选模式下最大选择个数,0为不限制
  188. * @type number 默认0
  189. */
  190. maxSelectLimit: 0,
  191. /**
  192. * @desc 选中项目后关闭列表
  193. * 该设置仅在多选模式下multiple:true有效
  194. * @type boolean 默认值true
  195. */
  196. selectToCloseList: true,
  197. /**
  198. * @desc 插件初始值指定,该值会与option.keyField字段进行匹配,若匹配到,则自动设置选中并高亮
  199. * @type string
  200. */
  201. initRecord: undefined,
  202. /**
  203. * @desc 使用ajax方式获取数据时,使用该参数设置对应的数据表名
  204. * @type string
  205. */
  206. dbTable: 'tbl',
  207. /**
  208. * @desc 值字段,通常该字段的内容会自动保存在隐藏域中
  209. * @type string 默认值为'id'
  210. */
  211. keyField: 'id',
  212. /**
  213. * @desc 结果集中用于显示的属性名
  214. * @type string 默认字段为'name'
  215. */
  216. showField: 'name',
  217. /**
  218. * @desc 查询字段,仅为使用URL(ajax)方式查询服务端时,设置后端查询的字段,不设置则默认使用showField设置的字段
  219. * @type string
  220. */
  221. searchField: undefined,
  222. /**
  223. * @desc 查询方式 ('AND' or 'OR')
  224. * @type string 默认为'AND'
  225. */
  226. andOr: 'AND',
  227. /**
  228. * @desc 数据排序方式
  229. * @type array 若不设置则默认对showField指定的字段进行排序
  230. * @example
  231. * orderBy : ['id desc']//对ID字段进行降序排序
  232. */
  233. orderBy: undefined,
  234. /**
  235. * @desc 每页显示的记录数
  236. * @type number
  237. */
  238. pageSize: 10,
  239. /**
  240. * @desc 使用URL进行AJAX查询时,可传递查询参数
  241. * @type function
  242. * @return object
  243. * @example params : function(){return {'name':'aa','sex':1};}
  244. */
  245. params: undefined,
  246. /**
  247. * 列表项目显示内容格式化
  248. * 参数类型:function
  249. * @type boolean
  250. * @param data {object} 行数据object格式
  251. * @return string
  252. */
  253. formatItem: undefined,
  254. /**
  255. * 是否在输入框获得焦点时,展开下拉窗口
  256. * @type boolean 默认值true
  257. */
  258. focusDropList: true,
  259. /**
  260. * 是否自动选择列表中的第一项内容(输入关键字查询模式,直接使用鼠标下拉并不触发)
  261. * @type boolean 默认值false
  262. */
  263. autoSelectFirst: true,
  264. /**
  265. * 是否自动填充内容
  266. * 若有列表项目被高亮显示,在焦点离开控件后,自动设置该项为选中内容
  267. * @type boolean 默认值false
  268. */
  269. autoFillResult: true,
  270. /**
  271. * 是否清空输入关键字
  272. * 在输入框中输入内容进行查询,但没有匹配的内容返回,在焦点离开控件后,自动清空输入框输入的内容
  273. * @type boolean 默认值false
  274. */
  275. noResultClean: true,
  276. /**
  277. * @desc 只选择模式
  278. * @type boolean
  279. */
  280. selectOnly: false,
  281. /**
  282. * @desc 输入关键字查询延迟(仅ajax数据源模式下可用)
  283. * @type number 默认值:0.5秒
  284. */
  285. inputDelay: 0.5,
  286. /**
  287. * -----------------------------------------事件回调--------------------------------------------
  288. */
  289. /**
  290. * @type function
  291. * @param object
  292. * @param dom
  293. */
  294. eSelect: undefined,
  295. /**
  296. * ajax请求模式,请求成功后的数据处理回调
  297. * 回调的功能用于自定义处理服务端返回的数据
  298. * @type function
  299. * @param data {object} ajax服务端返回的json数据
  300. * @return {object} 函数返回的数据结构如下:
  301. * @example
  302. * {
  303. * list : [{name:'aa',sex:1},{name:'bb',sex:1}...],
  304. * totalRow : 100
  305. * }
  306. */
  307. eAjaxSuccess: undefined,
  308. /**
  309. * 多选模式下,关闭标签是的回调函数
  310. * @type function
  311. * @param removeCount 被移除的个数
  312. */
  313. eTagRemove: undefined,
  314. /**
  315. * 单选模式下,选中项目后的清除按钮功能回调
  316. * @type function
  317. */
  318. eClear: undefined
  319. };
  320. /**
  321. * @constructor
  322. * @classdesc 插件初始化
  323. * @param {Object} input - 插件的初始化输入框元素。
  324. * @param {Object} option - 初始化参数
  325. */
  326. var SelectPage = function (input, option) {
  327. var option = $.extend({}, option, $(input).data());
  328. //特殊字段处理
  329. $.each({data: 'source', keyField: 'primaryKey', showField: 'field', pageSize: 'perPage'}, function (i, j) {
  330. if (typeof option[j] !== 'undefined') {
  331. option[i] = option[j];
  332. delete option[j];
  333. }
  334. });
  335. this.setOption(option);
  336. this.setLanguage();
  337. this.setCssClass();
  338. this.setProp();
  339. this.setElem(input, option);
  340. this.setButtonAttrDefault();
  341. this.setInitRecord();
  342. this.eDropdownButton();
  343. this.eInput();
  344. this.eWhole();
  345. };
  346. /**
  347. * 插件版本号
  348. */
  349. SelectPage.version = '2.7';
  350. /**
  351. * 插件缓存内部对象的KEY
  352. */
  353. SelectPage.dataKey = 'selectPageObject';
  354. /**
  355. * 全局范围设置当前点击是否为插件自身的标识
  356. */
  357. SelectPage.objStatusKey = 'selectPage-self-mark';
  358. /**
  359. * 全局范围设置当前点击的selectpage的索引下标
  360. */
  361. SelectPage.objStatusIndex = 'selectPage-self-index';
  362. /**
  363. * @desc 参数初始化
  364. * @param {Object} option - 参数集
  365. */
  366. SelectPage.prototype.setOption = function (option) {
  367. //若没有设置搜索字段,则使用显示字段作为搜索字段
  368. option.searchField = (option.searchField === undefined) ? option.showField : option.searchField;
  369. //统一大写
  370. option.andOr = option.andOr.toUpperCase();
  371. if (option.andOr !== 'AND' && option.andOr !== 'OR')
  372. option.andOr = 'AND';
  373. //将参数内容从使用","分隔的字符串转换为数组
  374. var arr = ['searchField'];
  375. for (var i = 0; i < arr.length; i++) {
  376. option[arr[i]] = this.strToArray(option[arr[i]]);
  377. }
  378. //设置排序字段
  379. option.orderBy = (option.orderBy === undefined) ? option.searchField : option.orderBy;
  380. //设置多字段排序
  381. //例: [ ['id', 'ASC'], ['name', 'DESC'] ]
  382. option.orderBy = this.setOrderbyOption(option.orderBy, option.showField);
  383. //多选模式下,若设置了选择项目不关闭列表功能,则强制关闭自动选择第一项功能和自动选中高亮的项目功能
  384. //原因是打开了会总是莫明选择了第一项,体验不佳
  385. if (option.multiple && !option.selectToCloseList) {
  386. option.autoFillResult = false;
  387. option.autoSelectFirst = false;
  388. }
  389. if ($.type(option.data) === 'string') {
  390. option.autoSelectFirst = false;
  391. }
  392. //若不需要分页功能,则将所有数据都显示出来,上限200项
  393. if (!option.pagination)
  394. option.pageSize = 200;
  395. if ($.type(option.listSize) !== 'number' || option.listSize < 0)
  396. option.listSize = 10;
  397. this.option = option;
  398. };
  399. /**
  400. * @desc 字符串转换为数组
  401. * @param str {string} - 字符串
  402. * @return {Array} - 数组
  403. */
  404. SelectPage.prototype.strToArray = function (str) {
  405. if (!str)
  406. return '';
  407. return str.replace(/[\s ]+/g, '').split(',');
  408. };
  409. /**
  410. * @desc 设置多字段排序
  411. * @param {Array} arg_order - 排序顺序
  412. * @param {string} arg_field - 字段
  413. * @return {Array} - 处理后的排序字段内容
  414. */
  415. SelectPage.prototype.setOrderbyOption = function (arg_order, arg_field) {
  416. var arr = [], orders = [];
  417. if (typeof arg_order == 'object') {
  418. for (var i = 0; i < arg_order.length; i++) {
  419. orders = $.trim(arg_order[i]).split(' ');
  420. arr[i] = (orders.length == 2) ? orders : [orders[0], 'ASC'];
  421. }
  422. } else {
  423. orders = $.trim(arg_order).split(' ');
  424. arr[0] = (orders.length == 2) ? orders : (orders[0].match(/^(ASC|DESC)$/i)) ? [arg_field, orders[0]] : [orders[0], 'ASC'];
  425. }
  426. return arr;
  427. };
  428. /**
  429. * @desc 界面文字国际化
  430. */
  431. SelectPage.prototype.setLanguage = function () {
  432. var message;
  433. switch (this.option.lang) {
  434. // German
  435. case 'de':
  436. message = {
  437. add_btn: 'Hinzufügen-Button',
  438. add_title: 'Box hinzufügen',
  439. del_btn: 'Löschen-Button',
  440. del_title: 'Box löschen',
  441. next: 'Nächsten',
  442. next_title: 'Nächsten' + this.option.pageSize + ' (Pfeil-rechts)',
  443. prev: 'Vorherigen',
  444. prev_title: 'Vorherigen' + this.option.pageSize + ' (Pfeil-links)',
  445. first_title: 'Ersten (Umschalt + Pfeil-links)',
  446. last_title: 'Letzten (Umschalt + Pfeil-rechts)',
  447. get_all_btn: 'alle (Pfeil-runter)',
  448. get_all_alt: '(Button)',
  449. close_btn: 'Schließen (Tab)',
  450. close_alt: '(Button)',
  451. loading: 'lade...',
  452. loading_alt: '(lade)',
  453. page_info: 'num_page_top - num_page_end von cnt_whole',
  454. select_ng: 'Achtung: Bitte wählen Sie aus der Liste aus.',
  455. select_ok: 'OK : Richtig ausgewählt.',
  456. not_found: 'nicht gefunden',
  457. ajax_error: 'Bei der Verbindung zum Server ist ein Fehler aufgetreten.'
  458. };
  459. break;
  460. // English
  461. case 'en':
  462. message = {
  463. add_btn: 'Add button',
  464. add_title: 'add a box',
  465. del_btn: 'Del button',
  466. del_title: 'delete a box',
  467. next: 'Next',
  468. next_title: 'Next' + this.option.pageSize + ' (Right key)',
  469. prev: 'Prev',
  470. prev_title: 'Prev' + this.option.pageSize + ' (Left key)',
  471. first_title: 'First (Shift + Left key)',
  472. last_title: 'Last (Shift + Right key)',
  473. get_all_btn: 'Get All (Down key)',
  474. get_all_alt: '(button)',
  475. close_btn: 'Close (Tab key)',
  476. close_alt: '(button)',
  477. loading: 'loading...',
  478. loading_alt: '(loading)',
  479. page_info: 'num_page_top - num_page_end of cnt_whole',
  480. select_ng: 'Attention : Please choose from among the list.',
  481. select_ok: 'OK : Correctly selected.',
  482. not_found: 'not found',
  483. ajax_error: 'An error occurred while connecting to server.'
  484. };
  485. break;
  486. // 中文
  487. case 'cn':
  488. message = {
  489. add_btn: '添加按钮',
  490. add_title: '添加区域',
  491. del_btn: '删除按钮',
  492. del_title: '删除区域',
  493. next: '下一页',
  494. next_title: '下' + this.option.pageSize + ' (→)',
  495. prev: '上一页',
  496. prev_title: '上' + this.option.pageSize + ' (←)',
  497. first_title: '首页 (Shift + ←)',
  498. last_title: '尾页 (Shift + →)',
  499. get_all_btn: '获得全部 (↓)',
  500. get_all_alt: '(按钮)',
  501. close_btn: '关闭 (Tab键)',
  502. close_alt: '(按钮)',
  503. loading: '读取中...',
  504. loading_alt: '(读取中)',
  505. page_info: 'num_page_top - num_page_end (共 cnt_whole)',
  506. select_ng: '请注意:请从列表中选择.',
  507. select_ok: 'OK : 已经选择.',
  508. not_found: '无查询结果',
  509. ajax_error: '连接到服务器时发生错误!'
  510. };
  511. break;
  512. // Spanish
  513. case 'es':
  514. message = {
  515. add_btn: 'Agregar boton',
  516. add_title: 'Agregar una opcion',
  517. del_btn: 'Borrar boton',
  518. del_title: 'Borrar una opcion',
  519. next: 'Siguiente',
  520. next_title: 'Proximas ' + this.option.pageSize + ' (tecla derecha)',
  521. prev: 'Anterior',
  522. prev_title: 'Anteriores ' + this.option.pageSize + ' (tecla izquierda)',
  523. first_title: 'Primera (Shift + Left)',
  524. last_title: 'Ultima (Shift + Right)',
  525. get_all_btn: 'Ver todos (tecla abajo)',
  526. get_all_alt: '(boton)',
  527. close_btn: 'Cerrar (tecla TAB)',
  528. close_alt: '(boton)',
  529. loading: 'Cargando...',
  530. loading_alt: '(Cargando)',
  531. page_info: 'num_page_top - num_page_end de cnt_whole',
  532. select_ng: 'Atencion: Elija una opcion de la lista.',
  533. select_ok: 'OK: Correctamente seleccionado.',
  534. not_found: 'no encuentre',
  535. ajax_error: 'Un error ocurrió mientras conectando al servidor.'
  536. };
  537. break;
  538. // Brazilian Portuguese
  539. case 'pt-br':
  540. message = {
  541. add_btn: 'Adicionar botão',
  542. add_title: 'Adicionar uma caixa',
  543. del_btn: 'Apagar botão',
  544. del_title: 'Apagar uma caixa',
  545. next: 'Próxima',
  546. next_title: 'Próxima ' + this.option.pageSize + ' (tecla direita)',
  547. prev: 'Anterior',
  548. prev_title: 'Anterior ' + this.option.pageSize + ' (tecla esquerda)',
  549. first_title: 'Primeira (Shift + Left)',
  550. last_title: 'Última (Shift + Right)',
  551. get_all_btn: 'Ver todos (Seta para baixo)',
  552. get_all_alt: '(botão)',
  553. close_btn: 'Fechar (tecla TAB)',
  554. close_alt: '(botão)',
  555. loading: 'Carregando...',
  556. loading_alt: '(Carregando)',
  557. page_info: 'num_page_top - num_page_end de cnt_whole',
  558. select_ng: 'Atenção: Escolha uma opção da lista.',
  559. select_ok: 'OK: Selecionado Corretamente.',
  560. not_found: 'não encontrado',
  561. ajax_error: 'Um erro aconteceu enquanto conectando a servidor.'
  562. };
  563. break;
  564. // Japanese
  565. case 'ja':
  566. message = {
  567. add_btn: '追加ボタン',
  568. add_title: '入力ボックスを追加します',
  569. del_btn: '削除ボタン',
  570. del_title: '入力ボックスを削除します',
  571. next: '次へ',
  572. next_title: '次の' + this.option.pageSize + '件 (右キー)',
  573. prev: '前へ',
  574. prev_title: '前の' + this.option.pageSize + '件 (左キー)',
  575. first_title: '最初のページへ (Shift + 左キー)',
  576. last_title: '最後のページへ (Shift + 右キー)',
  577. get_all_btn: '全件取得 (下キー)',
  578. get_all_alt: '画像:ボタン',
  579. close_btn: '閉じる (Tabキー)',
  580. close_alt: '画像:ボタン',
  581. loading: '読み込み中...',
  582. loading_alt: '画像:読み込み中...',
  583. page_info: 'num_page_top - num_page_end 件 (全 cnt_whole 件)',
  584. select_ng: '注意 : リストの中から選択してください',
  585. select_ok: 'OK : 正しく選択されました。',
  586. not_found: '(0 件)',
  587. ajax_error: 'サーバとの通信でエラーが発生しました。'
  588. };
  589. break;
  590. }
  591. this.message = message;
  592. };
  593. /**
  594. * @desc CSS样式表名称字义
  595. */
  596. SelectPage.prototype.setCssClass = function () {
  597. var css_class = {
  598. container: 'sp_container',
  599. // SelectPage最外层DIV的打开状态
  600. container_open: 'sp_container_open',
  601. re_area: 'sp_result_area',
  602. control_box: 'sp_control_box',
  603. //标签及输入框的
  604. element_box: 'sp_element_box',
  605. // 分页导航
  606. navi: 'pagination',
  607. // 下拉结果列表
  608. results: 'sp_results',
  609. re_off: 'sp_results_off',
  610. select: 'sp_over',
  611. select_ok: 'sp_select_ok',
  612. select_ng: 'sp_select_ng',
  613. selected: 'sp_selected',
  614. input_off: 'sp_input_off',
  615. message_box: 'sp_message_box',
  616. // 多选模式的禁用状态样式
  617. disabled: 'sp_disabled',
  618. button: 'sp_button',
  619. btn_on: 'sp_btn_on',
  620. btn_out: 'sp_btn_out',
  621. input: 'sp_input',
  622. clear_btn: 'sp_clear_btn'
  623. };
  624. this.css_class = css_class;
  625. };
  626. /**
  627. * @desc 设置属性默认值
  628. */
  629. SelectPage.prototype.setProp = function () {
  630. this.prop = {
  631. //当前页
  632. current_page: 1,
  633. //总页数
  634. max_page: 1,
  635. //是否正在Ajax请求
  636. is_loading: false,
  637. xhr: false,
  638. //使用键盘进行分页
  639. key_paging: false,
  640. //使用键盘进行选择
  641. key_select: false,
  642. //上一个选择的项目值
  643. prev_value: '',
  644. //选中项目的文本内容
  645. selected_text: '',
  646. //上一次键盘输入的时间
  647. last_input_time: undefined
  648. };
  649. this.template = {
  650. tag: {
  651. content: '<li class="selected_tag" itemvalue="#item_value#">#item_text#<span class="tag_close">×</span></li>',
  652. textKey: '#item_text#',
  653. valueKey: '#item_value#'
  654. }
  655. };
  656. };
  657. /**
  658. * @desc 插件HTML结构生成
  659. * @param {Object} combo_input - 输入框源对象
  660. * @param {Object} option - 插件参数
  661. */
  662. SelectPage.prototype.setElem = function (combo_input, option) {
  663. // 1. 生成、替换DOM对象
  664. var elem = {};//本体
  665. var orgWidth = $(combo_input).outerWidth();
  666. elem.combo_input = $(combo_input).attr({'autocomplete': 'off'}).addClass(this.css_class.input).wrap('<div>');
  667. //只选择模式设置输入框为只读状态
  668. if (option.selectOnly)
  669. $(elem.combo_input).prop('readonly', true);
  670. elem.container = $(elem.combo_input).parent().addClass(this.css_class.container);
  671. if ($(elem.combo_input).prop('disabled')) {
  672. if (option.multiple)
  673. $(elem.container).addClass(this.css_class.disabled);
  674. else
  675. $(elem.combo_input).addClass(this.css_class.input_off);
  676. }
  677. //$(elem.container).width(orgWidth);
  678. elem.button = $('<div>').addClass(this.css_class.button);
  679. //bootstrap风格的向下三角箭头
  680. elem.dropdown = $('<span class="bs-caret"><span class="caret"></span></span>');
  681. //单选模式下清除的按钮X
  682. elem.clear_btn = $('<div>').append('×').addClass(this.css_class.clear_btn).attr('title', '清除内容');
  683. //多选模式下带标签显示及文本输入的组合框
  684. elem.element_box = $('<ul>').addClass(this.css_class.element_box);
  685. if (option.multiple && option.multipleControlbar)
  686. elem.control = $('<div>').addClass(this.css_class.control_box);
  687. //结果集列表
  688. elem.result_area = $('<div>').addClass(this.css_class.re_area);
  689. //列表中的分页栏pagination
  690. if (option.pagination)
  691. elem.navi = $('<ul>').addClass(this.css_class.navi).addClass("hide");
  692. elem.results = $('<ul>').addClass(this.css_class.results);
  693. /**
  694. * 将原输入框的Name交换到Hidden中,因为具体需要保存传递到后端的是ID,而非Title
  695. */
  696. var namePrefix = '_text';
  697. //将keyField的值放入"input:hidden"
  698. var input_id = ($(elem.combo_input).attr('id') !== undefined) ? $(elem.combo_input).attr('id') : $(elem.combo_input).attr('name');
  699. var input_name = ($(elem.combo_input).attr('name') !== undefined) ? $(elem.combo_input).attr('name') : 'selectPage';
  700. var hidden_name = input_name,
  701. hidden_id = input_id;
  702. // CakePHP使用的措施 例:data[search][user] -> data[search][user_primary_key]
  703. if (input_name.match(/\]$/))
  704. input_name = input_name.replace(/\]?$/, namePrefix);
  705. else
  706. input_name += namePrefix;
  707. if (input_id.match(/\]$/))
  708. input_id = input_id.replace(/\]?$/, namePrefix);
  709. else
  710. input_id += namePrefix;
  711. //将输入框的Name与Hidden的Name进行交换,使得可以将项目的具体ID被保存到后端进行处理
  712. elem.hidden = $('<input type="hidden" class="sp_hidden" />').attr({
  713. name: hidden_name,
  714. id: hidden_id
  715. }).val('');
  716. $(elem.combo_input).attr({
  717. name: input_name,
  718. id: input_id
  719. });
  720. // 2. DOM内容放置
  721. $(elem.container).append(elem.button).append(elem.result_area).append(elem.hidden);
  722. $(elem.button).append(elem.dropdown);
  723. $(elem.result_area).append(elem.results);
  724. if (option.pagination)
  725. $(elem.result_area).append(elem.navi);
  726. //多选模式下的特殊处理
  727. if (option.multiple) {
  728. if (option.multipleControlbar) {
  729. // $(elem.control).append('<button type="button" class="btn btn-default sp_select_all" ><i class="fa fa-check-square-o"></i> 全选本页</button>');
  730. $(elem.control).append('<button type="button" class="btn btn-default sp_unselect_all" ><i class="fa fa-square-o"></i> 取消本页</button>');
  731. $(elem.control).append('<button type="button" class="btn btn-default sp_clear_all" ><i class="fa fa-ban"></i> 清除全部</button>');
  732. $(elem.result_area).prepend(elem.control);
  733. }
  734. $(elem.container).addClass('sp_container_combo');
  735. $(elem.combo_input).addClass('sp_combo_input').before($(elem.element_box));
  736. var li = $('<li>').addClass('input_box');
  737. $(li).append($(elem.combo_input));
  738. $(elem.element_box).append($(li));
  739. if ($(elem.combo_input).attr('placeholder'))
  740. $(elem.combo_input).attr('placeholder_bak', $(elem.combo_input).attr('placeholder'));
  741. }
  742. this.elem = elem;
  743. };
  744. /**
  745. * @desc 将控件的部分内容设置为默认状态
  746. */
  747. SelectPage.prototype.setButtonAttrDefault = function () {
  748. /*
  749. if (this.option.selectOnly) {
  750. if ($(this.elem.combo_input).val() !== '') {
  751. if ($(this.elem.hidden).val() !== '') {
  752. //选择条件
  753. $(this.elem.combo_input).attr('title', this.message.select_ok).removeClass(this.css_class.select_ng).addClass(this.css_class.select_ok);
  754. } else {
  755. //输入方式
  756. $(this.elem.combo_input).attr('title', this.message.select_ng).removeClass(this.css_class.select_ok).addClass(this.css_class.select_ng);
  757. }
  758. } else {
  759. $(this.elem.hidden).val('');
  760. $(this.elem.combo_input).removeAttr('title').removeClass(this.css_class.select_ng);
  761. }
  762. }
  763. */
  764. $(this.elem.button).attr('title', this.message.get_all_btn);
  765. //按钮的title属性修改
  766. $(this.elem.button).attr('title', this.message.close_btn);
  767. };
  768. /**
  769. * @desc 为插件设置初始化的选中值(若有指定的话),执行第一步,数据匹配
  770. */
  771. SelectPage.prototype.setInitRecord = function (refresh) {
  772. var self = this;
  773. if ($.type($(self.elem.combo_input).data('init')) != 'undefined')
  774. self.option.initRecord = String($(self.elem.combo_input).data('init'));
  775. //若在输入框中放入了初始化值,则将它放到隐藏域中进行选中项目初始化
  776. //若输入框设置了初始值,同时又设置了data-init属性,那么以data-init属性为优先选择
  777. if (!self.option.initRecord)
  778. if ($(self.elem.combo_input).val())
  779. self.option.initRecord = $(self.elem.combo_input).val();
  780. $(self.elem.combo_input).val('');
  781. if ((refresh && $(self.elem.hidden).val()) || $.type(self.option.initRecord) === 'string') {
  782. // 初始的KEY值放入隐藏域
  783. if (!refresh)
  784. $(self.elem.hidden).val(self.option.initRecord);
  785. //将初始值放入控件
  786. if (typeof self.option.data === 'object') {//json数据源模式
  787. var data = new Array();
  788. var keyarr = refresh ? $(self.elem.hidden).val().split(',') : self.option.initRecord.split(',');
  789. $.each(keyarr, function (index, row) {
  790. for (var i = 0; i < self.option.data.length; i++) {
  791. if (self.option.data[i][self.option.keyField] == row) {
  792. data.push(self.option.data[i]);
  793. break;
  794. }
  795. }
  796. });
  797. //在单选模式下,若使用了多选模式的初始化值(“key1,key2,...”多选方式),则不进行初始化选中操作
  798. if (!self.option.multiple && data.length > 1)
  799. data = null;
  800. self.afterInit(self, data);
  801. } else {//ajax数据源模式
  802. $.ajax({
  803. dataType: 'json',
  804. type: 'POST',
  805. url: self.option.data,
  806. data: {
  807. searchTable: self.option.dbTable,
  808. searchKey: self.option.keyField,
  809. searchValue: refresh ? $(self.elem.hidden).val() : self.option.initRecord,
  810. field: self.option.showField,
  811. order_by: self.option.orderBy,
  812. pkey_name: self.option.keyField,
  813. pkey_value: refresh ? $(self.elem.hidden).val() : self.option.initRecord
  814. },
  815. success: function (returnData) {
  816. var data;
  817. if (self.option.eAjaxSuccess && $.isFunction(self.option.eAjaxSuccess)) {
  818. data = self.option.eAjaxSuccess(returnData);
  819. } else {
  820. data = returnData;
  821. }
  822. self.afterInit(self, data.list);
  823. },
  824. error: function (jqXHR, textStatus, errorThrown) {
  825. self.ajaxErrorNotify(self, errorThrown);
  826. }
  827. });
  828. }
  829. }
  830. };
  831. /**
  832. * @desc 匹配后的数据在插件中进行展示
  833. * @param {Object} self - 插件的内部对象
  834. * @param {Object} data - 列表数据
  835. */
  836. SelectPage.prototype.afterInit = function (self, data) {
  837. if (!data)
  838. return;
  839. if (!$.isArray(data))
  840. data = [data];
  841. if (data.length === 0)
  842. return;
  843. var getText = function (row) {
  844. var text = row[self.option.showField];
  845. if (self.option.formatItem && $.isFunction(self.option.formatItem)) {
  846. try {
  847. text = self.option.formatItem(row);
  848. } catch (e) {
  849. }
  850. }
  851. return text;
  852. };
  853. if (self.option.multiple) {//多选模式初始化
  854. self.clearAll(self);
  855. $.each(data, function (i, row) {
  856. var item = {text: getText(row), value: row[self.option.keyField]};
  857. if (!self.isAlreadySelected(self, item))
  858. self.addNewTag(self, item);
  859. });
  860. self.tagValuesSet(self);
  861. self.inputResize(self);
  862. } else {//单选模式初始化
  863. var row = data[0];
  864. var initval = self.option.init ? self.option.init : $(self.elem.hidden).val();
  865. if(initval){
  866. for(var i=0; i<data.length; i++){
  867. if(initval == data[i][self.option.keyField]){
  868. row = data[i];
  869. break;
  870. }
  871. }
  872. }
  873. $(self.elem.combo_input).val(getText(row));
  874. $(self.elem.hidden).val(row[self.option.keyField]);
  875. self.prop.prev_value = getText(row);
  876. self.prop.selected_text = getText(row);
  877. if (self.option.selectOnly) {
  878. $(self.elem.combo_input).attr('title', self.message.select_ok).removeClass(self.css_class.select_ng).addClass(self.css_class.select_ok);
  879. }
  880. self.putClearButton();
  881. }
  882. };
  883. /**
  884. * @desc 下拉按钮的事件处理
  885. */
  886. SelectPage.prototype.eDropdownButton = function () {
  887. var self = this;
  888. $(self.elem.button).mouseup(function (ev) {
  889. ev.stopPropagation();
  890. if ($(self.elem.result_area).is(':hidden') && !$(self.elem.combo_input).prop('disabled')) {
  891. $(self.elem.combo_input).focus();
  892. } else
  893. self.hideResults(self);
  894. }).mouseout(); // default: mouseout
  895. };
  896. /**
  897. * @desc 输入框的事件绑定
  898. */
  899. SelectPage.prototype.eInput = function () {
  900. var self = this;
  901. var showList = function () {
  902. self.prop.page_move = false;
  903. self.suggest(self);
  904. self.setCssFocusedInput(self);
  905. };
  906. $(self.elem.combo_input).keyup(function (e) {
  907. self.processKey(self, e);
  908. }).keydown(function (e) {
  909. self.processControl(self, e);
  910. }).focus(function (e) {
  911. //增加输入框获得焦点后,显示数据列表
  912. if ($(self.elem.result_area).is(':hidden')) {
  913. e.stopPropagation();
  914. self.prop.first_show = true;
  915. showList();
  916. }
  917. });
  918. $(self.elem.container).on('click.SelectPage', 'div.' + self.css_class.clear_btn, function (e) {
  919. e.stopPropagation();
  920. self.clearAll(self);
  921. $(self.elem.clear_btn).remove();
  922. if (self.option.eClear && $.isFunction(self.option.eClear))
  923. self.option.eClear();
  924. });
  925. if (self.option.multiple) {
  926. if (self.option.multipleControlbar) {
  927. //全选本页按钮
  928. $('.sp_select_all', self.elem.control).on('click.SelectPage', function (e) {
  929. self.selectAllLine(self);
  930. });
  931. //取消全选本页按钮
  932. $('.sp_unselect_all', self.elem.control).on('click.SelectPage', function (e) {
  933. self.unselectAllLine(self);
  934. });
  935. //清除全部按钮
  936. $('.sp_clear_all', self.elem.control).on('click.SelectPage', function (e) {
  937. self.clearAll(self);
  938. });
  939. }
  940. $(self.elem.element_box).on('click.SelectPage', function (e) {
  941. var srcEl = e.target || e.srcElement;
  942. if ($(srcEl).is('ul'))
  943. $(self.elem.combo_input).focus();
  944. });
  945. //标签关闭操作
  946. //关闭同时需要将该标签的key从已保存的隐藏域中删除
  947. $(self.elem.element_box).on('click.SelectPage', 'span.tag_close', function () {
  948. var li = $(this).closest('li');
  949. self.removeTag(self, li);
  950. showList();
  951. if (self.option.eTagRemove && $.isFunction(self.option.eTagRemove))
  952. self.option.eTagRemove(1);
  953. });
  954. self.inputResize(self);
  955. }
  956. };
  957. /**
  958. * 插件之外区域的事件处理
  959. */
  960. SelectPage.prototype.eWhole = function () {
  961. var self = this;
  962. /*
  963. //如果是点击了控件本身则不响应外部鼠标点击事件
  964. $(self.elem.container).mousedown(function() {
  965. var thisindex = $('div.sp_container').index(this);
  966. var lastindex = $(document.body).data(SelectPage.objStatusIndex);
  967. if(lastindex != undefined && thisindex != lastindex)
  968. $(document.body).data(SelectPage.objStatusKey,false);
  969. else
  970. $(document.body).data(SelectPage.objStatusKey,true);
  971. $(document.body).data(SelectPage.objStatusIndex,thisindex);
  972. });
  973. */
  974. //控件外部的鼠标点击事件处理
  975. $(document).off('mousedown.selectPage').on('mousedown.selectPage', function (e) {
  976. var ele = e.target || e.srcElement;
  977. var sm = $(ele).closest('div.' + self.css_class.container);
  978. //清除内容
  979. var cleanContent = function (obj) {
  980. $(obj.elem.combo_input).val('');
  981. if (!obj.option.multiple)
  982. $(obj.elem.hidden).val('');
  983. obj.prop.selected_text = '';
  984. };
  985. //列表是打开的状态
  986. $('div.' + self.css_class.container + '.' + self.css_class.container_open).each(function () {
  987. if (this == sm[0])
  988. return;
  989. var d = $('input.' + self.css_class.input, this).data(SelectPage.dataKey);
  990. //若控件已有选中的的项目,而文本输入框中清空了关键字,则清空控件已选中的项目
  991. if (!$(d.elem.combo_input).val() && $(d.elem.hidden).val() && !d.option.multiple) {
  992. d.prop.current_page = 1;//重置当前页为1
  993. cleanContent(d);
  994. d.hideResults(d);
  995. return true;
  996. }
  997. //匹配项且高亮时,下拉分页控件失去焦点后,自动选择该项目
  998. if ($('li', $(d.elem.results)).size() > 0) {
  999. if (d.option.autoFillResult) {//打开自动内容填充功能
  1000. //若已有选中项目,则直接隐藏列表
  1001. if ($('li.sp_selected', $(d.elem.results)).size() > 0) {
  1002. d.hideResults(d);
  1003. } else if ($('li.sp_over', $(d.elem.results)).size() > 0) {
  1004. //若控件已有选中的值,则忽略高亮的项目
  1005. if ($(d.elem.hidden).val())
  1006. d.hideResults(d);
  1007. //若没有已选中的项目,且列表中有高亮项目时,选中当前高亮的行
  1008. else
  1009. d.selectCurrentLine(d, true);
  1010. } else if (d.option.autoSelectFirst) {
  1011. //若控件已有选中的值,则忽略自动选择第一项的功能
  1012. if ($(d.elem.hidden).val())
  1013. d.hideResults(d);
  1014. else {
  1015. //对于没有选中,没有高亮的情况,若插件设置了自动选中第一项时,则选中第一项
  1016. d.nextLine(d);
  1017. //self.nextLine(self);
  1018. d.selectCurrentLine(d, true);
  1019. }
  1020. } else
  1021. d.hideResults(d);
  1022. } else
  1023. d.hideResults(d);
  1024. } else {
  1025. //无匹配项目时,自动清空用户输入的关键词
  1026. if (d.option.noResultClean)
  1027. cleanContent(d);
  1028. else {
  1029. if (!d.option.multiple)
  1030. $(d.elem.hidden).val('');
  1031. }
  1032. d.hideResults(d);
  1033. }
  1034. });
  1035. /*
  1036. if ($(document.body).data(SelectPage.objStatusKey)) $(document.body).data(SelectPage.objStatusKey,false);
  1037. else {
  1038. //清除内容
  1039. var cleanContent = function(obj){
  1040. $(obj.elem.combo_input).val('');
  1041. if(!obj.option.multiple) $(obj.elem.hidden).val('');
  1042. obj.prop.selected_text = '';
  1043. };
  1044. //列表是打开的状态
  1045. $('div.' + self.css_class.container + '.' + self.css_class.container_open).each(function(){
  1046. var d = $('input.'+self.css_class.input,this).data(SelectPage.dataKey);
  1047. //若控件已有选中的的项目,而文本输入框中清空了关键字,则清空控件已选中的项目
  1048. if(!$(d.elem.combo_input).val() && $(d.elem.hidden).val() && !d.option.multiple){
  1049. d.prop.current_page = 1;//重置当前页为1
  1050. cleanContent(d);
  1051. d.hideResults(d);
  1052. return true;
  1053. }
  1054. //匹配项且高亮时,下拉分页控件失去焦点后,自动选择该项目
  1055. if ($('li', $(d.elem.results)).size() > 0) {
  1056. if(d.option.autoFillResult) {//打开自动内容填充功能
  1057. //若已有选中项目,则直接隐藏列表
  1058. if ($('li.sp_selected', $(d.elem.results)).size() > 0) {
  1059. d.hideResults(d);
  1060. }else if($('li.sp_over', $(d.elem.results)).size() > 0){
  1061. //若控件已有选中的值,则忽略高亮的项目
  1062. if($(d.elem.hidden).val()) d.hideResults(d);
  1063. //若没有已选中的项目,且列表中有高亮项目时,选中当前高亮的行
  1064. else d.selectCurrentLine(d, true);
  1065. }else if(d.option.autoSelectFirst){
  1066. //若控件已有选中的值,则忽略自动选择第一项的功能
  1067. if($(d.elem.hidden).val()) d.hideResults(d);
  1068. else{
  1069. //对于没有选中,没有高亮的情况,若插件设置了自动选中第一项时,则选中第一项
  1070. d.nextLine(d);
  1071. //self.nextLine(self);
  1072. d.selectCurrentLine(d, true);
  1073. }
  1074. }else d.hideResults(d);
  1075. }else d.hideResults(d);
  1076. } else {
  1077. //无匹配项目时,自动清空用户输入的关键词
  1078. if (d.option.noResultClean) cleanContent(d);
  1079. else{
  1080. if(!d.option.multiple) $(d.elem.hidden).val('');
  1081. }
  1082. d.hideResults(d);
  1083. }
  1084. });
  1085. }
  1086. */
  1087. });
  1088. };
  1089. /**
  1090. * @desc 结果列表的事件处理
  1091. */
  1092. SelectPage.prototype.eResultList = function () {
  1093. var self = this;
  1094. $(self.elem.results).children('li').mouseenter(function () {
  1095. if (self.prop.key_select) {
  1096. self.prop.key_select = false;
  1097. return;
  1098. }
  1099. // if (!$(this).hasClass(self.css_class.selected) && !$(this).hasClass('sp_message_box')) {
  1100. $(this).addClass(self.css_class.select);
  1101. self.setCssFocusedResults(self);
  1102. // }
  1103. }).mouseleave(function () {
  1104. $(this).removeClass(self.css_class.select);
  1105. }).click(function (e) {
  1106. if (self.prop.key_select) {
  1107. self.prop.key_select = false;
  1108. return;
  1109. }
  1110. e.preventDefault();
  1111. e.stopPropagation();
  1112. // if (!$(this).hasClass(self.css_class.selected))
  1113. self.selectCurrentLine(self, false);
  1114. });
  1115. };
  1116. /**
  1117. * @desc 分页导航按钮的事件处理
  1118. */
  1119. SelectPage.prototype.ehNaviPaging = function () {
  1120. var self = this;
  1121. if (!self.option.pagination)
  1122. return;
  1123. $('li.csFirstPage', $(self.elem.navi)).off('click').on('click', function (ev) {
  1124. //$(self.elem.combo_input).focus();
  1125. ev.preventDefault();
  1126. self.firstPage(self);
  1127. });
  1128. $('li.csPreviousPage', $(self.elem.navi)).off('click').on('click', function (ev) {
  1129. //$(self.elem.combo_input).focus();
  1130. ev.preventDefault();
  1131. self.prevPage(self);
  1132. });
  1133. $('li.csNextPage', $(self.elem.navi)).off('click').on('click', function (ev) {
  1134. //$(self.elem.combo_input).focus();
  1135. ev.preventDefault();
  1136. self.nextPage(self);
  1137. });
  1138. $('li.csLastPage', $(self.elem.navi)).off('click').on('click', function (ev) {
  1139. //$(self.elem.combo_input).focus();
  1140. ev.preventDefault();
  1141. self.lastPage(self);
  1142. });
  1143. };
  1144. /**
  1145. * @desc Ajax请求失败的处理
  1146. * @param {Object} self - 插件内部对象
  1147. * @param {string} errorThrown - Ajax的错误输出内容
  1148. */
  1149. SelectPage.prototype.ajaxErrorNotify = function (self, errorThrown) {
  1150. self.showMessage(self.message.ajax_error);
  1151. };
  1152. /**
  1153. * @desc 交互消息显示
  1154. * @param {Object} self - 插件内部对象
  1155. * @param msg {string} 需要提示的文本
  1156. */
  1157. SelectPage.prototype.showMessage = function (self, msg) {
  1158. if (!msg)
  1159. return;
  1160. var msgLi = '<li class="sp_message_box"><i class="fa fa-exclamation-triangle"></i> ' + msg + '</li>';
  1161. $(self.elem.results).empty().append(msgLi);
  1162. self.calcResultsSize(self);
  1163. $(self.elem.container).addClass(self.css_class.container_open);
  1164. $(self.elem.control).hide();
  1165. if (self.option.pagination)
  1166. $(self.elem.navi).hide();
  1167. };
  1168. /**
  1169. * @desc 窗口滚动处理
  1170. * @param {Object} self - 插件内部对象
  1171. * @param {boolean} enforce - 是否定位到输入框的位置
  1172. */
  1173. SelectPage.prototype.scrollWindow = function (self, enforce) {
  1174. var current_result = self.getCurrentLine(self);
  1175. var target_top = (current_result && !enforce) ? current_result.offset().top : $(self.elem.container).offset().top;
  1176. var target_size;
  1177. self.prop.size_li = $(self.elem.results).children('li:first').outerHeight();
  1178. target_size = self.prop.size_li;
  1179. var client_height = $(window).height();
  1180. var scroll_top = $(window).scrollTop();
  1181. var scroll_bottom = scroll_top + client_height - target_size;
  1182. // 滚动处理
  1183. var gap;
  1184. if ($(current_result).length) {
  1185. if (target_top < scroll_top || target_size > client_height) {
  1186. //滚动到顶部
  1187. gap = target_top - scroll_top;
  1188. } else if (target_top > scroll_bottom) {
  1189. //向下滚动
  1190. gap = target_top - scroll_bottom;
  1191. } else
  1192. return; //不进行滚动
  1193. } else if (target_top < scroll_top)
  1194. gap = target_top - scroll_top;
  1195. window.scrollBy(0, gap);
  1196. };
  1197. /**
  1198. * @desc 输入框获得焦点的样式设置
  1199. * @param {Object} self - 插件内部对象
  1200. */
  1201. SelectPage.prototype.setCssFocusedInput = function (self) {
  1202. //$(self.elem.results).addClass(self.css_class.re_off);
  1203. //$(self.elem.combo_input).removeClass(self.css_class.input_off);
  1204. };
  1205. /**
  1206. * @desc 设置结果列表高亮,输入框失去焦点
  1207. * @param {Object} self - 插件内部对象
  1208. */
  1209. SelectPage.prototype.setCssFocusedResults = function (self) {
  1210. //$(self.elem.results).removeClass(self.css_class.re_off);
  1211. //$(self.elem.combo_input).addClass(self.css_class.input_off);
  1212. };
  1213. /**
  1214. * @desc 输入框输入值的变化监控
  1215. * @param {Object} self - 插件内部对象
  1216. */
  1217. SelectPage.prototype.checkValue = function (self) {
  1218. var now_value = $(self.elem.combo_input).val();
  1219. if (now_value != self.prop.prev_value) {
  1220. self.prop.prev_value = now_value;
  1221. self.prop.first_show = false;
  1222. if (self.option.selectOnly)
  1223. self.setButtonAttrDefault();
  1224. if (!self.option.multiple && !now_value) {
  1225. self.clearAll(self);
  1226. $(self.elem.clear_btn).remove();
  1227. }
  1228. self.suggest(self);
  1229. }
  1230. };
  1231. /**
  1232. * @desc 文本输入框键盘事件处理(普通字符输入处理)
  1233. * @param {Object} self - 插件内部对象
  1234. * @param {Object} e - 事件event对象
  1235. */
  1236. SelectPage.prototype.processKey = function (self, e) {
  1237. if ($.inArray(e.keyCode, [37, 38, 39, 40, 27, 9, 13]) === -1) {
  1238. if (e.keyCode != 16)
  1239. self.setCssFocusedInput(self); // except Shift(16)
  1240. self.inputResize(self);
  1241. if ($.type(self.option.data) === 'string') {
  1242. self.prop.last_input_time = e.timeStamp;
  1243. setTimeout(function () {
  1244. if ((e.timeStamp - self.prop.last_input_time) === 0)
  1245. self.checkValue(self);
  1246. }, self.option.inputDelay * 1000);
  1247. } else {
  1248. self.checkValue(self);
  1249. }
  1250. }
  1251. }
  1252. /**
  1253. * @desc 文本输入框键盘事件处理(控制键处理)
  1254. * @param {Object} self - 插件内部对象
  1255. * @param {Object} e - 事件event对象
  1256. */
  1257. SelectPage.prototype.processControl = function (self, e) {
  1258. if (($.inArray(e.keyCode, [37, 38, 39, 40, 27, 9]) > -1 && $(self.elem.result_area).is(':visible')) ||
  1259. ($.inArray(e.keyCode, [13, 9]) > -1 && self.getCurrentLine(self))) {
  1260. e.preventDefault();
  1261. e.stopPropagation();
  1262. e.cancelBubble = true;
  1263. e.returnValue = false;
  1264. switch (e.keyCode) {
  1265. case 37:
  1266. // left
  1267. if (e.shiftKey)
  1268. self.firstPage(self);
  1269. else
  1270. self.prevPage(self);
  1271. break;
  1272. case 38:
  1273. // up
  1274. self.prop.key_select = true;
  1275. self.prevLine(self);
  1276. break;
  1277. case 39:
  1278. // right
  1279. if (e.shiftKey)
  1280. self.lastPage(self);
  1281. else
  1282. self.nextPage(self);
  1283. break;
  1284. case 40:
  1285. // down
  1286. if ($(self.elem.results).children('li').length) {
  1287. self.prop.key_select = true;
  1288. self.nextLine(self);
  1289. } else
  1290. self.suggest(self);
  1291. break;
  1292. case 9:
  1293. // tab
  1294. self.prop.key_paging = true;
  1295. self.selectCurrentLine(self, true);
  1296. //self.hideResults(self);
  1297. break;
  1298. case 13:
  1299. // return
  1300. self.selectCurrentLine(self, true);
  1301. break;
  1302. case 27:
  1303. // escape
  1304. self.prop.key_paging = true;
  1305. self.hideResults(self);
  1306. break;
  1307. }
  1308. }
  1309. };
  1310. /**
  1311. * @desc 中断Ajax请求
  1312. * @param {Object} self - 插件内部对象
  1313. */
  1314. SelectPage.prototype.abortAjax = function (self) {
  1315. if (self.prop.xhr) {
  1316. self.prop.xhr.abort();
  1317. self.prop.xhr = false;
  1318. }
  1319. };
  1320. /**
  1321. * @desc 数据查询
  1322. * @param {Object} self - 插件内部对象
  1323. */
  1324. SelectPage.prototype.suggest = function (self) {
  1325. //搜索关键字
  1326. var q_word;
  1327. var val = $.trim($(self.elem.combo_input).val());
  1328. if (self.option.multiple)
  1329. q_word = val;
  1330. else {
  1331. if (val && val === self.prop.selected_text)
  1332. q_word = '';
  1333. else
  1334. q_word = val;
  1335. }
  1336. q_word = q_word.split(/[\s ]+/);
  1337. self.abortAjax(self);
  1338. self.setLoading(self);
  1339. var which_page_num = self.prop.current_page > 0 ? self.prop.current_page : 1;
  1340. // 数据查询
  1341. if (typeof self.option.data == 'object')
  1342. self.searchForJson(self, q_word, which_page_num);
  1343. else
  1344. self.searchForDb(self, q_word, which_page_num);
  1345. };
  1346. /**
  1347. * @private
  1348. * @desc 读取中状态显示
  1349. * @param {Object} self - 插件内部对象
  1350. */
  1351. SelectPage.prototype.setLoading = function (self) {
  1352. //加载中的状态提示
  1353. if ($(self.elem.results).html() === '') {
  1354. //self.calcResultsSize(self);
  1355. $(self.elem.container).addClass(self.css_class.container_open);
  1356. }
  1357. };
  1358. /**
  1359. * @desc 服务端数据查询
  1360. * @param {Object} self - 插件内部对象
  1361. * @param {Array} q_word - 查询关键字
  1362. * @param {number} which_page_num - 目标页
  1363. */
  1364. SelectPage.prototype.searchForDb = function (self, q_word, which_page_num) {
  1365. if(typeof dosomething == "function"){
  1366. self.option.eAjaxSuccess = dosomething;
  1367. }
  1368. if (!self.option.eAjaxSuccess || !$.isFunction(self.option.eAjaxSuccess))
  1369. self.hideResults(self);
  1370. /**
  1371. * 增加自定义查询参数
  1372. */
  1373. var _paramsFunc = self.option.params;
  1374. var _params = {};
  1375. //原始参数
  1376. var searchKey = self.option.searchField;
  1377. //若有查询关键字,则重置当前页码为1
  1378. if (q_word.length > 0 && q_word[0] && q_word[0] !== self.prop.prev_value)
  1379. which_page_num = 1;
  1380. var _orgParams = {
  1381. q_word: q_word,
  1382. pageNumber: which_page_num,
  1383. pageSize: self.option.pageSize,
  1384. andOr: self.option.andOr,
  1385. orderBy: self.option.orderBy,
  1386. searchTable: self.option.dbTable,
  1387. page: which_page_num,
  1388. per_page: self.option.pageSize,
  1389. and_or: self.option.andOr,
  1390. order_by: self.option.orderBy,
  1391. field: self.option.showField,
  1392. pkey_name: self.option.keyField,
  1393. search_field: searchKey
  1394. };
  1395. _orgParams[searchKey] = q_word[0];
  1396. if (_paramsFunc) {
  1397. var result = $.isFunction(_paramsFunc) ? _paramsFunc() : _paramsFunc;
  1398. if (result && $.isPlainObject(result)) {
  1399. _params = $.extend({}, _orgParams, result);
  1400. } else {
  1401. _params = _orgParams;
  1402. }
  1403. } else {
  1404. _params = _orgParams;
  1405. }
  1406. //增加自定义查询参数End
  1407. self.prop.xhr = $.ajax({
  1408. dataType: 'json',
  1409. url: self.option.data,
  1410. type: 'POST',
  1411. data: _params,
  1412. success: function (returnData) {
  1413. if (!returnData || !$.isPlainObject(returnData)) {
  1414. self.hideResults(self);
  1415. self.ajaxErrorNotify(self);
  1416. return;
  1417. }
  1418. var data;
  1419. if (self.option.eAjaxSuccess && $.isFunction(self.option.eAjaxSuccess)) {
  1420. data = self.option.eAjaxSuccess(returnData);
  1421. } else {
  1422. data = returnData;
  1423. }
  1424. //数据结构处理
  1425. var json = {};
  1426. json.originalResult = data.list;
  1427. json.cnt_whole = typeof data.total !== 'undefined' ? data.total : (typeof data.totalRow !== 'undefined' ? data.totalRow : data.list.length);
  1428. json.candidate = [];
  1429. json.keyField = [];
  1430. if (typeof json.originalResult != 'object') {
  1431. self.prop.xhr = null;
  1432. self.notFoundSearch(self);
  1433. return;
  1434. }
  1435. json.cnt_page = json.originalResult.length;
  1436. for (var i = 0; i < json.cnt_page; i++) {
  1437. for (var key in json.originalResult[i]) {
  1438. if (key == self.option.keyField) {
  1439. json.keyField.push(json.originalResult[i][key]);
  1440. }
  1441. if (key == self.option.showField) {
  1442. json.candidate.push(json.originalResult[i][key]);
  1443. }
  1444. }
  1445. }
  1446. self.prepareResults(self, json, q_word, which_page_num);
  1447. },
  1448. error: function (jqXHR, textStatus, errorThrown) {
  1449. if (textStatus != 'abort') {
  1450. self.hideResults(self);
  1451. self.ajaxErrorNotify(self, errorThrown);
  1452. }
  1453. },
  1454. complete: function () {
  1455. self.prop.xhr = null;
  1456. }
  1457. });
  1458. };
  1459. /**
  1460. * @desc 对JSON源数据进行搜索
  1461. * @param {Object} self - 插件内部对象
  1462. * @param {Array} q_word - 搜索关键字
  1463. * @param {number} which_page_num - 目标页数
  1464. */
  1465. SelectPage.prototype.searchForJson = function (self, q_word, which_page_num) {
  1466. var matched = [];
  1467. var esc_q = [];
  1468. var sorted = [];
  1469. var json = {};
  1470. var i = 0;
  1471. var arr_reg = [];
  1472. //查询条件过滤
  1473. do {
  1474. //'/\W/g'正则代表全部不是字母,数字,下划线,汉字的字符
  1475. //将非法字符进行转义
  1476. esc_q[i] = q_word[i].replace(/\W/g, '\\$&').toString();
  1477. arr_reg[i] = new RegExp(esc_q[i], 'gi');
  1478. i++;
  1479. } while (i < q_word.length);
  1480. // SELECT * FROM data WHERE field LIKE q_word;
  1481. for (i = 0; i < self.option.data.length; i++) {
  1482. var flag = false;
  1483. var row = self.option.data[i];
  1484. for (var j = 0; j < arr_reg.length; j++) {
  1485. var itemText = row[self.option.showField];//默认获取showField字段的文本
  1486. if (self.option.formatItem && $.isFunction(self.option.formatItem))
  1487. itemText = self.option.formatItem(row);
  1488. if (itemText.match(arr_reg[j])) {
  1489. flag = true;
  1490. if (self.option.andOr == 'OR')
  1491. break;
  1492. } else {
  1493. flag = false;
  1494. if (self.option.andOr == 'AND')
  1495. break;
  1496. }
  1497. }
  1498. if (flag)
  1499. matched.push(row);
  1500. }
  1501. // (CASE WHEN ...) 然后 く order 指定列
  1502. var reg1 = new RegExp('^' + esc_q[0] + '$', 'gi');
  1503. var reg2 = new RegExp('^' + esc_q[0], 'gi');
  1504. var matched1 = [];
  1505. var matched2 = [];
  1506. var matched3 = [];
  1507. for (i = 0; i < matched.length; i++) {
  1508. var orderField = self.option.orderBy[0][0];
  1509. var orderValue = String(matched[i][orderField]);
  1510. if (orderValue.match(reg1)) {
  1511. matched1.push(matched[i]);
  1512. } else if (orderValue.match(reg2)) {
  1513. matched2.push(matched[i]);
  1514. } else {
  1515. matched3.push(matched[i]);
  1516. }
  1517. }
  1518. if (self.option.orderBy[0][1].match(/^asc$/i)) {
  1519. matched1 = self.sortAsc(self, matched1);
  1520. matched2 = self.sortAsc(self, matched2);
  1521. matched3 = self.sortAsc(self, matched3);
  1522. } else {
  1523. matched1 = self.sortDesc(self, matched1);
  1524. matched2 = self.sortDesc(self, matched2);
  1525. matched3 = self.sortDesc(self, matched3);
  1526. }
  1527. sorted = sorted.concat(matched1).concat(matched2).concat(matched3);
  1528. //若没有匹配项目,则结束搜索
  1529. /*
  1530. if (sorted.length === undefined || sorted.length === 0 ) {
  1531. self.notFoundSearch(self);
  1532. return;
  1533. }
  1534. */
  1535. json.cnt_whole = sorted.length;
  1536. //page_move参数用于区别数据加载是在初始化列表还是在进行分页的翻页操作
  1537. if (!self.prop.page_move) {
  1538. //仅单选模式进行选中项目定位页功能
  1539. if (!self.option.multiple) {
  1540. //若控件当前已有选中值,则获得该项目所在的页数,并跳转到该页进行显示
  1541. var currentValue = $(self.elem.hidden).val();
  1542. if ($.type(currentValue) !== 'undefined' && $.trim(currentValue) !== '') {
  1543. var index = 0;
  1544. $.each(sorted, function (i, row) {
  1545. if (row[self.option.keyField] == currentValue) {
  1546. index = i + 1;
  1547. return false;
  1548. }
  1549. });
  1550. which_page_num = Math.ceil(index / self.option.pageSize);
  1551. if (which_page_num < 1)
  1552. which_page_num = 1;
  1553. self.prop.current_page = which_page_num;
  1554. }
  1555. }
  1556. } else {
  1557. //过滤后的数据个数不足一页显示的个数时,强制设置页码
  1558. if (sorted.length <= ((which_page_num - 1) * self.option.pageSize)) {
  1559. which_page_num = 1;
  1560. self.prop.current_page = 1;
  1561. }
  1562. }
  1563. // LIMIT xx OFFSET xx
  1564. var start = (which_page_num - 1) * self.option.pageSize;
  1565. var end = start + self.option.pageSize;
  1566. //储存原始行数据,包括所有属性
  1567. json.originalResult = [];
  1568. // 查询后的数据处理
  1569. for (i = start; i < end; i++) {
  1570. if (sorted[i] === undefined)
  1571. break;
  1572. json.originalResult.push(sorted[i]);
  1573. for (var key in sorted[i]) {
  1574. if (key == self.option.keyField) {
  1575. if (json.keyField === undefined)
  1576. json.keyField = [];
  1577. json.keyField.push(sorted[i][key]);
  1578. }
  1579. if (key == self.option.showField) {
  1580. if (json.candidate === undefined)
  1581. json.candidate = [];
  1582. json.candidate.push(sorted[i][key]);
  1583. }
  1584. }
  1585. }
  1586. if (json.candidate === undefined)
  1587. json.candidate = [];
  1588. json.cnt_page = json.candidate.length;
  1589. self.prepareResults(self, json, q_word, which_page_num);
  1590. };
  1591. /**
  1592. * @desc 升序排序
  1593. * @param {Object} self - 插件内部对象
  1594. * @param {Array} arr - 结果集数组
  1595. */
  1596. SelectPage.prototype.sortAsc = function (self, arr) {
  1597. arr.sort(function (a, b) {
  1598. var valA = a[self.option.orderBy[0][0]];
  1599. var valB = b[self.option.orderBy[0][0]];
  1600. return $.type(valA) === 'number' ? valA - valB : String(valA).localeCompare(String(valB));
  1601. });
  1602. return arr;
  1603. };
  1604. /**
  1605. * @desc 降序排序
  1606. * @param {Object} self - 插件内部对象
  1607. * @param {Array} arr - 结果集数组
  1608. */
  1609. SelectPage.prototype.sortDesc = function (self, arr) {
  1610. arr.sort(function (a, b) {
  1611. var valA = a[self.option.orderBy[0][0]];
  1612. var valB = b[self.option.orderBy[0][0]];
  1613. return $.type(valA) === 'number' ? valB - valA : String(valB).localeCompare(String(valA));
  1614. });
  1615. return arr;
  1616. };
  1617. /**
  1618. * @desc 查询无结果的处理
  1619. * @param {Object} self - 插件内部对象
  1620. */
  1621. SelectPage.prototype.notFoundSearch = function (self) {
  1622. $(self.elem.results).empty();
  1623. self.calcResultsSize(self);
  1624. $(self.elem.container).addClass(self.css_class.container_open);
  1625. self.setCssFocusedInput(self);
  1626. };
  1627. /**
  1628. * @desc 查询结果处理
  1629. * @param {Object} self - 插件内部对象
  1630. * @param {Object} json - 数据结果
  1631. * @param {Array} q_word - 查询关键字
  1632. * @param {number} which_page_num - 目标页
  1633. */
  1634. SelectPage.prototype.prepareResults = function (self, json, q_word, which_page_num) {
  1635. //处理分页栏
  1636. if (self.option.pagination)
  1637. self.setNavi(self, json.cnt_whole, json.cnt_page, which_page_num);
  1638. if (!json.keyField)
  1639. json.keyField = false;
  1640. //仅选择模式
  1641. if (self.option.selectOnly && json.candidate.length === 1 && json.candidate[0] == q_word[0]) {
  1642. $(self.elem.hidden).val(json.keyField[0]);
  1643. this.setButtonAttrDefault();
  1644. }
  1645. //是否是输入关键词进行查找
  1646. var is_query = false;
  1647. if (q_word && q_word.length > 0 && q_word[0])
  1648. is_query = true;
  1649. //显示结果列表
  1650. self.displayResults(self, json, is_query);
  1651. };
  1652. /**
  1653. * @desc 生成分页栏
  1654. * @param {Object} self - 插件内部对象
  1655. * @param {number} cnt_whole - 数据总条数
  1656. * @param {number} cnt_page - 页面显示记录数
  1657. * @param {number} page_num - 当前页数
  1658. */
  1659. SelectPage.prototype.setNavi = function (self, cnt_whole, cnt_page, page_num) {
  1660. /**
  1661. * 生成分页条
  1662. */
  1663. var buildPageNav = function (self, pagebar, page_num, last_page) {
  1664. if ($('li', $(pagebar)).size() == 0) {
  1665. $(pagebar).empty();
  1666. //处理当当前页码为1时,首页和上一页按钮不允许点击
  1667. var btnclass = '', isNewFontAwesome = true;
  1668. //判断是否使用了font-awesome3.2.1
  1669. $.each(document.styleSheets, function (i, n) {
  1670. if (n && n.href && n.href.indexOf('font-awesome-3.2.1') != -1) {
  1671. isNewFontAwesome = false;
  1672. return false;
  1673. }
  1674. });
  1675. //为不同版本图标设置样式
  1676. var iconFist = 'fa fa-angle-double-left', iconPrev = 'fa fa-angle-left', iconNext = 'fa fa-angle-right', iconLast = 'fa fa-angle-double-right';
  1677. if (!isNewFontAwesome) {
  1678. iconFist = 'icon-step-backward';
  1679. iconPrev = 'icon-backward';
  1680. iconNext = 'icon-forward';
  1681. iconLast = 'icon-step-forward';
  1682. }
  1683. if (page_num == 1)
  1684. btnclass = ' disabled ';
  1685. //首页
  1686. $(pagebar).append('<li class="csFirstPage' + btnclass + '" title="' + self.message.first_title + '" ><a href="javascript:void(0);"> <i class="' + iconFist + '"></i> </a></li>');
  1687. //上一页
  1688. $(pagebar).append('<li class="csPreviousPage' + btnclass + '" title="' + self.message.prev_title + '" ><a href="javascript:void(0);"><i class="' + iconPrev + '"></i></a></li>');
  1689. var pageInfo = '第 ' + page_num + ' 页(共' + last_page + '页)';
  1690. //设置分页信息
  1691. $(pagebar).append('<li class="pageInfoBox"><a href="javascript:void(0);"> ' + pageInfo + ' </a></li>');
  1692. if (page_num == last_page)
  1693. btnclass = ' disabled ';
  1694. else
  1695. btnclass = '';
  1696. //首页
  1697. $(pagebar).append('<li class="csNextPage' + btnclass + '" title="' + self.message.next_title + '" ><a href="javascript:void(0);"><i class="' + iconNext + '"></i></a></li>');
  1698. //上一页
  1699. $(pagebar).append('<li class="csLastPage' + btnclass + '" title="' + self.message.last_title + '" ><a href="javascript:void(0);"> <i class="' + iconLast + '"></i> </a></li>');
  1700. }
  1701. };
  1702. var pagebar = $(self.elem.navi);
  1703. var last_page = Math.ceil(cnt_whole / self.option.pageSize); //计算总页数
  1704. if (last_page == 0)
  1705. page_num = 0;
  1706. else {
  1707. if (last_page < page_num)
  1708. page_num = last_page;
  1709. else if (page_num == 0)
  1710. page_num = 1;
  1711. }
  1712. self.prop.current_page = page_num;//更新当前页参数
  1713. self.prop.max_page = last_page;//更新总页数参数
  1714. buildPageNav(self, pagebar, page_num, last_page);
  1715. //刷新分页信息
  1716. var pageInfoBox = $('li.pageInfoBox', $(pagebar));
  1717. var pageInfo = '第 ' + page_num + ' 页(共' + last_page + '页)';
  1718. $(pageInfoBox).html('<a href="javascript:void(0);"> ' + pageInfo + ' </a>');
  1719. //更新分页样式
  1720. var dClass = 'disabled';
  1721. var first = $('li.csFirstPage', $(pagebar));
  1722. var previous = $('li.csPreviousPage', $(pagebar));
  1723. var next = $('li.csNextPage', $(pagebar));
  1724. var last = $('li.csLastPage', $(pagebar));
  1725. //处理首页,上一页按钮样式
  1726. if (page_num === 1 || page_num === 0) {
  1727. if (!$(first).hasClass(dClass))
  1728. $(first).addClass(dClass);
  1729. if (!$(previous).hasClass(dClass))
  1730. $(previous).addClass(dClass);
  1731. } else {
  1732. if ($(first).hasClass(dClass))
  1733. $(first).removeClass(dClass);
  1734. if ($(previous).hasClass(dClass))
  1735. $(previous).removeClass(dClass);
  1736. }
  1737. //处理下一页,最后一页按钮的样式
  1738. if (page_num == last_page || last_page == 0) {
  1739. if (!$(next).hasClass(dClass))
  1740. $(next).addClass(dClass);
  1741. if (!$(last).hasClass(dClass))
  1742. $(last).addClass(dClass);
  1743. } else {
  1744. if ($(next).hasClass(dClass))
  1745. $(next).removeClass(dClass);
  1746. if ($(last).hasClass(dClass))
  1747. $(last).removeClass(dClass);
  1748. }
  1749. if (last_page > 1)
  1750. self.ehNaviPaging(); //导航按钮的事件设置
  1751. };
  1752. /**
  1753. * @desc 显示结果集列表
  1754. * @param {Object} self - 插件内部对象
  1755. * @param {Object} json 源数据
  1756. * @param {boolean} is_query - 是否是通过关键字搜索(用于区分是鼠标点击下拉还是输入框输入关键字进行查找)
  1757. */
  1758. SelectPage.prototype.displayResults = function (self, json, is_query) {
  1759. $(self.elem.results).empty();
  1760. if (self.option.multiple && $.type(self.option.maxSelectLimit) === 'number' && self.option.maxSelectLimit > 0) {
  1761. var selectedSize = $('li.selected_tag', self.elem.element_box).size();
  1762. if (selectedSize > 0 && selectedSize >= self.option.maxSelectLimit) {
  1763. self.showMessage(self, '最多只能选择 ' + self.option.maxSelectLimit + ' 个项目');
  1764. return;
  1765. }
  1766. }
  1767. if (json.candidate.length > 0) {
  1768. var arr_candidate = json.candidate;
  1769. var arr_primary_key = json.keyField;
  1770. var keystr = $(self.elem.hidden).val();
  1771. var keyArr = keystr ? keystr.split(',') : new Array();
  1772. for (var i = 0; i < arr_candidate.length; i++) {
  1773. var itemText = '';
  1774. if (self.option.formatItem && $.isFunction(self.option.formatItem)) {
  1775. try {
  1776. itemText = self.option.formatItem(json.originalResult[i]);
  1777. } catch (e) {
  1778. console.error('formatItem内容格式化函数内容设置不正确!');
  1779. itemText = arr_candidate[i];
  1780. }
  1781. } else
  1782. itemText = arr_candidate[i];
  1783. //XSS対策
  1784. var list = $('<li>').html(itemText).attr({
  1785. pkey: arr_primary_key[i],
  1786. title: itemText
  1787. });
  1788. //选中项目设置高亮样式
  1789. if ($.inArray(arr_primary_key[i].toString(), keyArr) !== -1) {
  1790. $(list).addClass(self.css_class.selected);
  1791. }
  1792. //缓存原始行对象
  1793. $(list).data('dataObj', json.originalResult[i]);
  1794. $(self.elem.results).append(list);
  1795. }
  1796. } else {
  1797. var li = '<li class="sp_message_box"><i class="fa fa-exclamation-triangle"></i> ' + self.message.not_found + '</li>';
  1798. $(self.elem.results).append(li);
  1799. }
  1800. if (self.option.multiple && self.option.multipleControlbar)
  1801. $(self.elem.control).show();
  1802. if (self.option.pagination)
  1803. $(self.elem.navi).toggleClass("hide", json.cnt_whole <= json.cnt_page);
  1804. //显示结果集列表并调整位置
  1805. self.calcResultsSize(self);
  1806. $(self.elem.container).addClass(self.css_class.container_open);
  1807. //结果集列表事件绑定
  1808. self.eResultList();
  1809. //若是键盘输入关键字进行查询且有内容时,列表自动选中第一行(autoSelectFirst为true时)
  1810. if (is_query && json.candidate.length > 0 && self.option.autoSelectFirst)
  1811. self.nextLine(self);
  1812. };
  1813. /**
  1814. * @desc 处理结果列表尺寸及位置
  1815. * @param {Object} self - 插件内部对象
  1816. */
  1817. SelectPage.prototype.calcResultsSize = function (self) {
  1818. var rePosition = function () {
  1819. if ($(self.elem.container).css('position') === 'static') {
  1820. // position: static
  1821. var offset = $(self.elem.combo_input).offset();
  1822. $(self.elem.result_area).css({
  1823. top: offset.top + $(self.elem.combo_input).outerHeight() + 'px',
  1824. left: offset.left + 'px'
  1825. });
  1826. } else {
  1827. if (!self.option.pagination) {
  1828. var itemHeight = $('li:first', self.elem.results).outerHeight(true);
  1829. var listHeight = itemHeight * self.option.listSize;
  1830. $(self.elem.results).css({
  1831. 'max-height': listHeight,
  1832. 'overflow-y': 'auto'
  1833. });
  1834. }
  1835. //在展示下拉列表时,判断默认与输入框左对齐的列表是否会超出屏幕边界,是则右对齐,否则默认左对齐
  1836. var docWidth = $(document).width();
  1837. var docHeight = $(document).height();//文档全部高度
  1838. var viewHeight = $(window).height();//可视区域高度
  1839. var offset = $(self.elem.container).offset();
  1840. var screenScrollTop = $(window).scrollTop();
  1841. var listWidth = $(self.elem.result_area).outerWidth();
  1842. //当前状态,列表并未被显示,数据未被填充,列表并未展现最终高度,所以只能使用默认一页显示10条数据的固定高度进行计算
  1843. var listHeight = $(self.elem.result_area).outerHeight();
  1844. //默认方向的坐标,在多选模式下,因为外框架是DIV,所以需要向左靠一个像素
  1845. var defaultLeft = self.option.multiple ? -1 : 0;
  1846. //输入框高度
  1847. var inputHeight = $(self.elem.container).outerHeight();
  1848. var left = (offset.left + listWidth) > docWidth ? -(listWidth - $(self.elem.container).outerWidth()) : defaultLeft;
  1849. //控件在全文档范围中的实际TOP(非当前可视区域中的相对TOP)
  1850. var screenTop = offset.top;//$(self.elem.container).scrollTop();//offset.top - screenScrollTop;
  1851. var top = 0, dist = 5;//设置偏移量,让列表与输入框有5px的间距
  1852. //列表展开后的坐标高度
  1853. var listBottom = screenTop + inputHeight + listHeight + dist;
  1854. var hasOverflow = docHeight > viewHeight;
  1855. if ((screenTop - screenScrollTop - dist > listHeight) &&
  1856. (hasOverflow && listBottom > (viewHeight + screenScrollTop)) ||
  1857. (!hasOverflow && listBottom > viewHeight)) {
  1858. //控件当前位置+控件高度+列表高度超过实际body高度
  1859. //列表则需要向上展示
  1860. top = -(listHeight + 1) - dist;
  1861. $(self.elem.result_area).removeClass('shadowUp shadowDown').addClass('shadowUp');
  1862. } else {
  1863. //列表正常向下展示
  1864. top = self.option.multiple ? $(self.elem.container).innerHeight() + 1 : $(self.elem.container).outerHeight();
  1865. $(self.elem.result_area).removeClass('shadowUp shadowDown').addClass('shadowDown');
  1866. top += dist;
  1867. }
  1868. /*
  1869. $(self.elem.result_area).css({
  1870. top : top + 'px',
  1871. left: left + 'px'
  1872. });
  1873. */
  1874. return {
  1875. top: top + 'px',
  1876. left: left + 'px'
  1877. };
  1878. }
  1879. };
  1880. if ($(self.elem.result_area).is(':visible')) {
  1881. $(self.elem.result_area).css(rePosition());
  1882. } else {
  1883. $(self.elem.result_area).show(1, function () {
  1884. $(this).css(rePosition());
  1885. });
  1886. }
  1887. };
  1888. /**
  1889. * @desc 隐藏结果列表
  1890. * @param {Object} self - 插件内部对象
  1891. */
  1892. SelectPage.prototype.hideResults = function (self) {
  1893. if (self.prop.key_paging) {
  1894. self.scrollWindow(self, true);
  1895. self.prop.key_paging = false;
  1896. }
  1897. self.setCssFocusedInput(self);
  1898. if (self.option.autoFillResult) {
  1899. //self.selectCurrentLine(self, true);
  1900. }
  1901. $(self.elem.results).empty();
  1902. $(self.elem.result_area).hide();
  1903. $(self.elem.container).removeClass(self.css_class.container_open);
  1904. self.abortAjax(self);
  1905. self.setButtonAttrDefault(); // 按钮title属性初期化
  1906. };
  1907. /**
  1908. * @desc 跳转到首页
  1909. * @param {Object} self - 插件内部对象
  1910. */
  1911. SelectPage.prototype.firstPage = function (self) {
  1912. if (self.prop.current_page > 1) {
  1913. self.prop.current_page = 1;
  1914. self.prop.page_move = true;
  1915. self.suggest(self);
  1916. }
  1917. };
  1918. /**
  1919. * @desc 跳转到上一页
  1920. * @param {Object} self - 插件内部对象
  1921. */
  1922. SelectPage.prototype.prevPage = function (self) {
  1923. if (self.prop.current_page > 1) {
  1924. self.prop.current_page--;
  1925. self.prop.page_move = true;
  1926. self.suggest(self);
  1927. }
  1928. };
  1929. /**
  1930. * @desc 跳转到下一页
  1931. * @param {Object} self - 插件内部对象
  1932. */
  1933. SelectPage.prototype.nextPage = function (self) {
  1934. if (self.prop.current_page < self.prop.max_page) {
  1935. self.prop.current_page++;
  1936. self.prop.page_move = true;
  1937. self.suggest(self);
  1938. }
  1939. };
  1940. /**
  1941. * @desc 跳转到尾页
  1942. * @param {Object} self - 插件内部对象
  1943. */
  1944. SelectPage.prototype.lastPage = function (self) {
  1945. if (self.prop.current_page < self.prop.max_page) {
  1946. self.prop.current_page = self.prop.max_page;
  1947. self.prop.page_move = true;
  1948. self.suggest(self);
  1949. }
  1950. };
  1951. /**
  1952. * @desc 跳转到指定页
  1953. * @param {Object} self
  1954. * @param {number} page 目标页数
  1955. */
  1956. SelectPage.prototype.goPage = function (self, page) {
  1957. if (typeof (page) === 'undefined')
  1958. page = 1;
  1959. if (self.prop.current_page < self.prop.max_page) {
  1960. self.prop.current_page = page;
  1961. self.prop.page_move = true;
  1962. self.suggest(self);
  1963. }
  1964. };
  1965. /**
  1966. * @desc 操作结束后的一些收尾工作
  1967. */
  1968. SelectPage.prototype.afterAction = function (self) {
  1969. self.inputResize(self);
  1970. $(self.elem.combo_input).trigger("change");
  1971. $(self.elem.hidden).trigger("change");
  1972. self.setCssFocusedInput(self);
  1973. if (self.option.multiple) {
  1974. if (self.option.selectToCloseList) {
  1975. self.hideResults(self);
  1976. $(self.elem.combo_input).blur();
  1977. } else {
  1978. self.suggest(self);
  1979. $(self.elem.combo_input).focus();
  1980. }
  1981. } else {
  1982. self.hideResults(self);
  1983. $(self.elem.combo_input).blur();
  1984. }
  1985. };
  1986. /**
  1987. * @desc 选择当前行
  1988. * @param {Object} self - 插件内部对象
  1989. * @param {boolean} is_enter_key - 是否为回车键
  1990. */
  1991. SelectPage.prototype.selectCurrentLine = function (self, is_enter_key) {
  1992. self.scrollWindow(self, true);
  1993. var current = self.getCurrentLine(self);
  1994. if (current) {
  1995. if (!self.option.multiple) {
  1996. $(self.elem.combo_input).val($(current).text());
  1997. $(self.elem.hidden).val($(current).attr('pkey'));
  1998. } else {
  1999. //多选模式的项目选择处理
  2000. $(self.elem.combo_input).val('');
  2001. var item = {text: $(current).text(), value: $(current).attr('pkey')};
  2002. if (!self.isAlreadySelected(self, item)) {
  2003. self.addNewTag(self, item);
  2004. self.tagValuesSet(self);
  2005. }
  2006. }
  2007. if (self.option.selectOnly)
  2008. self.setButtonAttrDefault();
  2009. //项目选择回调函数触发
  2010. if (self.option.eSelect && $.isFunction(self.option.eSelect))
  2011. self.option.eSelect($(current).data('dataObj'));
  2012. self.prop.prev_value = $(self.elem.combo_input).val();
  2013. self.prop.selected_text = $(self.elem.combo_input).val();
  2014. self.putClearButton();
  2015. }
  2016. self.afterAction(self);
  2017. };
  2018. /**
  2019. * 单选模式下选中项目后,显示清空按钮
  2020. */
  2021. SelectPage.prototype.putClearButton = function () {
  2022. if (!this.option.multiple && !$(this.elem.combo_input).prop('disabled'))
  2023. $(this.elem.container).append(this.elem.clear_btn);
  2024. };
  2025. /**
  2026. * @desc 全选当前页的行
  2027. * @param {Object} self - 插件内部对象
  2028. */
  2029. SelectPage.prototype.selectAllLine = function (self) {
  2030. var jsonarr = new Array();
  2031. $('li', self.elem.results).each(function (i, row) {
  2032. var item = {text: $(row).text(), value: $(row).attr('pkey')};
  2033. if (!self.isAlreadySelected(self, item)) {
  2034. self.addNewTag(self, item);
  2035. self.tagValuesSet(self);
  2036. }
  2037. jsonarr.push($(row).data('dataObj'));
  2038. //若有最大选择数量限制,则添加最大个数后,不再添加
  2039. if ($.type(self.option.maxSelectLimit) === 'number' &&
  2040. self.option.maxSelectLimit > 0 &&
  2041. self.option.maxSelectLimit === $('li.selected_tag', self.elem.element_box).size()) {
  2042. return false;
  2043. }
  2044. });
  2045. if (self.option.eSelect && $.isFunction(self.option.eSelect))
  2046. self.option.eSelect(jsonarr);
  2047. self.afterAction(self);
  2048. };
  2049. /**
  2050. * @desc 取消选择本页全部项目
  2051. * @param {Object} self - 插件内部对象
  2052. */
  2053. SelectPage.prototype.unselectAllLine = function (self) {
  2054. var size = $('li', self.elem.results).size();
  2055. $('li', self.elem.results).each(function (i, row) {
  2056. var key = $(row).attr('pkey');
  2057. var tag = $('li.selected_tag[itemvalue="' + key + '"]', self.elem.element_box);
  2058. self.removeTag(self, tag);
  2059. });
  2060. self.afterAction(self);
  2061. if (self.option.eTagRemove && $.isFunction(self.option.eTagRemove))
  2062. self.option.eTagRemove(size);
  2063. };
  2064. /**
  2065. * @desc 清除所有选中的项目
  2066. * @param {Object} self - 插件内部对象
  2067. */
  2068. SelectPage.prototype.clearAll = function (self) {
  2069. var size = 0;
  2070. if (self.option.multiple) {
  2071. size = $('li.selected_tag', self.elem.element_box).size();
  2072. $('li.selected_tag', self.elem.element_box).remove();
  2073. }
  2074. $(self.elem.combo_input).val('');
  2075. $(self.elem.hidden).val('');
  2076. self.afterAction(self);
  2077. if (self.option.multiple) {
  2078. if (self.option.eTagRemove && $.isFunction(self.option.eTagRemove))
  2079. self.option.eTagRemove(size);
  2080. }
  2081. };
  2082. /**
  2083. * @desc 获得当前行对象
  2084. * @param {Object} self - 插件内部对象
  2085. */
  2086. SelectPage.prototype.getCurrentLine = function (self) {
  2087. if ($(self.elem.result_area).is(':hidden'))
  2088. return false;
  2089. var obj = $('li.' + self.css_class.select, self.elem.results);
  2090. if ($(obj).size())
  2091. return obj;
  2092. else
  2093. return false;
  2094. };
  2095. /**
  2096. * @desc 多选模式下判断当前选中项目是否已经存在已选中列表中
  2097. * @param {Object} self - 插件内部对象
  2098. * @param {Object} item - 选中行对象
  2099. */
  2100. SelectPage.prototype.isAlreadySelected = function (self, item) {
  2101. var isExist = false;
  2102. if (item.value) {
  2103. var keys = $(self.elem.hidden).val();
  2104. if (keys) {
  2105. var karr = keys.split(',');
  2106. if (karr && karr.length > 0 && $.inArray(item.value, karr) != -1)
  2107. isExist = true;
  2108. }
  2109. }
  2110. return isExist;
  2111. };
  2112. /**
  2113. * @desc 多选模式下增加一个标签
  2114. * @param {Object} self - 插件内部对象
  2115. * @param {Object} item - 选中行对象
  2116. */
  2117. SelectPage.prototype.addNewTag = function (self, item) {
  2118. if (!self.option.multiple || !item)
  2119. return;
  2120. var tmp = self.template.tag.content, tag;
  2121. tmp = tmp.replace(self.template.tag.textKey, item.text);
  2122. tmp = tmp.replace(self.template.tag.valueKey, item.value);
  2123. tag = $(tmp);
  2124. if ($(self.elem.combo_input).prop('disabled'))
  2125. $('span.tag_close', tag).hide();
  2126. $(self.elem.combo_input).closest('li').before(tag);
  2127. };
  2128. /**
  2129. * @desc 多选模式下移除一个标签
  2130. * @param {Object} self - 插件内部对象
  2131. * @param {Object} item - 标签对象
  2132. */
  2133. SelectPage.prototype.removeTag = function (self, item) {
  2134. var key = $(item).attr('itemvalue');
  2135. var keys = $(self.elem.hidden).val();
  2136. //从已保存的key列表中删除该标签对应的项目
  2137. if ($.type(key) != 'undefined' && keys) {
  2138. var keyarr = keys.split(',');
  2139. var index = $.inArray(key.toString(), keyarr);
  2140. if (index != -1) {
  2141. keyarr.splice(index, 1);
  2142. $(self.elem.hidden).val(keyarr.toString());
  2143. }
  2144. }
  2145. $(item).remove();
  2146. self.inputResize(self);
  2147. };
  2148. /**
  2149. * @desc 多选模式下标签结果值放入隐藏域
  2150. * @param {Object} self - 插件内部对象
  2151. */
  2152. SelectPage.prototype.tagValuesSet = function (self) {
  2153. if (!self.option.multiple)
  2154. return;
  2155. var tags = $('li.selected_tag', $(self.elem.element_box));
  2156. if (tags && $(tags).size() > 0) {
  2157. var result = new Array();
  2158. $.each(tags, function (i, li) {
  2159. var v = $(li).attr('itemvalue');
  2160. if ($.type(v) !== 'undefined')
  2161. result.push(v);
  2162. });
  2163. if (result.length > 0) {
  2164. $(self.elem.hidden).val(result.join(','));
  2165. }
  2166. }
  2167. };
  2168. /**
  2169. * @desc 多选模式下输入框根据输入内容调整输入框宽度
  2170. * @param {Object} self - 插件内部对象
  2171. */
  2172. SelectPage.prototype.inputResize = function (self) {
  2173. if (!self.option.multiple)
  2174. return;
  2175. var width = '';
  2176. var inputLi = self.elem.combo_input.closest('li');
  2177. //设置默认宽度
  2178. var setDefaultSize = function (self, inputLi) {
  2179. inputLi.removeClass('full_width');
  2180. var minimumWidth = self.elem.combo_input.val().length + 1;
  2181. var width = (minimumWidth * 0.75) + 'em';
  2182. self.elem.combo_input.css('width', width);
  2183. self.elem.combo_input.removeAttr('placeholder');
  2184. };
  2185. if ($('li.selected_tag', $(self.elem.element_box)).size() === 0) {
  2186. if (self.elem.combo_input.attr('placeholder_bak')) {
  2187. if (!inputLi.hasClass('full_width'))
  2188. inputLi.addClass('full_width');
  2189. self.elem.combo_input.attr('placeholder', self.elem.combo_input.attr('placeholder_bak'));
  2190. self.elem.combo_input.removeAttr('style');
  2191. } else
  2192. setDefaultSize(self, inputLi);
  2193. } else
  2194. setDefaultSize(self, inputLi);
  2195. };
  2196. /**
  2197. * @desc 选择下一行
  2198. * @param {Object} self - 插件内部对象
  2199. */
  2200. SelectPage.prototype.nextLine = function (self) {
  2201. var obj = self.getCurrentLine(self);
  2202. var idx;
  2203. if (!obj)
  2204. idx = -1;
  2205. else {
  2206. idx = $(self.elem.results).children('li').index(obj);
  2207. $(obj).removeClass(self.css_class.select);
  2208. }
  2209. idx++;
  2210. if (idx < $(self.elem.results).children('li').length) {
  2211. var next = $(self.elem.results).children('li').eq(idx);
  2212. $(next).addClass(self.css_class.select);
  2213. self.setCssFocusedResults(self);
  2214. } else
  2215. self.setCssFocusedInput(self);
  2216. self.scrollWindow(self, false);
  2217. };
  2218. /**
  2219. * @desc 选择上一行
  2220. * @param {Object} self - 插件内部对象
  2221. */
  2222. SelectPage.prototype.prevLine = function (self) {
  2223. var obj = self.getCurrentLine(self);
  2224. var idx;
  2225. if (!obj)
  2226. idx = $(self.elem.results).children('li').length;
  2227. else {
  2228. idx = $(self.elem.results).children('li').index(obj);
  2229. $(obj).removeClass(self.css_class.select);
  2230. }
  2231. idx--;
  2232. if (idx > -1) {
  2233. var prev = $(self.elem.results).children('li').eq(idx);
  2234. $(prev).addClass(self.css_class.select);
  2235. self.setCssFocusedResults(self);
  2236. } else
  2237. self.setCssFocusedInput(self);
  2238. self.scrollWindow(self, false);
  2239. };
  2240. /**
  2241. * @desc 下拉分页查询控件初始化入口
  2242. * @global
  2243. * @memberof jQuery,bootstrap2,bootstrap3
  2244. * @param option {Object} 初始化参数集
  2245. */
  2246. function Plugin(option) {
  2247. return this.each(function () {
  2248. var $this = $(this),
  2249. data = $this.data(SelectPage.dataKey),
  2250. params = $.extend({}, defaults, $this.data(), data && data.option, typeof option === 'object' && option);
  2251. if (!data)
  2252. $this.data(SelectPage.dataKey, (data = new SelectPage(this, params)));
  2253. });
  2254. }
  2255. /**
  2256. * 获得稿件内部对象
  2257. * @param {object} obj
  2258. * @returns
  2259. */
  2260. function getPlugin(obj) {
  2261. var container = $(obj).closest('div.sp_container');
  2262. return $('input.sp_input', container);
  2263. }
  2264. /**
  2265. * @desc 清除所有模式下选择的项目
  2266. */
  2267. function ClearSelected() {
  2268. return this.each(function () {
  2269. var $this = getPlugin(this),
  2270. data = $this.data(SelectPage.dataKey);
  2271. if (data)
  2272. data.clearAll(data);
  2273. });
  2274. }
  2275. /**
  2276. * 刷新选中项目内容
  2277. * 使用场景:使用$().val('xxx')修改插件的选中项目ID,此时需要刷新插件在输入框中的显示文本
  2278. */
  2279. function SelectedRefresh() {
  2280. return this.each(function () {
  2281. var $this = getPlugin(this),
  2282. data = $this.data(SelectPage.dataKey);
  2283. if (data && data.elem.hidden.val())
  2284. data.setInitRecord(true);
  2285. });
  2286. }
  2287. /**
  2288. * 修改插件数据源
  2289. * 仅在json数据源模式有效
  2290. * @param {array} data
  2291. * @example
  2292. * [{name:'aa',sex:1},{name:'bb',sex:0},{...}]
  2293. */
  2294. function ModifyDataSource(data) {
  2295. return this.each(function () {
  2296. if (data && $.isArray(data) && data.length > 0) {
  2297. var $this = getPlugin(this),
  2298. plugin = $this.data(SelectPage.dataKey);
  2299. if (plugin) {
  2300. plugin.clearAll(plugin);
  2301. plugin.option.data = data;
  2302. }
  2303. }
  2304. });
  2305. }
  2306. /**
  2307. * @desc 获得选中项目的文本
  2308. * @returns {string}
  2309. */
  2310. function GetInputText() {
  2311. var str = '';
  2312. this.each(function () {
  2313. var $this = getPlugin(this), data = $this.data(SelectPage.dataKey);
  2314. if (data)
  2315. str += data.elem.combo_input.val();
  2316. });
  2317. return str;
  2318. }
  2319. /**
  2320. * @desc 获得选中项目的数据
  2321. * @returns {String}
  2322. */
  2323. function GetInputValue() {
  2324. var str = '';
  2325. this.each(function () {
  2326. var $this = getPlugin(this), data = $this.data(SelectPage.dataKey);
  2327. if (data)
  2328. str += data.elem.hidden.val();
  2329. });
  2330. return str;
  2331. }
  2332. var old = $.fn.selectPage;
  2333. $.fn.selectPage = Plugin;
  2334. $.fn.selectPage.Constructor = SelectPage;
  2335. $.fn.selectPageClear = ClearSelected;
  2336. $.fn.selectPageRefresh = SelectedRefresh;
  2337. $.fn.selectPageData = ModifyDataSource;
  2338. $.fn.selectPageText = GetInputText;
  2339. $.fn.selectPageValue = GetInputValue;
  2340. // 处理新旧版本冲突
  2341. // =================
  2342. $.fn.selectPage.noConflict = function () {
  2343. $.fn.selectPage = old;
  2344. return this;
  2345. };
  2346. }));