init
This commit is contained in:
2025-06-27 17:42:38 +08:00
commit bd6402478b
5317 changed files with 785994 additions and 0 deletions
+197
View File
@@ -0,0 +1,197 @@
interface TreeHelperConfig {
id: string;
children: string;
pid: string;
}
// 默认配置
const DEFAULT_CONFIG: TreeHelperConfig = {
id: 'id',
children: 'children',
pid: 'pid',
};
// 获取配置。 Object.assign 从一个或多个源对象复制到目标对象
const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config);
// tree from list
// 列表中的树
export function listToTree<T = any>(list: any[], config: Partial<TreeHelperConfig> = {}): T[] {
const conf = getConfig(config) as TreeHelperConfig;
const nodeMap = new Map();
const result: T[] = [];
const { id, children, pid } = conf;
for (const node of list) {
node[children] = node[children] || [];
nodeMap.set(node[id], node);
}
for (const node of list) {
const parent = nodeMap.get(node[pid]);
(parent ? parent[children] : result).push(node);
}
return result;
}
export function treeToList<T = any>(tree: any, config: Partial<TreeHelperConfig> = {}): T {
config = getConfig(config);
const { children } = config;
const result: any = [...tree];
for (let i = 0; i < result.length; i++) {
if (!result[i][children!]) continue;
result.splice(i + 1, 0, ...result[i][children!]);
}
return result;
}
export function findNode<T = any>(tree: any, func: Fn, config: Partial<TreeHelperConfig> = {}): T | null {
config = getConfig(config);
const { children } = config;
const list = [...tree];
for (const node of list) {
if (func(node)) return node;
node[children!] && list.push(...node[children!]);
}
return null;
}
export function findNodeAll<T = any>(tree: any, func: Fn, config: Partial<TreeHelperConfig> = {}): T[] {
config = getConfig(config);
const { children } = config;
const list = [...tree];
const result: T[] = [];
for (const node of list) {
func(node) && result.push(node);
node[children!] && list.push(...node[children!]);
}
return result;
}
export function findPath<T = any>(tree: any, func: Fn, config: Partial<TreeHelperConfig> = {}): T | T[] | null {
config = getConfig(config);
const path: T[] = [];
const list = [...tree];
const visitedSet = new Set();
const { children } = config;
while (list.length) {
const node = list[0];
if (visitedSet.has(node)) {
path.pop();
list.shift();
} else {
visitedSet.add(node);
node[children!] && list.unshift(...node[children!]);
path.push(node);
if (func(node)) {
return path;
}
}
}
return null;
}
export function findPathAll(tree: any, func: Fn, config: Partial<TreeHelperConfig> = {}) {
config = getConfig(config);
const path: any[] = [];
const list = [...tree];
const result: any[] = [];
const visitedSet = new Set(),
{ children } = config;
while (list.length) {
const node = list[0];
if (visitedSet.has(node)) {
path.pop();
list.shift();
} else {
visitedSet.add(node);
node[children!] && list.unshift(...node[children!]);
path.push(node);
func(node) && result.push([...path]);
}
}
return result;
}
export function filter<T = any>(
tree: T[],
func: (n: T) => boolean,
// Partial 将 T 中的所有属性设为可选
config: Partial<TreeHelperConfig> = {}
): T[] {
// 获取配置
config = getConfig(config);
const children = config.children as string;
function listFilter(list: T[]) {
return list
.map((node: any) => ({ ...node }))
.filter((node) => {
// 递归调用 对含有children项 进行再次调用自身函数 listFilter
node[children] = node[children] && listFilter(node[children]);
// 执行传入的回调 func 进行过滤
return func(node) || (node[children] && node[children].length);
});
}
return listFilter(tree);
}
export function forEach<T = any>(tree: T[], func: (n: T) => any, config: Partial<TreeHelperConfig> = {}): void {
config = getConfig(config);
const list: any[] = [...tree];
const { children } = config;
for (let i = 0; i < list.length; i++) {
//func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿
if (func(list[i])) {
return;
}
children && list[i][children] && list.splice(i + 1, 0, ...list[i][children]);
}
}
/**
* @description: Extract tree specified structure
* @description: 提取树指定结构
*/
export function treeMap<T = any>(treeData: T[], opt: { children?: string; conversion: Fn }): T[] {
return treeData.map((item) => treeMapEach(item, opt));
}
/**
* @description: Extract tree specified structure
* @description: 提取树指定结构
*/
export function treeMapEach(data: any, { children = 'children', conversion }: { children?: string; conversion: Fn }) {
const haveChildren = Array.isArray(data[children]) && data[children].length > 0;
const conversionData = conversion(data) || {};
if (haveChildren) {
return {
...conversionData,
[children]: data[children].map((i: number) =>
treeMapEach(i, {
children,
conversion,
})
),
};
} else {
return {
...conversionData,
};
}
}
/**
* 递归遍历树结构
* @param treeDatas 树
* @param callBack 回调
* @param parentNode 父节点
*/
export function eachTree(treeDatas: any[], callBack: Fn, parentNode = {}) {
treeDatas.forEach((element) => {
const newNode = callBack(element, parentNode) || element;
if (element.children) {
eachTree(element.children, callBack, newNode);
}
});
}
+35
View File
@@ -0,0 +1,35 @@
import { Slots } from 'vue';
import { isFunction } from '/@/utils/is';
/**
* @description: Get slot to prevent empty error
*/
export function getSlot(slots: Slots, slot = 'default', data?: any) {
if (!slots || !Reflect.has(slots, slot)) {
return null;
}
if (!isFunction(slots[slot])) {
console.error(`${slot} is not a function!`);
return null;
}
const slotFn = slots[slot];
if (!slotFn) return null;
return slotFn(data);
}
/**
* extends slots
* @param slots
* @param excludeKeys
*/
export function extendSlots(slots: Slots, excludeKeys: string[] = []) {
const slotKeys = Object.keys(slots);
const ret: any = {};
slotKeys.map((key) => {
if (excludeKeys.includes(key)) {
return null;
}
ret[key] = () => getSlot(slots, key);
});
return ret;
}
+279
View File
@@ -0,0 +1,279 @@
import { dateUtil } from '/@/utils/dateUtil';
import { duplicateCheck } from '/@/views/system/user/user.api';
import { Rule } from '/@/components/Form';
import { message } from 'ant-design-vue';
export const rules = {
rule(type, required): Rule[] {
if (type === 'email') {
return this.email(required);
}
if (type === 'phone') {
return this.phone(required);
}
if (type === 'idcard') {
return this.idcard(required);
}
return [];
},
idcard(required): Rule[] {
return [
{
required: required ? required : false,
validator: async (_rule, value) => {
if (required == true && !value) {
return Promise.reject('请输入身份证号!');
}
if (value && !new RegExp(/^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|[xX])$/).test(value)) {
return Promise.reject('请输入正确的身份证号!');
}
return Promise.resolve();
},
trigger: 'change',
},
];
},
email(required): Rule[] {
return [
{
required: required ? required : false,
validator: async (_rule, value) => {
if (required == true && !value) {
return Promise.reject('请输入邮箱!');
}
if (
value &&
!new RegExp(
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
).test(value)
) {
return Promise.reject('请输入正确邮箱格式!');
}
return Promise.resolve();
},
trigger: 'change',
},
];
},
phone(required): Rule[] {
return [
{
required: required,
validator: async (_, value) => {
if (!value) {
return required ? Promise.reject('请输入手机号码!') : Promise.resolve();
}
if (!/^1[3456789]\d{9}$/.test(value)) {
return Promise.reject('手机号码格式有误');
}
return Promise.resolve();
},
trigger: 'change',
},
];
},
telPhone(required: boolean, tip?: string = '电话号码格式有误'): Rule[] {
return [
{
required: required,
validator: async (_, value) => {
if (!value) {
return required ? Promise.reject('请输入电话号码!') : Promise.resolve();
}
if (!/^(0\d{2,3}-\d{7,8})$|^(0\d{2,3}\d{7,8})|^(\(\d{3,4}\)-\d{7,8})$|^(\(\d{3,4}\)\d{7,8})$/.test(value)) {
return Promise.reject(tip);
}
return Promise.resolve();
},
trigger: 'change',
},
];
},
phoneAndTelPhone(required: boolean, tip?: string = '电话号码格式有误'): Rule[] {
return [
{
required: required,
validator: async (_, value) => {
if (!value) {
return required ? Promise.reject('请输入号码!') : Promise.resolve();
}
if (!/^(0\d{2,3}-\d{7,8})$|^(0\d{2,3}\d{7,8})|^(\(\d{3,4}\)-\d{7,8})$|^(\(\d{3,4}\)\d{7,8})$|^1[3456789]\d{9}$/.test(value)) {
return Promise.reject(tip);
}
return Promise.resolve();
},
trigger: 'change',
},
];
},
// 多个电话号码验证
multiplePhone(required: boolean): Rule[] {
return [
{
required: required,
validator: async (_, value) => {
if (!value) {
return required ? Promise.reject('请输入号码!') : Promise.resolve();
}
const phoneRegex = /^(?:1[3-9]\d{9}(?:,|$))*$/;
if (!phoneRegex.test(value)) {
return Promise.reject('请检查格式');
}
return Promise.resolve();
},
trigger: 'change',
},
];
},
startTime(endTime, required): Rule[] {
return [
{
required: required ? required : false,
validator: (_, value) => {
if (required && !value) {
return Promise.reject('请选择开始时间');
}
if (endTime && value && dateUtil(endTime).isBefore(value)) {
return Promise.reject('开始时间需小于结束时间');
}
return Promise.resolve();
},
trigger: 'change',
},
];
},
endTime(startTime, required): Rule[] {
return [
{
required: required ? required : false,
validator: (_, value) => {
if (required && !value) {
return Promise.reject('请选择结束时间');
}
if (startTime && value && dateUtil(value).isBefore(startTime)) {
return Promise.reject('结束时间需大于开始时间');
}
return Promise.resolve();
},
trigger: 'change',
},
];
},
confirmPassword(values, required): Rule[] {
return [
{
required: required ? required : false,
validator: (_, value) => {
if (!value) {
return Promise.reject('密码不能为空');
}
if (value !== values.password) {
return Promise.reject('两次输入的密码不一致!');
}
return Promise.resolve();
},
},
];
},
duplicateCheckRule(tableName, fieldName, model, schema, required?): Rule[] {
return [
{
validator: (_, value) => {
if (!value && required) {
return Promise.reject(`请输入${schema.label}`);
}
return new Promise<void>((resolve, reject) => {
duplicateCheck({
tableName,
fieldName,
fieldVal: value,
dataId: model.id,
})
.then((res) => {
res.success ? resolve() : reject(res.message || '校验失败');
})
.catch((err) => {
reject(err.message || '验证失败');
});
});
},
trigger: 'blur',
},
];
},
duplicateCheckRuleAndDelFlag(tableName, fieldName, delFlag, model, schema, required?): Rule[] {
return [
{
validator: (_, value) => {
if (!value && required) {
return Promise.reject(`请输入${schema.label}`);
}
return new Promise<void>((resolve, reject) => {
duplicateCheck({
tableName,
fieldName,
delFlag,
fieldVal: value,
dataId: model.id,
})
.then((res) => {
res.success ? resolve() : reject(res.message || '校验失败');
})
.catch((err) => {
reject(err.message || '验证失败');
});
});
},
trigger: 'blur',
},
];
},
//模拟实测中数据验证
//数字大于0
numberGreaterThanZero(value: number) {
const patternNumber = /^\d*(\.\d+)?$/;
if (!patternNumber.test(value) || value < 0) {
message.warn('请输入大于0的数字');
return false;
}
return true;
},
//数字大于0的整数
numberGreaterThanZeroInt(value: number) {
const patternNumber = /^\d+$/;
if (!patternNumber.test(value) || value < 0) {
message.warn('请输入大于0的整数');
return false;
}
return true;
},
};
//update-begin-author:taoyan date:2022-6-16 for: 代码生成-原生表单用
/**
* 唯一校验函数,给原生<a-form>使用,vben的表单校验建议使用上述rules
* @param tableName 表名
* @param fieldName 字段名
* @param fieldVal 字段值
* @param dataId 数据ID
*/
export async function duplicateValidate(tableName, fieldName, fieldVal, dataId) {
try {
const params = {
tableName,
fieldName,
fieldVal,
dataId: dataId,
};
const res = await duplicateCheck(params);
if (res.success) {
return Promise.resolve();
} else {
return Promise.reject(res.message || '校验失败');
}
} catch (e) {
return Promise.reject('校验失败,可能是断网等问题导致的校验失败');
}
}
//update-end-author:taoyan date:2022-6-16 for: 代码生成-原生表单用