feat: 公开商品页重设计 + web 上传修复 + 二维码配置

- 公开商品页(/product/:uuid)全面重设计:全宽正方形图片轮播、
  左右滑动切图、点击放大全屏查看、商品参数含描述、页脚贴底
- 修复 Flutter web 文件上传无反应(path→bytes)
- 修复 web 路由空白页(usePathUrlStrategy + 单层 MaterialApp.router)
- 二维码 URL 改为从 STORAGE_PUBLIC_URL 环境变量读取
- 新增 PUBLIC_URL dart-define → AppConfig.publicBaseUrl
- 新增 CI/CD workflows + NAS runner compose 配置
- seed S001 补充商品描述字段

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-04-30 00:31:11 +08:00
parent 393e227de5
commit 655c1d366e
17 changed files with 738 additions and 157 deletions
+58 -9
View File
@@ -28,6 +28,9 @@ var allModels = []any{
&model.License{},
&model.ProductCategory{},
&model.Product{},
&model.ProductNameOption{},
&model.ProductSeriesOption{},
&model.ProductSpecOption{},
&model.Warehouse{},
&model.Partner{},
&model.StockInOrder{},
@@ -50,7 +53,7 @@ var truncateOrder = []string{
"stock_in_items", "stock_in_orders",
"finance_records", "number_rules",
"partners", "warehouses",
"products", "product_categories",
"products", "product_spec_options", "product_series_options", "product_name_options", "product_categories",
"licenses", "users", "shops",
}
@@ -186,14 +189,14 @@ func main() {
remark string
}
prodSeeds := []prodSeed{
{"MT-001", "6901234567890", "飞天茅台 53度 500ml", "茅台", "500ml/瓶", "瓶", "贵州茅台", &catBaijiu.ID, 2350, 2800, 10, "酱香型白酒,53度,飞天系列"},
{"WLY-001", "6902345678901", "五粮液 52度 500ml", "五粮液", "500ml/瓶", "瓶", "宜宾五粮液", &catBaijiu.ID, 950, 1200, 6, "浓香型白酒,52度,普五系列"},
{"YH-001", "6903456789012", "洋河梦之蓝 M6 500ml", "洋河", "500ml/瓶", "瓶", "江苏洋河", &catBaijiu.ID, 560, 680, 6, "浓香型,绵柔苏酒代表"},
{"LZ-001", "6904567890123", "泸州老窖 特曲 500ml", "泸州老窖", "500ml/瓶", "瓶", "泸州老窖", &catBaijiu.ID, 420, 520, 6, "浓香鼻祖,特曲系列"},
{"JNC-001", "6905678901234", "剑南春 水晶剑 500ml", "剑南春", "500ml/瓶", "瓶", "剑南春", &catBaijiu.ID, 390, 480, 6, "浓香型,绵竹名酒"},
{"LJ-001", "6906789012345", "郎酒 红花郎10 500ml", "郎酒", "500ml/瓶", "瓶", "古蔺郎酒", &catBaijiu.ID, 320, 420, 6, "酱香型,赤水河畔酿造"},
{"LF-001", "3760093550058", "拉菲古堡 2018 750ml", "波尔多", "750ml/瓶", "瓶", "Château Lafite", &catImport.ID, 3800, 5200, 3, "波尔多一级名庄,2018年份"},
{"RTM-001", "3021691010008", "人头马 VSOP 700ml", "人头马", "700ml/瓶", "瓶", "Rémy Martin", &catImport.ID, 480, 680, 3, "法国干邑,VSOP级别"},
{"MT-001", "6901234567890", "飞天茅台 53度", "茅台", "500ml/瓶", "瓶", "贵州茅台", &catBaijiu.ID, 2350, 2800, 10, "酱香型白酒,53度,飞天系列"},
{"WLY-001", "6902345678901", "五粮液 52度", "五粮液", "500ml/瓶", "瓶", "宜宾五粮液", &catBaijiu.ID, 950, 1200, 6, "浓香型白酒,52度,普五系列"},
{"YH-001", "6903456789012", "洋河梦之蓝 M6", "洋河", "500ml/瓶", "瓶", "江苏洋河", &catBaijiu.ID, 560, 680, 6, "浓香型,绵柔苏酒代表"},
{"LZ-001", "6904567890123", "泸州老窖 特曲", "泸州老窖", "500ml/瓶", "瓶", "泸州老窖", &catBaijiu.ID, 420, 520, 6, "浓香鼻祖,特曲系列"},
{"JNC-001", "6905678901234", "剑南春 水晶剑", "剑南春", "500ml/瓶", "瓶", "剑南春", &catBaijiu.ID, 390, 480, 6, "浓香型,绵竹名酒"},
{"LJ-001", "6906789012345", "郎酒 红花郎10", "郎酒", "500ml/瓶", "瓶", "古蔺郎酒", &catBaijiu.ID, 320, 420, 6, "酱香型,赤水河畔酿造"},
{"LF-001", "3760093550058", "拉菲古堡 2018", "波尔多", "750ml/瓶", "瓶", "Château Lafite", &catImport.ID, 3800, 5200, 3, "波尔多一级名庄,2018年份"},
{"RTM-001", "3021691010008", "人头马 VSOP", "人头马", "700ml/瓶", "瓶", "Rémy Martin", &catImport.ID, 480, 680, 3, "法国干邑,VSOP级别"},
}
var prods []model.Product
for _, s := range prodSeeds {
@@ -209,6 +212,52 @@ func main() {
prods = append(prods, p)
}
// ═══════════════════════════════════════════════════
// 商品名称/系列/规格选项
// ═══════════════════════════════════════════════════
type dimSeed struct{ code, name string }
for _, s := range []dimSeed{
{"NA001", "飞天茅台 53度"}, {"NA002", "五粮液 52度"}, {"NA003", "洋河梦之蓝 M6"},
{"NA004", "泸州老窖 特曲"}, {"NA005", "剑南春 水晶剑"}, {"NA006", "郎酒 红花郎10"},
{"NA007", "拉菲古堡 2018"}, {"NA008", "人头马 VSOP"},
} {
var opt model.ProductNameOption
if db.Where("shop_id = ? AND name = ?", shop.ID, s.name).First(&opt).Error != nil {
opt = model.ProductNameOption{TenantBase: model.TenantBase{ShopID: shop.ID}, Code: s.code, Name: s.name}
db.Create(&opt)
fmt.Printf("✅ 名称选项:%s %s\n", s.code, s.name)
}
}
for _, s := range []dimSeed{
{"SE001", "茅台"}, {"SE002", "五粮液"}, {"SE003", "洋河"}, {"SE004", "泸州老窖"},
{"SE005", "剑南春"}, {"SE006", "郎酒"}, {"SE007", "波尔多"}, {"SE008", "人头马"},
} {
var opt model.ProductSeriesOption
if db.Where("shop_id = ? AND name = ?", shop.ID, s.name).First(&opt).Error != nil {
opt = model.ProductSeriesOption{TenantBase: model.TenantBase{ShopID: shop.ID}, Code: s.code, Name: s.name}
db.Create(&opt)
fmt.Printf("✅ 系列选项:%s %s\n", s.code, s.name)
}
}
for _, s := range []struct{ code, name string; quantity int }{
{"GG001", "500ml/瓶", 1}, {"GG002", "750ml/瓶", 1}, {"GG003", "700ml/瓶", 1},
} {
var opt model.ProductSpecOption
if db.Where("shop_id = ? AND name = ?", shop.ID, s.name).First(&opt).Error != nil {
opt = model.ProductSpecOption{
TenantBase: model.TenantBase{ShopID: shop.ID},
Code: s.code,
Name: s.name,
Quantity: s.quantity,
}
db.Create(&opt)
fmt.Printf("✅ 规格选项:%s\n", s.name)
}
}
// ═══════════════════════════════════════════════════
// 往来单位
// ═══════════════════════════════════════════════════