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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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
}
}
}