关于前端常用的部分公共方法(二)

1.读取json文件内容

typescript 复制代码
/**
 * 读取JSON文件内容
 * 
 * @template T 返回的数据类型
 * @param {File} file 文件选择框选择的文件对象
 * @returns {Promise<T>} 解析后的JSON数据
 */
export function readJSON<T>(file: File): Promise<T> {
  return new Promise((resolve, reject) => {
    const fileName = file.name;
    const fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
    
    if (fileExtension !== "json") {
      reject(new Error("文件类型不合法,请选择JSON格式文件!"));
      return;
    }

    if (window.FileReader) {
      const reader = new FileReader();
      reader.readAsText(file, "UTF-8");
      
      reader.onload = () => {
        try {
          resolve(JSON.parse(reader.result as string));
        } catch (error) {
          reject(new Error("解析JSON文件失败"));
        }
      };
      
      reader.onerror = () => {
        reject(new Error("读取文件失败"));
      };
    }
  });
}

代码改进点

  1. 参数类型从any改为明确的File类型,提升类型安全性
  2. 添加错误处理逻辑,使用reject返回错误状态
  3. 优化变量命名,fileType改为更准确的fileExtension
  4. 添加try-catch处理JSON解析可能出现的异常
  5. 添加文件读取错误的处理逻辑

使用示例

typescript 复制代码
const fileInput = document.getElementById('file-input') as HTMLInputElement;
fileInput.addEventListener('change', async (event) => {
  const file = event.target.files?.[0];
  if (!file) return;
  
  try {
    const data = await readJSON<MyDataType>(file);
    console.log(data);
  } catch (error) {
    console.error(error.message);
  }
});

2. 判断是否 "经度,纬度" 字符串值

以下是实现判断字符串是否为"经度,纬度"格式的TypeScript代码:

typescript 复制代码
/**
 * 判断是否 "经度,纬度" 字符串值
 *
 * @export
 * @param {string} text 传入的字符串
 * @return {boolean} 是否 经度,纬度
 */
export function isLonLat(text: string): boolean {
  const reg = /^-?((0|1?[0-7]?[0-9]?)(([.][0-9]*)?)|180(([.][0]*)?)),-?((0|[1-8]?[0-9]?)(([.][0-9]*)?)|90(([.][0]*)?))$/
  return reg.test(text)
}

该正则表达式解析:

  • 经度部分:-?((0|1?[0-7]?[0-9]?)(([.][0-9]*)?)|180(([.][0]*)?))

    • 允许负号开头
    • 0-179度,可选小数部分
    • 或180度整数(可选.0结尾)
  • 纬度部分:-?((0|[1-8]?[0-9]?)(([.][0-9]*)?)|90(([.][0]*)?))

    • 允许负号开头
    • 0-89度,可选小数部分
    • 或90度整数(可选.0结尾)
  • 经度纬度用逗号分隔

3. 获取URL参数

typescript 复制代码
/**
 * 获取URL参数
 *
 * @export
 * @param {string} parameter url参数名
 * @return {string | null} 参数值
 */
export function getQueryString(parameter: string): string | null {
  return new URL(window.location.href).searchParams.get(parameter)
}

函数说明: 该函数使用现代URL API解析当前页面URL的查询参数,比传统字符串分割方法更可靠。参数说明:

  • parameter: 需要获取的URL参数名称
  • 返回值: 参数值字符串,若不存在则返回null

使用示例:

typescript 复制代码
// 假设当前URL为 https://example.com?name=test&id=123
const name = getQueryString('name') // 返回 'test'
const age = getQueryString('age') // 返回 null

注意事项:

  • 需要浏览器支持URL接口(所有现代浏览器均支持)
  • 返回的字符串不会被自动解码,如需解码URI组件需额外处理
  • 参数名称区分大小写

4. 将指定的异步方法转为Promise

typescript 复制代码
/**
 * 将指定的异步方法转为Promise
 * 
 * @param {*} context
 * @param {string} apiName
 * @param {string} [success="success"]
 * @param {string} [error="error"]
 * @return {*} Promise
 */
export function apiToSync(context: any, apiName: string, success = "success", error = "error") {
  return new Promise((resolve, reject) => {
    context[apiName]({
      [success]: (res: any) => resolve(res),
      [error]: (err: any) => reject(err)
    })
  })
}
typescript 复制代码
/**
 * 将多个异步方法转为Promise数组
 * 
 * @param {*} context
 * @param {string[]} apiNames
 * @param {string} [success="success"]
 * @param {string} [error="error"]
 * @return {*} Promise[]
 */
export function apiArrayToSync(context: any, apiNames: string[], success = "success", error = "error") {
  return apiNames.map(apiName => apiToSync(context, apiName, success, error))
}

代码实现了以下功能:

  • apiToSync函数将单个异步API转换为Promise
  • apiArrayToSync函数批量转换多个API为Promise数组
  • 支持自定义成功和失败的回调字段名
  • 保留了原始API的context调用方式
  • 类型标注使用TypeScript语法

5. 将指定的多个异步方法转为Promise

typescript 复制代码
/**
 * 将指定的多个异步方法转为Promise
 *
 * @param {*} context
 * @param {string[]} apiNames
 * @param {string} [success="success"]
 * @param {string} [error="error"]
 * @return {*} Promise[]
 */
export function apiArrayToSync(context: any, apiNames: string[], success = "success", error = "error") {
  return apiNames.map((name) => {
    const apiFunc = context[name]

    return (options: any) =>
      new Promise((resolve, reject) => {
        options[success] = function (result: any) {
          resolve(result)
        }
        options[error] = function (error) {
          reject(error)
        }
        apiFunc.call(context, options)
      })
  })
}

使用方法

typescript 复制代码
// 假设有一个对象包含异步API方法
const apiObject = {
  asyncApi1(options: any) {
    setTimeout(() => {
      options.success('Result from asyncApi1')
    }, 1000)
  },
  asyncApi2(options: any) {
    setTimeout(() => {
      options.success('Result from asyncApi2')
    }, 500)
  }
}

// 转换为Promise数组
const promiseArray = apiArrayToSync(apiObject, ['asyncApi1', 'asyncApi2'])

// 使用Promise.all并行调用
Promise.all([
  promiseArray[0]({}),
  promiseArray[1]({})
]).then(results => {
  console.log(results) // ['Result from asyncApi1', 'Result from asyncApi2']
}).catch(error => {
  console.error(error)
})

关键点说明

  • 该函数将基于回调的异步方法转换为Promise形式
  • 支持自定义成功和失败的回调键名
  • 返回的是Promise数组,可以配合Promise.all使用
  • 保持了原始方法的this绑定

参数说明

  • context: 包含异步方法的对象
  • apiNames: 需要转换的方法名数组
  • success: 成功回调的键名,默认为'success'
  • error: 失败回调的键名,默认为'error'

6. 单个权限验证

typescript 复制代码
/**
 * 单个权限验证
 * @param value 权限值
 * @returns 有权限,返回 `true`,反之则反
 */
export function auth(value: string): boolean {
    const stores = useUserInfo();
    return stores.userInfos.authBtnList.some((v: string) => v === value);
}

7. 多个权限验证,满足一个则为true

typescript 复制代码
/**
 * 多个权限验证,满足一个则为 true
 * @param value 权限值
 * @returns 有权限,返回 `true`,反之则反
 */
export function auths(value: Array<string>): boolean {
	let flag = false;
	const stores = useUserInfo();
	stores.userInfos.authBtnList.map((val: string) => {
		value.map((v: string) => {
			if (val === v) flag = true;
		});
	});
	return flag;
}

8. 多个权限验证,全部满足则为 true

typescript 复制代码
/**
 * 多个权限验证,全部满足则为 true
 * @param value 权限值
 * @returns 有权限,返回 `true`,反之则反
 */
export function authAll(value: Array<string>): boolean {
	const stores = useUserInfo();
	return judementSameArr(value, stores.userInfos.authBtnList);
}

/**
 * 判断两数组字符串是否相同(用于按钮权限验证),数组字符串中存在相同时会自动去重(按钮权限标识不会重复)
 * @param news 新数据
 * @param old 源数据
 * @returns 两数组相同返回 `true`,反之则反
 */
export function judementSameArr(newArr: unknown[] | string[], oldArr: string[]): boolean {
	const news = removeDuplicate(newArr);
	const olds = removeDuplicate(oldArr);
	let count = 0;
	const leng = news.length;
	for (let i in olds) {
		for (let j in news) {
			if (olds[i] === news[j]) count++;
		}
	}
	return count === leng ? true : false;
}

9. 获取UI框架图片URL

typescript 复制代码
/**
 * 获取UI框架图片URL
 * @param name 图片名称
 * @param prefix 前置根路径
 */
export function getImageUrl(name: string | unknown, prefix = 'frame') {
    return new URL(`/src/assets/images/${prefix}/${name}.png`, import.meta.url).href;
}

/**
 * 获取业务图片URL
 * @param name 图片名称
 */
export function getBaseImage(name: string | unknown) {
    return new URL(`/src/assets/images/${name}.png`, import.meta.url).href;
}

/**
 * 获取IConURL
 * @param name 图标名称
 */
export function getIconUrl(name: string | unknown) {
    return new URL(`/src/assets/icons/${name}.png`, import.meta.url).href;
}

10. 转换秒单位为X小时X分钟X秒的格式

typescript 复制代码
/**
 * 转换秒单位为X小时X分钟X秒的格式
 * @returns {String}
 */
export function transSeconds(rawSeconds: number) {
    if (rawSeconds && rawSeconds > 0) {
        let last = 0;
        const hours = Math.floor(rawSeconds / 3600);
        last = rawSeconds - hours * 3600;
        const minute = parseInt(Math.floor(last / 60).toString());
        const seconds = parseInt((rawSeconds - hours * 3600 - minute * 60).toString());
        return `${hours > 0 ? hours + '小时' : ''}${minute}分${seconds}秒`;
    }
    return '';
}

11. 输入框限制只能输入数字,并且范围为0-999

typescript 复制代码
<el-input clearable maxlength="3" v-model.number="form.warningthresholdmin" @input="validateMinValue"></el-input>

// 输入框限制输入只能输入正整数,并且不能大于999
const validateMinValue = (value: any) => {
  // 将输入值转换为数字
  let num = Number(value);
  if (!value) {
    form.warningthresholdmin = '';
    return;
  }
  // 检查输入值是否是数字
  if (isNaN(num)) {
    // 如果不是数字,将其设置为空字符串
    form.warningthresholdmin = '';
    return;
  }
  // 检查输入值是否在 0 到 999 的范围内
  if (num < 0) {
    num = 0;
  } else if (num > 999) {
    num = 999;
  }
  // 更新输入框的值
  form.warningthresholdmin = num;

}

12. 输入框限制只能输入数字,支持小数,小数点前四位,小数点后六位(经纬度输入框)

typescript 复制代码
 <el-input placeholder="请输入经度" maxlength="10" @input="e => handleInput(e,'longitude')" clearable v-model="form.longitude"></el-input>
<el-input placeholder="请输入纬度" maxlength="10" @input="e => handleInput(e, 'latitude')" clearable v-model="form.latitude"></el-input>

// 限制经纬度输入只能输入数字0-9999.999999
const handleInput = (value: any, type: string) => {
  // 处理空值
  if (!value) {
    if (type === "latitude") {
      form.latitude = '';
    } else {
      form.longitude = '';
    }
    return;
  }

  let val = value.toString();
  // 非法输入,限制范围或清空
  if (val === '.') {
    // 允许小数点开头,后续通过正则处理
    if (type === "latitude") {
      form.latitude = '0.';
    } else {
      form.longitude = '0.';
    }
  } else {
    // 移除非法字符
    val = val.replace(/[^0-9\.]/g, '');
    val = val.replace(/(\..*)\./g, '$1'); // 只允许一个小数点

    // 限制小数点后六位
    const parts = val.split('.');
    if (parts.length > 1 && parts[1].length > 6) {
      parts[1] = parts[1].substring(0, 6);
      val = parts.join('.');
    }
    // 转换为数字并限制范围
    let num = parseFloat(val);
    let number = val;
    // 检查输入值是否是数字
    if (isNaN(num)) {
      // 如果不是数字,将其设置为空字符串
      if (type === "latitude") {
        form.latitude = '';
      } else {
        form.longitude = '';
      }
      return;
    }
    // 检查输入值是否在 0 到 999 的范围内
    if (number < 0) {
      number = 0;
    } else if (number > 9999.999999) {
      number = 9999.999999;
    }
    if (type === "latitude") {
      form.latitude = number;
    } else {
      form.longitude = number;
    }
  }
}
相关推荐
七灵微40 分钟前
【后端】单点登录
服务器·前端
持久的棒棒君5 小时前
npm安装electron下载太慢,导致报错
前端·electron·npm
渔舟唱晚@6 小时前
大模型数据流处理实战:Vue+NDJSON的Markdown安全渲染架构
vue.js·大模型·数据流
crary,记忆7 小时前
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
前端·webpack·angular·angular.js
漂流瓶jz7 小时前
让数据"流动"起来!Node.js实现流式渲染/流式传输与背后的HTTP原理
前端·javascript·node.js
SamHou07 小时前
手把手 CSS 盒子模型——从零开始的奶奶级 Web 开发教程2
前端·css·web
我不吃饼干8 小时前
从 Vue3 源码中了解你所不知道的 never
前端·typescript
开航母的李大8 小时前
【中间件】Web服务、消息队列、缓存与微服务治理:Nginx、Kafka、Redis、Nacos 详解
前端·redis·nginx·缓存·微服务·kafka
Bruk.Liu8 小时前
《Minio 分片上传实现(基于Spring Boot)》
前端·spring boot·minio
鱼樱前端8 小时前
Vue3+d3-cloud+d3-scale+d3-scale-chromatic实现词云组件
前端·javascript·vue.js