first commit
This commit is contained in:
14
src/utils/auth.js
Normal file
14
src/utils/auth.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { TOKEN_KEY } from '@/enums/cacheEnums'
|
||||
import cache from './cache'
|
||||
|
||||
export function getToken() {
|
||||
return cache.get(TOKEN_KEY)
|
||||
}
|
||||
|
||||
export function setToken(token) {
|
||||
return cache.set(TOKEN_KEY, token)
|
||||
}
|
||||
|
||||
export function clearToken() {
|
||||
cache.remove(TOKEN_KEY)
|
||||
}
|
||||
51
src/utils/cache.js
Normal file
51
src/utils/cache.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const cache = {
|
||||
key: 'app_',
|
||||
|
||||
//设置缓存(expire为缓存时效)
|
||||
set(key, value, expire = null) {
|
||||
key = this.getKey(key)
|
||||
let data = {
|
||||
expire: expire ? this.time() + expire : '',
|
||||
value
|
||||
}
|
||||
|
||||
if (typeof data === 'object') {
|
||||
data = JSON.stringify(data)
|
||||
}
|
||||
try {
|
||||
uni.setStorageSync(key, data)
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
get(key) {
|
||||
key = this.getKey(key)
|
||||
try {
|
||||
const data = uni.getStorageSync(key)
|
||||
if (!data) {
|
||||
return null
|
||||
}
|
||||
const { value, expire } = JSON.parse(data)
|
||||
if (expire && expire < this.time()) {
|
||||
uni.removeStorageSync(key)
|
||||
return null
|
||||
}
|
||||
return value
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
//获取当前时间
|
||||
time() {
|
||||
return Math.round(new Date().getTime() / 1000)
|
||||
},
|
||||
remove(key) {
|
||||
key = this.getKey(key)
|
||||
uni.removeStorageSync(key)
|
||||
},
|
||||
getKey(key) {
|
||||
return this.key + key
|
||||
}
|
||||
}
|
||||
|
||||
export default cache
|
||||
89
src/utils/datetime.js
Normal file
89
src/utils/datetime.js
Normal file
@@ -0,0 +1,89 @@
|
||||
// 格式化时间戳为 "刚刚, 几分钟前"
|
||||
export function formatTimestamp(times) {
|
||||
const now = new Date().getTime();
|
||||
const date = new Date(times);
|
||||
const diff = now - date.getTime();
|
||||
|
||||
if (diff > 0) {
|
||||
// 刚刚
|
||||
if (diff < 60000) {
|
||||
return '刚刚';
|
||||
}
|
||||
|
||||
// n分钟前
|
||||
if (diff < 3600000) {
|
||||
return Math.floor(diff / 60000) + '分钟前';
|
||||
}
|
||||
|
||||
// n小时前
|
||||
if (diff < 86400000) {
|
||||
return Math.floor(diff / 3600000) + '小时前';
|
||||
}
|
||||
|
||||
// 昨天
|
||||
if (diff < 172800000) {
|
||||
return '昨天';
|
||||
}
|
||||
|
||||
// 前天
|
||||
if (diff < 259200000) {
|
||||
return '前天';
|
||||
}
|
||||
|
||||
// 2天前 4 * 24 * 60 * 60 * 1000
|
||||
if (diff < 345600000) {
|
||||
return '2天前';
|
||||
}
|
||||
|
||||
// 3天前 5 * 24 * 60 * 60 * 1000
|
||||
if (diff < 432000000) {
|
||||
return '3天前';
|
||||
}
|
||||
}
|
||||
|
||||
// n月n日
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
// const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
return month + '-' + day + ' ' + hours + ':' + minutes;
|
||||
}
|
||||
|
||||
|
||||
// 格式化时间 '2025-09-19 星期三'
|
||||
export function getWeekStr(time) {
|
||||
if (!time) return '';
|
||||
|
||||
const date = new Date(time);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
|
||||
const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||||
const weekday = weekdays[date.getDay()];
|
||||
|
||||
return `${year}-${month}-${day} ${weekday}`;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 获取时间 start end
|
||||
export function getDate(type) {
|
||||
const date = new Date();
|
||||
|
||||
let year = date.getFullYear();
|
||||
let month = date.getMonth() + 1;
|
||||
let day = date.getDate();
|
||||
|
||||
if (type === 'start') {
|
||||
year = year - 10;
|
||||
} else if (type === 'end') {
|
||||
year = year + 10;
|
||||
}
|
||||
month = month > 9 ? month : '0' + month;;
|
||||
day = day > 9 ? day : '0' + day;
|
||||
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
47
src/utils/formatter.js
Normal file
47
src/utils/formatter.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 手机号加掩码
|
||||
* 15112345678 => 151****5678
|
||||
*/
|
||||
export function maskPhoneNumber(phoneNumber) {
|
||||
// 检查手机号码是否合法
|
||||
if (!/^1\d{10}$/.test(phoneNumber)) {
|
||||
return ''
|
||||
}
|
||||
return phoneNumber.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
|
||||
}
|
||||
|
||||
/**
|
||||
* 身份证号码加掩码
|
||||
* 110101198506020011 => 1101********0011
|
||||
*/
|
||||
export function maskIdNumber() {
|
||||
return idNumber.replace(/(\d{4})\d+(\d{4})$/, '$1****$2');
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额格式化
|
||||
* num 金额
|
||||
* decimals 保留几位小数,默认2位
|
||||
* units 默认 units ¥
|
||||
*/
|
||||
export function formatMoney(num, decimals,units){
|
||||
units = units || '¥ '
|
||||
|
||||
num = parseFloat(num/100);
|
||||
if (isNaN(num)) return units+0;
|
||||
|
||||
decimals = typeof decimals === 'undefined' ? 2 : decimals;
|
||||
|
||||
var parts = num.toFixed(decimals).split('.');
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
|
||||
let retVal = 0;
|
||||
// let part1 = parts[1]
|
||||
// if(parseInt(part1)==0){
|
||||
// retVal = parts[0];
|
||||
// }else{
|
||||
retVal = parts.join('.');
|
||||
// }
|
||||
|
||||
return units+retVal;
|
||||
}
|
||||
71
src/utils/message.js
Normal file
71
src/utils/message.js
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 弹出 toast
|
||||
*
|
||||
* @param msg
|
||||
* @param icon
|
||||
* @param mask
|
||||
* @returns
|
||||
*/
|
||||
export const showToast = (msg, icon = 'none', mask = false) => {
|
||||
return uni.showToast({
|
||||
title: msg,
|
||||
icon: icon,
|
||||
mask
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出 alert
|
||||
* @param content
|
||||
* @param title
|
||||
* @returns
|
||||
*/
|
||||
export const showAlert = (content, title = '提示',showCancel=true,succFun) => {
|
||||
return uni.showModal({
|
||||
title: title,
|
||||
content: content,
|
||||
showCancel,
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
// console.log('用户点击确定');
|
||||
succFun();
|
||||
} else if (res.cancel) {
|
||||
// console.log('用户点击取消');
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出加载动画
|
||||
*
|
||||
* @param text
|
||||
* @param mask
|
||||
* @returns
|
||||
*/
|
||||
export const showLoading = (text = null, mask = true) => {
|
||||
return uni.showLoading({
|
||||
title: text,
|
||||
mask: mask
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏加载动画
|
||||
*/
|
||||
export const hideLoading = () => uni.hideLoading()
|
||||
|
||||
|
||||
/**
|
||||
* 组合式API
|
||||
*/
|
||||
export const useMessage = () => {
|
||||
return {
|
||||
toast: showToast,
|
||||
success: (msg, mask = true) => showToast(msg, 'success', mask),
|
||||
error: (msg, mask = true) => showToast(msg, 'error', mask),
|
||||
alert: showAlert,
|
||||
showLoading,
|
||||
hideLoading
|
||||
}
|
||||
}
|
||||
32
src/utils/request/cancel.js
Normal file
32
src/utils/request/cancel.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const cancelerMap = new Map();
|
||||
|
||||
class RequestCancel {
|
||||
|
||||
// 获取实例
|
||||
static getInstance() {
|
||||
if (!this._instance) {
|
||||
this._instance = new RequestCancel()
|
||||
}
|
||||
return this._instance
|
||||
}
|
||||
|
||||
add(url, requestTask) {
|
||||
this.remove(url);
|
||||
if (cancelerMap.has(url)) {
|
||||
cancelerMap.delete(url);
|
||||
}
|
||||
cancelerMap.set(url, requestTask);
|
||||
}
|
||||
|
||||
remove(url) {
|
||||
if (cancelerMap.has(url)) {
|
||||
const requestTask = cancelerMap.get(url);
|
||||
requestTask && requestTask.abort();
|
||||
cancelerMap.delete(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const requestCancel = RequestCancel.getInstance();
|
||||
|
||||
export default requestCancel;
|
||||
165
src/utils/request/http.js
Normal file
165
src/utils/request/http.js
Normal file
@@ -0,0 +1,165 @@
|
||||
import { RequestMethodsEnum, RequestErrMsgEnum } from '@/enums/requestEnums';
|
||||
import { merge } from 'lodash-es';
|
||||
import { isFunction } from '@vue/shared'
|
||||
import requestCancel from './cancel'
|
||||
|
||||
export default class HttpRequest {
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
/**
|
||||
* @description 重新请求
|
||||
*/
|
||||
retryRequest(options, config) {
|
||||
const { retryCount, retryTimeout } = config;
|
||||
if (!retryCount || options.method?.toUpperCase() == RequestMethodsEnum.POST) {
|
||||
return Promise.reject();
|
||||
}
|
||||
uni.showLoading({ title: '加载中...' });
|
||||
config.hasRetryCount = config.hasRetryCount ?? 0;
|
||||
if (config.hasRetryCount >= retryCount) {
|
||||
return Promise.reject();
|
||||
}
|
||||
config.hasRetryCount++;
|
||||
config.requestHooks.requestInterceptorsHook = (options) => options;
|
||||
return new Promise((resolve) => setTimeout(resolve, retryTimeout))
|
||||
.then(() => this.request(options, config))
|
||||
.finally(() => uni.hideLoading());
|
||||
}
|
||||
/**
|
||||
* @description get请求
|
||||
*/
|
||||
get(options, config) {
|
||||
return this.request({ ...options, method: RequestMethodsEnum.GET }, config);
|
||||
}
|
||||
/**
|
||||
* @description post请求
|
||||
*/
|
||||
post(options, config) {
|
||||
return this.request({ ...options, method: RequestMethodsEnum.POST }, config);
|
||||
}
|
||||
/**
|
||||
* @description put请求
|
||||
*/
|
||||
put(options, config) {
|
||||
return this.request({ ...options, method: RequestMethodsEnum.PUT }, config);
|
||||
}
|
||||
/**
|
||||
* @description delete请求
|
||||
*/
|
||||
delete(options, config) {
|
||||
return this.request({ ...options, method: RequestMethodsEnum.DELETE }, config);
|
||||
}
|
||||
/**
|
||||
* @description 上传图片
|
||||
*/
|
||||
uploadFile(options, config) {
|
||||
let mergeOptions = merge({}, this.options.requestOptions, options);
|
||||
const mergeConfig = merge({}, this.options, config);
|
||||
const {
|
||||
requestInterceptorsHook,
|
||||
responseInterceptorsHook,
|
||||
responseInterceptorsCatchHook
|
||||
} = mergeConfig.requestHooks || {};
|
||||
|
||||
if (requestInterceptorsHook && isFunction(requestInterceptorsHook)) {
|
||||
mergeOptions = requestInterceptorsHook(mergeOptions, mergeConfig);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
...mergeOptions,
|
||||
success: async (response) => {
|
||||
if (response.statusCode == 200) {
|
||||
response.data = JSON.parse(response.data);
|
||||
if (responseInterceptorsHook && isFunction(responseInterceptorsHook)) {
|
||||
try {
|
||||
response = await responseInterceptorsHook(response, mergeConfig);
|
||||
if(response){
|
||||
resolve(response);
|
||||
}else{
|
||||
reject('error')
|
||||
}
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
resolve(response);
|
||||
}
|
||||
},
|
||||
fail: async (err) => {
|
||||
if (responseInterceptorsCatchHook &&
|
||||
isFunction(responseInterceptorsCatchHook)) {
|
||||
reject(await responseInterceptorsCatchHook(mergeOptions, err));
|
||||
return;
|
||||
}
|
||||
reject(err);
|
||||
},
|
||||
complete: (response) => {
|
||||
if (response.statusCode == 200) {
|
||||
if (response.data instanceof String) {
|
||||
response.data = JSON.parse(response.data);
|
||||
}
|
||||
resolve(response.data);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description 请求函数
|
||||
*/
|
||||
async request(options, config) {
|
||||
let mergeOptions = merge({}, this.options.requestOptions, options);
|
||||
const mergeConfig = merge({}, this.options, config);
|
||||
const { requestInterceptorsHook, responseInterceptorsHook, responseInterceptorsCatchHook } =
|
||||
mergeConfig.requestHooks || {};
|
||||
if (requestInterceptorsHook && isFunction(requestInterceptorsHook)) {
|
||||
mergeOptions = requestInterceptorsHook(mergeOptions, mergeConfig);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const requestTask = uni.request({
|
||||
...mergeOptions,
|
||||
async success(response) {
|
||||
// console.log("success111=>",response)
|
||||
if (responseInterceptorsHook && isFunction(responseInterceptorsHook)) {
|
||||
try {
|
||||
response = await responseInterceptorsHook(response, mergeConfig);
|
||||
resolve(response);
|
||||
}
|
||||
catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
resolve(response);
|
||||
},
|
||||
fail: async (err) => {
|
||||
// console.log("fail222=>",err)
|
||||
if (err.errMsg == RequestErrMsgEnum.TIMEOUT) {
|
||||
this.retryRequest(mergeOptions, mergeConfig)
|
||||
.then((res) => resolve(res))
|
||||
.catch((err) => reject(err));
|
||||
return;
|
||||
}
|
||||
if (responseInterceptorsCatchHook &&
|
||||
isFunction(responseInterceptorsCatchHook)) {
|
||||
reject(await responseInterceptorsCatchHook(mergeOptions, err));
|
||||
return;
|
||||
}
|
||||
reject(err);
|
||||
},
|
||||
complete(err) {
|
||||
// console.log("complete333=>",err)
|
||||
if (err.errMsg !== RequestErrMsgEnum.ABORT) {
|
||||
requestCancel.remove(options.url);
|
||||
}
|
||||
}
|
||||
});
|
||||
const { ignoreCancel } = mergeConfig;
|
||||
!ignoreCancel && requestCancel.add(options.url, requestTask);
|
||||
});
|
||||
}
|
||||
}
|
||||
96
src/utils/request/index.js
Normal file
96
src/utils/request/index.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import HttpRequest from './http';
|
||||
import { merge } from 'lodash-es';
|
||||
import { getToken } from '../auth';
|
||||
import { RequestCodeEnum, RequestMethodsEnum } from '@/enums/requestEnums';
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useMessage } from '../message';
|
||||
|
||||
const message = useMessage();
|
||||
|
||||
const requestHooks = {
|
||||
// 请求拦截器
|
||||
requestInterceptorsHook(options, config) {
|
||||
// console.log("request=>",options,config)
|
||||
const { urlPrefix, baseUrl, withToken, isAuth } = config;
|
||||
options.header = options.header ?? {};
|
||||
if (urlPrefix) {
|
||||
options.url = `${urlPrefix}${options.url}`;
|
||||
}
|
||||
if (baseUrl) {
|
||||
options.url = `${baseUrl}${options.url}`;
|
||||
}
|
||||
const token = getToken();
|
||||
if (withToken && !options.header.token) {
|
||||
options.header.token = token;
|
||||
}
|
||||
return options;
|
||||
},
|
||||
// 响应拦截器
|
||||
responseInterceptorsHook(response, config) {
|
||||
// console.log("response=>",response,config)
|
||||
const { isTransformResponse, isReturnDefaultResponse, isAuth } = config;
|
||||
|
||||
if (isReturnDefaultResponse) {
|
||||
return response;
|
||||
}
|
||||
if (!isTransformResponse) {
|
||||
return response.data;
|
||||
}
|
||||
const { logout, isLogin } = useUserStore();
|
||||
const { code, data, msg, show } = response.data;
|
||||
// console.log(code,data,msg,show)
|
||||
switch (code) {
|
||||
case RequestCodeEnum.SUCCESS:
|
||||
msg && show && message.toast(msg);
|
||||
return data;
|
||||
case RequestCodeEnum.FAILED:
|
||||
message.toast(msg);
|
||||
return Promise.reject(msg);
|
||||
case RequestCodeEnum.TOKEN_INVALID:
|
||||
if (isAuth && isLogin) {
|
||||
|
||||
}
|
||||
|
||||
return Promise.reject();
|
||||
|
||||
default:
|
||||
message.toast(msg)
|
||||
// return data;
|
||||
return Promise.reject(msg);
|
||||
}
|
||||
},
|
||||
// 响应异常拦截器
|
||||
responseInterceptorsCatchHook(options, err) {
|
||||
if (options.method.toUpperCase() == RequestMethodsEnum.POST) {
|
||||
console.log('请求失败:', err, options);
|
||||
}
|
||||
return Promise.reject();
|
||||
}
|
||||
};
|
||||
|
||||
// 默认配置
|
||||
const defaultOptions = {
|
||||
requestOptions: {// 请求配置
|
||||
timeout: 10 * 1000,
|
||||
header: { version: '1.0.0' }
|
||||
},
|
||||
baseUrl: `${import.meta.env.VITE_APP_BASE_URL || ''}`,// 基础 URL
|
||||
isReturnDefaultResponse: false,// 是否返回默认响应
|
||||
isTransformResponse: true, // 是否转换响应
|
||||
urlPrefix: '',// url 前缀
|
||||
ignoreCancel: true,// 忽略重复请求取消
|
||||
withToken: true,// 携带 Token
|
||||
isAuth: false,// 接口是否鉴权
|
||||
retryCount: 2,// 重试次数
|
||||
retryTimeout: 300, // 重试超时
|
||||
requestHooks: requestHooks,// 请求 Hook
|
||||
sslVerify: false // 设置为false,不验证 ssl 证书
|
||||
};
|
||||
|
||||
function createRequest(opt) {
|
||||
return new HttpRequest(
|
||||
merge(defaultOptions, opt || {})
|
||||
);
|
||||
}
|
||||
const request = createRequest();
|
||||
export default request;
|
||||
44
src/utils/system.js
Normal file
44
src/utils/system.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// 旧版
|
||||
// const SYSTEM_INFO = uni.getSystemInfoSync();
|
||||
const SYSTEM_INFO = uni.getWindowInfo();
|
||||
|
||||
// 获取状态栏高度
|
||||
// const systemInfo = uni.getSystemInfoSync()
|
||||
// statusBarHeight.value = systemInfo.statusBarHeight || 0
|
||||
|
||||
// 计算导航栏总高度 (状态栏 + 导航栏)
|
||||
// 通常导航栏高度在H5中是44px,小程序中是48px
|
||||
// const platform = systemInfo.platform
|
||||
// navbarHeight.value = statusBarHeight.value + (platform === 'ios' ? 44 : 48)
|
||||
|
||||
//时间电量状态栏高度
|
||||
export const getStatusBarHeight = ()=> SYSTEM_INFO.statusBarHeight;
|
||||
|
||||
export const getTitleBarHeight = ()=>{
|
||||
//关闭按钮胶囊
|
||||
if(uni.getMenuButtonBoundingClientRect){
|
||||
let {top,height} = uni.getMenuButtonBoundingClientRect();
|
||||
return height + (top - getStatusBarHeight())*2
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export const getNavBarHeight = () => getStatusBarHeight()+getTitleBarHeight();
|
||||
|
||||
export const getNavBarPaddingTop = () => {
|
||||
let h=0;
|
||||
if(uni.getMenuButtonBoundingClientRect){
|
||||
let {top,height} = uni.getMenuButtonBoundingClientRect();
|
||||
h = height + (top - getStatusBarHeight());
|
||||
}else{
|
||||
// #ifdef APP-PLUS
|
||||
h=22
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
h=0
|
||||
// #endif
|
||||
}
|
||||
console.log("h=>",h)
|
||||
return h;
|
||||
}
|
||||
43
src/utils/validate.js
Normal file
43
src/utils/validate.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 验证手机号格式是否合法
|
||||
*/
|
||||
export function isPhoneNumber(phoneNumber) {
|
||||
const phoneRegex = /^1[3456789]\d{9}$/;
|
||||
return phoneRegex.test(phoneNumber)
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证身份证号码是否合法
|
||||
*/
|
||||
export function isIdCardValid(idCard) {
|
||||
// 15位身份证号码正则表达式
|
||||
var reg15 = /^[1-9]\d{7}((0[1-9])|(1[0-2]))((0[1-9])|([1-2]\d)|(3[0-1]))\d{3}$/;
|
||||
// 18位身份证号码正则表达式
|
||||
var reg18 = /^[1-9]\d{5}(19|20)\d{2}((0[1-9])|(1[0-2]))((0[1-9])|([1-2]\d)|(3[0-1]))\d{3}(\d|X|x)$/;
|
||||
return reg15.test(idCard) || reg18.test(idCard);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为 http 协议
|
||||
*/
|
||||
export function isHttpProtocol(url) {
|
||||
const httpRegex = /^(http:\/\/)/;
|
||||
return httpRegex.test(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为 https 协议
|
||||
*/
|
||||
export function isHttpsProtocol(url) {
|
||||
const httpRegex = /^(https:\/\/)/;
|
||||
return httpRegex.test(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为 url
|
||||
*/
|
||||
export function isUrl(url) {
|
||||
const urlRegex = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/;
|
||||
return urlRegex.test(url);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user