diff --git a/api/index.js b/api/index.js index aa61f9b..74d9675 100644 --- a/api/index.js +++ b/api/index.js @@ -26,4 +26,7 @@ export * from './customer'; export * from './common'; // 导入审批相关 API -export * from './verify'; \ No newline at end of file +export * from './verify'; + +// 导入项目相关 API +export * from './project'; \ No newline at end of file diff --git a/api/project.js b/api/project.js new file mode 100644 index 0000000..a887eec --- /dev/null +++ b/api/project.js @@ -0,0 +1,102 @@ +/** + * 项目相关 API + */ + +/** + * 获取项目列表 + * @param {Object} params 请求参数 + * @param {number} params.pageNum 页码 + * @param {number} params.pageSize 每页数量 + * @param {string} params.orderByColumn 排序字段 + * @param {string} params.isAsc 排序方式(ascending/descending) + * @param {number[]} params.statusList 项目状态列表 + * @param {string} params.createId 创建人ID + * @param {string} params.ownerId 负责人ID + * @param {string[]} params.keys 搜索关键词数组 + * @returns {Promise} 返回项目列表 + */ +export const getProjectList = (params = {}) => { + const queryParams = []; + + // 分页参数 + if (params.pageNum !== undefined) { + queryParams.push(`pageNum=${params.pageNum}`); + } + if (params.pageSize !== undefined) { + queryParams.push(`pageSize=${params.pageSize}`); + } + + // 排序参数 + if (params.orderByColumn !== undefined && params.orderByColumn !== '') { + queryParams.push(`orderByColumn=${encodeURIComponent(params.orderByColumn)}`); + } + if (params.isAsc !== undefined && params.isAsc !== '') { + queryParams.push(`isAsc=${encodeURIComponent(params.isAsc)}`); + } + + // 状态列表 + if (params.statusList !== undefined && Array.isArray(params.statusList) && params.statusList.length > 0) { + params.statusList.forEach((status, index) => { + queryParams.push(`statusList=${status}`); + }); + } + + // 创建人ID + if (params.createId !== undefined && params.createId !== '') { + queryParams.push(`createId=${params.createId}`); + } + + // 负责人ID + if (params.ownerId !== undefined && params.ownerId !== '') { + queryParams.push(`ownerId=${params.ownerId}`); + } + + // 搜索关键词 + if (params.keys !== undefined && Array.isArray(params.keys) && params.keys.length > 0) { + params.keys.forEach((key, index) => { + queryParams.push(`keys[${index}]=${encodeURIComponent(key)}`); + }); + } + + // 项目名称搜索 + if (params.projectName !== undefined && params.projectName !== '') { + queryParams.push(`projectName=${encodeURIComponent(params.projectName)}`); + } + + // 项目编号搜索 + if (params.projectId !== undefined && params.projectId !== '') { + queryParams.push(`projectId=${encodeURIComponent(params.projectId)}`); + } + + // 客户名称搜索 + if (params.customerName !== undefined && params.customerName !== '') { + queryParams.push(`customerName=${encodeURIComponent(params.customerName)}`); + } + + // 成员ID搜索 + if (params.memberId !== undefined && params.memberId !== '') { + queryParams.push(`memberId=${params.memberId}`); + } + + const queryString = queryParams.length > 0 ? `?${queryParams.join('&')}` : ''; + + return uni.$uv.http.get(`bst/project/list${queryString}`, { + custom: { + auth: true // 启用 token 认证 + } + }); +}; + +/** + * 获取项目详情 + * @param {string} id 项目ID + * @returns {Promise} 返回项目详情 + */ +export const getProjectDetail = (id) => { + return uni.$uv.http.get(`bst/project/${id}`, { + custom: { + auth: true + } + }); +}; + diff --git a/api/task.js b/api/task.js index 120eeb9..ea7c936 100644 --- a/api/task.js +++ b/api/task.js @@ -106,7 +106,8 @@ export const applyTaskDelay = (payload) => { export const createTask = (payload) => { return uni.$uv.http.post('/bst/task', payload, { custom: { - auth: true + auth: true, + catch: true // 允许在 catch 中处理错误 } }); }; diff --git a/components/index/Workbench.vue b/components/index/Workbench.vue index e707423..61a8851 100644 --- a/components/index/Workbench.vue +++ b/components/index/Workbench.vue @@ -62,6 +62,12 @@ const handleClick = (item) => { uni.switchTab({ url: '/pages/index/index' }); return; } + if (item.key === 'project') { + uni.navigateTo({ + url: '/pages/project/list/index' + }); + return; + } // 其他入口占位 uni.showToast({ title: '开发中', icon: 'none' }); }; diff --git a/pages.json b/pages.json index c4d18d4..a06ec67 100644 --- a/pages.json +++ b/pages.json @@ -137,6 +137,12 @@ "navigationBarTitleText": "修改跟进记录", "navigationStyle": "custom" } + }, + { + "path": "pages/project/list/index", + "style": { + "navigationBarTitleText": "项目管理" + } } ], diff --git a/pages/project/list/index.vue b/pages/project/list/index.vue new file mode 100644 index 0000000..55a6a96 --- /dev/null +++ b/pages/project/list/index.vue @@ -0,0 +1,661 @@ + + + + + + diff --git a/pages/task/add/index.vue b/pages/task/add/index.vue index d9f0d3c..7be9fc8 100644 --- a/pages/task/add/index.vue +++ b/pages/task/add/index.vue @@ -436,7 +436,8 @@ const handleSubmit = async () => { }); try { - await createTask(payload); + const res = await createTask(payload); + console.log('@@@@@@@@@@',res); uni.hideLoading(); uni.showToast({ title: '创建成功', @@ -448,14 +449,11 @@ const handleSubmit = async () => { }, 1200); } catch (error) { uni.hideLoading(); - submitting.value = false; console.error('创建任务失败:', error); - uni.showToast({ - title: error.message || '创建失败,请重试', - icon: 'none' - }); + // 错误提示已在响应拦截器中统一处理,这里不需要重复显示 } finally { submitting.value = false; + console.log('submitting', submitting.value); } }; @@ -843,7 +841,7 @@ onLoad(async (options) => { } .form-label { - flex: 1; + font-size: 15px; color: #333; } diff --git a/pages/task/detail/index.vue b/pages/task/detail/index.vue index c4dd45b..d292223 100644 --- a/pages/task/detail/index.vue +++ b/pages/task/detail/index.vue @@ -88,6 +88,21 @@ /> + + + + {{ getFileIcon(file.name) }} + + {{ file.name }} + {{ formatFileSize(file.size) }} + + + 申请延期 @@ -269,23 +284,19 @@ const determineTaskStatus = (status, expireTime) => { const now = new Date(); // 设置时间到当天0点,便于日期比较 - now.setHours(0, 0, 0, 0); - expireDate.setHours(23, 59, 59, 999); - + // now.setHours(0, 0, 0, 0); + // expireDate.setHours(23, 59, 59, 999); // 如果已过期,标记为逾期 if (expireDate.getTime() < now.getTime()) { return 'overdue'; } - // 计算距离过期的天数 const diffTime = expireDate.getTime() - now.getTime(); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); - // 如果3天内到期,标记为即将逾期 if (diffDays <= 3 && diffDays > 0) { return 'imminent'; } - // 否则返回待完成状态 return 'pending'; }; @@ -329,6 +340,32 @@ const isImageUrl = (url) => { return /\.(jpg|jpeg|png|gif|bmp|webp)(\?|$)/i.test(url); }; +// 解析任务附件(区分图片和文件) +const parseTaskAttachments = (attachStr) => { + if (!attachStr) return { pictures: [], files: [] }; + if (typeof attachStr !== 'string') return { pictures: [], files: [] }; + + const urls = parseAttachUrls(attachStr); + const pictures = []; + const files = []; + + urls.forEach(url => { + if (isImageUrl(url)) { + pictures.push(url); + } else { + // 从URL中提取文件名 + const fileName = url.split('/').pop().split('?')[0] || '文件'; + files.push({ + name: fileName, + path: url, + size: 0 // 如果API没有返回文件大小,默认为0 + }); + } + }); + + return { pictures, files }; +}; + // 转换提交记录数据 const transformSubmitRecords = (submitList) => { if (!Array.isArray(submitList) || submitList.length === 0) { @@ -492,6 +529,73 @@ const previewTaskImages = (imageUrls, index) => { } }; +// 预览/下载任务文件 +const previewTaskFile = (file) => { + if (!file.path) { + uni.showToast({ + title: '文件路径不存在', + icon: 'none' + }); + return; + } + + // 如果是图片,使用预览图片功能 + const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp']; + const ext = file.name ? file.name.split('.').pop().toLowerCase() : ''; + + if (imageExts.includes(ext)) { + uni.previewImage({ + urls: [file.path], + current: file.path + }); + } else { + // 其他文件类型,尝试打开或下载 + // #ifdef H5 + window.open(file.path, '_blank'); + // #endif + + // #ifdef APP-PLUS + plus.runtime.openURL(file.path); + // #endif + + // #ifndef H5 || APP-PLUS + uni.showToast({ + title: '正在下载文件...', + icon: 'loading', + duration: 2000 + }); + // 下载并打开文档 + uni.downloadFile({ + url: file.path, + success: (res) => { + if (res.statusCode === 200) { + uni.openDocument({ + filePath: res.tempFilePath, + success: () => { + console.log('打开文档成功'); + }, + fail: (err) => { + console.error('打开文档失败:', err); + uni.showToast({ + title: '无法打开此文件', + icon: 'none' + }); + } + }); + } + }, + fail: (err) => { + console.error('下载文件失败:', err); + uni.showToast({ + title: '下载文件失败', + icon: 'none' + }); + } + }); + // #endif + } +}; + // 预览提交记录图片 const previewRecordImages = (imageUrls, index) => { if (imageUrls && imageUrls.length > 0) { @@ -683,8 +787,16 @@ const loadTaskData = async (taskId) => { // 转换提交记录 const submitRecords = transformSubmitRecords(res.submitList || []); - // 解析任务图片(逗号分隔的URL字符串) - const taskPictures = res.picture ? parseAttachUrls(res.picture) : []; + // 解析任务附件(图片和文件) + // 优先使用 file 字段,如果没有则使用 picture 字段(可能包含图片和文件) + let taskAttachments = { pictures: [], files: [] }; + if (res.file) { + // 如果 API 返回了 file 字段,解析它 + taskAttachments = parseTaskAttachments(res.file); + } else if (res.picture) { + // 如果只有 picture 字段,也解析它(可能包含图片和文件) + taskAttachments = parseTaskAttachments(res.picture); + } // 更新任务数据 task.value = { @@ -698,7 +810,8 @@ const loadTaskData = async (taskId) => { responsible: getOwnerNames(res.memberList || []), publishTime: res.createTime ? formatTimeToChinese(res.createTime) : '', content: res.description || '', - pictures: taskPictures, // 任务图片数组 + pictures: taskAttachments.pictures, // 任务图片数组 + files: taskAttachments.files, // 任务文件数组 submitRecords: submitRecords, // 保存原始数据,供其他功能使用 rawData: res @@ -722,24 +835,26 @@ onLoad((options) => { task.value.id = taskId; // 优先从 API 加载数据 loadTaskData(taskId); - } else { - // 如果没有 taskId,尝试从 Pinia store 获取任务详情数据(兼容旧逻辑) - const taskStore = useTaskStore(); - const storedTask = taskStore.getTaskDetail; - if (storedTask) { - task.value = { - ...task.value, - ...storedTask - }; - } else { - uni.showToast({ - title: '缺少任务ID', - icon: 'none' - }); - setTimeout(() => { - uni.navigateBack(); - }, 1500); - } + } + else { + // // 如果没有 taskId,尝试从 Pinia store 获取任务详情数据(兼容旧逻辑) + // const taskStore = useTaskStore(); + // const storedTask = taskStore.getTaskDetail; + // if (storedTask) { + // task.value = { + // ...task.value, + // ...storedTask + // }; + // } + // else { + // uni.showToast({ + // title: '缺少任务ID', + // icon: 'none' + // }); + // setTimeout(() => { + // uni.navigateBack(); + // }, 1500); + // } } }); @@ -1187,6 +1302,14 @@ onShow(() => { object-fit: cover; } +/* 任务文件展示 */ +.task-files-wrapper { + display: flex; + flex-direction: column; + gap: 8px; + margin-bottom: 16px; +} + /* 提交记录图片展示(一行三个) */ .record-images-wrapper { display: flex; diff --git a/pages/task/list/index.vue b/pages/task/list/index.vue index 99cd41c..35caaf1 100644 --- a/pages/task/list/index.vue +++ b/pages/task/list/index.vue @@ -87,7 +87,7 @@ diff --git a/pages/task/submit/index.vue b/pages/task/submit/index.vue index bc5d204..98183af 100644 --- a/pages/task/submit/index.vue +++ b/pages/task/submit/index.vue @@ -321,7 +321,6 @@ const handleSubmit = async () => { } .form-label { - flex: 1; font-size: 15px; color: #333; } diff --git a/utils/request/index.js b/utils/request/index.js index 1a9501b..814ab9f 100644 --- a/utils/request/index.js +++ b/utils/request/index.js @@ -10,7 +10,7 @@ export const Request = () => { // 初始化请求配置 uni.$uv.http.setConfig((config) => { /* config 为默认全局配置*/ - config.baseURL = 'http://192.168.1.5:4001'; /* 根域名 */ + config.baseURL = 'http://192.168.1.9:4001'; /* 根域名 */ // config.baseURL = 'https://pm.ccttiot.com/prod-api'; /* 根域名 */ return config })