feat: 完成需求工单管理模块(全栈开发)
后端: - 创建 Requirement 实体类(映射 requirement 表) - 创建 RequirementMapper 接口(含自定义查询) - 创建 RequirementService 业务逻辑层 - 分页查询(支持多条件筛选) - 新增/更新/删除需求 - 更新需求状态和进度 - 自动设置默认租户ID - 创建 RequirementController 控制器 - RESTful API 设计 - 状态管理和进度跟踪 前端: - 创建 requirement.js API 文件(完整的 CRUD 接口) - 创建 requirement.vue 管理页面 - 多条件搜索(需求名称、项目、客户、状态) - 表格展示(含状态标签、优先级标签、进度条) - 新增/编辑表单(支持工时、日期、应收款管理) - 状态更新对话框 - 进度更新对话框(滑块组件) - 项目和客户下拉选择 - 添加路由配置(/project/requirement)
This commit is contained in:
parent
1a47943b10
commit
9c00696baf
76
fund-admin/src/api/requirement.js
Normal file
76
fund-admin/src/api/requirement.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import request from '../utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取需求工单列表
|
||||||
|
*/
|
||||||
|
export const getRequirementList = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/proj/api/v1/requirement/list',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取需求工单详情
|
||||||
|
*/
|
||||||
|
export const getRequirementById = (requirementId) => {
|
||||||
|
return request({
|
||||||
|
url: `/proj/api/v1/requirement/${requirementId}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建需求工单
|
||||||
|
*/
|
||||||
|
export const createRequirement = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/proj/api/v1/requirement',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新需求工单
|
||||||
|
*/
|
||||||
|
export const updateRequirement = (requirementId, data) => {
|
||||||
|
return request({
|
||||||
|
url: `/proj/api/v1/requirement/${requirementId}`,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除需求工单
|
||||||
|
*/
|
||||||
|
export const deleteRequirement = (requirementId) => {
|
||||||
|
return request({
|
||||||
|
url: `/proj/api/v1/requirement/${requirementId}`,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新需求状态
|
||||||
|
*/
|
||||||
|
export const updateRequirementStatus = (requirementId, status) => {
|
||||||
|
return request({
|
||||||
|
url: `/proj/api/v1/requirement/${requirementId}/status`,
|
||||||
|
method: 'put',
|
||||||
|
params: { status }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新需求进度
|
||||||
|
*/
|
||||||
|
export const updateRequirementProgress = (requirementId, progress) => {
|
||||||
|
return request({
|
||||||
|
url: `/proj/api/v1/requirement/${requirementId}/progress`,
|
||||||
|
method: 'put',
|
||||||
|
params: { progress }
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -86,6 +86,12 @@ const routes = [
|
|||||||
name: 'Contract',
|
name: 'Contract',
|
||||||
component: () => import('../views/project/contract.vue'),
|
component: () => import('../views/project/contract.vue'),
|
||||||
meta: { title: '合同管理' }
|
meta: { title: '合同管理' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'requirement',
|
||||||
|
name: 'Requirement',
|
||||||
|
component: () => import('../views/project/requirement.vue'),
|
||||||
|
meta: { title: '需求工单' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
641
fund-admin/src/views/project/requirement.vue
Normal file
641
fund-admin/src/views/project/requirement.vue
Normal file
@ -0,0 +1,641 @@
|
|||||||
|
<template>
|
||||||
|
<div class="requirement-container">
|
||||||
|
<el-card>
|
||||||
|
<!-- 搜索栏 -->
|
||||||
|
<el-form :inline="true" :model="searchForm">
|
||||||
|
<el-form-item label="需求名称">
|
||||||
|
<el-input v-model="searchForm.requirementName" placeholder="请输入需求名称" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目">
|
||||||
|
<el-select v-model="searchForm.projectId" placeholder="请选择项目" clearable>
|
||||||
|
<el-option
|
||||||
|
v-for="project in projectOptions"
|
||||||
|
:key="project.projectId"
|
||||||
|
:label="project.projectName"
|
||||||
|
:value="project.projectId"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="客户">
|
||||||
|
<el-select v-model="searchForm.customerId" placeholder="请选择客户" clearable>
|
||||||
|
<el-option
|
||||||
|
v-for="customer in customerOptions"
|
||||||
|
:key="customer.customerId"
|
||||||
|
:label="customer.customerName"
|
||||||
|
:value="customer.customerId"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="searchForm.status" placeholder="请选择状态" clearable>
|
||||||
|
<el-option label="待开发" value="pending" />
|
||||||
|
<el-option label="开发中" value="developing" />
|
||||||
|
<el-option label="已交付" value="delivered" />
|
||||||
|
<el-option label="已完成" value="completed" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||||
|
<el-button @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 操作栏 -->
|
||||||
|
<el-row style="margin-bottom: 15px;">
|
||||||
|
<el-button type="primary" @click="handleAdd">新增需求</el-button>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 表格 -->
|
||||||
|
<el-table :data="tableData" border v-loading="loading">
|
||||||
|
<el-table-column prop="requirementCode" label="需求编号" width="150" />
|
||||||
|
<el-table-column prop="requirementName" label="需求名称" width="200" />
|
||||||
|
<el-table-column prop="projectId" label="项目" width="150">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ getProjectName(row.projectId) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="customerName" label="客户" width="150">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ getCustomerName(row.customerId) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="priority" label="优先级" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.priority === 'high'" type="danger">高</el-tag>
|
||||||
|
<el-tag v-else-if="row.priority === 'normal'" type="warning">中</el-tag>
|
||||||
|
<el-tag v-else type="info">低</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.status === 'pending'" type="info">待开发</el-tag>
|
||||||
|
<el-tag v-else-if="row.status === 'developing'" type="primary">开发中</el-tag>
|
||||||
|
<el-tag v-else-if="row.status === 'delivered'" type="warning">已交付</el-tag>
|
||||||
|
<el-tag v-else-if="row.status === 'completed'" type="success">已完成</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="progress" label="进度" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-progress :percentage="row.progress" :color="getProgressColor(row.progress)" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="estimatedHours" label="预估工时(h)" width="120" />
|
||||||
|
<el-table-column prop="actualHours" label="实际工时(h)" width="120" />
|
||||||
|
<el-table-column prop="receivableAmount" label="应收款" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.receivableAmount ? '¥' + row.receivableAmount : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="createdTime" label="创建时间" width="180" />
|
||||||
|
<el-table-column label="操作" width="280" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
|
||||||
|
<el-button type="info" size="small" @click="handleUpdateStatus(row)">更新状态</el-button>
|
||||||
|
<el-button type="warning" size="small" @click="handleUpdateProgress(row)">更新进度</el-button>
|
||||||
|
<el-button type="danger" size="small" @click="handleDelete(row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="page.current"
|
||||||
|
v-model:page-size="page.size"
|
||||||
|
:total="page.total"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="fetchData"
|
||||||
|
@current-change="fetchData"
|
||||||
|
style="margin-top: 20px; justify-content: flex-end;"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 新增/编辑对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="form.requirementId ? '编辑需求' : '新增需求'"
|
||||||
|
width="800px"
|
||||||
|
>
|
||||||
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="需求编号" prop="requirementCode">
|
||||||
|
<el-input v-model="form.requirementCode" placeholder="请输入需求编号" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="需求名称" prop="requirementName">
|
||||||
|
<el-input v-model="form.requirementName" placeholder="请输入需求名称" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="项目" prop="projectId">
|
||||||
|
<el-select v-model="form.projectId" placeholder="请选择项目" style="width: 100%;">
|
||||||
|
<el-option
|
||||||
|
v-for="project in projectOptions"
|
||||||
|
:key="project.projectId"
|
||||||
|
:label="project.projectName"
|
||||||
|
:value="project.projectId"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="客户" prop="customerId">
|
||||||
|
<el-select v-model="form.customerId" placeholder="请选择客户" style="width: 100%;">
|
||||||
|
<el-option
|
||||||
|
v-for="customer in customerOptions"
|
||||||
|
:key="customer.customerId"
|
||||||
|
:label="customer.customerName"
|
||||||
|
:value="customer.customerId"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="优先级" prop="priority">
|
||||||
|
<el-select v-model="form.priority" placeholder="请选择优先级" style="width: 100%;">
|
||||||
|
<el-option label="高" value="high" />
|
||||||
|
<el-option label="中" value="normal" />
|
||||||
|
<el-option label="低" value="low" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-select v-model="form.status" placeholder="请选择状态" style="width: 100%;">
|
||||||
|
<el-option label="待开发" value="pending" />
|
||||||
|
<el-option label="开发中" value="developing" />
|
||||||
|
<el-option label="已交付" value="delivered" />
|
||||||
|
<el-option label="已完成" value="completed" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-form-item label="需求描述" prop="description">
|
||||||
|
<el-input
|
||||||
|
v-model="form.description"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入需求描述"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="预估工时(h)" prop="estimatedHours">
|
||||||
|
<el-input-number v-model="form.estimatedHours" :min="0" :precision="2" style="width: 100%;" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="实际工时(h)" prop="actualHours">
|
||||||
|
<el-input-number v-model="form.actualHours" :min="0" :precision="2" style="width: 100%;" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="计划开始日期" prop="plannedStart">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.plannedStart"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择日期"
|
||||||
|
style="width: 100%;"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="计划结束日期" prop="plannedEnd">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.plannedEnd"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择日期"
|
||||||
|
style="width: 100%;"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="实际开始日期" prop="actualStart">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.actualStart"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择日期"
|
||||||
|
style="width: 100%;"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="实际结束日期" prop="actualEnd">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.actualEnd"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择日期"
|
||||||
|
style="width: 100%;"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="应收款金额" prop="receivableAmount">
|
||||||
|
<el-input-number v-model="form.receivableAmount" :min="0" :precision="2" style="width: 100%;" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="应收款日期" prop="receivableDate">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.receivableDate"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择日期"
|
||||||
|
style="width: 100%;"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-form-item label="交付日期" prop="deliveryDate">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.deliveryDate"
|
||||||
|
type="date"
|
||||||
|
placeholder="选择日期"
|
||||||
|
style="width: 100%;"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 更新状态对话框 -->
|
||||||
|
<el-dialog v-model="statusDialogVisible" title="更新状态" width="400px">
|
||||||
|
<el-form>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="statusForm.status" placeholder="请选择状态" style="width: 100%;">
|
||||||
|
<el-option label="待开发" value="pending" />
|
||||||
|
<el-option label="开发中" value="developing" />
|
||||||
|
<el-option label="已交付" value="delivered" />
|
||||||
|
<el-option label="已完成" value="completed" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="statusDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSubmitStatus">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 更新进度对话框 -->
|
||||||
|
<el-dialog v-model="progressDialogVisible" title="更新进度" width="400px">
|
||||||
|
<el-form>
|
||||||
|
<el-form-item label="进度">
|
||||||
|
<el-slider v-model="progressForm.progress" :min="0" :max="100" show-input />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="progressDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSubmitProgress">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import {
|
||||||
|
getRequirementList,
|
||||||
|
createRequirement,
|
||||||
|
updateRequirement,
|
||||||
|
deleteRequirement,
|
||||||
|
updateRequirementStatus,
|
||||||
|
updateRequirementProgress
|
||||||
|
} from '../../api/requirement'
|
||||||
|
import { getProjectList } from '../../api/project'
|
||||||
|
import { getCustomerList } from '../../api/customer'
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
requirementName: '',
|
||||||
|
projectId: null,
|
||||||
|
customerId: null,
|
||||||
|
status: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表格数据
|
||||||
|
const tableData = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const page = reactive({
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 项目选项
|
||||||
|
const projectOptions = ref([])
|
||||||
|
// 客户选项
|
||||||
|
const customerOptions = ref([])
|
||||||
|
|
||||||
|
// 对话框
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const formRef = ref(null)
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const form = reactive({
|
||||||
|
requirementId: null,
|
||||||
|
requirementCode: '',
|
||||||
|
requirementName: '',
|
||||||
|
description: '',
|
||||||
|
projectId: null,
|
||||||
|
customerId: null,
|
||||||
|
priority: 'normal',
|
||||||
|
status: 'pending',
|
||||||
|
estimatedHours: 0,
|
||||||
|
actualHours: 0,
|
||||||
|
plannedStart: null,
|
||||||
|
plannedEnd: null,
|
||||||
|
actualStart: null,
|
||||||
|
actualEnd: null,
|
||||||
|
deliveryDate: null,
|
||||||
|
receivableAmount: 0,
|
||||||
|
receivableDate: null
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules = {
|
||||||
|
requirementCode: [
|
||||||
|
{ required: true, message: '请输入需求编号', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
requirementName: [
|
||||||
|
{ required: true, message: '请输入需求名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
projectId: [
|
||||||
|
{ required: true, message: '请选择项目', trigger: 'change' }
|
||||||
|
],
|
||||||
|
customerId: [
|
||||||
|
{ required: true, message: '请选择客户', trigger: 'change' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态对话框
|
||||||
|
const statusDialogVisible = ref(false)
|
||||||
|
const statusForm = reactive({
|
||||||
|
requirementId: null,
|
||||||
|
status: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 进度对话框
|
||||||
|
const progressDialogVisible = ref(false)
|
||||||
|
const progressForm = reactive({
|
||||||
|
requirementId: null,
|
||||||
|
progress: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载项目列表
|
||||||
|
const loadProjects = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getProjectList({ current: 1, size: 1000 })
|
||||||
|
projectOptions.value = res.records || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载项目列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载客户列表
|
||||||
|
const loadCustomers = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getCustomerList({ current: 1, size: 1000 })
|
||||||
|
customerOptions.value = res.records || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载客户列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取项目名称
|
||||||
|
const getProjectName = (projectId) => {
|
||||||
|
const project = projectOptions.value.find(p => p.projectId === projectId)
|
||||||
|
return project ? project.projectName : '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取客户名称
|
||||||
|
const getCustomerName = (customerId) => {
|
||||||
|
const customer = customerOptions.value.find(c => c.customerId === customerId)
|
||||||
|
return customer ? customer.customerName : '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进度颜色
|
||||||
|
const getProgressColor = (percentage) => {
|
||||||
|
if (percentage < 30) return '#f56c6c'
|
||||||
|
if (percentage < 70) return '#e6a23c'
|
||||||
|
return '#67c23a'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
const fetchData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
current: page.current,
|
||||||
|
size: page.size,
|
||||||
|
requirementName: searchForm.requirementName || undefined,
|
||||||
|
projectId: searchForm.projectId || undefined,
|
||||||
|
customerId: searchForm.customerId || undefined,
|
||||||
|
status: searchForm.status || undefined
|
||||||
|
}
|
||||||
|
const res = await getRequirementList(params)
|
||||||
|
tableData.value = res.records
|
||||||
|
page.total = res.total
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载数据失败:', error)
|
||||||
|
ElMessage.error(error.message || '加载数据失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const handleSearch = () => {
|
||||||
|
page.current = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const handleReset = () => {
|
||||||
|
searchForm.requirementName = ''
|
||||||
|
searchForm.projectId = null
|
||||||
|
searchForm.customerId = null
|
||||||
|
searchForm.status = ''
|
||||||
|
page.current = 1
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const handleAdd = () => {
|
||||||
|
Object.assign(form, {
|
||||||
|
requirementId: null,
|
||||||
|
requirementCode: '',
|
||||||
|
requirementName: '',
|
||||||
|
description: '',
|
||||||
|
projectId: null,
|
||||||
|
customerId: null,
|
||||||
|
priority: 'normal',
|
||||||
|
status: 'pending',
|
||||||
|
estimatedHours: 0,
|
||||||
|
actualHours: 0,
|
||||||
|
plannedStart: null,
|
||||||
|
plannedEnd: null,
|
||||||
|
actualStart: null,
|
||||||
|
actualEnd: null,
|
||||||
|
deliveryDate: null,
|
||||||
|
receivableAmount: 0,
|
||||||
|
receivableDate: null
|
||||||
|
})
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = (row) => {
|
||||||
|
Object.assign(form, {
|
||||||
|
requirementId: row.requirementId,
|
||||||
|
requirementCode: row.requirementCode,
|
||||||
|
requirementName: row.requirementName,
|
||||||
|
description: row.description,
|
||||||
|
projectId: row.projectId,
|
||||||
|
customerId: row.customerId,
|
||||||
|
priority: row.priority,
|
||||||
|
status: row.status,
|
||||||
|
estimatedHours: row.estimatedHours,
|
||||||
|
actualHours: row.actualHours,
|
||||||
|
plannedStart: row.plannedStart,
|
||||||
|
plannedEnd: row.plannedEnd,
|
||||||
|
actualStart: row.actualStart,
|
||||||
|
actualEnd: row.actualEnd,
|
||||||
|
deliveryDate: row.deliveryDate,
|
||||||
|
receivableAmount: row.receivableAmount,
|
||||||
|
receivableDate: row.receivableDate
|
||||||
|
})
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
await formRef.value.validate()
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.warning('请检查表单填写是否完整')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (form.requirementId) {
|
||||||
|
await updateRequirement(form.requirementId, form)
|
||||||
|
ElMessage.success('更新成功')
|
||||||
|
} else {
|
||||||
|
await createRequirement(form)
|
||||||
|
ElMessage.success('创建成功')
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
await fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存失败:', error)
|
||||||
|
ElMessage.error(error.message || '操作失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
|
const handleUpdateStatus = (row) => {
|
||||||
|
statusForm.requirementId = row.requirementId
|
||||||
|
statusForm.status = row.status
|
||||||
|
statusDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交状态
|
||||||
|
const handleSubmitStatus = async () => {
|
||||||
|
try {
|
||||||
|
await updateRequirementStatus(statusForm.requirementId, statusForm.status)
|
||||||
|
ElMessage.success('状态更新成功')
|
||||||
|
statusDialogVisible.value = false
|
||||||
|
await fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('状态更新失败:', error)
|
||||||
|
ElMessage.error(error.message || '状态更新失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新进度
|
||||||
|
const handleUpdateProgress = (row) => {
|
||||||
|
progressForm.requirementId = row.requirementId
|
||||||
|
progressForm.progress = row.progress
|
||||||
|
progressDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交进度
|
||||||
|
const handleSubmitProgress = async () => {
|
||||||
|
try {
|
||||||
|
await updateRequirementProgress(progressForm.requirementId, progressForm.progress)
|
||||||
|
ElMessage.success('进度更新成功')
|
||||||
|
progressDialogVisible.value = false
|
||||||
|
await fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('进度更新失败:', error)
|
||||||
|
ElMessage.error(error.message || '进度更新失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const handleDelete = async (row) => {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm('确定要删除该需求吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
|
||||||
|
await deleteRequirement(row.requirementId)
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
|
||||||
|
// 如果是最后一条且不是第一页,返回上一页
|
||||||
|
if (tableData.value.length === 1 && page.current > 1) {
|
||||||
|
page.current--
|
||||||
|
}
|
||||||
|
|
||||||
|
await fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
if (error !== 'cancel') {
|
||||||
|
console.error('删除失败:', error)
|
||||||
|
ElMessage.error(error.message || '删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
loadProjects()
|
||||||
|
loadCustomers()
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.requirement-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user