- 定义资源实体类型及相关请求参数类型 - 实现资源增删改查及状态、数量更新接口 - 添加资源预算汇总、待审批和即将到位资源查询接口 - 在项目详情页增加资源权限控制相关计算属性 - 实现资源编辑模态框及新增、编辑、保存、删除功能 - 资源列表新增操作列支持资源编辑和删除权限控制 - 在界面中显示资源数量及新增资源按钮 - 移除冗余操作按钮,统一资源操作权限管理
This commit is contained in:
@@ -605,3 +605,148 @@ export const getTaskStatusStats = (projectId: string) => {
|
||||
{ params: { projectId } }
|
||||
);
|
||||
};
|
||||
|
||||
// ==================== 资源管理 API ====================
|
||||
|
||||
/** 资源实体 - 根据 OpenAPI 定义 */
|
||||
export type Resource = {
|
||||
id?: string;
|
||||
resourceCode?: string;
|
||||
projectId?: string;
|
||||
resourceType?: string; // human-人力, material-物料, equipment-设备, software-软件, finance-资金, other-其他
|
||||
resourceName?: string;
|
||||
description?: string;
|
||||
specification?: string;
|
||||
unit?: string;
|
||||
planQuantity?: number;
|
||||
actualQuantity?: number;
|
||||
unitPrice?: number;
|
||||
currency?: string;
|
||||
supplier?: string;
|
||||
status?: string; // planned-计划中, requested-已申请, approved-已批准, procuring-采购中, arrived-已到货, in_use-使用中, completed-已完成
|
||||
planArriveDate?: string;
|
||||
actualArriveDate?: string;
|
||||
responsibleId?: string;
|
||||
responsibleName?: string;
|
||||
location?: string;
|
||||
tags?: string[];
|
||||
extraData?: Record<string, any>;
|
||||
createBy?: string;
|
||||
createTime?: string;
|
||||
updateBy?: string;
|
||||
updateTime?: string;
|
||||
};
|
||||
|
||||
/** 资源更新请求 - 根据 OpenAPI 定义 */
|
||||
export type ResourceUpdateRequest = {
|
||||
id: string;
|
||||
resourceType?: string;
|
||||
resourceName?: string;
|
||||
description?: string;
|
||||
specification?: string;
|
||||
unit?: string;
|
||||
planQuantity?: number;
|
||||
actualQuantity?: number;
|
||||
unitPrice?: number;
|
||||
currency?: string;
|
||||
supplier?: string;
|
||||
status?: string;
|
||||
planArriveDate?: string;
|
||||
actualArriveDate?: string;
|
||||
responsibleId?: string;
|
||||
responsibleName?: string;
|
||||
location?: string;
|
||||
tags?: string[];
|
||||
extraData?: Record<string, any>;
|
||||
};
|
||||
|
||||
/** 资源查询参数 */
|
||||
export type ResourceQueryParams = {
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
projectId?: string;
|
||||
resourceType?: string;
|
||||
status?: string;
|
||||
keyword?: string;
|
||||
};
|
||||
|
||||
/** 分页数据结构(资源列表用) */
|
||||
export type PageResult<T> = {
|
||||
records: T[];
|
||||
total: number;
|
||||
size: number;
|
||||
current: number;
|
||||
};
|
||||
|
||||
/** 分页查询资源列表 */
|
||||
export const getResourceList = (params?: ResourceQueryParams) => {
|
||||
return http.request<Result<PageResult<Resource>>>(
|
||||
"get",
|
||||
"/api/v1/resource/list",
|
||||
{ params }
|
||||
);
|
||||
};
|
||||
|
||||
/** 根据ID查询资源详情 */
|
||||
export const getResourceById = (id: string) => {
|
||||
return http.request<Result<Resource>>("get", `/api/v1/resource/${id}`);
|
||||
};
|
||||
|
||||
/** 新增资源 */
|
||||
export const createResource = (data: Resource) => {
|
||||
return http.request<Result<string>>("post", "/api/v1/resource", { data });
|
||||
};
|
||||
|
||||
/** 修改资源 */
|
||||
export const updateResource = (data: ResourceUpdateRequest) => {
|
||||
return http.request<Result<void>>("put", "/api/v1/resource", { data });
|
||||
};
|
||||
|
||||
/** 删除资源 */
|
||||
export const deleteResource = (id: string) => {
|
||||
return http.request<Result<void>>("delete", `/api/v1/resource/${id}`);
|
||||
};
|
||||
|
||||
/** 更新资源状态 */
|
||||
export const updateResourceStatus = (id: string, status: string) => {
|
||||
return http.request<Result<void>>("put", `/api/v1/resource/${id}/status`, {
|
||||
params: { status }
|
||||
});
|
||||
};
|
||||
|
||||
/** 更新资源数量 */
|
||||
export const updateResourceQuantity = (id: string, actualQuantity: string) => {
|
||||
return http.request<Result<void>>("put", `/api/v1/resource/${id}/quantity`, {
|
||||
params: { actualQuantity }
|
||||
});
|
||||
};
|
||||
|
||||
/** 查询资源预算汇总 */
|
||||
export const getResourceBudgetStats = (projectId: string) => {
|
||||
return http.request<Result<Record<string, any>[]>>(
|
||||
"get",
|
||||
"/api/v1/resource/stats/budget",
|
||||
{ params: { projectId } }
|
||||
);
|
||||
};
|
||||
|
||||
/** 查询即将到位的资源 */
|
||||
export const getPendingArrivalResources = (
|
||||
projectId: string,
|
||||
days: number = 7
|
||||
) => {
|
||||
return http.request<Result<Resource[]>>(
|
||||
"get",
|
||||
"/api/v1/resource/pending-arrival",
|
||||
{ params: { projectId, days } }
|
||||
);
|
||||
};
|
||||
|
||||
/** 查询待审批的资源申请 */
|
||||
export const getPendingApprovalResources = (projectId?: string) => {
|
||||
return http.request<Result<Record<string, any>[]>>(
|
||||
"get",
|
||||
"/api/v1/resource/pending-approval",
|
||||
{ params: { projectId } }
|
||||
);
|
||||
};
|
||||
|
||||
917
src/api/资源模块.openapi.json
Normal file
917
src/api/资源模块.openapi.json
Normal file
@@ -0,0 +1,917 @@
|
||||
{
|
||||
"openapi": "3.0.1",
|
||||
"info": {
|
||||
"title": "默认模块",
|
||||
"description": "",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"tags": [],
|
||||
"paths": {
|
||||
"/api/v1/resource/list": {
|
||||
"get": {
|
||||
"summary": "分页查询资源列表",
|
||||
"deprecated": false,
|
||||
"description": "",
|
||||
"tags": [],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "pageNum",
|
||||
"in": "query",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"example": 1,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pageSize",
|
||||
"in": "query",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"example": 10,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "projectId",
|
||||
"in": "query",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "resourceType",
|
||||
"in": "query",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"in": "query",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "keyword",
|
||||
"in": "query",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"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/BaseResponsePageMapObject"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
}
|
||||
},
|
||||
"/api/v1/resource/{id}": {
|
||||
"get": {
|
||||
"summary": "根据ID查询资源详情",
|
||||
"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/BaseResponseResource"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
},
|
||||
"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/resource": {
|
||||
"post": {
|
||||
"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/Resource",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BaseResponseLong"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
},
|
||||
"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/ResourceUpdateRequest",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BaseResponseVoid"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
}
|
||||
},
|
||||
"/api/v1/resource/{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/resource/{id}/quantity": {
|
||||
"put": {
|
||||
"summary": "更新资源数量",
|
||||
"deprecated": false,
|
||||
"description": "",
|
||||
"tags": [],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "actualQuantity",
|
||||
"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/resource/stats/budget": {
|
||||
"get": {
|
||||
"summary": "查询资源预算汇总",
|
||||
"deprecated": false,
|
||||
"description": "",
|
||||
"tags": [],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "projectId",
|
||||
"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/BaseResponseListMapObject"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
}
|
||||
},
|
||||
"/api/v1/resource/pending-arrival": {
|
||||
"get": {
|
||||
"summary": "查询即将到位的资源",
|
||||
"deprecated": false,
|
||||
"description": "",
|
||||
"tags": [],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "projectId",
|
||||
"in": "query",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "days",
|
||||
"in": "query",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"example": 7,
|
||||
"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/BaseResponseListResource"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
}
|
||||
},
|
||||
"/api/v1/resource/pending-approval": {
|
||||
"get": {
|
||||
"summary": "查询待审批的资源申请",
|
||||
"deprecated": false,
|
||||
"description": "",
|
||||
"tags": [],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "projectId",
|
||||
"in": "query",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"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/BaseResponseListMapObject"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"OrderItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"column": {
|
||||
"type": "string",
|
||||
"description": "需要进行排序的字段"
|
||||
},
|
||||
"asc": {
|
||||
"type": "boolean",
|
||||
"description": "是否正序排列,默认 true",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"MapObject": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"$ref": "#/components/schemas/key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PageMapObject": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"records": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/MapObject"
|
||||
},
|
||||
"description": "查询数据列表",
|
||||
"default": "Collections.emptyList()"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer",
|
||||
"description": "总数",
|
||||
"format": "int64",
|
||||
"default": 0
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"description": "每页显示条数,默认 10",
|
||||
"format": "int64",
|
||||
"default": 10
|
||||
},
|
||||
"current": {
|
||||
"type": "integer",
|
||||
"description": "当前页",
|
||||
"format": "int64",
|
||||
"default": 1
|
||||
},
|
||||
"orders": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/OrderItem",
|
||||
"description": "com.baomidou.mybatisplus.core.metadata.OrderItem"
|
||||
},
|
||||
"description": "排序字段信息",
|
||||
"default": "new ArrayList<>()"
|
||||
},
|
||||
"optimizeCountSql": {
|
||||
"type": "boolean",
|
||||
"description": "自动优化 COUNT SQL",
|
||||
"default": true
|
||||
},
|
||||
"searchCount": {
|
||||
"type": "boolean",
|
||||
"description": "是否进行 count 查询",
|
||||
"default": true
|
||||
},
|
||||
"optimizeJoinOfCountSql": {
|
||||
"type": "boolean",
|
||||
"description": "{@link #optimizeJoinOfCountSql()}",
|
||||
"default": true
|
||||
},
|
||||
"maxLimit": {
|
||||
"type": "integer",
|
||||
"description": "单页分页条数限制",
|
||||
"format": "int64"
|
||||
},
|
||||
"countId": {
|
||||
"type": "string",
|
||||
"description": "countId"
|
||||
},
|
||||
"pages": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BaseResponsePageMapObject": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"description": ""
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/PageMapObject",
|
||||
"description": ""
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"Resource": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"description": "",
|
||||
"format": "int64"
|
||||
},
|
||||
"resourceCode": {
|
||||
"type": "string",
|
||||
"description": "资源编号"
|
||||
},
|
||||
"projectId": {
|
||||
"type": "integer",
|
||||
"description": "项目ID",
|
||||
"format": "int64"
|
||||
},
|
||||
"resourceType": {
|
||||
"type": "string",
|
||||
"description": "资源类型: human-人力, material-物料, equipment-设备, software-软件, finance-资金, other-其他"
|
||||
},
|
||||
"resourceName": {
|
||||
"type": "string",
|
||||
"description": "资源名称"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "资源描述"
|
||||
},
|
||||
"specification": {
|
||||
"type": "string",
|
||||
"description": "规格型号"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"description": "单位"
|
||||
},
|
||||
"planQuantity": {
|
||||
"type": "number",
|
||||
"description": "计划数量"
|
||||
},
|
||||
"actualQuantity": {
|
||||
"type": "number",
|
||||
"description": "实际数量"
|
||||
},
|
||||
"unitPrice": {
|
||||
"type": "number",
|
||||
"description": "单价"
|
||||
},
|
||||
"currency": {
|
||||
"type": "string",
|
||||
"description": "币种"
|
||||
},
|
||||
"supplier": {
|
||||
"type": "string",
|
||||
"description": "供应商/来源"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "状态: planned-计划中, requested-已申请, approved-已批准, procuring-采购中, arrived-已到货, in_use-使用中, completed-已完成"
|
||||
},
|
||||
"planArriveDate": {
|
||||
"type": "string",
|
||||
"description": "计划到位日期"
|
||||
},
|
||||
"actualArriveDate": {
|
||||
"type": "string",
|
||||
"description": "实际到位日期"
|
||||
},
|
||||
"responsibleId": {
|
||||
"type": "integer",
|
||||
"description": "负责人ID",
|
||||
"format": "int64"
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "存放位置"
|
||||
},
|
||||
"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": "删除标记"
|
||||
}
|
||||
}
|
||||
},
|
||||
"key": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"BaseResponseLong": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"description": ""
|
||||
},
|
||||
"data": {
|
||||
"type": "integer",
|
||||
"description": "",
|
||||
"format": "int64"
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"BaseResponseResource": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"description": ""
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/Resource",
|
||||
"description": ""
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"BaseResponseVoid": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"description": ""
|
||||
},
|
||||
"data": {
|
||||
"description": "",
|
||||
"type": "null"
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"ResourceUpdateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"description": "资源ID(必填)",
|
||||
"format": "int64"
|
||||
},
|
||||
"resourceType": {
|
||||
"type": "string",
|
||||
"description": "资源类型: human-人力, material-物料, equipment-设备, software-软件, finance-资金, other-其他"
|
||||
},
|
||||
"resourceName": {
|
||||
"type": "string",
|
||||
"description": "资源名称"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "资源描述"
|
||||
},
|
||||
"specification": {
|
||||
"type": "string",
|
||||
"description": "规格型号"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"description": "单位"
|
||||
},
|
||||
"planQuantity": {
|
||||
"type": "number",
|
||||
"description": "计划数量"
|
||||
},
|
||||
"actualQuantity": {
|
||||
"type": "number",
|
||||
"description": "实际数量"
|
||||
},
|
||||
"unitPrice": {
|
||||
"type": "number",
|
||||
"description": "单价"
|
||||
},
|
||||
"currency": {
|
||||
"type": "string",
|
||||
"description": "币种"
|
||||
},
|
||||
"supplier": {
|
||||
"type": "string",
|
||||
"description": "供应商/来源"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "状态: planned-计划中, requested-已申请, approved-已批准, procuring-采购中, arrived-已到货, in_use-使用中, completed-已完成"
|
||||
},
|
||||
"planArriveDate": {
|
||||
"type": "string",
|
||||
"description": "计划到位日期"
|
||||
},
|
||||
"actualArriveDate": {
|
||||
"type": "string",
|
||||
"description": "实际到位日期"
|
||||
},
|
||||
"responsibleId": {
|
||||
"type": "integer",
|
||||
"description": "负责人ID(直接传递ID时使用)",
|
||||
"format": "int64"
|
||||
},
|
||||
"responsibleName": {
|
||||
"type": "string",
|
||||
"description": "负责人姓名(根据姓名自动匹配用户ID)"
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "存放位置"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "标签"
|
||||
},
|
||||
"extraData": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"description": "扩展数据"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BaseResponseListMapObject": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"description": ""
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/MapObject"
|
||||
},
|
||||
"description": ""
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"BaseResponseListResource": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"description": ""
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Resource",
|
||||
"description": "资源实体类\n对应数据库表: resource"
|
||||
},
|
||||
"description": ""
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"servers": [],
|
||||
"security": []
|
||||
}
|
||||
@@ -10,12 +10,17 @@ import {
|
||||
createMilestone,
|
||||
updateMilestone,
|
||||
deleteMilestone,
|
||||
createResource,
|
||||
updateResource,
|
||||
deleteResource,
|
||||
type ProjectDetail,
|
||||
type ProjectMember,
|
||||
type ProjectMilestone,
|
||||
type ProjectTask,
|
||||
type ProjectResource,
|
||||
type ProjectRisk
|
||||
type ProjectRisk,
|
||||
type Resource,
|
||||
type ResourceUpdateRequest
|
||||
} from "@/api/project";
|
||||
import { hasPerms } from "@/utils/auth";
|
||||
import { GGanttChart, GGanttRow } from "@infectoone/vue-ganttastic";
|
||||
@@ -56,6 +61,13 @@ const canCreateMilestone = computed(() => hasPerms("project:milestone:create"));
|
||||
const canUpdateMilestone = computed(() => hasPerms("project:milestone:update"));
|
||||
const canDeleteMilestone = computed(() => hasPerms("project:milestone:delete"));
|
||||
|
||||
// 权限控制 - 资源(基础权限)
|
||||
const canCreateResource = computed(() => hasPerms("project:resource:create"));
|
||||
const canUpdateResource = computed(() => hasPerms("project:resource:update"));
|
||||
const canDeleteResource = computed(() => hasPerms("project:resource:delete"));
|
||||
const canViewResource = computed(() => hasPerms("project:resource:view"));
|
||||
const canStatsResource = computed(() => hasPerms("project:resource:stats"));
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(false);
|
||||
const ganttLoading = ref(false);
|
||||
@@ -121,6 +133,32 @@ const milestoneEditForm = ref<ProjectMilestone>({
|
||||
const milestoneEditLoading = ref(false);
|
||||
const isMilestoneEdit = ref(false); // true=编辑, false=新增
|
||||
|
||||
// 资源编辑模态框
|
||||
const resourceEditModal = ref(false);
|
||||
const resourceEditForm = ref<Resource>({
|
||||
id: "",
|
||||
resourceCode: "",
|
||||
resourceType: "material",
|
||||
resourceName: "",
|
||||
description: "",
|
||||
specification: "",
|
||||
unit: "",
|
||||
planQuantity: 0,
|
||||
actualQuantity: 0,
|
||||
unitPrice: 0,
|
||||
currency: "CNY",
|
||||
supplier: "",
|
||||
status: "planned",
|
||||
planArriveDate: "",
|
||||
actualArriveDate: "",
|
||||
responsibleId: "",
|
||||
responsibleName: "",
|
||||
location: "",
|
||||
tags: []
|
||||
});
|
||||
const resourceEditLoading = ref(false);
|
||||
const isResourceEdit = ref(false); // true=编辑, false=新增
|
||||
|
||||
// 权限控制 - 派生权限(需要放在 isTaskEdit/isMilestoneEdit 定义之后)
|
||||
// 任务编辑权限:新增或编辑任一即可显示按钮
|
||||
const canEditTask = computed(() => canCreateTask.value || canUpdateTask.value);
|
||||
@@ -138,6 +176,16 @@ const canSaveMilestone = computed(() => {
|
||||
? canUpdateMilestone.value
|
||||
: canCreateMilestone.value;
|
||||
});
|
||||
// 资源编辑权限:新增或编辑任一即可显示按钮
|
||||
const canEditResource = computed(
|
||||
() => canCreateResource.value || canUpdateResource.value
|
||||
);
|
||||
// 资源保存权限:新增时需要create权限,编辑时需要update权限
|
||||
const canSaveResource = computed(() => {
|
||||
return isResourceEdit.value
|
||||
? canUpdateResource.value
|
||||
: canCreateResource.value;
|
||||
});
|
||||
|
||||
// 项目基本信息(计算属性)
|
||||
const projectInfo = computed(() => {
|
||||
@@ -736,6 +784,103 @@ async function handleDeleteMilestone(milestoneId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 资源编辑功能 ====================
|
||||
|
||||
/** 打开新增资源对话框 */
|
||||
function openAddResourceModal() {
|
||||
isResourceEdit.value = false;
|
||||
resourceEditForm.value = {
|
||||
id: "",
|
||||
resourceCode: "",
|
||||
resourceType: "material",
|
||||
resourceName: "",
|
||||
description: "",
|
||||
specification: "",
|
||||
unit: "",
|
||||
planQuantity: 0,
|
||||
actualQuantity: 0,
|
||||
unitPrice: 0,
|
||||
currency: "CNY",
|
||||
supplier: "",
|
||||
status: "planned",
|
||||
planArriveDate: "",
|
||||
actualArriveDate: "",
|
||||
responsibleId: "",
|
||||
responsibleName: "",
|
||||
location: "",
|
||||
tags: []
|
||||
};
|
||||
resourceEditModal.value = true;
|
||||
}
|
||||
|
||||
/** 打开编辑资源对话框 */
|
||||
function openEditResourceModal(resource: Resource) {
|
||||
isResourceEdit.value = true;
|
||||
resourceEditForm.value = { ...resource };
|
||||
resourceEditModal.value = true;
|
||||
}
|
||||
|
||||
/** 保存资源 */
|
||||
async function saveResource() {
|
||||
if (!resourceEditForm.value.resourceName) {
|
||||
message("请输入资源名称", { type: "warning" });
|
||||
return;
|
||||
}
|
||||
resourceEditLoading.value = true;
|
||||
try {
|
||||
if (isResourceEdit.value) {
|
||||
const updateData: ResourceUpdateRequest = {
|
||||
id: resourceEditForm.value.id!,
|
||||
resourceType: resourceEditForm.value.resourceType,
|
||||
resourceName: resourceEditForm.value.resourceName,
|
||||
description: resourceEditForm.value.description,
|
||||
specification: resourceEditForm.value.specification,
|
||||
unit: resourceEditForm.value.unit,
|
||||
planQuantity: resourceEditForm.value.planQuantity,
|
||||
actualQuantity: resourceEditForm.value.actualQuantity,
|
||||
unitPrice: resourceEditForm.value.unitPrice,
|
||||
currency: resourceEditForm.value.currency,
|
||||
supplier: resourceEditForm.value.supplier,
|
||||
status: resourceEditForm.value.status,
|
||||
planArriveDate: resourceEditForm.value.planArriveDate,
|
||||
actualArriveDate: resourceEditForm.value.actualArriveDate,
|
||||
responsibleId: resourceEditForm.value.responsibleId,
|
||||
responsibleName: resourceEditForm.value.responsibleName,
|
||||
location: resourceEditForm.value.location,
|
||||
tags: resourceEditForm.value.tags
|
||||
};
|
||||
await updateResource(updateData);
|
||||
message("资源更新成功", { type: "success" });
|
||||
} else {
|
||||
const createData = {
|
||||
...resourceEditForm.value,
|
||||
projectId: projectId.value
|
||||
};
|
||||
await createResource(createData);
|
||||
message("资源创建成功", { type: "success" });
|
||||
}
|
||||
resourceEditModal.value = false;
|
||||
await fetchProjectDetail();
|
||||
} catch (error) {
|
||||
console.error("保存资源失败:", error);
|
||||
message("保存资源失败", { type: "error" });
|
||||
} finally {
|
||||
resourceEditLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除资源 */
|
||||
async function handleDeleteResource(resourceId: string) {
|
||||
try {
|
||||
await deleteResource(resourceId);
|
||||
message("资源删除成功", { type: "success" });
|
||||
await fetchProjectDetail();
|
||||
} catch (error) {
|
||||
console.error("删除资源失败:", error);
|
||||
message("删除资源失败", { type: "error" });
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchProjectDetail();
|
||||
fetchGanttData();
|
||||
@@ -1210,11 +1355,27 @@ onMounted(() => {
|
||||
<div class="flex items-center gap-2">
|
||||
<component :is="useRenderIcon(FileListIcon)" />
|
||||
<span class="font-medium">项目物料清单</span>
|
||||
<el-tag size="small" type="info"
|
||||
>{{ resourceList.length }} 个</el-tag
|
||||
>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<el-button
|
||||
v-if="canCreateResource"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="openAddResourceModal"
|
||||
>
|
||||
<template #icon>
|
||||
<component :is="useRenderIcon('ri/add-line')" />
|
||||
</template>
|
||||
新增资源
|
||||
</el-button>
|
||||
<el-button link type="primary">
|
||||
查看全部
|
||||
<component :is="useRenderIcon(ArrowRightIcon)" />
|
||||
</el-button>
|
||||
</div>
|
||||
<el-button link type="primary">
|
||||
查看全部
|
||||
<component :is="useRenderIcon(ArrowRightIcon)" />
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="resourceList" style="width: 100%">
|
||||
@@ -1263,9 +1424,28 @@ onMounted(() => {
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" fixed="right">
|
||||
<template #default>
|
||||
<el-button link type="primary" size="small">详情</el-button>
|
||||
<el-table-column label="操作" width="130" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="canUpdateResource"
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="openEditResourceModal(row)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-popconfirm
|
||||
v-if="canDeleteResource"
|
||||
title="确定要删除该资源吗?"
|
||||
@confirm="handleDeleteResource(row.id)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button link type="danger" size="small">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -1603,6 +1783,183 @@ onMounted(() => {
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 资源编辑模态框 -->
|
||||
<el-dialog
|
||||
v-model="resourceEditModal"
|
||||
:title="isResourceEdit ? '编辑资源' : '新增资源'"
|
||||
width="650px"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form
|
||||
ref="resourceFormRef"
|
||||
:model="resourceEditForm"
|
||||
label-width="100px"
|
||||
class="resource-edit-form"
|
||||
>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="资源名称" prop="resourceName" required>
|
||||
<el-input
|
||||
v-model="resourceEditForm.resourceName"
|
||||
placeholder="请输入资源名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="资源类型">
|
||||
<el-select
|
||||
v-model="resourceEditForm.resourceType"
|
||||
placeholder="请选择资源类型"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="人力" value="human" />
|
||||
<el-option label="物料" value="material" />
|
||||
<el-option label="设备" value="equipment" />
|
||||
<el-option label="软件" value="software" />
|
||||
<el-option label="资金" value="finance" />
|
||||
<el-option label="其他" value="other" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="规格型号">
|
||||
<el-input
|
||||
v-model="resourceEditForm.specification"
|
||||
placeholder="请输入规格型号"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="计划数量">
|
||||
<el-input-number
|
||||
v-model="resourceEditForm.planQuantity"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
placeholder="数量"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="单位">
|
||||
<el-input
|
||||
v-model="resourceEditForm.unit"
|
||||
placeholder="如:个、台、套"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="单价">
|
||||
<el-input-number
|
||||
v-model="resourceEditForm.unitPrice"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
placeholder="单价"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="币种">
|
||||
<el-select
|
||||
v-model="resourceEditForm.currency"
|
||||
placeholder="请选择币种"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="人民币 (CNY)" value="CNY" />
|
||||
<el-option label="美元 (USD)" value="USD" />
|
||||
<el-option label="欧元 (EUR)" value="EUR" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="供应商">
|
||||
<el-input
|
||||
v-model="resourceEditForm.supplier"
|
||||
placeholder="请输入供应商"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态">
|
||||
<el-select
|
||||
v-model="resourceEditForm.status"
|
||||
placeholder="请选择状态"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="计划中" value="planned" />
|
||||
<el-option label="已申请" value="requested" />
|
||||
<el-option label="已批准" value="approved" />
|
||||
<el-option label="采购中" value="procuring" />
|
||||
<el-option label="已到货" value="arrived" />
|
||||
<el-option label="使用中" value="in_use" />
|
||||
<el-option label="已完成" value="completed" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="计划到位日期">
|
||||
<el-date-picker
|
||||
v-model="resourceEditForm.planArriveDate"
|
||||
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="resourceEditForm.actualArriveDate"
|
||||
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-input
|
||||
v-model="resourceEditForm.responsibleName"
|
||||
placeholder="请输入负责人姓名"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="存放位置">
|
||||
<el-input
|
||||
v-model="resourceEditForm.location"
|
||||
placeholder="请输入存放位置"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="描述">
|
||||
<el-input
|
||||
v-model="resourceEditForm.description"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入资源描述"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="resourceEditModal = false">取消</el-button>
|
||||
<el-button
|
||||
v-if="canSaveResource"
|
||||
type="primary"
|
||||
:loading="resourceEditLoading"
|
||||
@click="saveResource"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user