utils.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. function GoTo(url, isNewWindow) {
  2. if (isNewWindow || isNewWindow === undefined) {
  3. window.open(url)
  4. } else {
  5. window.location.href = url
  6. }
  7. }
  8. function IwbAjax(opt) {
  9. opt = opt || {}
  10. if (!opt.url) {
  11. MsgWarning('请传入url')
  12. }
  13. if (opt.method === undefined) {
  14. opt.method = 'POST'
  15. }
  16. if (opt.data === undefined) {
  17. opt.data = {}
  18. }
  19. opt = $.extend(
  20. {},
  21. {
  22. isAlert: true,
  23. },
  24. opt
  25. )
  26. fetch(opt.url, {
  27. method: opt.method,
  28. headers:opt.headers || {
  29. 'Content-Type': 'application/json',
  30. },
  31. body: opt.body || JSON.stringify(opt.data),
  32. })
  33. .then((response) => response.json())
  34. .then((data) => {
  35. if (data.success) {
  36. if (opt.isAlert) {
  37. MsgSuccess('操作成功')
  38. }
  39. if (opt.table) {
  40. IwbTable($(opt.table))
  41. }
  42. if (opt.modal) {
  43. $(opt.modal).modal('hide')
  44. }
  45. opt.success && opt.success(data)
  46. } else {
  47. console.error(opt.url, data.message)
  48. if ((opt.isAlert && opt.isAlertError === undefined) || opt.isAlertError) {
  49. MsgError(data.message)
  50. }
  51. }
  52. })
  53. }
  54. function IwbAjax_1(opt) {
  55. opt.isAlert = false
  56. opt.isAlertError = true
  57. IwbAjax(opt)
  58. }
  59. function IwbAjax_2(opt, modal, table) {
  60. opt.modal = modal || '#modal'
  61. opt.table = table || '#table'
  62. IwbAjax(opt)
  63. }
  64. function IwbTable(table, opts,isReload) {
  65. const $table = $(table)
  66. const $tableBox = $table.closest('.table-box')
  67. if (table.length === 0) return
  68. const defaultOptions = {
  69. pageSize: 15,
  70. pageNum: 1,
  71. search: {
  72. keyword: '',
  73. },
  74. }
  75. const options = isReload ? defaultOptions : ($table.data('options') || defaultOptions)
  76. const tableOptions = $.extend({}, options, opts || {})
  77. let isSearch = false
  78. function ajaxTable(opt) {
  79. if (isSearch) {
  80. return
  81. }
  82. $table.data('options', opt)
  83. loading()
  84. const data = $.extend({}, opt.search, { pageNum: opt.pageNum, pageSize: opt.pageSize })
  85. fetch(opt.url, {
  86. method: 'POST',
  87. headers: {
  88. 'Content-Type': 'application/json',
  89. },
  90. body: JSON.stringify(data),
  91. })
  92. .then((response) => response.json())
  93. .then((res) => {
  94. if (res.success) {
  95. opt.callBeforeRender && opt.callBeforeRender($table,res.data.rows)
  96. renderTable(res.data.rows, res.data.total)
  97. setTimeout(() => {
  98. opt.callAfterRender && opt.callAfterRender($table,res.data.rows)
  99. },1000)
  100. } else {
  101. renderTable([], 0)
  102. console.error('加载表格出错:', res.message, opt.url)
  103. }
  104. })
  105. .catch((error) => {
  106. console.error(error)
  107. })
  108. .finally(() => {
  109. clearLoading()
  110. })
  111. }
  112. function loading() {
  113. isSearch = true
  114. $tableBox.addClass('table-loading').append(`<div class="table-loading-message"><span>正在加载中...</span></div>`)
  115. }
  116. function clearLoading() {
  117. isSearch = false
  118. $tableBox.removeClass('table-loading').find('.table-loading-message').remove()
  119. }
  120. function renderTable(rows, total) {
  121. const opt = $table.data('options')
  122. let head_str = '',
  123. body_str = ''
  124. for (let i = 0; i < opt.columns.length; i++) {
  125. const col = opt.columns[i]
  126. head_str += `<th style="${col.width ? `width: ${col.width};` : ``}${col.style ? `${col.style};` : ``}">${col.title}</th>`
  127. }
  128. if (rows && rows.length) {
  129. for (let i = 0; i < rows.length; i++) {
  130. const row = rows[i]
  131. body_str += '<tr>'
  132. for (let j = 0; j < opt.columns.length; j++) {
  133. const col = opt.columns[j]
  134. if (col.render) {
  135. body_str += `<td>${col.render(row, rows)}</td>`
  136. } else {
  137. body_str += `<td>${row[col.data] || '-'}</td>`
  138. }
  139. }
  140. body_str += '</tr>'
  141. }
  142. } else {
  143. body_str += '<tr><td colspan="' + opt.columns.length + '" class="text-center">暂无数据</td></tr>'
  144. }
  145. $tableBox.fadeOut(300, () => {
  146. let page_str = formatPage(opt.pageNum, opt.pageSize, total)
  147. $table.parent().find('.pagination-row').html(page_str)
  148. $table.html(`<thead><tr>${head_str}</tr></thead><tbody>${body_str}</tbody>`)
  149. $tableBox.fadeIn(500,function (){
  150. $tableBox.find(`[data-bs-toggle="tooltip"]`).each(function (){
  151. new bootstrap.Tooltip(this)
  152. })
  153. })
  154. })
  155. }
  156. function formatPage(pageNum, pageSize, total) {
  157. const totalPages = Math.ceil(total / pageSize)
  158. // const startIndex = (pageNum - 1) * pageSize + 1
  159. // const endIndex = Math.min(pageNum * pageSize, total)
  160. let str = '<div class=" d-flex align-items-center justify-content-center justify-content-md-start dt-toolbar">'
  161. // 每页显示条数选择
  162. str += `<div><select class="form-select form-select-solid form-select-sm" onchange="IwbTableChangePageSize(this)">`
  163. ;[15, 25, 50, 100].forEach((size) => {
  164. str += `<option value="${size}" ${pageSize === size ? ' selected' : ''}>${size}</option>`
  165. })
  166. str += '</select></div>'
  167. // 显示记录信息
  168. str += `<div class="dt-info">当前第 ${pageNum}/${totalPages} 页 共 ${total} 条</div></div>`
  169. // 分页导航
  170. str += '<div class="d-flex align-items-center justify-content-center justify-content-md-end"><div class="dt-paging paging_simple_numbers"><nav aria-label="pagination"><ul class="pagination">'
  171. // 上一页按钮
  172. if (pageNum > 1) {
  173. str += '<li class="dt-paging-button page-item"><button class="page-link previous" onclick="IwbTableJumpPage(this,' + (pageNum - 1) + ')"><i class="previous"></i></button></li>'
  174. } else {
  175. str += '<li class="dt-paging-button page-item disabled"><button class="page-link previous"><i class="previous"></i></button></li>'
  176. }
  177. // 计算显示的页码范围
  178. let startPage = Math.max(1, pageNum - 2)
  179. let endPage = Math.min(totalPages, pageNum + 2)
  180. // 显示第一页和省略号
  181. if (startPage > 1) {
  182. str += '<li class="dt-paging-button page-item"><button class="page-link" onclick="IwbTableJumpPage(this,1)">1</button></li>'
  183. if (startPage > 2) {
  184. str += '<li class="dt-paging-button page-item disabled"><button class="page-link ellipsis" tabindex="-1">…</button></li>'
  185. }
  186. }
  187. // 显示页码
  188. for (let i = startPage; i <= endPage; i++) {
  189. if (i === pageNum) {
  190. str += `<li class="dt-paging-button page-item active"><button class="page-link" onclick="IwbTableJumpPage(this,${i})">${i}</button></li>`
  191. } else {
  192. str += `<li class="dt-paging-button page-item"><button class="page-link" onclick="IwbTableJumpPage(this,${i})">${i}</button></li>`
  193. }
  194. }
  195. // 显示最后一页和省略号
  196. if (endPage < totalPages) {
  197. if (endPage < totalPages - 1) {
  198. str += '<li class="dt-paging-button page-item disabled"><button class="page-link ellipsis" tabindex="-1">…</button></li>'
  199. }
  200. str += `<li class="dt-paging-button page-item"><button class="page-link" onclick="IwbTableJumpPage(this,${totalPages})">${totalPages}</button></li>`
  201. }
  202. // 下一页按钮
  203. if (pageNum < totalPages) {
  204. str += '<li class="dt-paging-button page-item"><button class="page-link next" onclick="IwbTableJumpPage(this,' + (pageNum + 1) + ')"><i class="next"></i></button></li>'
  205. } else {
  206. str += '<li class="dt-paging-button page-item disabled"><button class="page-link next"><i class="next"></i></button></li>'
  207. }
  208. str += '</ul></nav></div></div>'
  209. return str
  210. }
  211. ajaxTable(tableOptions)
  212. }
  213. function IwbTableSearch(that) {
  214. const $search = $(that).closest('form.search-box')
  215. const $table = $(that).closest('.table-box').find('.table')
  216. const search = $table.data('options').search || {}
  217. $search.find('.form-control,.form-select').each((i, el) => {
  218. const v = $(el).val()
  219. if (v.trim() !== '') {
  220. search[$(el).attr('name')] = v
  221. } else {
  222. delete search[$(el).attr('name')]
  223. }
  224. })
  225. IwbTable($table, { search: search, pageNum: 1 })
  226. }
  227. function IwbTableResetSearch(that) {
  228. const $search = $(that).closest('form.search-box')
  229. $search[0].reset()
  230. IwbTableSearch(that)
  231. }
  232. function IwbTableJumpPage(that, pageNum) {
  233. const $table = $(that).closest('.table-box').find('.table')
  234. IwbTable($table, { pageNum: pageNum })
  235. }
  236. function IwbTableChangePageSize(that) {
  237. const $table = $(that).closest('.table-box').find('.table')
  238. const pageSize = parseInt($(that).val())
  239. IwbTable($table, { pageSize: pageSize })
  240. }
  241. function AddModal(modal, callback) {
  242. const $modal = $(modal)
  243. $modal.find('.modal-header .modal-title span.prefix').html('添加')
  244. $modal.find('.modal-body .form-control').val('')
  245. callback && callback($modal)
  246. $modal.modal('show')
  247. }
  248. function EditModal(modal, callback) {
  249. const $modal = $(modal)
  250. $modal.find('.modal-header .modal-title span.prefix').html('修改')
  251. callback && callback($modal)
  252. $modal.modal('show')
  253. }
  254. function Msg(msg, type){
  255. const opts={
  256. text: msg,
  257. icon: type||"info",
  258. buttonsStyling: false,
  259. confirmButtonText: "确定",
  260. customClass: {
  261. confirmButton: "btn btn-primary"
  262. },
  263. toast:false
  264. }
  265. if(type==='success'){
  266. opts.toast=true
  267. opts.timer=3000
  268. opts.showConfirmButton=false
  269. }
  270. Swal.fire(opts)
  271. // toastr.options = {
  272. // "closeButton": false,
  273. // "debug": false,
  274. // "newestOnTop": false,
  275. // "progressBar": false,
  276. // "positionClass": "toastr-top-center",
  277. // "preventDuplicates": false,
  278. // "onclick": null,
  279. // "showDuration": "300",
  280. // "hideDuration": "1000",
  281. // "timeOut": "2000",
  282. // "extendedTimeOut": "1000",
  283. // "showEasing": "swing",
  284. // "hideEasing": "linear",
  285. // "showMethod": "fadeIn",
  286. // "hideMethod": "fadeOut"
  287. // };
  288. // if (type==='success'){
  289. // title ? toastr.success(msg,title):toastr.success(msg);
  290. // }else if (type==='error'){
  291. // toastr.options.timeOut = "0";
  292. // title ? toastr.error(msg,title):toastr.error(msg);
  293. // }else if(type==='warning'){
  294. // toastr.options.timeOut = "5000";
  295. // title ? toastr.warning(msg,title):toastr.warning(msg);
  296. // }else{
  297. // toastr.options.timeOut = "0";
  298. // title ? toastr.info(msg,title):toastr.info(msg);
  299. // }
  300. }
  301. function MsgSuccess(msg, title) {
  302. Msg(msg, 'success',title)
  303. }
  304. function MsgError(msg, title) {
  305. Msg(msg, 'error',title)
  306. }
  307. function MsgWarning(msg, title) {
  308. Msg(msg, 'warning',title)
  309. }
  310. function Confirm(title, callback) {
  311. const opts={
  312. text: title,
  313. icon: "info",
  314. buttonsStyling: false,
  315. showCancelButton: true,
  316. showConfirmButton: true,
  317. cancelButtonText: "取消",
  318. confirmButtonText: "确定",
  319. customClass: {
  320. cancelButton: "btn btn-light",
  321. confirmButton: "btn btn-primary"
  322. },
  323. toast:false
  324. }
  325. Swal.fire(opts).then((result)=>{
  326. console.log("CONFIRM",result)
  327. if(result.isConfirmed){
  328. callback && callback()
  329. }
  330. });
  331. }
  332. function ConfirmUrl(title, url, table) {
  333. Confirm(title,function () {
  334. IwbAjax({
  335. url: url,
  336. table: table || '#table',
  337. })
  338. })
  339. }
  340. function ChangeHeadMenu(menu) {
  341. $('#header_menu .menu-item').removeClass('here')
  342. $('#header_menu ' + menu).addClass('here')
  343. }
  344. function DownloadFile(url, fileName) {
  345. fetch(url,{
  346. method:'POST'
  347. }).then((response) => {
  348. if (!response.ok) {
  349. return response.json().then((err) => {
  350. throw new Error(err.message || '下载失败')
  351. })
  352. }
  353. return response.blob()
  354. })
  355. .then((blob) => {
  356. const downloadUrl = window.URL.createObjectURL(blob)
  357. const a = document.createElement('a')
  358. a.href = downloadUrl
  359. a.download = fileName
  360. document.body.appendChild(a)
  361. a.click()
  362. window.URL.revokeObjectURL(downloadUrl)
  363. document.body.removeChild(a)
  364. })
  365. .catch((error) => {
  366. MsgError(error.message)
  367. })
  368. }
  369. // 添加字符串序列化方法
  370. String.prototype.format = function () {
  371. let args = arguments
  372. return this.replace(/{(\d+)}/g, function (match, index) {
  373. return typeof args[index] != 'undefined' ? args[index] : match
  374. })
  375. }