feat: 商品详情页、XLS导入修复、分页选择器、导出功能
后端: - 新增 product_images 表,支持每商品最多5张图(服务端压缩至1200px/JPEG85%) - products 表新增 public_id(UUID)、description 字段 - 新增商品详情接口、二维码接口、公开商品接口(无鉴权) - 修复 XLS 导入:OLE2 magic bytes 检测 + 临时文件解析,兼容 extrame/xls - 修复商品/名称/系列/规格三张表导入数据为0(LastCol()=0 bug) - 所有导入接口返回 total/imported/skipped 统计 - config 新增 StorageConfig,支持 STORAGE_* 环境变量覆盖 - 种子数据修复:products 补 public_id、新增 product_images TRUNCATE、schema.sql 表名修正 前端: - 商品详情页:图片上传/删除、描述内联编辑、二维码弹窗、公开链接复制 - 公开商品页:无鉴权路由 /product/:public_id,Flutter Web SPA - 商品详情列表(批次追踪)商品名超链接跳转详情页 - 导航「商品管理」改名「商品详情」 - 所有列表表格新增每页条数选择(10/20/50/100) - 表格列头内嵌筛选(FilterableColumnHeader) - 导出 Excel 功能(入库/出库/库存/财务/批次/往来单位) - 网络恢复自动刷新 + 离线缓存展示 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+76
-16
@@ -6,10 +6,13 @@ fi
|
||||
# dev.sh — 本地开发环境管理
|
||||
#
|
||||
# 启动:
|
||||
# sh scripts/dev.sh run # 启动前后端(后端无改动则跳过重启)
|
||||
# sh scripts/dev.sh run # 启动前后端(macOS 桌面端)
|
||||
# sh scripts/dev.sh run --force # 强制重启后端
|
||||
# sh scripts/dev.sh run --web # 启动前后端(Chrome Web 端)
|
||||
# sh scripts/dev.sh run --all # 启动前后端(macOS + Web 同时)
|
||||
# sh scripts/dev.sh --backend-only # 仅启动后端
|
||||
# sh scripts/dev.sh --frontend-only # 仅启动前端
|
||||
# sh scripts/dev.sh --frontend-only # 仅启动前端(macOS)
|
||||
# sh scripts/dev.sh --web-only # 仅启动前端(Web)
|
||||
#
|
||||
# 数据库:
|
||||
# sh scripts/dev.sh seed H001 # 清空并写入 H001 测试数据(SQL 文件)
|
||||
@@ -27,6 +30,8 @@ LOG_DIR="$ROOT/scripts/.logs"
|
||||
BACKEND_LOG="$LOG_DIR/backend.log"
|
||||
BACKEND_PID_FILE="$LOG_DIR/backend.pid"
|
||||
BACKEND_HASH_FILE="$LOG_DIR/backend.hash"
|
||||
WEB_LOG="$LOG_DIR/web.log"
|
||||
WEB_PID_FILE="$LOG_DIR/web.pid"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
@@ -86,11 +91,16 @@ error() { echo -e "${RED}[dev]${NC} $*"; }
|
||||
COMMAND="${1:-run}"
|
||||
FORCE=false
|
||||
RUN_BACKEND=true
|
||||
RUN_FRONTEND=true
|
||||
RUN_FRONTEND=true # macOS
|
||||
RUN_WEB=false
|
||||
|
||||
case "$COMMAND" in
|
||||
run)
|
||||
[ "${2:-}" = "--force" ] && FORCE=true
|
||||
case "${2:-}" in
|
||||
--force) FORCE=true ;;
|
||||
--web) RUN_FRONTEND=false; RUN_WEB=true ;;
|
||||
--all) RUN_WEB=true ;;
|
||||
esac
|
||||
;;
|
||||
seed)
|
||||
cmd_seed "${2:-}"
|
||||
@@ -117,14 +127,23 @@ case "$COMMAND" in
|
||||
COMMAND=run
|
||||
RUN_BACKEND=false
|
||||
;;
|
||||
--web-only)
|
||||
COMMAND=run
|
||||
RUN_BACKEND=false
|
||||
RUN_FRONTEND=false
|
||||
RUN_WEB=true
|
||||
;;
|
||||
stop)
|
||||
# 优先用 PID 文件
|
||||
if [ -f "$BACKEND_PID_FILE" ]; then
|
||||
pid=$(cat "$BACKEND_PID_FILE")
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
rm -f "$BACKEND_PID_FILE"
|
||||
fi
|
||||
# 无论如何,再用 lsof 兜底清理端口(只杀监听 8080 的进程,不杀连接客户端)
|
||||
if [ -f "$WEB_PID_FILE" ]; then
|
||||
pid=$(cat "$WEB_PID_FILE")
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
rm -f "$WEB_PID_FILE"
|
||||
fi
|
||||
lsof -ti TCP:8080 -s TCP:LISTEN 2>/dev/null | xargs kill -9 2>/dev/null || true
|
||||
sleep 0.5
|
||||
if lsof -ti TCP:8080 -s TCP:LISTEN >/dev/null 2>&1; then
|
||||
@@ -136,7 +155,7 @@ case "$COMMAND" in
|
||||
;;
|
||||
*)
|
||||
error "未知命令: $COMMAND"
|
||||
echo "用法: sh scripts/dev.sh [run [--force] | seed <shop> | reset | clear | stop | --backend-only | --frontend-only]"
|
||||
echo "用法: sh scripts/dev.sh [run [--force|--web|--all] | seed <shop> | reset | clear | stop | --backend-only | --frontend-only | --web-only]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -145,8 +164,13 @@ esac
|
||||
cleanup() {
|
||||
echo ""
|
||||
info "正在关闭服务..."
|
||||
# 不杀后端:run 模式下后端是持久进程,Ctrl+C 只退出 flutter
|
||||
info "后端继续在后台运行。如需停止:bash scripts/dev.sh stop"
|
||||
if [ -f "$WEB_PID_FILE" ]; then
|
||||
pid=$(cat "$WEB_PID_FILE")
|
||||
kill "$pid" 2>/dev/null || true
|
||||
rm -f "$WEB_PID_FILE"
|
||||
info "Web 服务已停止"
|
||||
fi
|
||||
info "后端继续在后台运行。如需停止:sh scripts/dev.sh stop"
|
||||
exit 0
|
||||
}
|
||||
trap cleanup INT TERM
|
||||
@@ -246,6 +270,25 @@ start_backend() {
|
||||
wait_backend
|
||||
}
|
||||
|
||||
# ── 启动 Web 前端(后台) ─────────────────────────────────
|
||||
start_web_bg() {
|
||||
info "启动 Flutter Web (Chrome,后台)..."
|
||||
flutter run -d chrome --web-port 8081 >>"$WEB_LOG" 2>&1 &
|
||||
local pid=$!
|
||||
echo "$pid" > "$WEB_PID_FILE"
|
||||
# 等待 Chrome 打开(最多 15 秒)
|
||||
local i=0
|
||||
while [ $i -lt 15 ]; do
|
||||
if grep -q "To hot reload\|Launching lib/main.dart" "$WEB_LOG" 2>/dev/null; then
|
||||
success "Web 已启动 → http://localhost:8081 (日志: tail -f $WEB_LOG)"
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
warn "Web 启动较慢,继续等待... (日志: tail -f $WEB_LOG)"
|
||||
}
|
||||
|
||||
# ════════════════════════════════════════════════════════
|
||||
check_deps
|
||||
|
||||
@@ -253,16 +296,33 @@ if [ "$RUN_BACKEND" = true ]; then
|
||||
start_backend
|
||||
fi
|
||||
|
||||
if [ "$RUN_FRONTEND" = true ]; then
|
||||
info "启动 Flutter (macOS)..."
|
||||
cd "$CLIENT_DIR"
|
||||
flutter pub get
|
||||
# --all 模式:先在后台启动 Web,再在前台启动 macOS
|
||||
if [ "$RUN_WEB" = true ] && [ "$RUN_FRONTEND" = true ]; then
|
||||
cd "$CLIENT_DIR" && flutter pub get
|
||||
start_web_bg
|
||||
echo ""
|
||||
success "Flutter 启动中,窗口将自动弹出..."
|
||||
echo -e "${CYAN}提示:关闭 Flutter 窗口或按 q 退出前端,后端继续运行${NC}"
|
||||
echo -e "${CYAN} 再次执行 bash scripts/dev.sh run 可复用已有后端${NC}"
|
||||
success "Flutter macOS 启动中..."
|
||||
echo -e "${CYAN}提示:macOS 窗口 + Chrome 同时运行,Ctrl+C 退出时 Web 同步停止${NC}"
|
||||
echo ""
|
||||
flutter run -d macos
|
||||
|
||||
elif [ "$RUN_WEB" = true ]; then
|
||||
cd "$CLIENT_DIR" && flutter pub get
|
||||
echo ""
|
||||
success "Chrome 启动中,稍候自动打开..."
|
||||
echo -e "${CYAN}提示:按 r 热重载,按 q 退出,后端继续在后台运行${NC}"
|
||||
echo ""
|
||||
flutter run -d chrome --web-port 8081
|
||||
|
||||
elif [ "$RUN_FRONTEND" = true ]; then
|
||||
cd "$CLIENT_DIR" && flutter pub get
|
||||
echo ""
|
||||
success "Flutter macOS 启动中,窗口将自动弹出..."
|
||||
echo -e "${CYAN}提示:按 q 退出前端,后端继续运行${NC}"
|
||||
echo -e "${CYAN} 再次执行 sh scripts/dev.sh run 可复用已有后端${NC}"
|
||||
echo ""
|
||||
flutter run -d macos
|
||||
|
||||
else
|
||||
echo ""
|
||||
success "后端运行中,按 Ctrl+C 退出此脚本(后端仍在后台)"
|
||||
|
||||
Reference in New Issue
Block a user