diff --git a/antis-ncc-admin/src/views/lqReimbursementApplication/approval-history-dialog.vue b/antis-ncc-admin/src/views/lqReimbursementApplication/approval-history-dialog.vue index 711b8cf..a2eb8c1 100644 --- a/antis-ncc-admin/src/views/lqReimbursementApplication/approval-history-dialog.vue +++ b/antis-ncc-admin/src/views/lqReimbursementApplication/approval-history-dialog.vue @@ -162,7 +162,7 @@ export default { diff --git a/antis-ncc-admin/src/views/lqReimbursementApplication/edit-dialog.vue b/antis-ncc-admin/src/views/lqReimbursementApplication/edit-dialog.vue new file mode 100644 index 0000000..6665d75 --- /dev/null +++ b/antis-ncc-admin/src/views/lqReimbursementApplication/edit-dialog.vue @@ -0,0 +1,657 @@ + + + + + + + + + 基本信息 + + + + + + + + + + + + + + + + + + + + + + + + + + + + ¥ + + + + + + + + + + + 选择购买记录 + + + + 选择购买记录 + + + 已选择 {{ selectedPurchaseRecords.length }} 条记录 + + + + + + + + + + ¥{{ formatMoney(scope.row.unitPrice) }} + + + + + + ¥{{ formatMoney(scope.row.amount) }} + + + + + {{ formatDateTime(scope.row.purchaseTime) }} + + + + + + 移除 + + 初始记录 + + + + + + + + + 取 消 + + 保存修改 + + + + + + + + + + + + + + + + + ¥{{ formatMoney(scope.row.unitPrice) }} + + + + + + ¥{{ formatMoney(scope.row.amount) }} + + + + + {{ formatDateTime(scope.row.purchaseTime) }} + + + + + + + + + 取 消 + 确 定 + + + + + + + + + diff --git a/antis-ncc-admin/src/views/lqReimbursementApplication/form-dialog.vue b/antis-ncc-admin/src/views/lqReimbursementApplication/form-dialog.vue new file mode 100644 index 0000000..972a458 --- /dev/null +++ b/antis-ncc-admin/src/views/lqReimbursementApplication/form-dialog.vue @@ -0,0 +1,848 @@ + + + + + + + + + 基本信息 + + + + + + + + + + + + + + + + + + + + + + + + + + + + ¥ + + + + + + + + + + + 选择购买记录 + + + + 选择购买记录 + + + 已选择 {{ selectedPurchaseRecords.length }} 条记录 + + + + + + + + + + ¥{{ formatMoney(scope.row.unitPrice) }} + + + + + + ¥{{ formatMoney(scope.row.amount) }} + + + + + {{ formatDateTime(scope.row.purchaseTime) }} + + + + + + 移除 + + + + + + + + + + + + 审批节点配置(1-5个节点) + + 添加节点 + + + + + + 节点 {{ node.nodeOrder }} + + 删除 + + + + + + + + + + + + + + + + + + + handleApproverChange(index, val)" + style="width: 100%" + /> + + + + + + {{ name }} + + + + + + + + + + + + + + 取 消 + + 创建并提交审批 + + + + + + + + + + + + + + + + + ¥{{ formatMoney(scope.row.unitPrice) }} + + + + + + ¥{{ formatMoney(scope.row.amount) }} + + + + + {{ formatDateTime(scope.row.purchaseTime) }} + + + + + + + + + 取 消 + 确 定 + + + + + + + + + diff --git a/antis-ncc-admin/src/views/lqReimbursementApplication/index.vue b/antis-ncc-admin/src/views/lqReimbursementApplication/index.vue index b3423e0..127d47d 100644 --- a/antis-ncc-admin/src/views/lqReimbursementApplication/index.vue +++ b/antis-ncc-admin/src/views/lqReimbursementApplication/index.vue @@ -1,30 +1,13 @@ - - - - 报销申请管理 - 管理报销申请,包括待办审批、所有申请列表等 - - - - 刷新 - - - - - + 我的待办 @@ -40,17 +23,25 @@ - - - + + + > + + - + - + - + 搜索 重置 + + 新增 + @@ -131,21 +129,20 @@ :data="list" border stripe - height="calc(100vh - 500px)" :header-cell-style="{ background: '#f5f7fa', color: '#606266' }" > - + - + @@ -154,18 +151,18 @@ - - + + - {{ scope.row.applicationStoreId || '无' }} + {{ scope.row.applicationStoreName || '无' }} - + @@ -175,7 +172,7 @@ - + @@ -185,7 +182,7 @@ - + - + @@ -207,7 +204,7 @@ - + @@ -217,7 +214,7 @@ - + @@ -227,7 +224,7 @@ - + 审批历史 + + 修改 + @@ -267,6 +274,13 @@ /> + + + + + + + + + diff --git a/antis-ncc-admin/src/views/wageManagement/healthCoach.vue b/antis-ncc-admin/src/views/wageManagement/healthCoach.vue index 69e7beb..91de2f6 100644 --- a/antis-ncc-admin/src/views/wageManagement/healthCoach.vue +++ b/antis-ncc-admin/src/views/wageManagement/healthCoach.vue @@ -523,6 +523,18 @@ + + + + 详情 + + + @@ -576,6 +588,14 @@ @close="handleExtraDataDialogClose" @refresh="getList" /> + + + @@ -583,11 +603,13 @@ import { getHealthCoachSalaryList, calculateHealthCoachSalary } from '@/api/extend/healthCoachSalary' import { getStoreSelector } from '@/api/extend/store' import ExtraDataDialog from './extra-data-dialog.vue' +import DetailDialog from './detail-dialog.vue' export default { name: 'HealthCoachSalary', components: { - ExtraDataDialog + ExtraDataDialog, + DetailDialog }, data() { // 获取当前年月 @@ -612,6 +634,8 @@ export default { calculateYear: getCurrentYear(), calculateMonth: getCurrentMonth(), extraDataDialogVisible: false, + detailDialogVisible: false, + currentDetailData: null, queryParams: { currentPage: 1, pageSize: 300, // 写死为300 @@ -745,6 +769,18 @@ export default { this.extraDataDialogVisible = false }, + // 查看详情 + handleViewDetail(row) { + this.currentDetailData = row + this.detailDialogVisible = true + }, + + // 关闭详情弹窗 + handleDetailDialogClose() { + this.detailDialogVisible = false + this.currentDetailData = null + }, + // 确认计算工资 async handleCalculateConfirm() { if (!this.calculateYear || !this.calculateMonth) { diff --git a/绿纤uni-app/apis/modules/oauth.js b/绿纤uni-app/apis/modules/oauth.js index a9c771c..af44d7c 100644 --- a/绿纤uni-app/apis/modules/oauth.js +++ b/绿纤uni-app/apis/modules/oauth.js @@ -23,4 +23,8 @@ export default { UploadBase64Image(data) { return request.post(`${config.getApiBaseUrl()}/api/file/UploadBase64Image`, data); }, + // 获取用户列表 + getUserList(params) { + return request.get(`${config.getApiBaseUrl()}/api/permission/Users`, params); + }, } \ No newline at end of file diff --git a/绿纤uni-app/apis/modules/reimbursement.js b/绿纤uni-app/apis/modules/reimbursement.js index 29e3e37..f6a3842 100644 --- a/绿纤uni-app/apis/modules/reimbursement.js +++ b/绿纤uni-app/apis/modules/reimbursement.js @@ -26,13 +26,30 @@ export default { getStoreList(params) { return request.get(`${config.getApiBaseUrl()}/api/Extend/LqMdxx`, params); }, - // 通过审批 - approveReimbursement(id) { - return request.post(`${config.getApiBaseUrl()}/api/Extend/LqReimbursementApplication/${id}/Actions/Approve`); + // 审批(支持通过、不通过、退回) + approveReimbursement(id, params) { + const queryParams = new URLSearchParams() + if (params && params.result) { + queryParams.append('result', params.result) + } + if (params && params.opinion) { + queryParams.append('opinion', params.opinion) + } + const queryString = queryParams.toString() + const url = `${config.getApiBaseUrl()}/api/Extend/LqReimbursementApplication/${id}/Actions/Approve${queryString ? '?' + queryString : ''}` + return request.post(url); }, // 拒绝审批 rejectReimbursement(id) { return request.post(`${config.getApiBaseUrl()}/api/Extend/LqReimbursementApplication/${id}/Actions/Reject`); + }, + // 提交审批 + submitApproval(id) { + return request.post(`${config.getApiBaseUrl()}/api/Extend/LqReimbursementApplication/${id}/Actions/SubmitApproval`); + }, + // 获取我的待办列表 + getMyPendingList(params) { + return request.get(`${config.getApiBaseUrl()}/api/Extend/LqReimbursementApplication/Actions/PendingApproval`, params); } } diff --git a/绿纤uni-app/pages/purchase-list/purchase-list.vue b/绿纤uni-app/pages/purchase-list/purchase-list.vue index 70efe84..90038f6 100644 --- a/绿纤uni-app/pages/purchase-list/purchase-list.vue +++ b/绿纤uni-app/pages/purchase-list/purchase-list.vue @@ -79,7 +79,7 @@ 查看 - + 编辑 @@ -306,7 +306,7 @@ // 编辑记录(只有未审批状态才能编辑) handleEdit(item) { - if (item.approveStatus === '未审批' || item.approveStatus === '未通过') { + if (item.approveStatus === '未审批' || item.approveStatus === '已退回') { uni.navigateTo({ url: `/pages/purchase-form/purchase-form?id=${item.id}` }) diff --git a/绿纤uni-app/pages/reimbursement-audit-list/reimbursement-audit-list copy.vue b/绿纤uni-app/pages/reimbursement-audit-list/reimbursement-audit-list copy.vue new file mode 100644 index 0000000..6efba2c --- /dev/null +++ b/绿纤uni-app/pages/reimbursement-audit-list/reimbursement-audit-list copy.vue @@ -0,0 +1,613 @@ + + + + + + + {{ formatDateRange() }} + 📅 + + + + 审批状态 + + + {{ approveStatusOptions[approveStatusIndex].label }} + ▼ + + + + + + + + + + + + 报销审核列表 + 共 {{ totalCount }} 条 + + + + + + {{ item.applicationUserName || '无' }} + {{ formatTime(item.applicationTime) }} + + + + 门店: + {{ getStoreName(item.applicationStoreId) || '无' }} + + + 总金额: + ¥{{ item.amount || 0 }} + + + + + {{ item.approveStatus || '未审批' }} + + + + 查看 + + + 审核 + + + + + + + + 📋 + 暂无报销申请记录 + + + + 正在加载数据... + + + + + + + + + + + + diff --git a/绿纤uni-app/pages/reimbursement-audit-list/reimbursement-audit-list.vue b/绿纤uni-app/pages/reimbursement-audit-list/reimbursement-audit-list.vue index 6efba2c..f8ec16b 100644 --- a/绿纤uni-app/pages/reimbursement-audit-list/reimbursement-audit-list.vue +++ b/绿纤uni-app/pages/reimbursement-audit-list/reimbursement-audit-list.vue @@ -1,7 +1,7 @@ - + - - {{ item.approveStatus || '未审批' }} + + {{ item.approveStatus || '待审批' }} - - 查看 - - + 审核 + + 查看 + @@ -127,12 +127,14 @@ endDate: formatDateLocal(now), dateRange: [firstDay.getTime(), today.getTime()], // 审批状态筛选 - approveStatusIndex: 1, // 默认选择"未审批"(索引1) + approveStatusIndex: 0, // 默认选择"全部"(索引0) approveStatusOptions: [ { label: '全部', value: '' }, - { label: '未审批', value: '未审批' }, - { label: '已审批', value: '已审批' }, - { label: '未通过', value: '未通过' } + { label: '待审批', value: '待审批' }, + { label: '审批中', value: '审批中' }, + { label: '已通过', value: '已通过' }, + { label: '未通过', value: '未通过' }, + { label: '已退回', value: '已退回' } ] } }, @@ -226,7 +228,7 @@ this.loadReimbursementList() }, - // 加载报销申请列表 + // 加载报销申请列表(我的待办) async loadReimbursementList() { if (this.loading) return @@ -234,39 +236,73 @@ this.loading = true this.loadmoreStatus = 'loading' - // 构建查询参数 + // 构建查询参数(我的待办接口只支持门店筛选和分页参数) const params = { currentPage: this.currentPage, - pageSize: this.pageSize + pageSize: this.pageSize, + sidx: 'applicationTime', + sort: 'desc' } - // 添加审批状态筛选 - const selectedStatus = this.approveStatusOptions[this.approveStatusIndex] - if (selectedStatus && selectedStatus.value) { - params.approveStatus = selectedStatus.value - } - - // 添加日期范围筛选 - if (this.startDate && this.endDate) { - const startTimestamp = new Date(this.startDate + 'T00:00:00').getTime() - const endTimestamp = new Date(this.endDate + 'T23:59:59').getTime() - params.applicationTime = `${startTimestamp},${endTimestamp}` - } + // 我的待办接口支持门店筛选(如果需要的话,可以添加门店选择功能) + // 注意:我的待办接口不支持审批状态和日期范围筛选 + // 如果需要这些筛选,需要在前端进行过滤,或者使用其他接口 - const res = await api.getReimbursementList(params) + // 使用我的待办接口 + const res = await api.getMyPendingList(params) if (res.code === 200 && res.data) { - const list = res.data.list || [] + let list = res.data.list || [] const pagination = res.data.pagination || {} + // 前端过滤:根据审批状态筛选 + const selectedStatus = this.approveStatusOptions[this.approveStatusIndex] + if (selectedStatus && selectedStatus.value) { + list = list.filter(item => item.approveStatus === selectedStatus.value) + } + + // 前端过滤:根据日期范围筛选 + if (this.startDate && this.endDate) { + const startTimestamp = new Date(this.startDate + 'T00:00:00').getTime() + const endTimestamp = new Date(this.endDate + 'T23:59:59').getTime() + list = list.filter(item => { + if (!item.applicationTime) return false + const itemTime = new Date(item.applicationTime).getTime() + return itemTime >= startTimestamp && itemTime <= endTimestamp + }) + } + if (this.currentPage === 1) { this.dataList = list } else { - this.dataList = [...this.dataList, ...list] + // 加载更多时,需要去重(避免重复数据) + const existingIds = new Set(this.dataList.map(item => item.id)) + const newItems = list.filter(item => !existingIds.has(item.id)) + this.dataList = [...this.dataList, ...newItems] } - this.totalCount = pagination.total || 0 - this.hasMore = this.dataList.length < this.totalCount + // 注意:由于前端过滤,总数可能不准确 + // 如果第一页且有筛选条件,使用过滤后的数量;否则使用后端返回的总数 + if (this.currentPage === 1) { + const hasFilter = (selectedStatus && selectedStatus.value) || (this.startDate && this.endDate) + if (hasFilter) { + // 有筛选条件时,总数不准确,使用当前列表长度 + this.totalCount = list.length + } else { + // 无筛选条件时,使用后端返回的总数 + this.totalCount = pagination.total || list.length + } + } + + // 判断是否还有更多数据 + const hasFilter = (selectedStatus && selectedStatus.value) || (this.startDate && this.endDate) + if (hasFilter) { + // 有筛选条件时,如果返回的数据少于pageSize,说明没有更多了 + this.hasMore = list.length >= this.pageSize + } else { + // 无筛选条件时,使用后端分页信息 + this.hasMore = this.dataList.length < (pagination.total || 0) + } if (!this.hasMore) { this.loadmoreStatus = 'nomore' @@ -311,14 +347,14 @@ // 查看详情 handleView(item) { uni.navigateTo({ - url: `/pages/reimbursement-audit-detail/reimbursement-audit-detail?id=${item.id}` + url: `/pages/reimbursement-detail/reimbursement-detail?id=${item.id}` }) }, // 审核(跳转到详情页进行审核) handleAudit(item) { uni.navigateTo({ - url: `/pages/reimbursement-audit-detail/reimbursement-audit-detail?id=${item.id}` + url: `/pages/reimbursement-detail/reimbursement-detail?id=${item.id}` }) }, @@ -333,6 +369,24 @@ hour: '2-digit', minute: '2-digit' }) + }, + + // 获取状态样式类 + getStatusClass(status) { + switch (status) { + case '待审批': + return 'pending' + case '审批中': + return 'pending' + case '已通过': + return 'approved' + case '未通过': + return 'rejected' + case '已退回': + return 'rejected' + default: + return 'unapproved' + } } } } diff --git a/绿纤uni-app/pages/reimbursement-detail/reimbursement-detail - 副本.vue b/绿纤uni-app/pages/reimbursement-detail/reimbursement-detail - 副本.vue new file mode 100644 index 0000000..4f10d38 --- /dev/null +++ b/绿纤uni-app/pages/reimbursement-detail/reimbursement-detail - 副本.vue @@ -0,0 +1,562 @@ + + + + + 报销申请详情 + + + 正在加载详情... + + + + {{ error }} + 重试 + + + + + + + 基本信息 + + + 申请人 + {{ detailData.applicationUserName || '无' }} + + + 申请门店 + {{ getStoreName(detailData.applicationStoreId) || '无' }} + + + 申请时间 + {{ formatTime(detailData.applicationTime) }} + + + 总金额 + ¥{{ detailData.amount || 0 }} + + + 审批状态 + + {{ detailData.approveStatus || '未审批' }} + + + + + + + + 购买物品清单 + + + + {{ item.reimbursementCategoryName || '无' }} + ¥{{ item.amount || 0 }} + + + + 单价: + ¥{{ item.unitPrice || 0 }} + + + 数量: + {{ item.quantity || 0 }} + + + 购买时间: + {{ formatTime(item.purchaseTime) }} + + + + 备注: + {{ item.memo }} + + + + 附件: + + + 📎 + {{ file.name || '文件' }} + + + + + + + + + + 审批信息 + + + 审批人 + {{ detailData.approveUserName || detailData.approveUser || '无' }} + + + 审批时间 + {{ formatTime(detailData.approveTime) }} + + + + + + + + + + + + + diff --git a/绿纤uni-app/pages/reimbursement-detail/reimbursement-detail.vue b/绿纤uni-app/pages/reimbursement-detail/reimbursement-detail.vue index 4f10d38..65dacc8 100644 --- a/绿纤uni-app/pages/reimbursement-detail/reimbursement-detail.vue +++ b/绿纤uni-app/pages/reimbursement-detail/reimbursement-detail.vue @@ -20,47 +20,41 @@ 基本信息 - 申请人 - {{ detailData.applicationUserName || '无' }} + 申请人姓名 + {{ (formData && formData.applicationUserName) || detailData.applicationUserName || '无' }} 申请门店 - {{ getStoreName(detailData.applicationStoreId) || '无' }} + {{ formData ? (formData.applicationStoreName || getStoreName(formData.applicationStoreId)) : (getStoreName(detailData.applicationStoreId) || '无') }} 申请时间 - {{ formatTime(detailData.applicationTime) }} + {{ formatTime((formData && formData.applicationTime) || detailData.applicationTime) }} 总金额 - ¥{{ detailData.amount || 0 }} - - - 审批状态 - - {{ detailData.approveStatus || '未审批' }} - + ¥{{ formatMoney((formData && formData.amount) || detailData.amount) }} - + - 购买物品清单 + 购买记录列表 {{ item.reimbursementCategoryName || '无' }} - ¥{{ item.amount || 0 }} + ¥{{ formatMoney(item.amount) }} 单价: - ¥{{ item.unitPrice || 0 }} + ¥{{ formatMoney(item.unitPrice) }} 数量: - {{ item.quantity || 0 }} + {{ item.quantity || '无' }} 购买时间: @@ -68,20 +62,38 @@ - 备注: - {{ item.memo }} + 备注说明: + {{ item.memo || '无' }} 附件: - + 📎 - {{ file.name || '文件' }} + {{ item.attachment.length }}个文件 + + + + + + + + + + + 📄 + {{ file.name || '文件' + (fileIndex + 1) }} + + @@ -89,17 +101,137 @@ - - - 审批信息 - - - 审批人 - {{ detailData.approveUserName || detailData.approveUser || '无' }} + + + 审批流程 + + + + {{ node.nodeOrder }} + + {{ node.nodeName || '无' }} + + + {{ node.approvalType || '无' }} + + 必审 + + + + + + + 审批人: + + + {{ approver.userName || '无' }} + + + + + + + 审批记录: + + + + {{ record.approverName || '无' }} + + {{ record.approvalResult || '无' }} + + + + 意见:{{ record.approvalOpinion }} + + + 时间:{{ formatTime(record.approvalTime) }} + + + + - - 审批时间 - {{ formatTime(detailData.approveTime) }} + + + + + + 当前审批人 + + + {{ approver.userName || '无' }} + + + + + + + 退回原因 + + {{ returnedReason }} + + + + + + 审批操作 + + + 审批结果 + + + 通过 + + + 不通过 + + + 退回 + + + + + 审批意见 + + + + + {{ approving ? '提交中...' : '提交审批' }} + @@ -120,16 +252,55 @@ loading: false, error: null, detailData: null, + formData: null, // 表单数据 + nodes: [], // 审批节点 + currentApprovers: [], // 当前审批人 + currentNodeOrder: 0, // 当前节点顺序 + approvalStatus: '', // 审批状态 + returnedReason: null, // 退回原因 purchaseRecords: [], reimbursementId: null, storeOptions: [], - baseUrl: config.getApiBaseUrl() + baseUrl: config.getApiBaseUrl(), + userInfo: null, // 当前登录用户信息 + // 审批操作表单 + approvalForm: { + result: '通过', + opinion: '' + }, + approving: false // 审批提交中 + } + }, + + computed: { + // 判断是否可以审批 + canApprove() { + // 判断是否可以审批:状态为"审批中"且当前用户是当前审批人 + if (this.approvalStatus !== '审批中') { + return false + } + + if (!this.userInfo || !this.userInfo.userId) { + return false + } + + if (!this.currentApprovers || this.currentApprovers.length === 0) { + return false + } + + // 检查当前用户的userId是否在审批人列表中 + const isCurrentApprover = this.currentApprovers.some(approver => { + return approver.userId === this.userInfo.userId + }) + + return isCurrentApprover } }, onLoad(options) { if (options.id) { this.reimbursementId = options.id + this.userInfo = uni.getStorageSync('userInfo') this.loadStoreOptions() this.loadDetail() } else { @@ -174,10 +345,34 @@ const res = await api.getReimbursementDetail(this.reimbursementId) if (res.code === 200 && res.data) { - this.detailData = res.data + // 根据PC端的数据结构处理 + this.formData = res.data.form || res.data + this.detailData = this.formData // 保持兼容 + this.nodes = res.data.nodes || [] + this.currentApprovers = res.data.currentApprovers || [] + this.currentNodeOrder = res.data.currentNodeOrder || 0 + this.approvalStatus = res.data.approvalStatus || this.formData.approveStatus || this.formData.approvalStatus || '' + this.returnedReason = res.data.returnedReason || null - // 加载关联的购买记录 - if (this.detailData.purchaseRecordsId) { + // 处理购买记录 + if (res.data.purchaseRecords && res.data.purchaseRecords.length > 0) { + this.purchaseRecords = res.data.purchaseRecords.map(item => { + // 处理附件字段 + if (item.attachment) { + try { + item.attachment = typeof item.attachment === 'string' + ? JSON.parse(item.attachment) + : item.attachment + } catch (e) { + item.attachment = [] + } + } else { + item.attachment = [] + } + return item + }) + } else if (this.detailData.purchaseRecordsId) { + // 如果没有直接返回购买记录,则通过ID加载 await this.loadPurchaseRecords(this.detailData.purchaseRecordsId) } } else { @@ -231,30 +426,129 @@ // 格式化时间 formatTime(timestamp) { if (!timestamp) return '无' + // 如果是时间戳(数字),需要转换 + if (typeof timestamp === 'number') { + const date = new Date(timestamp) + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + const seconds = String(date.getSeconds()).padStart(2, '0') + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` + } + // 如果是字符串,尝试解析 const date = new Date(timestamp) - return date.toLocaleString('zh-CN', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit' + if (isNaN(date.getTime())) return timestamp + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + const seconds = String(date.getSeconds()).padStart(2, '0') + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` + }, + + // 格式化金额 + formatMoney(amount) { + if (!amount) return '0.00' + return Number(amount).toLocaleString('zh-CN', { + minimumFractionDigits: 2, + maximumFractionDigits: 2 }) }, + + // 获取图片URL + getImageUrl(url) { + if (!url) return '' + if (url.startsWith('http')) return url + return `${this.baseUrl}${url}` + }, + + // 判断是否为图片文件 + isImageFile(file) { + if (!file || !file.url) return false + const url = file.url.toLowerCase() + const name = (file.name || '').toLowerCase() + const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'] + return imageExtensions.some(ext => url.includes(ext) || name.includes(ext)) + }, // 获取状态样式类 getStatusClass(status) { switch (status) { - case '已审批': - return 'approved' case '待审批': return 'pending' + case '审批中': + return 'pending' + case '已通过': + return 'approved' case '未通过': return 'rejected' - case '未审批': + case '已退回': + return 'rejected' default: return 'unapproved' } }, + + // 获取审批记录样式类 + getRecordClass(result) { + switch (result) { + case '通过': + return 'result-success' + case '不通过': + return 'result-danger' + case '退回': + return 'result-warning' + case '待审批': + default: + return 'result-info' + } + }, + + // 提交审批 + async handleApprove() { + if (!this.approvalForm.result) { + uni.showToast({ + title: '请选择审批结果', + icon: 'none' + }) + return + } + + this.approving = true + try { + const res = await api.approveReimbursement(this.reimbursementId, { + result: this.approvalForm.result, + opinion: this.approvalForm.opinion || '' + }) + + if (res.code === 200) { + uni.showToast({ + title: '审批成功', + icon: 'success' + }) + // 重新加载详情 + setTimeout(() => { + this.loadDetail() + }, 1500) + } else { + uni.showToast({ + title: res.message || '审批失败', + icon: 'none' + }) + } + } catch (error) { + console.error('审批失败:', error) + uni.showToast({ + title: '审批失败', + icon: 'none' + }) + } finally { + this.approving = false + } + }, // 预览文件 previewFile(file, attachmentList) { if (!file || !file.url) { @@ -536,27 +830,393 @@ margin-top: 8rpx; } - .attachment-item { + .attachment-count { display: flex; align-items: center; gap: 8rpx; - padding: 8rpx 12rpx; - background: #f5f5f5; - border-radius: 8rpx; - cursor: pointer; + margin-bottom: 16rpx; } .file-icon { font-size: 24rpx; } - .file-name { + .file-count { font-size: 24rpx; - color: #409EFF; + color: #606266; + } + + .attachment-items { + display: flex; + flex-wrap: wrap; + gap: 16rpx; + margin-top: 16rpx; + } + + .attachment-item { + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s; + } + + .attachment-item:active { + opacity: 0.7; + transform: scale(0.98); + } + + .attachment-image-wrapper { + width: 200rpx; + height: 200rpx; + border-radius: 12rpx; + overflow: hidden; + border: 2rpx solid #e0e0e0; + background: #f5f5f5; + } + + .attachment-image { + width: 100%; + height: 100%; + } + + .attachment-file-info { + display: flex; + align-items: center; + gap: 12rpx; + padding: 16rpx 24rpx; + background: #f9fff9; + border: 2rpx solid #c8e6c9; + border-radius: 12rpx; + min-width: 200rpx; + } + + .attachment-file-info .file-icon { + font-size: 32rpx; + } + + .attachment-file-info .file-name { + font-size: 24rpx; + color: #2e7d32; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + + /* 审批流程样式 */ + .approval-flow { + display: flex; + flex-direction: column; + gap: 24rpx; + } + + .flow-node { + padding: 24rpx; + border: 2rpx solid #e4e7ed; + border-radius: 16rpx; + background: #fafafa; + transition: all 0.3s; + } + + .flow-node.node-active { + border-color: #409EFF; + background: #ecf5ff; + } + + .flow-node.node-completed { + border-color: #67C23A; + background: #f0f9ff; + } + + .flow-node.node-pending { + border-color: #e4e7ed; + background: #fafafa; + opacity: 0.6; + } + + .node-header { + display: flex; + align-items: center; + margin-bottom: 24rpx; + } + + .node-order { + width: 64rpx; + height: 64rpx; + line-height: 64rpx; + text-align: center; + background: #409EFF; + color: #fff; + border-radius: 50%; + font-weight: 600; + font-size: 28rpx; + margin-right: 24rpx; + flex-shrink: 0; + } + + .node-info { + flex: 1; + } + + .node-name { + font-size: 32rpx; + font-weight: 600; + color: #303133; + margin-bottom: 16rpx; + } + + .node-type { + display: flex; + gap: 16rpx; + } + + .node-type-tag { + display: inline-block; + padding: 4rpx 16rpx; + border-radius: 8rpx; + font-size: 22rpx; + font-weight: 500; + } + + .node-type-tag.type-success { + background: #f0f9ff; + color: #67C23A; + border: 2rpx solid #c8e6c9; + } + + .node-type-tag.type-warning { + background: #fff3e0; + color: #e6a23c; + border: 2rpx solid #ffe0b2; + } + + .node-type-tag.type-info { + background: #e3f2fd; + color: #909399; + border: 2rpx solid #bbdefb; + } + + .approvers-list { + margin-bottom: 24rpx; + display: flex; + align-items: flex-start; + gap: 16rpx; + } + + .approvers-label { + min-width: 160rpx; + color: #606266; + font-weight: 500; + font-size: 26rpx; + } + + .approvers-items { + flex: 1; + display: flex; + flex-wrap: wrap; + gap: 12rpx; + } + + .approver-tag { + display: inline-block; + padding: 8rpx 16rpx; + background: #e8f5e9; + border: 2rpx solid #c8e6c9; + border-radius: 16rpx; + font-size: 24rpx; + color: #2e7d32; + } + + .approval-records { + margin-top: 24rpx; + padding-top: 24rpx; + border-top: 2rpx solid #e4e7ed; + } + + .records-label { + color: #606266; + font-weight: 500; + font-size: 26rpx; + margin-bottom: 16rpx; + display: block; + } + + .records-list { + display: flex; + flex-direction: column; + gap: 16rpx; + } + + .record-item { + padding: 16rpx; + background: #fff; + border-radius: 8rpx; + border-left: 6rpx solid #409EFF; + } + + .record-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8rpx; + } + + .record-approver { + font-weight: 500; + color: #303133; + font-size: 26rpx; + } + + .record-result-tag { + display: inline-block; + padding: 4rpx 12rpx; + border-radius: 8rpx; + font-size: 22rpx; + font-weight: 500; + } + + .record-result-tag.result-success { + background: #f0f9ff; + color: #67C23A; + } + + .record-result-tag.result-danger { + background: #ffebee; + color: #F56C6C; + } + + .record-result-tag.result-warning { + background: #fff3e0; + color: #e6a23c; + } + + .record-result-tag.result-info { + background: #e3f2fd; + color: #909399; + } + + .record-opinion { + color: #606266; + font-size: 24rpx; + margin-bottom: 8rpx; + line-height: 1.6; + } + + .record-time { + color: #909399; + font-size: 22rpx; + } + + /* 当前审批人样式 */ + .current-approvers { + padding: 24rpx; + background: #fff3cd; + border-radius: 16rpx; + display: flex; + flex-wrap: wrap; + gap: 16rpx; + } + + .current-approver-tag { + display: inline-block; + padding: 12rpx 24rpx; + background: #fff; + border: 2rpx solid #e6a23c; + border-radius: 16rpx; + font-size: 26rpx; + color: #e6a23c; + font-weight: 500; + } + + /* 退回原因样式 */ + .returned-reason { + padding: 24rpx; + background: #fef0f0; + border-radius: 16rpx; + color: #F56C6C; + line-height: 1.6; + font-size: 26rpx; + } + + /* 审批操作表单样式 */ + .approval-form { + display: flex; + flex-direction: column; + gap: 32rpx; + } + + .form-item { + display: flex; + flex-direction: column; + gap: 16rpx; + } + + .form-label { + font-size: 28rpx; + color: #2e7d32; + font-weight: 600; + } + + .radio-group { + display: flex; + gap: 24rpx; + } + + .radio-item { + flex: 1; + padding: 20rpx; + background: #f9fff9; + border: 3rpx solid #c8e6c9; + border-radius: 16rpx; + text-align: center; + font-size: 26rpx; + color: #2e7d32; + transition: all 0.2s; + } + + .radio-item.active { + background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); + border-color: #43e97b; + color: #fff; + font-weight: 600; + } + + .form-textarea { + width: 100%; + min-height: 200rpx; + padding: 24rpx; + background: #f9fff9; + border: 3rpx solid #c8e6c9; + border-radius: 16rpx; + font-size: 26rpx; + color: #2e7d32; + box-sizing: border-box; + } + + .form-actions { + margin-top: 16rpx; + } + + .approve-btn { + width: 100%; + padding:10rpx 32rpx; + background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); + color: #fff; + border-radius: 48rpx; + font-size: 32rpx; + font-weight: 600; + border: none; + box-shadow: 0 8rpx 24rpx rgba(67, 233, 123, 0.3); + } + + .approve-btn:active { + transform: scale(0.98); + box-shadow: 0 4rpx 12rpx rgba(67, 233, 123, 0.4); + } + + .approve-btn:disabled { + opacity: 0.5; + } diff --git a/绿纤uni-app/pages/reimbursement-form/reimbursement-form - 副本.vue b/绿纤uni-app/pages/reimbursement-form/reimbursement-form - 副本.vue new file mode 100644 index 0000000..f26ba0c --- /dev/null +++ b/绿纤uni-app/pages/reimbursement-form/reimbursement-form - 副本.vue @@ -0,0 +1,887 @@ + + + + + + + + + + + + + + 申请时间 + + + + {{ formData.applicationTimeStr || '请选择申请时间' }} + ▼ + + + + + + + + 总金额 + + + + + + + + 购买物品清单 + + + 选择购买物品 + + + 已选择 {{ selectedPurchaseRecords.length }} 条记录 + + + + + + + + + + {{ item.reimbursementCategoryName || '无' }} + + ✕ + + + + 单价: ¥{{ item.unitPrice || 0 }} + 数量: {{ item.quantity || 0 }} + 金额: ¥{{ item.amount || 0 }} + + + 购买时间: {{ formatTime(item.purchaseTime) }} + + + + + + + + + {{ isSubmitting ? '提交中...' : (formData.id ? '更新' : '提交') }} + + + + + + + + + + + 选择购买物品 + + ✕ + + + + + + + + + + 加载中... + 暂无可用记录 + + + + {{ item.reimbursementCategoryName || '无' }} + ✓ + + + 单价: ¥{{ item.unitPrice || 0 }} + 数量: {{ item.quantity || 0 }} + 金额: ¥{{ item.amount || 0 }} + + + {{ formatTime(item.purchaseTime) }} + + + + + + + 取消 + 确定 + + + + + + + + + + diff --git a/绿纤uni-app/pages/reimbursement-form/reimbursement-form.vue b/绿纤uni-app/pages/reimbursement-form/reimbursement-form.vue index f26ba0c..0440f38 100644 --- a/绿纤uni-app/pages/reimbursement-form/reimbursement-form.vue +++ b/绿纤uni-app/pages/reimbursement-form/reimbursement-form.vue @@ -62,8 +62,11 @@ {{ item.reimbursementCategoryName || '无' }} - - ✕ + + 初始记录 + + ✕ + @@ -78,6 +81,54 @@ + + + + 审批节点配置(1-5个节点) + + + 添加节点 + + + + + + 节点 {{ node.nodeOrder }} + + 删除 + + + + + 节点名称 + + + + 审批类型 + { node.approvalType = ['会签', '或签'][e.detail.value] }"> + + {{ node.approvalType || '请选择审批类型' }} + ▼ + + + + + 审批人 + + + {{ getApproverDisplayText(node) }} + + + + {{ name }} + + + + + + + + + - 加载中... - 暂无可用记录 + 加载中... + 暂无可用记录 {{ formatTime(item.purchaseTime) }} + 加载更多... @@ -138,12 +190,63 @@ + + + + + + 选择审批人 + + ✕ + + + + + + + + + + 加载中... + 暂无用户 + + + + {{ item.fullName || item.realName || item.userName || item.id || '无' }} + ✓ + + + 账号: {{ item.account || '无' }} + ID: {{ item.id || '无' }} + + + 加载更多... + 没有更多了 + + 共 {{ userTotal }} 人,已加载 {{ filteredUserList.length }} 人 + + + + + + 取消 + 确定 + + + + + + diff --git a/绿纤uni-app/pages/reimbursement-list/reimbursement-list.vue b/绿纤uni-app/pages/reimbursement-list/reimbursement-list.vue index 40628b1..2eddbbf 100644 --- a/绿纤uni-app/pages/reimbursement-list/reimbursement-list.vue +++ b/绿纤uni-app/pages/reimbursement-list/reimbursement-list.vue @@ -58,14 +58,14 @@ - - {{ item.approveStatus || '未审批' }} + + {{ item.approveStatus || '待审批' }} 查看 - + 编辑 @@ -138,16 +138,18 @@ approveStatusIndex: 0, approveStatusOptions: [ { label: '全部', value: '' }, - { label: '未审批', value: '未审批' }, { label: '待审批', value: '待审批' }, - { label: '已审批', value: '已审批' }, - { label: '未通过', value: '未通过' } + { label: '审批中', value: '审批中' }, + { label: '已通过', value: '已通过' }, + { label: '未通过', value: '未通过' }, + { label: '已退回', value: '已退回' } ] } }, - onLoad() { + onShow() { // this.loadStoreOptions() + this.currentPage = 1 this.loadReimbursementList() }, @@ -320,15 +322,15 @@ }) }, - // 编辑记录(只有未审批状态才能编辑) + // 编辑记录(已退回或未通过状态才能编辑) handleEdit(item) { - if (item.approveStatus === '未审批') { + if (item.approveStatus === '已退回' || item.approveStatus === '未通过') { uni.navigateTo({ url: `/pages/reimbursement-form/reimbursement-form?id=${item.id}` }) } else { uni.showToast({ - title: '已审批的记录不能编辑', + title: '该状态的记录不能编辑', icon: 'none' }) } @@ -336,8 +338,8 @@ // 处理列表项点击(用于编辑) handleItemClick(item) { - // 只有未审批状态才能编辑 - if (item.approveStatus === '未审批') { + // 已退回或未通过状态才能编辑 + if (item.approveStatus === '已退回' || item.approveStatus === '未通过') { this.handleEdit(item) } }, @@ -353,21 +355,6 @@ hour: '2-digit', minute: '2-digit' }) - }, - - // 获取状态样式类 - getStatusClass(status) { - switch (status) { - case '已审批': - return 'approved' - case '待审批': - return 'pending' - case '未通过': - return 'rejected' - case '未审批': - default: - return 'unapproved' - } } } }
管理报销申请,包括待办审批、所有申请列表等