增加刀具寿命界面

This commit is contained in:
zhangshun
2026-06-11 17:27:45 +08:00
parent 42dab65fed
commit 2ac9e1f66e
25 changed files with 3395 additions and 590 deletions

View File

@@ -73,7 +73,10 @@ const webpackConfig = merge(baseWebpackConfig, {
}
}
}),
new CssMinimizerPlugin()
new CssMinimizerPlugin({
// TinyMCE 4 静态资源目录下的样式不参与二次压缩,避免老资源和当前压缩器的兼容问题
exclude: /static[\\/]tinymce4\.7\.5[\\/].*\.css$/i
})
],
splitChunks: {
chunks: 'all',

View File

@@ -0,0 +1,45 @@
IF OBJECT_ID(N'dbo.设备管理_刀具寿命_明细_查询', N'P') IS NOT NULL
DROP PROCEDURE dbo.设备管理_刀具寿命_明细_查询
GO
CREATE PROCEDURE dbo.设备管理_刀具寿命_明细_查询
@设备编号 NVARCHAR(100) = N''
AS
BEGIN
SET NOCOUNT ON
SELECT
LTRIM(RTRIM(所在工位号)) AS 设备编号,
LTRIM(RTRIM(所在工位号)) AS 设备名称,
ISNULL(名称, '') AS 刀具名称,
ISNULL(刀具代码, '') AS 刀具代码,
ISNULL(刀具组号, '') AS 刀具组号,
ISNULL(规格, '') AS 规格,
ISNULL(型号, '') AS 型号,
CAST(ISNULL(刀具额定寿命, 0) AS DECIMAL(18, 2)) AS 额定寿命,
CAST(ISNULL(已用寿命, 0) AS DECIMAL(18, 2)) AS 已用寿命,
CAST(ISNULL(预警值1, 0) AS DECIMAL(18, 2)) AS 预警值1,
CAST(ISNULL(刀具额定寿命, 0) - ISNULL(已用寿命, 0) AS DECIMAL(18, 2)) AS 剩余寿命,
CASE
WHEN ISNULL(刀具额定寿命, 0) > 0 AND ISNULL(已用寿命, 0) >= ISNULL(刀具额定寿命, 0) THEN N'报废'
WHEN ISNULL(预警值1, 0) > 0 AND ISNULL(已用寿命, 0) >= ISNULL(预警值1, 0) THEN N'预警'
ELSE N'正常'
END AS 状态,
所属工艺,
采购日期,
装刀到工位日期,
操作时间
FROM dbo.设备管理_刀具设备台账
WHERE ISNULL(LTRIM(RTRIM(@设备编号)), '') = ''
OR LTRIM(RTRIM(所在工位号)) = LTRIM(RTRIM(@设备编号))
ORDER BY
LTRIM(RTRIM(所在工位号)),
CASE
WHEN ISNULL(刀具额定寿命, 0) > 0 AND ISNULL(已用寿命, 0) >= ISNULL(刀具额定寿命, 0) THEN 3
WHEN ISNULL(预警值1, 0) > 0 AND ISNULL(已用寿命, 0) >= ISNULL(预警值1, 0) THEN 2
ELSE 1
END DESC,
ISNULL(名称, ''),
ISNULL(刀具代码, '')
END
GO

View File

@@ -0,0 +1,23 @@
IF OBJECT_ID(N'dbo.设备管理_刀具寿命_机床列表_查询', N'P') IS NOT NULL
DROP PROCEDURE dbo.设备管理_刀具寿命_机床列表_查询
GO
CREATE PROCEDURE dbo.设备管理_刀具寿命_机床列表_查询
AS
BEGIN
SET NOCOUNT ON
/*
当前表“设备管理_刀具设备台账”中:
1. 设备编号为空
2. 所在工位号有实际值,可作为设备/机床维度使用
*/
SELECT DISTINCT
LTRIM(RTRIM(所在工位号)) AS 设备编号,
LTRIM(RTRIM(所在工位号)) AS 设备名称
FROM dbo.设备管理_刀具设备台账
WHERE ISNULL(LTRIM(RTRIM(所在工位号)), '') <> ''
ORDER BY LTRIM(RTRIM(所在工位号))
END
GO

View File

@@ -0,0 +1,46 @@
CREATE OR ALTER PROCEDURE [dbo].[质量数据查询_测量数据发动机在线状态_工位JPH_历史]
@开始时间 nvarchar(50) = null,
@结束时间 nvarchar(50) = null,
@工位_check bit = null,
@工位列表 nvarchar(max) = null,
@订单号_check bit = null,
@订单号 nvarchar(50) = null,
@产品型号代码_check bit = null,
@产品型号代码 nvarchar(50) = null
AS
BEGIN
SET NOCOUNT ON
DECLARE @startTime datetime = null
DECLARE @endTime datetime = null
IF @开始时间 IS NOT NULL AND LTRIM(RTRIM(@开始时间)) <> N''
SET @startTime = CONVERT(datetime, @开始时间 + N' 00:00:00', 120)
IF @结束时间 IS NOT NULL AND LTRIM(RTRIM(@结束时间)) <> N''
SET @endTime = CONVERT(datetime, @结束时间 + N' 23:59:59', 120)
;WITH StationFilter AS (
SELECT LTRIM(RTRIM(value)) AS 工位号
FROM STRING_SPLIT(ISNULL(@工位列表, N''), N',')
WHERE LTRIM(RTRIM(value)) <> N''
)
SELECT
日期 = CONVERT(nvarchar(10), CONVERT(datetime, [离开时间], 120), 23),
小时段 = RIGHT('0' + CAST(DATEPART(HOUR, CONVERT(datetime, [离开时间], 120)) AS nvarchar(2)), 2) + N':00',
产量 = COUNT(1)
FROM [dbo].[测量数据发动机在线状态] m
WHERE m.[ID] <> 0
AND m.[离开时间] IS NOT NULL
AND (@startTime IS NULL OR CONVERT(datetime, m.[离开时间], 120) >= @startTime)
AND (@endTime IS NULL OR CONVERT(datetime, m.[离开时间], 120) <= @endTime)
AND (ISNULL(@订单号_check, 0) = 0 OR m.[订单号] = @订单号)
AND (ISNULL(@产品型号代码_check, 0) = 0 OR m.[机型代码] = @产品型号代码)
AND (ISNULL(@工位_check, 0) = 0 OR EXISTS (
SELECT 1 FROM StationFilter f WHERE f.工位号 = m.[工位号]
))
GROUP BY
CONVERT(nvarchar(10), CONVERT(datetime, m.[离开时间], 120), 23),
DATEPART(HOUR, CONVERT(datetime, m.[离开时间], 120))
ORDER BY 日期, 小时段
END

View File

@@ -0,0 +1,23 @@
CREATE OR ALTER PROCEDURE [dbo].[质量数据查询_测量数据发动机在线状态_工位JPH_历史_导出]
@开始时间 nvarchar(50) = null,
@结束时间 nvarchar(50) = null,
@工位_check bit = null,
@工位列表 nvarchar(max) = null,
@订单号_check bit = null,
@订单号 nvarchar(50) = null,
@产品型号代码_check bit = null,
@产品型号代码 nvarchar(50) = null
AS
BEGIN
SET NOCOUNT ON
EXEC [dbo].[质量数据查询_测量数据发动机在线状态_工位JPH_历史]
@开始时间 = @开始时间,
@结束时间 = @结束时间,
@工位_check = @工位_check,
@工位列表 = @工位列表,
@订单号_check = @订单号_check,
@订单号 = @订单号,
@产品型号代码_check = @产品型号代码_check,
@产品型号代码 = @产品型号代码
END

View File

@@ -0,0 +1,25 @@
CREATE OR ALTER PROCEDURE [dbo].[质量数据查询_测量数据发动机在线状态_工位JPH_实时]
@开始时间 nvarchar(50) = null,
@结束时间 nvarchar(50) = null,
@工位_check bit = null,
@工位列表 nvarchar(max) = null,
@订单号_check bit = null,
@订单号 nvarchar(50) = null,
@产品型号代码_check bit = null,
@产品型号代码 nvarchar(50) = null
AS
BEGIN
SET NOCOUNT ON
DECLARE @today nvarchar(10) = CONVERT(nvarchar(10), GETDATE(), 23)
EXEC [dbo].[质量数据查询_测量数据发动机在线状态_工位JPH_历史]
@开始时间 = @today,
@结束时间 = @today,
@工位_check = @工位_check,
@工位列表 = @工位列表,
@订单号_check = @订单号_check,
@订单号 = @订单号,
@产品型号代码_check = @产品型号代码_check,
@产品型号代码 = @产品型号代码
END

View File

@@ -0,0 +1,71 @@
CREATE OR ALTER PROCEDURE [dbo].[质量数据查询_测量数据发动机在线状态_工位节拍统计]
@开始时间 nvarchar(50) = null,
@结束时间 nvarchar(50) = null,
@工位_check bit = null,
@工位列表 nvarchar(max) = null,
@订单号_check bit = null,
@订单号 nvarchar(50) = null,
@产品型号代码_check bit = null,
@产品型号代码 nvarchar(50) = null
AS
BEGIN
SET NOCOUNT ON
DECLARE @startTime datetime = null
DECLARE @endTime datetime = null
IF @开始时间 IS NOT NULL AND LTRIM(RTRIM(@开始时间)) <> N''
SET @startTime = CONVERT(datetime, @开始时间 + N' 00:00:00', 120)
IF @结束时间 IS NOT NULL AND LTRIM(RTRIM(@结束时间)) <> N''
SET @endTime = CONVERT(datetime, @结束时间 + N' 23:59:59', 120)
;WITH StationFilter AS (
SELECT LTRIM(RTRIM(value)) AS 工位号
FROM STRING_SPLIT(ISNULL(@工位列表, N''), N',')
WHERE LTRIM(RTRIM(value)) <> N''
),
SourceData AS (
SELECT
s.[工位号] AS 工位,
CONVERT(nvarchar(10), CONVERT(datetime, m.[离开时间], 120), 23) AS 日期,
CAST(m.[在线时间] AS decimal(18, 2)) AS 在线时间
FROM [dbo].[测量数据发动机在线状态] m
INNER JOIN [dbo].[MES_计划BOM_工位与名称] s
ON m.[工位号] = s.[工位号]
WHERE m.[ID] <> 0
AND m.[离开时间] IS NOT NULL
AND m.[在线时间] IS NOT NULL
AND ISNUMERIC(CONVERT(nvarchar(50), m.[在线时间])) = 1
AND (@startTime IS NULL OR CONVERT(datetime, m.[离开时间], 120) >= @startTime)
AND (@endTime IS NULL OR CONVERT(datetime, m.[离开时间], 120) <= @endTime)
AND (ISNULL(@订单号_check, 0) = 0 OR m.[订单号] = @订单号)
AND (ISNULL(@产品型号代码_check, 0) = 0 OR m.[机型代码] = @产品型号代码)
AND (ISNULL(@工位_check, 0) = 0 OR EXISTS (
SELECT 1 FROM StationFilter f WHERE f.工位号 = m.[工位号]
))
),
DailyAgg AS (
SELECT
工位,
日期,
CAST(AVG(在线时间) AS decimal(18, 2)) AS 当日平均节拍
FROM SourceData
GROUP BY 工位, 日期
),
TotalAgg AS (
SELECT
工位,
CAST(AVG(在线时间) AS decimal(18, 2)) AS 总平均节拍
FROM SourceData
GROUP BY 工位
)
SELECT
d.工位,
t.总平均节拍,
d.日期,
d.当日平均节拍
FROM DailyAgg d
INNER JOIN TotalAgg t ON d.工位 = t.工位
ORDER BY d.工位, d.日期
END

View File

@@ -0,0 +1,140 @@
CREATE OR ALTER PROCEDURE [dbo].[质量数据查询_测量数据发动机在线状态_工位节拍统计_导出]
@开始时间 nvarchar(50) = null,
@结束时间 nvarchar(50) = null,
@工位_check bit = null,
@工位列表 nvarchar(max) = null,
@订单号_check bit = null,
@订单号 nvarchar(50) = null,
@产品型号代码_check bit = null,
@产品型号代码 nvarchar(50) = null
AS
BEGIN
SET NOCOUNT ON
DECLARE @startTime datetime = null
DECLARE @endTime datetime = null
DECLARE @cols nvarchar(max) = N''
DECLARE @sql nvarchar(max)
IF @开始时间 IS NOT NULL AND LTRIM(RTRIM(@开始时间)) <> N''
SET @startTime = CONVERT(datetime, @开始时间 + N' 00:00:00', 120)
IF @结束时间 IS NOT NULL AND LTRIM(RTRIM(@结束时间)) <> N''
SET @endTime = CONVERT(datetime, @结束时间 + N' 23:59:59', 120)
;WITH StationFilter AS (
SELECT LTRIM(RTRIM(value)) AS 工位号
FROM STRING_SPLIT(ISNULL(@工位列表, N''), N',')
WHERE LTRIM(RTRIM(value)) <> N''
),
SourceData AS (
SELECT
m.[工位号] AS 工位,
CONVERT(nvarchar(10), CONVERT(datetime, m.[离开时间], 120), 23) AS 日期,
CAST(m.[在线时间] AS decimal(18, 2)) AS 在线时间
FROM [dbo].[测量数据发动机在线状态] m
INNER JOIN [dbo].[MES_计划BOM_工位与名称] s
ON m.[工位号] = s.[工位号]
WHERE m.[ID] <> 0
AND m.[离开时间] IS NOT NULL
AND m.[在线时间] IS NOT NULL
AND ISNUMERIC(CONVERT(nvarchar(50), m.[在线时间])) = 1
AND (@startTime IS NULL OR CONVERT(datetime, m.[离开时间], 120) >= @startTime)
AND (@endTime IS NULL OR CONVERT(datetime, m.[离开时间], 120) <= @endTime)
AND (ISNULL(@订单号_check, 0) = 0 OR m.[订单号] = @订单号)
AND (ISNULL(@产品型号代码_check, 0) = 0 OR m.[机型代码] = @产品型号代码)
AND (ISNULL(@工位_check, 0) = 0 OR EXISTS (
SELECT 1 FROM StationFilter f WHERE f.工位号 = m.[工位号]
))
),
DailyAgg AS (
SELECT
工位,
日期,
CAST(AVG(在线时间) AS decimal(18, 2)) AS 当日平均节拍
FROM SourceData
GROUP BY 工位, 日期
)
SELECT @cols = STUFF((
SELECT N',' + QUOTENAME(日期)
FROM (SELECT DISTINCT 日期 FROM DailyAgg) d
ORDER BY 日期
FOR XML PATH(''), TYPE
).value('.', 'nvarchar(max)'), 1, 1, N'')
IF ISNULL(@cols, N'') = N''
BEGIN
SELECT CAST(N'' AS nvarchar(50)) AS 工位, CAST(0 AS decimal(18, 2)) AS 总平均节拍
WHERE 1 = 0
RETURN
END
SET @sql = N'
;WITH StationFilter AS (
SELECT LTRIM(RTRIM(value)) AS 工位号
FROM STRING_SPLIT(ISNULL(@工位列表, N''''), N'','')
WHERE LTRIM(RTRIM(value)) <> N''''
),
SourceData AS (
SELECT
m.[工位号] AS 工位,
CONVERT(nvarchar(10), CONVERT(datetime, m.[离开时间], 120), 23) AS 日期,
CAST(m.[在线时间] AS decimal(18, 2)) AS 在线时间
FROM [dbo].[测量数据发动机在线状态] m
INNER JOIN [dbo].[MES_计划BOM_工位与名称] s
ON m.[工位号] = s.[工位号]
WHERE m.[ID] <> 0
AND m.[离开时间] IS NOT NULL
AND m.[在线时间] IS NOT NULL
AND ISNUMERIC(CONVERT(nvarchar(50), m.[在线时间])) = 1
AND (@startTime IS NULL OR CONVERT(datetime, m.[离开时间], 120) >= @startTime)
AND (@endTime IS NULL OR CONVERT(datetime, m.[离开时间], 120) <= @endTime)
AND (ISNULL(@订单号_check, 0) = 0 OR m.[订单号] = @订单号)
AND (ISNULL(@产品型号代码_check, 0) = 0 OR m.[机型代码] = @产品型号代码)
AND (ISNULL(@工位_check, 0) = 0 OR EXISTS (
SELECT 1 FROM StationFilter f WHERE f.工位号 = m.[工位号]
))
),
DailyAgg AS (
SELECT
工位,
日期,
CAST(AVG(在线时间) AS decimal(18, 2)) AS 当日平均节拍
FROM SourceData
GROUP BY 工位, 日期
),
TotalAgg AS (
SELECT
工位,
CAST(AVG(在线时间) AS decimal(18, 2)) AS 总平均节拍
FROM SourceData
GROUP BY 工位
),
PivotData AS (
SELECT 工位, 日期, 当日平均节拍
FROM DailyAgg
)
SELECT 工位, 总平均节拍, ' + @cols + N'
FROM (
SELECT p.工位, t.总平均节拍, p.日期, p.当日平均节拍
FROM PivotData p
INNER JOIN TotalAgg t ON p.工位 = t.工位
) src
PIVOT (
MAX(当日平均节拍)
FOR 日期 IN (' + @cols + N')
) p
ORDER BY 工位'
EXEC sp_executesql
@sql,
N'@startTime datetime, @endTime datetime, @工位_check bit, @工位列表 nvarchar(max), @订单号_check bit, @订单号 nvarchar(50), @产品型号代码_check bit, @产品型号代码 nvarchar(50)',
@startTime = @startTime,
@endTime = @endTime,
@工位_check = @工位_check,
@工位列表 = @工位列表,
@订单号_check = @订单号_check,
@订单号 = @订单号,
@产品型号代码_check = @产品型号代码_check,
@产品型号代码 = @产品型号代码
END

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1561220647782" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3503" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><defs><style type="text/css"></style></defs><path d="M976.068378 432.323827l-103.740035 0c-18.395807 0-38.215939-14.281091-44.030211-31.73939l-14.146916-33.581723c-8.427254-16.359092-4.682381-40.38511 8.332643-53.396694l73.825776-73.825776c13.011584-13.013305 13.011584-34.305927 0-47.319231l-66.236259-66.234538c-13.013305-13.013305-34.304206-13.013305-47.314071 0l-74.362478 74.357318c-13.009864 13.011584-37.097809 16.885472-53.53431 8.609595l-32.826556-13.507002c-17.506464-5.676656-31.8254-25.374654-31.8254-43.775622L590.210563 46.469453c0-18.395807-15.05174-33.450988-33.452708-33.450988l-93.676871 0c-18.397527 0-33.454428 15.055181-33.454428 33.450988l0 105.443032c0 18.395807-14.318936 38.098965-31.821959 43.775622l-32.829996 13.507002c-16.42962 8.275877-40.522725 4.401989-53.53603-8.609595l-74.357318-74.357318c-13.013305-13.013305-34.304206-13.013305-47.31235 0l-66.241419 66.234538c-13.011584 13.013305-13.011584 34.305927 0 47.319231l73.830937 73.825776c13.011584 13.011584 16.761618 37.037602 8.327483 53.396694l-14.146916 33.581723c-5.814272 17.458299-25.627523 31.73939-44.028491 31.73939L43.773901 432.325548C25.378094 432.323827 10.321193 447.380728 10.321193 465.779976l0 93.673431c0 18.400968 15.056901 33.452708 33.452708 33.452708l102.680392 0c18.400968 0 38.026717 14.348179 43.613923 31.878726l14.248407 34.978524c8.250074 16.450262 4.350383 40.550249-8.657761 53.563553l-72.12794 72.12794c-13.011584 13.011584-13.011584 34.299046 0 47.314071l66.236259 66.241419c13.011584 13.009864 34.304206 13.009864 47.31235 0l71.601559-71.601559c13.011584-13.011584 37.053084-16.783981 53.424217-8.382529l35.726811 14.898643c17.492703 5.721382 31.797877 25.457223 31.797877 43.861632l0 100.979116c0 18.397527 15.055181 33.452708 33.457869 33.452708l93.673431 0c18.400968 0 33.456148-15.055181 33.456148-33.452708l0-100.979116c0-18.404408 14.305174-38.14025 31.797877-43.861632l26.014568-10.852735-50.484397-50.486117c-27.85002 9.437011-57.354872 14.368821-87.620051 14.368821-72.683564 0-141.016745-28.309313-192.411128-79.700255-51.390942-51.396103-79.693375-119.722403-79.693375-192.405967 0-72.681844 28.302432-141.015025 79.693375-192.411128 51.394382-51.390942 119.727563-79.693375 192.411128-79.693375 72.681844 0 141.009864 28.304153 192.404247 79.693375 51.390942 51.396103 79.700255 119.729284 79.700255 192.411128 0 29.587421-4.694423 58.454079-13.725467 85.753635l50.68222 50.67878 10.787367-26.494503c5.590646-17.530547 25.216396-31.878726 43.617363-31.878726l102.680392 0c18.397527 0 33.452708-15.05174 33.452708-33.452708l0-93.673431c0-18.399247-15.055181-33.456148-33.452708-33.456148L976.068378 432.325548zM715.060879 622.278511c-10.691036-10.46569-15.681333-18.935949-9.68472-35.835183 31.517484-88.777745 2.674909-179.905282-74.592985-233.895444-48.748717-34.054778-103.17409-43.877113-163.250316-25.978444 22.457197 26.37409 47.701115 48.34963 71.412337 71.876791 46.52794 46.18734 47.207418 103.322027 2.828007 147.015079-43.010133 42.347856-101.077167 40.951055-145.73353-4.102674-23.43599-23.632092-46.025642-48.096761-69.002338-72.184706-2.760919 1.64623-5.526999 3.29074-8.294799 4.93181-13.271334 72.4737 3.173767 137.263271 57.640425 189.218438 57.981024 55.314716 127.354925 70.941003 203.437602 45.341003 19.436527-6.536756 29.735358-3.087757 42.908641 10.285069 56.718398 57.590539 159.455557 155.187744 217.259401 211.701438 27.942911 27.33396 62.286682 28.221583 89.830507 4.32114 34.204435-29.678592 35.933235-68.924929 3.153125-101.732563C875.840989 776.081494 772.806236 678.812848 715.060879 622.278511L715.060879 622.278511zM881.810079 910.578686c-17.55463 0-31.787555-14.232926-31.787555-31.787555s14.231205-31.787555 31.787555-31.787555 31.782395 14.234646 31.782395 31.787555S899.364709 910.578686 881.810079 910.578686L881.810079 910.578686zM881.810079 910.578686" p-id="3504"></path></svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -7,6 +7,7 @@ export default {
cancel: 'Cancel',
home: 'Home',
logout: 'Logout',
online: 'Online',
language: 'Language',
chinese: 'Chinese',
english: 'English'
@@ -30,6 +31,30 @@ export default {
editFailed: 'Edit failed',
accountPasswordError: 'Incorrect username or password'
},
dashboard: {
workbench: 'MES Workbench',
heroDescription: '{date}, the current workbench includes {moduleCount} top-level modules, {entryCount} quick entries.',
focus: 'Focus',
module: 'Module',
pendingTitle: 'Pending Alerts',
sortHint: 'Sorted by count in descending order',
handleHint: 'Open the related business page to process it',
emptyTitle: 'No pending alerts at the moment',
emptyText: 'New business alerts will appear here automatically when available.',
quickAccess: 'Quick Access',
moduleEntryCount: '{count} quick entries',
metricTopModules: 'Top-Level Modules',
metricTopModulesHint: 'Primary navigation groups',
metricQuickEntries: 'Quick Entries',
metricQuickEntriesHint: 'Direct dashboard shortcuts',
metricPendingMessages: 'Pending Messages',
metricPendingMessagesHint: 'Items requiring timely action',
metricNoticeSources: 'Alert Sources',
metricNoticeSourcesHint: 'Active alert categories',
greetingMorning: 'Good morning',
greetingAfternoon: 'Good afternoon',
greetingEvening: 'Good evening'
},
menuManagement: {
filterPlaceholder: 'Enter keyword to filter',
menuName: 'Menu Name',
@@ -310,13 +335,57 @@ export default {
selectOrder: 'Please select an order!',
confirmCompleteOrder: 'Confirm early completion for order {orderNo}?'
},
cylinderMaintenance: {
keywordLabel: 'Keyword:',
keywordPlaceholder: 'Please enter keyword',
productionDateLabel: 'Production Date',
selectProductionDate: 'Please select production date',
productionStatusLabel: 'Production Status',
selectProductionStatus: 'Please select production status',
onlineTimeLabel: 'Online Time',
startDate: 'Start Date',
endDate: 'End Date',
dateRangeSeparator: '~',
search: 'Search',
importData: 'Import',
importTitle: 'Cylinder Group Import',
importHint: 'The first Excel row must match the `缸体分组号` table fields before import.',
confirmImport: 'Confirm Import',
dragExcelFile: 'Drop the Excel file here or',
browse: 'Browse',
sequence: 'No.',
blankNo: 'Blank No.',
materialNo: 'Material No.',
groupNo: 'Group No.',
productionMonth: 'Production Month',
crankcaseNo: 'Crankcase No.',
bindingTime: 'Binding Time',
qrCode: 'QR Code',
serialNo: 'Serial No.',
statusAll: 'All',
statusCompleted: 'Completed',
statusPending: 'Not Produced',
loadingData: 'Loading data',
noData: 'No data',
noImportData: 'No import data',
queryFailed: 'Query failed',
importPreviewFailed: 'Import preview failed',
importSuccess: 'Import succeeded',
importFailed: 'Import failed. Check the file or field mapping.',
uploadOneFileOnly: 'Only one file can be uploaded',
uploadExcelOnly: 'Only .xlsx, .xls, and .csv files are supported'
},
recipeManagement: {
productModelLabel: 'Product Model:',
productModel: 'Product Model',
productModelLabel: 'Cylinder Model:',
productModel: 'Cylinder Model',
search: 'Search',
loadingData: 'Loading data',
productModelCode: 'Pro. Model Code',
largeModel: 'Large Model',
productModelCode: 'Cylinder Code',
largeModel: 'Model Name',
crankcaseCode: 'Crankcase Code',
crankcaseModel: 'Crankcase Model',
crankcaseModelName: 'Crankcase Model Name',
combinePartNumber: 'Combined Part No.',
add: 'Add',
edit: 'Edit',
partType: 'Part Type',
@@ -630,6 +699,7 @@ export default {
'下线统计': 'Offline Count',
'工位节拍': 'Station Beat',
'工位JPH': 'Station JPH',
'缸体分组号': 'Cylinder Group No.',
'生产计划订单完工': 'Order Complete',
'SPC分析': 'SPC Analysis',
'基本趋势图': 'Trend Chart',

View File

@@ -7,6 +7,7 @@ export default {
cancel: '取消',
home: '主页',
logout: '退出',
online: '在线',
language: '语言',
chinese: '中文',
english: 'English'
@@ -30,6 +31,30 @@ export default {
editFailed: '编辑失败',
accountPasswordError: '用户名或密码不正确'
},
dashboard: {
workbench: 'MES 工作台',
heroDescription: '{date},当前工作台已汇总 {moduleCount} 个一级模块、{entryCount} 个快捷入口。',
focus: '重点关注',
module: '模块',
pendingTitle: '待办提醒',
sortHint: '按数量从高到低展示',
handleHint: '进入对应业务页面处理',
emptyTitle: '当前没有待办提醒',
emptyText: '通知列表为空时,这里会自动显示新的业务提醒。',
quickAccess: '快速进入',
moduleEntryCount: '{count} 个快捷入口',
metricTopModules: '一级模块',
metricTopModulesHint: '系统主导航分组',
metricQuickEntries: '快捷入口',
metricQuickEntriesHint: '首页可直达功能',
metricPendingMessages: '待办消息',
metricPendingMessagesHint: '需要及时处理事项',
metricNoticeSources: '提醒来源',
metricNoticeSourcesHint: '当前激活通知类型',
greetingMorning: '早上好',
greetingAfternoon: '下午好',
greetingEvening: '晚上好'
},
menuManagement: {
filterPlaceholder: '输入关键字进行过滤',
menuName: '菜单名称',
@@ -310,13 +335,57 @@ export default {
selectOrder: '请选择订单!',
confirmCompleteOrder: '确定要提前完工订单:{orderNo}吗?'
},
cylinderMaintenance: {
keywordLabel: '关键字:',
keywordPlaceholder: '请输入关键字',
productionDateLabel: '生产日期',
selectProductionDate: '请选择生产日期',
productionStatusLabel: '生产状态',
selectProductionStatus: '请选择生产状态',
onlineTimeLabel: '上线时间',
startDate: '开始日期',
endDate: '结束日期',
dateRangeSeparator: '~',
search: '查询',
importData: '导入',
importTitle: '缸体分组号导入',
importHint: 'Excel首行需与缸体分组号表字段一致,确认无误后再导入。',
confirmImport: '确认导入',
dragExcelFile: '将Excel文件拖入或',
browse: '浏览',
sequence: '序号',
blankNo: '毛坯号',
materialNo: '物料号',
groupNo: '分组号',
productionMonth: '生产月份',
crankcaseNo: '曲轴箱号',
bindingTime: '绑定时间',
qrCode: '二维码',
serialNo: '流水号',
statusAll: '全部',
statusCompleted: '已完成',
statusPending: '未生产',
loadingData: '数据加载中',
noData: '暂无数据',
noImportData: '暂无可导入数据',
queryFailed: '查询失败',
importPreviewFailed: '导入预览失败',
importSuccess: '导入成功',
importFailed: '导入失败,请检查导入文件或字段映射',
uploadOneFileOnly: '仅支持上传一个文件',
uploadExcelOnly: '仅支持上传 .xlsx、.xls、.csv 格式文件'
},
recipeManagement: {
productModelLabel: '产品型号:',
productModel: '产品型号',
productModelLabel: '缸体型号:',
productModel: '缸体型号',
search: '查询',
loadingData: '数据加载中',
productModelCode: '产品型号代码',
largeModel: '大机型',
productModelCode: '缸体代码',
largeModel: '机型名称',
crankcaseCode: '曲轴箱代码',
crankcaseModel: '曲轴箱型号',
crankcaseModelName: '曲轴箱型号名称',
combinePartNumber: '合箱零件号',
add: '增加',
edit: '编辑',
partType: '零件类型',
@@ -630,6 +699,7 @@ export default {
'下线统计': '下线统计',
'工位节拍': '工位节拍',
'工位JPH': '工位JPH',
'缸体分组号': '缸体分组号',
'生产计划订单完工': '生产计划订单完工',
'SPC分析': 'SPC分析',
'基本趋势图': '基本趋势图',

View File

@@ -5,13 +5,13 @@
min-width: 1360px; // div3固定宽度后新增
min-height: 100%;
transition: margin-left .28s;
margin-left: 220px;
margin-left: 236px;
position: relative;
}
// 侧边栏
.sidebar-container {
transition: width 0.28s;
width: 220px !important;
width: 236px !important;
height: 100%;
position: fixed;
font-size: 0px;
@@ -20,17 +20,36 @@
left: 0;
z-index: 1001;
overflow: hidden;
background: linear-gradient(180deg, #f8fbff 0%, #eef5ff 100%);
border-right: 1px solid rgba(108, 146, 210, 0.18);
box-shadow: 18px 0 40px rgba(15, 43, 92, 0.08);
&::before {
content: '';
position: absolute;
top: -18%;
right: -22%;
width: 180px;
height: 180px;
border-radius: 50%;
background: radial-gradient(circle, rgba(89, 168, 255, 0.22) 0%, rgba(89, 168, 255, 0) 72%);
pointer-events: none;
}
//reset element-ui css
.horizontal-collapse-transition {
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
}
.el-scrollbar__bar.is-vertical{
right: 0px;
right: 4px;
width: 6px;
}
.scrollbar-wrapper {
overflow-x: hidden!important;
.el-scrollbar__view {
height: 100%;
display: flex;
flex-direction: column;
}
}
.is-horizontal {
@@ -42,33 +61,145 @@
overflow: hidden;
}
.svg-icon {
margin-right: 16px;
margin-right: 12px;
font-size: 18px;
color: inherit;
transition: color .2s ease;
}
.el-menu {
border: none;
height: 100%;
background: white!important;
background: transparent !important;
width: 100% !important;
}
.el-scrollbar__thumb {
background-color: rgba(102, 136, 191, 0.36);
border-radius: 999px;
}
.sidebar-menu {
padding: 0 10px 14px;
}
.submenu-title-noDropdown,
.el-submenu > .el-submenu__title,
.el-menu-item {
height: 46px;
line-height: 46px;
margin-bottom: 8px;
padding-left: 18px !important;
border-radius: 14px;
color: #31456a !important;
font-size: 14px;
font-weight: 500;
transition: background-color .22s ease, color .22s ease, box-shadow .22s ease, transform .22s ease;
}
.submenu-title-noDropdown,
.el-submenu > .el-submenu__title {
position: relative;
}
.submenu-title-noDropdown:hover,
.el-submenu > .el-submenu__title:hover,
.el-menu-item:hover {
background: rgba(21, 118, 232, 0.10) !important;
color: #0a67d1 !important;
transform: translateX(2px);
}
.submenu-title-noDropdown:hover .svg-icon,
.el-submenu > .el-submenu__title:hover .svg-icon,
.el-menu-item:hover .svg-icon {
color: #0a67d1 !important;
}
.el-submenu.is-active > .el-submenu__title,
.submenu-title-noDropdown.is-active,
.el-menu-item.is-active {
background: linear-gradient(135deg, #1576e8 0%, #44a0ff 100%) !important;
color: #ffffff !important;
box-shadow: 0 14px 24px rgba(21, 118, 232, 0.26);
}
.el-submenu.is-active > .el-submenu__title .svg-icon,
.submenu-title-noDropdown.is-active .svg-icon,
.el-menu-item.is-active .svg-icon {
color: #ffffff !important;
}
.el-submenu .el-menu {
height: auto !important;
margin: 2px 0 12px;
padding: 8px;
background: rgba(255, 255, 255, 0.68) !important;
border-radius: 16px;
box-shadow: inset 0 0 0 1px rgba(145, 178, 233, 0.18);
backdrop-filter: blur(8px);
}
.el-submenu .el-menu-item {
min-width: 100% !important;
height: 40px;
line-height: 40px;
margin-bottom: 6px;
padding-left: 42px !important;
border-radius: 12px;
font-size: 13px;
color: #48617f !important;
background: transparent !important;
}
.el-submenu .el-menu-item:last-child {
margin-bottom: 0;
}
.el-submenu .el-menu-item:hover {
background: rgba(21, 118, 232, 0.10) !important;
color: #0a67d1 !important;
}
.el-submenu .el-menu-item.is-active {
background: rgba(21, 118, 232, 0.14) !important;
color: #0a67d1 !important;
box-shadow: none;
}
.el-submenu .el-menu-item.is-active::before {
content: '';
position: absolute;
left: 18px;
top: 50%;
width: 8px;
height: 8px;
border-radius: 50%;
background: #1576e8;
transform: translateY(-50%);
}
.el-submenu__icon-arrow {
margin-top: -5px;
right: 18px;
color: #90a3bd;
font-weight: 700;
}
}
.hideSidebar {
.sidebar-container {
width: 36px !important;
width: 72px !important;
}
.main-container {
margin-left: 36px;
margin-left: 72px;
}
.submenu-title-noDropdown {
padding-left: 10px !important;
padding-left: 0 !important;
position: relative;
.el-tooltip {
padding: 0 10px !important;
padding: 0 !important;
}
}
.sidebar-container .sidebar-menu {
padding-left: 8px;
padding-right: 8px;
}
.sidebar-container .el-menu-item,
.sidebar-container .el-submenu > .el-submenu__title {
padding: 0 !important;
text-align: center;
}
.sidebar-container .svg-icon {
margin-right: 0;
}
.el-submenu {
overflow: hidden;
&>.el-submenu__title {
padding-left: 12px !important;
padding-left: 0 !important;
.el-submenu__icon-arrow {
display: none;
}
@@ -90,14 +221,13 @@
}
.sidebar-container .nest-menu .el-submenu>.el-submenu__title,
.sidebar-container .el-submenu .el-menu-item {
min-width: 200px !important;
background-color: white !important;
min-width: 100% !important;
&:hover {
background-color: variables.$menuHover !important;
background-color: rgba(21, 118, 232, 0.10) !important;
}
}
.el-menu--collapse .el-menu .el-submenu {
min-width: 180px !important;
min-width: 220px !important;
}
//适配移动端
@@ -107,12 +237,12 @@
}
.sidebar-container {
transition: transform .28s;
width: 180px !important;
width: 236px !important;
}
&.hideSidebar {
.sidebar-container {
transition-duration: 0.3s;
transform: translate3d(-180px, 0, 0);
transform: translate3d(-236px, 0, 0);
}
}
}

View File

@@ -80,8 +80,8 @@
<el-row class="personnel-shell contentRow">
<el-col :span="6">
<el-card class="box-card departmentCard" style="min-height: 600px; max-height: 650px">
<div slot="header" class="clearfix" style="display: flex; align-items: center;justify-content: space-between;">
<el-card class="box-card departmentCard" style="min-height: 600px; max-height: 700px">
<div slot="header" style="display: flex; align-items: center;justify-content: space-between;width: 100%;padding: 0 10px;">
<span>{{ $t('personnelManagement.departmentMaintenance') }}</span>
<el-button style="float: right; padding: 3px 0" type="text" @click="expandALL()">{{ $t('personnelManagement.expand') }}</el-button>
</div>
@@ -242,7 +242,7 @@
</template>
</el-table-column>
</el-table>
<div class="block" style="margin-top: 10px;">
<div class="block">
<el-pagination
:current-page="PageCurrent"
:page-size="PageSize"
@@ -535,10 +535,11 @@
<el-table-column align="center" :label="$t('personnelManagement.operation')" width="100">
<template slot-scope="scope">
<el-button
icon="el-icon-circle-close-outline"
icon="el-icon-delete"
size="mini"
type="danger"
type="text"
@click="deletePeople(scope.row)"
style="color: red"
>{{ $t('personnelManagement.delete') }}
</el-button>
</template>
@@ -1282,7 +1283,7 @@ export default {
<style scoped>
.personnel-shell {
margin-bottom: 14px;
margin-bottom: 0;
}
.actionCard,

View File

@@ -0,0 +1,655 @@
<template>
<div class="app-container mes-admin-page">
<el-card class="toolbar-card" shadow="never">
<div class="toolbar">
<div class="toolbar-left">
<span class="toolbar-label">设备/工位:</span>
<el-select
v-model="machineCode"
clearable
filterable
placeholder="不选则统计全部设备/工位"
size="small"
style="width: 260px"
@change="handleMachineChange">
<el-option
v-for="item in machineOptions"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
<el-button
icon="el-icon-search"
plain
size="small"
style="margin-left: 10px"
type="primary"
@click="searchData">查询
</el-button>
</div>
<div class="toolbar-right" v-if="selectedMachineName">
<span class="machine-name">{{ selectedMachineName }}</span>
</div>
</div>
</el-card>
<div v-loading="loading" class="content-wrapper">
<el-row :gutter="16" class="summary-row">
<el-col :span="6">
<div class="summary-card total">
<div class="summary-title">刀具总数</div>
<div class="summary-value">{{ summaryInfo.totalCount }}</div>
</div>
</el-col>
<el-col :span="6">
<div class="summary-card normal">
<div class="summary-title">正常刀具</div>
<div class="summary-value">{{ summaryInfo.normalCount }}</div>
<div class="summary-unit">{{ normalRateText }}</div>
</div>
</el-col>
<el-col :span="6">
<div class="summary-card warning">
<div class="summary-title">预警刀具</div>
<div class="summary-value">{{ summaryInfo.warningCount }}</div>
<div class="summary-unit">{{ warningRateText }}</div>
</div>
</el-col>
<el-col :span="6">
<div class="summary-card scrap">
<div class="summary-title">报废刀具</div>
<div class="summary-value">{{ summaryInfo.scrapCount }}</div>
<div class="summary-unit">{{ scrapRateText }}</div>
</div>
</el-col>
</el-row>
<el-row :gutter="16" class="detail-row">
<el-col :span="7" style="padding: 0">
<el-card class="panel-card overview-card" shadow="never">
<div slot="header" class="panel-header">
<div style="height:32px;align-items: center;display: flex;justify-content: space-between;width:100%;color:#243447">
<span>状态概览</span>
<span class="panel-subtitle">按设备统计刀具状态</span>
</div>
</div>
<div class="status-ring-wrapper">
<div class="status-ring" :style="statusRingStyle">
<div class="status-ring-inner">
<div class="status-ring-total">{{ summaryInfo.totalCount }}</div>
<div class="status-ring-label">刀具总数</div>
</div>
</div>
</div>
<div class="bar-group">
<div class="bar-item">
<div class="bar-label">
<span>正常占比</span>
<span>{{ normalRateText }}</span>
</div>
<el-progress
:percentage="normalRate"
:stroke-width="14"
color="#10b981"
:show-text="false"/>
</div>
<div class="bar-item">
<div class="bar-label">
<span>预警占比</span>
<span>{{ warningRateText }}</span>
</div>
<el-progress
:percentage="warningRate"
:stroke-width="14"
color="#f59e0b"
:show-text="false"/>
</div>
<div class="bar-item">
<div class="bar-label">
<span>报废占比</span>
<span>{{ scrapRateText }}</span>
</div>
<el-progress
:percentage="scrapRate"
:stroke-width="14"
color="#ef4444"
:show-text="false"/>
</div>
</div>
</el-card>
</el-col>
<el-col :span="17" style="padding: 0">
<div slot="header" class="panel-header">
<span>刀具明细</span>
<span class="panel-subtitle">该设备下每把刀的寿命与状态</span>
</div>
<el-table
:data="tableData"
:header-cell-style="{ background: '#f3f6fb', color: '#3a4a5d' }"
border
height="520"
size="mini"
style="width: 100%">
<el-table-column align="center" label="序号" type="index" width="60"/>
<el-table-column align="center" label="设备/工位" min-width="120">
<template slot-scope="scope">
{{ scope.row.设备编号 || '-' }}
</template>
</el-table-column>
<el-table-column align="center" label="刀具代码" min-width="120">
<template slot-scope="scope">
{{ scope.row.刀具代码 || '-' }}
</template>
</el-table-column>
<!-- <el-table-column align="center" label="刀具名称" min-width="120">-->
<!-- <template slot-scope="scope">-->
<!-- {{ scope.row.刀具名称 || '-' }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- <el-table-column align="center" label="刀具组号" min-width="110">-->
<!-- <template slot-scope="scope">-->
<!-- {{ scope.row.刀具组号 || '-' }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column align="center" label="额定寿命" min-width="100">
<template slot-scope="scope">
{{ formatNumber(scope.row.额定寿命) }}
</template>
</el-table-column>
<el-table-column align="center" label="已用寿命" min-width="100">
<template slot-scope="scope">
{{ formatNumber(scope.row.已用寿命) }}
</template>
</el-table-column>
<el-table-column align="center" label="预警值1" min-width="100">
<template slot-scope="scope">
{{ formatNumber(scope.row.预警值1) }}
</template>
</el-table-column>
<el-table-column align="center" label="剩余寿命" min-width="100">
<template slot-scope="scope">
{{ formatNumber(getRemainingLife(scope.row)) }}
</template>
</el-table-column>
<el-table-column align="center" label="寿命进度" min-width="150">
<template slot-scope="scope">
<div class="table-progress">
<el-progress
:text-inside="true"
:percentage="calcRate(scope.row.已用寿命, scope.row.额定寿命)"
:color="getRowProgressColor(scope.row)"
:stroke-width="12"/>
</div>
</template>
</el-table-column>
<el-table-column align="center" label="状态" min-width="90">
<template slot-scope="scope">
<el-tag :type="getRowStatus(scope.row).type" size="mini">
{{ getRowStatus(scope.row).text }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
<div v-if="!loading && !tableData.length" class="empty-block">
当前范围没有刀具寿命数据
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
loading: false,
machineCode: '',
selectedMachineName: '',
machineOptions: [],
tableData: [],
summaryInfo: {
totalCount: 0,
normalCount: 0,
warningCount: 0,
scrapCount: 0
}
}
},
computed: {
normalRate() {
return this.calcCountRate(this.summaryInfo.normalCount, this.summaryInfo.totalCount)
},
warningRate() {
return this.calcCountRate(this.summaryInfo.warningCount, this.summaryInfo.totalCount)
},
scrapRate() {
return this.calcCountRate(this.summaryInfo.scrapCount, this.summaryInfo.totalCount)
},
normalRateText() {
return this.normalRate.toFixed(1) + '%'
},
warningRateText() {
return this.warningRate.toFixed(1) + '%'
},
scrapRateText() {
return this.scrapRate.toFixed(1) + '%'
},
statusRingStyle() {
const normalEnd = this.normalRate
const warningEnd = this.normalRate + this.warningRate
return {
background: 'conic-gradient(#10b981 0% ' + normalEnd + '%, #f59e0b ' + normalEnd + '% ' + warningEnd + '%, #ef4444 ' + warningEnd + '% 100%)'
}
}
},
created() {
this.getMachineOptions()
},
methods: {
getMachineOptions() {
this.machineOptions = []
var param = []
var Data = this.CreateData('11', '设备管理_刀具寿命_机床列表_查询', param)
this.ExecDatabase(Data).then(response => {
const list = response.data || []
for (let i = 0; i < list.length; i++) {
const machineCode = list[i].设备编号 || ''
const machineName = list[i].设备名称 || machineCode
if (machineCode) {
this.machineOptions.push({
label: machineName,
value: machineCode,
machineName: machineName
})
}
}
this.selectedMachineName = '全部设备/工位'
this.searchData()
})
},
handleMachineChange(value) {
if (!value) {
this.selectedMachineName = '全部设备/工位'
return
}
const current = this.machineOptions.find(item => item.value === value)
this.selectedMachineName = current ? current.machineName : ''
},
searchData() {
this.loading = true
this.handleMachineChange(this.machineCode)
var param = []
param[0] = ['设备编号', this.machineCode || '']
var Data = this.CreateData('11', '设备管理_刀具寿命_明细_查询', param)
this.ExecDatabase(Data).then(response => {
const list = response.data || []
this.tableData = list
this.buildSummary(list)
this.loading = false
}).catch(() => {
this.loading = false
this.tableData = []
this.resetSummary()
this.$message.error('刀具寿命数据查询失败')
})
},
buildSummary(list) {
let normalCount = 0
let warningCount = 0
let scrapCount = 0
for (let i = 0; i < list.length; i++) {
const status = this.getRowStatus(list[i]).text
if (status === '正常') {
normalCount++
} else if (status === '预警') {
warningCount++
} else if (status === '报废') {
scrapCount++
}
}
this.summaryInfo = {
totalCount: list.length,
normalCount: normalCount,
warningCount: warningCount,
scrapCount: scrapCount
}
},
resetSummary() {
this.summaryInfo = {
totalCount: 0,
normalCount: 0,
warningCount: 0,
scrapCount: 0
}
},
toNumber(value) {
const num = Number(value)
return isNaN(num) ? 0 : num
},
formatNumber(value) {
const num = this.toNumber(value)
if (num % 1 === 0) {
return num
}
return num.toFixed(2)
},
calcRate(used, total) {
const totalNum = this.toNumber(total)
const usedNum = this.toNumber(used)
if (totalNum <= 0) {
return 0
}
let rate = usedNum / totalNum * 100
if (rate < 0) {
rate = 0
}
if (rate > 100) {
rate = 100
}
return Number(rate.toFixed(1))
},
calcCountRate(count, total) {
if (!total) {
return 0
}
return Number((count / total * 100).toFixed(1))
},
getRemainingLife(row) {
const remaining = this.toNumber(row.额定寿命) - this.toNumber(row.已用寿命)
return remaining > 0 ? remaining : 0
},
getRowStatus(row) {
const rated = this.toNumber(row.额定寿命)
const used = this.toNumber(row.已用寿命)
const warning = this.toNumber(row.预警值1)
if (rated > 0 && used >= rated) {
return {
text: '报废',
type: 'danger'
}
}
if (warning > 0 && used >= warning) {
return {
text: '预警',
type: 'warning'
}
}
return {
text: '正常',
type: 'success'
}
},
getRowProgressColor(row) {
const status = this.getRowStatus(row)
if (status.type === 'danger') {
return '#ef4444'
}
if (status.type === 'warning') {
return '#f59e0b'
}
return '#10b981'
}
}
}
</script>
<style scoped>
.tool-life-page {
background: #f5f7fb;
min-height: calc(100vh - 84px);
}
.toolbar-card,
.panel-card {
border: none;
border-radius: 16px;
}
.toolbar {
align-items: center;
display: flex;
justify-content: space-between;
min-height: 42px;
}
.toolbar-left {
align-items: center;
display: flex;
flex-wrap: wrap;
}
.toolbar-label {
color: #4c5a67;
font-size: 14px;
margin-right: 8px;
}
.toolbar-right {
color: #6b7785;
font-size: 13px;
}
.machine-name {
background: linear-gradient(135deg, #eef5ff, #f7fbff);
border: 1px solid #dbeafe;
border-radius: 999px;
color: #1d4ed8;
display: inline-block;
padding: 7px 14px;
}
.content-wrapper {
margin-top: 16px;
}
.summary-row {
margin-bottom: 16px;
}
.summary-card {
border-radius: 18px;
color: #fff;
min-height: 138px;
overflow: hidden;
padding: 22px 20px;
position: relative;
}
.summary-card::after {
background: rgba(255, 255, 255, 0.14);
border-radius: 50%;
content: '';
height: 120px;
position: absolute;
right: -20px;
top: -20px;
width: 120px;
}
.summary-card.total {
background: linear-gradient(135deg, #2563eb, #60a5fa);
}
.summary-card.normal {
background: linear-gradient(135deg, #059669, #34d399);
}
.summary-card.warning {
background: linear-gradient(135deg, #d97706, #fbbf24);
}
.summary-card.scrap {
background: linear-gradient(135deg, #dc2626, #f87171);
}
.summary-title {
font-size: 14px;
opacity: 0.92;
}
.summary-value {
font-size: 36px;
font-weight: 700;
line-height: 1.2;
margin-top: 16px;
}
.summary-unit {
font-size: 13px;
margin-top: 8px;
opacity: 0.88;
}
.detail-row {
margin-bottom: 16px;
}
.panel-header {
align-items: center;
display: flex;
justify-content: space-between;
}
.panel-subtitle {
color: #8a97a6;
font-size: 12px;
}
.overview-card {
min-height: 520px;
}
.status-ring-wrapper {
display: flex;
justify-content: center;
margin-top: 12px;
}
.status-ring {
align-items: center;
border-radius: 50%;
display: flex;
height: 220px;
justify-content: center;
width: 220px;
}
.status-ring-inner {
align-items: center;
background: #fff;
border-radius: 50%;
box-shadow: inset 0 0 30px rgba(37, 99, 235, 0.08);
display: flex;
flex-direction: column;
height: 166px;
justify-content: center;
width: 166px;
}
.status-ring-total {
color: #1f2937;
font-size: 34px;
font-weight: 700;
}
.status-ring-label {
color: #7b8794;
font-size: 13px;
margin-top: 8px;
}
.status-list {
background: #f8fafc;
border-radius: 14px;
margin-top: 22px;
padding: 10px 14px;
}
.status-item {
align-items: center;
display: flex;
min-height: 66px;
}
.status-dot {
border-radius: 50%;
height: 12px;
margin-right: 12px;
width: 12px;
}
.normal-dot {
background: #10b981;
}
.warning-dot {
background: #f59e0b;
}
.scrap-dot {
background: #ef4444;
}
.status-text {
flex: 1;
}
.status-name {
color: #243142;
font-size: 14px;
font-weight: 600;
}
.status-desc {
color: #8090a3;
font-size: 12px;
margin-top: 4px;
}
.status-count {
color: #111827;
font-size: 22px;
font-weight: 700;
}
.bar-group {
margin-top: 22px;
}
.bar-item {
margin-bottom: 18px;
}
.bar-label {
color: #495566;
display: flex;
font-size: 13px;
justify-content: space-between;
margin-bottom: 8px;
}
.table-progress {
padding: 0 10px;
}
.empty-block {
background: #fff;
border: 1px dashed #d6deea;
border-radius: 16px;
color: #8a97a6;
margin-top: 16px;
padding: 28px 16px;
text-align: center;
}
::v-deep .el-card__body {
padding: 18px 20px;
}
</style>

View File

@@ -141,7 +141,7 @@
size="mini">
<!-- <el-table-column :selectable="selectable" type="selection" width="45" align="center"/>-->
<el-table-column type="index" width="60" :label="$t('assemblyQualitydataQuery.sequence')" align="center"/>
<el-table-column align="center" :label="$t('assemblyQualitydataQuery.orderNumber')">
<el-table-column align="center" width="100px" :label="$t('assemblyQualitydataQuery.orderNumber')">
<template slot-scope="scope">
{{ scope.row.订单号 }}
</template>
@@ -157,7 +157,7 @@
</template>
</el-table-column>
<el-table-column align="center" :label="$t('assemblyQualitydataQuery.productModel')">
<el-table-column align="center" width="120px" :label="$t('assemblyQualitydataQuery.productModel')">
<template slot-scope="scope">
{{ scope.row.发动机型号 }}
</template>
@@ -188,7 +188,7 @@
<!-- {{ scope.row.工位名称 }}-->
<!-- </template>-->
<!--</el-table-column>-->
<el-table-column align="center" width="150px" :label="$t('assemblyQualitydataQuery.measuringPosition')">
<el-table-column align="center" width="100px" :label="$t('assemblyQualitydataQuery.measuringPosition')">
<template slot-scope="scope">
{{ scope.row.测量位置 }}
</template>
@@ -198,7 +198,7 @@
{{ scope.row.测量项目 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('assemblyQualitydataQuery.measuringValue')" width="120px">
<el-table-column align="center" :label="$t('assemblyQualitydataQuery.measuringValue')" width="100px">
<template slot-scope="scope">
{{ scope.row.测量值 }}
</template>
@@ -213,16 +213,16 @@
<!-- {{ scope.row.理论值 }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column align="center" :label="$t('assemblyQualitydataQuery.upperLimit')" width="100px">
<template slot-scope="scope">
{{ scope.row.上限值 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('assemblyQualitydataQuery.lowerLimit')" width="100px">
<template slot-scope="scope">
{{ scope.row.下限值 }}
</template>
</el-table-column>
<!-- <el-table-column align="center" :label="$t('assemblyQualitydataQuery.upperLimit')" width="100px">-->
<!-- <template slot-scope="scope">-->
<!-- {{ scope.row.上限值 }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- <el-table-column align="center" :label="$t('assemblyQualitydataQuery.lowerLimit')" width="100px">-->
<!-- <template slot-scope="scope">-->
<!-- {{ scope.row.下限值 }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column align="center" width="150px" :label="$t('assemblyQualitydataQuery.productionDate')">
<template slot-scope="scope">
{{ scope.row.生产日期 }}

View File

@@ -127,7 +127,7 @@
{{ scope.row.工位号 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('inquiryTransitInformation.orderNumber')">
<el-table-column align="center" :label="$t('inquiryTransitInformation.orderNumber')" width="120">
<template slot-scope="scope">
{{ scope.row.订单号 }}
</template>
@@ -137,7 +137,7 @@
{{ scope.row.发动机号 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('inquiryTransitInformation.productModel')">
<el-table-column align="center" :label="$t('inquiryTransitInformation.productModel')" width="120">
<template slot-scope="scope">
{{ scope.row.机型 }}
</template>
@@ -162,17 +162,17 @@
<!-- {{ scope.row.单元号 }}-->
<!-- </template>-->
<!--</el-table-column>-->
<el-table-column align="center" :label="$t('inquiryTransitInformation.arrivalTime')">
<el-table-column align="center" :label="$t('inquiryTransitInformation.arrivalTime')" width="150">
<template slot-scope="scope">
{{ scope.row.到达时间 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('inquiryTransitInformation.departureTime')">
<el-table-column align="center" :label="$t('inquiryTransitInformation.departureTime')" width="150">
<template slot-scope="scope">
{{ scope.row.离开时间 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('inquiryTransitInformation.onlineTimeSeconds')">
<el-table-column align="center" :label="$t('inquiryTransitInformation.onlineTimeSeconds')" width="100">
<template slot-scope="scope">
{{ scope.row.在线时间 }}
</template>

View File

@@ -8,8 +8,8 @@
<!-- <el-button type="distribution" size="small" icon="el-icon-circle-plus" @click="creatOrder('form')">增加</el-button>-->
</div>
<el-table
v-loading="loading"
ref="multipleTable"
v-loading="loading"
:data="tableData"
:header-cell-style="{background:'#eef1f6',color:'#606266'}"
:element-loading-text="$t('recipeManagement.loadingData')"
@@ -18,9 +18,10 @@
class="main"
tooltip-effect="dark"
height="600px"
style="width: 60%;"
style="width: 100%;"
border
size="mini">
size="mini"
>
<el-table-column align="center" :label="$t('recipeManagement.productModelCode')" width="130px">
<template slot-scope="scope">
{{ scope.row.发动机机型标志 }}
@@ -41,21 +42,27 @@
{{ scope.row.标识字符 }}
</template>
</el-table-column>
<!-- <el-table-column align="center" label="特征">-->
<!-- <template slot-scope="scope">-->
<!-- {{ scope.row.壳体编号型号条码 }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- <el-table-column align="center" label="规格(大机型)">-->
<!-- <template slot-scope="scope">-->
<!-- {{ scope.row.箱体名称 }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- <el-table-column align="center" label="螺栓数量">-->
<!-- <template slot-scope="scope">-->
<!-- {{ scope.row.螺栓数量 }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column align="center" :label="$t('recipeManagement.crankcaseCode')">
<template slot-scope="scope">
{{ scope.row.箱体标志 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('recipeManagement.crankcaseModel')">
<template slot-scope="scope">
{{ scope.row.工厂编号2 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('recipeManagement.crankcaseModelName')">
<template slot-scope="scope">
{{ scope.row.流水号后缀 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('recipeManagement.combinePartNumber')">
<template slot-scope="scope">
{{ scope.row.壳体编号型号 }}
</template>
</el-table-column>
<!-- <el-table-column align="center" label="是否禁用">-->
<!-- <template slot-scope="scope">-->
<!-- <span v-if="Number(scope.row.是否禁用) === 1"> 是 </span>-->
@@ -96,7 +103,8 @@
<el-input
v-model="form.equipmentType"
size="small"
:placeholder="$t('recipeManagement.partType')"/>
:placeholder="$t('recipeManagement.partType')"
/>
</el-form-item>
<el-form-item :label="$t('recipeManagement.partTypeSign')" prop="equipmentSign" style="display: inline-block">
<el-input-number
@@ -106,14 +114,16 @@
:step="1"
style="width: 200px"
size="small"
:placeholder="$t('recipeManagement.partTypeSign')"/>
:placeholder="$t('recipeManagement.partTypeSign')"
/>
</el-form-item>
<el-form-item :label="$t('recipeManagement.partTypeName')" prop="itemNo" style="display: inline-block">
<el-input
v-model="form.itemNo"
clearable
size="small"
:placeholder="$t('recipeManagement.partTypeName')"/>
:placeholder="$t('recipeManagement.partTypeName')"
/>
</el-form-item>
<!-- <el-form-item label="螺栓数量" prop="deviceNum" style="display: inline-block">-->
<!-- <el-input-number-->

View File

@@ -0,0 +1,134 @@
<template>
<div>
<input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
<div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
{{ $t('cylinderMaintenance.dragExcelFile') }}
<el-button :loading="loading" style="margin-left: 16px" plain type="success" size="mini" @click="handleUpload">{{ $t('cylinderMaintenance.browse') }}</el-button>
</div>
</div>
</template>
<script>
import * as XLSX from 'xlsx'
export default {
props: {
beforeUpload: Function, // eslint-disable-line
onSuccess: Function// eslint-disable-line
},
data() {
return {
loading: false,
excelData: {
header: null,
results: null
}
}
},
methods: {
generateData({ header, results }) {
this.excelData.header = header
this.excelData.results = results
this.onSuccess && this.onSuccess(this.excelData)
},
handleDrop(e) {
e.stopPropagation()
e.preventDefault()
if (this.loading) return
const files = e.dataTransfer.files
if (files.length !== 1) {
this.$message.error(this.$t('cylinderMaintenance.uploadOneFileOnly'))
return
}
const rawFile = files[0] // only use files[0]
if (!this.isExcel(rawFile)) {
this.$message.error(this.$t('cylinderMaintenance.uploadExcelOnly'))
return false
}
this.upload(rawFile)
e.stopPropagation()
e.preventDefault()
},
handleDragover(e) {
e.stopPropagation()
e.preventDefault()
e.dataTransfer.dropEffect = 'copy'
},
handleUpload() {
this.$refs['excel-upload-input'].click()
},
handleClick(e) {
const files = e.target.files
const rawFile = files[0] // only use files[0]
if (!rawFile) return
this.upload(rawFile)
},
upload(rawFile) {
this.$refs['excel-upload-input'].value = null // fix can't select the same excel
if (!this.beforeUpload) {
this.readerData(rawFile)
return
}
const before = this.beforeUpload(rawFile)
if (before) {
this.readerData(rawFile)
}
},
readerData(rawFile) {
this.loading = true
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = e => {
const data = e.target.result
const workbook = XLSX.read(data, { type: 'array' })
const firstSheetName = workbook.SheetNames[0]
const worksheet = workbook.Sheets[firstSheetName]
const header = this.getHeaderRow(worksheet)
const results = XLSX.utils.sheet_to_json(worksheet)
this.generateData({ header, results })
this.loading = false
resolve()
}
reader.readAsArrayBuffer(rawFile)
})
},
getHeaderRow(sheet) {
const headers = []
const range = XLSX.utils.decode_range(sheet['!ref'])
let C
const R = range.s.r
/* start in the first row */
for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
/* find the cell in the first row */
let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
headers.push(hdr)
}
return headers
},
isExcel(file) {
return /\.(xlsx|xls|csv)$/.test(file.name)
}
}
}
</script>
<style scoped>
.excel-upload-input{
display: none;
z-index: -9999;
}
.drop{
border: 2px dashed #bbb;
width: 600px;
height: 160px;
line-height: 160px;
margin: 0 auto;
font-size: 24px;
border-radius: 5px;
text-align: center;
color: #bbb;
position: relative;
}
</style>

View File

@@ -0,0 +1,402 @@
<template>
<div class="app-container mes-admin-page">
<el-card class="topCard">
<div class="topDiv toolbar">
<span style="margin-left: 10px">{{ $t('cylinderMaintenance.keywordLabel') }}</span>
<el-input
v-model="keyword"
clearable
size="small"
style="width: 240px"
:placeholder="$t('cylinderMaintenance.keywordPlaceholder')"
@keyup.enter.native="handleSearch"
/>
<span style="margin-left: 10px">{{ $t('cylinderMaintenance.productionDateLabel') }}</span>
<el-date-picker
v-model="dateTime2"
align="center"
format="yyyy-MM-dd"
:placeholder="$t('cylinderMaintenance.selectProductionDate')"
size="small"
style="width: 150px;"
type="date"
value-format="yyyy-MM-dd"
/>
<span style="margin-left: 10px">{{ $t('cylinderMaintenance.productionStatusLabel') }}</span>
<el-select
v-model="productStatus"
:placeholder="$t('cylinderMaintenance.selectProductionStatus')"
size="small"
style="width: 150px"
>
<el-option
v-for="item in productStatusList"
:key="item.value"
:label="item.label"
:value="item.value"
size="mini"
/>
</el-select>
<span v-if="productStatus === 1" style="margin-left: 10px">{{ $t('cylinderMaintenance.onlineTimeLabel') }}</span>
<el-date-picker
v-if="productStatus === 1"
v-model="dateTime1"
align="center"
:end-placeholder="$t('cylinderMaintenance.endDate')"
format="yyyy-MM-dd"
:range-separator="$t('cylinderMaintenance.dateRangeSeparator')"
size="small"
:start-placeholder="$t('cylinderMaintenance.startDate')"
style="width: 240px;"
type="daterange"
value-format="yyyy-MM-dd"
/>
<el-button
type="primary"
plain
size="small"
icon="el-icon-search"
style="margin-left: 10px"
@click="handleSearch"
>{{ $t('cylinderMaintenance.search') }}</el-button>
<el-button
type="warning"
size="small"
icon="el-icon-upload2"
@click="openImportDialog"
>{{ $t('cylinderMaintenance.importData') }}</el-button>
</div>
<div class="tablePanel">
<el-table
v-loading="loading"
:data="tableDataAll"
:header-cell-style="{ background: '#eef1f6', color: '#606266' }"
:element-loading-text="$t('cylinderMaintenance.loadingData')"
:empty-text="$t('cylinderMaintenance.noData')"
border
height="640px"
size="mini"
style="width: 100%"
>
<el-table-column
type="index"
width="60"
align="center"
:label="$t('cylinderMaintenance.sequence')"
/>
<el-table-column align="center" :label="$t('cylinderMaintenance.blankNo')">
<template slot-scope="scope">
{{ scope.row.毛坯号 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('cylinderMaintenance.materialNo')" width="100px">
<template slot-scope="scope">
{{ scope.row.物料号 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('cylinderMaintenance.groupNo')" width="80px">
<template slot-scope="scope">
{{ scope.row.分组号 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('cylinderMaintenance.productionMonth')" width="100px">
<template slot-scope="scope">
{{ scope.row.生产月份 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('cylinderMaintenance.crankcaseNo')">
<template slot-scope="scope">
{{ scope.row.曲轴箱号 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('cylinderMaintenance.bindingTime')">
<template slot-scope="scope">
{{ scope.row.绑定时间 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('cylinderMaintenance.qrCode')">
<template slot-scope="scope">
{{ scope.row.二维码 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('cylinderMaintenance.serialNo')" width="100px">
<template slot-scope="scope">
{{ scope.row.流水号 }}
</template>
</el-table-column>
</el-table>
</div>
<div class="block" style="margin-top: 10px">
<el-pagination
:current-page="pageCurrent"
:page-sizes="[30, 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
:visible.sync="dialogFormVisible3"
:title="$t('cylinderMaintenance.importTitle')"
center
style="min-height: 200px"
width="1000px"
>
<div class="app-container">
<upload-excel-component :on-success="handleSuccess" :before-upload="beforeUpload"/>
<el-table
v-loading="loading"
:data="tableDataListAll"
:header-cell-style="{ background: '#f5f5f5', color: '#606266' }"
:element-loading-text="$t('cylinderMaintenance.loadingData')"
:empty-text="$t('cylinderMaintenance.noImportData')"
border
height="320px"
size="mini"
style="width: 100%"
>
<el-table-column align="center" :label="$t('cylinderMaintenance.blankNo')">
<template slot-scope="scope">
{{ scope.row.毛坯号 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('cylinderMaintenance.materialNo')">
<template slot-scope="scope">
{{ scope.row.物料号 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('cylinderMaintenance.groupNo')">
<template slot-scope="scope">
{{ scope.row.分组号 }}
</template>
</el-table-column>
</el-table>
</div>
<div slot="footer" class="dialog-footer">
<el-button size="small" @click="materialAddAllCancel">{{ $t('common.cancel') }}</el-button>
<el-date-picker
v-model="dateTime"
:clearable="false"
align="center"
format="yyyy-MM-dd"
size="small"
style="width: 150px;"
type="date"
value-format="yyyy-MM-dd"
/>
<el-button
type="primary"
icon="el-icon-upload"
size="small"
@click="materialAddAllSubmit"
>{{ $t('cylinderMaintenance.confirmImport') }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import UploadExcelComponent from './UploadExcel.vue'
import { getNowShotTime } from '../../../utils/tool'
export default {
name: 'CylinderMaintenance',
components: { UploadExcelComponent },
data() {
return {
productStatus: 0,
dateTime: '',
dateTime2: '',
dateTime1: [],
keyword: '',
loading: false,
tableDataAll: [],
tableDataListAll: [],
dialogFormVisible3: false,
total: 0,
pageSize: 30,
pageCurrent: 1
}
},
computed: {
productStatusList() {
return [
{
label: this.$t('cylinderMaintenance.statusAll'),
value: 0
},
{
label: this.$t('cylinderMaintenance.statusCompleted'),
value: 1
},
{
label: this.$t('cylinderMaintenance.statusPending'),
value: 2
}
]
}
},
created() {
this.dateTime1 = [getNowShotTime(), getNowShotTime()]
},
mounted() {
this.searchOrder()
},
methods: {
handleSearch() {
this.pageCurrent = 1
this.searchOrder()
},
searchOrder() {
this.loading = true
var param = []
let dateTime1_check = 0
let dateTime1_start = ''
let dateTime1_end = ''
if (!this.dateTime1) {
dateTime1_check = 0
} else {
dateTime1_check = 1
dateTime1_start = this.dateTime1[0]
dateTime1_end = this.dateTime1[1]
}
param[0] = ['生产日期', this.dateTime2]
param[1] = ['关键字', this.keyword]
param[2] = ['生产时间_check', dateTime1_check]
param[3] = ['生产开始时间', dateTime1_start]
param[4] = ['生产结束时间', dateTime1_end]
param[5] = ['生产状态', this.productStatus]
param[6] = ['PageCurrent', this.pageCurrent]
param[7] = ['PageSize', this.pageSize]
param[8] = ['PageCount', '1111', 'int', '1']
param[9] = ['ItemCount', '1111', 'int', '1']
var Data = this.CreateData(
'11',
'缸体分组号_分页查询',
param
)
this.ExecDatabase(Data).then((response) => {
if (response.data.result.length !== 0) {
this.tableDataAll = response.data.result
this.total = parseInt(response.data.output[0].ItemCount)
} else {
this.tableDataAll = []
this.total = 0
}
}).catch(() => {
this.tableDataAll = []
this.total = 0
this.$message.error(this.$t('cylinderMaintenance.queryFailed'))
}).finally(() => {
this.loading = false
})
},
openImportDialog() {
this.clearImportData()
this.dialogFormVisible3 = true
},
beforeUpload(file) {
return true
},
handleSuccess({ results, header }) {
const tableName = `缸体分组号_导入临时表`
var Data = this.CreateData('7', tableName, results, header)
this.ExecDatabase(Data).then(response => {
this.fetchTempTableData()
})
},
fetchTempTableData() {
this.tableDataListAll = []
var param = []
var Data = this.CreateData('11', '缸体分组号_导入临时表_查询', param)
this.ExecDatabase(Data).then(response => {
this.tableDataListAll = response.data
}).catch(() => {
this.tableDataListAll = []
this.$message.error(this.$t('cylinderMaintenance.importPreviewFailed'))
})
},
materialAddAllSubmit() {
if (!this.tableDataListAll.length) {
this.$message.error(this.$t('cylinderMaintenance.noImportData'))
return
}
if (!this.dateTime) {
this.$message.error(this.$t('cylinderMaintenance.selectProductionDate'))
return
}
this.loading = true
const param = []
param[0] = ['生产月份', this.dateTime]
const Data = this.CreateData('12', '缸体分组号_确认导入_新增', param)
this.ExecDatabase(Data).then((response) => {
if (response.data.length > 0 && response.data[0].result === '1') {
this.$message.success(this.$t('cylinderMaintenance.importSuccess'))
this.dialogFormVisible3 = false
this.clearImportData()
this.searchOrder()
} else {
this.$message.error(this.$t('cylinderMaintenance.importFailed'))
}
}).catch(() => {
this.$message.error(this.$t('cylinderMaintenance.importFailed'))
}).finally(() => {
this.loading = false
})
},
materialAddAllCancel() {
this.dialogFormVisible3 = false
this.clearImportData()
},
clearImportData() {
this.tableDataListAll = []
},
handleSizeChange(val) {
this.pageCurrent = 1
this.pageSize = val
},
handleCurrentChange(val) {
this.pageCurrent = val
}
}
}
</script>
<style scoped>
.topCard {
margin: 5px;
}
.topDiv {
margin-bottom: 10px;
}
.toolbar {
align-items: center;
}
.tablePanel {
padding-top: 4px;
border-radius: 14px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.52) 0%, rgba(243, 248, 252, 0.92) 100%);
}
.importHint {
margin: 10px 0;
color: #909399;
font-size: 13px;
}
.el-input {
width: 200px;
}
::v-deep .toolbar .el-button {
margin-bottom: 2px;
}
</style>

View File

@@ -101,7 +101,12 @@
size="mini"
>
<el-table-column align="center" type="index" :label="$t('releaseOfProductionPlan.sequence')" />
<el-table-column align="center" :label="$t('releaseOfProductionPlan.crankcaseAssemblyNo')" width="250">
<el-table-column align="center" label="缸体码">
<template slot-scope="scope">
{{ scope.row.箱体码 }}
</template>
</el-table-column>
<el-table-column align="center" :label="$t('releaseOfProductionPlan.crankcaseAssemblyNo')">
<template slot-scope="scope">
{{ scope.row.发动机号 }}
</template>

View File

@@ -1,48 +1,140 @@
<template>
<el-card style="min-height:calc(100vh - 84px);background: white;background-size:100% 100%">
<div class="dashboard-container">
<div class="sudoku_row">
<svg-icon icon-class="人员" style="width: 35px;height: 35px"/>
<div class="dashboard-text" style="color:black">/{{ name }}</div>
<el-divider content-position="right" style="margin:24px"/>
<div class="dashboard-page">
<el-card class="dashboard-card">
<div class="dashboard-container">
<section class="hero-panel">
<div class="hero-copy">
<div class="hero-badge">
<svg-icon icon-class="人员" class="hero-badge-icon"/>
<span>{{ $t('dashboard.workbench') }}</span>
</div>
<h1 class="hero-title">{{ greeting }},{{ name }}</h1>
<p class="hero-description">
{{ $t('dashboard.heroDescription', {
date: todayLabel,
moduleCount: fathers.length,
entryCount: sons.length,
noticeCount: noticeTotal
}) }}
</p>
</div>
<div class="hero-summary">
<div v-for="metric in heroMetrics" :key="metric.label" class="summary-pill">
<span class="summary-value">{{ metric.value }}</span>
<span class="summary-label">{{ metric.label }}</span>
</div>
</div>
</section>
<div v-for="(father, index1) in fathers" :key="index1" style="width:100%;background: white;margin-bottom: 15px">
<div style="width: 100%;height: 40px;line-height: 40px;background: rgb(127,172,237);color:white">
<svg-icon :icon-class="father.icon" style="color: white;margin: 5px 5px 0px 10px;border: 0 solid black;width:20px;height:20px"/>
{{ translateMenuTitle(father.title) }}
<section class="stats-grid">
<div
v-for="(metric, index) in heroMetrics"
:key="metric.label"
class="stat-card"
:style="getGradientStyle(index)"
>
<div class="stat-card__label">{{ metric.label }}</div>
<div class="stat-card__value">{{ metric.value }}</div>
<div class="stat-card__hint">{{ metric.hint }}</div>
</div>
</section>
<section class="notice-panel">
<div class="section-heading">
<div>
<div class="section-eyebrow">{{ $t('dashboard.focus') }}</div>
<h2 class="section-title">{{ $t('dashboard.pendingTitle') }}</h2>
</div>
<div class="section-meta">{{ $t('dashboard.sortHint') }}</div>
</div>
<!-- ✅ 使用计算属性后的 sonsMap,不再有 v-if -->
<div v-if="sudokus.length" class="notice-grid">
<router-link
v-for="(son, index2) in getSonsByPid(father.id)"
:key="index2"
:to="son.path"
v-for="(item, index) in sudokus"
:key="item.id || index"
:to="item.router"
class="notice-card"
>
<el-card shadow="hover" class="sudoku_item1" style="height:130px;width:175px;margin: 5px;display: inline-block;background: rgba(255,255,255,0.4);border: 2px solid rgb(208,222,234)">
<svg-icon :icon-class="son.icon" class="bgc" style="color: rgb(37,89,164);display:block;margin: auto;border: 0px solid black;width:40px;height:40px"/>
<h4 style="margin-top: 10px;color:rgb(37,89,164);text-align:center">{{ translateMenuTitle(son.title) }}</h4>
</el-card>
<div class="notice-card__icon" :style="getSoftStyle(index)">
<svg-icon :icon-class="item.icon || '看板_board'"/>
</div>
<div class="notice-card__body">
<div class="notice-card__title">{{ translateMenuTitle(item.name) }}</div>
<div class="notice-card__subtitle">{{ $t('dashboard.handleHint') }}</div>
</div>
<div class="notice-card__count">{{ item.value }}</div>
</router-link>
</div>
<div v-else class="empty-panel">
<svg-icon icon-class="看板_board" class="empty-panel__icon"/>
<div class="empty-panel__title">{{ $t('dashboard.emptyTitle') }}</div>
<div class="empty-panel__text">{{ $t('dashboard.emptyText') }}</div>
</div>
</section>
<section
v-for="(section, sectionIndex) in moduleSections"
:key="section.id"
class="module-panel"
>
<div class="section-heading">
<div class="module-title-wrap">
<div class="module-icon" :style="getSoftStyle(sectionIndex)">
<svg-icon :icon-class="section.icon || '模块化'"/>
</div>
<div>
<div class="section-eyebrow">{{ $t('dashboard.module') }}</div>
<h2 class="section-title">{{ translateMenuTitle(section.title) }}</h2>
</div>
</div>
<div class="section-meta">{{ $t('dashboard.moduleEntryCount', { count: section.children.length }) }}</div>
</div>
<div class="module-grid">
<router-link
v-for="(son, itemIndex) in section.children"
:key="son.id"
:to="son.path"
class="module-entry"
>
<div class="module-entry__glow" :style="getSoftStyle(sectionIndex + itemIndex)"/>
<div class="module-entry__icon" :style="getSoftStyle(sectionIndex + itemIndex)">
<svg-icon :icon-class="son.icon || '模块化'"/>
</div>
<div class="module-entry__title">{{ translateMenuTitle(son.title) }}</div>
<div class="module-entry__meta">
<span>{{ $t('dashboard.quickAccess') }}</span>
<svg-icon icon-class="右箭头" class="module-entry__arrow"/>
</div>
</router-link>
</div>
</section>
</div>
</div>
</el-card>
</el-card>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import request from '@/utils/request'
import { translateMenuTitle } from '@/utils/i18n'
const DASHBOARD_TONES = [
['#1576e8', '#44a0ff'],
['#0f9f85', '#43c4a8'],
['#f08a24', '#ffb255'],
['#6e5df6', '#958bff']
]
export default {
name: 'Dashboard',
data() {
return {
colors: ['rgb(162,126,253)', 'rgb(254,178,0)', 'rgb(66,222,144)', 'rgb(68,176,254)', 'rgb(255,87,116)'],
sudokus: [],
curSelect: null,
number: '',
timer: '',
fathers: [],
sons: []
sons: [],
sudokus: []
}
},
computed: {
@@ -50,7 +142,6 @@ export default {
'name',
'token'
]),
// 预先按 pid 分组,避免每次渲染都过滤
sonsByPid() {
const map = {}
for (const son of this.sons) {
@@ -61,196 +152,595 @@ export default {
map[pid].push(son)
}
return map
},
moduleSections() {
return this.fathers.map(father => ({
...father,
children: this.sonsByPid[father.id] || []
})).filter(section => section.children.length > 0)
},
noticeTotal() {
return this.sudokus.reduce((total, item) => total + (Number(item.value) || 0), 0)
},
heroMetrics() {
return [
{
label: this.$t('dashboard.metricTopModules'),
value: this.fathers.length,
hint: this.$t('dashboard.metricTopModulesHint')
},
{
label: this.$t('dashboard.metricQuickEntries'),
value: this.sons.length,
hint: this.$t('dashboard.metricQuickEntriesHint')
}
// {
// label: this.$t('dashboard.metricPendingMessages'),
// value: this.noticeTotal,
// hint: this.$t('dashboard.metricPendingMessagesHint')
// },
// {
// label: this.$t('dashboard.metricNoticeSources'),
// value: this.sudokus.length,
// hint: this.$t('dashboard.metricNoticeSourcesHint')
// }
]
},
greeting() {
const hour = new Date().getHours()
if (hour < 12) return this.$t('dashboard.greetingMorning')
if (hour < 18) return this.$t('dashboard.greetingAfternoon')
return this.$t('dashboard.greetingEvening')
},
todayLabel() {
return new Intl.DateTimeFormat(this.$i18n.locale, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
weekday: 'long'
}).format(new Date())
}
},
mounted() {
this.getFileList()
this.timer = setInterval(this.getFileList, 60 * 60 * 1000) // 60 分钟一刷新
this.initFirstModule()
this.initSecondModule()
this.loadDashboardData()
this.timer = setInterval(this.getFileList, 60 * 60 * 1000)
},
beforeDestroy() {
clearInterval(this.timer)
},
methods: {
getSonsByPid(pid) {
return this.sonsByPid[pid] || []
},
translateMenuTitle,
async loadDashboardData() {
await Promise.all([
this.initFirstModule(),
this.initSecondModule(),
this.getFileList()
])
},
getGradientStyle(index) {
const tone = DASHBOARD_TONES[index % DASHBOARD_TONES.length]
return {
background: `linear-gradient(135deg, ${tone[0]} 0%, ${tone[1]} 100%)`
}
},
getSoftStyle(index) {
const tone = DASHBOARD_TONES[index % DASHBOARD_TONES.length]
return {
background: `linear-gradient(135deg, ${tone[0]}1f 0%, ${tone[1]}24 100%)`,
color: tone[0]
}
},
async initFirstModule() {
await request({
const data = await request({
url: '',
method: 'post',
data: {
type: '1',
name: `菜单系统模块_首页信息_查询数据1`,
name: '菜单系统模块_首页信息_查询数据1',
param: '角色编号=' + this.token
}
}).then(data => {
this.fathers = []
data.data.map(v => {
this.fathers.push({
id: v.id,
title: v.title,
icon: v.icon
})
})
})
},
Color(index) {
const colorAngle = Math.floor(Math.random() * 360 + index)
return 'hsla(' + colorAngle + ',70%,30%,1)'
this.fathers = (data.data || []).map(v => ({
id: v.id,
title: v.title,
icon: v.icon
}))
},
async initSecondModule() {
await request({
const data = await request({
url: '',
method: 'post',
data: {
type: '1',
name: `菜单系统模块_首页信息_查询数据2`,
name: '菜单系统模块_首页信息_查询数据2',
param: '角色编号=' + this.token
}
}).then(data => {
this.sons = []
data.data.map(v => {
this.sons.push({
id: v.id,
pid: v.pid,
path: v.path,
icon: v.icon,
title: v.title
})
})
})
this.sons = (data.data || []).map(v => ({
id: v.id,
pid: v.pid,
path: v.path,
icon: v.icon,
title: v.title
}))
},
async getFileList() {
await request({
const data = await request({
url: '',
method: 'post',
data: {
type: '1',
name: `菜单系统模块_通知信息_查询数据`,
name: '菜单系统模块_通知信息_查询数据',
param: '角色编号=' + this.token
}
}).then(data => {
this.sudokus = []
for (let i = 0; i < data.data.length; i++) {
this.getMessageNum(data.data[i].消息通知来源, data.data[i].通知条件, data, i)
}
})
const noticeRows = data.data || []
const sudokus = await Promise.all(noticeRows.map(item => this.getMessageNum(item)))
this.sudokus = sudokus
.filter(Boolean)
.sort((a, b) => (Number(b.value) || 0) - (Number(a.value) || 0))
},
async getMessageNum(tableName, condition, rawData, i) {
await request({
async getMessageNum(item) {
const res = await request({
url: '',
method: 'post',
data: {
type: '3',
name: `select count(*) from ${tableName} where ${condition}`
name: `select count(*) from ${item.消息通知来源} where ${item.通知条件}`
}
}).then(res => {
this.sudokus.push({
id: rawData.data[i].id,
name: rawData.data[i].title,
icon: rawData.data[i].icon,
router: rawData.data[i].path,
value: res.data[0].Column1
})
})
return {
id: item.id,
name: item.title,
icon: item.icon,
router: item.path,
value: Number(res.data[0].Column1) || 0
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss">
.dashboard-container .el-divider__text {
color: gray;
font-size: 22px;
background-color: rgba(255,255,255,0.8);
}
.dashboard-page .el-badge__content {
background-color: rgb(255, 151, 91);
}
</style>
<style rel="stylesheet/scss" lang="scss" scoped>
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
.dashboard-page {
min-height: calc(100vh - 84px);
padding: 18px;
background:
radial-gradient(circle at top right, rgba(21, 118, 232, 0.12), transparent 24%),
linear-gradient(180deg, #f4f8ff 0%, #eef4fb 100%);
}
.dashboard-card {
min-height: calc(100vh - 120px);
border: none;
border-radius: 28px;
overflow: hidden;
background: rgba(255, 255, 255, 0.78);
box-shadow: 0 24px 60px rgba(26, 61, 111, 0.12);
}
.dashboard-container {
padding: 28px;
}
.hero-panel {
display: flex;
justify-content: space-between;
align-items: stretch;
gap: 24px;
padding: 28px;
border-radius: 28px;
background:
radial-gradient(circle at top right, rgba(255, 255, 255, 0.18), transparent 32%),
linear-gradient(135deg, #0e67d0 0%, #2f8fff 52%, #62b2ff 100%);
box-shadow: 0 24px 48px rgba(21, 118, 232, 0.22);
color: #ffffff;
}
.hero-copy {
flex: 1;
min-width: 0;
}
.hero-badge {
display: inline-flex;
align-items: center;
gap: 8px;
min-height: 36px;
padding: 0 14px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.16);
font-size: 13px;
letter-spacing: 0.04em;
}
.hero-badge-icon {
width: 16px;
height: 16px;
}
.hero-title {
margin: 18px 0 10px;
font-size: 34px;
line-height: 1.18;
font-weight: 700;
}
.hero-description {
max-width: 720px;
margin: 0;
font-size: 15px;
line-height: 1.75;
color: rgba(255, 255, 255, 0.9);
}
.hero-summary {
display: grid;
grid-template-columns: repeat(2, minmax(120px, 1fr));
gap: 12px;
width: 320px;
}
.summary-pill {
display: flex;
flex-direction: column;
justify-content: center;
min-height: 108px;
padding: 18px 20px;
border-radius: 22px;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(8px);
}
.summary-value {
font-size: 26px;
line-height: 1;
font-weight: 700;
}
.summary-label {
margin-top: 10px;
font-size: 13px;
color: rgba(255, 255, 255, 0.86);
}
.stats-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 16px;
margin-top: 22px;
}
.stat-card {
position: relative;
overflow: hidden;
min-height: 132px;
padding: 22px;
border-radius: 24px;
color: #ffffff;
}
.stat-card::after {
content: '';
position: absolute;
top: -12px;
right: -12px;
width: 86px;
height: 86px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.12);
}
.stat-card__label {
position: relative;
z-index: 1;
font-size: 14px;
opacity: 0.9;
}
.stat-card__value {
position: relative;
z-index: 1;
margin-top: 16px;
font-size: 34px;
line-height: 1;
font-weight: 700;
}
.stat-card__hint {
position: relative;
z-index: 1;
margin-top: 12px;
font-size: 13px;
opacity: 0.84;
}
.notice-panel,
.module-panel {
margin-top: 24px;
padding: 24px;
border-radius: 26px;
background: rgba(255, 255, 255, 0.8);
box-shadow: inset 0 0 0 1px rgba(149, 178, 214, 0.18);
}
.section-heading {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
margin-bottom: 18px;
}
.section-eyebrow {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.12em;
color: #7a91b1;
}
.section-title {
margin: 8px 0 0;
font-size: 24px;
line-height: 1.2;
color: #16345f;
}
.section-meta {
font-size: 13px;
color: #7d8ea8;
white-space: nowrap;
}
.notice-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 16px;
}
.notice-card {
display: flex;
align-items: center;
gap: 16px;
min-height: 112px;
padding: 20px;
border-radius: 22px;
background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
box-shadow: 0 14px 30px rgba(28, 66, 117, 0.08);
transition: transform .22s ease, box-shadow .22s ease;
}
.notice-card:hover,
.module-entry:hover {
transform: translateY(-4px);
box-shadow: 0 18px 36px rgba(28, 66, 117, 0.14);
}
.notice-card__icon,
.module-icon,
.module-entry__icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
border-radius: 18px;
}
.notice-card__icon {
width: 52px;
height: 52px;
font-size: 24px;
}
.notice-card__body {
flex: 1;
min-width: 0;
}
.notice-card__title,
.module-entry__title {
color: #1a355f;
font-weight: 600;
}
.notice-card__title {
font-size: 15px;
line-height: 1.5;
}
.notice-card__subtitle {
margin-top: 6px;
font-size: 12px;
color: #8aa0bb;
}
.notice-card__count {
min-width: 48px;
height: 48px;
padding: 0 12px;
border-radius: 16px;
background: #eef5ff;
color: #1576e8;
font-size: 22px;
font-weight: 700;
line-height: 48px;
text-align: center;
}
.empty-panel {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 220px;
border: 1px dashed #c8d7ea;
border-radius: 24px;
background: linear-gradient(180deg, #fbfdff 0%, #f3f8ff 100%);
text-align: center;
}
.empty-panel__icon {
width: 42px;
height: 42px;
color: #8ea6c3;
}
.empty-panel__title {
margin-top: 16px;
font-size: 18px;
color: #26456f;
font-weight: 600;
}
.empty-panel__text {
margin-top: 8px;
font-size: 13px;
color: #8a9cb4;
}
.module-title-wrap {
display: flex;
align-items: center;
gap: 14px;
}
.module-icon {
width: 54px;
height: 54px;
font-size: 24px;
}
.module-grid {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 16px;
}
.module-entry {
position: relative;
overflow: hidden;
min-height: 170px;
padding: 20px;
border-radius: 24px;
background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
box-shadow: 0 14px 30px rgba(28, 66, 117, 0.08);
transition: transform .22s ease, box-shadow .22s ease;
}
.module-entry__glow {
position: absolute;
top: -22px;
right: -10px;
width: 82px;
height: 82px;
border-radius: 50%;
opacity: 0.9;
}
.module-entry__icon {
position: relative;
z-index: 1;
width: 56px;
height: 56px;
font-size: 26px;
}
.module-entry__title {
position: relative;
z-index: 1;
margin-top: 38px;
font-size: 15px;
line-height: 1.55;
min-height: 46px;
}
.module-entry__meta {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 16px;
font-size: 12px;
color: #7d93ae;
}
.module-entry__arrow {
width: 14px;
height: 14px;
}
@media (max-width: 1600px) {
.notice-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.box-card {
width: 100%
.module-grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.item {
margin-top: 10px;
margin-right: 40px;
}
@media (max-width: 1280px) {
.hero-panel {
flex-direction: column;
}
.sudoku_row{
display: flex;
align-items: center;
width:100%;
.hero-summary {
width: 100%;
}
.stats-grid,
.notice-grid,
.module-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 768px) {
.dashboard-page {
padding: 12px;
}
.dashboard-container {
padding: 16px;
}
.hero-panel,
.notice-panel,
.module-panel {
padding: 18px;
border-radius: 22px;
}
.hero-title {
font-size: 28px;
}
.hero-summary,
.stats-grid,
.notice-grid,
.module-grid {
grid-template-columns: 1fr;
}
.section-heading {
align-items: flex-start;
flex-direction: column;
}
.notice-card {
align-items: flex-start;
flex-wrap: wrap;
}
.sudoku_item{
margin-top: 24ps;
margin-bottom: 20px;
margin-right: 20px;
border: 0 solid red;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width:240px;
height: 80px;
/*padding-top: 20px;*/
/*padding-bottom: 10px;*/
}
.sudoku_item:hover {
border: 2px solid white;
}
.sudoku_item img{
margin-bottom: 3px;
display: block;
}
.sudoku_item1{
margin-top: 24ps;
margin-bottom: 20px;
margin-left: 20px;
border: 0 solid red;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width:15%;
height: 80px;
padding-top: 20px;
/*padding-bottom: 10px;*/
}
.sudoku_item1:hover {
border: 2px solid rgb(135,187,224);
}
.sudoku_item1 img{
margin-bottom: 3px;
display: block;
}
.opacity{
opacity: 0.4;
background: #e5e5e5;
}
.dashboard {
&-container {
margin: 30px;
}
&-text {
font-size: 30px;
line-height: 46px;
}
}
</style>
<style>
.el-badge__content {
background-color: rgb(255,151,91);
}
/*#dashbadge {*/
/*background-color: rgb(255,151,91);*/
/*}*/
</style>

View File

@@ -1,48 +1,73 @@
<template>
<div>
<el-menu class="navbar" mode="horizontal" style="background: #1576e8!important;">
<hamburger :toggle-click="toggleSideBar" :is-active="sidebar.opened" class="hamburger-container"/>
<breadcrumb />
<el-dropdown class="language-container" trigger="click" @command="changeLanguage">
<el-button class="language-button" size="mini" type="text">
{{ currentLocaleLabel }}
<i class="el-icon-arrow-down el-icon--right"/>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="item in localeOptions"
:key="item.value"
:command="item.value"
:disabled="item.value === currentLocale"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown class="avatar-container" trigger="click">
<div class="avatar-wrapper">
<img :src="require('@/assets/img/leftPic.png')" class="user-avatar">
<i class="el-icon-caret-bottom"/>
<div class="navbar">
<div class="navbar__backdrop"/>
<div class="navbar__content">
<div class="navbar__left">
<div class="navbar__toggle">
<hamburger :toggle-click="toggleSideBar" :is-active="sidebar.opened" class="hamburger-container"/>
</div>
<div class="navbar__brand">
<!-- <div class="navbar__brand-title">{{ $t('login.systemName') }}</div>-->
<!-- <div class="navbar__brand-meta">-->
<!-- <span class="navbar__brand-chip">{{ currentDateLabel }}</span>-->
<!-- </div>-->
</div>
<div class="navbar__breadcrumb">
<breadcrumb />
</div>
</div>
<el-dropdown-menu slot="dropdown" class="user-dropdown">
<router-link class="inlineBlock" to="/">
<el-dropdown-item>
{{ $t('common.home') }}
</el-dropdown-item>
</router-link>
<el-dropdown-item divided @click.native="logout">
<span style="display:block;">{{ $t('common.logout') }}</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<div class="navbar__right">
<el-dropdown class="language-container" trigger="click" @command="changeLanguage">
<div class="nav-action language-button">
<span class="nav-action__icon el-icon-connection"/>
<span class="nav-action__text">{{ currentLocaleLabel }}</span>
<i class="el-icon-arrow-down el-icon--right"/>
</div>
<el-dropdown-menu slot="dropdown" class="navbar-dropdown">
<el-dropdown-item
v-for="item in localeOptions"
:key="item.value"
:command="item.value"
:disabled="item.value === currentLocale"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown class="avatar-container" trigger="click">
<div class="avatar-wrapper">
<div class="avatar-wrapper__meta">
<span class="avatar-wrapper__label">{{ name }}</span>
</div>
<div class="avatar-wrapper__image-box">
<img :src="require('@/assets/img/leftPic.png')" class="user-avatar">
</div>
<i class="el-icon-caret-bottom"/>
</div>
<el-dropdown-menu slot="dropdown" class="user-dropdown navbar-dropdown">
<router-link class="inlineBlock" to="/">
<el-dropdown-item>
{{ $t('common.home') }}
</el-dropdown-item>
</router-link>
<el-dropdown-item divided @click.native="logout">
<span class="dropdown-action">{{ $t('common.logout') }}</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
<!-- <div style="position: absolute;right: 20%;top:8px">-->
<!-- <el-badge v-if="showError" :value="errorCount" type="danger">-->
<!-- <el-button size="small" type="warning" @click="showOrder">昨日未生产订单</el-button>-->
<!-- </el-badge>-->
<!-- </div>-->
</el-menu>
<el-dialog :visible.sync="dialogFormVisible4" title="未生产订单" center style="min-height: 200px" width="400px">
<div style="font-size: 1.5rem;color: #ff0000">
</div>
<el-dialog :visible.sync="dialogFormVisible4" title="未生产订单" center class="navbar-dialog" width="400px">
<div class="navbar-dialog__content">
存在订单号 {{ errorData[0].订单号 }} 等{{ errorCount }}个逾期订单,请前往生产计划下发模块进行处理!
</div>
<div slot="footer" class="dialog-footer">
@@ -131,6 +156,13 @@ export default {
currentLocaleLabel() {
const current = this.localeOptions.find(item => item.value === this.currentLocale)
return current ? current.label : this.$t('common.language')
},
currentDateLabel() {
return new Intl.DateTimeFormat(this.currentLocale, {
year: 'numeric',
month: '2-digit',
day: '2-digit'
}).format(new Date())
}
},
created() {
@@ -477,69 +509,296 @@ export default {
<style rel="stylesheet/scss" lang="scss" scoped>
.navbar {
height: 50px;
line-height: 50px;
border-radius: 0px !important;
.hamburger-container {
line-height: 58px;
height: 50px;
float: left;
padding: 0 10px;
position: relative;
padding: 5px 18px 5px;
overflow: hidden;
background: linear-gradient(135deg, #0d66cf 0%, #2084f3 54%, #54abff 100%);
box-shadow: 0 18px 36px rgba(18, 86, 171, 0.2);
}
.navbar__backdrop {
position: absolute;
inset: 0;
background:
radial-gradient(circle at left top, rgba(255, 255, 255, 0.18), transparent 28%),
radial-gradient(circle at right bottom, rgba(255, 255, 255, 0.12), transparent 24%);
pointer-events: none;
}
.navbar__content {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
min-height: 56px;
}
.navbar__left,
.navbar__right {
display: flex;
align-items: center;
}
.navbar__left {
flex: 1;
min-width: 0;
}
.navbar__right {
gap: 12px;
flex-shrink: 0;
}
.navbar__toggle {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.14);
backdrop-filter: blur(10px);
transition: background-color .2s ease, transform .2s ease;
}
.navbar__toggle:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-1px);
}
.hamburger-container {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.navbar__brand {
display: flex;
flex-direction: column;
justify-content: center;
min-width: 0;
max-width: 360px;
}
.navbar__brand-title {
overflow: hidden;
color: #ffffff;
font-size: 18px;
font-weight: 700;
line-height: 1.1;
text-overflow: ellipsis;
white-space: nowrap;
}
.navbar__brand-meta {
margin-top: 6px;
}
.navbar__brand-chip {
display: inline-flex;
align-items: center;
min-height: 24px;
padding: 0 10px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.14);
color: rgba(255, 255, 255, 0.9);
font-size: 12px;
letter-spacing: 0.04em;
}
.navbar__breadcrumb {
flex: 1;
min-width: 0;
margin-left: 8px;
padding: 0 14px;
border-radius: 18px;
background: rgba(255, 255, 255, 0.12);
backdrop-filter: blur(10px);
}
.language-container,
.avatar-container {
display: flex;
align-items: center;
}
.nav-action,
.avatar-wrapper {
display: inline-flex;
align-items: center;
min-height: 48px;
border-radius: 18px;
background: rgba(255, 255, 255, 0.14);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
backdrop-filter: blur(10px);
color: #ffffff;
transition: background-color .2s ease, transform .2s ease;
cursor: pointer;
}
.nav-action:hover,
.avatar-wrapper:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-1px);
}
.language-button {
padding: 0 14px;
font-size: 13px;
}
.nav-action__icon {
font-size: 16px;
}
.nav-action__text {
margin-left: 8px;
}
.avatar-wrapper {
padding: 0 12px 0 14px;
gap: 10px;
}
.avatar-wrapper__meta {
display: flex;
flex-direction: column;
align-items: flex-end;
line-height: 1.2;
}
.avatar-wrapper__label {
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px;
font-weight: 600;
}
.avatar-wrapper__sub {
margin-top: 4px;
font-size: 11px;
color: rgba(255, 255, 255, 0.8);
letter-spacing: 0.05em;
text-transform: uppercase;
}
.avatar-wrapper__image-box {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.18);
}
.user-avatar {
width: 26px;
height: 26px;
object-fit: contain;
}
.el-icon-caret-bottom {
font-size: 12px;
color: rgba(255, 255, 255, 0.9);
}
.dropdown-action {
display: block;
}
.navbar-dialog__content {
font-size: 22px;
line-height: 1.6;
color: #f56c6c;
}
::v-deep .app-breadcrumb.el-breadcrumb {
display: flex;
align-items: center;
min-height: 48px;
margin-left: 0;
line-height: 1.4;
}
::v-deep .app-breadcrumb .el-breadcrumb__item {
display: inline-flex;
align-items: center;
}
::v-deep .app-breadcrumb .el-breadcrumb__inner,
::v-deep .app-breadcrumb .el-breadcrumb__inner a,
::v-deep .app-breadcrumb .no-redirect,
::v-deep .app-breadcrumb .el-breadcrumb__separator {
color: rgba(255, 255, 255, 0.92) !important;
}
::v-deep .app-breadcrumb .el-breadcrumb__inner {
font-size: 13px;
font-weight: 500;
}
::v-deep .navbar-dropdown.el-dropdown-menu {
border-radius: 16px;
border: none;
box-shadow: 0 16px 34px rgba(22, 56, 102, 0.16);
padding: 8px;
}
::v-deep .navbar-dropdown .el-dropdown-menu__item {
border-radius: 10px;
line-height: 38px;
font-size: 13px;
color: #214269;
}
::v-deep .navbar-dropdown .el-dropdown-menu__item:not(.is-disabled):hover {
background: #edf5ff;
color: #1576e8;
}
::v-deep .el-notification.right {
cursor: pointer !important;
}
@media (max-width: 1200px) {
.navbar__brand {
display: none;
}
.screenfull {
position: absolute;
right: 90px;
top: 16px;
color: red;
}
@media (max-width: 900px) {
.navbar {
padding: 10px 12px;
}
.language-container {
height: 50px;
display: inline-block;
position: absolute;
right: 95px;
top: 0;
.language-button {
height: 50px;
min-width: 76px;
padding: 0 8px;
color: #fff;
font-size: 13px;
line-height: 50px;
cursor: pointer;
}
.language-button:hover,
.language-button:focus {
color: #f2f6fc;
}
.navbar__breadcrumb {
display: none;
}
.avatar-container {
height: 50px;
display: inline-block;
position: absolute;
right: 35px;
.avatar-wrapper {
cursor: pointer;
margin-top: 5px;
position: relative;
width: 40px;
height: 40px;
background-color: #1576e8;
.user-avatar {
width: 30px;
height: 30px;
margin: 3px 0 0 3px;
}
.el-icon-caret-bottom {
color: #fff;
position: absolute;
right: -10px;
top: 25px;
font-size: 12px;
}
::v-deep .el-notification.right{
cursor:pointer!important;
}
}
.avatar-wrapper__meta {
display: none;
}
}
@media (max-width: 640px) {
.language-button {
min-width: 48px;
padding: 0 12px;
}
.nav-action__text {
display: none;
}
.navbar__right {
gap: 8px;
}
}
</style>

View File

@@ -1,24 +1,24 @@
<template>
<div>
<div class="sidebar-shell">
<el-scrollbar wrap-class="scrollbar-wrapper">
<div v-show="isShow" class="img-wrapper">
<img :src="require('@/assets/img/LOGO1.png')" height="49px">
<!-- <span style="font-size: 18px; font-weight: bold; color: white;margin-top: 7px">迈思信息<br>-->
<!-- <span style="font-size: 13px; color: white;margin-top: 0px!important;">MESWORK</span>-->
<!-- </span>-->
<div class="sidebar-shell">
<div v-show="showBrand" class="img-wrapper">
<img :src="require('@/assets/img/LOGO1.png')" class="brand-logo" alt="WODEER MES">
</div>
<el-menu
:show-timeout="200"
:default-active="$route.path"
:collapse="isCollapse"
:style="menuStyle"
class="sidebar-menu"
mode="vertical"
background-color="transparent"
text-color="#31456a"
active-text-color="#ffffff"
>
<sidebar-item v-for="route in permission_routers" :key="route.name" :item="route" :base-path="route.path"/>
</el-menu>
</div>
<el-menu
:show-timeout="200"
:default-active="$route.path"
:collapse="isCollapse"
mode="vertical"
background-color="white"
text-color="black"
active-text-color="#1576e8"
style="height:calc(100% - 124px)"
>
<sidebar-item v-for="route in permission_routers" :key="route.name" :item="route" :base-path="route.path"/>
</el-menu>
</el-scrollbar>
</div>
</template>
@@ -29,12 +29,6 @@ import SidebarItem from './SidebarItem'
export default {
components: { SidebarItem },
data() {
return {
isShow: '',
leftPic: ''
}
},
computed: {
...mapGetters([
'sidebar',
@@ -44,37 +38,46 @@ export default {
return this.$router.options.routes
},
isCollapse() {
this.showLogo()
return !this.sidebar.opened
}
},
// created() {
// this.axios.get('../MESWORK.png', {
// responseType: 'arraybuffer'
// }).then(res => {
// this.leftPic = 'data:image/jpeg;base64,' + btoa(new Uint8Array(res.data).reduce((data, byte) => data + String.fromCharCode(byte), ''))
// })
// },
methods: {
showLogo() {
if (this.sidebar.opened === true) {
this.isShow = true
} else {
this.isShow = false
},
showBrand() {
return this.sidebar.opened
},
menuStyle() {
return {
height: this.sidebar.opened ? 'calc(100% - 128px)' : 'calc(100% - 12px)'
}
}
}
}
</script>
<style lang="scss" scoped>
.img-wrapper{
background-color: #ffffff !important;
text-align: center;
//border-bottom: 1px solid #1576e8;
img{
//width: 137px;
//height: 38px;
//margin: 6px 10px 5px 45px;
}
.sidebar-shell {
min-height: 100%;
display: flex;
flex-direction: column;
}
.img-wrapper {
display: flex;
align-items: center;
justify-content: center;
margin: 12px 10px 18px;
padding: 12px 10px;
min-height: 76px;
border-radius: 18px;
background: rgba(255, 255, 255, 0.72);
box-shadow: inset 0 0 0 1px rgba(145, 178, 233, 0.18);
}
.brand-logo {
display: block;
max-width: 100%;
height: 49px;
object-fit: contain;
}
.sidebar-menu {
flex: 1;
}
</style>

View File

@@ -1,70 +1,90 @@
<template>
<div>
<div class="login-container">
<!-- <i class="login-img"/>-->
<el-form
ref="loginForm"
:model="loginForm"
:rules="loginRules"
auto-complete="on"
label-position="left"
label-width="0px"
class="card-box login-form">
<h1 class="title" style="justify-content: center;align-items: center;">
<!-- <div style="width: 100px;">
<img :src="require('@/assets/img/LOGO.png')" alt="" style="height: 50px;">
</div> -->
<div style="margin: 0 10px">
<!-- <div>550KV GIS隔离开关</div> -->
<div>{{ $t('login.systemName') }}</div>
<div class="login-backdrop">
<div class="login-orb login-orb--one"/>
<div class="login-orb login-orb--two"/>
<div class="login-orb login-orb--three"/>
</div>
<div class="login-shell">
<section class="login-hero">
<!-- <div class="login-hero__badge">{{ currentLocaleLabel }}</div>-->
<h1 class="login-hero__title">{{ $t('login.systemName') }}</h1>
<div class="login-hero__grid">
<div class="hero-card hero-card--primary">
<span class="hero-card__value">{{ currentDateLabel }}</span>
<span class="hero-card__label">{{ currentLocaleLabel }}</span>
</div>
<!-- <div class="hero-card hero-card&#45;&#45;soft">-->
<!-- <span class="hero-card__value">{{ currentLocaleLabel }}</span>-->
<!-- <span class="hero-card__label">{{ $t('login.loginButton') }}</span>-->
<!-- </div>-->
</div>
</h1>
<el-dropdown class="login-language" trigger="click" @command="changeLanguage">
<el-button class="login-language-button" size="mini" type="text">
{{ currentLocaleLabel }}
<i class="el-icon-arrow-down el-icon--right"/>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="item in localeOptions"
:key="item.value"
:command="item.value"
:disabled="item.value === currentLocale"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<div class="line"/>
<div class="content">
<el-form-item prop="username">
<span class="svg-container svg-container_login">
<svg-icon icon-class="username" />
</span>
<el-input v-model="loginForm.username" name="username" type="text" auto-complete="off" :placeholder="$t('login.usernamePlaceholder')" @input.native="changeUser" />
</el-form-item>
<el-form-item prop="password">
<span class="svg-container">
<svg-icon icon-class="password"/>
</span>
<el-input
:type="pwdType"
v-model="loginForm.password"
name="password"
auto-complete="on"
:placeholder="$t('login.passwordPlaceholder')"
@keyup.enter.native="handleLogin"/>
<span class="show-pwd" @click="showPwd"><svg-icon icon-class="eye" /></span>
</el-form-item>
<div style="display: flex;align-items: center;">
<el-checkbox v-model="loginForm.checked" class="remeberPassword">{{ $t('login.rememberPassword') }}</el-checkbox>
</section>
<el-form
ref="loginForm"
:model="loginForm"
:rules="loginRules"
auto-complete="on"
label-position="left"
label-width="0px"
class="card-box login-form"
>
<div class="login-form__header">
<el-dropdown class="login-language" trigger="click" @command="changeLanguage">
<el-button class="login-language-button" size="mini" type="text">
{{ currentLocaleLabel }}
<i class="el-icon-arrow-down el-icon--right"/>
</el-button>
<el-dropdown-menu slot="dropdown" class="login-dropdown">
<el-dropdown-item
v-for="item in localeOptions"
:key="item.value"
:command="item.value"
:disabled="item.value === currentLocale"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<h1 class="title">
<div class="title__main">{{ $t('login.systemName') }}</div>
<div class="title__meta">{{ currentDateLabel }}</div>
</h1>
</div>
<div class="line"/>
<div class="content">
<el-form-item prop="username">
<span class="svg-container svg-container_login">
<svg-icon icon-class="username" />
</span>
<el-input v-model="loginForm.username" name="username" type="text" auto-complete="off" :placeholder="$t('login.usernamePlaceholder')" @input.native="changeUser" />
</el-form-item>
<el-form-item prop="password">
<span class="svg-container">
<svg-icon icon-class="password"/>
</span>
<el-input
v-model="loginForm.password"
:type="pwdType"
name="password"
auto-complete="on"
:placeholder="$t('login.passwordPlaceholder')"
@keyup.enter.native="handleLogin"
/>
<span class="show-pwd" @click="showPwd"><svg-icon icon-class="eye" /></span>
</el-form-item>
<div style="display: flex;align-items: center;">
<el-checkbox v-model="loginForm.checked" class="remeberPassword">{{ $t('login.rememberPassword') }}</el-checkbox>
<!-- <el-button type="text" class="change-password-button" @click="handleEdit('form')">{{ $t('login.changePassword') }}</el-button> -->
</div>
<el-button :loading="loading" class="signIn" type="primary" style="width:100%;" @click.native.prevent="handleLogin">
<span style="font-size: 20px!important;">{{ $t('login.loginButton') }}</span>
</el-button>
</div>
<el-button :loading="loading" class="signIn" type="primary" style="width:100%;" @click.native.prevent="handleLogin">
<span style="font-size: 20px!important;">{{ $t('login.loginButton') }}</span>
</el-button>
</div>
</el-form>
</el-form>
</div>
</div>
<el-dialog :visible.sync="dialogFormVisible" :title="$t('login.changePassword')" center style="min-height: 200px" width="400px">
@@ -128,6 +148,13 @@ export default {
const current = this.localeOptions.find(item => item.value === this.currentLocale)
return current ? current.label : this.$t('common.language')
},
currentDateLabel() {
return new Intl.DateTimeFormat(this.currentLocale, {
year: 'numeric',
month: '2-digit',
day: '2-digit'
}).format(new Date())
},
rules() {
return {
againPassword: [
@@ -255,175 +282,347 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss">
$bg:#2d3a4b;
$dark_gray:#889aa4;
$light_gray:#eee;
.login-container {
position: fixed;
height: 100%;
width:100%;
background-color: #ebebeb;
background-image:url("../../../static/login.png");background-repeat:no-repeat; background-size:100% 100%;-moz-background-size:100% 100%;
inset: 0;
overflow: hidden;
background:
linear-gradient(115deg, rgba(7, 30, 72, 0.72), rgba(17, 91, 174, 0.44)),
url("../../../static/login.png") center center / cover no-repeat;
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px #293444 inset !important;
-webkit-text-fill-color: #fff !important;
-webkit-box-shadow: 0 0 0 1000px #ffffff inset !important;
-webkit-text-fill-color: #24405f !important;
}
/*.login-img{*/
/* position: absolute;*/
/* width: 100%;*/
/* height: 100%;*/
/* background: url("../../../static/login.png") no-repeat;*/
/*}*/
input {
background: transparent;
border: 1px;
border: none;
-webkit-appearance: none;
border-radius: 0;
padding: 12px 5px 12px 15px;
color: $light_gray;
height: 47px;
padding: 0 8px 0 14px;
color: #24405f;
height: 56px;
line-height: 56px;
}
.el-input {
display: inline-block;
height: 47px;
width: 85%;
height: 56px;
width: calc(100% - 56px);
}
.svg-container {
padding: 6px 5px 6px 15px;
color: #1576e8;
font-size: 20px!important;
vertical-align: middle;
width: 30px;
display: inline-block;
&_login {
font-size: 20px;
}
}
.title {
font-size: 22px;
line-height: 30px;
color: #1576e8;
margin: 0 auto 20px auto;
text-align: center;
font-weight: bold;
font-family: 微软雅黑Regular&Bold;
display: flex;
}
.content{
width: 330px;
margin: 30px auto 0 auto;
}
.line{
width: 100%;
margin: 0 auto 25px auto;
background-color: #1576e8;
height: 1px;
}
.login-form {
height: 450px;
border-radius: 20px;
background-color: white;
position: absolute;
left: 0;
right: 0;
width: 400px;
padding: 35px 15px 15px 15px;
margin: 120px auto;
}
.login-language {
position: absolute;
right: 20px;
top: 14px;
.login-language-button {
min-width: 78px;
padding: 6px 8px;
color: #1576e8;
font-size: 13px;
cursor: pointer;
}
.login-language-button:hover,
.login-language-button:focus {
color: #0f5eb8;
}
}
.tips {
font-size: 14px;
color: #fff;
margin-bottom: 10px;
span {
&:first-of-type {
margin-right: 16px;
}
}
}
.el-input__inner{
border: none!important;
font-size: 18px!important;
color: #29335b!important;
.el-input__inner {
border: none !important;
background: transparent !important;
font-size: 17px !important;
color: #24405f !important;
padding: 0 !important;
height: 56px;
line-height: 56px;
}
.el-form-item {
border: 1px solid #1576e8;
background: #ffffff;
border-radius: 5px;
margin-bottom: 18px;
border: 1px solid rgba(125, 162, 214, 0.36);
background: rgba(245, 249, 255, 0.92);
border-radius: 18px;
color: #454545;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease;
}
/*登录button*/
.signIn{
width: 100%;
border: none;
margin-top: 20px;
border-radius: 5px;
font-size: 18px;
color:white;
background: -webkit-linear-gradient(left, #1576e8 , #9fd2ab); /* Safari 5.1 - 6.0 */
background: -o-linear-gradient(right, #1576e8, #9fd2ab); /* Opera 11.1 - 12.0 */
background: -moz-linear-gradient(right, #1576e8, #9fd2ab); /* Firefox 3.6 - 15 */
background: linear-gradient(to right, #1576e8 , #9fd2ab); /* 标准的语法(必须放在最后) */
.el-form-item:focus-within {
border-color: rgba(21, 118, 232, 0.72);
box-shadow: 0 14px 26px rgba(21, 118, 232, 0.14);
transform: translateY(-1px);
}
.remeberPassword{
}
.login-backdrop {
position: absolute;
inset: 0;
background:
linear-gradient(90deg, rgba(8, 33, 79, 0.78) 0%, rgba(8, 33, 79, 0.32) 40%, rgba(255, 255, 255, 0.08) 100%);
}
.login-orb {
position: absolute;
border-radius: 50%;
filter: blur(6px);
}
.login-orb--one {
top: 10%;
left: 8%;
width: 240px;
height: 240px;
background: radial-gradient(circle, rgba(93, 176, 255, 0.28) 0%, rgba(93, 176, 255, 0) 72%);
}
.login-orb--two {
right: 18%;
bottom: 12%;
width: 280px;
height: 280px;
background: radial-gradient(circle, rgba(159, 210, 171, 0.22) 0%, rgba(159, 210, 171, 0) 72%);
}
.login-orb--three {
top: 28%;
right: 10%;
width: 140px;
height: 140px;
background: radial-gradient(circle, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 74%);
}
.login-shell {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
gap: 36px;
max-width: 1320px;
min-height: 100%;
margin: 0 auto;
padding: 40px 48px;
}
.login-hero {
flex: 1;
max-width: 540px;
color: #ffffff;
}
.login-hero__badge {
display: inline-flex;
align-items: center;
min-height: 34px;
padding: 0 14px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.14);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
font-size: 13px;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.login-hero__title {
margin: 22px 0 0;
font-size: 44px;
line-height: 1.2;
font-weight: 700;
text-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
}
.login-hero__grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
margin-top: 34px;
}
.hero-card {
display: flex;
flex-direction: column;
justify-content: center;
min-height: 132px;
padding: 22px;
border-radius: 24px;
backdrop-filter: blur(12px);
box-shadow: 0 20px 40px rgba(8, 26, 58, 0.18);
}
.hero-card--primary {
background: rgba(255, 255, 255, 0.16);
}
.hero-card--soft {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(126, 198, 255, 0.12));
}
.hero-card__value {
font-size: 26px;
font-weight: 700;
line-height: 1.2;
}
.hero-card__label {
margin-top: 12px;
font-size: 13px;
color: rgba(255, 255, 255, 0.84);
letter-spacing: 0.04em;
}
.login-form {
position: relative;
width: 100%;
max-width: 500px;
padding: 28px 28px 30px;
border-radius: 30px;
background: rgba(255, 255, 255, 0.88);
box-shadow: 0 28px 60px rgba(9, 31, 74, 0.22);
backdrop-filter: blur(18px);
}
.login-form__header {
position: relative;
padding-right: 110px;
}
.login-language {
position: absolute;
top: 0;
right: 0;
.login-language-button {
min-width: 86px;
min-height: 38px;
padding: 0 12px;
border-radius: 12px;
background: rgba(21, 118, 232, 0.08);
color: #1576e8;
font-size: 13px;
box-shadow: inset 0 0 0 1px rgba(21, 118, 232, 0.12);
}
.change-password-button {
margin-left: auto;
color: #1576e8;
.login-language-button:hover,
.login-language-button:focus {
color: #0f5eb8;
background: rgba(21, 118, 232, 0.12);
}
.show-pwd {
position: absolute;
right: 10px;
top: 7px;
font-size: 16px;
color: #1576e8;
cursor: pointer;
user-select:none;
}
.title {
margin: 0;
padding-right: 10px;
color: #123d70;
text-align: left;
}
.title__main {
font-size: 31px;
line-height: 1.28;
font-weight: 700;
letter-spacing: 0.02em;
}
.title__meta {
margin-top: 10px;
font-size: 13px;
color: #7890ab;
letter-spacing: 0.05em;
}
.line {
width: 100%;
margin: 24px auto 24px;
height: 1px;
background: linear-gradient(90deg, rgba(21, 118, 232, 0.44), rgba(21, 118, 232, 0));
}
.content {
width: 100%;
}
.svg-container {
padding: 0 0 0 18px;
color: #1576e8;
font-size: 20px !important;
vertical-align: middle;
width: 48px;
line-height: 56px;
display: inline-block;
&_login {
font-size: 20px;
}
@media (min-width: 1024px) {
/*根据屏幕大小确定显示的位置*/
.login-form{
position: absolute;
left: 40%;
top: 0;
width: 600px;
}
.title{
font-size: 32px!important;
line-height: 32px;
}
.el-form-item{
width: 400px;
height: 48px;
}
.content{
width: 400px;
}
}
.signIn {
width: 100%;
min-height: 56px;
margin-top: 16px;
border: none;
border-radius: 18px;
font-size: 18px;
color: #ffffff;
box-shadow: 0 18px 32px rgba(21, 118, 232, 0.26);
background: linear-gradient(135deg, #1576e8 0%, #2ea2ff 55%, #77d1b8 100%);
}
.signIn:hover,
.signIn:focus {
background: linear-gradient(135deg, #1169cf 0%, #2194ee 55%, #67c2aa 100%);
}
.remeberPassword {
color: #4f6987;
}
.change-password-button {
margin-left: auto;
color: #1576e8;
}
.show-pwd {
position: absolute;
right: 16px;
top: 16px;
font-size: 17px;
color: #1576e8;
cursor: pointer;
user-select: none;
}
.login-dropdown.el-dropdown-menu {
border: none;
border-radius: 14px;
box-shadow: 0 18px 36px rgba(25, 63, 115, 0.16);
padding: 8px;
}
.login-dropdown .el-dropdown-menu__item {
border-radius: 10px;
color: #23466f;
}
.login-dropdown .el-dropdown-menu__item:not(.is-disabled):hover {
background: #edf5ff;
color: #1576e8;
}
@media (max-width: 1080px) {
.login-shell {
justify-content: center;
padding: 28px 20px;
}
@media (min-width: 1281px) {
.login-form{
position: absolute;
left: 30%;
top: 6%;
width: 600px;
}
.login-hero {
display: none;
}
}
@media (max-width: 640px) {
.login-form {
max-width: none;
padding: 22px 18px 24px;
border-radius: 24px;
}
.login-form__header {
padding-right: 0;
}
.login-language {
position: static;
display: flex;
justify-content: flex-end;
margin-bottom: 14px;
}
.title__main {
font-size: 24px;
}
}
</style>

View File

@@ -5,7 +5,7 @@ window.g = {
mqttIP: '192.168.0.80',
mqttPortNumber: '8083',
printTemplate: 'BQFT_BarCode_001',
baseURL: 'http://127.0.0.1:10026',
baseURL: 'http://192.168.5.100:10026',
requestConfig_baseURL: 'http://127.0.0.1:9981/mes'
// requestConfig_baseURL: 'http://10.89.33.67:6867/mes'
}