- 新增 Project 类型定义,完善项目数据结构 - 新增 updateProject、updateProjectStatus 等接口封装 - 添加权限控制,实现基于角色的编辑、删除、状态更新等操作权限判断 - 实现项目编辑模态框,支持项目基本信息、预算、进度等字段的修改 - 实现项目状态更新模态框,支持项目状态的切换操作 - 实现项目经理更换模态框,支持更新项目负责人信息 - 更新项目列表操作菜单,添加编辑、状态更新、项目经理更换等功能入口 - 优化项目状态显示,新增状态中文映射文本显示 - 完善项目编辑和状态更新的保存逻辑,增加操作成功与失败提示信息 - 引入表单校验和操作Loading状态,提升交互体验
This commit is contained in:
@@ -17,6 +17,7 @@ export type ProjectItem = {
|
|||||||
projectType?: string;
|
projectType?: string;
|
||||||
managerId?: number;
|
managerId?: number;
|
||||||
managerName?: string;
|
managerName?: string;
|
||||||
|
managerAvatar?: string;
|
||||||
planStartDate?: string;
|
planStartDate?: string;
|
||||||
planEndDate?: string;
|
planEndDate?: string;
|
||||||
progress?: number;
|
progress?: number;
|
||||||
@@ -60,6 +61,60 @@ export const deleteProject = (id: string) => {
|
|||||||
return http.request<Result<void>>("delete", `/api/v1/project/${id}`);
|
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 定义 */
|
/** 项目统计数据 - 根据 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">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { useProject } from "./utils/hook";
|
import { useProject } from "./utils/hook";
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
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 CreateProjectWizard from "./components/CreateProjectWizard.vue";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
@@ -23,6 +32,34 @@ defineOptions({
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const wizardVisible = ref(false);
|
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 {
|
const {
|
||||||
form,
|
form,
|
||||||
formRef,
|
formRef,
|
||||||
@@ -59,8 +96,106 @@ function handleView(row: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 编辑项目
|
// 编辑项目
|
||||||
function handleEdit(row: any) {
|
function handleEdit(row: ProjectItem) {
|
||||||
console.log("编辑项目", row);
|
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";
|
return "success";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取状态文本
|
||||||
|
function getStatusText(status?: string): string {
|
||||||
|
const statusMap: Record<string, string> = {
|
||||||
|
draft: "草稿",
|
||||||
|
planning: "规划中",
|
||||||
|
ongoing: "进行中",
|
||||||
|
paused: "已暂停",
|
||||||
|
completed: "已完成",
|
||||||
|
cancelled: "已取消"
|
||||||
|
};
|
||||||
|
return statusMap[status || ""] || status || "未知";
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -344,11 +492,38 @@ function getRiskType(risk?: string): "success" | "warning" | "danger" {
|
|||||||
<component :is="useRenderIcon(ViewIcon)" class="mr-2" />
|
<component :is="useRenderIcon(ViewIcon)" class="mr-2" />
|
||||||
查看详情
|
查看详情
|
||||||
</el-dropdown-item>
|
</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" />
|
<component :is="useRenderIcon(EditPenIcon)" class="mr-2" />
|
||||||
编辑项目
|
编辑项目
|
||||||
</el-dropdown-item>
|
</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" />
|
<component :is="useRenderIcon(DeleteIcon)" class="mr-2" />
|
||||||
<span class="text-red-500">删除项目</span>
|
<span class="text-red-500">删除项目</span>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
@@ -363,7 +538,7 @@ function getRiskType(risk?: string): "success" | "warning" | "danger" {
|
|||||||
size="small"
|
size="small"
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
>
|
>
|
||||||
{{ item.status || "未知" }}
|
{{ getStatusText(item.status) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<el-tag
|
<el-tag
|
||||||
:type="getRiskType(item.riskLevel)"
|
:type="getRiskType(item.riskLevel)"
|
||||||
@@ -400,7 +575,10 @@ function getRiskType(risk?: string): "success" | "warning" | "danger" {
|
|||||||
|
|
||||||
<div class="flex-bc">
|
<div class="flex-bc">
|
||||||
<div class="flex items-center gap-2">
|
<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)" />
|
<component :is="useRenderIcon(UserIcon)" />
|
||||||
</el-avatar>
|
</el-avatar>
|
||||||
<span class="text-sm">{{ item.managerName || "未分配" }}</span>
|
<span class="text-sm">{{ item.managerName || "未分配" }}</span>
|
||||||
@@ -436,6 +614,207 @@ function getRiskType(risk?: string): "success" | "warning" | "danger" {
|
|||||||
v-model:visible="wizardVisible"
|
v-model:visible="wizardVisible"
|
||||||
@success="handleWizardSuccess"
|
@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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user