diff --git a/api/common/upload.js b/api/common/upload.js
index 1abaefd..9e4b7e3 100644
--- a/api/common/upload.js
+++ b/api/common/upload.js
@@ -1,4 +1,5 @@
import { getToken } from '@/util/auth'
+import request from "@/util/oaRequest"
const BASE_URL = 'http://110.41.139.73:8080'
@@ -34,4 +35,191 @@ export function uploadImage(filePath) {
}
})
})
-}
\ No newline at end of file
+}
+
+/**
+ * 文件上传API
+ * @param {Object} file - 文件对象
+ * @param {string} file.path - 文件路径
+ * @param {string} file.name - 文件名
+ * @param {number} file.size - 文件大小
+ * @param {string} file.type - 文件类型
+ * @param {Object} options - 上传选项
+ * @param {Array} options.allowedTypes - 允许的文件类型扩展名数组
+ * @param {number} options.maxSize - 最大文件大小(MB)
+ * @param {number} options.extraData - 额外数据,默认为1
+ * @returns {Promise} 返回上传结果 { ossId, url, fileName, originalName }
+ */
+export function uploadFile(file, options = {}) {
+ const {
+ allowedTypes = ['doc', 'xls', 'ppt', 'txt', 'pdf', 'docx', 'xlsx', 'png', 'jpg', 'jpeg', 'zip', 'rar', '7z', 'dwg'],
+ maxSize = 200, // 默认200MB
+ extraData = 1
+ } = options
+
+ return new Promise((resolve, reject) => {
+ // 文件类型验证
+ const ext = file.name.split('.').pop().toLowerCase()
+ if (allowedTypes && !allowedTypes.includes(ext)) {
+ reject(new Error(`文件格式不正确,请上传${allowedTypes.join('/')}格式文件!`))
+ return
+ }
+
+ // 文件大小验证
+ if (maxSize && file.size / 1024 / 1024 > maxSize) {
+ reject(new Error(`上传文件大小不能超过 ${maxSize} MB!`))
+ return
+ }
+
+ // 验证文件路径
+ if (!file.path) {
+ reject(new Error('文件路径为空'))
+ return
+ }
+
+ console.log('[uploadFile] 开始上传文件:', {
+ name: file.name,
+ size: file.size,
+ path: file.path,
+ type: file.type
+ })
+
+ // 处理文件路径(安卓平台)
+ let filePath = file.path
+ // #ifdef APP-PLUS
+ if (typeof plus !== 'undefined' && plus.io) {
+ if (!filePath.startsWith('/') && !filePath.startsWith('file://')) {
+ filePath = plus.io.convertLocalFileSystemURL(filePath)
+ }
+ }
+ // #endif
+
+ uni.uploadFile({
+ url: BASE_URL + '/system/oss/upload',
+ filePath,
+ name: 'file',
+ formData: {
+ isPublic: extraData
+ },
+ header: {
+ 'Authorization': 'Bearer ' + getToken()
+ },
+ success: (res) => {
+ console.log('[uploadFile] 上传响应:', res)
+ try {
+ const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
+ if (data.code === 200) {
+ console.log('[uploadFile] 上传成功:', data.data)
+ resolve({
+ ossId: data.data.ossId,
+ url: data.data.url,
+ fileName: data.data.fileName,
+ originalName: file.name
+ })
+ } else {
+ console.error('[uploadFile] 上传失败:', data)
+ reject(data.msg || '上传失败')
+ }
+ } catch (e) {
+ console.error('[uploadFile] 解析返回数据失败:', e, res.data)
+ reject('上传返回格式错误')
+ }
+ },
+ fail: (err) => {
+ console.error('[uploadFile] 上传接口调用失败:', err)
+ reject(err)
+ }
+ })
+ })
+}
+
+/**
+ * 批量上传文件
+ * @param {Array} files - 文件数组
+ * @param {Object} options - 上传选项
+ * @returns {Promise} 返回上传结果数组
+ */
+export function uploadFiles(files, options = {}) {
+ const {
+ allowedTypes = ['doc', 'xls', 'ppt', 'txt', 'pdf', 'docx', 'xlsx', 'png', 'jpg', 'jpeg', 'zip', 'rar', '7z', 'dwg'],
+ maxSize = 200,
+ extraData = 1,
+ onProgress = null
+ } = options
+
+ return new Promise((resolve, reject) => {
+ if (!files || files.length === 0) {
+ resolve([])
+ return
+ }
+
+ const results = []
+ let completedCount = 0
+ let hasError = false
+
+ files.forEach((file, index) => {
+ uploadFile(file, { allowedTypes, maxSize, extraData })
+ .then(result => {
+ results[index] = result
+ completedCount++
+
+ // 调用进度回调
+ if (onProgress) {
+ onProgress({
+ index,
+ file,
+ result,
+ progress: (completedCount / files.length) * 100
+ })
+ }
+
+ // 所有文件上传完成
+ if (completedCount === files.length && !hasError) {
+ resolve(results)
+ }
+ })
+ .catch(error => {
+ hasError = true
+ console.error(`文件 ${file.name} 上传失败:`, error)
+ reject(new Error(`文件 "${file.name}" 上传失败: ${error.message || error}`))
+ })
+ })
+ })
+}
+
+/**
+ * 根据OSS ID列表获取文件信息
+ * @param {Array} ossIds - OSS ID数组
+ * @returns {Promise} 返回文件信息数组
+ */
+// 查询OSS对象基于id串
+export function listByIds(ossId) {
+ return request({
+ url: '/system/oss/listByIds/' + ossId,
+ method: 'get'
+ })
+}
+
+/**
+ * 根据OSS ID列表获取文件信息
+ * @param {Array} ossIds - OSS ID数组
+ * @returns {Promise} 返回文件信息数组
+ */
+export function getFilesByIds(ossIds) {
+ if (!ossIds || ossIds.length === 0) {
+ return Promise.resolve([])
+ }
+
+ const ossIdString = Array.isArray(ossIds) ? ossIds.join(',') : ossIds
+
+ return request({
+ url: '/system/oss/listByIds/' + ossIdString,
+ method: 'get'
+ }).then(response => {
+ if (response.code === 200) {
+ return response.data || []
+ } else {
+ throw new Error(response.msg || '获取文件信息失败')
+ }
+ })
+}
\ No newline at end of file
diff --git a/pages/workbench/task/components/TaskList.vue b/pages/workbench/task/components/TaskList.vue
new file mode 100644
index 0000000..a13712a
--- /dev/null
+++ b/pages/workbench/task/components/TaskList.vue
@@ -0,0 +1,375 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ task.taskTitle || '未命名任务' }}
+
+ {{ getStatusText(task.state) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/workbench/task/create.vue b/pages/workbench/task/create.vue
index d0758b6..9ca3b33 100644
--- a/pages/workbench/task/create.vue
+++ b/pages/workbench/task/create.vue
@@ -228,6 +228,7 @@
+
选择文件
+
{
+ const processedFiles = res.tempFiles.map(file => ({
+ name: file.name,
+ size: file.size,
+ path: file.path || file.tempFilePath,
+ type: file.type
+ }))
+
+ // 验证文件
+ const validFiles = this.validateFiles(processedFiles)
+ if (validFiles.length > 0) {
+ this.fileList = [...this.fileList, ...validFiles]
+ }
+ },
+ fail: (err) => {
+ console.error('选择文件失败:', err)
+ uni.showToast({
+ title: '选择文件失败',
+ icon: 'none'
+ })
+ }
+ })
+ return
+ }
+
+ // H5平台使用 uni.chooseFile
+ // #ifdef H5
uni.chooseFile({
count: 5,
type: 'all',
success: (res) => {
- this.fileList = [...this.fileList, ...res.tempFiles]
+ const processedFiles = res.tempFiles.map(file => ({
+ name: file.name,
+ size: file.size,
+ path: file.path || file.tempFilePath || file.url,
+ type: file.type
+ }))
+
+ // 验证文件
+ const validFiles = this.validateFiles(processedFiles)
+ if (validFiles.length > 0) {
+ this.fileList = [...this.fileList, ...validFiles]
+ }
+ },
+ fail: (err) => {
+ console.error('选择文件失败:', err)
+ uni.showToast({
+ title: '选择文件失败',
+ icon: 'none'
+ })
}
})
+ // #endif
+
+ // 其他平台提示不支持
+ // #ifndef APP-PLUS || H5 || MP-WEIXIN
+ uni.showToast({
+ title: '当前平台不支持文件选择',
+ icon: 'none'
+ })
+ // #endif
},
// 移除文件
@@ -424,6 +495,139 @@ export default {
this.fileList.splice(index, 1)
},
+ // 根据文件名获取文件类型
+ getFileType(fileName) {
+ const ext = fileName.split('.').pop().toLowerCase()
+ const typeMap = {
+ 'jpg': 'image/jpeg',
+ 'jpeg': 'image/jpeg',
+ 'png': 'image/png',
+ 'gif': 'image/gif',
+ 'bmp': 'image/bmp',
+ 'webp': 'image/webp',
+ 'pdf': 'application/pdf',
+ 'doc': 'application/msword',
+ 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'xls': 'application/vnd.ms-excel',
+ 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'ppt': 'application/vnd.ms-powerpoint',
+ 'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 'txt': 'text/plain',
+ 'zip': 'application/zip',
+ 'rar': 'application/x-rar-compressed',
+ '7z': 'application/x-7z-compressed'
+ }
+ return typeMap[ext] || 'application/octet-stream'
+ },
+
+ // 执行文件选择
+ doPickFiles() {
+ try {
+ plus.io.pickFiles({
+ multiple: true,
+ maximum: 5,
+ filter: 'none',
+ onSuccess: (files) => {
+ console.log('安卓平台选择文件成功:', files)
+ const processedFiles = files.map(file => ({
+ name: file.name,
+ size: file.size,
+ path: file.path,
+ type: file.type || this.getFileType(file.name)
+ }))
+
+ // 验证文件
+ const validFiles = this.validateFiles(processedFiles)
+ if (validFiles.length > 0) {
+ this.fileList = [...this.fileList, ...validFiles]
+ console.log('处理后的文件列表:', this.fileList)
+ }
+ },
+ onFail: (error) => {
+ console.error('安卓平台选择文件失败:', error)
+ // 尝试使用备用方法
+ this.fallbackPickFiles()
+ }
+ })
+ } catch (error) {
+ console.error('plus.io.pickFiles 不可用:', error)
+ // 尝试使用备用方法
+ this.fallbackPickFiles()
+ }
+ },
+
+ // 备用文件选择方法
+ fallbackPickFiles() {
+ // 使用 uni.chooseImage 作为备用方案
+ uni.chooseImage({
+ count: 5,
+ sizeType: ['original'],
+ sourceType: ['album'],
+ success: (res) => {
+ console.log('备用方法选择文件成功:', res)
+ const processedFiles = res.tempFilePaths.map((path, index) => ({
+ name: `image_${Date.now()}_${index}.jpg`,
+ size: 0,
+ path: path,
+ type: 'image/jpeg'
+ }))
+
+ // 验证文件
+ const validFiles = this.validateFiles(processedFiles)
+ if (validFiles.length > 0) {
+ this.fileList = [...this.fileList, ...validFiles]
+ console.log('处理后的文件列表:', this.fileList)
+ }
+ },
+ fail: (error) => {
+ console.error('备用方法选择文件失败:', error)
+ uni.showToast({
+ title: '选择文件失败',
+ icon: 'none'
+ })
+ }
+ })
+ },
+
+ // 验证文件
+ validateFiles(files) {
+ const allowedTypes = ['doc', 'xls', 'ppt', 'txt', 'pdf', 'docx', 'xlsx', 'png', 'jpg', 'jpeg', 'zip', 'rar', '7z', 'dwg']
+ const maxSize = 200 * 1024 * 1024 // 200MB
+
+ const validFiles = []
+ const invalidFiles = []
+
+ files.forEach(file => {
+ // 文件类型验证
+ const ext = file.name.split('.').pop().toLowerCase()
+ if (!allowedTypes.includes(ext)) {
+ invalidFiles.push(`${file.name} (格式不支持)`)
+ return
+ }
+
+ // 文件大小验证
+ if (file.size > maxSize) {
+ invalidFiles.push(`${file.name} (超过200MB)`)
+ return
+ }
+
+ validFiles.push(file)
+ })
+
+ // 显示无效文件提示
+ if (invalidFiles.length > 0) {
+ uni.showToast({
+ title: `以下文件不符合要求:${invalidFiles.join(', ')}`,
+ icon: 'none',
+ duration: 3000
+ })
+ }
+
+ return validFiles
+ },
+
+
+
// 格式化文件大小
formatFileSize(size) {
if (size < 1024) {
@@ -437,21 +641,24 @@ export default {
// 上传文件
async uploadFiles() {
- const uploadPromises = this.fileList.map(async (file) => {
- try {
- const result = await uploadImage(file.path)
- return {
- originalName: file.name,
- url: result.url,
- ossId: result.ossId
+ if (this.fileList.length === 0) {
+ return []
+ }
+
+ try {
+ const results = await uploadFiles(this.fileList, {
+ allowedTypes: ['doc', 'xls', 'ppt', 'txt', 'pdf', 'docx', 'xlsx', 'png', 'jpg', 'jpeg', 'zip', 'rar', '7z', 'dwg'],
+ maxSize: 200,
+ extraData: 1,
+ onProgress: (progress) => {
+ console.log(`上传进度: ${progress.progress.toFixed(1)}% - ${progress.file.name}`)
}
- } catch (error) {
- console.error('文件上传失败:', error)
- throw error
- }
- })
-
- return Promise.all(uploadPromises)
+ })
+
+ return results
+ } catch (error) {
+ throw error
+ }
},
// 表单验证
@@ -510,12 +717,37 @@ export default {
try {
// 上传文件
if (this.fileList.length > 0) {
+ // 显示上传进度提示
+ uni.showLoading({
+ title: '正在上传文件...',
+ mask: true
+ })
+
const uploadedFiles = await this.uploadFiles()
- this.form.accessory = uploadedFiles.map(file => file.ossId).join(',')
+
+ // 隐藏上传进度提示
+ uni.hideLoading()
+
+ // 提取ossId并拼接成字符串
+ const ossIds = uploadedFiles.map(file => file.ossId).filter(Boolean)
+ this.form.accessory = ossIds.join(',')
+
+ console.log('上传完成,ossId字符串:', this.form.accessory)
+ } else {
+ this.form.accessory = ''
}
+ // 显示提交提示
+ uni.showLoading({
+ title: '正在创建任务...',
+ mask: true
+ })
+
// 提交任务
await addTask(this.form)
+
+ // 隐藏提交提示
+ uni.hideLoading()
uni.showToast({
title: '任务创建成功',
@@ -529,9 +761,16 @@ export default {
} catch (error) {
console.error('创建任务失败:', error)
+
+ // 隐藏所有loading
+ uni.hideLoading()
+
+ // 显示错误信息
+ const errorMessage = error.message || '创建任务失败'
uni.showToast({
- title: '创建任务失败',
- icon: 'none'
+ title: errorMessage,
+ icon: 'none',
+ duration: 3000
})
} finally {
this.loading = false
diff --git a/pages/workbench/task/reportTaskDetail.vue b/pages/workbench/task/reportTaskDetail.vue
index d53be6d..ef3276a 100644
--- a/pages/workbench/task/reportTaskDetail.vue
+++ b/pages/workbench/task/reportTaskDetail.vue
@@ -2,16 +2,71 @@
+
{{ taskDetail.taskTitle }}
- 负责人:{{ taskDetail.workerNickName || '-' }}
- 开始时间:{{ taskDetail.beginTime || '-' }}
- 结束时间:{{ taskDetail.finishTime || '-' }}
- 创建人:{{ taskDetail.createUserNickName || '-' }}
- 创建时间:{{ taskDetail.createTime || '-' }}
+
+
+ 基本信息
+
+
+ 任务状态:
+
+ {{ getStatusText(taskDetail.state) }}
+
+
+
+ 优先级:
+
+ {{ getPriorityText(taskDetail.taskRank) }}
+
+
+
+ 负责人:
+ {{ taskDetail.workerNickName || '-' }}
+
+
+ 创建人:
+ {{ taskDetail.createUserNickName || '-' }}
+
+
+ 所属项目:
+ {{ taskDetail.projectName || '-' }}
+
+
+
+
+ 时间信息
+
+
+ 开始时间:
+ {{ formatDate(taskDetail.beginTime) }}
+
+
+ 结束时间:
+ {{ formatDate(taskDetail.finishTime) }}
+
+
+ 超期天数:
+ {{ taskDetail.overDays }}天
+
+
+ 创建时间:
+ {{ formatDate(taskDetail.createTime) }}
+
+
+
+
任务描述
-
- 报工进度
-
+ {{ taskDetail.content }}
+ 暂无任务描述
+
+
+ 备注
+ {{ taskDetail.remark }}
+
+
+ 报工进度
+
进度时间:{{ item.signTime || '-' }}
进度区间:{{ item.beginTime || '-' }} ~ {{ item.endTime || '-' }}
@@ -20,11 +75,40 @@
完成时间:{{ item.completedTime || '-' }}
- 暂无报工进度
+
+ 暂无报工进度
+
- 新增报工
+
+ 附件
+
+
+
+
+
+
+ {{ getFileName(file.originalName) }}
+ {{ formatFileSize(file.fileSize) }}
+
+
+
+
+
+
+
+ 暂无附件
+
-
+
+ 新增报工
+
+
+
新增报工
@@ -58,6 +142,7 @@