diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..552220f --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,33 @@ +module.exports = { + root: true, + env: { + browser: true, + es2021: true, + node: true + }, + extends: [ + 'eslint:recommended', + 'plugin:vue/vue3-recommended', + 'plugin:prettier/recommended' + ], + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module' + }, + globals: { + uni: 'readonly', + wx: 'readonly', + plus: 'readonly' + }, + rules: { + 'vue/multi-word-component-names': 'off', + 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'prettier/prettier': [ + 'error', + { + endOfLine: 'auto' + } + ] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d557f3c --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +dist/ +unpackage/ +.DS_Store +*.log +.env.local +.env.*.local diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..36af219 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx lint-staged diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..8116af0 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,8 @@ +module.exports = { + semi: false, + singleQuote: true, + printWidth: 100, + trailingComma: 'none', + arrowParens: 'avoid', + endOfLine: 'auto' +} diff --git a/docs/uView使用指南.md b/docs/uView使用指南.md new file mode 100644 index 0000000..84b0695 --- /dev/null +++ b/docs/uView使用指南.md @@ -0,0 +1,151 @@ +# uView-Plus 使用指南 + +## 已完成集成步骤 + +1. ✅ 安装依赖:`npm install uview-plus --save` +2. ✅ 在 `main.js` 中引入并注册 +3. ✅ 创建 `src/uni.scss` 并引入 uView 主题变量 +4. ✅ 在 `vite.config.js` 中配置 SCSS 预处理器 +5. ✅ 在 `pages.json` 中配置 easycom 自动引入 + +## 配置说明 + +### uni.scss +```scss +/* uView-Plus 主题变量配置 */ +@import 'uview-plus/theme.scss'; +``` + +### vite.config.js +```javascript +css: { + preprocessorOptions: { + scss: { + additionalData: '@import "@/uni.scss"; @import "@/styles/variables.scss"; @import "@/styles/mixins.scss";', + api: 'modern-compiler', + silenceDeprecations: ['legacy-js-api', 'import'] + } + } +} +``` + +### pages.json +```json +"easycom": { + "autoscan": true, + "custom": { + "^u-(.*)": "uview-plus/components/u-$1/u-$1.vue" + } +} +``` + +## 常用组件示例 + +### 1. 按钮 (u-button) +```vue + + + +``` + +### 2. 加载动画 (u-loading-icon) +```vue + + +``` + +### 3. 弹窗 (u-popup) +```vue + + 弹窗内容 + +``` + +### 4. 输入框 (u-input) +```vue + +``` + +### 5. 表单 (u-form) +```vue + + + + + +``` + +### 6. Toast 提示 +```javascript +// 在组件中使用 +this.$u.toast('提示内容') +``` + +### 7. 日历 (u-calendar) +```vue + +``` + +### 8. 时间选择器 (u-datetime-picker) +```vue + +``` + +## 可以优化的现有组件 + +### LoadingOverlay.vue +可以使用 `u-loading-icon` 替代自定义加载动画: +```vue + +``` + +### BookingPopup.vue +可以使用 `u-popup` 替代自定义弹窗: +```vue + + + +``` + +可以使用 `u-form` 和 `u-form-item` 优化表单: +```vue + + + + + + + + +``` + +可以使用 `u-calendar` 替代自定义日期选择器 + +## 文档链接 + +- 官方文档:https://uview-plus.jiangruyi.com/ +- 组件列表:https://uview-plus.jiangruyi.com/components/intro.html + +## 注意事项 + +1. uview-plus 是专为 Vue3 + uni-app 设计的组件库 +2. 组件通过 easycom 自动引入,无需手动 import +3. 所有组件以 `u-` 开头 +4. 支持主题定制和暗黑模式 diff --git a/docs/多端适配方案.md b/docs/多端适配方案.md new file mode 100644 index 0000000..5ceb79b --- /dev/null +++ b/docs/多端适配方案.md @@ -0,0 +1,403 @@ + + + +# 多端适配方案 + +## 一、平台差异处理 + +### 1.1 条件编译 +使用 UniApp 的条件编译处理不同平台的差异: + +```javascript +// #ifdef MP-WEIXIN +// 微信小程序特有代码 +wx.login() +// #endif + +// #ifdef H5 +// H5特有代码 +window.location.href = '/login' +// #endif + +// #ifdef APP-PLUS +// App特有代码 +plus.runtime.restart() +// #endif +``` + +### 1.2 平台判断工具 +使用封装的平台判断工具: + +```javascript +import { getPlatform, isMiniProgram, isH5, isApp } from '@/utils/platform' + +if (isMiniProgram()) { + // 小程序端逻辑 +} else if (isH5()) { + // H5端逻辑 +} else if (isApp()) { + // App端逻辑 +} +``` + +## 二、API 适配 + +### 2.1 统一 API 封装 +对不同平台的 API 进行统一封装: + +```javascript +// 跨端导航 +export const navigateTo = (url, params = {}) => { + const query = Object.keys(params) + .map(key => `${key}=${encodeURIComponent(params[key])}`) + .join('&') + const fullUrl = query ? `${url}?${query}` : url + + uni.navigateTo({ url: fullUrl }) +} + +// 跨端存储 +export const setStorage = (key, value) => { + // #ifdef MP-WEIXIN + wx.setStorageSync(key, value) + // #endif + + // #ifdef H5 + localStorage.setItem(key, JSON.stringify(value)) + // #endif + + // #ifdef APP-PLUS + plus.storage.setItem(key, JSON.stringify(value)) + // #endif +} +``` + +### 2.2 第三方登录适配 +不同平台的第三方登录实现: + +```javascript +// 微信登录 +export const wechatLogin = () => { + return new Promise((resolve, reject) => { + // #ifdef MP-WEIXIN + wx.login({ + success: (res) => { + resolve(res.code) + }, + fail: reject + }) + // #endif + + // #ifdef H5 + // H5微信授权登录 + const appId = 'YOUR_APP_ID' + const redirectUri = encodeURIComponent(window.location.href) + window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_userinfo#wechat_redirect` + // #endif + }) +} +``` + +## 三、样式适配 + +### 3.1 尺寸单位适配 +- **小程序端**:使用 `rpx`(750rpx = 屏幕宽度) +- **H5端**:使用 `rem` 或 `vw` +- **App端**:使用 `rpx` 或 `upx` + +```scss +// 统一使用 rpx,UniApp 会自动转换 +.container { + width: 750rpx; + padding: 30rpx; + font-size: 28rpx; +} +``` + +### 3.2 安全区域适配 +处理刘海屏、底部安全区域: + +```scss +.page { + // 顶部安全区域 + padding-top: constant(safe-area-inset-top); + padding-top: env(safe-area-inset-top); + + // 底部安全区域 + padding-bottom: constant(safe-area-inset-bottom); + padding-bottom: env(safe-area-inset-bottom); +} +``` + +### 3.3 状态栏高度适配 +```javascript +// 获取状态栏高度 +const systemInfo = uni.getSystemInfoSync() +const statusBarHeight = systemInfo.statusBarHeight + +// 在样式中使用 +const navBarHeight = statusBarHeight + 44 // 44为导航栏高度 +``` + +## 四、功能适配 + +### 4.1 分享功能 +```javascript +// 小程序分享 +// #ifdef MP-WEIXIN +onShareAppMessage() { + return { + title: '分享标题', + path: '/pages/index/index', + imageUrl: '/static/share.png' + } +} +// #endif + +// H5分享 +// #ifdef H5 +const shareToWechat = () => { + // 调用微信 JS-SDK + wx.updateAppMessageShareData({ + title: '分享标题', + desc: '分享描述', + link: window.location.href, + imgUrl: 'https://example.com/share.png' + }) +} +// #endif +``` + +### 4.2 支付功能 +```javascript +// 统一支付接口 +export const pay = (orderInfo) => { + return new Promise((resolve, reject) => { + // #ifdef MP-WEIXIN + wx.requestPayment({ + timeStamp: orderInfo.timeStamp, + nonceStr: orderInfo.nonceStr, + package: orderInfo.package, + signType: 'MD5', + paySign: orderInfo.paySign, + success: resolve, + fail: reject + }) + // #endif + + // #ifdef H5 + // H5微信支付 + WeixinJSBridge.invoke('getBrandWCPayRequest', orderInfo, (res) => { + if (res.err_msg === 'get_brand_wcpay_request:ok') { + resolve(res) + } else { + reject(res) + } + }) + // #endif + + // #ifdef APP-PLUS + // App支付 + plus.payment.request('wxpay', orderInfo, resolve, reject) + // #endif + }) +} +``` + +### 4.3 定位功能 +```javascript +// 获取位置 +export const getLocation = () => { + return new Promise((resolve, reject) => { + uni.getLocation({ + type: 'gcj02', + success: (res) => { + resolve({ + latitude: res.latitude, + longitude: res.longitude + }) + }, + fail: (err) => { + // #ifdef H5 + // H5使用浏览器定位API + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition( + (position) => { + resolve({ + latitude: position.coords.latitude, + longitude: position.coords.longitude + }) + }, + reject + ) + } else { + reject(new Error('浏览器不支持定位')) + } + // #endif + + // #ifndef H5 + reject(err) + // #endif + } + }) + }) +} +``` + +## 五、性能适配 + +### 5.1 小程序分包 +```json +{ + "pages": [ + "pages/index/index", + "pages/user/index" + ], + "subPackages": [ + { + "root": "subPackages/order", + "pages": [ + "list/index", + "detail/index" + ] + }, + { + "root": "subPackages/product", + "pages": [ + "list/index", + "detail/index" + ] + } + ], + "preloadRule": { + "pages/index/index": { + "network": "all", + "packages": ["subPackages/order"] + } + } +} +``` + +### 5.2 图片懒加载 +```vue + +``` + +### 5.3 长列表优化 +```vue + +``` + +## 六、兼容性处理 + +### 6.1 API 兼容性检查 +```javascript +// 检查API是否支持 +if (uni.canIUse('getSystemInfoSync')) { + const systemInfo = uni.getSystemInfoSync() +} + +// 版本号比较 +const compareVersion = (v1, v2) => { + const arr1 = v1.split('.') + const arr2 = v2.split('.') + const len = Math.max(arr1.length, arr2.length) + + for (let i = 0; i < len; i++) { + const num1 = parseInt(arr1[i] || 0) + const num2 = parseInt(arr2[i] || 0) + + if (num1 > num2) return 1 + if (num1 < num2) return -1 + } + + return 0 +} + +// 使用示例 +const systemInfo = uni.getSystemInfoSync() +if (compareVersion(systemInfo.SDKVersion, '2.10.0') >= 0) { + // 支持新API +} +``` + +### 6.2 样式兼容性 +```scss +// 使用 CSS 变量实现主题切换 +:root { + --primary-color: #1890ff; + --text-color: #333; +} + +.button { + background-color: var(--primary-color); + color: var(--text-color); +} + +// 使用 autoprefixer 自动添加前缀 +.flex-container { + display: flex; + // 自动添加 -webkit-flex +} +``` + +## 七、调试技巧 + +### 7.1 多端调试 +- **微信小程序**:使用微信开发者工具 +- **H5**:使用浏览器开发者工具 +- **App**:使用 HBuilderX 真机调试 + +### 7.2 日志输出 +```javascript +// 开发环境输出日志 +if (process.env.NODE_ENV === 'development') { + console.log('调试信息:', data) +} + +// 使用条件编译输出平台信息 +// #ifdef MP-WEIXIN +console.log('当前平台:微信小程序') +// #endif +``` + +### 7.3 错误监控 +```javascript +// 全局错误捕获 +uni.onError((error) => { + console.error('全局错误:', error) + // 上报到错误监控平台 +}) + +// 网络错误监控 +uni.onNetworkStatusChange((res) => { + if (!res.isConnected) { + uni.showToast({ + title: '网络已断开', + icon: 'none' + }) + } +}) +``` diff --git a/docs/开发规范.md b/docs/开发规范.md new file mode 100644 index 0000000..49295a5 --- /dev/null +++ b/docs/开发规范.md @@ -0,0 +1,300 @@ +# 开发规范文档 + +## 一、命名规范 + +### 1.1 文件命名 +- **页面文件**:使用小写字母,多个单词用短横线连接,如 `user-profile.vue` +- **组件文件**:使用大驼峰命名,如 `UserCard.vue`、`LoadingView.vue` +- **工具类文件**:使用小写字母,多个单词用短横线连接,如 `request.js`、`storage.js` +- **常量文件**:使用小写字母,如 `constants.js`、`env.js` + +### 1.2 变量命名 +- **普通变量**:使用小驼峰命名,如 `userName`、`userInfo` +- **常量**:使用全大写字母,单词间用下划线连接,如 `API_BASE_URL`、`MAX_RETRY_COUNT` +- **私有变量**:以下划线开头,如 `_privateMethod` +- **布尔值**:以 `is`、`has`、`can` 等开头,如 `isLogin`、`hasPermission` + +### 1.3 函数命名 +- **普通函数**:使用小驼峰命名,动词开头,如 `getUserInfo`、`handleClick` +- **事件处理函数**:以 `handle` 或 `on` 开头,如 `handleSubmit`、`onLoad` +- **工具函数**:使用动词开头,如 `formatDate`、`validatePhone` + +### 1.4 组件命名 +- **全局组件**:使用大驼峰命名,至少两个单词,如 `LoadingView`、`EmptyView` +- **页面组件**:使用大驼峰命名,如 `LoginPage`、`UserProfile` +- **业务组件**:使用大驼峰命名,体现业务含义,如 `UserCard`、`OrderList` + +## 二、代码注释规范 + +### 2.1 文件注释 +每个文件开头必须包含文件说明注释: + +```javascript +/** + * 用户相关接口 + * @author 张三 + * @date 2024-01-01 + */ +``` + +### 2.2 函数注释 +关键函数必须添加注释,说明功能、参数、返回值: + +```javascript +/** + * 用户登录 + * @param {object} data 登录数据 + * @param {string} data.phone 手机号 + * @param {string} data.password 密码 + * @returns {Promise} 登录结果 + */ +export const login = (data) => { + return request.post('/auth/login', data) +} +``` + +### 2.3 复杂逻辑注释 +对于复杂的业务逻辑,必须添加行内注释: + +```javascript +// 检查Token是否过期 +if (tokenExpireTime < Date.now()) { + // Token已过期,刷新Token + await refreshToken() +} +``` + +### 2.4 TODO注释 +待完成的功能使用 TODO 标记: + +```javascript +// TODO: 添加图片压缩功能 +// FIXME: 修复在iOS端的兼容性问题 +``` + +## 三、代码风格规范 + +### 3.1 缩进与空格 +- 使用 2 个空格缩进 +- 运算符前后加空格 +- 逗号后加空格 +- 对象属性冒号后加空格 + +### 3.2 引号使用 +- JavaScript 统一使用单引号 +- HTML 属性统一使用双引号 + +### 3.3 分号使用 +- 语句结尾不使用分号(根据 Prettier 配置) + +### 3.4 代码长度 +- 单行代码不超过 100 个字符 +- 函数代码行数不超过 50 行 +- 文件代码行数不超过 500 行 + +## 四、Vue 组件规范 + +### 4.1 组件结构顺序 +```vue + + + + + +``` + +### 4.2 Props 定义 +Props 必须定义类型、默认值和校验: + +```javascript +props: { + title: { + type: String, + required: true + }, + count: { + type: Number, + default: 0, + validator: (value) => value >= 0 + } +} +``` + +### 4.3 事件命名 +- 使用短横线命名:`@update-value` +- 使用动词开头:`@click`、`@change`、`@submit` + +## 五、Git 提交规范 + +### 5.1 提交信息格式 +``` +(): + + + +