commit 5dc52ce6df67d6189b5e24f16b535484b6e0cd7f Author: liyongde <1419499670@qq.com> Date: Mon Jan 12 19:13:10 2026 +0800 init project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b8356fc --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# uni-app +unpackage +.hbuilderx + diff --git a/App.vue b/App.vue new file mode 100644 index 0000000..decbe2a --- /dev/null +++ b/App.vue @@ -0,0 +1,21 @@ + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..7625f73 --- /dev/null +++ b/README.md @@ -0,0 +1,136 @@ +# nl-uniapp + +基于 Vue3 + Vite + Ant Design Vue 的 uni-app 项目模板 + +## 技术栈 + +- **框架**: Vue 3 +- **构建工具**: Vite +- **UI组件库**: Ant Design Vue 4.x +- **状态管理**: Pinia +- **HTTP请求**: Axios (已封装) +- **样式预处理**: Sass/SCSS + +## 项目结构 + +``` +nl-uniapp/ +├── pages/ # 页面目录 +│ └── index/ # 首页 +├── src/ +│ ├── api/ # API接口 +│ │ └── index.js # 接口定义 +│ ├── stores/ # Pinia状态管理 +│ │ └── user.js # 用户状态 +│ ├── utils/ # 工具函数 +│ │ └── request.js # axios封装 +│ └── styles/ # 样式文件 +│ ├── common.scss # 公共样式 +│ └── variables.scss # 样式变量 +├── static/ # 静态资源 +├── App.vue # 应用入口 +├── main.js # 主入口文件 +├── pages.json # 页面配置 +├── manifest.json # 应用配置 +├── vite.config.js # Vite配置 +└── package.json # 项目配置 +``` + +## 快速开始 + +### 安装依赖 + +```bash +npm install +# 或 +yarn install +# 或 +pnpm install +``` + +### 开发运行 + +```bash +# H5 +npm run dev:h5 + +# 微信小程序 +npm run dev:mp-weixin + +# 支付宝小程序 +npm run dev:mp-alipay + +# 其他平台... +``` + +### 构建打包 + +```bash +# H5 +npm run build:h5 + +# 微信小程序 +npm run build:mp-weixin + +# 其他平台... +``` + +## 功能特性 + +### 1. Axios封装 + +已封装好的axios请求工具,位于 `src/utils/request.js`,包含: + +- 请求/响应拦截器 +- 自动添加token +- 统一错误处理 +- Loading状态管理 +- 支持GET、POST、PUT、DELETE、文件上传等方法 + +使用示例: + +```javascript +import { api } from '@/api' + +// GET请求 +const res = await api.getList({ page: 1 }) + +// POST请求 +const res = await api.create({ name: '测试' }) +``` + +### 2. 状态管理 + +使用Pinia进行状态管理,示例store位于 `src/stores/user.js` + +### 3. API接口管理 + +所有API接口统一管理在 `src/api/index.js`,方便维护和调用 + +### 4. 样式系统 + +- 支持SCSS预处理器 +- 统一的样式变量定义 +- 公共样式类 + +## 注意事项 + +1. **Ant Design Vue兼容性**: Ant Design Vue主要针对H5端,在小程序端可能不完全兼容,建议: + - H5端使用Ant Design Vue组件 + - 小程序端使用uni-app原生组件或uni-ui组件库 + +2. **API配置**: 请在 `src/utils/request.js` 中修改 `baseURL` 为你的实际API地址 + +3. **Token管理**: Token会自动从store中获取并添加到请求头,请确保正确设置token + +## 开发建议 + +1. 页面开发时,优先使用uni-app原生组件以保证跨平台兼容性 +2. 样式使用rpx单位以适配不同屏幕尺寸 +3. API请求统一使用封装好的request工具 +4. 状态管理使用Pinia,避免直接使用localStorage + +## License + +MIT + diff --git a/env.example b/env.example new file mode 100644 index 0000000..ed80a8e --- /dev/null +++ b/env.example @@ -0,0 +1,11 @@ +# 环境变量配置示例 +# 复制此文件为 .env.development 和 .env.production 并修改相应配置 + +# 开发环境 +VITE_APP_BASE_API=/api +VITE_APP_TITLE=nl-uniapp + +# 生产环境 +# VITE_APP_BASE_API=https://your-api-domain.com +# VITE_APP_TITLE=nl-uniapp + diff --git a/index.html b/index.html new file mode 100644 index 0000000..16ea8d8 --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + + nl-uniapp + + + +
+ + + + diff --git a/main.js b/main.js new file mode 100644 index 0000000..128d1bb --- /dev/null +++ b/main.js @@ -0,0 +1,18 @@ +import { createSSRApp } from 'vue' +import App from './App.vue' +import Antd from 'ant-design-vue' +import 'ant-design-vue/dist/reset.css' +import { createPinia } from 'pinia' + +export function createApp() { + const app = createSSRApp(App) + const pinia = createPinia() + + app.use(pinia) + app.use(Antd) + + return { + app + } +} + diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..eaf9610 --- /dev/null +++ b/manifest.json @@ -0,0 +1,116 @@ +{ + "name": "nl-uniapp", + "appid": "__UNI__NLUNAPP", + "description": "uniapp项目", + "versionName": "1.0.0", + "versionCode": "100", + "transformPx": false, + "app-plus": { + "usingComponents": true, + "nvueStyleCompiler": "uni-app", + "compilerVersion": 3, + "splashscreen": { + "alwaysShowBeforeRender": true, + "waiting": true, + "autoclose": true, + "delay": 0 + }, + "modules": {}, + "distribute": { + "android": { + "permissions": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + }, + "ios": {}, + "sdkConfigs": {} + } + }, + "quickapp": {}, + "mp-weixin": { + "appid": "", + "setting": { + "urlCheck": false, + "es6": true, + "enhance": true, + "postcss": true, + "preloadBackgroundData": false, + "minified": true, + "newFeature": false, + "coverView": true, + "nodeModules": false, + "autoAudits": false, + "showShadowRootInWxmlPanel": true, + "scopeDataCheck": false, + "uglifyFileName": false, + "checkInvalidKey": true, + "checkSiteMap": true, + "uploadWithSourceMap": true, + "compileHotReLoad": false, + "lazyloadPlaceholderEnable": false, + "useMultiFrameRuntime": true, + "useApiHook": true, + "useApiHostProcess": true, + "babelSetting": { + "ignore": [], + "disablePlugins": [], + "outputPath": "" + }, + "enableEngineNative": false, + "useIsolateContext": true, + "userConfirmedBundleSwitch": false, + "packNpmManually": false, + "packNpmRelationList": [], + "minifyWXSS": true, + "showES6CompileOption": false, + "minifyWXML": true, + "useStaticServer": true + }, + "usingComponents": true, + "permission": { + "scope.userLocation": { + "desc": "你的位置信息将用于小程序位置接口的效果展示" + } + }, + "optimization": { + "subPackages": true + } + }, + "mp-alipay": { + "usingComponents": true + }, + "mp-baidu": { + "usingComponents": true + }, + "mp-toutiao": { + "usingComponents": true + }, + "uniStatistics": { + "enable": false + }, + "vueVersion": "3", + "h5": { + "devServer": { + "port": 3000, + "disableHostCheck": true + }, + "router": { + "mode": "hash" + } + } +} + diff --git a/package.json b/package.json new file mode 100644 index 0000000..fb993f9 --- /dev/null +++ b/package.json @@ -0,0 +1,66 @@ +{ + "name": "nl-uniapp", + "version": "1.0.0", + "description": "uniapp项目 - Vue3 + Vite + Ant Design Vue", + "main": "main.js", + "scripts": { + "install": "npm install", + "dev:h5": "uni", + "dev:mp-weixin": "uni -p mp-weixin", + "dev:mp-alipay": "uni -p mp-alipay", + "dev:mp-baidu": "uni -p mp-baidu", + "dev:mp-toutiao": "uni -p mp-toutiao", + "dev:mp-qq": "uni -p mp-qq", + "dev:mp-kuaishou": "uni -p mp-kuaishou", + "dev:mp-lark": "uni -p mp-lark", + "dev:quickapp-webview": "uni -p quickapp-webview", + "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei", + "dev:quickapp-webview-union": "uni -p quickapp-webview-union", + "build:h5": "uni build", + "build:mp-weixin": "uni build -p mp-weixin", + "build:mp-alipay": "uni build -p mp-alipay", + "build:mp-baidu": "uni build -p mp-baidu", + "build:mp-toutiao": "uni build -p mp-toutiao", + "build:mp-qq": "uni build -p mp-qq", + "build:mp-kuaishou": "uni build -p mp-kuaishou", + "build:mp-lark": "uni build -p mp-lark", + "build:quickapp-webview": "uni build -p quickapp-webview", + "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei", + "build:quickapp-webview-union": "uni build -p quickapp-webview-union" + }, + "dependencies": { + "@dcloudio/uni-app": "^3.0.0", + "@dcloudio/uni-app-plus": "^3.0.0", + "@dcloudio/uni-components": "^3.0.0", + "@dcloudio/uni-h5": "^3.0.0", + "@dcloudio/uni-mp-alipay": "^3.0.0", + "@dcloudio/uni-mp-baidu": "^3.0.0", + "@dcloudio/uni-mp-kuaishou": "^3.0.0", + "@dcloudio/uni-mp-lark": "^3.0.0", + "@dcloudio/uni-mp-qq": "^3.0.0", + "@dcloudio/uni-mp-toutiao": "^3.0.0", + "@dcloudio/uni-mp-weixin": "^3.0.0", + "@dcloudio/uni-quickapp-webview": "^3.0.0", + "ant-design-vue": "^4.0.0", + "axios": "^1.6.0", + "pinia": "^2.1.0", + "vue": "^3.3.0" + }, + "devDependencies": { + "@dcloudio/types": "^3.4.0", + "@dcloudio/uni-automator": "^3.0.0", + "@dcloudio/uni-cli-shared": "^3.0.0", + "@dcloudio/vite-plugin-uni": "^3.0.0", + "@vitejs/plugin-vue": "^4.5.0", + "sass": "^1.69.0", + "vite": "^5.0.0" + }, + "browserslist": [ + "Android >= 4.4", + "ios >= 9" + ], + "uni-app": { + "scripts": {} + } +} + diff --git a/pages.json b/pages.json new file mode 100644 index 0000000..67a7aae --- /dev/null +++ b/pages.json @@ -0,0 +1,37 @@ +{ + "easycom": { + "autoscan": true, + "custom": { + "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue" + } + }, + "pages": [ + { + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "首页" + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "nl-uniapp", + "navigationBarBackgroundColor": "#F8F8F8", + "backgroundColor": "#F8F8F8" + }, + "tabBar": { + "color": "#7A7E83", + "selectedColor": "#3cc51f", + "borderStyle": "black", + "backgroundColor": "#ffffff", + "list": [ + { + "pagePath": "pages/index/index", + "iconPath": "static/tab-home.png", + "selectedIconPath": "static/tab-home-current.png", + "text": "首页" + } + ] + } +} + diff --git a/pages/index/index.vue b/pages/index/index.vue new file mode 100644 index 0000000..be2bc7e --- /dev/null +++ b/pages/index/index.vue @@ -0,0 +1,291 @@ + + + + + + diff --git a/src/api/index.js b/src/api/index.js new file mode 100644 index 0000000..60df025 --- /dev/null +++ b/src/api/index.js @@ -0,0 +1,77 @@ +import request from '@/utils/request' +import { useUserStore } from '@/stores/user' + +// 获取token的辅助函数 +function getToken() { + try { + const userStore = useUserStore() + return userStore.getToken() || '' + } catch (e) { + return '' + } +} + +// 示例API接口 +export const api = { + // 获取用户信息 + getUserInfo() { + return request.get('/user/info') + }, + + // 登录 + login(data) { + return request.post('/user/login', data) + }, + + // 退出登录 + logout() { + return request.post('/user/logout') + }, + + // 获取列表数据 + getList(params) { + return request.get('/list', params) + }, + + // 创建数据 + create(data) { + return request.post('/create', data) + }, + + // 更新数据 + update(id, data) { + return request.put(`/update/${id}`, data) + }, + + // 删除数据 + delete(id) { + return request.delete(`/delete/${id}`) + }, + + // 上传文件 + uploadFile(filePath) { + return new Promise((resolve, reject) => { + const baseURL = import.meta.env.VITE_APP_BASE_API || (process.env.NODE_ENV === 'development' ? '/api' : 'https://your-api-domain.com') + uni.uploadFile({ + url: baseURL + '/upload', + filePath: filePath, + name: 'file', + header: { + Authorization: `Bearer ${getToken()}` + }, + success: (res) => { + try { + const data = JSON.parse(res.data) + resolve(data) + } catch (e) { + resolve(res.data) + } + }, + fail: (err) => { + reject(err) + } + }) + }) + } +} + diff --git a/src/components/README.md b/src/components/README.md new file mode 100644 index 0000000..3e89268 --- /dev/null +++ b/src/components/README.md @@ -0,0 +1,25 @@ +# 组件目录 + +此目录用于存放自定义组件。 + +## 使用方式 + +在页面中引入组件: + +```vue + + + +``` + +## 组件命名规范 + +- 组件文件名使用 PascalCase(大驼峰命名) +- 组件名与文件名保持一致 + diff --git a/src/stores/user.js b/src/stores/user.js new file mode 100644 index 0000000..d51e1d3 --- /dev/null +++ b/src/stores/user.js @@ -0,0 +1,55 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useUserStore = defineStore('user', () => { + const token = ref('') + const userInfo = ref(null) + + // 设置token + function setToken(val) { + token.value = val + // 可以同时存储到本地 + uni.setStorageSync('token', val) + } + + // 获取token + function getToken() { + if (!token.value) { + token.value = uni.getStorageSync('token') || '' + } + return token.value + } + + // 设置用户信息 + function setUserInfo(info) { + userInfo.value = info + uni.setStorageSync('userInfo', info) + } + + // 获取用户信息 + function getUserInfo() { + if (!userInfo.value) { + userInfo.value = uni.getStorageSync('userInfo') || null + } + return userInfo.value + } + + // 退出登录 + function logout() { + token.value = '' + userInfo.value = null + uni.removeStorageSync('token') + uni.removeStorageSync('userInfo') + } + + return { + token, + userInfo, + setToken, + getToken, + setUserInfo, + getUserInfo, + logout + } +}) + diff --git a/src/styles/common.scss b/src/styles/common.scss new file mode 100644 index 0000000..3cda76a --- /dev/null +++ b/src/styles/common.scss @@ -0,0 +1,122 @@ +/* 全局样式 */ +page { + background-color: #f5f5f5; + font-size: 14px; + color: #333; +} + +/* 通用容器 */ +.container { + padding: 16px; +} + +/* 通用卡片 */ +.card { + background: #fff; + border-radius: 8px; + padding: 16px; + margin-bottom: 16px; +} + +/* 通用按钮 */ +.btn { + padding: 12px 24px; + border-radius: 4px; + border: none; + font-size: 14px; + cursor: pointer; + + &.btn-primary { + background-color: #1890ff; + color: #fff; + } + + &.btn-success { + background-color: #52c41a; + color: #fff; + } + + &.btn-warning { + background-color: #faad14; + color: #fff; + } + + &.btn-error { + background-color: #f5222d; + color: #fff; + } +} + +/* 通用文本 */ +.text-primary { + color: #1890ff; +} + +.text-success { + color: #52c41a; +} + +.text-warning { + color: #faad14; +} + +.text-error { + color: #f5222d; +} + +.text-center { + text-align: center; +} + +.text-right { + text-align: right; +} + +/* 通用布局 */ +.flex { + display: flex; +} + +.flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +.flex-between { + display: flex; + align-items: center; + justify-content: space-between; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +/* 通用间距 */ +.mt-xs { margin-top: 4px; } +.mt-sm { margin-top: 8px; } +.mt-md { margin-top: 16px; } +.mt-lg { margin-top: 24px; } + +.mb-xs { margin-bottom: 4px; } +.mb-sm { margin-bottom: 8px; } +.mb-md { margin-bottom: 16px; } +.mb-lg { margin-bottom: 24px; } + +.ml-xs { margin-left: 4px; } +.ml-sm { margin-left: 8px; } +.ml-md { margin-left: 16px; } +.ml-lg { margin-left: 24px; } + +.mr-xs { margin-right: 4px; } +.mr-sm { margin-right: 8px; } +.mr-md { margin-right: 16px; } +.mr-lg { margin-right: 24px; } + +.p-xs { padding: 4px; } +.p-sm { padding: 8px; } +.p-md { padding: 16px; } +.p-lg { padding: 24px; } + diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 0000000..d18ab5a --- /dev/null +++ b/src/styles/variables.scss @@ -0,0 +1,34 @@ +// 主题颜色 +$primary-color: #1890ff; +$success-color: #52c41a; +$warning-color: #faad14; +$error-color: #f5222d; +$info-color: #1890ff; + +// 文字颜色 +$text-color: #333333; +$text-color-secondary: #666666; +$text-color-disabled: #999999; + +// 背景颜色 +$bg-color: #ffffff; +$bg-color-secondary: #f5f5f5; + +// 边框颜色 +$border-color: #e8e8e8; +$border-radius: 4px; + +// 间距 +$spacing-xs: 4px; +$spacing-sm: 8px; +$spacing-md: 16px; +$spacing-lg: 24px; +$spacing-xl: 32px; + +// 字体大小 +$font-size-xs: 12px; +$font-size-sm: 14px; +$font-size-md: 16px; +$font-size-lg: 18px; +$font-size-xl: 20px; + diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 0000000..259a400 --- /dev/null +++ b/src/utils/index.js @@ -0,0 +1,164 @@ +/** + * 工具函数集合 + */ + +/** + * 格式化日期 + * @param {Date|string|number} date 日期 + * @param {string} format 格式,默认 'YYYY-MM-DD HH:mm:ss' + * @returns {string} 格式化后的日期字符串 + */ +export function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') { + const d = new Date(date) + const year = d.getFullYear() + const month = String(d.getMonth() + 1).padStart(2, '0') + const day = String(d.getDate()).padStart(2, '0') + const hour = String(d.getHours()).padStart(2, '0') + const minute = String(d.getMinutes()).padStart(2, '0') + const second = String(d.getSeconds()).padStart(2, '0') + + return format + .replace('YYYY', year) + .replace('MM', month) + .replace('DD', day) + .replace('HH', hour) + .replace('mm', minute) + .replace('ss', second) +} + +/** + * 防抖函数 + * @param {Function} func 要防抖的函数 + * @param {number} wait 等待时间(毫秒) + * @returns {Function} 防抖后的函数 + */ +export function debounce(func, wait = 300) { + let timeout + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout) + func(...args) + } + clearTimeout(timeout) + timeout = setTimeout(later, wait) + } +} + +/** + * 节流函数 + * @param {Function} func 要节流的函数 + * @param {number} limit 时间限制(毫秒) + * @returns {Function} 节流后的函数 + */ +export function throttle(func, limit = 300) { + let inThrottle + return function executedFunction(...args) { + if (!inThrottle) { + func(...args) + inThrottle = true + setTimeout(() => (inThrottle = false), limit) + } + } +} + +/** + * 深拷贝 + * @param {any} obj 要拷贝的对象 + * @returns {any} 拷贝后的对象 + */ +export function deepClone(obj) { + 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 => deepClone(item)) + if (typeof obj === 'object') { + const clonedObj = {} + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + clonedObj[key] = deepClone(obj[key]) + } + } + return clonedObj + } +} + +/** + * 生成唯一ID + * @returns {string} 唯一ID + */ +export function generateId() { + return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}` +} + +/** + * 存储数据到本地 + * @param {string} key 键名 + * @param {any} value 值 + */ +export function setStorage(key, value) { + try { + uni.setStorageSync(key, value) + } catch (e) { + console.error('存储数据失败:', e) + } +} + +/** + * 从本地获取数据 + * @param {string} key 键名 + * @param {any} defaultValue 默认值 + * @returns {any} 存储的值 + */ +export function getStorage(key, defaultValue = null) { + try { + const value = uni.getStorageSync(key) + return value !== '' ? value : defaultValue + } catch (e) { + console.error('获取数据失败:', e) + return defaultValue + } +} + +/** + * 删除本地存储 + * @param {string} key 键名 + */ +export function removeStorage(key) { + try { + uni.removeStorageSync(key) + } catch (e) { + console.error('删除数据失败:', e) + } +} + +/** + * 显示提示消息 + * @param {string} title 提示内容 + * @param {string} icon 图标类型 + * @param {number} duration 显示时长 + */ +export function showToast(title, icon = 'none', duration = 2000) { + uni.showToast({ + title, + icon, + duration + }) +} + +/** + * 显示加载提示 + * @param {string} title 提示内容 + */ +export function showLoading(title = '加载中...') { + uni.showLoading({ + title, + mask: true + }) +} + +/** + * 隐藏加载提示 + */ +export function hideLoading() { + uni.hideLoading() +} + diff --git a/src/utils/request.js b/src/utils/request.js new file mode 100644 index 0000000..7aa8dee --- /dev/null +++ b/src/utils/request.js @@ -0,0 +1,205 @@ +import axios from 'axios' +import { useUserStore } from '@/stores/user' + +// 创建axios实例 +const service = axios.create({ + baseURL: import.meta.env.VITE_APP_BASE_API || (process.env.NODE_ENV === 'development' ? '/api' : 'https://your-api-domain.com'), + timeout: 10000, + headers: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}) + +// 请求拦截器 +service.interceptors.request.use( + config => { + // 在发送请求之前做些什么 + const userStore = useUserStore() + + // 添加token + if (userStore.token) { + config.headers.Authorization = `Bearer ${userStore.token}` + } + + // 显示loading(可根据需要自定义) + if (config.showLoading !== false) { + uni.showLoading({ + title: '加载中...', + mask: true + }) + } + + return config + }, + error => { + // 对请求错误做些什么 + uni.hideLoading() + console.error('请求错误:', error) + return Promise.reject(error) + } +) + +// 响应拦截器 +service.interceptors.response.use( + response => { + // 隐藏loading + uni.hideLoading() + + const res = response.data + + // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据 + // 否则的话抛出错误 + if (res.code !== undefined && res.code !== 200) { + // 根据不同的错误码,做相应的处理 + handleError(res.code, res.message || '请求失败') + + // 返回一个reject的Promise,让错误信息能够被catch到 + return Promise.reject(new Error(res.message || '请求失败')) + } else { + // 返回成功的数据 + return res + } + }, + error => { + // 隐藏loading + uni.hideLoading() + + // 对响应错误做点什么 + console.error('响应错误:', error) + + if (error.response) { + // 服务器返回了错误状态码 + const { status, data } = error.response + + switch (status) { + case 401: + // 未授权,清除token并跳转到登录页 + handleError(401, '未授权,请重新登录') + try { + const userStore = useUserStore() + userStore.logout() + // 注意:如果登录页面不存在,请注释掉下面的跳转 + // uni.reLaunch({ + // url: '/pages/login/login' + // }) + } catch (e) { + console.error('退出登录失败:', e) + } + break + case 403: + handleError(403, '拒绝访问') + break + case 404: + handleError(404, '请求错误,未找到该资源') + break + case 500: + handleError(500, '服务器错误') + break + default: + handleError(status, data?.message || `连接错误${status}`) + } + } else if (error.request) { + // 请求已经发出,但没有收到响应 + handleError(-1, '网络连接失败,请检查网络') + } else { + // 发送请求时出了点问题 + handleError(-1, error.message || '请求失败') + } + + return Promise.reject(error) + } +) + +// 错误处理函数 +function handleError(code, message) { + // 可以根据不同的错误码做不同的处理 + uni.showToast({ + title: message, + icon: 'none', + duration: 2000 + }) +} + +// 封装请求方法 +const request = { + /** + * GET请求 + * @param {string} url 请求地址 + * @param {object} params 请求参数 + * @param {object} config 请求配置 + */ + get(url, params = {}, config = {}) { + return service({ + method: 'get', + url, + params, + ...config + }) + }, + + /** + * POST请求 + * @param {string} url 请求地址 + * @param {object} data 请求数据 + * @param {object} config 请求配置 + */ + post(url, data = {}, config = {}) { + return service({ + method: 'post', + url, + data, + ...config + }) + }, + + /** + * PUT请求 + * @param {string} url 请求地址 + * @param {object} data 请求数据 + * @param {object} config 请求配置 + */ + put(url, data = {}, config = {}) { + return service({ + method: 'put', + url, + data, + ...config + }) + }, + + /** + * DELETE请求 + * @param {string} url 请求地址 + * @param {object} params 请求参数 + * @param {object} config 请求配置 + */ + delete(url, params = {}, config = {}) { + return service({ + method: 'delete', + url, + params, + ...config + }) + }, + + /** + * 上传文件 + * @param {string} url 请求地址 + * @param {FormData} formData 表单数据 + * @param {object} config 请求配置 + */ + upload(url, formData, config = {}) { + return service({ + method: 'post', + url, + data: formData, + headers: { + 'Content-Type': 'multipart/form-data' + }, + ...config + }) + } +} + +export default request + diff --git a/static/.gitkeep b/static/.gitkeep new file mode 100644 index 0000000..cff4153 --- /dev/null +++ b/static/.gitkeep @@ -0,0 +1,3 @@ +# 静态资源目录 +# 请在此目录放置图片、字体等静态资源 + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b9477e5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} + diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..e8c30d3 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.js"] +} + diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..4542d7e --- /dev/null +++ b/vite.config.js @@ -0,0 +1,36 @@ +import { defineConfig } from 'vite' +import uni from '@dcloudio/vite-plugin-uni' +import vue from '@vitejs/plugin-vue' +import path from 'path' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + vue(), + uni() + ], + resolve: { + alias: { + '@': path.resolve(__dirname, 'src') + } + }, + css: { + preprocessorOptions: { + scss: { + additionalData: `@import "@/styles/variables.scss";` + } + } + }, + server: { + port: 3000, + open: true, + proxy: { + '/api': { + target: 'http://localhost:8080', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, '') + } + } + } +}) +