Commit 599d2940d301a21fc137276c5eaf3db9ae058364
Merge branch 'main' of http://39.98.150.180/wangming/Food-Labeling-Management-Platform
Showing
11 changed files
with
324 additions
and
14 deletions
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateCreateInputVo.cs
| ... | ... | @@ -57,5 +57,11 @@ public class LabelTemplateCreateInputVo |
| 57 | 57 | /// </summary> |
| 58 | 58 | [JsonPropertyName("appliedLocationIds")] |
| 59 | 59 | public List<string> AppliedLocationIds { get; set; } = new(); |
| 60 | + | |
| 61 | + /// <summary> | |
| 62 | + /// 模板与产品/标签类型绑定默认值 | |
| 63 | + /// </summary> | |
| 64 | + [JsonPropertyName("templateProductDefaults")] | |
| 65 | + public List<LabelTemplateProductDefaultDto>? TemplateProductDefaults { get; set; } | |
| 60 | 66 | } |
| 61 | 67 | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs
| ... | ... | @@ -13,6 +13,9 @@ public class LabelTemplateElementDto |
| 13 | 13 | [JsonPropertyName("type")] |
| 14 | 14 | public string ElementType { get; set; } = string.Empty; |
| 15 | 15 | |
| 16 | + [JsonPropertyName("elementName")] | |
| 17 | + public string ElementName { get; set; } = string.Empty; | |
| 18 | + | |
| 16 | 19 | [JsonPropertyName("x")] |
| 17 | 20 | public decimal PosX { get; set; } |
| 18 | 21 | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetOutputDto.cs
| ... | ... | @@ -29,5 +29,7 @@ public class LabelTemplateGetOutputDto |
| 29 | 29 | public List<LabelTemplateElementDto> Elements { get; set; } = new(); |
| 30 | 30 | |
| 31 | 31 | public List<string> AppliedLocationIds { get; set; } = new(); |
| 32 | + | |
| 33 | + public List<LabelTemplateProductDefaultDto> TemplateProductDefaults { get; set; } = new(); | |
| 32 | 34 | } |
| 33 | 35 | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateProductDefaultDto.cs
0 → 100644
| 1 | +using System.Text.Json.Serialization; | |
| 2 | + | |
| 3 | +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate; | |
| 4 | + | |
| 5 | +/// <summary> | |
| 6 | +/// 模板与产品/标签类型绑定后的默认值行 | |
| 7 | +/// </summary> | |
| 8 | +public class LabelTemplateProductDefaultDto | |
| 9 | +{ | |
| 10 | + [JsonPropertyName("productId")] | |
| 11 | + public string ProductId { get; set; } = string.Empty; | |
| 12 | + | |
| 13 | + [JsonPropertyName("labelTypeId")] | |
| 14 | + public string LabelTypeId { get; set; } = string.Empty; | |
| 15 | + | |
| 16 | + /// <summary> | |
| 17 | + /// 默认值JSON(建议结构:{ "el-xxx": "默认值" }) | |
| 18 | + /// </summary> | |
| 19 | + [JsonPropertyName("defaultValues")] | |
| 20 | + public object? DefaultValues { get; set; } | |
| 21 | + | |
| 22 | + [JsonPropertyName("orderNum")] | |
| 23 | + public int OrderNum { get; set; } | |
| 24 | +} | |
| 25 | + | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPreviewDto.cs
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 1 | 3 | using FoodLabeling.Application.Contracts.Dtos.Label; |
| 2 | 4 | |
| 3 | 5 | namespace FoodLabeling.Application.Contracts.Dtos.UsAppLabeling; |
| ... | ... | @@ -7,6 +9,8 @@ namespace FoodLabeling.Application.Contracts.Dtos.UsAppLabeling; |
| 7 | 9 | /// </summary> |
| 8 | 10 | public class UsAppLabelPreviewDto |
| 9 | 11 | { |
| 12 | + public string LabelId { get; set; } = string.Empty; | |
| 13 | + | |
| 10 | 14 | public string LocationId { get; set; } = string.Empty; |
| 11 | 15 | |
| 12 | 16 | public string LabelCode { get; set; } = string.Empty; |
| ... | ... | @@ -23,6 +27,8 @@ public class UsAppLabelPreviewDto |
| 23 | 27 | |
| 24 | 28 | public string? LabelCategoryName { get; set; } |
| 25 | 29 | |
| 30 | + public DateTime? LabelLastEdited { get; set; } | |
| 31 | + | |
| 26 | 32 | /// <summary> |
| 27 | 33 | /// 预览图(base64 png,可空;若为空,客户端可用 Template 自行渲染) |
| 28 | 34 | /// </summary> |
| ... | ... | @@ -32,5 +38,11 @@ public class UsAppLabelPreviewDto |
| 32 | 38 | /// 预览模板结构(与 LabelCanvas/LabelPreviewOnly 结构尽量一致) |
| 33 | 39 | /// </summary> |
| 34 | 40 | public LabelTemplatePreviewDto Template { get; set; } = new(); |
| 41 | + | |
| 42 | + /// <summary> | |
| 43 | + /// 当前预览上下文(模板+产品+标签类型)命中的默认值配置。 | |
| 44 | + /// 数据来源:fl_label_template_product_default.DefaultValuesJson | |
| 45 | + /// </summary> | |
| 46 | + public Dictionary<string, object?>? TemplateProductDefaultValues { get; set; } | |
| 35 | 47 | } |
| 36 | 48 | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateElementDbEntity.cs
| ... | ... | @@ -14,6 +14,9 @@ public class FlLabelTemplateElementDbEntity |
| 14 | 14 | |
| 15 | 15 | public string ElementType { get; set; } = string.Empty; |
| 16 | 16 | |
| 17 | + [SugarColumn(ColumnName = "ElementName")] | |
| 18 | + public string ElementName { get; set; } = string.Empty; | |
| 19 | + | |
| 17 | 20 | public decimal PosX { get; set; } |
| 18 | 21 | |
| 19 | 22 | public decimal PosY { get; set; } | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateProductDefaultDbEntity.cs
0 → 100644
| 1 | +using SqlSugar; | |
| 2 | + | |
| 3 | +namespace FoodLabeling.Application.Services.DbModels; | |
| 4 | + | |
| 5 | +[SugarTable("fl_label_template_product_default")] | |
| 6 | +public class FlLabelTemplateProductDefaultDbEntity | |
| 7 | +{ | |
| 8 | + [SugarColumn(IsPrimaryKey = true)] | |
| 9 | + public string Id { get; set; } = string.Empty; | |
| 10 | + | |
| 11 | + public string TemplateId { get; set; } = string.Empty; | |
| 12 | + | |
| 13 | + public string ProductId { get; set; } = string.Empty; | |
| 14 | + | |
| 15 | + public string LabelTypeId { get; set; } = string.Empty; | |
| 16 | + | |
| 17 | + /// <summary> | |
| 18 | + /// 默认值JSON(字符串保存) | |
| 19 | + /// </summary> | |
| 20 | + public string? DefaultValuesJson { get; set; } | |
| 21 | + | |
| 22 | + public int OrderNum { get; set; } | |
| 23 | +} | |
| 24 | + | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTemplateAppService.cs
| ... | ... | @@ -157,6 +157,7 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ |
| 157 | 157 | { |
| 158 | 158 | Id = e.ElementKey, |
| 159 | 159 | ElementType = e.ElementType, |
| 160 | + ElementName = e.ElementName, | |
| 160 | 161 | PosX = e.PosX, |
| 161 | 162 | PosY = e.PosY, |
| 162 | 163 | Width = e.Width, |
| ... | ... | @@ -180,6 +181,28 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ |
| 180 | 181 | .Select(x => x.LocationId) |
| 181 | 182 | .ToListAsync(); |
| 182 | 183 | |
| 184 | + var defaultRows = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateProductDefaultDbEntity>() | |
| 185 | + .Where(x => x.TemplateId == template.Id) | |
| 186 | + .OrderBy(x => x.OrderNum) | |
| 187 | + .ToListAsync(); | |
| 188 | + | |
| 189 | + var productDefaults = defaultRows.Select(x => | |
| 190 | + { | |
| 191 | + object? defaults = null; | |
| 192 | + if (!string.IsNullOrWhiteSpace(x.DefaultValuesJson)) | |
| 193 | + { | |
| 194 | + defaults = JsonSerializer.Deserialize<object>(x.DefaultValuesJson); | |
| 195 | + } | |
| 196 | + | |
| 197 | + return new LabelTemplateProductDefaultDto | |
| 198 | + { | |
| 199 | + ProductId = x.ProductId, | |
| 200 | + LabelTypeId = x.LabelTypeId, | |
| 201 | + DefaultValues = defaults, | |
| 202 | + OrderNum = x.OrderNum | |
| 203 | + }; | |
| 204 | + }).ToList(); | |
| 205 | + | |
| 183 | 206 | return new LabelTemplateGetOutputDto |
| 184 | 207 | { |
| 185 | 208 | Id = template.TemplateCode, |
| ... | ... | @@ -195,7 +218,8 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ |
| 195 | 218 | VersionNo = template.VersionNo, |
| 196 | 219 | State = template.State, |
| 197 | 220 | Elements = MapElements(), |
| 198 | - AppliedLocationIds = appliedLocationIds | |
| 221 | + AppliedLocationIds = appliedLocationIds, | |
| 222 | + TemplateProductDefaults = productDefaults | |
| 199 | 223 | }; |
| 200 | 224 | } |
| 201 | 225 | |
| ... | ... | @@ -246,7 +270,12 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ |
| 246 | 270 | |
| 247 | 271 | await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync(); |
| 248 | 272 | |
| 249 | - await RebuildTemplateElementsAndLocationsAsync(entity.Id, input.Elements, entity.AppliedLocationType, input.AppliedLocationIds); | |
| 273 | + await RebuildTemplateElementsLocationsAndDefaultsAsync( | |
| 274 | + entity.Id, | |
| 275 | + input.Elements, | |
| 276 | + entity.AppliedLocationType, | |
| 277 | + input.AppliedLocationIds, | |
| 278 | + new List<LabelTemplateProductDefaultDto>()); | |
| 250 | 279 | |
| 251 | 280 | return await GetAsync(code); |
| 252 | 281 | } |
| ... | ... | @@ -292,7 +321,12 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ |
| 292 | 321 | |
| 293 | 322 | await _dbContext.SqlSugarClient.Updateable(template).ExecuteCommandAsync(); |
| 294 | 323 | |
| 295 | - await RebuildTemplateElementsAndLocationsAsync(template.Id, input.Elements, template.AppliedLocationType, input.AppliedLocationIds); | |
| 324 | + await RebuildTemplateElementsLocationsAndDefaultsAsync( | |
| 325 | + template.Id, | |
| 326 | + input.Elements, | |
| 327 | + template.AppliedLocationType, | |
| 328 | + input.AppliedLocationIds, | |
| 329 | + input.TemplateProductDefaults); | |
| 296 | 330 | |
| 297 | 331 | return await GetAsync(template.TemplateCode); |
| 298 | 332 | } |
| ... | ... | @@ -327,13 +361,17 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ |
| 327 | 361 | await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateLocationDbEntity>() |
| 328 | 362 | .Where(x => x.TemplateId == template.Id) |
| 329 | 363 | .ExecuteCommandAsync(); |
| 364 | + await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateProductDefaultDbEntity>() | |
| 365 | + .Where(x => x.TemplateId == template.Id) | |
| 366 | + .ExecuteCommandAsync(); | |
| 330 | 367 | } |
| 331 | 368 | |
| 332 | - private async Task RebuildTemplateElementsAndLocationsAsync( | |
| 369 | + private async Task RebuildTemplateElementsLocationsAndDefaultsAsync( | |
| 333 | 370 | string templateDbId, |
| 334 | 371 | List<LabelTemplateElementDto> elements, |
| 335 | 372 | string appliedLocationType, |
| 336 | - List<string> appliedLocationIds) | |
| 373 | + List<string> appliedLocationIds, | |
| 374 | + List<LabelTemplateProductDefaultDto>? templateProductDefaults) | |
| 337 | 375 | { |
| 338 | 376 | // elements 重建 |
| 339 | 377 | await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateElementDbEntity>() |
| ... | ... | @@ -342,9 +380,9 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ |
| 342 | 380 | |
| 343 | 381 | if (elements is not null && elements.Count > 0) |
| 344 | 382 | { |
| 345 | - var now = DateTime.Now; | |
| 346 | 383 | var rows = elements.Select(e => |
| 347 | 384 | { |
| 385 | + var elementName = EnsureElementName(e.ElementName); | |
| 348 | 386 | object? cfg = e.ConfigJson; |
| 349 | 387 | var configJson = cfg == null ? null : JsonSerializer.Serialize(cfg); |
| 350 | 388 | return new FlLabelTemplateElementDbEntity |
| ... | ... | @@ -353,6 +391,7 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ |
| 353 | 391 | TemplateId = templateDbId, |
| 354 | 392 | ElementKey = e.Id, |
| 355 | 393 | ElementType = e.ElementType, |
| 394 | + ElementName = elementName, | |
| 356 | 395 | PosX = e.PosX, |
| 357 | 396 | PosY = e.PosY, |
| 358 | 397 | Width = e.Width, |
| ... | ... | @@ -395,6 +434,73 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ |
| 395 | 434 | |
| 396 | 435 | await _dbContext.SqlSugarClient.Insertable(locRows).ExecuteCommandAsync(); |
| 397 | 436 | } |
| 437 | + | |
| 438 | + // 模板-产品-标签类型默认值:仅在显式传入时重建,避免普通编辑误清空 | |
| 439 | + if (templateProductDefaults is not null) | |
| 440 | + { | |
| 441 | + var duplicateCheckSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | |
| 442 | + foreach (var row in templateProductDefaults) | |
| 443 | + { | |
| 444 | + var productId = row.ProductId?.Trim(); | |
| 445 | + var labelTypeId = row.LabelTypeId?.Trim(); | |
| 446 | + if (string.IsNullOrWhiteSpace(productId) || string.IsNullOrWhiteSpace(labelTypeId)) | |
| 447 | + { | |
| 448 | + continue; | |
| 449 | + } | |
| 450 | + | |
| 451 | + var key = $"{productId}::{labelTypeId}"; | |
| 452 | + if (!duplicateCheckSet.Add(key)) | |
| 453 | + { | |
| 454 | + throw new UserFriendlyException($"模板默认值绑定重复:产品[{productId}]与标签类型[{labelTypeId}]只能存在一条"); | |
| 455 | + } | |
| 456 | + } | |
| 457 | + | |
| 458 | + await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateProductDefaultDbEntity>() | |
| 459 | + .Where(x => x.TemplateId == templateDbId) | |
| 460 | + .ExecuteCommandAsync(); | |
| 461 | + | |
| 462 | + if (templateProductDefaults.Count > 0) | |
| 463 | + { | |
| 464 | + var rows = templateProductDefaults.Select((x, idx) => | |
| 465 | + { | |
| 466 | + var productId = x.ProductId?.Trim(); | |
| 467 | + var labelTypeId = x.LabelTypeId?.Trim(); | |
| 468 | + if (string.IsNullOrWhiteSpace(productId)) | |
| 469 | + { | |
| 470 | + throw new UserFriendlyException("模板默认值绑定中,产品Id不能为空"); | |
| 471 | + } | |
| 472 | + | |
| 473 | + if (string.IsNullOrWhiteSpace(labelTypeId)) | |
| 474 | + { | |
| 475 | + throw new UserFriendlyException("模板默认值绑定中,标签类型Id不能为空"); | |
| 476 | + } | |
| 477 | + | |
| 478 | + var json = x.DefaultValues is null ? null : JsonSerializer.Serialize(x.DefaultValues); | |
| 479 | + return new FlLabelTemplateProductDefaultDbEntity | |
| 480 | + { | |
| 481 | + Id = _guidGenerator.Create().ToString(), | |
| 482 | + TemplateId = templateDbId, | |
| 483 | + ProductId = productId, | |
| 484 | + LabelTypeId = labelTypeId, | |
| 485 | + DefaultValuesJson = json, | |
| 486 | + OrderNum = x.OrderNum <= 0 ? idx + 1 : x.OrderNum | |
| 487 | + }; | |
| 488 | + }).ToList(); | |
| 489 | + | |
| 490 | + await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync(); | |
| 491 | + } | |
| 492 | + } | |
| 493 | + } | |
| 494 | + | |
| 495 | + private static string EnsureElementName(string? elementName) | |
| 496 | + { | |
| 497 | + var normalizedName = elementName?.Trim(); | |
| 498 | + if (string.IsNullOrWhiteSpace(normalizedName)) | |
| 499 | + { | |
| 500 | + throw new UserFriendlyException("组件名字不能为空"); | |
| 501 | + } | |
| 502 | + | |
| 503 | + return normalizedName; | |
| 398 | 504 | } |
| 399 | 505 | |
| 400 | 506 | private static PagedResultWithPageDto<T> BuildPagedResult<T>(int skipCount, int maxResultCount, int total, List<T> items) | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs
| ... | ... | @@ -238,8 +238,13 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ |
| 238 | 238 | .Where((l, c, t, tpl) => l.LabelCode == labelCode) |
| 239 | 239 | .Select((l, c, t, tpl) => new |
| 240 | 240 | { |
| 241 | + l.Id, | |
| 241 | 242 | l.LabelCode, |
| 242 | 243 | l.LocationId, |
| 244 | + l.LabelTypeId, | |
| 245 | + l.TemplateId, | |
| 246 | + l.LastModificationTime, | |
| 247 | + l.CreationTime, | |
| 243 | 248 | LabelCategoryName = c.CategoryName, |
| 244 | 249 | TypeName = t.TypeName, |
| 245 | 250 | TemplateCode = tpl.TemplateCode, |
| ... | ... | @@ -259,21 +264,46 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ |
| 259 | 264 | throw new UserFriendlyException("该标签不属于当前门店"); |
| 260 | 265 | } |
| 261 | 266 | |
| 267 | + var previewProductId = await ResolvePreviewProductIdAsync(labelRow.Id, input.ProductId); | |
| 268 | + | |
| 262 | 269 | var template = await _labelAppService.PreviewAsync(new LabelPreviewResolveInputVo |
| 263 | 270 | { |
| 264 | 271 | LabelCode = labelCode, |
| 265 | - ProductId = input.ProductId?.Trim(), | |
| 272 | + ProductId = previewProductId, | |
| 266 | 273 | BaseTime = input.BaseTime, |
| 267 | 274 | PrintInputJson = input.PrintInputJson?.ToDictionary(x => x.Key, x => (object?)x.Value) |
| 268 | 275 | }); |
| 269 | 276 | |
| 277 | + Dictionary<string, object?>? templateProductDefaultValues = null; | |
| 278 | + if (!string.IsNullOrWhiteSpace(previewProductId)) | |
| 279 | + { | |
| 280 | + var productDefault = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateProductDefaultDbEntity>() | |
| 281 | + .Where(x => x.TemplateId == labelRow.TemplateId) | |
| 282 | + .Where(x => x.ProductId == previewProductId) | |
| 283 | + .Where(x => x.LabelTypeId == labelRow.LabelTypeId) | |
| 284 | + .OrderBy(x => x.OrderNum) | |
| 285 | + .FirstAsync(); | |
| 286 | + | |
| 287 | + if (!string.IsNullOrWhiteSpace(productDefault?.DefaultValuesJson)) | |
| 288 | + { | |
| 289 | + try | |
| 290 | + { | |
| 291 | + templateProductDefaultValues = | |
| 292 | + JsonSerializer.Deserialize<Dictionary<string, object?>>(productDefault.DefaultValuesJson!); | |
| 293 | + } | |
| 294 | + catch | |
| 295 | + { | |
| 296 | + templateProductDefaultValues = null; | |
| 297 | + } | |
| 298 | + } | |
| 299 | + } | |
| 300 | + | |
| 270 | 301 | var productName = string.Empty; |
| 271 | 302 | var productCategoryName = "无"; |
| 272 | - if (!string.IsNullOrWhiteSpace(input.ProductId)) | |
| 303 | + if (!string.IsNullOrWhiteSpace(previewProductId)) | |
| 273 | 304 | { |
| 274 | - var pid = input.ProductId.Trim(); | |
| 275 | 305 | var p = await _dbContext.SqlSugarClient.Queryable<FlProductDbEntity>() |
| 276 | - .FirstAsync(x => !x.IsDeleted && x.State && x.Id == pid); | |
| 306 | + .FirstAsync(x => !x.IsDeleted && x.State && x.Id == previewProductId); | |
| 277 | 307 | if (p is not null) |
| 278 | 308 | { |
| 279 | 309 | productName = p.ProductName ?? string.Empty; |
| ... | ... | @@ -288,6 +318,7 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ |
| 288 | 318 | |
| 289 | 319 | return new UsAppLabelPreviewDto |
| 290 | 320 | { |
| 321 | + LabelId = labelRow.Id, | |
| 291 | 322 | LocationId = locationId, |
| 292 | 323 | LabelCode = labelCode, |
| 293 | 324 | TemplateCode = labelRow.TemplateCode, |
| ... | ... | @@ -296,8 +327,10 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ |
| 296 | 327 | ProductName = string.IsNullOrWhiteSpace(productName) ? null : productName, |
| 297 | 328 | ProductCategoryName = productCategoryName, |
| 298 | 329 | LabelCategoryName = labelRow.LabelCategoryName, |
| 330 | + LabelLastEdited = labelRow.LastModificationTime ?? labelRow.CreationTime, | |
| 299 | 331 | PreviewImageBase64Png = null, |
| 300 | - Template = template | |
| 332 | + Template = template, | |
| 333 | + TemplateProductDefaultValues = templateProductDefaultValues | |
| 301 | 334 | }; |
| 302 | 335 | } |
| 303 | 336 | |
| ... | ... | @@ -525,6 +558,20 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ |
| 525 | 558 | return string.IsNullOrWhiteSpace(s) ? null : s; |
| 526 | 559 | } |
| 527 | 560 | |
| 561 | + private async Task<string?> ResolvePreviewProductIdAsync(string labelId, string? productId) | |
| 562 | + { | |
| 563 | + var resolvedProductId = productId?.Trim(); | |
| 564 | + if (!string.IsNullOrWhiteSpace(resolvedProductId)) | |
| 565 | + { | |
| 566 | + return resolvedProductId; | |
| 567 | + } | |
| 568 | + | |
| 569 | + return await _dbContext.SqlSugarClient.Queryable<FlLabelProductDbEntity>() | |
| 570 | + .Where(x => x.LabelId == labelId) | |
| 571 | + .Select(x => x.ProductId) | |
| 572 | + .FirstAsync(); | |
| 573 | + } | |
| 574 | + | |
| 528 | 575 | private static UsAppLabelTypeNodeDto BuildLabelTypeNode(LabelingTreeRow r) |
| 529 | 576 | { |
| 530 | 577 | return new UsAppLabelTypeNodeDto | ... | ... |
项目相关文档/标签模块接口对接说明.md
| ... | ... | @@ -236,6 +236,9 @@ Swagger 地址: |
| 236 | 236 | 说明: |
| 237 | 237 | - 模板标识入参 `id` 使用 `fl_label_template.TemplateCode`。 |
| 238 | 238 | - 创建/编辑的 Body 字段名对齐你前端 editor JSON(`id/name/appliedLocation/elements/config`)。 |
| 239 | +- 模板组件 `elements[]` 的 `elementName` 为必填(前端传值,后端校验为空会报错)。 | |
| 240 | +- `templateProductDefaults[]` 用于模板内“产品 + 标签类型”绑定默认值(进入模板详情页后的绑定列表)。 | |
| 241 | +- **新增模板时不处理默认值**;默认值仅在后续“产品关联/编辑模板”阶段维护。 | |
| 239 | 242 | |
| 240 | 243 | ### 4.1 分页列表 |
| 241 | 244 | |
| ... | ... | @@ -283,6 +286,7 @@ Swagger 地址: |
| 283 | 286 | "elements": [ |
| 284 | 287 | { |
| 285 | 288 | "id": "el-fixed-title", |
| 289 | + "elementName": "标题文本", | |
| 286 | 290 | "type": "TEXT_STATIC", |
| 287 | 291 | "x": 32, |
| 288 | 292 | "y": 24, |
| ... | ... | @@ -309,6 +313,8 @@ Swagger 地址: |
| 309 | 313 | |
| 310 | 314 | 说明: |
| 311 | 315 | - 当 `appliedLocation=SPECIFIED` 时,`appliedLocationIds` 必须至少选择一个门店。 |
| 316 | +- `elements[].elementName` 必填;为空或空白将返回友好错误:`组件名字不能为空`。 | |
| 317 | +- 新增模板时即使传了 `templateProductDefaults`,后端也不会写入默认值数据。 | |
| 312 | 318 | |
| 313 | 319 | ### 4.4 编辑模板 |
| 314 | 320 | |
| ... | ... | @@ -332,14 +338,84 @@ Swagger 地址: |
| 332 | 338 | "showRuler": true, |
| 333 | 339 | "showGrid": true, |
| 334 | 340 | "state": true, |
| 335 | - "elements": [], | |
| 336 | - "appliedLocationIds": ["11111111-1111-1111-1111-111111111111"] | |
| 341 | + "elements": [ | |
| 342 | + { | |
| 343 | + "id": "el-price", | |
| 344 | + "elementName": "价格文本", | |
| 345 | + "type": "TEXT_PRICE", | |
| 346 | + "x": 40, | |
| 347 | + "y": 120, | |
| 348 | + "width": 140, | |
| 349 | + "height": 28, | |
| 350 | + "rotation": "horizontal", | |
| 351 | + "border": "none", | |
| 352 | + "zIndex": 2, | |
| 353 | + "orderNum": 2, | |
| 354 | + "valueSourceType": "PRINT_INPUT", | |
| 355 | + "inputKey": "price", | |
| 356 | + "isRequiredInput": true, | |
| 357 | + "config": { | |
| 358 | + "text": "", | |
| 359 | + "fontFamily": "Arial", | |
| 360 | + "fontSize": 18, | |
| 361 | + "fontWeight": "bold", | |
| 362 | + "textAlign": "left" | |
| 363 | + } | |
| 364 | + } | |
| 365 | + ], | |
| 366 | + "appliedLocationIds": ["11111111-1111-1111-1111-111111111111"], | |
| 367 | + "templateProductDefaults": [ | |
| 368 | + { | |
| 369 | + "productId": "3a20-xxxx", | |
| 370 | + "labelTypeId": "3a20-yyyy", | |
| 371 | + "defaultValues": { | |
| 372 | + "el-fixed-title": "Chicken", | |
| 373 | + "el-price": "2.00", | |
| 374 | + "el-desc": "23" | |
| 375 | + }, | |
| 376 | + "orderNum": 1 | |
| 377 | + } | |
| 378 | + ] | |
| 337 | 379 | } |
| 338 | 380 | ``` |
| 339 | 381 | |
| 340 | 382 | 版本: |
| 341 | 383 | - `VersionNo` 会在编辑时自动 `+1`。 |
| 342 | 384 | - `elements` 会按传入内容全量重建。 |
| 385 | +- 查询/预览返回的 `elements[]` 同样会带 `elementName` 字段。 | |
| 386 | +- `templateProductDefaults` 在编辑接口中**仅当显式传入时**才会重建(同一模板先删后插)。 | |
| 387 | +- 若编辑时不传 `templateProductDefaults`,后端会保留数据库中原有默认值,不做覆盖。 | |
| 388 | + | |
| 389 | +`templateProductDefaults` 结构说明: | |
| 390 | +- 每一行需传 `productId` 与 `labelTypeId`。 | |
| 391 | +- `defaultValues` 建议使用 `element.id => 默认文本` 结构;页面展示时可结合模板 `elements[].config.text` 作为列头与初始值。 | |
| 392 | + | |
| 393 | +### 4.6 模板与产品默认值关联表(新增) | |
| 394 | + | |
| 395 | +用于存储“模板-产品-标签类型”的默认值,推荐执行以下建表 SQL: | |
| 396 | + | |
| 397 | +```sql | |
| 398 | +CREATE TABLE `fl_label_template_product_default` ( | |
| 399 | + `Id` varchar(36) NOT NULL COMMENT '主键', | |
| 400 | + `TemplateId` varchar(36) NOT NULL COMMENT '模板Id(关联 fl_label_template.Id)', | |
| 401 | + `ProductId` varchar(36) NOT NULL COMMENT '产品Id(关联 fl_product.Id)', | |
| 402 | + `LabelTypeId` varchar(36) NOT NULL COMMENT '标签类型Id(关联 fl_label_type.Id)', | |
| 403 | + `DefaultValuesJson` text NULL COMMENT '默认值JSON(如 elementId=>默认文本)', | |
| 404 | + `OrderNum` int NOT NULL DEFAULT 1 COMMENT '排序', | |
| 405 | + PRIMARY KEY (`Id`), | |
| 406 | + KEY `idx_fl_ltpd_template` (`TemplateId`), | |
| 407 | + KEY `idx_fl_ltpd_product` (`ProductId`), | |
| 408 | + KEY `idx_fl_ltpd_label_type` (`LabelTypeId`), | |
| 409 | + UNIQUE KEY `uk_fl_ltpd_template_product_label_type` (`TemplateId`, `ProductId`, `LabelTypeId`) | |
| 410 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='标签模板-产品默认值关联表'; | |
| 411 | +``` | |
| 412 | + | |
| 413 | +若表已存在,可执行以下 SQL 增加唯一约束: | |
| 414 | + | |
| 415 | +```sql | |
| 416 | +ALTER TABLE `fl_label_template_product_default` | |
| 417 | +ADD UNIQUE KEY `uk_fl_ltpd_template_product_label_type` (`TemplateId`, `ProductId`, `LabelTypeId`); | |
| 418 | +``` | |
| 343 | 419 | |
| 344 | 420 | ### 4.5 删除(逻辑删除) |
| 345 | 421 | |
| ... | ... | @@ -758,7 +834,12 @@ curl -X GET "http://localhost:19001/api/app/us-app-labeling/labeling-tree?locati |
| 758 | 834 | |
| 759 | 835 | - `template`:`LabelTemplatePreviewDto` |
| 760 | 836 | - `width` / `height` / `unit`:模板物理尺寸 |
| 761 | - - `elements[]`:元素数组(对齐前端 editor JSON:`id/type/x/y/width/height/rotation/border/zIndex/orderNum/config`) | |
| 837 | + - `elements[]`:元素数组(对齐前端 editor JSON:`id/elementName/type/x/y/width/height/rotation/border/zIndex/orderNum/config`) | |
| 838 | +- `templateProductDefaultValues`:`object | null` | |
| 839 | + - 来源:`fl_label_template_product_default.DefaultValuesJson` | |
| 840 | + - 命中条件:当前预览上下文的 `templateId + productId + labelTypeId` | |
| 841 | + - 建议结构:`{ "elementId": "默认值" }` | |
| 842 | + - 未命中时返回 `null`(向后兼容) | |
| 762 | 843 | |
| 763 | 844 | `elements[].config` 内常用字段(示例): |
| 764 | 845 | ... | ... |