diff --git a/src/api/admin/sys-public-param.js b/src/api/admin/sys-public-param.js
new file mode 100644
index 0000000000000000000000000000000000000000..5408ddbc9e5c37bae47f4247834d64a6963e4a7e
--- /dev/null
+++ b/src/api/admin/sys-public-param.js
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+ return request({
+ url: '/admin/param/page',
+ method: 'get',
+ params: query
+ })
+}
+
+export function addObj(obj) {
+ return request({
+ url: '/admin/param',
+ method: 'post',
+ data: obj
+ })
+}
+
+export function getObj(key) {
+ return request({
+ url: '/admin/param/publicValue/' + key,
+ method: 'get'
+ })
+}
+
+export function delObj(id) {
+ return request({
+ url: '/admin/param/' + id,
+ method: 'delete'
+ })
+}
+
+export function putObj(obj) {
+ return request({
+ url: '/admin/param',
+ method: 'put',
+ data: obj
+ })
+}
+
+export function refreshCache() {
+ return request({
+ url: '/admin/param/sync',
+ method: 'put'
+ })
+}
diff --git a/src/const/crud/admin/sys-public-param.js b/src/const/crud/admin/sys-public-param.js
new file mode 100644
index 0000000000000000000000000000000000000000..f8e8b2e39141b7b8b3161f567fde11e87670a952
--- /dev/null
+++ b/src/const/crud/admin/sys-public-param.js
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import { rule } from "@/util/validateRules";
+
+import {getObj} from '@/api/admin/sys-public-param'
+
+
+var validateParam = (rule, value, callback) => {
+ getObj(value).then(response => {
+ if (window.boxType === 'edit') callback()
+ const result = response.data.data
+ if (result !== null) {
+ callback(new Error('参数键已经存在'))
+ } else {
+ callback()
+ }
+ })
+}
+
+export const tableOption = {
+ border: true,
+ index: true,
+ indexLabel: '序号',
+ stripe: true,
+ menuAlign: 'center',
+ searchMenuSpan: 6,
+ column: [
+ {
+ label: '名称',
+ search: true,
+ prop: 'publicName',
+ rules: [
+ { required: true, message: '请输名称', trigger: 'blur' },
+ { max: 30, message: '长度在 30 个字符', trigger: 'blur' },
+ { validator: rule.validatorNameCn, trigger: 'blur'}
+ ]
+ },
+ {
+ label: '键',
+ prop: 'publicKey',
+ rules: [
+ { required: true, message: '请输入键', trigger: 'blur' },
+ { validator: rule.validatorKey, trigger: 'blur'},
+ { validator: validateParam, trigger: 'blur'},
+ ]
+
+ },
+ {
+ label: '值',
+ overHidden: true,
+ prop: 'publicValue',
+ rules: [
+ { required: true, message: '请输入值', trigger: 'blur' }
+ ]
+ },
+ {
+ label: '编码',
+ prop: 'validateCode'
+ },
+ {
+ label: '类型',
+ prop: 'systemFlag',
+ type: 'select',
+ dicUrl: '/admin/dict/type/dict_type',
+ rules: [{
+ required: true,
+ message: '请输入类型',
+ trigger: 'blur'
+ }],
+ search: true
+ },
+ {
+ label: '状态',
+ prop: 'status',
+ width: 80,
+ type: 'select',
+ dicUrl: '/admin/dict/type/status_type',
+ rules: [
+ { required: true, message: '请输入值', trigger: 'blur' }
+ ]
+ },
+ {
+ label: '类型',
+ prop: 'publicType',
+ width: 80,
+ type: 'select',
+ dicUrl: '/admin/dict/type/param_type',
+ rules: [{
+ required: true,
+ message: '请选择类型',
+ trigger: 'blur'
+ }]
+ }
+ // 省略 ...
+ ]
+}
diff --git a/src/util/util.js b/src/util/util.js
index f8b61cb6a89e7ecaeda2810def67ab28b551c65b..1e2352b06e4b1c829f65fd47fc2e5da4be877616 100644
--- a/src/util/util.js
+++ b/src/util/util.js
@@ -1,328 +1,328 @@
-import {validatenull} from "./validate";
-import request from "@/router/axios";
-import * as CryptoJS from "crypto-js";
-
-// 表单序列化
-export const serialize = data => {
- let list = [];
- Object.keys(data).forEach(ele => {
- list.push(`${ele}=${data[ele]}`);
- });
- return list.join("&");
-};
-export const getObjType = obj => {
- var toString = Object.prototype.toString;
- var map = {
- "[object Boolean]": "boolean",
- "[object Number]": "number",
- "[object String]": "string",
- "[object Function]": "function",
- "[object Array]": "array",
- "[object Date]": "date",
- "[object RegExp]": "regExp",
- "[object Undefined]": "undefined",
- "[object Null]": "null",
- "[object Object]": "object"
- };
- if (obj instanceof Element) {
- return "element";
- }
- return map[toString.call(obj)];
-};
-/**
- * 对象深拷贝
- */
-export const deepClone = data => {
- var type = getObjType(data);
- var obj;
- if (type === "array") {
- obj = [];
- } else if (type === "object") {
- obj = {};
- } else {
- // 不再具有下一层次
- return data;
- }
- if (type === "array") {
- for (var i = 0, len = data.length; i < len; i++) {
- obj.push(deepClone(data[i]));
- }
- } else if (type === "object") {
- for (var key in data) {
- obj[key] = deepClone(data[key]);
- }
- }
- return obj;
-};
-/**
- * 判断路由是否相等
- */
-export const diff = (obj1, obj2) => {
- delete obj1.close;
- var o1 = obj1 instanceof Object;
- var o2 = obj2 instanceof Object;
- if (!o1 || !o2) {
- /* 判断不是对象 */
- return obj1 === obj2;
- }
-
- if (Object.keys(obj1).length !== Object.keys(obj2).length) {
- return false;
- // Object.keys() 返回一个由对象的自身可枚举属性(key值)组成的数组,例如:数组返回下表:let arr = ["a", "b", "c"];console.log(Object.keys(arr))->0,1,2;
- }
-
- for (var attr in obj1) {
- var t1 = obj1[attr] instanceof Object;
- var t2 = obj2[attr] instanceof Object;
- if (t1 && t2) {
- return diff(obj1[attr], obj2[attr]);
- } else if (obj1[attr] !== obj2[attr]) {
- return false;
- }
- }
- return true;
-};
-/**
- * 设置灰度模式
- */
-export const toggleGrayMode = status => {
- if (status) {
- document.body.className = document.body.className + " grayMode";
- } else {
- document.body.className = document.body.className.replace(" grayMode", "");
- }
-};
-/**
- * 设置主题
- */
-export const setTheme = name => {
- document.body.className = name;
-};
-
-/**
- *加密处理
- */
-export const encryption = params => {
- let {data, type, param, key} = params;
- const result = JSON.parse(JSON.stringify(data));
- if (type === "Base64") {
- param.forEach(ele => {
- result[ele] = btoa(result[ele]);
- });
- } else {
- param.forEach(ele => {
- var data = result[ele];
- key = CryptoJS.enc.Latin1.parse(key);
- var iv = key;
- // 加密
- var encrypted = CryptoJS.AES.encrypt(data, key, {
- iv: iv,
- mode: CryptoJS.mode.CFB,
- padding: CryptoJS.pad.NoPadding
- });
- result[ele] = encrypted.toString();
- });
- }
- return result;
-};
-/**
- * 浏览器判断是否全屏
- */
-export const fullscreenToggel = () => {
- if (fullscreenEnable()) {
- exitFullScreen();
- } else {
- reqFullScreen();
- }
-};
-/**
- * esc监听全屏
- */
-export const listenfullscreen = callback => {
- function listen() {
- callback();
- }
-
- document.addEventListener("fullscreenchange", function () {
- listen();
- });
- document.addEventListener("mozfullscreenchange", function () {
- listen();
- });
- document.addEventListener("webkitfullscreenchange", function () {
- listen();
- });
- document.addEventListener("msfullscreenchange", function () {
- listen();
- });
-};
-/**
- * 浏览器判断是否全屏
- */
-export const fullscreenEnable = () => {
- return (
- document.isFullScreen ||
- document.mozIsFullScreen ||
- document.webkitIsFullScreen
- );
-};
-
-/**
- * 浏览器全屏
- */
-export const reqFullScreen = () => {
- if (document.documentElement.requestFullScreen) {
- document.documentElement.requestFullScreen();
- } else if (document.documentElement.webkitRequestFullScreen) {
- document.documentElement.webkitRequestFullScreen();
- } else if (document.documentElement.mozRequestFullScreen) {
- document.documentElement.mozRequestFullScreen();
- }
-};
-/**
- * 浏览器退出全屏
- */
-export const exitFullScreen = () => {
- if (document.documentElement.requestFullScreen) {
- document.exitFullScreen();
- } else if (document.documentElement.webkitRequestFullScreen) {
- document.webkitCancelFullScreen();
- } else if (document.documentElement.mozRequestFullScreen) {
- document.mozCancelFullScreen();
- }
-};
-/**
- * 递归寻找子类的父类
- */
-
-export const findParent = (menu, id) => {
- for (let i = 0; i < menu.length; i++) {
- if (menu[i].children.length != 0) {
- for (let j = 0; j < menu[i].children.length; j++) {
- if (menu[i].children[j].id == id) {
- return menu[i];
- } else {
- if (menu[i].children[j].children.length != 0) {
- return findParent(menu[i].children[j].children, id);
- }
- }
- }
- }
- }
-};
-
-/**
- * 动态插入css
- */
-
-export const loadStyle = url => {
- const link = document.createElement("link");
- link.type = "text/css";
- link.rel = "stylesheet";
- link.href = url;
- const head = document.getElementsByTagName("head")[0];
- head.appendChild(link);
-};
-/**
- * 判断路由是否相等
- */
-export const isObjectValueEqual = (a, b) => {
- let result = true;
- Object.keys(a).forEach(ele => {
- const type = typeof a[ele];
- if (type === "string" && a[ele] !== b[ele]) result = false;
- else if (
- type === "object" &&
- JSON.stringify(a[ele]) !== JSON.stringify(b[ele])
- )
- result = false;
- });
- return result;
-};
-/**
- * 根据字典的value显示label
- */
-export const findByvalue = (dic, value) => {
- let result = "";
- if (validatenull(dic)) return value;
- if (
- typeof value === "string" ||
- typeof value === "number" ||
- typeof value === "boolean"
- ) {
- let index = 0;
- index = findArray(dic, value);
- if (index != -1) {
- result = dic[index].label;
- } else {
- result = value;
- }
- } else if (value instanceof Array) {
- result = [];
- let index = 0;
- value.forEach(ele => {
- index = findArray(dic, ele);
- if (index != -1) {
- result.push(dic[index].label);
- } else {
- result.push(value);
- }
- });
- result = result.toString();
- }
- return result;
-};
-/**
- * 根据字典的value查找对应的index
- */
-export const findArray = (dic, value) => {
- for (let i = 0; i < dic.length; i++) {
- if (dic[i].value == value) {
- return i;
- }
- }
- return -1;
-};
-/**
- * 生成随机len位数字
- */
-export const randomLenNum = (len, date) => {
- let random = "";
- random = Math.ceil(Math.random() * 100000000000000)
- .toString()
- .substr(0, len || 4);
- if (date) random = random + Date.now();
- return random;
-};
-
-/**
- *
- * @param url 目标下载接口
- * @param query 查询参数
- * @param fileName 文件名称
- * @returns {*}
- */
-export function downBlobFile(url, query, fileName) {
- return request({
- url: url,
- method: "get",
- responseType: "blob",
- params: query
- }).then(response => {
- // 处理返回的文件流
- const blob = response.data;
- if (blob && blob.size === 0) {
- this.$notify.error("内容为空,无法下载");
- return;
- }
- const link = document.createElement("a");
- link.href = URL.createObjectURL(blob);
- link.download = fileName;
- document.body.appendChild(link);
- link.click();
- window.setTimeout(function () {
- URL.revokeObjectURL(blob);
- document.body.removeChild(link);
- }, 0);
- });
-}
+import {validatenull} from "./validate";
+import request from "@/router/axios";
+import * as CryptoJS from "crypto-js";
+
+// 表单序列化
+export const serialize = data => {
+ let list = [];
+ Object.keys(data).forEach(ele => {
+ list.push(`${ele}=${data[ele]}`);
+ });
+ return list.join("&");
+};
+export const getObjType = obj => {
+ var toString = Object.prototype.toString;
+ var map = {
+ "[object Boolean]": "boolean",
+ "[object Number]": "number",
+ "[object String]": "string",
+ "[object Function]": "function",
+ "[object Array]": "array",
+ "[object Date]": "date",
+ "[object RegExp]": "regExp",
+ "[object Undefined]": "undefined",
+ "[object Null]": "null",
+ "[object Object]": "object"
+ };
+ if (obj instanceof Element) {
+ return "element";
+ }
+ return map[toString.call(obj)];
+};
+/**
+ * 对象深拷贝
+ */
+export const deepClone = data => {
+ var type = getObjType(data);
+ var obj;
+ if (type === "array") {
+ obj = [];
+ } else if (type === "object") {
+ obj = {};
+ } else {
+ // 不再具有下一层次
+ return data;
+ }
+ if (type === "array") {
+ for (var i = 0, len = data.length; i < len; i++) {
+ obj.push(deepClone(data[i]));
+ }
+ } else if (type === "object") {
+ for (var key in data) {
+ obj[key] = deepClone(data[key]);
+ }
+ }
+ return obj;
+};
+/**
+ * 判断路由是否相等
+ */
+export const diff = (obj1, obj2) => {
+ delete obj1.close;
+ var o1 = obj1 instanceof Object;
+ var o2 = obj2 instanceof Object;
+ if (!o1 || !o2) {
+ /* 判断不是对象 */
+ return obj1 === obj2;
+ }
+
+ if (Object.keys(obj1).length !== Object.keys(obj2).length) {
+ return false;
+ // Object.keys() 返回一个由对象的自身可枚举属性(key值)组成的数组,例如:数组返回下表:let arr = ["a", "b", "c"];console.log(Object.keys(arr))->0,1,2;
+ }
+
+ for (var attr in obj1) {
+ var t1 = obj1[attr] instanceof Object;
+ var t2 = obj2[attr] instanceof Object;
+ if (t1 && t2) {
+ return diff(obj1[attr], obj2[attr]);
+ } else if (obj1[attr] !== obj2[attr]) {
+ return false;
+ }
+ }
+ return true;
+};
+/**
+ * 设置灰度模式
+ */
+export const toggleGrayMode = status => {
+ if (status) {
+ document.body.className = document.body.className + " grayMode";
+ } else {
+ document.body.className = document.body.className.replace(" grayMode", "");
+ }
+};
+/**
+ * 设置主题
+ */
+export const setTheme = name => {
+ document.body.className = name;
+};
+
+/**
+ *加密处理
+ */
+export const encryption = params => {
+ let {data, type, param, key} = params;
+ const result = JSON.parse(JSON.stringify(data));
+ if (type === "Base64") {
+ param.forEach(ele => {
+ result[ele] = btoa(result[ele]);
+ });
+ } else {
+ param.forEach(ele => {
+ var data = result[ele];
+ key = CryptoJS.enc.Latin1.parse(key);
+ var iv = key;
+ // 加密
+ var encrypted = CryptoJS.AES.encrypt(data, key, {
+ iv: iv,
+ mode: CryptoJS.mode.CFB,
+ padding: CryptoJS.pad.NoPadding
+ });
+ result[ele] = encrypted.toString();
+ });
+ }
+ return result;
+};
+/**
+ * 浏览器判断是否全屏
+ */
+export const fullscreenToggel = () => {
+ if (fullscreenEnable()) {
+ exitFullScreen();
+ } else {
+ reqFullScreen();
+ }
+};
+/**
+ * esc监听全屏
+ */
+export const listenfullscreen = callback => {
+ function listen() {
+ callback();
+ }
+
+ document.addEventListener("fullscreenchange", function () {
+ listen();
+ });
+ document.addEventListener("mozfullscreenchange", function () {
+ listen();
+ });
+ document.addEventListener("webkitfullscreenchange", function () {
+ listen();
+ });
+ document.addEventListener("msfullscreenchange", function () {
+ listen();
+ });
+};
+/**
+ * 浏览器判断是否全屏
+ */
+export const fullscreenEnable = () => {
+ return (
+ document.isFullScreen ||
+ document.mozIsFullScreen ||
+ document.webkitIsFullScreen
+ );
+};
+
+/**
+ * 浏览器全屏
+ */
+export const reqFullScreen = () => {
+ if (document.documentElement.requestFullScreen) {
+ document.documentElement.requestFullScreen();
+ } else if (document.documentElement.webkitRequestFullScreen) {
+ document.documentElement.webkitRequestFullScreen();
+ } else if (document.documentElement.mozRequestFullScreen) {
+ document.documentElement.mozRequestFullScreen();
+ }
+};
+/**
+ * 浏览器退出全屏
+ */
+export const exitFullScreen = () => {
+ if (document.documentElement.requestFullScreen) {
+ document.exitFullScreen();
+ } else if (document.documentElement.webkitRequestFullScreen) {
+ document.webkitCancelFullScreen();
+ } else if (document.documentElement.mozRequestFullScreen) {
+ document.mozCancelFullScreen();
+ }
+};
+/**
+ * 递归寻找子类的父类
+ */
+
+export const findParent = (menu, id) => {
+ for (let i = 0; i < menu.length; i++) {
+ if (menu[i].children.length != 0) {
+ for (let j = 0; j < menu[i].children.length; j++) {
+ if (menu[i].children[j].id == id) {
+ return menu[i];
+ } else {
+ if (menu[i].children[j].children.length != 0) {
+ return findParent(menu[i].children[j].children, id);
+ }
+ }
+ }
+ }
+ }
+};
+
+/**
+ * 动态插入css
+ */
+
+export const loadStyle = url => {
+ const link = document.createElement("link");
+ link.type = "text/css";
+ link.rel = "stylesheet";
+ link.href = url;
+ const head = document.getElementsByTagName("head")[0];
+ head.appendChild(link);
+};
+/**
+ * 判断路由是否相等
+ */
+export const isObjectValueEqual = (a, b) => {
+ let result = true;
+ Object.keys(a).forEach(ele => {
+ const type = typeof a[ele];
+ if (type === "string" && a[ele] !== b[ele]) result = false;
+ else if (
+ type === "object" &&
+ JSON.stringify(a[ele]) !== JSON.stringify(b[ele])
+ )
+ result = false;
+ });
+ return result;
+};
+/**
+ * 根据字典的value显示label
+ */
+export const findByvalue = (dic, value) => {
+ let result = "";
+ if (validatenull(dic)) return value;
+ if (
+ typeof value === "string" ||
+ typeof value === "number" ||
+ typeof value === "boolean"
+ ) {
+ let index = 0;
+ index = findArray(dic, value);
+ if (index != -1) {
+ result = dic[index].label;
+ } else {
+ result = value;
+ }
+ } else if (value instanceof Array) {
+ result = [];
+ let index = 0;
+ value.forEach(ele => {
+ index = findArray(dic, ele);
+ if (index != -1) {
+ result.push(dic[index].label);
+ } else {
+ result.push(value);
+ }
+ });
+ result = result.toString();
+ }
+ return result;
+};
+/**
+ * 根据字典的value查找对应的index
+ */
+export const findArray = (dic, value) => {
+ for (let i = 0; i < dic.length; i++) {
+ if (dic[i].value == value) {
+ return i;
+ }
+ }
+ return -1;
+};
+/**
+ * 生成随机len位数字
+ */
+export const randomLenNum = (len, date) => {
+ let random = "";
+ random = Math.ceil(Math.random() * 100000000000000)
+ .toString()
+ .substr(0, len || 4);
+ if (date) random = random + Date.now();
+ return random;
+};
+
+/**
+ *
+ * @param url 目标下载接口
+ * @param query 查询参数
+ * @param fileName 文件名称
+ * @returns {*}
+ */
+export function downBlobFile(url, query, fileName) {
+ return request({
+ url: url,
+ method: "get",
+ responseType: "blob",
+ params: query
+ }).then(response => {
+ // 处理返回的文件流
+ const blob = response.data;
+ if (blob && blob.size === 0) {
+ this.$notify.error("内容为空,无法下载");
+ return;
+ }
+ const link = document.createElement("a");
+ link.href = URL.createObjectURL(blob);
+ link.download = fileName;
+ document.body.appendChild(link);
+ link.click();
+ window.setTimeout(function () {
+ URL.revokeObjectURL(blob);
+ document.body.removeChild(link);
+ }, 0);
+ });
+}
diff --git a/src/util/validateRules.js b/src/util/validateRules.js
new file mode 100644
index 0000000000000000000000000000000000000000..ede0f4bb90a4cd83b83ec2e02407585943a92b75
--- /dev/null
+++ b/src/util/validateRules.js
@@ -0,0 +1,48 @@
+/**
+ * @desc [自定义校验规则]
+ * @example
+ * import { validateRule } from "@/utils/validateRules";
+ * rules: [
+ * { validator: validateRule.emailValue, trigger: 'blur'}
+ * ]
+ */
+
+export const rule = {
+ /**
+ * 校验 请输入中文、英文、数字包括下划线
+ * 名称校验
+ */
+ validatorNameCn(rule, value, callback) {
+ let acount = /^[\u4E00-\u9FA5A-Za-z0-9_]+$/
+ if (value && (!(acount).test(value))) {
+ callback(new Error('请输入中文、英文、数字包括下划线'))
+ } else {
+ callback()
+ }
+ },
+ /**
+ * 校验 请输入中文、英文、数字包括下划线
+ * 名称校验
+ */
+ validatorKey(rule, value, callback) {
+ let acount = /^[A-Z_]+$/
+ if (value && (!(acount).test(value))) {
+ callback(new Error('请输入大写英文、下划线'))
+ } else {
+ callback()
+ }
+ },
+
+ /**
+ * 校验首尾空白字符的正则表达式
+ *
+ */
+ checkSpace(rule, value, callback) {
+ let longrg = /[^\s]+$/;
+ if(!longrg.test(value)){
+ callback(new Error('请输入非空格信息'));
+ } else {
+ callback();
+ }
+ },
+}
diff --git a/src/views/admin/param/index.vue b/src/views/admin/param/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..c790e024107891e6d2083549696c4d475785b2f4
--- /dev/null
+++ b/src/views/admin/param/index.vue
@@ -0,0 +1,166 @@
+
+
+
+
+
+