新增:全量优化

This commit is contained in:
yuantao
2025-11-05 16:20:06 +08:00
parent ca6bf7f211
commit 65656f1810
27 changed files with 2407 additions and 292 deletions

30
common/constants/app.js Normal file
View File

@@ -0,0 +1,30 @@
/**
* 应用相关常量
*/
// 应用信息
export const APP_NAME = '小程序模板'
export const APP_VERSION = '1.0.0'
// 页面路径常量
export const PAGE_PATHS = {
INDEX: '/pages/index/index',
WELCOME: '/subPages/welcome/index',
LOGIN: '/pages/login/login', // 示例路径
REGISTER: '/pages/register/register' // 示例路径
}
// 存储键名常量
export const STORAGE_KEYS = {
TOKEN: 'token',
USER_INFO: 'userInfo',
THEME: 'theme',
LANG: 'language'
}
// 事件常量
export const EVENTS = {
USER_LOGIN: 'userLogin',
USER_LOGOUT: 'userLogout',
THEME_CHANGE: 'themeChange'
}

View File

@@ -1,118 +1,97 @@
// 定义内外边距,历遍1-100
@for $i from 0 through 100 {
// 只要双数和能被5除尽的数
@if $i % 2==0 or $i % 5==0 {
// 得出u-margin-30或者u-m-30
.w-#{$i} {
width: calc($i * 1%) !important;
}
.p-x-#{$i} {
padding-left: $i + rpx !important;
padding-right: $i + rpx !important;
}
.p-y-#{$i} {
padding-top: $i + rpx !important;
padding-bottom: $i + rpx !important;
}
.m-x-#{$i} {
margin-left: $i + rpx !important;
margin-right: $i + rpx !important;
}
.m-y-#{$i} {
margin-top: $i + rpx !important;
margin-bottom: $i + rpx !important;
}
.m-#{$i} {
margin-left: $i + rpx !important;
margin-right: $i + rpx !important;
margin-top: $i + rpx !important;
margin-bottom: $i + rpx !important;
}
.p-#{$i} {
padding-left: $i + rpx !important;
padding-right: $i + rpx !important;
padding-top: $i + rpx !important;
padding-bottom: $i + rpx !important;
}
.m-l-#{$i} {
margin-left: $i + rpx !important;
}
.m-t-#{$i} {
margin-top: $i + rpx !important;
}
.m-r-#{$i} {
margin-right: $i + rpx !important;
}
.m-b-#{$i} {
margin-bottom: $i + rpx !important;
}
.p-l-#{$i} {
padding-left: $i + rpx !important;
}
.p-t-#{$i} {
padding-top: $i + rpx !important;
}
.p-r-#{$i} {
padding-right: $i + rpx !important;
}
.p-b-#{$i} {
padding-bottom: $i + rpx !important;
}
.l-p-#{$i} {
letter-spacing: $i + rpx !important;
}
.z-i-#{$i} {
z-index: $i;
}
.l-h-#{$i} {
line-height: $i + rpx !important;
}
.r-#{$i} {
right: $i + rpx !important;
}
.l-#{$i} {
left: $i + rpx !important;
}
.t-#{$i} {
top: $i + rpx !important;
}
.b-#{$i} {
bottom: $i + rpx !important;
}
// 定义内外边距,只生成常用的数值
$spacing-sizes: (0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100);
@each $i in $spacing-sizes {
.w-#{$i} {
width: calc($i * 1%) !important;
}
.p-x-#{$i} {
padding-left: $i + rpx !important;
padding-right: $i + rpx !important;
}
.p-y-#{$i} {
padding-top: $i + rpx !important;
padding-bottom: $i + rpx !important;
}
.m-x-#{$i} {
margin-left: $i + rpx !important;
margin-right: $i + rpx !important;
}
.m-y-#{$i} {
margin-top: $i + rpx !important;
margin-bottom: $i + rpx !important;
}
.m-#{$i} {
margin-left: $i + rpx !important;
margin-right: $i + rpx !important;
margin-top: $i + rpx !important;
margin-bottom: $i + rpx !important;
}
.p-#{$i} {
padding-left: $i + rpx !important;
padding-right: $i + rpx !important;
padding-top: $i + rpx !important;
padding-bottom: $i + rpx !important;
}
.m-l-#{$i} {
margin-left: $i + rpx !important;
}
.m-t-#{$i} {
margin-top: $i + rpx !important;
}
.m-r-#{$i} {
margin-right: $i + rpx !important;
}
.m-b-#{$i} {
margin-bottom: $i + rpx !important;
}
.p-l-#{$i} {
padding-left: $i + rpx !important;
}
.p-t-#{$i} {
padding-top: $i + rpx !important;
}
.p-r-#{$i} {
padding-right: $i + rpx !important;
}
.p-b-#{$i} {
padding-bottom: $i + rpx !important;
}
.l-p-#{$i} {
letter-spacing: $i + rpx !important;
}
.z-i-#{$i} {
z-index: $i;
}
.l-h-#{$i} {
line-height: $i + rpx !important;
}
.r-#{$i} {
right: $i + rpx !important;
}
.l-#{$i} {
left: $i + rpx !important;
}
.t-#{$i} {
top: $i + rpx !important;
}
.b-#{$i} {
bottom: $i + rpx !important;
}
}
// 定义字体(rpx)单位大于或等于20的都为rpx单位字体
@for $i from 9 through 60 {
// 定义字体(rpx)单位,只生成常用的字体大小
$font-sizes: (9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 40, 44, 48, 52, 56, 60);
@each $i in $font-sizes {
.font-#{$i} {
font-size: $i + rpx !important;
}
}
// 圆角
@for $i from 4 through 60 {
$rounded-sizes: (4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60);
@each $i in $rounded-sizes {
.rounded-#{$i} {
border-radius: $i + rpx;
}
}
// 多行文本溢出
@for $i from 1 through 5 {
.over-line-#{$i} {
@@ -125,3 +104,120 @@
-webkit-box-orient: vertical;
}
}
// 显示相关工具类
.display-none {
display: none !important;
}
.display-block {
display: block !important;
}
.display-inline {
display: inline !important;
}
.display-inline-block {
display: inline-block !important;
}
.display-flex {
display: flex !important;
}
// 浮动相关
.float-left {
float: left !important;
}
.float-right {
float: right !important;
}
.float-none {
float: none !important;
}
.clearfix::after {
content: '';
display: table;
clear: both;
}
// 文本对齐
.text-left {
text-align: left !important;
}
.text-center {
text-align: center !important;
}
.text-right {
text-align: right !important;
}
.text-justify {
text-align: justify !important;
}
// 文本装饰
.text-underline {
text-decoration: underline !important;
}
.text-line-through {
text-decoration: line-through !important;
}
.text-no-underline {
text-decoration: none !important;
}
// 字体粗细
.font-normal {
font-weight: normal !important;
}
.font-bold {
font-weight: bold !important;
}
// 文本大小写
.text-uppercase {
text-transform: uppercase !important;
}
.text-lowercase {
text-transform: lowercase !important;
}
.text-capitalize {
text-transform: capitalize !important;
}
// 垂直对齐
.align-baseline {
vertical-align: baseline !important;
}
.align-top {
vertical-align: top !important;
}
.align-middle {
vertical-align: middle !important;
}
.align-bottom {
vertical-align: bottom !important;
}
// 位置相关
.position-relative {
position: relative !important;
}
.position-absolute {
position: absolute !important;
}
.position-fixed {
position: fixed !important;
}
.position-static {
position: static !important;
}
// 溢出处理
.overflow-auto {
overflow: auto !important;
}
.overflow-hidden {
overflow: hidden !important;
}
.overflow-visible {
overflow: visible !important;
}
.overflow-scroll {
overflow: scroll !important;
}
// 可见性
.visible {
visibility: visible !important;
}
.invisible {
visibility: hidden !important;
}

View File

@@ -0,0 +1,88 @@
/* 暗黑模式样式 */
:root {
// 默认使用亮色模式变量
--uni-color-primary: #007aff;
--uni-color-success: #4cd964;
--uni-color-warning: #f0ad4e;
--uni-color-error: #dd524d;
--uni-text-color: #333;
--uni-text-color-inverse: #fff;
--uni-text-color-grey: #999;
--uni-text-color-placeholder: #808080;
--uni-text-color-disable: #c0c0c0;
--uni-bg-color: #ffffff;
--uni-bg-color-grey: #f8f8f8;
--uni-bg-color-hover: #f1f1f1;
--uni-bg-color-mask: rgba(0, 0, 0, 0.4);
--uni-border-color: #c8c7cc;
}
/* 暗黑模式 */
[data-theme='dark'] {
--uni-color-primary: #0a84ff;
--uni-color-success: #34c759;
--uni-color-warning: #ff9500;
--uni-color-error: #ff3b30;
--uni-text-color: #f2f2f7;
--uni-text-color-inverse: #000000;
--uni-text-color-grey: #8e8e93;
--uni-text-color-placeholder: #4c4c4c;
--uni-text-color-disable: #636366;
--uni-bg-color: #1c1c1e;
--uni-bg-color-grey: #2c2c2e;
--uni-bg-color-hover: #3a3a3c;
--uni-bg-color-mask: rgba(0, 0, 0, 0.6);
--uni-border-color: #434346;
}
/* 系统级暗黑模式 */
@media (prefers-color-scheme: dark) {
:root:not([data-theme='light']) {
--uni-color-primary: #0a84ff;
--uni-color-success: #34c759;
--uni-color-warning: #ff9500;
--uni-color-error: #ff3b30;
--uni-text-color: #f2f2f7;
--uni-text-color-inverse: #000000;
--uni-text-color-grey: #8e8e93;
--uni-text-color-placeholder: #4c4c4c;
--uni-text-color-disable: #636366;
--uni-bg-color: #1c1c1e;
--uni-bg-color-grey: #2c2c2e;
--uni-bg-color-hover: #3a3a3c;
--uni-bg-color-mask: rgba(0, 0, 0, 0.6);
--uni-border-color: #434346;
}
}
/* 应用暗黑模式的通用类 */
.dark-mode {
background-color: var(--uni-bg-color);
color: var(--uni-text-color);
}
.dark-mode .dark-invert {
background-color: var(--uni-bg-color-inverse);
color: var(--uni-text-color-inverse);
}
/* 通用暗黑模式切换类 */
.light-mode {
background-color: var(--uni-bg-color);
color: var(--uni-text-color);
}
/* 通用暗黑模式切换类 */
.dark-mode {
background-color: var(--uni-bg-color);
color: var(--uni-text-color);
}

80
common/utils/env.js Normal file
View File

@@ -0,0 +1,80 @@
/**
* 环境变量验证工具
*/
/**
* 验证必需的环境变量
* @param {Array<string>} requiredVars 必需的环境变量键名数组
* @throws {Error} 如果有任何必需的环境变量缺失则抛出错误
*/
export function validateEnv(requiredVars = []) {
const missingVars = []
for (const key of requiredVars) {
if (!import.meta.env[key]) {
missingVars.push(key)
}
}
if (missingVars.length > 0) {
throw new Error(`缺少必需的环境变量: ${missingVars.join(', ')}`)
}
}
/**
* 获取环境变量,带默认值和类型转换
* @param {string} key 环境变量键名
* @param {any} defaultValue 默认值
* @param {string} type 类型 ('string' | 'number' | 'boolean' | 'array')
* @returns {any} 环境变量值
*/
export function getEnv(key, defaultValue = null, type = 'string') {
const value = import.meta.env[key]
if (value === undefined || value === null || value === '') {
return defaultValue
}
switch (type) {
case 'number':
const numValue = Number(value)
return isNaN(numValue) ? defaultValue : numValue
case 'boolean':
return value === 'true' || value === '1'
case 'array':
try {
return JSON.parse(value)
} catch (e) {
return Array.isArray(defaultValue) ? defaultValue : []
}
case 'string':
default:
return value
}
}
/**
* 环境变量配置
*/
export const EnvConfig = {
// API基础URL
BASE_URL: getEnv('VITE_BASE_URL', '', 'string'),
// 静态资源URL
ASSETS_URL: getEnv('VITE_ASSETSURL', '', 'string'),
// 小程序APPID
APP_ID: getEnv('VITE_APPID', '', 'string'),
// UNI-APPID
UNI_APP_ID: getEnv('VITE_UNI_APPID', '', 'string'),
// 是否为开发环境
IS_DEV: getEnv('NODE_ENV', 'development', 'string') === 'development',
// 是否为生产环境
IS_PROD: getEnv('NODE_ENV', 'development', 'string') === 'production',
// 调试模式
DEBUG: getEnv('VITE_DEBUG', false, 'boolean'),
// API超时时间毫秒
API_TIMEOUT: getEnv('VITE_API_TIMEOUT', 10000, 'number'),
}
// 验证必需的环境变量
try {
validateEnv(['VITE_BASE_URL', 'VITE_APPID'])
} catch (error) {
console.error('环境变量验证失败:', error.message)
// 在开发环境中抛出错误
if (EnvConfig.IS_DEV) {
throw error
}
}
export default EnvConfig

View File

@@ -1,6 +1,5 @@
const baseUrl = import.meta.env.VITE_BASE_URL
const assetsUrl = import.meta.env.VITE_ASSETSURL
/**
* 工具类 - 提供常用的工具方法
* @class Tool
@@ -13,11 +12,9 @@ class Tool {
SUCCESS: 1,
LOADING: 2,
}
// 字体加载状态缓存
this.loadedFonts = new Set()
}
/**
* 文字轻提示
* @param {string} str 提示文字
@@ -30,13 +27,11 @@ class Tool {
console.warn('alert方法需要提供提示文字')
return
}
const iconMap = {
[this.ICON_TYPES.NONE]: 'none',
[this.ICON_TYPES.SUCCESS]: 'success',
[this.ICON_TYPES.LOADING]: 'loading',
}
uni.showToast({
title: String(str),
icon: iconMap[icon] || 'none',
@@ -49,7 +44,6 @@ class Tool {
})
})
}
/**
* 显示loading加载
* @param {string} [title=' '] 加载文案
@@ -58,14 +52,12 @@ class Tool {
loading(title = ' ', mask = true) {
uni.showLoading({ title, mask })
}
/**
* 关闭loading提示框
*/
hideLoading() {
uni.hideLoading()
}
/**
* 统一处理URL格式确保以/开头
* @param {string} url 页面地址
@@ -76,17 +68,14 @@ class Tool {
if (!url || typeof url !== 'string') {
throw new Error('URL必须是字符串')
}
return url.startsWith('/') ? url : `/${url}`
}
/**
* 可返回跳转(导航到新页面)
* @param {string} url 页面地址
*/
navigateTo(url) {
const formattedUrl = this._formatUrl(url)
uni.navigateTo({
url: formattedUrl,
fail: err => {
@@ -95,7 +84,6 @@ class Tool {
},
})
}
/**
* 不可返回跳转(重定向到新页面)
* @param {string} url 页面地址
@@ -103,7 +91,6 @@ class Tool {
redirectTo(url) {
uni.redirectTo({ url: this._formatUrl(url) })
}
/**
* 清除页面栈跳转(重新启动到新页面)
* @param {string} url 页面地址
@@ -111,7 +98,6 @@ class Tool {
reLaunch(url) {
uni.reLaunch({ url: this._formatUrl(url) })
}
/**
* 跳转tabBar页
* @param {string} url 页面地址
@@ -119,7 +105,6 @@ class Tool {
switchTab(url) {
uni.switchTab({ url: this._formatUrl(url) })
}
/**
* 返回上一页面或指定页面
* @param {number} [delta=1] 返回的页面数
@@ -127,7 +112,6 @@ class Tool {
*/
navigateBack(delta = 1, fallbackUrl = '/pages/index/index') {
const pages = getCurrentPages()
if (pages.length <= 1) {
console.warn('无上一页,使用回退地址')
uni.reLaunch({ url: fallbackUrl })
@@ -135,7 +119,6 @@ class Tool {
uni.navigateBack({ delta })
}
}
/**
* 操作本地缓存
* @param {string} key 缓存键值
@@ -146,24 +129,20 @@ class Tool {
if (typeof key !== 'string') {
throw new Error('key必须是字符串')
}
// 设置操作
if (value !== undefined && value !== null) {
uni.setStorageSync(key, value)
return
}
// 读取操作
if (key !== '#') {
return uni.getStorageSync(key)
}
// 特殊操作
if (key === '#') {
uni.clearStorageSync()
}
}
/**
* 删除指定缓存
* @param {string} key 要删除的缓存键
@@ -172,10 +151,8 @@ class Tool {
if (typeof key !== 'string') {
throw new Error('key必须是字符串')
}
uni.removeStorageSync(key)
}
/**
* 获取缓存信息
* @returns {Object} 缓存信息
@@ -183,7 +160,6 @@ class Tool {
getStorageInfo() {
return uni.getStorageInfoSync()
}
/**
* 复制文本到剪贴板
* @param {string} data 要复制的文本
@@ -194,7 +170,6 @@ class Tool {
this.alert('暂无内容')
return false
}
try {
await new Promise((resolve, reject) => {
uni.setClipboardData({
@@ -203,7 +178,6 @@ class Tool {
fail: reject,
})
})
this.alert('复制成功')
return true
} catch (error) {
@@ -212,7 +186,6 @@ class Tool {
return false
}
}
/**
* 导入外部字体
* @param {string} fontName 字体文件名(不含路径)
@@ -222,15 +195,12 @@ class Tool {
if (!fontName || typeof fontName !== 'string') {
throw new Error('字体名称必须是字符串')
}
// 检查是否已加载过
if (this.loadedFonts.has(fontName)) {
return true
}
try {
const fontFamily = fontName.replace(/\.[^/.]+$/, '') // 移除文件扩展名
await new Promise((resolve, reject) => {
uni.loadFontFace({
family: fontFamily,
@@ -240,7 +210,6 @@ class Tool {
fail: reject,
})
})
this.loadedFonts.add(fontName)
return true
} catch (error) {
@@ -248,7 +217,6 @@ class Tool {
return false
}
}
/**
* 保存图片到相册
* @param {string} url 图片URL
@@ -259,7 +227,6 @@ class Tool {
this.alert('图片地址不能为空')
return false
}
try {
// 检查权限
const { authSetting } = await new Promise((resolve, reject) => {
@@ -268,7 +235,6 @@ class Tool {
fail: reject,
})
})
if (!authSetting['scope.writePhotosAlbum']) {
// 请求权限
await new Promise((resolve, reject) => {
@@ -279,7 +245,6 @@ class Tool {
})
})
}
// 获取图片信息
const { path } = await new Promise((resolve, reject) => {
uni.getImageInfo({
@@ -288,7 +253,6 @@ class Tool {
fail: reject,
})
})
// 保存到相册
await new Promise((resolve, reject) => {
uni.saveImageToPhotosAlbum({
@@ -297,12 +261,10 @@ class Tool {
fail: reject,
})
})
this.alert('已保存到相册')
return true
} catch (error) {
console.error('保存图片失败:', error)
if (error.errMsg && error.errMsg.includes('auth')) {
// 权限相关错误
await new Promise(resolve => {
@@ -313,16 +275,240 @@ class Tool {
success: resolve,
})
})
uni.openSetting()
} else {
this.alert('保存失败,请重试')
}
return false
}
}
/**
* 防抖函数
* @param {Function} func 要防抖的函数
* @param {number} wait 延迟时间(ms)
* @param {boolean} immediate 是否立即执行
* @returns {Function} 防抖后的函数
*/
debounce(func, wait, immediate = false) {
let timeout
return function (...args) {
const later = () => {
timeout = null
if (!immediate) func.apply(this, args)
}
const callNow = immediate && !timeout
clearTimeout(timeout)
timeout = setTimeout(later, wait)
if (callNow) func.apply(this, args)
}
}
/**
* 节流函数
* @param {Function} func 要节流的函数
* @param {number} wait 延迟时间(ms)
* @returns {Function} 节流后的函数
*/
throttle(func, wait) {
let timeout
let previous = 0
return function (...args) {
const now = Date.now()
const remaining = wait - (now - previous)
const context = this
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
previous = now
func.apply(context, args)
} else if (!timeout) {
timeout = setTimeout(() => {
previous = Date.now()
timeout = null
func.apply(context, args)
}, remaining)
}
}
}
/**
* 日期格式化
* @param {Date|string|number} date 日期对象、字符串或时间戳
* @param {string} fmt 格式字符串, 默认为 'YYYY-MM-DD HH:mm:ss'
* @returns {string} 格式化后的日期字符串
*/
formatDate(date, fmt = 'YYYY-MM-DD HH:mm:ss') {
// 如果传入的是时间戳转换为Date对象
if (typeof date === 'number') {
date = new Date(date)
} else if (typeof date === 'string') {
// 如果传入的是字符串尝试转换为Date对象
date = new Date(date)
}
if (!(date instanceof Date) || isNaN(date.getTime())) {
throw new Error('Invalid date')
}
const o = {
'Y+': date.getFullYear(),
'M+': date.getMonth() + 1,
'D+': date.getDate(),
'H+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds(),
'q+': Math.floor((date.getMonth() + 3) / 3),
S: date.getMilliseconds(),
}
for (let k in o) {
if (new RegExp('(' + k + ')').test(fmt)) {
let str = o[k] + ''
if (k === 'S') {
fmt = fmt.replace(RegExp.$1, str.padStart(3, '0'))
} else {
fmt = fmt.replace(RegExp.$1, str.length === 1 ? '0' + str : str)
}
}
}
return fmt
}
/**
* 深拷贝函数
* @param {*} obj 要深拷贝的对象
* @returns {*} 拷贝后的对象
*/
deepClone(obj) {
// 处理null、undefined和原始类型
if (obj === null || typeof obj !== 'object') {
return obj
}
// 处理日期对象
if (obj instanceof Date) {
return new Date(obj.getTime())
}
// 处理数组和对象
if (obj instanceof Array) {
return obj.map(item => this.deepClone(item))
}
// 处理普通对象
const clonedObj = {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clonedObj[key] = this.deepClone(obj[key])
}
}
return clonedObj
}
/**
* 表单验证工具
* @class Validator
*/
getValidator() {
const rules = {
/**
* 必填验证
* @param {*} value 值
* @param {boolean} required 是否必填
* @returns {boolean} 验证结果
*/
required(value, required = true) {
if (!required) return true
return value !== undefined && value !== null && value !== ''
},
/**
* 邮箱验证
* @param {string} value 值
* @returns {boolean} 验证结果
*/
email(value) {
if (!value) return true
const reg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
return reg.test(value)
},
/**
* 手机号验证
* @param {string} value 值
* @returns {boolean} 验证结果
*/
mobile(value) {
if (!value) return true
const reg = /^1[3-9]\d{9}$/
return reg.test(value)
},
/**
* 最小长度验证
* @param {string} value 值
* @param {number} length 最小长度
* @returns {boolean} 验证结果
*/
minLength(value, length) {
if (!value) return true
return value.length >= length
},
/**
* 最大长度验证
* @param {string} value 值
* @param {number} length 最大长度
* @returns {boolean} 验证结果
*/
maxLength(value, length) {
if (!value) return true
return value.length <= length
},
/**
* 数值范围验证
* @param {number} value 值
* @param {number} min 最小值
* @param {number} max 最大值
* @returns {boolean} 验证结果
*/
range(value, min, max) {
if (value === undefined || value === null) return true
return value >= min && value <= max
},
}
return {
/**
* 验证单个字段
* @param {*} value 值
* @param {Object} rule 规则
* @returns {string|null} 错误信息
*/
validateField(value, rule) {
// 必填验证
if (rule.required !== undefined && !rules.required(value, rule.required)) {
return rule.message || '此字段为必填项'
}
// 如果值为空且非必填,跳过其他验证
if (value === undefined || value === null || value === '') {
return null
}
// 其他验证规则
if (rule.type && rules[rule.type]) {
if (!rules[rule.type](value, rule.param1, rule.param2)) {
return rule.message || '字段格式不正确'
}
}
return null
},
/**
* 验证整个表单
* @param {Object} data 表单数据
* @param {Object} rules 验证规则
* @returns {Object} 验证结果 { isValid: boolean, errors: Object }
*/
validate(data, rules) {
const errors = {}
let isValid = true
for (let field in rules) {
const error = this.validateField(data[field], rules[field])
if (error) {
errors[field] = error
isValid = false
}
}
return { isValid, errors }
},
}
}
/**
* 微信支付
* @param {Object} paymentData 支付参数
@@ -338,6 +524,11 @@ class Tool {
})
})
}
/**
* 文件上传
* @param {string} filePath 文件路径
* @returns {Promise<Object>} 上传结果
*/
upload(filePath) {
return new Promise((resolve, reject) => {
uni.uploadFile({
@@ -357,7 +548,120 @@ class Tool {
})
})
}
/**
* 暗黑模式切换
* @param {boolean} isDark 是否启用暗黑模式
*/
toggleDarkMode(isDark) {
try {
if (isDark) {
// 启用暗黑模式
document.documentElement.setAttribute('data-theme', 'dark')
uni.setStorageSync('theme', 'dark')
} else {
// 启用亮色模式
document.documentElement.setAttribute('data-theme', 'light')
uni.setStorageSync('theme', 'light')
}
} catch (error) {
console.error('切换主题失败:', error)
}
}
/**
* 获取当前主题模式
* @returns {string} 当前主题 ('light' | 'dark')
*/
getCurrentTheme() {
try {
// 优先使用用户设置的主题
const savedTheme = uni.getStorageSync('theme')
if (savedTheme) {
return savedTheme
}
// 检查系统主题
const systemInfo = uni.getSystemInfoSync()
if (systemInfo.theme) {
return systemInfo.theme
}
// 默认返回亮色主题
return 'light'
} catch (error) {
console.error('获取主题失败:', error)
return 'light'
}
}
/**
* 权限检查
* @param {string} permission 权限标识
* @returns {boolean} 是否拥有权限
*/
hasPermission(permission) {
try {
// 获取用户权限列表
const permissions = uni.getStorageSync('permissions') || []
return permissions.includes(permission)
} catch (error) {
console.error('权限检查失败:', error)
return false
}
}
/**
* 设置用户权限
* @param {Array<string>} permissions 权限列表
*/
setPermissions(permissions) {
try {
uni.setStorageSync('permissions', Array.isArray(permissions) ? permissions : [])
} catch (error) {
console.error('设置权限失败:', error)
}
}
/**
* 获取用户所有权限
* @returns {Array<string>} 权限列表
*/
getUserPermissions() {
try {
return uni.getStorageSync('permissions') || []
} catch (error) {
console.error('获取权限失败:', error)
return []
}
}
/**
* 检查用户是否有任意权限
* @param {Array<string>} permissions 权限列表
* @returns {boolean} 是否有任意权限
*/
hasAnyPermission(permissions) {
if (!Array.isArray(permissions)) {
return false
}
const userPermissions = this.getUserPermissions()
return permissions.some(permission => userPermissions.includes(permission))
}
/**
* 检查用户是否拥有所有权限
* @param {Array<string>} permissions 权限列表
* @returns {boolean} 是否拥有所有权限
*/
hasAllPermissions(permissions) {
if (!Array.isArray(permissions)) {
return false
}
const userPermissions = this.getUserPermissions()
return permissions.every(permission => userPermissions.includes(permission))
}
/**
* 清除用户权限
*/
clearPermissions() {
try {
uni.removeStorageSync('permissions')
} catch (error) {
console.error('清除权限失败:', error)
}
}
}
// 创建单例并导出
export default new Tool()