- 新增 Project 类型定义,完善项目数据结构 - 新增 updateProject、updateProjectStatus 等接口封装 - 添加权限控制,实现基于角色的编辑、删除、状态更新等操作权限判断 - 实现项目编辑模态框,支持项目基本信息、预算、进度等字段的修改 - 实现项目状态更新模态框,支持项目状态的切换操作 - 实现项目经理更换模态框,支持更新项目负责人信息 - 更新项目列表操作菜单,添加编辑、状态更新、项目经理更换等功能入口 - 优化项目状态显示,新增状态中文映射文本显示 - 完善项目编辑和状态更新的保存逻辑,增加操作成功与失败提示信息 - 引入表单校验和操作Loading状态,提升交互体验
This commit is contained in:
@@ -17,6 +17,7 @@ export type ProjectItem = {
|
||||
projectType?: string;
|
||||
managerId?: number;
|
||||
managerName?: string;
|
||||
managerAvatar?: string;
|
||||
planStartDate?: string;
|
||||
planEndDate?: string;
|
||||
progress?: number;
|
||||
@@ -60,6 +61,60 @@ export const deleteProject = (id: string) => {
|
||||
return http.request<Result<void>>("delete", `/api/v1/project/${id}`);
|
||||
};
|
||||
|
||||
// ==================== 项目管理 API ====================
|
||||
|
||||
/** 项目实体 - 根据 OpenAPI 定义(用于编辑) */
|
||||
export type Project = {
|
||||
id?: string;
|
||||
projectCode?: string;
|
||||
projectName?: string;
|
||||
projectType?: string;
|
||||
description?: string;
|
||||
objectives?: string;
|
||||
managerId?: string;
|
||||
sponsorId?: string;
|
||||
planStartDate?: string;
|
||||
planEndDate?: string;
|
||||
actualStartDate?: string;
|
||||
actualEndDate?: string;
|
||||
budget?: number;
|
||||
cost?: number;
|
||||
currency?: string;
|
||||
progress?: number;
|
||||
status?: string; // draft-草稿, planning-规划中, ongoing-进行中, paused-暂停, completed-已完成, cancelled-已取消
|
||||
priority?: string; // critical-关键, high-高, medium-中, low-低
|
||||
riskLevel?: string; // high-高, medium-中, low-低
|
||||
visibility?: number; // 1-公开, 2-部门内, 3-项目成员
|
||||
tags?: string[];
|
||||
extraData?: Record<string, any>;
|
||||
};
|
||||
|
||||
/** 修改项目 */
|
||||
export const updateProject = (data: Project) => {
|
||||
return http.request<Result<void>>("put", "/api/v1/project", { data });
|
||||
};
|
||||
|
||||
/** 更新项目状态 */
|
||||
export const updateProjectStatus = (id: string, status: string) => {
|
||||
return http.request<Result<void>>("put", `/api/v1/project/${id}/status`, {
|
||||
params: { status }
|
||||
});
|
||||
};
|
||||
|
||||
/** 更新项目进度 */
|
||||
export const updateProjectProgress = (id: string, progress: number) => {
|
||||
return http.request<Result<void>>("put", `/api/v1/project/${id}/progress`, {
|
||||
params: { progress }
|
||||
});
|
||||
};
|
||||
|
||||
/** 更新项目经理 */
|
||||
export const updateProjectManager = (id: string, managerName: string) => {
|
||||
return http.request<Result<void>>("put", `/api/v1/project/${id}/manager`, {
|
||||
params: { managerName }
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== 项目统计 ====================
|
||||
|
||||
/** 项目统计数据 - 根据 OpenAPI 定义 */
|
||||
|
||||
396
src/api/项目管理.openapi.json
Normal file
396
src/api/项目管理.openapi.json
Normal file
@@ -0,0 +1,396 @@
|
||||
{
|
||||
"openapi": "3.0.1",
|
||||
"info": {
|
||||
"title": "默认模块",
|
||||
"description": "",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"tags": [],
|
||||
"paths": {
|
||||
"/api/v1/project": {
|
||||
"put": {
|
||||
"summary": "修改项目",
|
||||
"deprecated": false,
|
||||
"description": "",
|
||||
"tags": [],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"in": "header",
|
||||
"description": "",
|
||||
"example": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Project",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BaseResponseVoid"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
}
|
||||
},
|
||||
"/api/v1/project/{id}": {
|
||||
"delete": {
|
||||
"summary": "删除项目",
|
||||
"deprecated": false,
|
||||
"description": "",
|
||||
"tags": [],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"in": "header",
|
||||
"description": "",
|
||||
"example": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BaseResponseVoid"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
}
|
||||
},
|
||||
"/api/v1/project/{id}/status": {
|
||||
"put": {
|
||||
"summary": "更新项目状态",
|
||||
"deprecated": false,
|
||||
"description": "",
|
||||
"tags": [],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"in": "query",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"in": "header",
|
||||
"description": "",
|
||||
"example": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BaseResponseVoid"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
}
|
||||
},
|
||||
"/api/v1/project/{id}/progress": {
|
||||
"put": {
|
||||
"summary": "更新项目进度",
|
||||
"deprecated": false,
|
||||
"description": "",
|
||||
"tags": [],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "progress",
|
||||
"in": "query",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"in": "header",
|
||||
"description": "",
|
||||
"example": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BaseResponseVoid"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
}
|
||||
},
|
||||
"/api/v1/project/{id}/manager": {
|
||||
"put": {
|
||||
"summary": "更新项目经理",
|
||||
"deprecated": false,
|
||||
"description": "",
|
||||
"tags": [],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "managerId",
|
||||
"in": "query",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"in": "header",
|
||||
"description": "",
|
||||
"example": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BaseResponseVoid"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Project": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"description": "",
|
||||
"format": "int64"
|
||||
},
|
||||
"projectCode": {
|
||||
"type": "string",
|
||||
"description": "项目编号"
|
||||
},
|
||||
"projectName": {
|
||||
"type": "string",
|
||||
"description": "项目名称"
|
||||
},
|
||||
"projectType": {
|
||||
"type": "string",
|
||||
"description": "项目类型"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "项目描述"
|
||||
},
|
||||
"objectives": {
|
||||
"type": "string",
|
||||
"description": "项目目标"
|
||||
},
|
||||
"managerId": {
|
||||
"type": "integer",
|
||||
"description": "项目经理ID",
|
||||
"format": "int64"
|
||||
},
|
||||
"sponsorId": {
|
||||
"type": "integer",
|
||||
"description": "项目发起人ID",
|
||||
"format": "int64"
|
||||
},
|
||||
"planStartDate": {
|
||||
"type": "string",
|
||||
"description": "计划开始日期"
|
||||
},
|
||||
"planEndDate": {
|
||||
"type": "string",
|
||||
"description": "计划结束日期"
|
||||
},
|
||||
"actualStartDate": {
|
||||
"type": "string",
|
||||
"description": "实际开始日期"
|
||||
},
|
||||
"actualEndDate": {
|
||||
"type": "string",
|
||||
"description": "实际结束日期"
|
||||
},
|
||||
"budget": {
|
||||
"type": "number",
|
||||
"description": "项目预算"
|
||||
},
|
||||
"cost": {
|
||||
"type": "number",
|
||||
"description": "已花费金额"
|
||||
},
|
||||
"currency": {
|
||||
"type": "string",
|
||||
"description": "币种"
|
||||
},
|
||||
"progress": {
|
||||
"type": "integer",
|
||||
"description": "进度百分比"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "状态: draft-草稿, planning-规划中, ongoing-进行中, paused-暂停, completed-已完成, cancelled-已取消"
|
||||
},
|
||||
"priority": {
|
||||
"type": "string",
|
||||
"description": "优先级: critical-关键, high-高, medium-中, low-低"
|
||||
},
|
||||
"riskLevel": {
|
||||
"type": "string",
|
||||
"description": "风险等级: high-高, medium-中, low-低"
|
||||
},
|
||||
"visibility": {
|
||||
"type": "integer",
|
||||
"description": "可见性: 1-公开, 2-部门内, 3-项目成员"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "标签列表"
|
||||
},
|
||||
"extraData": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"description": "扩展数据"
|
||||
},
|
||||
"createBy": {
|
||||
"type": "integer",
|
||||
"description": "创建人",
|
||||
"format": "int64"
|
||||
},
|
||||
"createTime": {
|
||||
"type": "string",
|
||||
"description": "创建时间"
|
||||
},
|
||||
"updateBy": {
|
||||
"type": "integer",
|
||||
"description": "更新人",
|
||||
"format": "int64"
|
||||
},
|
||||
"updateTime": {
|
||||
"type": "string",
|
||||
"description": "更新时间"
|
||||
},
|
||||
"deleted": {
|
||||
"type": "integer",
|
||||
"description": "删除标记"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BaseResponseVoid": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"description": ""
|
||||
},
|
||||
"data": {
|
||||
"description": "",
|
||||
"type": "null"
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"servers": [],
|
||||
"security": []
|
||||
}
|
||||
@@ -1,8 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { ref, computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useProject } from "./utils/hook";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { hasPerms } from "@/utils/auth";
|
||||
import { message } from "@/utils/message";
|
||||
import {
|
||||
updateProject,
|
||||
updateProjectStatus,
|
||||
updateProjectManager,
|
||||
type Project,
|
||||
type ProjectItem
|
||||
} from "@/api/project";
|
||||
import CreateProjectWizard from "./components/CreateProjectWizard.vue";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
@@ -23,6 +32,34 @@ defineOptions({
|
||||
const router = useRouter();
|
||||
const wizardVisible = ref(false);
|
||||
|
||||
// 权限控制
|
||||
const canEditProject = computed(() => hasPerms("project:center:update"));
|
||||
const canDeleteProject = computed(() => hasPerms("project:center:delete"));
|
||||
const canUpdateProjectStatus = computed(() =>
|
||||
hasPerms("project:center:update")
|
||||
);
|
||||
|
||||
// 项目编辑模态框
|
||||
const projectEditModal = ref(false);
|
||||
const projectEditForm = ref<Project>({});
|
||||
const projectEditLoading = ref(false);
|
||||
|
||||
// 状态更新模态框
|
||||
const statusUpdateModal = ref(false);
|
||||
const statusUpdateForm = ref({
|
||||
id: "",
|
||||
status: ""
|
||||
});
|
||||
const statusUpdateLoading = ref(false);
|
||||
|
||||
// 项目经理更新模态框
|
||||
const managerUpdateModal = ref(false);
|
||||
const managerUpdateForm = ref({
|
||||
id: "",
|
||||
managerName: ""
|
||||
});
|
||||
const managerUpdateLoading = ref(false);
|
||||
|
||||
const {
|
||||
form,
|
||||
formRef,
|
||||
@@ -59,8 +96,106 @@ function handleView(row: any) {
|
||||
}
|
||||
|
||||
// 编辑项目
|
||||
function handleEdit(row: any) {
|
||||
console.log("编辑项目", row);
|
||||
function handleEdit(row: ProjectItem) {
|
||||
projectEditForm.value = {
|
||||
id: row.id,
|
||||
projectCode: row.projectCode,
|
||||
projectName: row.projectName,
|
||||
projectType: row.projectType,
|
||||
planStartDate: row.planStartDate,
|
||||
planEndDate: row.planEndDate,
|
||||
progress: row.progress,
|
||||
status: row.status,
|
||||
priority: row.priority,
|
||||
riskLevel: row.riskLevel,
|
||||
budget: row.budget,
|
||||
cost: row.cost
|
||||
};
|
||||
projectEditModal.value = true;
|
||||
}
|
||||
|
||||
/** 保存项目编辑 */
|
||||
async function saveProjectEdit() {
|
||||
if (!projectEditForm.value.projectName) {
|
||||
message("请输入项目名称", { type: "warning" });
|
||||
return;
|
||||
}
|
||||
projectEditLoading.value = true;
|
||||
try {
|
||||
await updateProject(projectEditForm.value);
|
||||
message("项目更新成功", { type: "success" });
|
||||
projectEditModal.value = false;
|
||||
onSearch();
|
||||
} catch (error) {
|
||||
console.error("更新项目失败:", error);
|
||||
message("更新项目失败", { type: "error" });
|
||||
} finally {
|
||||
projectEditLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开状态更新模态框 */
|
||||
function openStatusModal(row: ProjectItem) {
|
||||
statusUpdateForm.value = {
|
||||
id: row.id!,
|
||||
status: row.status || "ongoing"
|
||||
};
|
||||
statusUpdateModal.value = true;
|
||||
}
|
||||
|
||||
/** 保存状态更新 */
|
||||
async function saveStatusUpdate() {
|
||||
if (!statusUpdateForm.value.status) {
|
||||
message("请选择状态", { type: "warning" });
|
||||
return;
|
||||
}
|
||||
statusUpdateLoading.value = true;
|
||||
try {
|
||||
await updateProjectStatus(
|
||||
statusUpdateForm.value.id,
|
||||
statusUpdateForm.value.status
|
||||
);
|
||||
message("状态更新成功", { type: "success" });
|
||||
statusUpdateModal.value = false;
|
||||
onSearch();
|
||||
} catch (error) {
|
||||
console.error("更新状态失败:", error);
|
||||
message("更新状态失败", { type: "error" });
|
||||
} finally {
|
||||
statusUpdateLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开项目经理更新模态框 */
|
||||
function openManagerModal(row: ProjectItem) {
|
||||
managerUpdateForm.value = {
|
||||
id: row.id!,
|
||||
managerName: ""
|
||||
};
|
||||
managerUpdateModal.value = true;
|
||||
}
|
||||
|
||||
/** 保存项目经理更新 */
|
||||
async function saveManagerUpdate() {
|
||||
if (!managerUpdateForm.value.managerName) {
|
||||
message("请输入项目经理姓名", { type: "warning" });
|
||||
return;
|
||||
}
|
||||
managerUpdateLoading.value = true;
|
||||
try {
|
||||
await updateProjectManager(
|
||||
managerUpdateForm.value.id,
|
||||
managerUpdateForm.value.managerName
|
||||
);
|
||||
message("项目经理更新成功", { type: "success" });
|
||||
managerUpdateModal.value = false;
|
||||
onSearch();
|
||||
} catch (error) {
|
||||
console.error("更新项目经理失败:", error);
|
||||
message("更新项目经理失败", { type: "error" });
|
||||
} finally {
|
||||
managerUpdateLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取状态标签类型
|
||||
@@ -94,6 +229,19 @@ function getRiskType(risk?: string): "success" | "warning" | "danger" {
|
||||
return "success";
|
||||
}
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
function getStatusText(status?: string): string {
|
||||
const statusMap: Record<string, string> = {
|
||||
draft: "草稿",
|
||||
planning: "规划中",
|
||||
ongoing: "进行中",
|
||||
paused: "已暂停",
|
||||
completed: "已完成",
|
||||
cancelled: "已取消"
|
||||
};
|
||||
return statusMap[status || ""] || status || "未知";
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -344,11 +492,38 @@ function getRiskType(risk?: string): "success" | "warning" | "danger" {
|
||||
<component :is="useRenderIcon(ViewIcon)" class="mr-2" />
|
||||
查看详情
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="handleEdit(item)">
|
||||
<el-dropdown-item
|
||||
v-if="canEditProject"
|
||||
@click="handleEdit(item)"
|
||||
>
|
||||
<component :is="useRenderIcon(EditPenIcon)" class="mr-2" />
|
||||
编辑项目
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided @click="handleDelete(item)">
|
||||
<el-dropdown-item
|
||||
v-if="canUpdateProjectStatus"
|
||||
@click="openStatusModal(item)"
|
||||
>
|
||||
<component
|
||||
:is="useRenderIcon('ri/settings-3-line')"
|
||||
class="mr-2"
|
||||
/>
|
||||
更新状态
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="canEditProject"
|
||||
@click="openManagerModal(item)"
|
||||
>
|
||||
<component
|
||||
:is="useRenderIcon('ri-user-settings-line')"
|
||||
class="mr-2"
|
||||
/>
|
||||
更换项目经理
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="canDeleteProject"
|
||||
divided
|
||||
@click="handleDelete(item)"
|
||||
>
|
||||
<component :is="useRenderIcon(DeleteIcon)" class="mr-2" />
|
||||
<span class="text-red-500">删除项目</span>
|
||||
</el-dropdown-item>
|
||||
@@ -363,7 +538,7 @@ function getRiskType(risk?: string): "success" | "warning" | "danger" {
|
||||
size="small"
|
||||
class="mr-2"
|
||||
>
|
||||
{{ item.status || "未知" }}
|
||||
{{ getStatusText(item.status) }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
:type="getRiskType(item.riskLevel)"
|
||||
@@ -400,7 +575,10 @@ function getRiskType(risk?: string): "success" | "warning" | "danger" {
|
||||
|
||||
<div class="flex-bc">
|
||||
<div class="flex items-center gap-2">
|
||||
<el-avatar :size="28">
|
||||
<el-avatar v-if="item.managerAvatar" :size="28">
|
||||
<img :src="item.managerAvatar" alt="负责人头像" />
|
||||
</el-avatar>
|
||||
<el-avatar v-else :size="28">
|
||||
<component :is="useRenderIcon(UserIcon)" />
|
||||
</el-avatar>
|
||||
<span class="text-sm">{{ item.managerName || "未分配" }}</span>
|
||||
@@ -436,6 +614,207 @@ function getRiskType(risk?: string): "success" | "warning" | "danger" {
|
||||
v-model:visible="wizardVisible"
|
||||
@success="handleWizardSuccess"
|
||||
/>
|
||||
|
||||
<!-- 项目编辑模态框 -->
|
||||
<el-dialog
|
||||
v-model="projectEditModal"
|
||||
title="编辑项目"
|
||||
width="600px"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form
|
||||
ref="projectFormRef"
|
||||
:model="projectEditForm"
|
||||
label-width="100px"
|
||||
class="project-edit-form"
|
||||
>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="项目名称" prop="projectName" required>
|
||||
<el-input
|
||||
v-model="projectEditForm.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目编号">
|
||||
<el-input
|
||||
v-model="projectEditForm.projectCode"
|
||||
placeholder="请输入项目编号"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目类型">
|
||||
<el-input
|
||||
v-model="projectEditForm.projectType"
|
||||
placeholder="请输入项目类型"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="计划开始日期">
|
||||
<el-date-picker
|
||||
v-model="projectEditForm.planStartDate"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="计划结束日期">
|
||||
<el-date-picker
|
||||
v-model="projectEditForm.planEndDate"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="优先级">
|
||||
<el-select
|
||||
v-model="projectEditForm.priority"
|
||||
placeholder="请选择优先级"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="关键" value="critical" />
|
||||
<el-option label="高" value="high" />
|
||||
<el-option label="中" value="medium" />
|
||||
<el-option label="低" value="low" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="风险等级">
|
||||
<el-select
|
||||
v-model="projectEditForm.riskLevel"
|
||||
placeholder="请选择风险等级"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="高" value="high" />
|
||||
<el-option label="中" value="medium" />
|
||||
<el-option label="低" value="low" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目预算">
|
||||
<el-input-number
|
||||
v-model="projectEditForm.budget"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
placeholder="预算"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="已花费金额">
|
||||
<el-input-number
|
||||
v-model="projectEditForm.cost"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
placeholder="已花费"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="项目进度">
|
||||
<el-slider
|
||||
v-model="projectEditForm.progress"
|
||||
:max="100"
|
||||
show-input
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="projectEditModal = false">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="projectEditLoading"
|
||||
@click="saveProjectEdit"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 状态更新模态框 -->
|
||||
<el-dialog
|
||||
v-model="statusUpdateModal"
|
||||
title="更新项目状态"
|
||||
width="400px"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form :model="statusUpdateForm" label-width="80px">
|
||||
<el-form-item label="项目状态">
|
||||
<el-select
|
||||
v-model="statusUpdateForm.status"
|
||||
placeholder="请选择状态"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="草稿" value="draft" />
|
||||
<el-option label="规划中" value="planning" />
|
||||
<el-option label="进行中" value="ongoing" />
|
||||
<el-option label="已暂停" value="paused" />
|
||||
<el-option label="已完成" value="completed" />
|
||||
<el-option label="已取消" value="cancelled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="statusUpdateModal = false">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="statusUpdateLoading"
|
||||
@click="saveStatusUpdate"
|
||||
>
|
||||
确认
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 项目经理更新模态框 -->
|
||||
<el-dialog
|
||||
v-model="managerUpdateModal"
|
||||
title="更换项目经理"
|
||||
width="450px"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form :model="managerUpdateForm" label-width="100px">
|
||||
<el-form-item label="当前负责人">
|
||||
<el-input
|
||||
:model-value="managerUpdateForm.managerName"
|
||||
disabled
|
||||
placeholder="暂无"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="新项目经理" required>
|
||||
<el-input
|
||||
v-model="managerUpdateForm.managerName"
|
||||
placeholder="请输入新项目经理的姓名"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="managerUpdateModal = false">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="managerUpdateLoading"
|
||||
@click="saveManagerUpdate"
|
||||
>
|
||||
确认更换
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user