From 72f7d504d153f87814f68d32f25a7f046c31b824 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 29 May 2026 08:59:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96pc=E7=89=88=E6=9C=AC=E9=A6=96?= =?UTF-8?q?=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/layouts/AppLayout.vue | 1 - frontend/src/router/publicRoutes.ts | 5 - frontend/src/views/public/HomeView.vue | 1130 +++++++++++++++--------- scripts/dev.sh | 81 +- 4 files changed, 805 insertions(+), 412 deletions(-) diff --git a/frontend/src/layouts/AppLayout.vue b/frontend/src/layouts/AppLayout.vue index dc80192..ecdb307 100644 --- a/frontend/src/layouts/AppLayout.vue +++ b/frontend/src/layouts/AppLayout.vue @@ -20,7 +20,6 @@ const router = useRouter(); const navItems = [ { label: "首页", to: "/", icon: House }, - { label: "租号大厅", to: "/listings", icon: Shop }, { label: "我的订单", to: "/orders", icon: Tickets }, { label: "消息", to: "/messages", icon: ChatDotRound }, { label: "钱包", to: "/wallet", icon: Wallet }, diff --git a/frontend/src/router/publicRoutes.ts b/frontend/src/router/publicRoutes.ts index efcbd1b..7fad07e 100644 --- a/frontend/src/router/publicRoutes.ts +++ b/frontend/src/router/publicRoutes.ts @@ -6,11 +6,6 @@ export const publicRoutes: RouteRecordRaw[] = [ name: 'home', component: () => import('@/views/public/HomeView.vue'), }, - { - path: '/listings', - name: 'listings', - component: () => import('@/views/public/ListingsView.vue'), - }, { path: '/listings/:id', name: 'listing-detail', diff --git a/frontend/src/views/public/HomeView.vue b/frontend/src/views/public/HomeView.vue index 353f77c..f7f23ba 100644 --- a/frontend/src/views/public/HomeView.vue +++ b/frontend/src/views/public/HomeView.vue @@ -32,6 +32,13 @@ import { getServerRegion, hasAcceleratedSaleRatio, hasGiftResources, + readAssetNumber, + readAssetString, + getResourceQuantity, + getOnlineTimeText, + getSkinNames, + getRatioValue, + getDailyLoss, } from "@/utils/listingDisplay"; const loading = ref(false); @@ -44,8 +51,17 @@ const filters = reactive({ server: "", loginMethod: "", rank: "", + insurance: "", + stamina: "", + load: "", minCoin: undefined as number | undefined, + maxCoin: undefined as number | undefined, + minPrice: undefined as number | undefined, maxPrice: undefined as number | undefined, + minDeposit: undefined as number | undefined, + maxDeposit: undefined as number | undefined, + minTotal: undefined as number | undefined, + maxTotal: undefined as number | undefined, }); const sortBy = ref("recommended"); @@ -74,15 +90,33 @@ const visibleListings = computed(() => { getServerRegion(item), getLoginMethod(item), getListingTitle(item), + ...getSkinNames(item), ] .join(" ") .toLowerCase(); + if (keyword && !text.includes(keyword)) return false; if (filters.server && getServerRegion(item) !== filters.server) return false; if (filters.loginMethod && getLoginMethod(item) !== filters.loginMethod) return false; if (filters.rank && item.rank_level !== filters.rank) return false; + if (filters.insurance && readAssetString(item, "season_insurance") !== filters.insurance) return false; + if (filters.stamina && readAssetString(item, "stamina_level") !== filters.stamina) return false; + if (filters.load && readAssetString(item, "load_level") !== filters.load) return false; + + // Range filters + const price = getListingDisplayPrice(item); + const deposit = item.deposit_amount || 0; + const total = price + deposit; + if (filters.minCoin && getCoinM(item) < filters.minCoin) return false; - if (filters.maxPrice && getListingDisplayPrice(item) > filters.maxPrice) return false; + if (filters.maxCoin && getCoinM(item) > filters.maxCoin) return false; + if (filters.minPrice && price < filters.minPrice) return false; + if (filters.maxPrice && price > filters.maxPrice) return false; + if (filters.minDeposit && deposit < filters.minDeposit) return false; + if (filters.maxDeposit && deposit > filters.maxDeposit) return false; + if (filters.minTotal && total < filters.minTotal) return false; + if (filters.maxTotal && total > filters.maxTotal) return false; + return true; }); @@ -143,8 +177,17 @@ function resetFilters() { filters.server = ""; filters.loginMethod = ""; filters.rank = ""; + filters.insurance = ""; + filters.stamina = ""; + filters.load = ""; filters.minCoin = undefined; + filters.maxCoin = undefined; + filters.minPrice = undefined; filters.maxPrice = undefined; + filters.minDeposit = undefined; + filters.maxDeposit = undefined; + filters.minTotal = undefined; + filters.maxTotal = undefined; sortBy.value = "recommended"; } @@ -155,6 +198,7 @@ function uniqueOptions(values: string[]) { diff --git a/scripts/dev.sh b/scripts/dev.sh index 77a0f25..1883158 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -5,6 +5,11 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" COMPOSE_FILE="${ROOT_DIR}/deploy/docker-compose.dev.yml" BACKEND_ENV="${ROOT_DIR}/backend/.env" BACKEND_ENV_EXAMPLE="${ROOT_DIR}/backend/.env.example" +BACKEND_PORT="${DEV_BACKEND_PORT:-8080}" +FRONTEND_PORT="${DEV_FRONTEND_PORT:-5173}" +BACKEND_HEALTH_URL="${DEV_BACKEND_HEALTH_URL:-http://127.0.0.1:${BACKEND_PORT}/health}" +FRONTEND_HEALTH_URL="${DEV_FRONTEND_HEALTH_URL:-http://127.0.0.1:${FRONTEND_PORT}/}" +HTTP_READY_TIMEOUT="${DEV_HTTP_READY_TIMEOUT:-120}" BACKEND_PID="" FRONTEND_PID="" @@ -53,6 +58,16 @@ show_help() { 环境变量: DEV_RESET_DB=1 等同于 --reset-db + DEV_BACKEND_PORT=8080 + 后端端口占用检查端口 + DEV_FRONTEND_PORT=5173 + 前端端口占用检查端口 + DEV_BACKEND_HEALTH_URL=http://127.0.0.1:8080/health + 后端健康检查地址 + DEV_FRONTEND_HEALTH_URL=http://127.0.0.1:5173/ + 前端健康检查地址 + DEV_HTTP_READY_TIMEOUT=120 + HTTP 服务就绪等待秒数 示例: ./scripts/dev.sh # 正常启动 @@ -135,6 +150,55 @@ wait_container_healthy() { exit 1 } +wait_http_ready() { + local url="$1" + local label="$2" + local pid="${3:-}" + local attempt + + log "等待 ${label} HTTP 服务就绪..." + for ((attempt = 1; attempt <= HTTP_READY_TIMEOUT; attempt++)); do + if [[ -n "${pid}" ]] && ! kill -0 "${pid}" >/dev/null 2>&1; then + wait "${pid}" || true + log_error "${label} 进程已退出,请检查上方日志" + exit 1 + fi + + if curl -fsS --max-time 2 "${url}" >/dev/null 2>&1; then + log_success "${label} 已就绪:${url}" + return 0 + fi + + sleep 1 + done + + log_error "${label} HTTP 服务启动超时:${url}" + exit 1 +} + +ensure_port_free() { + local port="$1" + local label="$2" + + if command -v lsof >/dev/null 2>&1; then + if lsof -nP -iTCP:"${port}" -sTCP:LISTEN >/dev/null 2>&1; then + log_error "${label}端口 ${port} 已被占用,请先停止旧进程或调整端口" + exit 1 + fi + return 0 + fi + + if command -v nc >/dev/null 2>&1; then + if nc -z 127.0.0.1 "${port}" >/dev/null 2>&1; then + log_error "${label}端口 ${port} 已被占用,请先停止旧进程或调整端口" + exit 1 + fi + return 0 + fi + + log_warn "未找到 lsof 或 nc,跳过 ${label}端口 ${port} 占用检查" +} + mysql_root() { docker exec -i -e MYSQL_PWD=rootsecret hfb-mysql mysql -uroot "$@" } @@ -240,6 +304,9 @@ main() { if [[ "${SEED_ONLY}" != "1" && "${NO_FRONTEND}" == "0" ]]; then need_cmd npm fi + if [[ "${SEED_ONLY}" != "1" && ( "${NO_BACKEND}" == "0" || "${NO_FRONTEND}" == "0" ) ]]; then + need_cmd curl + fi trap cleanup EXIT trap 'cleanup; exit 130' INT TERM @@ -260,11 +327,20 @@ main() { fi if [[ "${NO_BACKEND}" == "0" ]]; then + ensure_port_free "${BACKEND_PORT}" "后端" start_backend + wait_http_ready "${BACKEND_HEALTH_URL}" "后端" "${BACKEND_PID}" fi if [[ "${NO_FRONTEND}" == "0" ]]; then + ensure_port_free "${FRONTEND_PORT}" "前端" start_frontend + wait_http_ready "${FRONTEND_HEALTH_URL}" "前端" "${FRONTEND_PID}" + fi + + if [[ -z "${BACKEND_PID}" && -z "${FRONTEND_PID}" ]]; then + log "仅启动 Docker 开发依赖,前后端未启动" + return 0 fi log_success "开发环境已启动" @@ -273,11 +349,6 @@ main() { log "后台:http://localhost:5173/admin/login(admin / admin123456)" fi - if [[ -z "${BACKEND_PID}" && -z "${FRONTEND_PID}" ]]; then - log "仅启动 Docker 开发依赖,前后端未启动" - return 0 - fi - log "按 Ctrl+C 停止前后端;Docker 依赖会保留运行" watch_processes