UNPKG

xq-admin-page

Version:

基于Bootstrap5的表格列表,主要有后台管理的列表与搜索、添加、编辑、删除、导出等功能,同时列表支持自定义排序、分页大小等。

731 lines (707 loc) 23.7 kB
/*! * xq-admin-page v1.0.10 (https://xqkeji.cn/demo/xq-admin-page) * Author xqkeji.cn * LICENSE SSPL-1.0 * Copyright 2024 xqkeji.cn */ 'use strict'; const xqUtil = require('xq-util'); const xqConfirm = require('xq-confirm'); const xqMask = require('xq-mask'); const sortable = require('xq-html5sortable'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } const xqConfirm__default = /*#__PURE__*/_interopDefaultCompat(xqConfirm); const sortable__default = /*#__PURE__*/_interopDefaultCompat(sortable); const DEFAULT_OPTIONS = { tableClass: ".xq-table", orderClass: ".xq-order", orderAscClass: ".xq-order-asc", orderDescClass: ".xq-order-desc", dragClass: ".xq-drag", checkAllClass: ".xq-table .xq-check-all", addBtnClass: ".xq-table .xq-add", editBtnClass: ".xq-edit", viewBtnClass: ".xq-view", deleteBtnClass: ".xq-delete", exportBtnClass: ".xq-export", copyBtnClass: ".xq-copy", batchBtnClass: ".xq-batch", backPageClass: ".xq-backpage", authTableClass: ".xq-auth-table", authCheckTableClass: ".xq-auth-check-table", authCheckRowClass: ".xq-auth-check-row", captchaClass: ".xq-captcha", pageSizeId: "#xq-page-size" }; let tablelistOptions = {}; const setOption = (options = {}) => { tablelistOptions = Object.assign({}, DEFAULT_OPTIONS); if (options) { for (const option in options) { if (Object.hasOwn(tablelistOptions, option)) { treegridOptions[option] = options[option]; } } } }; const getOption = (key) => { if (key in tablelistOptions) { return tablelistOptions[key]; } const tableClass = tablelistOptions["tableClass"]; const table = document.querySelector(tableClass); if (table.hasAttribute(key)) { return String(table.getAttribute(key)); } return ""; }; let table; const getTable = () => { if (table) { return table; } else { const tableClass = getOption("tableClass"); const container = document.querySelector(tableClass); table = container; return table; } }; const bindAdd = () => { const addBtnClass = getOption("addBtnClass"); const add_btn = document.querySelector(addBtnClass); if (add_btn) { add_btn.addEventListener("click", (event) => { const target = event.currentTarget; const table = xqUtil.parents(target, "table")[0]; const pid = table.getAttribute("pid"); let url = target.getAttribute("xq-url"); if (!url) { url = window.location.href; if (url.includes(".html") || url.includes("localhost")) { url = url.slice(0, Math.max(0, url.lastIndexOf("/"))); url += "/add.html"; } else { if (pid) { url = url.replace("/" + pid, ""); } url = url.slice(0, Math.max(0, url.lastIndexOf("/"))); url += pid ? "/add/" + pid : "/add"; } } window.location.href = url; }); } }; const bindEdit = (element) => { const editBtnClass = getOption("editBtnClass"); const edit_btn = element.querySelector(editBtnClass); if (edit_btn) { edit_btn.addEventListener("click", (event) => { const target = event.currentTarget; const table = getTable(); const pid = table?.getAttribute("pid"); let id = element.getAttribute("id"); id = id.replace("xq_", ""); if (id) { let url = target.getAttribute("xq-url"); if (!url) { url = window.location.href; if (url.includes(".html") || url.endsWith("/")) { url = url.slice(0, Math.max(0, url.lastIndexOf("/"))); url = url + "/edit.html?id=" + id; } else { if (pid) { url = url.replace("/" + pid, ""); } url = url.slice(0, Math.max(0, url.lastIndexOf("/"))); url = url + "/edit/" + id; } } window.location.href = url; } else { xqConfirm__default({ content: "\u627E\u4E0D\u5230tr\u7684id\u5C5E\u6027\uFF01" }); } }); } }; const bindView = (element) => { const viewBtnClass = getOption("viewBtnClass"); const view_btn = element.querySelector(viewBtnClass); if (view_btn) { view_btn.addEventListener("click", (event) => { const target = event.currentTarget; const table = getTable(); const pid = table?.getAttribute("pid"); let id = element.getAttribute("id"); id = id.replace("xq_", ""); if (id) { let url = target.getAttribute("xq-url"); if (!url) { url = window.location.href; if (url.includes(".html") || url.endsWith("/")) { url = url.slice(0, Math.max(0, url.lastIndexOf("/"))); url = url + "/view.html?id=" + id; } else { if (pid) { url = url.replace("/" + pid, ""); } url = url.slice(0, Math.max(0, url.lastIndexOf("/"))); url = url + "/view/" + id; } } window.location.href = url; } else { xqConfirm__default({ content: "\u627E\u4E0D\u5230tr\u7684id\u5C5E\u6027\uFF01" }); } }); } }; const copy = (text) => { const el = document.createElement("input"); el.setAttribute("value", text); document.body.append(el); el.select(); document.execCommand("copy"); el.remove(); }; const bindCopy = (element) => { const copyBtnClass = getOption("copyBtnClass"); const copy_btn = element.querySelector(copyBtnClass); if (copy_btn) { copy_btn.addEventListener("click", () => { let id = element.getAttribute("id"); id = id.replace("xq_", ""); if (id) { copy(id); xqConfirm__default({ content: "\u5DF2\u7ECF\u5C06id\u590D\u5236\u5230\u7C98\u8D34\u677F" }); } }); } }; const bindDelete = (element) => { const deleteBtnClass = getOption("deleteBtnClass"); const delete_btn = element.querySelector(deleteBtnClass); if (delete_btn) { delete_btn.addEventListener("click", (event) => { const target = event.currentTarget; const table = getTable(); const pid = table?.getAttribute("pid"); let id = element.getAttribute("id"); id = id.replace("xq_", ""); if (id) { let url = target.getAttribute("xq-url"); if (!url) { url = window.location.href; if (pid) { url = url.replace("/" + pid, ""); } url = url.slice(0, Math.max(0, url.lastIndexOf("/"))); url += "/delete"; } xqConfirm__default({ content: "\u786E\u5B9A\u8981\u5220\u9664\u5417\uFF1F", confirm() { fetch(url, { body: JSON.stringify({ id }), headers: { "content-type": "application/json" }, method: "POST" }).then(async (response) => { return response.json(); }).then((data) => { xqConfirm__default({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access content: data.message, confirm() { if (data.code === 200) { window.location.reload(); } } }); }); } }); } else { xqConfirm__default({ content: "\u627E\u4E0D\u5230tr\u7684id\u5C5E\u6027\uFF01" }); } }); } }; const bindChange = (element) => { const table = getTable(); let id = element.getAttribute("id"); id = id?.replace("xq_", ""); const textes = element.querySelectorAll("input,select"); for (const text of textes) { const elementId = text.getAttribute("id"); if (elementId !== "id" && elementId !== "check_all") { text.addEventListener("change", (event) => { const targetElement = event.currentTarget; let value = null; value = targetElement.type === "checkbox" ? targetElement.checked : targetElement.value; const target_id = targetElement.getAttribute("id"); const field = target_id?.slice(0, Math.max(0, target_id.indexOf("_"))); const pid = table?.getAttribute("pid"); let xq_url = table?.getAttribute("xq-url"); const data = { id, field, value }; if (!xq_url) { let url = window.location.href; if (pid) { url = url.replace("/" + pid, ""); } url = url.slice(0, Math.max(0, url.lastIndexOf("/"))); url += "/change"; xq_url = url; } else { xq_url += "/change"; } fetch(xq_url, { body: JSON.stringify(data), headers: { "content-type": "application/json" }, method: "POST" }).then(async (response) => { return response.json(); }).then((data2) => { console.log(data2); }); }); } } }; const bindBatch = () => { const batchBtnClass = getOption("batchBtnClass"); const batch_btns = document.querySelectorAll(batchBtnClass); if (batch_btns) { for (const batch_btn of batch_btns) { batch_btn.addEventListener("click", (event) => { const target = event.currentTarget; const table = xqUtil.parents(target, "table")[0]; const pid = table.getAttribute("pid"); const formObject = xqUtil.parents(target, "form")[0]; const action = target.getAttribute("name"); if (action) { let url = target.getAttribute("xq-url"); if (!url) { url = window.location.href; if (pid) { url = url.replace("/" + pid, ""); } url = url.slice(0, Math.max(0, url.lastIndexOf("/"))); url = url + "/" + action; } const formData = xqUtil.jsonFormData(formObject); const jsonData = JSON.stringify(formData); if (jsonData !== "{}") { xqConfirm__default({ content: "\u786E\u5B9A\u8981\u8FDB\u884C\u6279\u91CF\u64CD\u4F5C\u5417\uFF1F", confirm() { xqMask.xqMask(); fetch(url, { body: jsonData, headers: { "content-type": "application/json" }, method: "POST" }).then(async (response) => { return response.json(); }).then((data) => { xqMask.xqUnMask(); xqConfirm__default({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access content: data.message, confirm() { if (data.code === 200) { window.location.reload(); } } }); }); } }); } else { xqConfirm__default({ content: "\u6CA1\u6709\u9009\u62E9\u64CD\u4F5C\u6570\u636E." }); } } }); } } }; const bindDrag = () => { const dragClass = getOption("dragClass"); const dragger_table = document.querySelector(dragClass); if (dragger_table) { let tbody = dragger_table.querySelector("tbody"); if (tbody) { const firstTr = tbody.querySelector("tbody>tr:first-child"); const firstId = firstTr.getAttribute("id"); const firstTd = firstTr.querySelector("td:first-child"); firstTd?.classList.add("xq-move"); tbody.setAttribute("id", "tbody_" + firstId); const trs = tbody.querySelectorAll("tbody>tr:not(:first-child)"); for (const tr of trs) { const td = tr.querySelector("td:first-child"); td?.classList.add("xq-move"); const innerbody = document.createElement("tbody"); innerbody.append(tr); const trId = tr.getAttribute("id"); innerbody.setAttribute("id", "tbody_" + trId); tbody.after(innerbody); tbody = innerbody; } } sortable__default(dragClass, { items: "tbody", handle: ".xq-move", placeholder: '<tbody><tr><td colspan="99">&nbsp;</td></tr><tbody>' }); dragger_table.addEventListener("sortstop", () => { const rows = []; let rowIndex = 0; const tbodies = dragger_table.querySelectorAll("tbody"); for (const tbody2 of tbodies) { rowIndex++; const tr = tbody2.querySelector("tr:first-child"); const trId = tr?.getAttribute("id"); const row = { id: trId, ordernum: rowIndex }; rows.push(row); } const pid = dragger_table.getAttribute("pid"); let url = dragger_table.getAttribute("xq-url"); if (!url) { url = window.location.href; if (pid) { url = url.replace("/" + pid, ""); } url = url.slice(0, Math.max(0, url.lastIndexOf("/"))); url += "/b-order"; } fetch(url, { body: JSON.stringify(rows), headers: { "content-type": "application/json" }, method: "POST" }).then(async (response) => { return response.json(); }).then((data) => { if (data.code === 200) ; }); }); } }; const bindCheckAll = () => { const checkAllClass = getOption("checkAllClass"); const check_all = document.querySelector(checkAllClass); if (check_all) { check_all.addEventListener("click", (event) => { const target = event.currentTarget; const { checked } = target; const table = getTable(); const checks = table?.querySelectorAll("tr > td:first-child > input[type=checkbox]"); for (const check of checks) { if (check === target) { continue; } if (checked) { check.checked = true; } else { check.checked = false; } } }); } }; const bindBack = () => { let referrer; let backpage; let topReferrer; const backPageClass = getOption("backPageClass"); const els = document.querySelectorAll(backPageClass); if (els) { referrer = document.referrer; topReferrer = null; if (window.top !== window) { try { topReferrer = window.top?.location.href; } catch (e) { } } if (referrer && referrer !== document.location.href && referrer !== topReferrer) { sessionStorage.setItem(document.location.href, referrer); backpage = referrer; } else { const item = sessionStorage.getItem(document.location.href); if (item) { backpage = item.toString(); } } for (const el of els) { el.addEventListener("click", (event) => { event.preventDefault(); if (backpage) { document.location.href = backpage; } else { xqConfirm__default({ content: "\u7CFB\u7EDF\u65E0\u6CD5\u81EA\u52A8\u8FD4\u56DE\u4E0A\u4E00\u4E2A\u9875\u9762." }); } }); } } }; const bindAuth = () => { const authTableClass = getOption("authTableClass"); const auth_tables = document.querySelectorAll(authTableClass); if (auth_tables) { for (const auth_table of auth_tables) { const authCheckTableClass = getOption("authCheckTableClass"); const check_table = auth_table.querySelector(authCheckTableClass); if (check_table) { const table_check_all = check_table.querySelector("input[type=checkbox]"); if (table_check_all) { table_check_all.addEventListener("click", (event) => { const target = event.currentTarget; const { checked } = target; const other_checks = auth_table.querySelectorAll("input[type=checkbox]:not(" + authCheckTableClass + ">input[type=checkbox])"); for (const other_check of other_checks) { if (checked) { other_check.checked = true; } else { other_check.checked = false; } } }); } } const authCheckRowClass = getOption("authCheckRowClass"); const check_rows = auth_table.querySelectorAll(authCheckRowClass); for (const check_row of check_rows) { const row_check_all = check_row.querySelector("input[type=checkbox]"); if (row_check_all) { row_check_all.addEventListener("click", (event) => { const target = event.currentTarget; const { checked } = target; const next_td = xqUtil.next(check_row, "td")[0]; const next_td_checks = next_td.querySelectorAll("input[type=checkbox]"); for (const next_td_check of next_td_checks) { if (checked) { next_td_check.checked = true; } else { next_td_check.checked = false; } } }); } } } } }; const bindFileinput = () => { const fileinputs = document.querySelectorAll(".xq-fileinput"); if (fileinputs && typeof jQuery !== void 0) { let config; for (const fileinput of fileinputs) { config = fileinput.hasAttribute("xq-config") ? fileinput.getAttribute("xq-config") : "{}"; $(fileinput).fileinput(JSON.parse(config)).on("filedeleted", (event) => { const target = event.currentTarget; updateFileInputValue(target); }).on("fileuploaded", (event) => { const target = event.currentTarget; updateFileInputValue(target); }).on("filesorted", (event) => { const target = event.currentTarget; updateFileInputValue(target); }); updateFileInputValue(fileinput); } } }; const updateFileInputValue = (fileinput) => { const fileinput_id = fileinput.getAttribute("id"); const fileinput_value_id = fileinput_id.replace("-xq-fileinput", ""); const fileinput_value = document.querySelector("#" + fileinput_value_id); const fileinput_preview = $(fileinput).fileinput("getPreview"); fileinput_value?.setAttribute("value", JSON.stringify(fileinput_preview)); const fileCount = $(fileinput).fileinput("getFilesCount", true); const configStr = fileinput.getAttribute("xq-config"); const config = JSON.parse(configStr); if (config !== null && "required" in config) { if (fileCount > 0) { fileinput.removeAttribute("required"); } else { fileinput.setAttribute("required", "true"); } if ("content" in fileinput_preview) { const { content } = fileinput_preview; if (content.length === 0) { fileinput.setCustomValidity("\u9700\u8981\u81F3\u5C11\u4E0A\u4F20\u4E00\u4E2A\u6587\u4EF6"); } else { fileinput.setCustomValidity(""); } } } }; const bindCaptcha = () => { const captchaClass = getOption("captchaClass"); const captcha = document.querySelector(captchaClass); if (captcha) { captcha.addEventListener("click", (event) => { const target = event.currentTarget; let src = target.getAttribute("src"); if (src.includes("?")) { src = src.slice(0, Math.max(0, src.lastIndexOf("?"))); src = src + "?xq-r=" + Math.random().toString(); } else { src = src + "?xq-r=" + Math.random().toString(); } target.setAttribute("src", src); }); } }; const bindOrder = () => { const table = getTable(); const thead = table.querySelector("thead"); const orderClass = getOption("orderClass"); const orderAscClass = getOption("orderAscClass"); const orderDescClass = getOption("orderDescClass"); const orderClassStr = orderClass.slice(1); const orderAscClassStr = orderAscClass.slice(1); const orderDescClassStr = orderDescClass.slice(1); const thes = thead.querySelectorAll(orderClass); const urlParams = new URLSearchParams(window.location.search); if (urlParams.has("orderby")) { const field = urlParams.get("orderby"); const ordertype = urlParams.get("ordertype") ?? 1; const thsort = thead?.querySelector('th[field="' + field + '"]'); if (thsort) { thsort.classList.remove(orderClassStr); if (ordertype == 1) { thsort.classList.add(orderAscClassStr); } else { thsort.classList.add(orderDescClassStr); } } } for (const th of thes) { th.addEventListener("click", (event) => { let field = th.getAttribute("field"); let type = 0; if (th.classList.contains(orderClassStr)) { th.classList.remove(orderClassStr); th.classList.add(orderAscClassStr); type = 1; } else if (th.classList.contains(orderAscClassStr)) { th.classList.remove(orderAscClassStr); th.classList.add(orderDescClassStr); type = 2; } else if (th.classList.contains(orderDescClassStr)) { th.classList.remove(orderDescClassStr); th.classList.add(orderAscClassStr); type = 1; } let url = window.location.href; url = xqUtil.setUrlParam(url, "orderby", field); switch (type) { case 1: url = xqUtil.setUrlParam(url, "ordertype", 1); break; case 2: url = xqUtil.setUrlParam(url, "ordertype", 2); break; default: url = xqUtil.setUrlParam(url, "ordertype", 1); break; } window.location.href = url; }); } }; const bindPageSize = () => { const pageSizeId = getOption("pageSizeId"); const pageSize = document.querySelector(pageSizeId); if (pageSize) { const urlParams = new URLSearchParams(window.location.search); if (urlParams.has("page-size")) { const param = urlParams.get("page-size"); pageSize.value = param ?? "10"; } pageSize.addEventListener("change", (event) => { const target = event.currentTarget; const size = target.value; let url = window.location.href; url = xqUtil.setUrlParam(url, "page-size", size); window.location.href = url; }); } }; const bindExport = () => { const table = getTable(); const exportBtnClass = getOption("exportBtnClass"); const export_btn = table.querySelector(exportBtnClass); if (export_btn) { export_btn.addEventListener("click", () => { const url = new URL(window.location.href); let actionUrl = url.origin + url.pathname; actionUrl = actionUrl.slice(0, Math.max(0, actionUrl.lastIndexOf("/"))); actionUrl = actionUrl + "/export"; const urlParams = new URLSearchParams(window.location.search); if (urlParams.size > 0) { actionUrl = actionUrl + "?" + urlParams.toString(); } const id = "xq-export-form"; let export_form = document.querySelector("#" + id); if (export_form) { export_form.setAttribute("action", actionUrl); } else { const export_form_str = '<form action="' + actionUrl + '" method="post" style="display: none;" id="' + id + '"></form>'; xqUtil.append(document.body, export_form_str); export_form = document.querySelector("#" + id); } export_form.submit(); }); } }; const init = () => { const table = getTable(); if (table) { bindOrder(); bindPageSize(); const tres = table.querySelectorAll("tr"); for (const tr of tres) { bindView(tr); bindEdit(tr); bindDelete(tr); bindChange(tr); bindCopy(tr); } bindCheckAll(); bindAdd(); bindBatch(); bindExport(); bindDrag(); } bindBack(); bindAuth(); bindFileinput(); bindCaptcha(); }; const xqTableList = (options = {}) => { setOption(options); init(); }; xqUtil.domReady(() => { xqTableList(); });