From a0e5c9a024a2f1f0e1dd6540d86dfabb72a865a6 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Fri, 24 Oct 2025 22:20:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mobile/package.json | 1 + mobile/src/api/system/dict/data.js | 35 + mobile/src/common/README-API调用规范.md | 413 +++ mobile/src/common/config.js | 4 +- mobile/src/pages.json | 55 +- .../src/pages/xgss/wsjc/components/README.md | 210 ++ .../xgss/wsjc/components/SingleRoomCheck.vue | 2212 +++++++++++++++++ .../wsjc/components/ViolationRecordDialog.vue | 1901 ++++++++++++++ mobile/src/pages/xgss/wsjc/wsjc.vue | 2 +- mobile/src/pages/xgss/wsjc/wsjcMainAdd.vue | 971 ++++++++ mobile/src/utils/upload.js | 308 +++ 11 files changed, 6086 insertions(+), 26 deletions(-) create mode 100644 mobile/src/api/system/dict/data.js create mode 100644 mobile/src/common/README-API调用规范.md create mode 100644 mobile/src/pages/xgss/wsjc/components/README.md create mode 100644 mobile/src/pages/xgss/wsjc/components/SingleRoomCheck.vue create mode 100644 mobile/src/pages/xgss/wsjc/components/ViolationRecordDialog.vue create mode 100644 mobile/src/pages/xgss/wsjc/wsjcMainAdd.vue create mode 100644 mobile/src/utils/upload.js diff --git a/mobile/package.json b/mobile/package.json index eedf7efa..b2d21a2a 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -73,6 +73,7 @@ "@vue/shared": "^3.0.0", "core-js": "^3.8.3", "crypto-js": "^4.2.0", + "dayjs": "^1.11.18", "flyio": "^0.6.2", "jsencrypt": "^3.3.2", "uview-ui": "2.0.38", diff --git a/mobile/src/api/system/dict/data.js b/mobile/src/api/system/dict/data.js new file mode 100644 index 00000000..8a4b10e1 --- /dev/null +++ b/mobile/src/api/system/dict/data.js @@ -0,0 +1,35 @@ +// 字典数据API - 重新导出common/dict.js中的方法以保持兼容性 +import { + getDicts, + getDict, + useDict, + getDictLabel, + getDictValue, + hasDictValue, + refreshDict, + getDictTagType, +} from '@/common/dict.js'; + +// 重新导出所有字典相关方法 +export { + getDicts, + getDict, + useDict, + getDictLabel, + getDictValue, + hasDictValue, + refreshDict, + getDictTagType, +}; + +// 默认导出 +export default { + getDicts, + getDict, + useDict, + getDictLabel, + getDictValue, + hasDictValue, + refreshDict, + getDictTagType, +}; diff --git a/mobile/src/common/README-API调用规范.md b/mobile/src/common/README-API调用规范.md new file mode 100644 index 00000000..c4b02c1b --- /dev/null +++ b/mobile/src/common/README-API调用规范.md @@ -0,0 +1,413 @@ +# 移动端API调用标准规范 + +> 📌 **重要提示**: 移动端与Web端的API调用方式完全不同,请严格遵守本规范! + +## 🚫 常见错误 + +### ❌ 不要这样做(Web端方式) +```javascript +// 错误1:使用 this.$http +const res = await this.$http.get('/api/path'); +const res = await this.$http.post('/api/path', data); +const res = await this.$http.upload('/api/upload', options); + +// 错误2:使用 getDicts +import { getDicts } from '@/api/system/dict/data.js'; +const res = await getDicts('dict_type'); +this.dictData = res.data; // Web端返回格式 + +// 错误3:使用 this.$u.toast +this.$u.toast('提示信息'); +``` + +## ✅ 正确做法(移动端方式) + +### 1. HTTP请求 + +```javascript +// 导入 +import { request } from '@/common/request.js'; + +// GET请求(无参数) +const res = await request({ + url: '/api/path', + method: 'GET', +}); + +// GET请求(带参数) +const res = await request({ + url: '/api/path', + method: 'GET', + params: { + key1: 'value1', + key2: 'value2' + }, +}); + +// POST请求 +const res = await request({ + url: '/api/path', + method: 'POST', + data: { + key1: 'value1', + key2: 'value2' + }, +}); + +// PUT请求 +const res = await request({ + url: '/api/path', + method: 'PUT', + data: { ... }, +}); + +// DELETE请求 +const res = await request({ + url: '/api/path', + method: 'DELETE', +}); +``` + +### 2. 字典数据获取 + +```javascript +// 导入 +import { getDict, getDictLabel, getDictTagType } from '@/common/dict.js'; + +// 获取字典数据(直接返回数组) +const dictData = await getDict('dict_type'); // 返回: [{label, value, ...}, ...] + +// 批量获取字典 +const [dict1, dict2] = await Promise.all([ + getDict('dict_type_1'), + getDict('dict_type_2'), +]); + +// 获取字典标签 +const label = getDictLabel(dictData, '1'); // 返回: '标签名' + +// 获取字典Tag类型(用于u-tag组件) +const tagType = getDictTagType(dictData, '1'); // 返回: 'success', 'warning', 'error' 等 +``` + +### 3. 字典展示(u-tag) + +```vue + + + +``` + +### 4. 文件上传 + +```javascript +// 导入 +import { uploadFile } from '@/utils/upload.js'; + +// 选择并上传图片 +uni.chooseImage({ + count: 1, + success: async (res) => { + const filePath = res.tempFilePaths[0]; + + // 上传文件 + const result = await uploadFile(filePath, (progress) => { + console.log('上传进度:', progress); + // 更新进度条 + this.uploadProgress = progress; + }); + + if (result && result.code === 200 && result.data) { + // 上传成功 + this.ossId = result.data.ossId; + this.fileUrl = result.data.url; + } + } +}); +``` + +### 5. Toast提示 + +```javascript +// 普通提示 +uni.showToast({ + title: '提示信息', + icon: 'none' +}); + +// 成功提示 +uni.showToast({ + title: '操作成功', + icon: 'success' +}); + +// 失败提示 +uni.showToast({ + title: '操作失败', + icon: 'error' +}); + +// 加载中 +uni.showLoading({ + title: '加载中...', + mask: true +}); + +// 关闭加载 +uni.hideLoading(); +``` + +## 📊 完整示例 + +```vue + + + +``` + +## 🔑 核心差异对照表 + +| 功能 | Web端 | 移动端 | +|------|-------|--------| +| **HTTP请求** | `this.$http.get('/api')` | `request({ url: '/api', method: 'GET' })` | +| **字典获取** | `getDicts('type')` 返回 `{data:[]}` | `getDict('type')` 返回 `[...]` | +| **字典标签** | `selectDictLabel(dict, value)` | `getDictLabel(dict, value)` | +| **Tag类型** | 手动映射 | `getDictTagType(dict, value)` | +| **文件上传** | `this.$http.upload()` | `uploadFile(path, callback)` | +| **Toast提示** | `this.$u.toast('msg')` | `uni.showToast({ title: 'msg' })` | +| **加载提示** | `this.$u.loading.show()` | `uni.showLoading({ title: '...' })` | + +## 📝 注意事项 + +1. **导入路径固定**: + - HTTP请求: `@/common/request.js` + - 字典方法: `@/common/dict.js` + - 文件上传: `@/utils/upload.js` + +2. **字典数据格式**: + - 移动端 `getDict()` 直接返回数组,不需要 `.data` + - 字典项格式: `{ label, value, elTagType, elTagClass, remark }` + +3. **Tag类型自动转换**: + - `danger` → `error` + - `default` → `primary` + +4. **文件上传进度**: + - 进度回调返回 0-100 的数字 + - Web端可能是 `progress.progress`,移动端直接是数字 + +5. **错误处理**: + - 统一使用 `try-catch` + - 错误时使用 `uni.showToast` 提示 + +## 🔗 相关文档 + +- `mobile/src/common/request.js` - HTTP请求源码 +- `mobile/src/common/dict.js` - 字典方法源码 +- `mobile/src/utils/upload.js` - 文件上传源码 +- `.cursor/cursorrules-mobile.md` - 移动端开发规范 + +--- + +**最后更新**: 2025-01-24 +**适用范围**: 移动端工程(Vue 2.6.14 + uni-app 2.x) + diff --git a/mobile/src/common/config.js b/mobile/src/common/config.js index 85839def..af11a20f 100644 --- a/mobile/src/common/config.js +++ b/mobile/src/common/config.js @@ -12,6 +12,7 @@ export const ENCRYPT_CONFIG = { TENANT_ID: '000000', // 加密请求头 ENCRYPT_HEADER: 'encrypt-key', + //TODO 开发环境需要修改为false // 是否启用加密 ENCRYPT_ENABLED: true, }; @@ -36,8 +37,9 @@ export const REQUEST_CONFIG = { ? 'http://localhost/login?redirect=/index' : 'https://hzdyjs.camasort.com/login?redirect=/index', // 生产环境前端域名 + //TODO 生产环境需要修改为true // 是否启用加密 - encryptEnabled: true, + encryptEnabled: false, // 防重复提交间隔时间(ms) repeatSubmitInterval: 500, }; diff --git a/mobile/src/pages.json b/mobile/src/pages.json index 8db5e1ac..960c5afe 100644 --- a/mobile/src/pages.json +++ b/mobile/src/pages.json @@ -200,6 +200,13 @@ "navigationBarTextStyle": "white" } }, + { + "path": "pages/xgss/wsjc/wsjcMainAdd", + "style": { + "navigationBarTitleText": "宿舍卫生检查", + "navigationStyle": "custom" + } + }, { "path": "pages/xgss/xssswsjc/xssswsjc", "style": { @@ -303,30 +310,30 @@ "navigationBarTextStyle": "white" } }, - { - "path": "pages/xljk/reservation/index", - "style": { - "navigationBarTitleText": "心理咨询预约", - "navigationBarBackgroundColor": "#0051d9", - "navigationBarTextStyle": "white" - } - }, - { - "path": "pages/xljk/my-appointments/index", - "style": { - "navigationBarTitleText": "我的预约", - "navigationBarBackgroundColor": "#0051d9", - "navigationBarTextStyle": "white" - } - }, - { - "path": "pages/xljk/reservation/appointment-form", - "style": { - "navigationBarTitleText": "预约咨询", - "navigationBarBackgroundColor": "#0051d9", - "navigationBarTextStyle": "white" - } - } + { + "path": "pages/xljk/reservation/index", + "style": { + "navigationBarTitleText": "心理咨询预约", + "navigationBarBackgroundColor": "#0051d9", + "navigationBarTextStyle": "white" + } + }, + { + "path": "pages/xljk/my-appointments/index", + "style": { + "navigationBarTitleText": "我的预约", + "navigationBarBackgroundColor": "#0051d9", + "navigationBarTextStyle": "white" + } + }, + { + "path": "pages/xljk/reservation/appointment-form", + "style": { + "navigationBarTitleText": "预约咨询", + "navigationBarBackgroundColor": "#0051d9", + "navigationBarTextStyle": "white" + } + } ], "globalStyle": { "navigationBarTextStyle": "black", diff --git a/mobile/src/pages/xgss/wsjc/components/README.md b/mobile/src/pages/xgss/wsjc/components/README.md new file mode 100644 index 00000000..403034bc --- /dev/null +++ b/mobile/src/pages/xgss/wsjc/components/README.md @@ -0,0 +1,210 @@ +# 宿舍卫生检查组件说明 + +## 组件列表 + +### 1. SingleRoomCheck.vue - 单宿舍检查组件 + +**功能说明:** +- 移动端单个宿舍的卫生检查表单组件 +- 与web端 `DormHygieneCheckSingleDialog.vue` 功能完全一致 +- 用于嵌入在弹窗中进行单个宿舍的详细检查 + +**主要功能模块:** + +#### 1.1 宿舍整体情况 +- 违纪类型选择(从API加载) +- 宿舍扣分输入(0-100分) +- 卫生等级选择(字典:`xg_dorm_hygiene_grade`) +- 检查附件上传(支持图片,最多15个) + +#### 1.2 宿舍学生情况 +- 学生基本信息展示(姓名、学号、性别、床位、班级、专业) +- 学生评分项: + - 个人扣分(0-100分) + - 出勤状态(字典:`xg_student_attendance_status`) + - 学生评级(字典:`xg_dorm_hygiene_grade`) +- 请假信息展示(如果有请假记录) +- 违纪记录录入与管理 + +**Props:** +```javascript +{ + roomInfo: Object, // 房间信息(必填) + checkInfo: Object, // 检查基础信息(必填) + existingData: Object // 编辑时的现有数据(可选) +} +``` + +**Events:** +```javascript +{ + submit: (data) => {}, // 保存检查结果 + cancel: () => {} // 取消操作 +} +``` + +**API接口:** +- `GET /disciplineType/disciplineType/listByAreaType` - 加载违纪类型 +- `GET /dormBedStudent/dormBedStudent/getRoomStudents` - 加载房间学生信息 +- `GET /studentLeave/studentLeave/queryByCheckDate` - 查询学生请假信息 +- `POST /dormHygieneCheck/dormHygieneCheck/saveCompleteData` - 保存完整检查数据 +- `POST /resource/oss/upload` - 上传附件 + +**字典类型:** +- `xg_dorm_hygiene_grade` - 卫生等级 +- `xg_student_attendance_status` - 出勤状态 + +--- + +### 2. ViolationRecordDialog.vue - 违纪记录弹窗 + +**功能说明:** +- 学生违纪记录的录入和编辑弹窗 +- 支持新增和编辑两种模式 + +**主要字段:** +- 违纪类型(从API加载) +- 扣分分数(自动填入最低扣分) +- 违纪日期 +- 违纪地点 +- 违纪描述 +- 处理意见 + +**Props:** +```javascript +{ + visible: Boolean, // 弹窗显示状态 + studentInfo: Object, // 学生信息 + violationData: Object // 违纪数据(编辑模式) +} +``` + +**Events:** +```javascript +{ + confirm: (data) => {}, // 确认保存 + close: () => {} // 关闭弹窗 +} +``` + +**API接口:** +- `GET /disciplineType/disciplineType/listByAreaType` - 加载违纪类型 + +--- + +## 使用示例 + +### 在 wsjcMainAdd.vue 中使用 SingleRoomCheck + +```vue + + + +``` + +--- + +## 开发规范 + +### 1. API调用 +- 统一使用 `mobile/src/common/request.js` 中的 `request` 方法 +- 附件上传使用 `smartUploadFile` 方法(自动适配微信环境) + +### 2. 字典数据 +- 使用 `mobile/src/common/dict.js` 中的方法 + - `getDict(dictType)` - 获取字典数据 + - `getDictLabel(dictData, value)` - 获取字典标签 + - `getDictTagType(dictData, value)` - 获取u-tag类型 + +### 3. 样式规范 +- 使用uview-ui 2.x组件 +- 字典数据用 `` 展示 +- 遵循移动端UI设计规范 + +### 4. 数据提交 +- 与web端API接口完全一致 +- 参数名和数据结构保持一致 +- 返回数据格式相同 + +--- + +## 技术栈 + +- Vue 2.6.14 +- uni-app 2.x +- uview-ui 2.0.38 +- dayjs(日期处理) + +--- + +## 注意事项 + +1. **附件上传兼容性** + - 移动端附件功能兼容微信小程序 + - 参考 `wsjcAdd.vue` 的附件实现 + - 使用 `smartUploadFile` 方法自动适配环境 + +2. **字典数据加载** + - 组件初始化时自动加载字典数据 + - 使用缓存机制,避免重复请求 + +3. **请假信息自动填充** + - 加载学生信息时自动查询请假记录 + - 有请假记录的学生自动设置出勤状态为"请假" + +4. **违纪类型自动扣分** + - 选择违纪类型后自动填入最低扣分 + - 用户可手动修改 + +5. **数据验证** + - 扣分范围:0-100分 + - 必填字段验证 + - 表单提交前统一验证 + +--- + +## 更新日志 + +**2025-01-24** +- 创建 SingleRoomCheck.vue 组件 +- 创建 ViolationRecordDialog.vue 组件 +- 实现与web端完全一致的功能 +- 适配移动端UI和交互方式 + diff --git a/mobile/src/pages/xgss/wsjc/components/SingleRoomCheck.vue b/mobile/src/pages/xgss/wsjc/components/SingleRoomCheck.vue new file mode 100644 index 00000000..64eb75d9 --- /dev/null +++ b/mobile/src/pages/xgss/wsjc/components/SingleRoomCheck.vue @@ -0,0 +1,2212 @@ + + + + + diff --git a/mobile/src/pages/xgss/wsjc/components/ViolationRecordDialog.vue b/mobile/src/pages/xgss/wsjc/components/ViolationRecordDialog.vue new file mode 100644 index 00000000..2a7ac3c6 --- /dev/null +++ b/mobile/src/pages/xgss/wsjc/components/ViolationRecordDialog.vue @@ -0,0 +1,1901 @@ + + + + + diff --git a/mobile/src/pages/xgss/wsjc/wsjc.vue b/mobile/src/pages/xgss/wsjc/wsjc.vue index fe4531ce..81d5b182 100644 --- a/mobile/src/pages/xgss/wsjc/wsjc.vue +++ b/mobile/src/pages/xgss/wsjc/wsjc.vue @@ -450,7 +450,7 @@ export default { // 新增检查 handleAdd() { uni.navigateTo({ - url: '/pages/xgss/wsjc/wsjcAdd?type=add', + url: '/pages/xgss/wsjc/wsjcMainAdd', }); }, diff --git a/mobile/src/pages/xgss/wsjc/wsjcMainAdd.vue b/mobile/src/pages/xgss/wsjc/wsjcMainAdd.vue new file mode 100644 index 00000000..acecd989 --- /dev/null +++ b/mobile/src/pages/xgss/wsjc/wsjcMainAdd.vue @@ -0,0 +1,971 @@ + + + + + diff --git a/mobile/src/utils/upload.js b/mobile/src/utils/upload.js new file mode 100644 index 00000000..c9fa566a --- /dev/null +++ b/mobile/src/utils/upload.js @@ -0,0 +1,308 @@ +// 文件上传工具 - 移动端适配 +import { request } from '@/common/request.js'; +import { REQUEST_CONFIG } from '@/common/config.js'; + +/** + * 获取token + */ +function getToken() { + try { + const token = uni.getStorageSync('access_token'); + return token ? `Bearer ${token}` : ''; + } catch (e) { + console.error('❌ 获取Token失败:', e); + return ''; + } +} + +/** + * 构建完整的上传URL + */ +function buildUploadUrl(url) { + let fullUrl; + if (REQUEST_CONFIG.baseURL.startsWith('/')) { + // 开发环境,baseURL是'/api',需要使用完整的服务器URL + fullUrl = `${REQUEST_CONFIG.serverUrl}${url}`; + } else { + // 生产环境,直接拼接 + fullUrl = REQUEST_CONFIG.baseURL + url; + } + return fullUrl; +} + +/** + * 上传单个文件 + * @param {string} url - 上传接口URL + * @param {Object} options - 上传选项 + * @param {string} options.filePath - 文件路径 + * @param {string} options.name - 文件字段名,默认'file' + * @param {Object} options.formData - 额外的表单数据 + * @param {Function} options.onProgress - 进度回调 + * @returns {Promise} + */ +export function uploadFile(url, options = {}) { + return new Promise((resolve, reject) => { + const { filePath, name = 'file', formData = {}, onProgress } = options; + + if (!filePath) { + return reject(new Error('文件路径不能为空')); + } + + const token = getToken(); + const clientid = uni.getStorageSync('clientid') || REQUEST_CONFIG.clientId; + const fullUrl = buildUploadUrl(url); + + console.log('🚀 开始上传文件:', { + url: fullUrl, + filePath, + name, + }); + + const uploadTask = uni.uploadFile({ + url: fullUrl, + filePath, + name, + formData, + header: { + authorization: token, + clientid: clientid, + }, + success: (res) => { + console.log('✅ 文件上传成功:', res); + + if (res.statusCode === 200) { + try { + const data = JSON.parse(res.data); + if (data.code === 200) { + resolve(data); + } else { + uni.showToast({ + title: data.msg || '上传失败', + icon: 'none', + }); + reject(new Error(data.msg || '上传失败')); + } + } catch (error) { + console.error('❌ 解析上传响应失败:', error); + reject(new Error('解析响应数据失败')); + } + } else { + reject(new Error(`上传失败: HTTP ${res.statusCode}`)); + } + }, + fail: (err) => { + console.error('❌ 文件上传失败:', err); + uni.showToast({ + title: '上传失败', + icon: 'none', + }); + reject(new Error(err.errMsg || '上传失败')); + }, + }); + + // 监听上传进度 + if (onProgress && typeof onProgress === 'function') { + uploadTask.onProgressUpdate((res) => { + onProgress(res); + }); + } + }); +} + +/** + * 上传多个文件 + * @param {string} url - 上传接口URL + * @param {Array} files - 文件列表 + * @param {Object} options - 上传选项 + * @returns {Promise} + */ +export function uploadFiles(url, files, options = {}) { + const uploadPromises = files.map((file, index) => { + return uploadFile(url, { + ...options, + filePath: file.tempFilePath || file.path || file.url, + onProgress: (progress) => { + if (options.onProgress) { + options.onProgress({ + ...progress, + fileIndex: index, + totalFiles: files.length, + }); + } + }, + }); + }); + + return Promise.all(uploadPromises); +} + +/** + * 选择并上传图片 + * @param {string} url - 上传接口URL + * @param {Object} options - 选择和上传选项 + * @returns {Promise} + */ +export function chooseAndUploadImage(url, options = {}) { + return new Promise((resolve, reject) => { + const { + count = 1, + sizeType = ['compressed'], + sourceType = ['album', 'camera'], + ...uploadOptions + } = options; + + uni.chooseImage({ + count, + sizeType, + sourceType, + success: (chooseRes) => { + console.log('📷 选择图片成功:', chooseRes); + + if (count === 1) { + // 单张图片上传 + uploadFile(url, { + ...uploadOptions, + filePath: chooseRes.tempFilePaths[0], + }) + .then(resolve) + .catch(reject); + } else { + // 多张图片上传 + uploadFiles(url, chooseRes.tempFiles, uploadOptions) + .then(resolve) + .catch(reject); + } + }, + fail: (err) => { + console.error('❌ 选择图片失败:', err); + reject(new Error(err.errMsg || '选择图片失败')); + }, + }); + }); +} + +/** + * 选择并上传文件 + * @param {string} url - 上传接口URL + * @param {Object} options - 选择和上传选项 + * @returns {Promise} + */ +export function chooseAndUploadFile(url, options = {}) { + return new Promise((resolve, reject) => { + const { count = 1, type = 'all', extension = [], ...uploadOptions } = options; + + // #ifdef MP-WEIXIN + // 微信小程序使用 wx.chooseMessageFile + uni.chooseMessageFile({ + count, + type, + extension, + success: (chooseRes) => { + console.log('📁 选择文件成功:', chooseRes); + + if (count === 1) { + uploadFile(url, { + ...uploadOptions, + filePath: chooseRes.tempFiles[0].path, + }) + .then(resolve) + .catch(reject); + } else { + uploadFiles(url, chooseRes.tempFiles, uploadOptions) + .then(resolve) + .catch(reject); + } + }, + fail: (err) => { + console.error('❌ 选择文件失败:', err); + reject(new Error(err.errMsg || '选择文件失败')); + }, + }); + // #endif + + // #ifndef MP-WEIXIN + // H5和其他平台使用普通文件选择 + uni.chooseFile({ + count, + type, + extension, + success: (chooseRes) => { + console.log('📁 选择文件成功:', chooseRes); + + if (count === 1) { + uploadFile(url, { + ...uploadOptions, + filePath: chooseRes.tempFilePaths[0], + }) + .then(resolve) + .catch(reject); + } else { + uploadFiles(url, chooseRes.tempFiles, uploadOptions) + .then(resolve) + .catch(reject); + } + }, + fail: (err) => { + console.error('❌ 选择文件失败:', err); + reject(new Error(err.errMsg || '选择文件失败')); + }, + }); + // #endif + }); +} + +/** + * 压缩图片 + * @param {string} src - 图片路径 + * @param {Object} options - 压缩选项 + * @returns {Promise} + */ +export function compressImage(src, options = {}) { + return new Promise((resolve, reject) => { + const { quality = 80, compressedWidth, compressedHeight } = options; + + uni.compressImage({ + src, + quality, + compressedWidth, + compressedHeight, + success: (res) => { + console.log('✅ 图片压缩成功:', res); + resolve(res.tempFilePath); + }, + fail: (err) => { + console.error('❌ 图片压缩失败:', err); + reject(new Error(err.errMsg || '图片压缩失败')); + }, + }); + }); +} + +/** + * 获取图片信息 + * @param {string} src - 图片路径 + * @returns {Promise} + */ +export function getImageInfo(src) { + return new Promise((resolve, reject) => { + uni.getImageInfo({ + src, + success: (res) => { + resolve(res); + }, + fail: (err) => { + reject(new Error(err.errMsg || '获取图片信息失败')); + }, + }); + }); +} + +// 默认导出 +export default { + uploadFile, + uploadFiles, + chooseAndUploadImage, + chooseAndUploadFile, + compressImage, + getImageInfo, +};