diff --git a/front-end/mall4v/src/utils/crypto.js b/front-end/mall4v/src/utils/crypto.js index f1ecc2f..011dd77 100644 --- a/front-end/mall4v/src/utils/crypto.js +++ b/front-end/mall4v/src/utils/crypto.js @@ -1,14 +1,40 @@ -import CryptoJS from 'crypto-js' -// 加密 -const keyStr = '-mall4j-password' // 解密用的key -export function encrypt (word) { - const time = Date.now() - - const key = CryptoJS.enc.Utf8.parse(keyStr) - const srcs = CryptoJS.enc.Utf8.parse(time + word) // 加密方式: 时间戳 + 密文 +// 引入 CryptoJS 库 +// CryptoJS 是一个流行的 JavaScript 加密库,提供多种加密算法, +// 包括 AES(高级加密标准)。它允许开发者在客户端对数据进行加密和解密。 +import CryptoJS from 'crypto-js'; + +// 定义用于加密和解密的密钥 +// keyStr 是一个固定的字符串,作为加密和解密时使用的密钥。 +// 注意:在实际应用中,应确保密钥的安全性,避免硬编码或泄露。 +const keyStr = '-mall4j-password'; // 解密用的key + +/** + * 加密函数 + * @param {string} word - 需要加密的明文字符串 + * @returns {string} - 返回经过 AES 加密后的密文字符串 + */ +export function encrypt(word) { + // 获取当前时间戳,用于生成唯一的加密内容前缀 + // 这有助于防止相同的明文产生相同的密文,增加了安全性。 + const time = Date.now(); + + // 将密钥字符串转换为字节数组 (WordArray),以便用于加密操作 + // 使用 Utf8 编码来解析密钥字符串,确保正确处理非ASCII字符。 + const key = CryptoJS.enc.Utf8.parse(keyStr); + + // 将时间戳与需要加密的文本连接起来,并将其转换为字节数组 + // 同样使用 Utf8 编码来解析字符串,以确保正确处理所有字符。 + const srcs = CryptoJS.enc.Utf8.parse(time + word); // 加密方式: 时间戳 + 密文 + + // 使用 AES 算法进行加密 + // mode: ECB 表示使用电子密码本模式,这是一种简单的加密模式,但不适合加密大量数据或要求高安全性的场景。 + // padding: Pkcs7 表示使用 PKCS#7 填充方案,这是AES加密推荐的填充方式。 const encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 - }) - return encrypted.toString() + }); + + // 将加密后的结果转换为 Base64 字符串返回 + // 这样可以方便地在网络上传输或存储加密数据。 + return encrypted.toString(); } diff --git a/front-end/mall4v/src/utils/debounce.js b/front-end/mall4v/src/utils/debounce.js index ecbb79a..4e154ea 100644 --- a/front-end/mall4v/src/utils/debounce.js +++ b/front-end/mall4v/src/utils/debounce.js @@ -1,19 +1,44 @@ -// 防抖 防止表单重复提交 +/** + * 防抖函数 (Debounce Function) + * 用于限制某个函数在短时间内频繁触发时只执行一次。 + * 这对于防止表单重复提交、搜索框输入触发等场景非常有用, + * 因为它可以确保即使用户快速连续点击按钮或输入内容, + * 相关操作也只会被执行一次,直到经过一段设定的时间间隔后才会允许再次触发。 + * + * @param {Function} fn - 要防抖处理的目标函数 + * @param {number} t - 可选参数,指定等待的时间间隔(毫秒),默认为1000ms + * @returns {Function} - 返回一个新的函数,该函数具有防抖行为 + */ export const Debounce = (fn, t) => { - const delay = t || 1000 - let timer + // 设置默认延迟时间为1000毫秒(1秒) + const delay = t || 1000; + + // 定义一个变量来保存定时器ID,用于清除之前的定时器 + let timer; + + // 返回一个新函数,这个函数会替代原始函数被调用 return function () { - const args = arguments + // 保存当前函数调用的所有参数和上下文 + const args = arguments; + const context = this; + + // 如果之前有未完成的定时器,则清除它 if (timer) { - clearTimeout(timer) + clearTimeout(timer); } - const callNow = !timer + // 检查是否是首次调用或上一次调用已经超过了设定的时间间隔 + const callNow = !timer; + // 设置一个新的定时器,在指定的延迟时间后将timer置为空 + // 置空是为了下一次调用时能够正确判断是否立即执行目标函数 timer = setTimeout(() => { - timer = null - }, delay) + timer = null; + }, delay); - if (callNow) fn.apply(this, args) - } -} + // 如果是首次调用或者上一次调用已经超过了设定的时间间隔,则立即执行目标函数 + if (callNow) { + fn.apply(context, args); + } + }; +}; diff --git a/front-end/mall4v/src/utils/http.js b/front-end/mall4v/src/utils/http.js index 109564e..4cdee85 100644 --- a/front-end/mall4v/src/utils/http.js +++ b/front-end/mall4v/src/utils/http.js @@ -1,190 +1,202 @@ -import axios from 'axios' -import qs from 'qs' -import cookie from 'vue-cookies' -import router from '@/router' -import merge from 'lodash/merge' -import { clearLoginInfo } from '@/utils' -import { ElMessage } from 'element-plus' +// 引入必要的库和模块 +import axios from 'axios'; // 用于发起HTTP请求 +import qs from 'qs'; // 用于序列化查询参数 +import cookie from 'vue-cookies'; // 用于管理cookie +import router from '@/router'; // 项目路由实例 +import merge from 'lodash/merge'; // 用于合并对象 +import { clearLoginInfo } from '@/utils'; // 清除登录信息的工具函数 +import { ElMessage } from 'element-plus'; // Element Plus的消息提示组件 +// 创建一个自定义的axios实例 const http = axios.create({ - timeout: 1000 * 30, - withCredentials: true, + timeout: 1000 * 30, // 设置请求超时时间为30秒 + withCredentials: true, // 允许跨域请求时携带凭证(如Cookie) headers: { - 'Content-Type': 'application/json; charset=utf-8' + 'Content-Type': 'application/json; charset=utf-8' // 默认设置请求头为JSON格式 } -}) +}); /** - * 请求拦截 + * 请求拦截器 + * 在每个请求发送之前执行,可以在此处添加通用的请求头或处理请求参数等。 */ http.interceptors.request.use( config => { - config.headers.Authorization = cookie.get('Authorization') // 请求头带上token - // 只针对get方式进行序列化 - if (config.method === 'get' || config.method === 'GET') { - config.paramsSerializer = function (params) { - return qs.stringify(params, { arrayFormat: 'repeat' }) - } + // 如果存在Authorization token,则添加到请求头中 + config.headers.Authorization = cookie.get('Authorization'); + + // 对GET请求进行参数序列化,支持数组格式重复参数 + if (config.method.toLowerCase() === 'get') { + config.paramsSerializer = params => qs.stringify(params, { arrayFormat: 'repeat' }); } - return config + + return config; }, - error => { - return Promise.reject(error) - } -) + error => Promise.reject(error) // 捕获请求错误并返回拒绝的Promise +); /** - * 响应拦截 + * 响应拦截器 + * 在接收到响应数据后执行,可以根据响应状态码或自定义业务逻辑来处理响应。 */ http.interceptors.response.use( response => { - // blob 格式处理 + // 直接返回blob类型的响应数据 if (response.request.responseType === 'blob') { - return response - } - const res = response.data - // 00000 请求成功 - if (res.code === '00000' || res.code === 'A00002') { - return res - } - // A00001 用于直接显示提示用户的错误,内容由输入决定 - if (res.code === 'A00001') { - ElMessage({ - message: res.msg || res.data || 'Error', - type: 'error', - duration: 1.5 * 1000 - }) - return Promise.reject(res) - } - // A00004 未授权 - if (res.code === 'A00004') { - clearLoginInfo() - router.push({ name: 'login' }) + return response; } - // A00005 服务器异常 - if (res.code === 'A00005') { - // eslint-disable-next-line no-console - console.error('============== 请求异常 ==============', '\n', `接口地址: ${response.config.url.replace(import.meta.env.VITE_APP_BASE_API, '')}`, '\n', `异常信息: ${res}`, '\n', '============== 请求异常 end ==========') - ElMessage({ - message: '服务器出了点小差,请稍后再试', - type: 'error', - duration: 1.5 * 1000, - customClass: 'element-error-message-zindex' - }) - return Promise.reject(res) + const res = response.data; + + // 根据不同的响应码执行相应的操作 + switch (res.code) { + case '00000': // 成功响应 + case 'A00002': // 特定成功的响应码 + return res; + + case 'A00001': // 用户级错误消息 + ElMessage({ + message: res.msg || res.data || 'Error', + type: 'error', + duration: 1500 + }); + return Promise.reject(res); + + case 'A00004': // 未授权 + clearLoginInfo(); + router.push({ name: 'login' }); + break; + + case 'A00005': // 服务器异常 + console.error(`接口地址: ${response.config.url.replace(import.meta.env.VITE_APP_BASE_API, '')}`, '\n', `异常信息: ${res}`); + ElMessage({ + message: '服务器出了点小差,请稍后再试', + type: 'error', + duration: 1500, + customClass: 'element-error-message-zindex' + }); + return Promise.reject(res); + + default: + return res; } }, error => { - // eslint-disable-next-line no-console - console.log('========请求失败========', '\n', error.response, '\n', '========请求失败 end========') - switch (error.response.status) { - case 400: + // 处理不同HTTP状态码的错误响应 + console.log('请求失败:', error.response); + const status = error.response?.status; + + switch (status) { + case 400: // 客户端错误 ElMessage({ message: error.response.data, type: 'error', duration: 1500, customClass: 'element-error-message-zindex' - }) - break - case 401: - clearLoginInfo() - router.push({ name: 'login' }) - break - case 405: + }); + break; + + case 401: // 未授权 + clearLoginInfo(); + router.push({ name: 'login' }); + break; + + case 405: // 方法不允许 ElMessage({ message: 'http请求方式有误', type: 'error', duration: 1500, customClass: 'element-error-message-zindex' - }) - break - case 500: + }); + break; + + case 500: // 服务器内部错误 ElMessage({ message: '服务器出了点小差,请稍后再试', type: 'error', duration: 1500, customClass: 'element-error-message-zindex' - }) - break - case 501: + }); + break; + + case 501: // 服务器不支持当前请求功能 ElMessage({ message: '服务器不支持当前请求所需要的某个功能', type: 'error', duration: 1500, customClass: 'element-error-message-zindex' - }) - break + }); + break; } - return Promise.reject(error) + + return Promise.reject(error); } -) +); /** * 请求地址处理 - * @param {*} actionName action方法名称 + * @param {string} actionName - API方法名称 + * @returns {string} - 完整的API请求URL */ -http.adornUrl = actionName => { - return import.meta.env.VITE_APP_BASE_API + actionName -} +http.adornUrl = actionName => import.meta.env.VITE_APP_BASE_API + actionName; /** - * im请求地址处理 - * @param {*} actionName action方法名称 + * IM请求地址处理 + * @param {string} actionName - IM API方法名称 + * @returns {string} - 完整的IM API请求URL */ -http.adornImUrl = actionName => { - return import.meta.env.VITE_APP_IM_API + actionName -} +http.adornImUrl = actionName => import.meta.env.VITE_APP_IM_API + actionName; /** - * im ws 请求地址处理 - * @param {*} actionName action方法名称 + * IM WebSocket请求地址处理 + * @param {string} actionName - WebSocket IM API方法名称 + * @returns {string} - 完整的WebSocket IM API请求URL */ -http.adornWsImUrl = actionName => { - return import.meta.env.VITE_APP_WS_IM_API + actionName -} +http.adornWsImUrl = actionName => import.meta.env.VITE_APP_WS_IM_API + actionName; /** - * get请求参数处理 - * @param {*} params 参数对象 - * @param {*} openDefultParams 是否开启默认参数? + * GET请求参数处理 + * @param {Object} [params={}] - 参数对象 + * @param {boolean} [openDefultParams=true] - 是否开启默认参数 + * @returns {Object} - 处理后的参数对象 */ http.adornParams = (params = {}, openDefultParams = true) => { - const defaults = { - t: Date.now() - } - return openDefultParams ? merge(defaults, params) : params -} + const defaults = { t: Date.now() }; // 添加时间戳以防止缓存 + return openDefultParams ? merge(defaults, params) : params; +}; /** - * post请求数据处理 - * @param {*} data 数据对象 - * @param {*} openDefultdata 是否开启默认数据? - * @param {*} contentType 数据格式 - * json: 'application/json; charset=utf-8' - * form: 'application/x-www-form-urlencoded; charset=utf-8' + * POST请求数据处理 + * @param {Object} [data={}] - 数据对象 + * @param {boolean} [openDefultdata=true] - 是否开启默认数据 + * @param {string} [contentType='json'] - 数据格式:'json' 或 'form' + * @returns {string|FormData} - 处理后的数据 */ http.adornData = (data = {}, openDefultdata = true, contentType = 'json') => { - const defaults = { - t: Date.now() - } - data = openDefultdata ? merge(defaults, data) : data - return contentType === 'json' ? JSON.stringify(data) : qs.stringify(data) -} + const defaults = { t: Date.now() }; + data = openDefultdata ? merge(defaults, data) : data; + + // 根据contentType决定如何序列化数据 + return contentType === 'json' ? JSON.stringify(data) : qs.stringify(data); +}; +/** + * 文件上传函数 + * @param {string} url - 上传文件的目标URL + * @param {File} file - 要上传的文件 + * @returns {Promise} - 返回上传结果的Promise + */ const uploadFile = function (url, file) { const config = { - // 添加请求头 headers: { 'Content-Type': 'multipart/form-data', - Authorization: cookie.get('Authorization') // 请求头带上token + Authorization: cookie.get('Authorization') // 添加token到请求头 } - } - const param = new FormData() - // 通过append向form对象添加数据 - param.append('file', file) - return axios.post(url, param, config) -} - -export default http -export { uploadFile } + }; + const param = new FormData(); + param.append('file', file); // 向FormData对象添加文件 + return axios.post(url, param, config); +}; + +export default http; +export { uploadFile }; diff --git a/front-end/mall4v/src/utils/index.js b/front-end/mall4v/src/utils/index.js index 37196b1..d7fa95d 100644 --- a/front-end/mall4v/src/utils/index.js +++ b/front-end/mall4v/src/utils/index.js @@ -1,109 +1,150 @@ -import cookie from 'vue-cookies' -import router from '@/router' +import cookie from 'vue-cookies'; // 用于管理浏览器中的cookie +import router from '@/router'; // 引入Vue Router实例 /** - * 获取uuid + * 获取UUID(通用唯一识别码) + * @returns {string} - 返回一个随机生成的UUID字符串 */ -export function getUUID () { +export function getUUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { - return (c === 'x' ? (Math.random() * 16) | 0 : 'r&0x3' | '0x8').toString(16) - }) + // 使用位运算生成随机数,并根据'x'或'y'的不同生成不同的值 + return (c === 'x' ? (Math.random() * 16) | 0 : ('r&0x3' | '0x8')).toString(16); + }); } + /** - * 是否有权限 - * @param {*} key + * 检查用户是否有特定权限 + * @param {string} key - 权限标识符 + * @returns {boolean} - 如果用户有此权限则返回true,否则返回false */ -export function isAuth (key) { - const authorities = JSON.parse(sessionStorage.getItem('Authorities') || '[]') +export function isAuth(key) { + const authorities = JSON.parse(sessionStorage.getItem('Authorities') || '[]'); // 尝试从sessionStorage中获取用户权限列表 if (authorities.length) { for (const i in authorities) { - const element = authorities[i] + const element = authorities[i]; if (element === key) { - return true + return true; // 如果找到匹配的权限,则返回true } } } - return false + return false; // 如果没有找到匹配的权限,则返回false } /** * 清除登录信息 */ -export function clearLoginInfo () { - cookie.remove('Authorization') - router.options.isAddDynamicMenuRoutes = false +export function clearLoginInfo() { + cookie.remove('Authorization'); // 移除存储在cookie中的授权token + router.options.isAddDynamicMenuRoutes = false; // 禁用动态路由添加功能 } /** * 树形数据转换 - * @param {*} data - * @param {*} id - * @param {*} pid + * 将扁平的数据结构转换为树形结构 + * @param {Array} data - 扁平化的数据数组 + * @param {string} [id='id'] - 数据项ID字段名,默认为'id' + * @param {string} [pid='parentId'] - 数据项父级ID字段名,默认为'parentId' + * @returns {Array} - 转换后的树形数据结构 */ -export function treeDataTranslate (data, id = 'id', pid = 'parentId') { - const res = [] - const temp = {} +export function treeDataTranslate(data, id = 'id', pid = 'parentId') { + const res = []; // 存储根节点 + const temp = {}; // 临时对象,用于快速查找子节点 + + // 首先将所有元素存入temp对象中,以它们的ID作为键 for (let i = 0; i < data.length; i++) { - temp[data[i][id]] = data[i] + temp[data[i][id]] = data[i]; } + + // 再次遍历data数组,构建树结构 for (let k = 0; k < data.length; k++) { + // 如果当前元素有父级元素且不是自身,则将其作为子元素加入到父元素下 if (temp[data[k][pid]] && data[k][id] !== data[k][pid]) { if (!temp[data[k][pid]].children) { - temp[data[k][pid]].children = [] + temp[data[k][pid]].children = []; } if (!temp[data[k][pid]]._level) { - temp[data[k][pid]]._level = 1 + temp[data[k][pid]]._level = 1; } - data[k]._level = temp[data[k][pid]]._level + 1 - temp[data[k][pid]].children.push(data[k]) + data[k]._level = temp[data[k][pid]]._level + 1; + temp[data[k][pid]].children.push(data[k]); } else { - res.push(data[k]) + // 如果没有父级元素,则认为是根节点,直接加入到结果数组中 + res.push(data[k]); } } - return res + + return res; } -function idListFromTree (data, val, res = [], id = 'id', children = 'children') { +/** + * 递归函数:从树结构中提取指定ID路径 + * @param {Array} data - 树形数据结构 + * @param {any} val - 查找的目标ID + * @param {Array} [res=[]] - 存储找到的ID路径 + * @param {string} [id='id'] - 数据项ID字段名,默认为'id' + * @param {string} [children='children'] - 数据项子元素字段名,默认为'children' + * @returns {boolean} - 是否找到了目标ID + */ +function idListFromTree(data, val, res = [], id = 'id', children = 'children') { for (let i = 0; i < data.length; i++) { - const element = data[i] - if (element[children]) { - if (idListFromTree(element[children], val, res, id, children)) { - res.push(element[id]) - return true - } + const element = data[i]; + + // 递归处理子元素 + if (element[children] && idListFromTree(element[children], val, res, id, children)) { + res.push(element[id]); // 当找到目标时,将当前节点ID加入路径 + return true; } + + // 如果当前节点就是目标节点,直接加入路径并返回 if (element[id] === val) { - res.push(element[id]) - return true + res.push(element[id]); + return true; } } + return false; } + /** * 将数组中的parentId列表取出,倒序排列 + * @param {Array} data - 树形数据结构 + * @param {any} val - 查找的目标ID + * @param {string} [id='id'] - 数据项ID字段名,默认为'id' + * @param {string} [children='children'] - 数据项子元素字段名,默认为'children' + * @returns {Array} - 倒序排列的ID路径 */ -// eslint-disable-next-line no-unused-vars -export function idList (data, val, id = 'id', children = 'children') { - const res = [] - idListFromTree(data, val, res, id) - return res +export function idList(data, val, id = 'id', children = 'children') { + const res = []; + idListFromTree(data, val, res, id, children); // 调用递归函数查找ID路径 + return res.reverse(); // 返回倒序排列的结果 } /** - * 文件地址校验 - * @param fileUrl 获取到的文件路径 + * 文件地址校验与处理 + * @param {string|Array} fileUrl - 获取到的文件路径或文件路径数组 + * @returns {string|Array} - 处理后的文件路径或路径数组 */ -export function checkFileUrl (fileUrl) { - if (fileUrl === '') return '' - const baseUrl = import.meta.env.VITE_APP_RESOURCES_URL - // 适配el-image 图片组件预览功能 - if (fileUrl && typeof fileUrl === 'object') { - // eslint-disable-next-line no-return-assign - return fileUrl.map(el => el = checkFileUrl(el)) - } else { - if (fileUrl && fileUrl.indexOf('http') === -1) { - return baseUrl + fileUrl - } else { - return fileUrl - } +export function checkFileUrl(fileUrl) { + if (fileUrl === '') return ''; // 如果输入为空字符串,直接返回空字符串 + + const baseUrl = import.meta.env.VITE_APP_RESOURCES_URL; // 获取资源基础URL + + // 如果fileUrl是一个对象(如el-image组件传递的对象),则递归处理每个元素 + if (fileUrl && typeof fileUrl === 'object' && !Array.isArray(fileUrl)) { + return Object.keys(fileUrl).reduce((acc, key) => { + acc[key] = checkFileUrl(fileUrl[key]); + return acc; + }, {}); } + + // 如果fileUrl是数组,则映射处理每个元素 + if (Array.isArray(fileUrl)) { + return fileUrl.map(el => checkFileUrl(el)); + } + + // 对于单个文件路径,如果它不是绝对路径,则拼接基础URL + if (fileUrl && typeof fileUrl === 'string' && fileUrl.indexOf('http') === -1) { + return baseUrl + fileUrl; + } + + return fileUrl; // 如果已经是绝对路径或无效路径,则直接返回原值 } diff --git a/front-end/mall4v/src/utils/unitOption.js b/front-end/mall4v/src/utils/unitOption.js index 120bbec..58b3090 100644 --- a/front-end/mall4v/src/utils/unitOption.js +++ b/front-end/mall4v/src/utils/unitOption.js @@ -1,102 +1,35 @@ +/** + * 单位选项列表 + * 此数组包含了多个对象,每个对象代表一个可能的商品单位, + * 用于在用户界面中提供选择,比如商品库存管理、订单录入等场景。 + * 每个对象都有两个属性: + * - label: 显示给用户的文本标签 + * - value: 实际存储或传递的数据值 + */ export const unitOption = [ - { - lable: '件', - value: '件' - }, - { - lable: '盒', - value: '盒' - }, - { - lable: '箱', - value: '箱' - }, - { - lable: '包', - value: '包' - }, - { - lable: '瓶', - value: '瓶' - }, - { - lable: '只', - value: '只' - }, - { - lable: '千克', - value: '千克' - }, - { - lable: '克', - value: '克' - }, - { - lable: '斤', - value: '斤' - }, - { - lable: '两', - value: '两' - }, - { - lable: '双', - value: '双' - }, - { - lable: '套', - value: '套' - }, - { - lable: '对', - value: '对' - }, - { - lable: '块', - value: '块' - }, - { - lable: '台', - value: '台' - }, - { - lable: '本', - value: '本' - }, - { - lable: '把', - value: '把' - }, - { - lable: '码', - value: '码' - }, - { - lable: '捆', - value: '捆' - }, - { - lable: '提', - value: '提' - }, - { - lable: '杯', - value: '杯' - }, - { - lable: '听', - value: '听' - }, - { - lable: '条', - value: '条' - }, - { - lable: '副', - value: '副' - }, - { - lable: '顶', - value: '顶' - } -] + { label: '件', value: '件' }, // 通用计数单位 + { label: '盒', value: '盒' }, // 适用于装在盒子中的物品 + { label: '箱', value: '箱' }, // 大量物品的包装单位 + { label: '包', value: '包' }, // 适用于包裹或小袋装的物品 + { label: '瓶', value: '瓶' }, // 适用于液体或粉末状物品 + { label: '只', value: '只' }, // 适用于单个独立物品,如手套、袜子等 + { label: '千克', value: '千克' }, // 重量单位,1000克 + { label: '克', value: '克' }, // 较小的重量单位 + { label: '斤', value: '斤' }, // 传统中国重量单位,等于500克 + { label: '两', value: '两' }, // 传统中国重量单位,等于50克 + { label: '双', value: '双' }, // 适用于成对物品,如鞋子、筷子等 + { label: '套', value: '套' }, // 适用于一组或多件相关联的物品 + { label: '对', value: '对' }, // 适用于成对物品,类似“双” + { label: '块', value: '块' }, // 适用于固体物品,如巧克力、石头等 + { label: '台', value: '台' }, // 适用于大型设备或机器 + { label: '本', value: '本' }, // 适用于书籍或其他装订成册的物品 + { label: '把', value: '把' }, // 适用于手持工具或武器 + { label: '码', value: '码' }, // 长度单位,约等于0.9144米 + { label: '捆', value: '捆' }, // 适用于捆绑在一起的物品 + { label: '提', value: '提' }, // 适用于手提袋或携带方便的物品 + { label: '杯', value: '杯' }, // 适用于饮品或小容量容器 + { label: '听', value: '听' }, // 适用于罐装食品或饮料 + { label: '条', value: '条' }, // 适用于细长形物品,如鱼、香烟等 + { label: '副', value: '副' }, // 适用于眼镜、耳环等成对使用的物品 + { label: '顶', value: '顶' } // 适用于帽子、帐篷等覆盖物 +]; diff --git a/front-end/mall4v/src/utils/validate.js b/front-end/mall4v/src/utils/validate.js index 6462d1e..c991492 100644 --- a/front-end/mall4v/src/utils/validate.js +++ b/front-end/mall4v/src/utils/validate.js @@ -1,49 +1,69 @@ /** - * 邮箱 - * @param {*} s + * 验证邮箱格式是否正确 + * @param {string} s - 要验证的字符串 + * @returns {boolean} - 如果符合标准邮箱格式则返回true,否则返回false */ -export function isEmail (s) { - return /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(s) +export function isEmail(s) { + // 正则表达式用于匹配标准的电子邮件地址格式: + // - 用户名部分只能包含字母、数字、下划线或连字符 + // - 域名部分同样只允许字母、数字、下划线或连字符 + // - 域名后缀长度为2到3个字符,并且可以有一个或两个层级(例如.com, .co.uk) + return /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(s); } /** - * 手机号码 - * @param {*} s + * 验证手机号码格式是否正确 + * @param {string} s - 要验证的字符串 + * @returns {boolean} - 如果符合中国大陆手机号码格式则返回true,否则返回false */ -export function isMobile (s) { - return /^1[0-9]{10}$/.test(s) +export function isMobile(s) { + // 正则表达式用于匹配中国大陆的手机号码: + // - 以1开头,后面跟随10位数字 + return /^1[0-9]{10}$/.test(s); } /** - * 电话号码 - * @param {*} s + * 验证电话号码格式是否正确 + * @param {string} s - 要验证的字符串 + * @returns {boolean} - 如果符合固定电话号码格式则返回true,否则返回false */ -export function isPhone (s) { - return /^([0-9]{3,4}-)?[0-9]{7,8}$/.test(s) +export function isPhone(s) { + // 正则表达式用于匹配中国大陆的固定电话号码: + // - 可选区号(3到4位数字),用短横线分隔 + // - 主号码由7到8位数字组成 + return /^([0-9]{3,4}-)?[0-9]{7,8}$/.test(s); } /** - * URL地址 - * @param {*} s + * 验证URL地址格式是否正确 + * @param {string} s - 要验证的字符串 + * @returns {boolean} - 如果符合标准URL格式则返回true,否则返回false */ -export function isURL (s) { - return /^http[s]?:\/\/.*/.test(s) +export function isURL(s) { + // 正则表达式用于匹配标准的URL地址: + // - 开头必须是http:// 或 https:// + // - 后面跟任意数量的字符 + return /^http[s]?:\/\/.*/.test(s); } /** - * qq - * @param {*} s + * 验证QQ号码格式是否正确 + * @param {string} s - 要验证的字符串 + * @returns {boolean} - 如果符合QQ号码格式则返回true,否则返回false */ -export function isQq (s) { - return /[1-9][0-9]{4,14}/.test(s) +export function isQq(s) { + // 正则表达式用于匹配有效的QQ号码: + // - QQ号码至少5位,最多15位数字 + // - 不能以0开头 + return /^[1-9][0-9]{4,14}$/.test(s); } /** - * 判断是否全为空格 只要有一个其他字符返回false - * @param {String} str - * @returns {Boolean} + * 判断字符串是否全为空格,只要有一个非空格字符即返回false + * @param {string} str - 要验证的字符串 + * @returns {boolean} - 如果字符串全部为空格则返回true,否则返回false */ -export function validNoEmptySpace (str) { - const reg = /^\s+$/g - return reg.test(str) +export function validNoEmptySpace(str) { + const reg = /^\s+$/g; // 匹配整个字符串全是空白字符(包括空格、制表符等)的正则表达式 + return reg.test(str); // 如果字符串完全匹配上述规则,则返回true;否则返回false }