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 @@
+
+
+
+
+
+
+ 项目信息
+
+
+ Vue 3
+ Vite
+ Ant Design Vue
+ Pinia
+ Axios
+
+
+
+
+
+ 功能演示
+
+
+ 测试GET请求
+
+
+
+ 测试POST请求
+
+
+
+ 显示消息提示
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 欢迎使用 nl-uniapp
+
+
+ 项目信息
+
+ 框架:
+ Vue 3
+
+
+ 构建工具:
+ Vite
+
+
+ UI组件库:
+ Ant Design Vue
+
+
+ 状态管理:
+ Pinia
+
+
+ HTTP请求:
+ Axios
+
+
+
+
+
+
+ 功能演示
+
+
+
+
+
+
+
+
+ 提示
+ 这是一个基于 Vue3 + Vite + Ant Design Vue 的 uniapp 项目模板
+
+
+
+
+
+
+
+
+
+
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/, '')
+ }
+ }
+ }
+})
+