diff --git a/.gitignore b/.gitignore index a7ba22b..6a083e1 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ /WebAPIServer/WebAPIServer.csproj.user /WebAPIServer/bin /auts_new_mobile.client/obj +.vscode diff --git a/ViewModels/ResponseData/ResLoginData.cs b/ViewModels/ResponseData/ResLoginData.cs index f1dc6b2..4542229 100644 --- a/ViewModels/ResponseData/ResLoginData.cs +++ b/ViewModels/ResponseData/ResLoginData.cs @@ -11,6 +11,7 @@ namespace ViewModels.ResponseData public int ID { get; set; } public string UserName { get; set; } public bool IsAdmin { get; set; } + public object Auths { get; set; } public string AccessToken { get; set; } } diff --git a/WebAPIServer/Controllers/LoginController.cs b/WebAPIServer/Controllers/LoginController.cs index 7c2db9a..b3cf936 100644 --- a/WebAPIServer/Controllers/LoginController.cs +++ b/WebAPIServer/Controllers/LoginController.cs @@ -113,6 +113,7 @@ namespace WebAPIServer.Controllers entity = q.TblUtsManageUsers.SingleOrDefault(A => A.UserName.Equals(username) && A.Password.Equals(pwd)); if (entity != null) { + var auths = q.TblUtsManageUserauthOperations.Where(A => A.UserId == entity.Id).ToList(); TokenString = GetToken(entity); res.isok = true; @@ -123,6 +124,7 @@ namespace WebAPIServer.Controllers r.IsAdmin = entity.IsAdmin; r.ID = entity.Id; r.UserName = entity.UserName; + r.Auths = auths; res.response = r; } else diff --git a/WebAPIServer/Controllers/TestLogsController.cs b/WebAPIServer/Controllers/TestLogsController.cs index f17efd0..afd5310 100644 --- a/WebAPIServer/Controllers/TestLogsController.cs +++ b/WebAPIServer/Controllers/TestLogsController.cs @@ -197,7 +197,8 @@ namespace WebAPIServer.Controllers IDbCommand dataCmd = conn.CreateCommand(); dataCmd.CommandText = dataQuery.ToString(); - MySqlCommand command = dataCmd as MySqlCommand; + var command = dataCmd as MySqlCommand + ?? throw new InvalidOperationException("Database command is not MySqlCommand"); MySqlDataAdapter adapter = new(command); DataSet ds = new(); adapter.Fill(ds); @@ -239,11 +240,57 @@ namespace WebAPIServer.Controllers string? EndTime = T.EndTime; string? TestResult = T.TestResult; List? FailSteps = T.FailSteps; + string? dutSnLike = T.DutSnLike; + string? failstepsLike = T.FailstepsLike; string? ColumnList = T.ColumnList; int page = T.Page; int pageSize = T.PageSize; string distinctType = T.DistinctType ?? "None"; // 获取去重类型,默认为不去重 + static List SplitCommaTokens(string? input) + { + if (string.IsNullOrWhiteSpace(input)) + { + return []; + } + + return input + .Split(',', StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.Trim()) + .Where(s => !string.IsNullOrWhiteSpace(s)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + } + + static void AppendLikeOrClause(StringBuilder sb, IDbCommand cmd, string fieldName, string paramPrefix, List tokens) + { + if (tokens == null || tokens.Count == 0) + { + return; + } + + sb.Append("AND ("); + for (int i = 0; i < tokens.Count; i++) + { + if (i > 0) + { + sb.Append(" OR "); + } + + string paramName = $"@{paramPrefix}{i}"; + sb.Append($"{fieldName} LIKE {paramName} "); + + var p = cmd.CreateParameter(); + p.ParameterName = paramName; + p.Value = $"%{tokens[i]}%"; + cmd.Parameters.Add(p); + } + sb.Append(") "); + } + + var dutSnTokens = SplitCommaTokens(dutSnLike); + var failstepsTokens = SplitCommaTokens(failstepsLike); + if (string.IsNullOrEmpty(StartTime) || string.IsNullOrEmpty(EndTime)) { returnInfo.isok = false; @@ -265,6 +312,8 @@ namespace WebAPIServer.Controllers // 1. 构建基础查询(总数查询) StringBuilder baseQuery = new StringBuilder(); + IDbCommand countCmd = conn.CreateCommand(); + if (distinctType == "None") { // 不去重的情况 @@ -290,8 +339,10 @@ namespace WebAPIServer.Controllers baseQuery.Append($"AND Failsteps IN ({failStepsCondition}) "); } + AppendLikeOrClause(baseQuery, countCmd, "DUT_SN", "sn", dutSnTokens); + AppendLikeOrClause(baseQuery, countCmd, "Failsteps", "fs", failstepsTokens); + // 执行总数查询 - IDbCommand countCmd = conn.CreateCommand(); countCmd.CommandText = baseQuery.ToString(); long totalCount = Convert.ToInt64(countCmd.ExecuteScalar()); @@ -299,6 +350,8 @@ namespace WebAPIServer.Controllers long totalPassCount = 0; StringBuilder totalPassQuery = new StringBuilder(); + IDbCommand totalPassCmd = conn.CreateCommand(); + if (distinctType == "None") { // 不去重的情况:统计所有通过的记录 @@ -317,6 +370,9 @@ namespace WebAPIServer.Controllers string failStepsCondition = string.Join(",", FailSteps.Select(f => $"'{MySqlHelper.EscapeString(f)}'")); totalPassQuery.Append($"AND Failsteps IN ({failStepsCondition}) "); } + + AppendLikeOrClause(totalPassQuery, totalPassCmd, "DUT_SN", "sn", dutSnTokens); + AppendLikeOrClause(totalPassQuery, totalPassCmd, "Failsteps", "fs", failstepsTokens); } else { @@ -340,10 +396,11 @@ namespace WebAPIServer.Controllers totalPassQuery.Append($"AND Failsteps IN ({failStepsCondition}) "); } + AppendLikeOrClause(totalPassQuery, totalPassCmd, "DUT_SN", "sn", dutSnTokens); + AppendLikeOrClause(totalPassQuery, totalPassCmd, "Failsteps", "fs", failstepsTokens); + totalPassQuery.Append($") as t WHERE rn = 1 AND TestResult = '1'"); } - - IDbCommand totalPassCmd = conn.CreateCommand(); totalPassCmd.CommandText = totalPassQuery.ToString(); totalPassCount = Convert.ToInt64(totalPassCmd.ExecuteScalar()); @@ -352,6 +409,8 @@ namespace WebAPIServer.Controllers // 4. 构建数据查询 StringBuilder dataQuery = new StringBuilder(); + IDbCommand dataCmd = conn.CreateCommand(); + if (distinctType == "None") { // 不去重的情况 @@ -395,6 +454,9 @@ namespace WebAPIServer.Controllers dataQuery.Append($"AND Failsteps IN ({failStepsCondition}) "); } + AppendLikeOrClause(dataQuery, dataCmd, "DUT_SN", "sn", dutSnTokens); + AppendLikeOrClause(dataQuery, dataCmd, "Failsteps", "fs", failstepsTokens); + dataQuery.Append(") t WHERE rn = 1 "); } @@ -412,16 +474,19 @@ namespace WebAPIServer.Controllers string failStepsCondition = string.Join(",", FailSteps.Select(f => $"'{MySqlHelper.EscapeString(f)}'")); dataQuery.Append($"AND Failsteps IN ({failStepsCondition}) "); } + + AppendLikeOrClause(dataQuery, dataCmd, "DUT_SN", "sn", dutSnTokens); + AppendLikeOrClause(dataQuery, dataCmd, "Failsteps", "fs", failstepsTokens); } // 添加排序和分页 dataQuery.Append($"ORDER BY StartTime DESC LIMIT {pageSize} OFFSET {(page - 1) * pageSize}"); // 执行数据查询 - IDbCommand dataCmd = conn.CreateCommand(); dataCmd.CommandText = dataQuery.ToString(); - MySqlCommand command = dataCmd as MySqlCommand; + var command = dataCmd as MySqlCommand + ?? throw new InvalidOperationException("Database command is not MySqlCommand"); MySqlDataAdapter adapter = new(command); DataSet ds = new(); adapter.Fill(ds); @@ -461,10 +526,12 @@ namespace WebAPIServer.Controllers { public string? dbName { get; set; } public string? tbName { get; set; } - public string StartTime { get; set; } - public string EndTime { get; set; } + public string StartTime { get; set; } = string.Empty; + public string EndTime { get; set; } = string.Empty; public string? TestResult { get; set; } public List? FailSteps { get; set; } + public string? DutSnLike { get; set; } + public string? FailstepsLike { get; set; } public string? ColumnList { get; set; } public string DistinctType { get; set; } = "None"; // 新增字段:None-不去重, Latest-保留最新, Earliest-保留最早 } @@ -472,27 +539,27 @@ namespace WebAPIServer.Controllers public class ReturnTestLogs { - public string Step { get; set; } - public string ID { get; set; } - public string StartTime { get; set; } - public string UsedTime { get; set; } - public string FailSteps { get; set; } - public string FailMsg { get; set; } - public string TestResult { get; set; } - public List> RObj { get; set; } + public string Step { get; set; } = string.Empty; + public string ID { get; set; } = string.Empty; + public string StartTime { get; set; } = string.Empty; + public string UsedTime { get; set; } = string.Empty; + public string FailSteps { get; set; } = string.Empty; + public string FailMsg { get; set; } = string.Empty; + public string TestResult { get; set; } = string.Empty; + public List> RObj { get; set; } = new(); } public class TestLogsQueryModel { - public string DBName { get; set; } - public string SnCode { get; set; } + public string DBName { get; set; } = string.Empty; + public string SnCode { get; set; } = string.Empty; - public List DbList { get; set; } + public List DbList { get; set; } = new(); } public class DBL { - public string ProId { get; set; } - public string Step { get; set; } + public string ProId { get; set; } = string.Empty; + public string Step { get; set; } = string.Empty; } @@ -506,9 +573,9 @@ namespace WebAPIServer.Controllers public class ReturnInfoPage { public bool isok { get; set; } - public string message { get; set; } + public string? message { get; set; } public long total { get; set; } - public object response { get; set; } + public object? response { get; set; } public long passCount { get; set; } public double passRate { get; set; } } diff --git a/WebAPIServer/Properties/PublishProfiles/FolderProfile.pubxml.user b/WebAPIServer/Properties/PublishProfiles/FolderProfile.pubxml.user index 8df2dd3..25fe974 100644 --- a/WebAPIServer/Properties/PublishProfiles/FolderProfile.pubxml.user +++ b/WebAPIServer/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -4,8 +4,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. --> - <_PublishTargetUrl>E:\Project\AUTS_New\WebAPIServer\bin\Release\net8.0\publish\ - True|2025-08-28T09:10:06.0215159Z||;True|2025-08-27T20:08:53.7448405+08:00||;True|2025-08-27T09:30:37.1012354+08:00||;True|2025-08-19T18:15:37.7913274+08:00||;True|2025-08-19T17:20:58.7412452+08:00||;True|2025-08-19T15:55:20.9527200+08:00||;True|2025-04-07T19:28:10.1279229+08:00||;True|2025-03-31T11:42:19.8860538+08:00||;True|2025-03-29T17:38:19.1679458+08:00||;True|2025-03-28T19:10:51.5374819+08:00||;True|2025-03-28T17:57:42.2820238+08:00||;True|2025-03-21T17:12:53.2430355+08:00||;False|2025-03-21T17:12:26.4177469+08:00||;True|2025-03-18T15:58:56.0927860+08:00||;True|2025-03-18T15:57:00.4917451+08:00||;True|2025-03-18T15:55:00.3530973+08:00||; + <_PublishTargetUrl>E:\Project_Class\ATUS\Web_AUTS_New_Prod\WebAPIServer\bin\Release\net8.0\publish\ + True|2026-01-10T03:51:47.0554512Z||;True|2025-12-22T14:34:59.4524330+08:00||;True|2025-08-28T17:10:06.0215159+08:00||;True|2025-08-27T20:08:53.7448405+08:00||;True|2025-08-27T09:30:37.1012354+08:00||;True|2025-08-19T18:15:37.7913274+08:00||;True|2025-08-19T17:20:58.7412452+08:00||;True|2025-08-19T15:55:20.9527200+08:00||;True|2025-04-07T19:28:10.1279229+08:00||;True|2025-03-31T11:42:19.8860538+08:00||;True|2025-03-29T17:38:19.1679458+08:00||;True|2025-03-28T19:10:51.5374819+08:00||;True|2025-03-28T17:57:42.2820238+08:00||;True|2025-03-21T17:12:53.2430355+08:00||;False|2025-03-21T17:12:26.4177469+08:00||;True|2025-03-18T15:58:56.0927860+08:00||;True|2025-03-18T15:57:00.4917451+08:00||;True|2025-03-18T15:55:00.3530973+08:00||; \ No newline at end of file diff --git a/auts_new.client/.vscode/extensions.json b/auts_new.client/.vscode/extensions.json deleted file mode 100644 index 940260d..0000000 --- a/auts_new.client/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["dbaeumer.vscode-eslint"] -} diff --git a/auts_new.client/.vscode/launch.json b/auts_new.client/.vscode/launch.json deleted file mode 100644 index aed85ca..0000000 --- a/auts_new.client/.vscode/launch.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "type": "edge", - "request": "launch", - "name": "localhost (Edge)", - "url": "https://localhost:33891", - "webRoot": "${workspaceFolder}" - }, - { - "type": "chrome", - "request": "launch", - "name": "localhost (Chrome)", - "url": "https://localhost:33891", - "webRoot": "${workspaceFolder}" - } - ] -} diff --git a/auts_new.client/.vscode/settings.json b/auts_new.client/.vscode/settings.json deleted file mode 100644 index 02a0913..0000000 --- a/auts_new.client/.vscode/settings.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "files.eol":"\n", - "editor.tabSize": 2, - "eslint.format.enable": true, - "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"], - "[vue]": { - "editor.formatOnSave": true, - "editor.defaultFormatter": "dbaeumer.vscode-eslint" - }, - "[typescriptreact]": { - "editor.formatOnSave": true, - "editor.defaultFormatter": "dbaeumer.vscode-eslint" - }, - "[javascriptreact]": { - "editor.formatOnSave": true, - "editor.defaultFormatter": "dbaeumer.vscode-eslint" - }, - "[typescript]": { - "editor.formatOnSave": true, - "editor.defaultFormatter": "dbaeumer.vscode-eslint" - }, - "[javascript]": { - "editor.formatOnSave": true, - "editor.defaultFormatter": "dbaeumer.vscode-eslint" - }, - "cSpell.words": [ - "tdesign", - "tvision", - "echarts", - "nprogress", - "commitlint", - "stylelint", - "pinia", - "qrcode" - ], -} diff --git a/auts_new_mobile.client/.vscode/extensions.json b/auts_new_mobile.client/.vscode/extensions.json deleted file mode 100644 index 5efa012..0000000 --- a/auts_new_mobile.client/.vscode/extensions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "recommendations": [ - "Vue.volar", - "dbaeumer.vscode-eslint", - "EditorConfig.EditorConfig" - ] -} diff --git a/auts_new_mobile.client/package-lock.json b/auts_new_mobile.client/package-lock.json index 12ecb45..cfed08b 100644 --- a/auts_new_mobile.client/package-lock.json +++ b/auts_new_mobile.client/package-lock.json @@ -95,6 +95,7 @@ "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -1674,6 +1675,7 @@ "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/lodash": "*" } @@ -1952,6 +1954,7 @@ "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-13.6.0.tgz", "integrity": "sha512-DJbD5fV86muVmBgS9QQPddVX7d9hWYswzlf4bIyUD2dj8GC46R1uNClZhVAmsdVts4xb2jwp1PbpuiA50Qee1A==", "license": "MIT", + "peer": true, "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "13.6.0", @@ -1991,6 +1994,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2188,6 +2192,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001733", "electron-to-chromium": "^1.5.199", @@ -2848,6 +2853,7 @@ "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -3783,13 +3789,15 @@ "version": "4.17.21", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash-unified": { "version": "1.0.3", @@ -4377,6 +4385,7 @@ "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -5092,6 +5101,7 @@ "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -5254,6 +5264,7 @@ "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.18.tgz", "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==", "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.18", "@vue/compiler-sfc": "3.5.18", @@ -5365,6 +5376,7 @@ "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==", "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.", "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-sfc": "2.7.16", "csstype": "^3.1.0" diff --git a/auts_new_mobile.client/src/pages/layout/index.vue b/auts_new_mobile.client/src/pages/layout/index.vue index acde932..c82adde 100644 --- a/auts_new_mobile.client/src/pages/layout/index.vue +++ b/auts_new_mobile.client/src/pages/layout/index.vue @@ -176,7 +176,7 @@ } - const disDbDrop = ref(true) + const disDbDrop = ref(false) // 数据库变更处理 @@ -292,16 +292,24 @@ } $http.post('DataHandle/R_InfoCommon', requestBody) .then(rs => { - //console.log(rs) + console.log(rs) if (rs.data.isok) { product.options = [] + // 从localStorage获取auths权限列表 + const authsJson = localStorage.getItem('auths') + const auths = authsJson ? JSON.parse(authsJson) : [] + console.log(auths) rs.data.response.forEach(item => { if (item.Database.slice(0, 3) == "uts" && item.Database != "uts_manage") { - let dbname = { - label: item.Database, - value: item.Database + // 检查用户是否有该数据库的权限(大小写不敏感) + const hasPermission = auths.some(auth => auth.databaseName.toLowerCase() === item.Database.toLowerCase()) + if (hasPermission) { + let dbname = { + label: item.Database, + value: item.Database + } + product.options.push(dbname) } - product.options.push(dbname) } }) } else { @@ -329,7 +337,7 @@ if (localStorage.getItem('selectdb')) { product.value = localStorage.getItem('selectdb') } else { - product.value = "uts_blw" + product.value = "" localStorage.setItem('selectdb', product.value) } diff --git a/auts_new_mobile.client/src/pages/login/index.vue b/auts_new_mobile.client/src/pages/login/index.vue index 0299137..8c657de 100644 --- a/auts_new_mobile.client/src/pages/login/index.vue +++ b/auts_new_mobile.client/src/pages/login/index.vue @@ -136,7 +136,7 @@ direction: 'column', message: '登录成功!', });*/ - + } else { Toast(rs.data.message) } @@ -199,7 +199,7 @@ // 绘制噪点 ctx.fillStyle = isDarkMode.value ? '#FFFFFF' : '#000000'; - for (let i = 0; i < 33; i++) { + for (let i = 0; i < 33; i++) { const x = Math.random() * canvas.width; const y = Math.random() * canvas.height; ctx.beginPath(); @@ -231,7 +231,7 @@ // 添加5-10条干扰条纹 ctx.strokeStyle = '#6F4A2F'; const bug = Math.floor(Math.random() * 6) + 3 - for (let i = 0; i < bug; i++) { + for (let i = 0; i < bug; i++) { ctx.beginPath(); ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height); ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height); @@ -286,6 +286,7 @@ // 存储token localStorage.setItem('token', rs.data.response.accessToken) localStorage.setItem('username', rs.data.response.userName) + localStorage.setItem('auths', JSON.stringify(rs.data.response.auths)) localStorage.setItem('login', true) localStorage.setItem('uid', rs.data.response.id) diff --git a/auts_new_mobile.client/src/pages/testlog/index.vue b/auts_new_mobile.client/src/pages/testlog/index.vue index 416ebdf..afade9d 100644 --- a/auts_new_mobile.client/src/pages/testlog/index.vue +++ b/auts_new_mobile.client/src/pages/testlog/index.vue @@ -190,6 +190,16 @@
+ + + :style="scope.row.TestResult === 'True' ? 'color: #336600' : 'color: #990000'" size="small" text> {{ scope.row.DUT_SN }} @@ -247,10 +257,10 @@ @@ -275,42 +285,42 @@ @row-click="showDetailDialog"> @@ -329,7 +339,7 @@ - {{ value }} + {{ value }}
@@ -380,6 +390,8 @@ equipment: null, // 设备 result: '', // 结果 failSteps: [], // 失败步骤 + dutSnLike: '', // DUT_SN模糊筛选(支持逗号分隔) + failstepsLike: '', // Failsteps模糊筛选(支持逗号分隔) distinct: 'None', // 去重方式,默认None(全部) startDate: dayjs().format('YYYY-MM-DD'), // 开始日期 endDate: dayjs().format('YYYY-MM-DD') // 结束日期 @@ -522,7 +534,7 @@ } const rs = await $http.post('DataHandle/R_InfoCommon', rqs); - //console.log(rs); + console.log(rs); if (rs.data.isok) { machineModels.value = rs.data.response; @@ -675,7 +687,7 @@ if (rs.data.isok) { availableParams.value = rs.data.response; - await nextTick(); + await nextTick(); //console.log(availableParams.value) } else { Toast.error(rs.data.message); @@ -704,11 +716,17 @@ const rs = await $http.post('TestLogs/GetLogListBySN', JSON.stringify(rqs)) //console.log(rs.data) if (rs.data.isok) { - snLogs.value = rs.data.response.map(log => ({ + // 先按原始 startTime 降序,然后格式化时间并设置到 snLogs + const rawLogs = Array.isArray(rs.data.response) ? rs.data.response : []; + rawLogs.sort((a, b) => { + const aTime = a.startTime ? dayjs(a.startTime).valueOf() : 0; + const bTime = b.startTime ? dayjs(b.startTime).valueOf() : 0; + return bTime - aTime; + }); + snLogs.value = rawLogs.map(log => ({ ...log, startTime: log.startTime ? formatDateTime(log.startTime) : '', - })) - //console.log(snLogs.value) + })); snDialogVisible.value = true } else { Toast.error(rs.data.message) @@ -797,6 +815,8 @@ queryForm.equipment = null; queryForm.result = null; queryForm.failSteps = null; + queryForm.dutSnLike = ''; + queryForm.failstepsLike = ''; queryForm.startDate = ''; queryForm.endDate = ''; @@ -822,6 +842,8 @@ EndTime: queryForm.endDate, TestResult: queryForm.result || '', FailSteps: queryForm.failSteps || [], + DutSnLike: queryForm.dutSnLike || '', + FailstepsLike: queryForm.failstepsLike || '', ColumnList: selectedParams.value.length > 0 ? ',' + selectedParams.value.join(',') : '', @@ -838,11 +860,16 @@ } firstPassCount.value = rs.data.passCount; passRate.value = rs.data.passRate; - tableData.value = rs.data.response.map(item => { - return { - ...item, - CreationTime: formatDateTime(item.CreationTime) - } + tableData.value = rs.data.response.map(item => ({ + ...item, + CreationTime: formatDateTime(item.CreationTime) + })); + + // 保证前端按 StartTime 做降序排序(最新时间在前) + tableData.value.sort((a, b) => { + const aTime = a.StartTime ? dayjs(a.StartTime).valueOf() : 0; + const bTime = b.StartTime ? dayjs(b.StartTime).valueOf() : 0; + return bTime - aTime; }); //console.log(tableData.value) total.value = rs.data.total; // 设置总记录数 @@ -1010,6 +1037,8 @@ queryForm.equipment = settings.queryForm.equipment; queryForm.result = settings.queryForm.result; queryForm.failSteps = settings.queryForm.failSteps; + queryForm.dutSnLike = settings.queryForm.dutSnLike || ''; + queryForm.failstepsLike = settings.queryForm.failstepsLike || ''; queryForm.distinct = settings.queryForm.distinct || 'None'; // 恢复去重方式,默认None // 加载辅助数据 @@ -1041,6 +1070,8 @@ equipment: queryForm.equipment, result: queryForm.result, failSteps: queryForm.failSteps, + dutSnLike: queryForm.dutSnLike, + failstepsLike: queryForm.failstepsLike, distinct: queryForm.distinct }, selectedParams: selectedParams.value, @@ -1190,6 +1221,10 @@ min-width: 200px; } + .action-input { + width: 170px; + } + .collapse-panel { margin-bottom: 1px; border: none; @@ -1313,10 +1348,10 @@ } .action-btn { - flex-shrink: 0; - padding: 4px 6px; - font-size: 12px; - white-space: nowrap; + flex-shrink: 0; + padding: 4px 6px; + font-size: 12px; + white-space: nowrap; } } diff --git a/docs/寻址与索引.md b/docs/寻址与索引.md new file mode 100644 index 0000000..b25dbc6 --- /dev/null +++ b/docs/寻址与索引.md @@ -0,0 +1,131 @@ +# AUTS 寻址与索引(前后端快速定位) + +> 目标:后续同步修改前后端时,能快速定位“页面/路由 → 调用的 API → 后端 Controller/Action → 鉴权/配置”。 + +## 1. 仓库组成 + +- 后端:ASP.NET Core Web API(项目:WebAPIServer) +- PC 前端:Vue3 + TypeScript(目录:auts_new.client) +- 移动端前端:Vue3(目录:auts_new_mobile.client) + +## 2. 后端寻址(WebAPIServer) + +### 2.1 入口与全局中间件 + +- 入口:WebAPIServer/Program.cs +- 路由映射:`app.MapControllers()` +- CORS:策略名 `Vue3`,当前配置为 `AllowAnyOrigin/AnyHeader/AnyMethod` +- 鉴权:JWT Bearer(`AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(...)`) + +### 2.2 路由约定 + +所有 Controller 都使用: + +- 路由模板:`api/[controller]/[action]` +- 因此实际 URL 形如:`/api/Login/Login`、`/api/TestLogs/GetTestSummaryTable` + +### 2.3 Token 与鉴权要点 + +- 需要鉴权的 Action 会标注 `[Authorize()]` +- JWT 配置来自:WebAPIServer/appsettings.json(`JwT:SecretKey/Issuer/Audience`) +- 登录成功后后端返回 `AccessToken`(见 `LoginController.Login`) + +### 2.4 API 索引(按 Controller) + +#### LoginController(/api/Login/{action}) + +- `POST Login`:登录,返回 `ResLoginData`(含 `AccessToken/IsAdmin/ID/UserName/Auths`) +- `POST TokenXuQi`:续期(需 `[Authorize]`) +- `POST LogRecord`:记录登录信息(需 `[Authorize]`) +- `GET GetCaptcha`:验证码(当前返回固定 `CaptchaText = "1111"` + base64 图) + +#### TestLogsController(/api/TestLogs/{action}) + +- `POST GetLogListBySN`:按 SN 查询多步骤测试日志(需 `[Authorize]`) +- `POST GetTestSummaryTableExcel`:导出测试总表 Excel(需 `[Authorize]`) +- `POST GetTestSummaryTable`:分页查询测试总表 + 统计 passCount/passRate + +#### DataHandleController(/api/DataHandle/{action}) + +- `POST R_InfoCommon`:通用查询(带 MemoryCache,需 `[Authorize]`) +- `POST CUD_InfoCommon`:通用增删改(需 `[Authorize]`) +- `POST NNN`:简单 hello(无鉴权) +- `POST III`:看起来是实验/未完成接口(返回 null) + +#### UsersController(/api/Users/{action}) + +(均需 `[Authorize]`) + +- `POST GetUserInfo`:获取用户信息(All/Single) +- `POST AddUser`:新增用户 +- `POST EditUser`:修改用户 +- `POST DelUser`:删除(逻辑删除 IsValid=false) +- `POST ResetPassWord`:重置密码为指定明文(服务端双 MD5) +- `POST NewPassWord`:重置密码为 123456(服务端双 MD5) + +#### CompanyController(/api/Company/{action}) + +- `POST GetCompanyInfo`:获取公司信息(需 `[Authorize]`) + +#### ValuesController(/api/Values/{action}) + +- `GET GetAll_WebApiMethod`:从 `StaticData.scope` 取变量 +- `POST UpdateData`:触发 `StaticData.GetWebAPIMethod()` 刷新 + +> 备注:Controllers 目录里还有 `WeatherForecastController`、`TestLogsController` 等;如需要我可以把所有 action 完整拉平为表格(含 HttpVerb)。 + +## 3. 移动端前端寻址(auts_new_mobile.client) + +### 3.1 路由表 + +路由文件:auts_new_mobile.client/src/router/index.js + +- `/`:按 localStorage 是否有 `token` 动态跳转到 `/home` 或 `/login` +- `/login`:登录 +- `/home`:主页(requiresAuth) +- `/user`:个人中心(requiresAuth) +- `/usermanage`:用户管理(requiresAuth,且用户名必须是 Admin 或 MomoWen) +- `/loginlog`:用户日志(requiresAuth) +- `/dbmanage`:数据库管理(requiresAuth) +- `/inputcolorbox`:数据导入(requiresAuth) +- `/stationlog`:站位日志/数据导入(requiresAuth) +- `/testlog`:测试数据(requiresAuth) + +### 3.2 鉴权守卫 + +- 路由 meta `requiresAuth` + localStorage `token` +- 特权页面:`/usermanage`(以及守卫中出现的 `/scopemanage`,但移动端路由表里目前未定义该 path) + +### 3.3 当前编辑页面定位 + +- 当前页:auts_new_mobile.client/src/pages/testlog/index.vue +- 该页的后端关联点大概率在 `TestLogsController`(如 GetTestSummaryTable / GetLogListBySN / 导出 Excel) + +## 4. PC 端前端寻址(auts_new.client) + +### 4.1 路由聚合方式 + +路由入口:auts_new.client/src/router/index.ts + +- 从 `src/router/modules/**` 动态导入模块路由 +- 默认路由:`/login` 与 `/` → `/dashboard/base` + +### 4.2 已识别的模块路由 + +- 仪表盘:首页/统计报表 + - `/dashboard/base`:首页 + - `/dashboard/detail`:统计报表 +- 用户中心: + - `/permission/users`:用户管理 + - `/permission/character`:权限管理 + - `/permission/user`:个人中心 + +> 注意:PC 端部分模块(如 result/user 模块)目前在源码中被注释掉,实际生效以 modules 目录内容为准。 + +## 5. 后续同步修改前后端的推荐对齐点(准备清单) + +- Token:前端存储 key(当前移动端用 localStorage `token/username`)应与后端返回字段 `AccessToken` 映射一致 +- API BaseUrl:前端 axios 基础配置位置(移动端通常在 `src/axios.js`,PC 端在 `src/api`/请求封装) +- 鉴权 header:后端使用标准 Bearer 头即可(`Authorization: Bearer `),如前端用了自定义 `token` 头需要对齐 +- TestLog 页面:优先确认它调用的具体 action(GetTestSummaryTable / ExportExcel / GetLogListBySN)后再做字段联调 +