diff --git a/backend/internal/modules/listing/dto.go b/backend/internal/modules/listing/dto.go index c6c65c6..d8f7f7b 100644 --- a/backend/internal/modules/listing/dto.go +++ b/backend/internal/modules/listing/dto.go @@ -17,6 +17,7 @@ type ListingDTO struct { HafCoinAmount int64 `json:"haf_coin_amount"` AssetSummary map[string]any `json:"asset_summary,omitempty"` ScreenshotURLS []string `json:"screenshot_urls"` + CoverURL string `json:"cover_url"` PriceHourly float64 `json:"price_hourly"` PriceDaily float64 `json:"price_daily"` PriceWeekly float64 `json:"price_weekly"` diff --git a/backend/internal/modules/listing/repository.go b/backend/internal/modules/listing/repository.go index 547dc22..649a8a5 100644 --- a/backend/internal/modules/listing/repository.go +++ b/backend/internal/modules/listing/repository.go @@ -448,6 +448,8 @@ func rowsToDTO(rows []listingRow) []ListingDTO { } func (row listingRow) toDTO() ListingDTO { + assetSummary := decodeAssetSummary(row.AssetSummary) + screenshotURLS := cleanScreenshotURLs(decodeScreenshots(row.ScreenshotURLS)) return ListingDTO{ ID: row.ID, AccountID: row.AccountID, @@ -461,8 +463,9 @@ func (row listingRow) toDTO() ListingDTO { LoginPlatform: row.LoginPlatform, RankLevel: row.RankLevel, HafCoinAmount: row.HafCoinAmount, - AssetSummary: decodeAssetSummary(row.AssetSummary), - ScreenshotURLS: decodeScreenshots(row.ScreenshotURLS), + AssetSummary: assetSummary, + ScreenshotURLS: screenshotURLS, + CoverURL: firstScreenshotURL(screenshotURLS), PriceHourly: row.PriceHourly, PriceDaily: row.PriceDaily, PriceWeekly: row.PriceWeekly, @@ -479,6 +482,8 @@ func (row listingRow) toDTO() ListingDTO { } func toDTO(account model.GameAccount, listing model.RentalListing) *ListingDTO { + assetSummary := decodeAssetSummary(account.AssetSummary) + screenshotURLS := cleanScreenshotURLs(decodeScreenshots(account.ScreenshotURLS)) return &ListingDTO{ ID: listing.ID, AccountID: account.ID, @@ -490,8 +495,9 @@ func toDTO(account model.GameAccount, listing model.RentalListing) *ListingDTO { LoginPlatform: account.LoginPlatform, RankLevel: account.RankLevel, HafCoinAmount: account.HafCoinAmount, - AssetSummary: decodeAssetSummary(account.AssetSummary), - ScreenshotURLS: decodeScreenshots(account.ScreenshotURLS), + AssetSummary: assetSummary, + ScreenshotURLS: screenshotURLS, + CoverURL: firstScreenshotURL(screenshotURLS), PriceHourly: listing.PriceHourly, PriceDaily: listing.PriceDaily, PriceWeekly: listing.PriceWeekly, @@ -508,21 +514,9 @@ func toDTO(account model.GameAccount, listing model.RentalListing) *ListingDTO { } func marshalScreenshots(urls []string) (datatypes.JSON, error) { - cleaned := make([]string, 0, len(urls)) - seen := make(map[string]struct{}, len(urls)) - for _, url := range urls { - url = strings.TrimSpace(url) - if url == "" { - continue - } - if _, ok := seen[url]; ok { - continue - } - seen[url] = struct{}{} - cleaned = append(cleaned, url) - if len(cleaned) >= 12 { - break - } + cleaned := cleanScreenshotURLs(urls) + if len(cleaned) > 12 { + cleaned = cleaned[:12] } raw, err := json.Marshal(cleaned) if err != nil { @@ -564,6 +558,30 @@ func decodeAssetSummary(raw datatypes.JSON) map[string]any { return summary } +func cleanScreenshotURLs(urls []string) []string { + cleaned := make([]string, 0, len(urls)) + seen := make(map[string]struct{}, len(urls)) + for _, url := range urls { + url = strings.TrimSpace(url) + if url == "" { + continue + } + if _, ok := seen[url]; ok { + continue + } + seen[url] = struct{}{} + cleaned = append(cleaned, url) + } + return cleaned +} + +func firstScreenshotURL(urls []string) string { + if len(urls) == 0 { + return "" + } + return urls[0] +} + func appendAuditLog(tx *gorm.DB, actorID uint64, action string, bizType string, bizID uint64, meta AuditMeta, detail map[string]any) error { raw, err := json.Marshal(detail) if err != nil { diff --git a/backend/internal/modules/listing/service.go b/backend/internal/modules/listing/service.go index 7d320e5..a5625a7 100644 --- a/backend/internal/modules/listing/service.go +++ b/backend/internal/modules/listing/service.go @@ -2,6 +2,7 @@ package listing import ( "errors" + "strings" ) var ( @@ -151,5 +152,17 @@ func validateRequest(req CreateRequest) error { if req.HafCoinAmount < 0 { return ErrInvalidInput } + if !hasScreenshotURL(req.ScreenshotURLS) { + return ErrInvalidInput + } return nil } + +func hasScreenshotURL(urls []string) bool { + for _, url := range urls { + if strings.TrimSpace(url) != "" { + return true + } + } + return false +} diff --git a/frontend/src/api/listingOptions.ts b/frontend/src/api/listingOptions.ts index 4dabb23..f41020e 100644 --- a/frontend/src/api/listingOptions.ts +++ b/frontend/src/api/listingOptions.ts @@ -127,16 +127,7 @@ export function mergeListingPublishOptions(options?: Partial ({ - ...item, - key: String(item.key) === 'armor6Ammo' ? 'level6Ammo' : item.key, - })) -} diff --git a/frontend/src/api/listings.ts b/frontend/src/api/listings.ts index aca58b5..94bdf18 100644 --- a/frontend/src/api/listings.ts +++ b/frontend/src/api/listings.ts @@ -15,6 +15,7 @@ export interface Listing { haf_coin_amount: number asset_summary?: Record screenshot_urls: string[] + cover_url: string price_hourly: number price_daily: number price_weekly: number diff --git a/frontend/src/views/mobile/MobileHomeView.vue b/frontend/src/views/mobile/MobileHomeView.vue index c9c9e36..6779877 100644 --- a/frontend/src/views/mobile/MobileHomeView.vue +++ b/frontend/src/views/mobile/MobileHomeView.vue @@ -17,7 +17,6 @@ import { import MobileHomeFilterSheet, { type FilterSection, } from "./MobileHomeFilterSheet.vue"; -import { buildFallbackListings } from "./mobileHomeFallback"; import { assetRegions, formatHafCoinM, @@ -32,8 +31,6 @@ import { getServerRegion, getSkinGroup, hasGiftResources, - normalizeLoginMethod, - normalizeServerRegion, readAssetNumber, readAssetString, } from "./listingDisplay"; @@ -77,12 +74,6 @@ const rangePresets: Record buildFallbackListings(publishOptions.value)); - -const sourceListings = computed(() => - loadFailed.value ? fallbackListings.value : listings.value -); - const activeSortLabel = computed( () => sortOptions.find((option) => option.key === activeSort.value)?.label || @@ -92,7 +83,7 @@ const activeSortLabel = computed( const serverFilterOptions = computed(() => uniqueOptions( publishOptions.value.server_options - .map((item) => normalizeServerRegion(item)) + .map((item) => item.trim()) .filter(Boolean) ) ); @@ -100,7 +91,7 @@ const serverFilterOptions = computed(() => const loginMethodFilterOptions = computed(() => uniqueOptions( publishOptions.value.login_method_options - .map((item) => normalizeLoginMethod(item)) + .map((item) => item.trim()) .filter(Boolean) ) ); @@ -145,7 +136,7 @@ const activeFilterCount = computed(() => { const displayListings = computed(() => { const keyword = searchValue.value.trim().toLowerCase(); - const filtered = sourceListings.value.filter((item) => { + const filtered = listings.value.filter((item) => { if (!matchesFilters(item)) return false; if (!keyword) return true; return searchText(item).includes(keyword); @@ -432,7 +423,7 @@ function uniqueOptions(values: string[]) { left-icon="info-o" color="#6b7a90" background="transparent" - text="接口暂不可用,当前展示移动端示例数据。" + text="接口暂不可用,请稍后刷新。" />
diff --git a/frontend/src/views/mobile/MobileListingDetailView.vue b/frontend/src/views/mobile/MobileListingDetailView.vue index 763a39c..267656f 100644 --- a/frontend/src/views/mobile/MobileListingDetailView.vue +++ b/frontend/src/views/mobile/MobileListingDetailView.vue @@ -4,15 +4,14 @@ import { useRoute, useRouter } from "vue-router"; import { showToast, showDialog } from "vant"; import { fetchListing, type Listing } from "@/api/listings"; -import { defaultListingPublishOptions } from "@/api/listingOptions"; import { createOrder } from "@/api/orders"; import { useSessionStore } from "@/stores/session"; -import { buildFallbackListings } from "./mobileHomeFallback"; import { assetRegions, formatHafCoinM, formatRatio, getCoinWan, + getDailyLoss, getListingChips, getListingDisplayPrice, getListingResources, @@ -20,7 +19,6 @@ import { getListingTitle, getLoginMethod, getRentDays, - getScreenshotMap, getServerRegion, readAssetNumber, readAssetString, @@ -40,15 +38,7 @@ onMounted(async () => { listing.value = await fetchListing(String(route.params.id)); rentHours.value = Math.max(listing.value.min_rent_hours || 1, 1); } catch { - const fallback = buildFallbackListings(defaultListingPublishOptions).find( - (item) => String(item.id) === String(route.params.id) - ); - if (fallback) { - listing.value = fallback; - rentHours.value = Math.max(fallback.min_rent_hours || 1, 1); - } else { - showToast({ message: "加载失败", icon: "warning-o" }); - } + showToast({ message: "加载失败", icon: "warning-o" }); } finally { loading.value = false; } @@ -77,20 +67,9 @@ const detailMetrics = computed(() => { const detailScreenshots = computed(() => { if (!listing.value) return []; - const screenshotMap = getScreenshotMap(listing.value); - const labels: Record = { - coin: "纯币截图", - gameId: "游戏ID", - totalAsset: "总资产", - tencentSecurity: "腾讯安全中心", - skin: "皮肤截图", - }; - const mapped = Object.entries(screenshotMap) - .filter(([, url]) => typeof url === "string" && url) - .map(([key, url]) => ({ label: labels[key] || "账号截图", url: String(url) })); - if (mapped.length) return mapped; + const labels = ["纯币截图", "游戏ID截图", "总资产截图", "腾讯安全中心截图", "皮肤截图"]; return (listing.value.screenshot_urls || []).map((url, index) => ({ - label: `账号截图${index + 1}`, + label: labels[index] || `账号截图${index + 1}`, url, })); }); @@ -239,12 +218,12 @@ function isNavActive(path: string) { listing.rank_level }} - 损耗 {{ readAssetString(listing, "daily_loss") }} + 损耗 {{ getDailyLoss(listing) }}

{{ getListingTitle(listing) }}

diff --git a/frontend/src/views/mobile/MobileSellerListingCreateView.vue b/frontend/src/views/mobile/MobileSellerListingCreateView.vue index 2356286..ee6f1e2 100644 --- a/frontend/src/views/mobile/MobileSellerListingCreateView.vue +++ b/frontend/src/views/mobile/MobileSellerListingCreateView.vue @@ -28,7 +28,7 @@ function isNavActive(path: string) { const form = reactive({ server_region: "", face_owner: "", - haf_coin_amount: 0, + haf_coin_amount: "" as number | "", rank_level: "", secret_kd: "", fire_level: "" as number | "", @@ -43,9 +43,7 @@ const form = reactive({ deposit_amount: "" as number | "", ratio: "" as number | "", final_price: 0, - settlement_amount: 0, rent_days: 0, - daily_loss: "", remark: "", }); @@ -94,6 +92,9 @@ const screenshotSlots = computed(() => publishOptions.value.screenshot_slots); const screenshotUrls = computed(() => screenshotSlots.value.map((item) => screenshotFiles[item.key]).filter(Boolean) ); +const dailyLossText = computed(() => + form.rent_days ? calculateDailyLoss(Number(form.haf_coin_amount || 0)) : "" +); onMounted(loadPublishOptions); @@ -174,9 +175,7 @@ function calculatePrice() { const finalPrice = roundMoney(coinWan / ratio); const rentDays = coinWan >= 30000 ? 7 : coinWan >= 10000 ? 3 : 1; form.final_price = finalPrice; - form.settlement_amount = finalPrice; form.rent_days = rentDays; - form.daily_loss = coinWan >= 30000 ? "30M" : coinWan >= 10000 ? "20M" : "10M"; } async function handleSubmit() { @@ -232,7 +231,7 @@ function validateForm() { if (form.deposit_amount === "" || Number(form.deposit_amount) < 0) { return "请填写押金"; } - if (!form.final_price || !form.settlement_amount || !form.rent_days) { + if (!form.final_price || !form.rent_days) { return "请先点击计算价格"; } for (const item of screenshotSlots.value) { @@ -246,7 +245,6 @@ function validateForm() { function buildAssetSummary() { return { face_owner: form.face_owner, - haf_coin_wan: Number(form.haf_coin_amount), secret_kd: form.secret_kd, fire_level: Number(form.fire_level), season_insurance: form.season_insurance, @@ -258,7 +256,6 @@ function buildAssetSummary() { quantity: Number(quantityValues[item.key] || 0), mode: quantityModes[item.key], })), - skins: selectedSkins.value, skin_groups: skinGroups.value.reduce>((groups, group) => { groups[group.key] = group.options.filter((skin) => selectedSkins.value.includes(skin) @@ -269,16 +266,8 @@ function buildAssetSummary() { start: form.online_start, end: form.online_end, }, - login_method: form.login_method, ban_record: form.ban_record, common_regions: form.common_regions, - screenshots: { ...screenshotFiles }, - deposit_amount: Number(form.deposit_amount), - ratio: Number(form.ratio), - price: Number(form.final_price), - settlement_amount: Number(form.settlement_amount), - rent_days: Number(form.rent_days), - daily_loss: form.daily_loss, remark: form.remark, }; } @@ -287,6 +276,12 @@ function roundMoney(value: number) { return Math.round(value * 100) / 100; } +function calculateDailyLoss(coinWan: number) { + if (coinWan >= 30000) return "30M"; + if (coinWan >= 10000) return "20M"; + return "10M"; +} + function readError(error: unknown, fallback: string) { if (typeof error === "object" && error && "response" in error) { const response = (error as { response?: { data?: { message?: string } } }) @@ -642,7 +637,7 @@ function readError(error: unknown, fallback: string) { class="publish-field result-field" /> 0) return assetCoin; return Math.round(Number(item.haf_coin_amount || 0) / 10000); } @@ -29,8 +27,6 @@ export function formatHafCoinM(amountWan: number) { } export function getListingDisplayPrice(item: Listing) { - const assetPrice = readAssetNumber(item, "price"); - if (assetPrice > 0) return assetPrice; const rentDays = getRentDays(item); if (rentDays > 0 && item.price_daily > 0) { return roundMoney(item.price_daily * rentDays); @@ -39,15 +35,11 @@ export function getListingDisplayPrice(item: Listing) { } export function getRentDays(item: Listing) { - const assetDays = readAssetNumber(item, "rent_days"); - if (assetDays > 0) return assetDays; if (item.max_rent_hours >= 24) return Math.max(Math.round(item.max_rent_hours / 24), 1); return 1; } export function getRatioValue(item: Listing) { - const ratio = readAssetNumber(item, "ratio"); - if (ratio > 0) return ratio; const price = getListingDisplayPrice(item); if (price <= 0) return 0; return getCoinWan(item) / price; @@ -65,11 +57,11 @@ export function getValuePerYuanText(item: Listing) { } export function getLoginMethod(item: Listing) { - return normalizeLoginMethod(readAssetString(item, "login_method") || item.login_platform); + return item.login_platform.trim(); } export function getServerRegion(item: Listing) { - return normalizeServerRegion(item.server_region); + return item.server_region.trim(); } export function getListingTitle(item: Listing) { @@ -134,8 +126,7 @@ export function getListingResources(item: Listing): ListingDisplayResource[] { } export function getResourceQuantity(item: Listing, resourceKey: string) { - const keys = getResourceKeyAliases(resourceKey); - return getListingResources(item).find((resource) => keys.includes(resource.key))?.quantity || 0; + return getListingResources(item).find((resource) => resource.key === resourceKey)?.quantity || 0; } export function hasGiftResources(item: Listing) { @@ -158,13 +149,10 @@ export function getSkinGroup(item: Listing, groupKey: string) { export function getSkinNames(item: Listing) { const skinGroups = item.asset_summary?.skin_groups; - if (typeof skinGroups === "object" && skinGroups !== null) { - return Object.values(skinGroups as Record) - .flatMap((group) => (Array.isArray(group) ? group : [])) - .filter((skin): skin is string => typeof skin === "string"); - } - const skins = item.asset_summary?.skins; - return Array.isArray(skins) ? skins.filter((skin): skin is string => typeof skin === "string") : []; + if (typeof skinGroups !== "object" || skinGroups === null) return []; + return Object.values(skinGroups as Record) + .flatMap((group) => (Array.isArray(group) ? group : [])) + .filter((skin): skin is string => typeof skin === "string"); } export function assetRegions(item: Listing) { @@ -174,13 +162,6 @@ export function assetRegions(item: Listing) { : []; } -export function getScreenshotMap(item: Listing) { - const screenshots = item.asset_summary?.screenshots; - return typeof screenshots === "object" && screenshots !== null - ? (screenshots as Record) - : {}; -} - export function getOnlineTimeText(item: Listing) { const onlineTime = item.asset_summary?.online_time; if (typeof onlineTime !== "object" || onlineTime === null) return ""; @@ -190,6 +171,13 @@ export function getOnlineTimeText(item: Listing) { return `${start.replace(":00", "")}-${end.replace(":00", "")}点`; } +export function getDailyLoss(item: Listing) { + const coinWan = getCoinWan(item); + if (coinWan >= 30000) return "30M"; + if (coinWan >= 10000) return "20M"; + return "10M"; +} + export function readAssetString(item: Listing, key: string) { const value = item.asset_summary?.[key]; return typeof value === "string" ? value : ""; @@ -208,23 +196,6 @@ function readUnknownNumber(value: unknown) { return 0; } -export function normalizeServerRegion(value: string) { - const text = value.trim(); - const upper = text.toUpperCase(); - if (upper.includes("STEAM")) return "Steam"; - if (upper.includes("QQ")) return "QQ"; - if (text.includes("微信")) return "微信"; - return text; -} - -export function normalizeLoginMethod(value: string) { - const text = value.trim(); - if (!text) return ""; - if (text.includes("扫码")) return "扫码"; - if (text.includes("账密") || text.includes("账号密码")) return "账号密码"; - return text; -} - function formatInsuranceSlotText(value: string) { const parts = value.split("*").map((item) => Number(item)); const rows = parts[0] || 0; @@ -245,12 +216,6 @@ function formatResourceShort(item: Listing, key: string, label: string) { return quantity > 0 ? `${quantity}${label}` : ""; } -function getResourceKeyAliases(key: string) { - if (key === "level6Ammo") return ["level6Ammo", "armor6Ammo"]; - if (key === "armor6Ammo") return ["armor6Ammo", "level6Ammo"]; - return [key]; -} - function roundMoney(value: number) { return Math.round(value * 100) / 100; } diff --git a/frontend/src/views/mobile/mobileHomeFallback.ts b/frontend/src/views/mobile/mobileHomeFallback.ts deleted file mode 100644 index 58a50ec..0000000 --- a/frontend/src/views/mobile/mobileHomeFallback.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { - defaultListingPublishOptions, - type ListingPublishOptions, -} from "@/api/listingOptions"; -import type { Listing } from "@/api/listings"; - -const fallbackRegionOptions = ["广东", "上海", "江苏", "四川", "北京", "浙江"]; - -const coinWanOptions = [ - 1800, 3200, 5800, 9600, 12000, 18000, 26000, 36000, 52000, 68000, -]; -const depositOptions = [60, 80, 100, 120, 160, 200]; -const secretKdOptions = [1.2, 1.6, 2.1, 2.8, 3.4, 4.2]; -const onlineWindows = [ - ["09:00", "23:30"], - ["10:30", "01:00"], - ["12:00", "24:00"], - ["18:00", "02:00"], -] as const; -const resourceQuantityPresets: Record = { - awmAmmo: [0, 20, 48, 80, 120, 180], - helmet6: [0, 2, 4, 6, 10, 16], - armor6: [0, 1, 3, 5, 8, 12], - kit5: [0, 3, 6, 10, 15, 24], - level6Ammo: [0, 120, 240, 360, 520, 800], - gridCard9: [0, 1, 2, 4, 6, 10], -}; - -function pick(items: readonly T[], index: number, fallback: T): T { - if (!items.length) return fallback; - return items[index % items.length] ?? fallback; -} - -function pickMany(items: string[], start: number, count: number) { - if (!items.length) return []; - const fallback = items[0] || ""; - return Array.from({ length: Math.min(count, items.length) }, (_, idx) => - pick(items, start + idx, fallback) - ).filter(Boolean); -} - -function buildResources(options: ListingPublishOptions, seed: number) { - return options.quantity_items.map((item, index) => ({ - key: item.key, - label: item.label, - quantity: pick( - resourceQuantityPresets[item.key] || [0, 1, 3, 6], - seed + index, - 0 - ), - mode: "收费", - })); -} - -function buildSkinGroups(options: ListingPublishOptions, seed: number) { - return options.skin_groups.reduce>((groups, group, index) => { - const count = group.key === "melee" ? 2 : index % 2 === 0 ? 3 : 2; - groups[group.key] = pickMany(group.options, seed + index * 2, count); - return groups; - }, {}); -} - -function buildScreenshotMap(options: ListingPublishOptions, seed: number) { - return options.screenshot_slots.reduce>((screenshots, slot, index) => { - screenshots[slot.key] = `https://picsum.photos/seed/hfb-${slot.key}-${seed + index}/960/540`; - return screenshots; - }, {}); -} - -function flattenSkinGroups(groups: Record) { - return Object.values(groups).flat(); -} - -function calculateRentDays(coinWan: number) { - return coinWan >= 30000 ? 7 : coinWan >= 10000 ? 3 : 1; -} - -function calculateDailyLoss(coinWan: number) { - return coinWan >= 30000 ? "30M" : coinWan >= 10000 ? "20M" : "10M"; -} - -function roundMoney(value: number) { - return Math.round(value * 100) / 100; -} - -function formatHafCoinM(coinWan: number) { - const coinM = Math.round((coinWan / 100) * 10) / 10; - return `${Number.isInteger(coinM) ? coinM : coinM.toFixed(1)}M`; -} - -export function buildFallbackListings( - options: ListingPublishOptions = defaultListingPublishOptions -): Listing[] { - return Array.from({ length: 10 }, (_, index) => { - const seed = index + 1; - const server = pick(options.server_options, index, "QQ"); - const loginMethod = pick(options.login_method_options, index + 1, "账号密码"); - const rank = pick(options.rank_options, index + 3, "钻石"); - const insurance = pick(options.insurance_options, index + 1, "2*2"); - const stamina = pick(options.level_options, index + 4, "5级"); - const load = pick(options.level_options, index + 5, "5级"); - const faceOwner = pick(options.face_options, index, "是"); - const coinWan = pick(coinWanOptions, index, 12000); - const ratio = 5 + (index % 4); - const rentDays = calculateRentDays(coinWan); - const finalPrice = roundMoney(coinWan / ratio); - const settlementAmount = finalPrice; - const dailyPrice = roundMoney(finalPrice / rentDays); - const hourlyPrice = Math.max(roundMoney(dailyPrice / 24), 0.01); - const deposit = pick(depositOptions, index, 100); - const skinGroups = buildSkinGroups(options, seed); - const screenshots = buildScreenshotMap(options, seed); - const commonRegions = pickMany( - options.region_options.length ? options.region_options : fallbackRegionOptions, - index * 2, - 2 - ); - const [onlineStart, onlineEnd] = pick(onlineWindows, index, onlineWindows[0]); - - return { - id: 9100 + seed, - account_id: 0, - owner_id: 0, - title: `${server} ${rank} ${formatHafCoinM(coinWan)}哈夫币`, - description: `${loginMethod}交接,${insurance}保险,体力${stamina}、负重${load},示例数据按发布表单随机生成。`, - game_name: "三角洲行动", - server_region: server, - login_platform: loginMethod, - rank_level: rank, - haf_coin_amount: coinWan * 10000, - asset_summary: { - face_owner: faceOwner, - haf_coin_wan: coinWan, - total_asset_wan: Math.round(coinWan * (1.08 + (index % 4) * 0.04)), - secret_kd: pick(secretKdOptions, index, 1.6), - fire_level: 38 + seed * 3, - season_insurance: insurance, - stamina_level: stamina, - load_level: load, - daily_loss: calculateDailyLoss(coinWan), - ratio, - login_method: loginMethod, - common_regions: commonRegions, - resources: buildResources(options, seed), - skins: flattenSkinGroups(skinGroups), - skin_groups: skinGroups, - online_time: { - start: onlineStart, - end: onlineEnd, - }, - ban_record: index % 4 === 0 ? "无封禁记录,近期登录稳定" : "", - screenshots, - deposit_amount: deposit, - price: finalPrice, - settlement_amount: settlementAmount, - rent_days: rentDays, - remark: - index % 3 === 0 - ? "部分仓库材料请勿使用,下单后会在群聊再次提醒。" - : "", - }, - screenshot_urls: Object.values(screenshots), - price_hourly: hourlyPrice, - price_daily: dailyPrice, - price_weekly: Math.round(dailyPrice * 7 * 10) / 10, - deposit_amount: deposit, - min_rent_hours: coinWan >= 10000 ? 24 : 8, - max_rent_hours: coinWan >= 30000 ? 168 : 72, - status: "published", - review_status: "approved", - review_reason: "", - published_at: new Date(Date.now() - seed * 18 * 60 * 1000).toISOString(), - created_at: new Date(Date.now() - seed * 20 * 60 * 1000).toISOString(), - updated_at: new Date(Date.now() - seed * 10 * 60 * 1000).toISOString(), - }; - }); -} diff --git a/frontend/src/views/public/HomeView.vue b/frontend/src/views/public/HomeView.vue index 7177089..2445e8c 100644 --- a/frontend/src/views/public/HomeView.vue +++ b/frontend/src/views/public/HomeView.vue @@ -20,7 +20,6 @@ import { type ListingPublishOptions, } from "@/api/listingOptions"; import { fetchListings, type Listing } from "@/api/listings"; -import { buildFallbackListings } from "@/views/mobile/mobileHomeFallback"; import { formatHafCoinM, getCoinM, @@ -32,12 +31,9 @@ import { getLoginMethod, getServerRegion, hasGiftResources, - normalizeLoginMethod, - normalizeServerRegion, } from "@/views/mobile/listingDisplay"; const loading = ref(false); -const loadFailed = ref(false); const listings = ref([]); const announcements = ref(defaultHomeAnnouncements); const banners = ref(defaultHomeBanners); @@ -52,29 +48,24 @@ const filters = reactive({ }); const sortBy = ref("recommended"); -const fallbackListings = computed(() => buildFallbackListings(publishOptions.value)); -const sourceListings = computed(() => - loadFailed.value ? fallbackListings.value : listings.value -); - const serverOptions = computed(() => uniqueOptions( publishOptions.value.server_options - .map((item) => normalizeServerRegion(item)) + .map((item) => item.trim()) .filter(Boolean) ) ); const loginMethodOptions = computed(() => uniqueOptions( publishOptions.value.login_method_options - .map((item) => normalizeLoginMethod(item)) + .map((item) => item.trim()) .filter(Boolean) ) ); const visibleListings = computed(() => { const keyword = filters.keyword.trim().toLowerCase(); - const filtered = sourceListings.value.filter((item) => { + const filtered = listings.value.filter((item) => { const text = [ item.title, item.description, @@ -114,12 +105,12 @@ const statCards = computed(() => [ { label: "可租账号", value: `${visibleListings.value.length}`, hint: "当前筛选结果" }, { label: "高哈夫币", - value: `${sourceListings.value.filter((item) => getCoinM(item) >= 100).length}`, + value: `${listings.value.filter((item) => getCoinM(item) >= 100).length}`, hint: "100M 以上", }, { label: "账密登录", - value: `${sourceListings.value.filter((item) => getLoginMethod(item) === "账号密码").length}`, + value: `${listings.value.filter((item) => getLoginMethod(item) === "账号密码").length}`, hint: "交接更快", }, ]); @@ -128,7 +119,6 @@ onMounted(loadHome); async function loadHome() { loading.value = true; - loadFailed.value = false; try { const [nextListings, config] = await Promise.all([ fetchListings(), @@ -139,7 +129,6 @@ async function loadHome() { banners.value = config.banners; publishOptions.value = config.publish_options; } catch { - loadFailed.value = true; announcements.value = defaultHomeAnnouncements; banners.value = defaultHomeBanners; publishOptions.value = defaultListingPublishOptions; @@ -328,8 +317,8 @@ function uniqueOptions(values: string[]) { >
HFB diff --git a/frontend/src/views/public/ListingsView.vue b/frontend/src/views/public/ListingsView.vue index b88383c..d3a8ae3 100644 --- a/frontend/src/views/public/ListingsView.vue +++ b/frontend/src/views/public/ListingsView.vue @@ -179,8 +179,8 @@ function uniqueOptions(values: string[]) { >
HFB