feat: 完善设计报工与研发工时功能

This commit is contained in:
2026-07-24 14:59:37 +08:00
parent e7554da9c7
commit 62f7632fc6
9 changed files with 1338 additions and 86 deletions

View File

@@ -0,0 +1,122 @@
USE [YL_MESDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE OR ALTER PROCEDURE [dbo].[设计报工_工时校验_查询]
@DID int = NULL,
@项目号 nvarchar(100) = N'',
@物料编码 nvarchar(100) = N'',
@类别 nvarchar(100) = N'',
@操作人 nvarchar(100) = N'',
@开始日期 nvarchar(30) = N'',
@结束日期 nvarchar(30) = N'',
@校验状态 nvarchar(50) = N'未校验',
@当前操作人 nvarchar(100) = N'',
@角色编号 nvarchar(100) = N'',
@PageCurrent int = 1,
@PageSize int = 20,
@PageCount int = NULL OUTPUT,
@ItemCount int = NULL OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET @PageCurrent = CASE WHEN ISNULL(@PageCurrent, 0) < 1 THEN 1 ELSE @PageCurrent END;
SET @PageSize = CASE
WHEN ISNULL(@PageSize, 0) < 1 THEN 20
WHEN @PageSize > 100 THEN 100
ELSE @PageSize
END;
DECLARE @开始 datetime = TRY_CONVERT(datetime, NULLIF(@开始日期, N''));
DECLARE @结束 datetime = TRY_CONVERT(datetime, NULLIF(@结束日期, N''));
DECLARE @当前用户 nvarchar(100) = LTRIM(RTRIM(ISNULL(@当前操作人, N'')));
DECLARE @可查看全部 bit = CASE WHEN EXISTS
(
SELECT 1
FROM [dbo].[登录基础数据_角色信息]
WHERE CONVERT(nvarchar(100), [Roles_number]) = @角色编号
AND ([Roles_Function] LIKE N'%领导%' OR [Roles_Function] LIKE N'%管理员%')
) THEN 1 ELSE 0 END;
CREATE TABLE #FilteredRecords
(
[RID] int NOT NULL PRIMARY KEY
);
INSERT INTO #FilteredRecords ([RID])
SELECT r.[RID]
FROM [dbo].[MES_设计报工_记录] AS r
INNER JOIN [dbo].[MES_设计报工_任务] AS t ON r.[DID] = t.[DID]
WHERE
(@DID IS NULL OR @DID = 0 OR r.[DID] = @DID)
AND (ISNULL(@项目号, N'') = N'' OR t.[项目号] LIKE N'%' + @项目号 + N'%')
AND (ISNULL(@物料编码, N'') = N'' OR t.[物料编码] LIKE N'%' + @物料编码 + N'%')
AND (ISNULL(@类别, N'') = N'' OR t.[类别] LIKE N'%' + @类别 + N'%')
AND (ISNULL(@操作人, N'') = N'' OR r.[操作人] LIKE N'%' + @操作人 + N'%')
AND (@开始 IS NULL OR r.[开始时间] >= @开始)
AND (@结束 IS NULL OR r.[开始时间] < DATEADD(day, 1, @结束))
AND
(
ISNULL(@校验状态, N'') = N''
OR @校验状态 = N'全部'
OR (@校验状态 = N'已校验' AND r.[已校验] = 1)
OR (@校验状态 = N'未校验' AND r.[已校验] = 0)
)
AND
(
@可查看全部 = 1
OR
(
NULLIF(@当前用户, N'') IS NOT NULL
AND
(
LTRIM(RTRIM(ISNULL(t.[创建人], N''))) = @当前用户
OR LTRIM(RTRIM(ISNULL(r.[操作人], N''))) = @当前用户
)
)
);
SELECT @ItemCount = COUNT(1) FROM #FilteredRecords;
SET @PageCount = CASE
WHEN @ItemCount = 0 THEN 0
ELSE CEILING(CONVERT(decimal(18, 2), @ItemCount) / @PageSize)
END;
SELECT
r.[RID],
r.[DID],
t.[项目号],
t.[物料编码],
t.[物料名称],
t.[图号],
t.[类别],
t.[任务描述],
t.[指派对象],
r.[操作人ID],
r.[操作人],
CONVERT(varchar(19), r.[开始时间], 120) AS [开始时间],
CONVERT(varchar(19), r.[结束时间], 120) AS [结束时间],
r.[工时秒数],
CAST(CAST(ISNULL(r.[工时秒数], 0) / 3600.0 AS decimal(18, 2)) AS varchar(30)) AS [工时小时],
r.[报工状态],
r.[已校验],
CASE WHEN r.[已校验] = 1 THEN N'已校验' ELSE N'未校验' END AS [校验状态],
r.[校验人],
CONVERT(varchar(19), r.[校验时间], 120) AS [校验时间],
r.[进度说明],
r.[备注],
t.[单据状态]
FROM #FilteredRecords AS f
INNER JOIN [dbo].[MES_设计报工_记录] AS r ON f.[RID] = r.[RID]
INNER JOIN [dbo].[MES_设计报工_任务] AS t ON r.[DID] = t.[DID]
ORDER BY r.[RID] DESC
OFFSET (@PageCurrent - 1) * @PageSize ROWS
FETCH NEXT @PageSize ROWS ONLY;
END
GO

View File

@@ -0,0 +1,204 @@
USE [YL_MESDB];
GO
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
CREATE OR ALTER PROCEDURE [dbo].[设计报工_任务_查询]
@DID int = NULL,
@项目号 nvarchar(100) = N'',
@物料编码 nvarchar(100) = N'',
@图号 nvarchar(100) = N'',
@类别 nvarchar(100) = N'',
@任务来源 nvarchar(200) = N'',
@关联父件物料编码 nvarchar(100) = N'',
@预计开始时间_Start nvarchar(30) = N'',
@预计开始时间_End nvarchar(30) = N'',
@预计结束时间_Start nvarchar(30) = N'',
@预计结束时间_End nvarchar(30) = N'',
@单据状态 nvarchar(50) = N'',
@当前操作人ID nvarchar(100) = N'',
@当前操作人 nvarchar(100) = N'',
@角色编号 nvarchar(100) = N''
AS
BEGIN
SET NOCOUNT ON;
DECLARE @预计开始日期1 datetime = TRY_CONVERT(datetime, NULLIF(@预计开始时间_Start, N''));
DECLARE @预计开始日期2 datetime = TRY_CONVERT(datetime, NULLIF(@预计开始时间_End, N''));
DECLARE @预计结束日期1 datetime = TRY_CONVERT(datetime, NULLIF(@预计结束时间_Start, N''));
DECLARE @预计结束日期2 datetime = TRY_CONVERT(datetime, NULLIF(@预计结束时间_End, N''));
DECLARE @可查看全部 bit = CASE WHEN EXISTS
(
SELECT 1
FROM [dbo].[登录基础数据_角色信息]
WHERE CONVERT(nvarchar(100), [Roles_number]) = @角色编号
AND ([Roles_Function] LIKE N'%领导%' OR [Roles_Function] LIKE N'%管理员%')
) THEN 1 ELSE 0 END;
SELECT TOP 1000
ISNULL(task_notes.[领导批示], N'') AS [领导批示],
t.[DID],
t.[项目号],
t.[物料编码],
t.[物料名称],
t.[图号],
t.[类别],
t.[任务来源],
t.[任务描述],
t.[关联父件物料编码],
t.[关联父件物料名称],
t.[设计要求],
t.[研发立项号],
t.[研发目的],
t.[对齐可见ID],
t.[对齐可见],
t.[指派对象ID],
t.[指派对象],
CONVERT(varchar(19), t.[预计开始时间], 120) AS [预计开始时间],
CONVERT(varchar(19), t.[预计结束时间], 120) AS [预计结束时间],
t.[单据状态],
CONVERT(varchar(19), t.[创建日期], 120) AS [创建日期],
t.[创建人],
t.[最后编辑人],
CONVERT(varchar(19), t.[最后编辑时间], 120) AS [最后编辑时间],
t.[备注],
ISNULL(r.[报工次数], 0) AS [报工次数],
ISNULL(r.[未结束报工数], 0) AS [未结束报工数],
ISNULL(r.[当前用户未结束报工数], 0) AS [当前用户未结束报工数],
ISNULL(p.[未结束报工人员], N'') AS [未结束报工人员],
CONVERT(varchar(19), r.[最后开始时间], 120) AS [最后开始时间],
CONVERT(varchar(19), r.[最后结束时间], 120) AS [最后结束时间],
ISNULL(progress_notes.[进度说明], N'') AS [进度说明],
ISNULL(task_notes.[异常说明], N'') AS [异常说明],
ISNULL(task_notes.[奇思妙想], N'') AS [奇思妙想]
FROM [dbo].[MES_设计报工_任务] AS t
OUTER APPLY
(
SELECT
COUNT(1) AS [报工次数],
SUM(CASE WHEN rr.[结束时间] IS NULL THEN 1 ELSE 0 END) AS [未结束报工数],
SUM(CASE
WHEN rr.[结束时间] IS NULL
AND
(
(NULLIF(@当前操作人ID, N'') IS NOT NULL AND ISNULL(rr.[操作人ID], N'') = @当前操作人ID)
OR (NULLIF(@当前操作人ID, N'') IS NULL AND NULLIF(@当前操作人, N'') IS NOT NULL AND ISNULL(rr.[操作人], N'') = @当前操作人)
)
THEN 1 ELSE 0 END) AS [当前用户未结束报工数],
MAX(rr.[开始时间]) AS [最后开始时间],
MAX(rr.[结束时间]) AS [最后结束时间]
FROM [dbo].[MES_设计报工_记录] AS rr
WHERE rr.[DID] = t.[DID]
) AS r
OUTER APPLY
(
SELECT STRING_AGG(CONVERT(nvarchar(max), x.[操作人标识]), N'、') AS [未结束报工人员]
FROM
(
SELECT DISTINCT COALESCE(NULLIF(rr2.[操作人], N''), NULLIF(rr2.[操作人ID], N''), N'未填写操作人') AS [操作人标识]
FROM [dbo].[MES_设计报工_记录] AS rr2
WHERE rr2.[DID] = t.[DID]
AND rr2.[结束时间] IS NULL
) AS x
) AS p
OUTER APPLY
(
SELECT STRING_AGG(
CONVERT(nvarchar(max),
CONVERT(nvarchar(19), rr3.[结束时间], 120)
+ N' '
+ COALESCE(NULLIF(LTRIM(RTRIM(rr3.[操作人])), N''), NULLIF(LTRIM(RTRIM(rr3.[操作人ID])), N''), N'未填写编辑人')
+ NCHAR(13) + NCHAR(10)
+ rr3.[进度说明]),
NCHAR(13) + NCHAR(10) + NCHAR(13) + NCHAR(10)
) WITHIN GROUP (ORDER BY rr3.[结束时间] DESC, rr3.[RID] DESC) AS [进度说明]
FROM [dbo].[MES_设计报工_记录] AS rr3
WHERE rr3.[DID] = t.[DID]
AND NULLIF(LTRIM(RTRIM(rr3.[进度说明])), N'') IS NOT NULL
) AS progress_notes
OUTER APPLY
(
SELECT
STRING_AGG(
CASE WHEN n.[说明类型] = N'领导批示'
THEN CONVERT(nvarchar(max),
CONVERT(nvarchar(19), n.[录入时间], 120)
+ N' '
+ COALESCE(NULLIF(LTRIM(RTRIM(n.[操作人])), N''), NULLIF(LTRIM(RTRIM(n.[操作人ID])), N''), N'未填写编辑人')
+ NCHAR(13) + NCHAR(10)
+ n.[说明内容]) END,
NCHAR(13) + NCHAR(10) + NCHAR(13) + NCHAR(10)
) WITHIN GROUP (ORDER BY n.[录入时间] DESC, n.[SID] DESC) AS [领导批示],
STRING_AGG(
CASE WHEN n.[说明类型] = N'异常说明'
THEN CONVERT(nvarchar(max),
CONVERT(nvarchar(19), n.[录入时间], 120)
+ N' '
+ COALESCE(NULLIF(LTRIM(RTRIM(n.[操作人])), N''), NULLIF(LTRIM(RTRIM(n.[操作人ID])), N''), N'未填写编辑人')
+ NCHAR(13) + NCHAR(10)
+ n.[说明内容]) END,
NCHAR(13) + NCHAR(10) + NCHAR(13) + NCHAR(10)
) WITHIN GROUP (ORDER BY n.[录入时间] DESC, n.[SID] DESC) AS [异常说明],
STRING_AGG(
CASE WHEN n.[说明类型] = N'奇思妙想'
THEN CONVERT(nvarchar(max),
CONVERT(nvarchar(19), n.[录入时间], 120)
+ N' '
+ COALESCE(NULLIF(LTRIM(RTRIM(n.[操作人])), N''), NULLIF(LTRIM(RTRIM(n.[操作人ID])), N''), N'未填写编辑人')
+ NCHAR(13) + NCHAR(10)
+ n.[说明内容]) END,
NCHAR(13) + NCHAR(10) + NCHAR(13) + NCHAR(10)
) WITHIN GROUP (ORDER BY n.[录入时间] DESC, n.[SID] DESC) AS [奇思妙想]
FROM [dbo].[MES_设计报工_任务说明] AS n
WHERE n.[DID] = t.[DID]
) AS task_notes
WHERE
(@DID IS NULL OR @DID = 0 OR t.[DID] = @DID)
AND (ISNULL(@项目号, N'') = N'' OR t.[项目号] LIKE N'%' + @项目号 + N'%')
AND (ISNULL(@物料编码, N'') = N'' OR t.[物料编码] LIKE N'%' + @物料编码 + N'%')
AND (ISNULL(@图号, N'') = N'' OR t.[图号] LIKE N'%' + @图号 + N'%')
AND (ISNULL(@类别, N'') = N'' OR t.[类别] LIKE N'%' + @类别 + N'%')
AND (ISNULL(@任务来源, N'') = N'' OR t.[任务来源] LIKE N'%' + @任务来源 + N'%')
AND (ISNULL(@关联父件物料编码, N'') = N'' OR t.[关联父件物料编码] LIKE N'%' + @关联父件物料编码 + N'%')
AND
(
ISNULL(@单据状态, N'') = N''
OR @单据状态 = N'全部'
OR (@单据状态 = N'未完成' AND ISNULL(t.[单据状态], N'') <> N'已完成')
OR (@单据状态 <> N'未完成' AND t.[单据状态] = @单据状态)
)
AND (@预计开始日期1 IS NULL OR t.[预计开始时间] >= @预计开始日期1)
AND (@预计开始日期2 IS NULL OR t.[预计开始时间] < DATEADD(day, 1, @预计开始日期2))
AND (@预计结束日期1 IS NULL OR t.[预计结束时间] >= @预计结束日期1)
AND (@预计结束日期2 IS NULL OR t.[预计结束时间] < DATEADD(day, 1, @预计结束日期2))
AND
(
@可查看全部 = 1
OR (NULLIF(@当前操作人, N'') IS NOT NULL AND ISNULL(t.[创建人], N'') = @当前操作人)
OR
(
NULLIF(@当前操作人ID, N'') IS NOT NULL
AND CHARINDEX(N',' + @当前操作人ID + N',', N',' + ISNULL(t.[对齐可见ID], N'') + N',') > 0
)
OR
(
NULLIF(@当前操作人, N'') IS NOT NULL
AND CHARINDEX(N',' + @当前操作人 + N',', N',' + ISNULL(t.[对齐可见], N'') + N',') > 0
)
OR
(
NULLIF(@当前操作人ID, N'') IS NOT NULL
AND CHARINDEX(N',' + @当前操作人ID + N',', N',' + ISNULL(t.[指派对象ID], N'') + N',') > 0
)
OR
(
NULLIF(@当前操作人, N'') IS NOT NULL
AND CHARINDEX(N',' + @当前操作人 + N',', N',' + ISNULL(t.[指派对象], N'') + N',') > 0
)
)
ORDER BY t.[DID] DESC;
END
GO

View File

@@ -0,0 +1,197 @@
USE [YL_MESDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE OR ALTER PROCEDURE [dbo].[设计报工_研发财务工时_查询]
@开始日期 nvarchar(30) = N'',
@结束日期 nvarchar(30) = N'',
@操作人 nvarchar(100) = N'',
@项目号 nvarchar(100) = N'',
@物料编码 nvarchar(100) = N'',
@类别 nvarchar(100) = N'',
@任务来源 nvarchar(200) = N'',
@研发立项号 nvarchar(100) = N'',
@DID int = NULL,
@当前操作人 nvarchar(100) = N'',
@角色编号 nvarchar(100) = N''
AS
BEGIN
SET NOCOUNT ON;
DECLARE @开始 datetime = TRY_CONVERT(datetime, NULLIF(@开始日期, N''));
DECLARE @结束 datetime = TRY_CONVERT(datetime, NULLIF(@结束日期, N''));
DECLARE @当前用户 nvarchar(100) = LTRIM(RTRIM(ISNULL(@当前操作人, N'')));
DECLARE @可查看全部 bit = CASE WHEN EXISTS
(
SELECT 1
FROM [dbo].[登录基础数据_角色信息]
WHERE CONVERT(nvarchar(100), [Roles_number]) = @角色编号
AND ([Roles_Function] LIKE N'%领导%' OR [Roles_Function] LIKE N'%管理员%')
) THEN 1 ELSE 0 END;
SELECT
r.[RID],
r.[DID],
ISNULL(r.[操作人], N'') AS [操作人],
CONVERT(varchar(10), r.[开始时间], 120) AS [工作日期],
CONVERT(varchar(19), t.[创建日期], 120) AS [创建日期],
ISNULL(t.[类别], N'') AS [类别],
ISNULL(t.[任务来源], N'') AS [任务来源],
ISNULL(t.[任务描述], N'') AS [任务描述],
ISNULL(t.[设计要求], N'') AS [设计要求],
ISNULL(t.[对齐可见], N'') AS [对齐可见],
ISNULL(t.[指派对象], N'') AS [指派对象],
CONVERT(varchar(19), t.[预计开始时间], 120) AS [预计开始时间],
CONVERT(varchar(19), t.[预计结束时间], 120) AS [预计结束时间],
ISNULL(t.[项目号], N'') AS [项目号],
ISNULL(t.[物料编码], N'') AS [物料编码],
ISNULL(t.[物料名称], N'') AS [物料名称],
ISNULL(t.[图号], N'') AS [图号],
ISNULL(t.[关联父件物料编码], N'') AS [关联父件物料编码],
ISNULL(t.[关联父件物料名称], N'') AS [关联父件物料名称],
ISNULL(t.[研发立项号], N'') AS [研发立项号],
ISNULL(t.[研发目的], N'') AS [研发目的],
ISNULL(t.[单据状态], N'') AS [任务状态],
ISNULL(t.[创建人], N'') AS [创建人],
ISNULL(t.[备注], N'') AS [任务备注],
CONVERT(varchar(19), r.[开始时间], 120) AS [开始时间],
CONVERT(varchar(19), r.[结束时间], 120) AS [结束时间],
CAST(1 AS int) AS [报工次数],
CAST(ISNULL(r.[工时秒数], 0) / 3600.0 AS decimal(18, 2)) AS [工时小时],
CASE WHEN r.[已校验] = 1 THEN N'已校验' ELSE N'未校验' END AS [校验状态],
ISNULL(r.[进度说明], N'') AS [进度说明],
ISNULL(r.[备注], N'') AS [报工备注]
FROM [dbo].[MES_设计报工_记录] AS r
INNER JOIN [dbo].[MES_设计报工_任务] AS t ON r.[DID] = t.[DID]
WHERE
r.[开始时间] IS NOT NULL
AND r.[结束时间] IS NOT NULL
AND (@开始 IS NULL OR r.[开始时间] >= @开始)
AND (@结束 IS NULL OR r.[开始时间] < DATEADD(day, 1, @结束))
AND (ISNULL(@操作人, N'') = N'' OR r.[操作人] LIKE N'%' + @操作人 + N'%')
AND (ISNULL(@项目号, N'') = N'' OR t.[项目号] LIKE N'%' + @项目号 + N'%')
AND (ISNULL(@物料编码, N'') = N'' OR t.[物料编码] LIKE N'%' + @物料编码 + N'%')
AND (ISNULL(@类别, N'') = N'' OR t.[类别] LIKE N'%' + @类别 + N'%')
AND (ISNULL(@任务来源, N'') = N'' OR t.[任务来源] LIKE N'%' + @任务来源 + N'%')
AND (ISNULL(@研发立项号, N'') = N'' OR t.[研发立项号] LIKE N'%' + @研发立项号 + N'%')
AND (@DID IS NULL OR @DID = 0 OR r.[DID] = @DID)
AND
(
@可查看全部 = 1
OR
(
NULLIF(@当前用户, N'') IS NOT NULL
AND
(
LTRIM(RTRIM(ISNULL(t.[创建人], N''))) = @当前用户
OR LTRIM(RTRIM(ISNULL(r.[操作人], N''))) = @当前用户
)
)
)
ORDER BY r.[开始时间] DESC, r.[RID] DESC;
END
GO
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRANSACTION;
DECLARE @TargetMenus TABLE
(
[账号代码] int NOT NULL PRIMARY KEY,
[研发管理ID] int NOT NULL
);
INSERT INTO @TargetMenus ([账号代码], [研发管理ID])
SELECT
parent_menu.[账号代码],
MIN(parent_menu.[id]) AS [研发管理ID]
FROM [dbo].[登录基础数据_二级菜单] AS parent_menu WITH (UPDLOCK, HOLDLOCK)
WHERE parent_menu.[pid] = 0
AND parent_menu.[是否启用] = 1
AND
(
parent_menu.[title] = N'研发管理'
OR parent_menu.[path] = '/ResearchManagement'
OR parent_menu.[name] = 'ResearchManagement'
)
GROUP BY parent_menu.[账号代码];
UPDATE menu_row
SET
menu_row.[pid] = target.[研发管理ID],
menu_row.[path] = 'DesignReportTask/FinancialHours/index',
menu_row.[componet] = '/ProductionManagement/DesignReportTask/FinancialHours/index',
menu_row.[redirect] = NULL,
menu_row.[name] = 'DesignReportFinancialHours',
menu_row.[title] = N'研发财务工时',
menu_row.[icon] = 'WhiteCollarBonusesSummary',
menu_row.[模块代码] = menu_row.[id],
menu_row.[是否启用] = 1,
menu_row.[操作时间] = GETDATE()
FROM [dbo].[登录基础数据_二级菜单] AS menu_row
INNER JOIN @TargetMenus AS target ON target.[账号代码] = menu_row.[账号代码]
WHERE
menu_row.[path] = 'DesignReportTask/FinancialHours/index'
OR menu_row.[componet] = '/ProductionManagement/DesignReportTask/FinancialHours/index'
OR menu_row.[name] = 'DesignReportFinancialHours'
OR menu_row.[title] = N'研发财务工时';
INSERT INTO [dbo].[登录基础数据_二级菜单]
([id], [pid], [path], [componet], [redirect], [name], [title], [icon], [账号代码], [模块代码], [是否启用], [操作时间])
SELECT
next_id.[新菜单ID],
target.[研发管理ID],
'DesignReportTask/FinancialHours/index',
'/ProductionManagement/DesignReportTask/FinancialHours/index',
NULL,
'DesignReportFinancialHours',
N'研发财务工时',
'WhiteCollarBonusesSummary',
target.[账号代码],
next_id.[新菜单ID],
1,
GETDATE()
FROM @TargetMenus AS target
CROSS APPLY
(
SELECT ISNULL(MAX(existing.[id]), 0) + 1 AS [新菜单ID]
FROM [dbo].[登录基础数据_二级菜单] AS existing WITH (UPDLOCK, HOLDLOCK)
WHERE existing.[账号代码] = target.[账号代码]
) AS next_id
WHERE NOT EXISTS
(
SELECT 1
FROM [dbo].[登录基础数据_二级菜单] AS existing WITH (UPDLOCK, HOLDLOCK)
WHERE existing.[账号代码] = target.[账号代码]
AND
(
existing.[path] = 'DesignReportTask/FinancialHours/index'
OR existing.[componet] = '/ProductionManagement/DesignReportTask/FinancialHours/index'
OR existing.[name] = 'DesignReportFinancialHours'
OR existing.[title] = N'研发财务工时'
)
);
COMMIT TRANSACTION;
SELECT
menu_row.[id],
menu_row.[pid],
menu_row.[path],
menu_row.[componet],
menu_row.[name],
menu_row.[title],
menu_row.[icon],
menu_row.[账号代码],
menu_row.[是否启用]
FROM [dbo].[登录基础数据_二级菜单] AS menu_row
WHERE menu_row.[title] = N'研发财务工时'
ORDER BY menu_row.[账号代码], menu_row.[id];
GO

View File

@@ -1,7 +1,7 @@
<template>
<div class="app-container design-check-page">
<el-card>
<div class="search-container" @keyup.enter="searchTable">
<div class="search-container" @keyup.enter="queryTable">
<span class="show-text">任务ID:</span>
<el-input v-model="searchForm.did" clearable size="small" style="width: 90px; margin-right: 8px" />
<span class="show-text">项目号:</span>
@@ -54,8 +54,8 @@
<el-option label="已校验" value="已校验" />
<el-option label="全部" value="全部" />
</el-select>
<el-button :loading="loading" icon="el-icon-search" plain size="small" type="primary" @click="searchTable">查询</el-button>
<el-button :disabled="!multipleSelection.length" icon="el-icon-check" plain size="small" type="success" @click="batchValidate" style="width: auto">批量校验</el-button>
<el-button :loading="loading" icon="el-icon-search" plain size="small" type="primary" @click="queryTable">查询</el-button>
<el-button :disabled="!multipleSelection.length" icon="el-icon-check" plain size="small" style="width: auto" type="success" @click="batchValidate">批量校验</el-button>
</div>
<el-table
@@ -105,6 +105,18 @@
</template>
</el-table-column>
</el-table>
<div class="pagination-bar">
<el-pagination
:current-page="pageCurrent"
:page-sizes="[20, 50, 100]"
:page-size="pageSize"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
<el-dialog
@@ -157,6 +169,9 @@ export default {
multipleSelection: [],
projectOptions: [],
materialOptions: [],
pageCurrent: 1,
pageSize: 20,
total: 0,
searchForm: {
did: '',
projectNo: '',
@@ -220,22 +235,44 @@ export default {
['结束日期', this.searchForm.endDate || ''],
['校验状态', this.searchForm.checkStatus || '全部'],
['当前操作人', this.currentUserName()],
['角色编号', this.currentRoleId()]
['角色编号', this.currentRoleId()],
['PageCurrent', this.pageCurrent],
['PageSize', this.pageSize],
['PageCount', '0', 'int', '1'],
['ItemCount', '0', 'int', '1']
]
},
queryTable() {
this.pageCurrent = 1
this.searchTable()
},
async searchTable() {
this.loading = true
const data = this.CreateData('11', '设计报工_工时校验_查询', this.buildSearchParams())
try {
const response = await this.ExecDatabase(data)
this.tableData = response.data || []
const responseData = response.data || {}
this.tableData = Array.isArray(responseData.result) ? responseData.result : []
const output = Array.isArray(responseData.output) ? responseData.output[0] || {} : {}
this.total = Number(output.ItemCount) || 0
} catch (error) {
console.error('设计工时校验查询失败:', error)
this.tableData = []
this.total = 0
this.$message.error('查询失败')
} finally {
this.loading = false
}
},
handleSizeChange(size) {
this.pageCurrent = 1
this.pageSize = size
this.searchTable()
},
handleCurrentChange(page) {
this.pageCurrent = page
this.searchTable()
},
async queryProjects(query) {
const data = this.CreateData('11', '设计报工_项目查询', [['过滤', query || '']])
try {
@@ -312,7 +349,7 @@ export default {
return
}
this.$confirm(`确定批量校验 ${rows.length} 条设计报工记录吗?`, '提示', { type: 'warning' })
.then(async () => {
.then(async() => {
const params = [
['RIDs', rows.map(row => row.RID).join(',')],
['校验人ID', this.currentUserId()],
@@ -374,6 +411,10 @@ export default {
margin-right: 4px;
}
.pagination-bar {
margin-top: 10px;
}
.design-action-buttons {
display: inline-flex;
align-items: center;

View File

@@ -0,0 +1,495 @@
<template>
<div class="app-container financial-hours-page">
<el-card>
<div class="search-container" @keyup.enter="queryReport">
<span class="show-text">开始日期:</span>
<el-date-picker v-model="searchForm.startDate" clearable size="small" style="width: 140px; margin-right: 8px" type="date" value-format="yyyy-MM-dd" />
<span class="show-text">结束日期:</span>
<el-date-picker v-model="searchForm.endDate" clearable size="small" style="width: 140px; margin-right: 8px" type="date" value-format="yyyy-MM-dd" />
<span class="show-text">操作人:</span>
<el-input v-model="searchForm.operator" clearable size="small" style="width: 120px; margin-right: 8px" />
<span class="show-text">项目号:</span>
<el-select
v-model="searchForm.projectNo"
clearable
filterable
remote
:remote-method="queryProjects"
size="small"
style="width: 170px; margin-right: 8px"
@clear="loadDefaultProjectOptions"
@visible-change="handleProjectVisibleChange"
>
<el-option
v-for="item in projectOptions"
:key="item.项目号"
:label="formatProjectLabel(item)"
:value="item.项目号"
/>
</el-select>
<span class="show-text">物料编码:</span>
<el-select
v-model="searchForm.itemCode"
clearable
filterable
remote
:remote-method="queryMaterialOptions"
size="small"
style="width: 190px; margin-right: 8px"
>
<el-option
v-for="item in materialOptions"
:key="item.物料编码"
:label="formatMaterialLabel(item)"
:value="item.物料编码"
/>
</el-select>
<span class="show-text">类别:</span>
<el-input v-model="searchForm.category" clearable size="small" style="width: 110px; margin-right: 8px" />
<span class="show-text">任务来源:</span>
<el-input v-model="searchForm.taskSource" clearable size="small" style="width: 120px; margin-right: 8px" />
<span class="show-text">研发立项号:</span>
<el-input v-model="searchForm.rdProjectNo" clearable size="small" style="width: 130px; margin-right: 8px" />
<span class="show-text">任务ID:</span>
<el-input v-model="searchForm.did" clearable size="small" style="width: 90px; margin-right: 8px" />
<el-button :loading="loading" icon="el-icon-search" plain size="small" type="primary" @click="queryReport">查询</el-button>
<el-button
:disabled="loading || currentTableData.length === 0"
:loading="exporting"
icon="el-icon-download"
plain
size="small"
type="success"
@click="exportToExcel"
>导出</el-button>
</div>
<el-tabs v-model="activeTab" class="report-tabs">
<el-tab-pane label="作业者工时" name="operator">
<el-table
v-loading="loading"
:data="operatorTableData"
border
default-expand-all
:empty-text="emptyText"
height="68vh"
row-key="id"
size="mini"
style="width: 100%"
:tree-props="{ children: 'children' }"
>
<el-table-column fixed label="层级" width="150">
<template slot-scope="scope">
<span :class="{ 'bold-text': scope.row.Level < 3 }">{{ getLevelLabel(scope.row, 'operator') }}</span>
</template>
</el-table-column>
<el-table-column
v-for="column in operatorColumns"
:key="`operator-${column.prop}`"
:align="column.align"
:label="column.label"
:prop="column.prop"
show-overflow-tooltip
:width="column.width"
>
<template slot-scope="scope">
<span :class="{ 'bold-text': scope.row.Level < 3 }">{{ formatCell(scope.row, column.prop) }}</span>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="任务工时" name="task">
<el-table
v-loading="loading"
:data="taskTableData"
border
default-expand-all
:empty-text="emptyText"
height="68vh"
row-key="id"
size="mini"
style="width: 100%"
:tree-props="{ children: 'children' }"
>
<el-table-column fixed label="层级" width="100">
<template slot-scope="scope">
<span :class="{ 'bold-text': scope.row.Level < 3 }">{{ getLevelLabel(scope.row, 'task') }}</span>
</template>
</el-table-column>
<el-table-column
v-for="column in taskColumns"
:key="`task-${column.prop}`"
:align="column.align"
:label="column.label"
:prop="column.prop"
show-overflow-tooltip
:width="column.width"
>
<template slot-scope="scope">
<span :class="{ 'bold-text': scope.row.Level < 3 }">{{ formatCell(scope.row, column.prop) }}</span>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<div class="summary-footer">
<span>总工时:<strong>{{ totalHours }}</strong> 小时</span>
</div>
</el-card>
</div>
</template>
<script>
import * as XLSX from 'xlsx'
export default {
name: 'DesignReportFinancialHours',
data() {
return {
loading: false,
exporting: false,
hasSearched: false,
activeTab: 'operator',
operatorTableData: [],
taskTableData: [],
projectOptions: [],
materialOptions: [],
reportColumns: [
{ prop: '操作人', label: '操作人', width: 110, align: 'left', exportWidth: 16 },
{ prop: '工作日期', label: '工作日期', width: 110, align: 'center', exportWidth: 14 },
{ prop: 'RID', label: 'RID', width: 70, align: 'center', exportWidth: 10 },
{ prop: 'DID', label: 'DID', width: 70, align: 'center', exportWidth: 10 },
{ prop: '创建日期', label: '创建日期', width: 155, align: 'center', exportWidth: 21 },
{ prop: '类别', label: '类别', width: 110, align: 'left', exportWidth: 16 },
{ prop: '任务来源', label: '任务来源', width: 150, align: 'left', exportWidth: 22 },
{ prop: '任务描述', label: '任务描述', width: 260, align: 'left', exportWidth: 36 },
{ prop: '设计要求', label: '设计要求', width: 260, align: 'left', exportWidth: 36 },
{ prop: '对齐可见', label: '对其可见', width: 180, align: 'left', exportWidth: 24 },
{ prop: '指派对象', label: '指派对象', width: 180, align: 'left', exportWidth: 24 },
{ prop: '预计开始时间', label: '预计开始时间', width: 155, align: 'center', exportWidth: 21 },
{ prop: '预计结束时间', label: '预计结束时间', width: 155, align: 'center', exportWidth: 21 },
{ prop: '项目号', label: '项目号', width: 130, align: 'left', exportWidth: 18 },
{ prop: '物料编码', label: '物料编码', width: 180, align: 'left', exportWidth: 24 },
{ prop: '物料名称', label: '物料名称', width: 200, align: 'left', exportWidth: 28 },
{ prop: '图号', label: '图号', width: 140, align: 'left', exportWidth: 20 },
{ prop: '关联父件物料编码', label: '父件物料编码', width: 180, align: 'left', exportWidth: 24 },
{ prop: '关联父件物料名称', label: '父件物料名称', width: 200, align: 'left', exportWidth: 28 },
{ prop: '研发立项号', label: '研发立项号', width: 140, align: 'left', exportWidth: 20 },
{ prop: '研发目的', label: '研发目的', width: 260, align: 'left', exportWidth: 36 },
{ prop: '任务状态', label: '任务状态', width: 100, align: 'center', exportWidth: 14 },
{ prop: '创建人', label: '创建人', width: 110, align: 'left', exportWidth: 16 },
{ prop: '任务备注', label: '任务备注', width: 220, align: 'left', exportWidth: 30 },
{ prop: '开始时间', label: '报工开始时间', width: 155, align: 'center', exportWidth: 21 },
{ prop: '结束时间', label: '报工结束时间', width: 155, align: 'center', exportWidth: 21 },
{ prop: '报工次数', label: '报工次数', width: 90, align: 'center', exportWidth: 12 },
{ prop: '工时小时', label: '工时(h)', width: 90, align: 'right', exportWidth: 12 },
{ prop: '校验状态', label: '校验状态', width: 100, align: 'center', exportWidth: 14 },
{ prop: '进度说明', label: '进度说明', width: 240, align: 'left', exportWidth: 34 },
{ prop: '报工备注', label: '报工备注', width: 220, align: 'left', exportWidth: 30 }
],
searchForm: {
startDate: '',
endDate: '',
operator: '',
projectNo: '',
itemCode: '',
category: '',
taskSource: '',
rdProjectNo: '',
did: ''
}
}
},
computed: {
currentTableData() {
return this.activeTab === 'task' ? this.taskTableData : this.operatorTableData
},
operatorColumns() {
return this.orderColumns(['操作人', '工作日期', 'RID', 'DID'])
},
taskColumns() {
return this.orderColumns(['DID', '操作人', '工作日期', 'RID'])
},
totalHours() {
const total = this.currentTableData.reduce((sum, row) => sum + (Number(row.工时小时) || 0), 0)
return total.toFixed(2)
},
emptyText() {
return this.hasSearched ? '暂无数据' : '请设置查询条件后点击查询'
}
},
created() {
this.loadDefaultProjectOptions()
this.loadDefaultMaterialOptions()
},
methods: {
orderColumns(leadingProps) {
const leadingColumns = leadingProps.map(prop => this.reportColumns.find(column => column.prop === prop))
return leadingColumns.concat(this.reportColumns.filter(column => !leadingProps.includes(column.prop)))
},
loadDefaultProjectOptions() {
this.queryProjects('')
},
loadDefaultMaterialOptions() {
this.queryMaterialOptions('')
},
handleProjectVisibleChange(visible) {
if (visible && this.projectOptions.length === 0) this.loadDefaultProjectOptions()
},
buildSearchParams() {
return [
['开始日期', this.searchForm.startDate || ''],
['结束日期', this.searchForm.endDate || ''],
['操作人', this.searchForm.operator || ''],
['项目号', this.searchForm.projectNo || ''],
['物料编码', this.searchForm.itemCode || ''],
['类别', this.searchForm.category || ''],
['任务来源', this.searchForm.taskSource || ''],
['研发立项号', this.searchForm.rdProjectNo || ''],
['DID', this.searchForm.did || 0],
['当前操作人', this.currentUserName()],
['角色编号', this.currentRoleId()]
]
},
async queryReport() {
this.hasSearched = true
this.loading = true
const data = this.CreateData('11', '设计报工_研发财务工时_查询', this.buildSearchParams())
try {
const response = await this.ExecDatabase(data)
const rows = Array.isArray(response.data) ? response.data.map(row => ({ ...row })) : []
this.operatorTableData = this.buildOperatorTree(rows)
this.taskTableData = this.buildTaskTree(rows)
} catch (error) {
console.error('研发财务工时查询失败:', error)
this.operatorTableData = []
this.taskTableData = []
this.$message.error('查询失败')
} finally {
this.loading = false
}
},
buildOperatorTree(rows) {
const operatorGroups = this.groupBy(rows, row => row.操作人 || '未填写操作人')
return Array.from(operatorGroups.entries()).map(([operator, operatorRows]) => {
const personId = `p-${operator}`
const dateGroups = this.groupBy(operatorRows, row => row.工作日期 || '未填写日期')
const children = Array.from(dateGroups.entries()).map(([workDate, dateRows]) => {
const dateId = `${personId}-d-${workDate}`
return this.createSummaryRow(dateRows, {
Level: 2,
id: dateId,
parentId: personId,
操作人: operator,
工作日期: workDate,
children: dateRows.map(row => ({
...row,
Level: 3,
id: `${dateId}-r-${row.RID}`,
parentId: dateId
}))
})
})
return this.createSummaryRow(operatorRows, {
Level: 1,
id: personId,
parentId: '',
操作人: operator,
children
})
})
},
buildTaskTree(rows) {
const taskGroups = this.groupBy(rows, row => String(row.DID || '未填写任务'))
return Array.from(taskGroups.entries()).map(([did, taskRows]) => {
const taskId = `t-${did}`
const taskMetadata = this.pickTaskMetadata(taskRows[0] || {})
const operatorGroups = this.groupBy(taskRows, row => row.操作人 || '未填写操作人')
const children = Array.from(operatorGroups.entries()).map(([operator, operatorRows]) => {
const operatorId = `${taskId}-o-${operator}`
return this.createSummaryRow(operatorRows, {
...taskMetadata,
Level: 2,
id: operatorId,
parentId: taskId,
DID: did,
操作人: operator,
children: operatorRows.map(row => ({
...row,
Level: 3,
id: `${operatorId}-r-${row.RID}`,
parentId: operatorId
}))
})
})
return this.createSummaryRow(taskRows, {
...taskMetadata,
Level: 1,
id: taskId,
parentId: '',
DID: did,
children
})
})
},
groupBy(rows, keyGetter) {
return rows.reduce((groups, row) => {
const key = keyGetter(row)
if (!groups.has(key)) groups.set(key, [])
groups.get(key).push(row)
return groups
}, new Map())
},
pickTaskMetadata(row) {
const props = [
'DID', '创建日期', '类别', '任务来源', '任务描述', '设计要求', '对齐可见', '指派对象',
'预计开始时间', '预计结束时间', '项目号', '物料编码', '物料名称', '图号',
'关联父件物料编码', '关联父件物料名称', '研发立项号', '研发目的', '任务状态', '创建人', '任务备注'
]
return props.reduce((result, prop) => {
result[prop] = row[prop]
return result
}, {})
},
createSummaryRow(rows, values) {
const startTimes = rows.map(row => row.开始时间).filter(Boolean).sort()
const endTimes = rows.map(row => row.结束时间).filter(Boolean).sort()
const checkedCount = rows.filter(row => row.校验状态 === '已校验').length
return {
...values,
RID: '',
开始时间: startTimes[0] || '',
结束时间: endTimes[endTimes.length - 1] || '',
报工次数: rows.length,
工时小时: rows.reduce((sum, row) => sum + (Number(row.工时小时) || 0), 0),
校验状态: checkedCount === rows.length ? '已校验' : (checkedCount === 0 ? '未校验' : '部分校验'),
进度说明: '',
报工备注: ''
}
},
getLevelLabel(row, tabName) {
if (row.Level === 3) return '报工明细'
if (tabName === 'operator') return row.Level === 1 ? '作业者汇总' : '日期汇总'
return row.Level === 1 ? '任务汇总' : '作业者汇总'
},
formatCell(row, prop) {
if (prop === '工时小时') return this.formatHours(row[prop])
const value = row[prop]
return value === null || value === undefined ? '' : value
},
exportToExcel() {
if (this.currentTableData.length === 0) {
this.$message.warning('没有数据可导出')
return
}
this.exporting = true
try {
const tabName = this.activeTab
const reportName = tabName === 'task' ? '任务工时' : '作业者工时'
const columns = tabName === 'task' ? this.taskColumns : this.operatorColumns
const exportData = this.flattenTree(this.currentTableData).map(row => {
const exportRow = { 层级: this.getLevelLabel(row, tabName) }
columns.forEach(column => {
exportRow[column.label] = this.formatCell(row, column.prop)
})
return exportRow
})
const workbook = XLSX.utils.book_new()
const worksheet = XLSX.utils.json_to_sheet(exportData)
worksheet['!cols'] = [{ wch: 14 }, ...columns.map(column => ({ wch: column.exportWidth || 16 }))]
worksheet['!autofilter'] = { ref: worksheet['!ref'] }
XLSX.utils.book_append_sheet(workbook, worksheet, reportName)
XLSX.writeFile(workbook, `研发财务${reportName}_${this.formatDateForFileName(new Date())}.xlsx`)
this.$message.success('导出成功')
} catch (error) {
console.error('研发财务工时导出失败:', error)
this.$message.error('导出失败')
} finally {
this.exporting = false
}
},
flattenTree(rows) {
return rows.reduce((result, row) => {
result.push(row)
if (row.children && row.children.length > 0) result.push(...this.flattenTree(row.children))
return result
}, [])
},
formatHours(value) {
if (value === null || value === undefined || value === '') return ''
const numberValue = Number(value)
return Number.isNaN(numberValue) ? '' : numberValue.toFixed(2)
},
formatDateForFileName(date) {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
return `${year}${month}${day}_${hours}${minutes}${seconds}`
},
async queryProjects(query) {
const data = this.CreateData('11', '设计报工_项目查询', [['过滤', query || '']])
try {
const response = await this.ExecDatabase(data)
this.projectOptions = response.data || []
} catch (error) {
this.projectOptions = []
}
},
async queryMaterialOptions(query) {
const data = this.CreateData('11', '设计报工_物料查询', [['过滤', query || '']])
try {
const response = await this.ExecDatabase(data)
this.materialOptions = response.data || []
} catch (error) {
this.materialOptions = []
}
},
formatMaterialLabel(item) {
return item.物料名称 ? `${item.物料编码} | ${item.物料名称}` : item.物料编码
},
formatProjectLabel(item) {
return item.项目名称 ? `${item.项目号} | ${item.项目名称}` : item.项目号
},
currentUserName() {
return (this.$store && this.$store.getters && this.$store.getters.name) || ''
},
currentRoleId() {
return (this.$store && this.$store.getters && this.$store.getters.token) || ''
}
}
}
</script>
<style scoped>
.financial-hours-page {
padding: 12px;
}
.search-container {
line-height: 34px;
}
.show-text {
margin-right: 4px;
}
.bold-text {
font-weight: 600;
}
.report-tabs {
margin-top: 10px;
}
.summary-footer {
height: 36px;
line-height: 36px;
text-align: right;
color: #303133;
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<div class="app-container design-hours-report-page">
<el-card>
<div class="search-container" @keyup.enter="searchTable">
<div class="search-container" @keyup.enter="queryReport">
<span class="show-text">开始日期:</span>
<el-date-picker
v-model="searchForm.startDate"
@@ -62,7 +62,7 @@
<el-input v-model="searchForm.category" clearable size="small" style="width: 120px; margin-right: 8px" />
<span class="show-text">任务ID:</span>
<el-input v-model="searchForm.did" clearable size="small" style="width: 90px; margin-right: 8px" />
<el-button :loading="loading" icon="el-icon-search" plain size="small" type="primary" @click="searchTable">查询</el-button>
<el-button :loading="loading" icon="el-icon-search" plain size="small" type="primary" @click="queryReport">查询</el-button>
<el-button
:disabled="loading || currentTableData.length === 0"
:loading="exporting"
@@ -169,6 +169,7 @@ export default {
return {
loading: false,
exporting: false,
hasSearched: false,
activeTab: 'operator',
operatorTableData: [],
taskTableData: [],
@@ -197,7 +198,6 @@ export default {
created() {
this.loadDefaultProjectOptions()
this.loadDefaultMaterialOptions()
this.searchTable()
},
methods: {
loadDefaultProjectOptions() {
@@ -224,6 +224,10 @@ export default {
['角色编号', this.currentRoleId()]
]
},
queryReport() {
this.hasSearched = true
this.searchTable()
},
async searchTable() {
this.loading = true
const isTaskReport = this.activeTab === 'task'
@@ -402,7 +406,7 @@ export default {
return description ? `DID ${row.DID} | ${description}` : `DID ${row.DID}`
},
handleTabClick() {
this.searchTable()
if (this.hasSearched) this.searchTable()
},
formatHours(value) {
if (value === null || value === undefined || value === '') return ''

View File

@@ -81,18 +81,18 @@
v-loading="loading"
class="design-data-table"
:data="tableData"
:cell-class-name="getThreeLineCellClassName"
border
height="74vh"
highlight-current-row
size="mini"
style="width: 100%; margin-top: 12px"
@cell-mouse-enter="showThreeLineCellTooltip"
@cell-mouse-leave="scheduleHideThreeLineCellTooltip"
>
<el-table-column align="left" fixed label="领导批示" prop="领导批示" width="360">
<el-table-column align="left" fixed label="领导批示" prop="领导批示" width="200">
<template slot-scope="scope">
<el-tooltip :disabled="!scope.row.领导批示" effect="dark" placement="top" :open-delay="300">
<div slot="content" class="note-tooltip-content">{{ scope.row.领导批示 }}</div>
<div class="note-history">{{ scope.row.领导批示 }}</div>
</el-tooltip>
<div class="note-history">{{ scope.row.领导批示 }}</div>
</template>
</el-table-column>
<el-table-column align="center" fixed label="DID" prop="DID" width="90" />
@@ -108,26 +108,17 @@
<el-table-column align="center" label="预计结束" prop="预计结束时间" width="180" />
<el-table-column align="left" label="进度说明" prop="进度说明" width="360">
<template slot-scope="scope">
<el-tooltip :disabled="!scope.row.进度说明" effect="dark" placement="top" :open-delay="300">
<div slot="content" class="note-tooltip-content">{{ scope.row.进度说明 }}</div>
<div class="note-history">{{ scope.row.进度说明 }}</div>
</el-tooltip>
<div class="note-history">{{ scope.row.进度说明 }}</div>
</template>
</el-table-column>
<el-table-column align="left" label="异常说明" prop="异常说明" width="360">
<template slot-scope="scope">
<el-tooltip :disabled="!scope.row.异常说明" effect="dark" placement="top" :open-delay="300">
<div slot="content" class="note-tooltip-content">{{ scope.row.异常说明 }}</div>
<div class="note-history">{{ scope.row.异常说明 }}</div>
</el-tooltip>
<div class="note-history">{{ scope.row.异常说明 }}</div>
</template>
</el-table-column>
<el-table-column align="left" label="奇思妙想" prop="奇思妙想" width="360">
<template slot-scope="scope">
<el-tooltip :disabled="!scope.row.奇思妙想" effect="dark" placement="top" :open-delay="300">
<div slot="content" class="note-tooltip-content">{{ scope.row.奇思妙想 }}</div>
<div class="note-history">{{ scope.row.奇思妙想 }}</div>
</el-tooltip>
<div class="note-history">{{ scope.row.奇思妙想 }}</div>
</template>
</el-table-column>
<el-table-column align="left" label="项目号" prop="项目号" width="150" show-overflow-tooltip />
@@ -148,22 +139,38 @@
<el-table-column align="left" label="备注" prop="备注" width="240" show-overflow-tooltip />
<el-table-column align="center" label="最后开始" prop="最后开始时间" width="180" />
<el-table-column align="center" label="最后结束" prop="最后结束时间" width="180" />
<el-table-column align="center" fixed="right" label="操作" width="640">
<el-table-column align="center" fixed="right" label="操作" width="200">
<template slot-scope="scope">
<div class="design-action-buttons">
<el-button type="text" size="mini" :disabled="actionLoading || !canStart(scope.row)" @click="startTask(scope.row)">开始</el-button>
<el-button type="text" size="mini" :disabled="!canEnd(scope.row)" @click="endTask(scope.row)">结束</el-button>
<el-button v-if="canUseLeaderInstruction" type="text" size="mini" @click="openNoteDialog(scope.row, '领导批示')">领导批示</el-button>
<el-button type="text" size="mini" @click="openNoteDialog(scope.row, '异常说明')">异常说明</el-button>
<el-button type="text" size="mini" @click="openNoteDialog(scope.row, '奇思妙想')">奇思妙想</el-button>
<el-button type="text" size="mini" icon="el-icon-upload2" @click="openAttachments(scope.row, 'upload')">上传附件</el-button>
<el-button type="text" size="mini" icon="el-icon-view" @click="openAttachments(scope.row, 'view')">查看附件</el-button>
<el-dropdown class="design-action-more" trigger="click">
<el-button type="text" size="mini">
更多<i class="el-icon-arrow-down el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-warning-outline" @click.native="openNoteDialog(scope.row, '异常说明')">异常说明</el-dropdown-item>
<el-dropdown-item icon="el-icon-s-opportunity" @click.native="openNoteDialog(scope.row, '奇思妙想')">奇思妙想</el-dropdown-item>
<el-dropdown-item icon="el-icon-upload2" @click.native="openAttachments(scope.row, 'upload')">上传附件</el-dropdown-item>
<el-dropdown-item icon="el-icon-view" @click.native="openAttachments(scope.row, 'view')">查看附件</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</template>
</el-table-column>
</el-table>
</el-card>
<div
v-show="tableCellTooltip.visible"
class="table-cell-full-tooltip"
:style="tableCellTooltipStyle"
role="tooltip"
@mouseenter="keepThreeLineCellTooltipOpen"
@mouseleave="hideThreeLineCellTooltip"
>{{ tableCellTooltip.text }}</div>
<el-dialog
:visible.sync="endDialogVisible"
title="结束报工"
@@ -237,12 +244,14 @@
<script>
import TaskAttachments from '../components/TaskAttachments'
import threeLineTableTooltip from '../mixins/threeLineTableTooltip'
const AUTO_REFRESH_INTERVAL = 5 * 60 * 1000
export default {
name: 'DesignReportWork',
components: { TaskAttachments },
mixins: [threeLineTableTooltip],
data() {
return {
loading: false,
@@ -348,7 +357,8 @@ export default {
['预计结束时间_End', ''],
['单据状态', this.searchForm.status || ''],
['当前操作人ID', this.currentUserId()],
['当前操作人', this.currentUserName()]
['当前操作人', this.currentUserName()],
['角色编号', this.currentRoleId()]
]
},
async searchTable(options = {}) {
@@ -503,7 +513,8 @@ export default {
['预计结束时间_End', ''],
['单据状态', ''],
['当前操作人ID', this.currentUserId()],
['当前操作人', this.currentUserName()]
['当前操作人', this.currentUserName()],
['角色编号', this.currentRoleId()]
]
const data = this.CreateData('11', '设计报工_任务_查询', params)
try {
@@ -536,7 +547,8 @@ export default {
['预计结束时间_End', ''],
['单据状态', ''],
['当前操作人ID', this.currentUserId()],
['当前操作人', this.currentUserName()]
['当前操作人', this.currentUserName()],
['角色编号', this.currentRoleId()]
]
const data = this.CreateData('11', '设计报工_任务_查询', params)
try {
@@ -616,13 +628,15 @@ export default {
}
.design-action-buttons {
display: inline-flex;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-auto-rows: 32px;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
gap: 10px;
gap: 8px 1px;
width: 100%;
white-space: nowrap;
height: 72px;
white-space: normal;
}
.design-action-buttons ::v-deep .el-button {
@@ -636,6 +650,14 @@ export default {
margin-left: 0;
}
.design-action-more {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 32px;
}
.design-data-table ::v-deep .el-table__header th {
height: 48px;
padding: 8px 0;
@@ -644,7 +666,8 @@ export default {
.design-data-table ::v-deep .el-table__row,
.design-data-table ::v-deep .el-table__body td {
height: 108px;
height: 108px !important;
max-height: 108px;
}
.design-data-table ::v-deep .el-table__body td {
@@ -656,11 +679,15 @@ export default {
line-height: 24px;
}
.design-data-table ::v-deep .el-table__body .cell,
.design-data-table ::v-deep .el-table__body .cell.el-tooltip {
.design-data-table ::v-deep .el-table__body .three-line-table-cell .cell {
display: -webkit-box !important;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
white-space: normal !important;
overflow: visible;
text-overflow: clip;
height: 72px;
max-height: 72px;
overflow: hidden;
text-overflow: ellipsis;
overflow-wrap: anywhere;
word-break: break-word;
}
@@ -672,20 +699,45 @@ export default {
}
.note-history {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
width: 100%;
height: 72px;
min-height: 72px;
max-height: 72px;
line-height: 24px;
white-space: pre-line;
overflow: visible;
white-space: pre-wrap;
overflow: hidden;
text-overflow: ellipsis;
overflow-wrap: anywhere;
word-break: break-word;
}
.note-tooltip-content {
max-width: 520px;
.table-cell-full-tooltip {
position: fixed;
z-index: 4000;
width: max-content;
max-width: 600px;
min-width: 80px;
max-height: 60vh;
padding: 10px 12px;
color: #fff;
background: #303133;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.18);
pointer-events: auto;
font-size: 14px;
line-height: 20px;
white-space: pre-line;
white-space: pre-wrap;
overflow-y: auto;
overflow-wrap: anywhere;
word-break: break-word;
}
@media (max-width: 624px) {
.table-cell-full-tooltip {
max-width: calc(100vw - 24px);
}
}
</style>

View File

@@ -114,18 +114,18 @@
v-loading="loading"
class="design-data-table"
:data="tableData"
:cell-class-name="getThreeLineCellClassName"
border
height="70vh"
highlight-current-row
size="mini"
style="width: 100%; margin-top: 12px"
@cell-mouse-enter="showThreeLineCellTooltip"
@cell-mouse-leave="scheduleHideThreeLineCellTooltip"
>
<el-table-column align="left" fixed label="领导批示" prop="领导批示" width="360">
<el-table-column align="left" fixed label="领导批示" prop="领导批示" width="200">
<template slot-scope="scope">
<el-tooltip :disabled="!scope.row.领导批示" effect="dark" placement="top" :open-delay="300">
<div slot="content" class="note-tooltip-content">{{ scope.row.领导批示 }}</div>
<div class="note-history">{{ scope.row.领导批示 }}</div>
</el-tooltip>
<div class="note-history">{{ scope.row.领导批示 }}</div>
</template>
</el-table-column>
<el-table-column align="center" fixed label="DID" prop="DID" width="90" />
@@ -145,26 +145,17 @@
<el-table-column align="center" label="预计结束" prop="预计结束时间" width="180" />
<el-table-column align="left" label="进度说明" prop="进度说明" width="360">
<template slot-scope="scope">
<el-tooltip :disabled="!scope.row.进度说明" effect="dark" placement="top" :open-delay="300">
<div slot="content" class="note-tooltip-content">{{ scope.row.进度说明 }}</div>
<div class="note-history">{{ scope.row.进度说明 }}</div>
</el-tooltip>
<div class="note-history">{{ scope.row.进度说明 }}</div>
</template>
</el-table-column>
<el-table-column align="left" label="异常说明" prop="异常说明" width="360">
<template slot-scope="scope">
<el-tooltip :disabled="!scope.row.异常说明" effect="dark" placement="top" :open-delay="300">
<div slot="content" class="note-tooltip-content">{{ scope.row.异常说明 }}</div>
<div class="note-history">{{ scope.row.异常说明 }}</div>
</el-tooltip>
<div class="note-history">{{ scope.row.异常说明 }}</div>
</template>
</el-table-column>
<el-table-column align="left" label="奇思妙想" prop="奇思妙想" width="360">
<template slot-scope="scope">
<el-tooltip :disabled="!scope.row.奇思妙想" effect="dark" placement="top" :open-delay="300">
<div slot="content" class="note-tooltip-content">{{ scope.row.奇思妙想 }}</div>
<div class="note-history">{{ scope.row.奇思妙想 }}</div>
</el-tooltip>
<div class="note-history">{{ scope.row.奇思妙想 }}</div>
</template>
</el-table-column>
<el-table-column align="left" label="项目号" prop="项目号" width="150" show-overflow-tooltip />
@@ -185,21 +176,37 @@
<el-table-column align="center" label="创建日期" prop="创建日期" width="180" />
<el-table-column align="left" label="创建人" prop="创建人" width="140" show-overflow-tooltip />
<el-table-column align="left" label="备注" prop="备注" width="240" show-overflow-tooltip />
<el-table-column align="center" fixed="right" label="操作" width="540">
<el-table-column align="center" fixed="right" label="操作" width="200">
<template slot-scope="scope">
<div class="design-action-buttons">
<el-button type="text" size="mini" @click="openEdit(scope.row)">编辑</el-button>
<el-button type="text" size="mini" :disabled="actionLoading || !canComplete(scope.row)" @click="completeTask(scope.row)">完成</el-button>
<el-button v-if="canUseLeaderInstruction" type="text" size="mini" @click="openNoteDialog(scope.row)">领导批示</el-button>
<el-button type="text" size="mini" icon="el-icon-upload2" @click="openAttachments(scope.row, 'upload')">上传附件</el-button>
<el-button type="text" size="mini" icon="el-icon-view" @click="openAttachments(scope.row, 'view')">查看附件</el-button>
<el-button type="text" size="mini" class="danger-action" @click="deleteTask(scope.row)">删除</el-button>
<el-dropdown class="design-action-more" trigger="click">
<el-button type="text" size="mini">
更多<i class="el-icon-arrow-down el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-upload2" @click.native="openAttachments(scope.row, 'upload')">上传附件</el-dropdown-item>
<el-dropdown-item icon="el-icon-view" @click.native="openAttachments(scope.row, 'view')">查看附件</el-dropdown-item>
<el-dropdown-item icon="el-icon-delete" class="danger-action" @click.native="deleteTask(scope.row)">删除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</template>
</el-table-column>
</el-table>
</el-card>
<div
v-show="tableCellTooltip.visible"
class="table-cell-full-tooltip"
:style="tableCellTooltipStyle"
role="tooltip"
@mouseenter="keepThreeLineCellTooltipOpen"
@mouseleave="hideThreeLineCellTooltip"
>{{ tableCellTooltip.text }}</div>
<el-dialog
:visible.sync="dialogVisible"
:title="isEdit ? '编辑设计任务' : '新建设计任务'"
@@ -426,10 +433,12 @@
<script>
import TaskAttachments from '../components/TaskAttachments'
import threeLineTableTooltip from '../mixins/threeLineTableTooltip'
export default {
name: 'DesignReportTask',
components: { TaskAttachments },
mixins: [threeLineTableTooltip],
data() {
return {
loading: false,
@@ -542,7 +551,10 @@ export default {
['预计开始时间_End', this.searchForm.planStartTo || ''],
['预计结束时间_Start', this.searchForm.planEndFrom || ''],
['预计结束时间_End', this.searchForm.planEndTo || ''],
['单据状态', this.searchForm.status || '']
['单据状态', this.searchForm.status || ''],
['当前操作人ID', this.currentUserId()],
['当前操作人', this.currentUserName()],
['角色编号', this.currentRoleId()]
]
},
async searchTable() {
@@ -777,7 +789,10 @@ export default {
['预计开始时间_End', ''],
['预计结束时间_Start', ''],
['预计结束时间_End', ''],
['单据状态', '']
['单据状态', ''],
['当前操作人ID', this.currentUserId()],
['当前操作人', this.currentUserName()],
['角色编号', this.currentRoleId()]
]
const data = this.CreateData('11', '设计报工_任务_查询', params)
try {
@@ -1003,13 +1018,15 @@ export default {
}
.design-action-buttons {
display: inline-flex;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-auto-rows: 32px;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
gap: 10px;
gap: 8px 10px;
width: 100%;
white-space: nowrap;
height: 72px;
white-space: normal;
}
.design-action-buttons ::v-deep .el-button {
@@ -1023,6 +1040,14 @@ export default {
margin-left: 0;
}
.design-action-more {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 32px;
}
.danger-action {
color: #f56c6c;
}
@@ -1035,7 +1060,8 @@ export default {
.design-data-table ::v-deep .el-table__row,
.design-data-table ::v-deep .el-table__body td {
height: 108px;
height: 108px !important;
max-height: 108px;
}
.design-data-table ::v-deep .el-table__body td {
@@ -1047,11 +1073,15 @@ export default {
line-height: 24px;
}
.design-data-table ::v-deep .el-table__body .cell,
.design-data-table ::v-deep .el-table__body .cell.el-tooltip {
.design-data-table ::v-deep .el-table__body .three-line-table-cell .cell {
display: -webkit-box !important;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
white-space: normal !important;
overflow: visible;
text-overflow: clip;
height: 72px;
max-height: 72px;
overflow: hidden;
text-overflow: ellipsis;
overflow-wrap: anywhere;
word-break: break-word;
}
@@ -1063,20 +1093,45 @@ export default {
}
.note-history {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
width: 100%;
height: 72px;
min-height: 72px;
max-height: 72px;
line-height: 24px;
white-space: pre-line;
overflow: visible;
white-space: pre-wrap;
overflow: hidden;
text-overflow: ellipsis;
overflow-wrap: anywhere;
word-break: break-word;
}
.note-tooltip-content {
max-width: 520px;
.table-cell-full-tooltip {
position: fixed;
z-index: 4000;
width: max-content;
max-width: 600px;
min-width: 80px;
max-height: 60vh;
padding: 10px 12px;
color: #fff;
background: #303133;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.18);
pointer-events: auto;
font-size: 14px;
line-height: 20px;
white-space: pre-line;
white-space: pre-wrap;
overflow-y: auto;
overflow-wrap: anywhere;
word-break: break-word;
}
@media (max-width: 624px) {
.table-cell-full-tooltip {
max-width: calc(100vw - 24px);
}
}
</style>

View File

@@ -0,0 +1,82 @@
export default {
data() {
return {
tableCellTooltip: {
visible: false,
text: '',
left: 0,
top: 0,
placement: 'top'
},
tableCellTooltipRequestId: 0,
tableCellTooltipHideTimer: null
}
},
computed: {
tableCellTooltipStyle() {
const isBottom = this.tableCellTooltip.placement === 'bottom'
return {
left: `${this.tableCellTooltip.left}px`,
top: `${this.tableCellTooltip.top}px`,
transform: isBottom ? 'translate(-50%, 0)' : 'translate(-50%, -100%)'
}
}
},
beforeDestroy() {
this.clearThreeLineCellTooltipHideTimer()
},
methods: {
getThreeLineCellClassName({ column }) {
return column && column.label === '操作' ? 'operation-table-cell' : 'three-line-table-cell'
},
showThreeLineCellTooltip(row, column, cell) {
this.hideThreeLineCellTooltip()
if (!cell || !column || column.label === '操作') return
const requestId = ++this.tableCellTooltipRequestId
this.$nextTick(() => {
if (requestId !== this.tableCellTooltipRequestId) return
const content = cell.querySelector('.note-history') || cell.querySelector('.cell')
if (!content) return
const text = String(content.innerText || content.textContent || '').trim()
const isOverflowing = content.scrollHeight > content.clientHeight + 1
if (!text || !isOverflowing) return
const rect = content.getBoundingClientRect()
const viewportWidth = window.innerWidth || document.documentElement.clientWidth
const tooltipWidth = Math.min(600, viewportWidth - 24)
const halfWidth = tooltipWidth / 2
const left = Math.min(viewportWidth - 12 - halfWidth, Math.max(12 + halfWidth, rect.left + rect.width / 2))
const placement = rect.top >= 160 ? 'top' : 'bottom'
this.tableCellTooltip = {
visible: true,
text,
left,
top: placement === 'top' ? rect.top - 8 : rect.bottom + 8,
placement
}
})
},
scheduleHideThreeLineCellTooltip() {
this.clearThreeLineCellTooltipHideTimer()
this.tableCellTooltipHideTimer = window.setTimeout(() => {
this.hideThreeLineCellTooltip()
}, 150)
},
keepThreeLineCellTooltipOpen() {
this.clearThreeLineCellTooltipHideTimer()
},
clearThreeLineCellTooltipHideTimer() {
if (!this.tableCellTooltipHideTimer) return
window.clearTimeout(this.tableCellTooltipHideTimer)
this.tableCellTooltipHideTimer = null
},
hideThreeLineCellTooltip() {
this.clearThreeLineCellTooltipHideTimer()
this.tableCellTooltipRequestId++
this.tableCellTooltip.visible = false
}
}
}