发布功能可以 存草靠

This commit is contained in:
yml2213
2026-05-23 10:47:41 +08:00
parent 7a52cecf8c
commit ce554432c2
2 changed files with 215 additions and 61 deletions
@@ -431,15 +431,32 @@
font-weight: 800; font-weight: 800;
} }
.publish-actions {
display: grid;
grid-template-columns: 92px minmax(0, 1fr);
gap: 10px;
margin-top: 8px;
}
.reset-btn,
.submit-btn { .submit-btn {
height: 44px; height: 44px;
margin-top: 8px; width: 100%;
border: none !important; border: none !important;
background: #ff6a00 !important;
font-size: 15px; font-size: 15px;
font-weight: 700; font-weight: 700;
} }
.reset-btn {
border: 1px solid #d7dce3 !important;
background: #fff !important;
color: #4b5563 !important;
}
.submit-btn {
background: #ff6a00 !important;
}
.bottom-nav { .bottom-nav {
position: fixed; position: fixed;
left: 0; left: 0;
@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, reactive, ref } from "vue"; import { computed, onMounted, reactive, ref, watch } from "vue";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import { showToast } from "vant"; import { showDialog, showToast } from "vant";
import { uploadFile } from "@/api/files"; import { uploadFile } from "@/api/files";
import { import {
@@ -14,10 +14,42 @@ import {
} from "@/api/listingOptions"; } from "@/api/listingOptions";
import { createListing } from "@/api/listings"; import { createListing } from "@/api/listings";
type PublishForm = {
server_region: string;
face_owner: string;
haf_coin_amount: number | "";
rank_level: string;
secret_kd: string;
fire_level: number | "";
season_insurance: string;
stamina_level: string;
load_level: string;
login_method: string;
online_start: string;
online_end: string;
ban_record: string;
common_regions: string[];
deposit_amount: number | "";
ratio: number | "";
final_price: number;
rent_days: number;
remark: string;
};
interface PublishDraft {
form: PublishForm;
quantityValues: Record<QuantityKey, number>;
quantityModes: Record<QuantityKey, ChargeMode>;
screenshotFiles: Record<ScreenshotKey, string>;
selectedSkins: string[];
}
const draftKey = "hfb.mobile.publish.draft";
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const loading = ref(false); const loading = ref(false);
const uploading = ref(false); const uploading = ref(false);
const suppressDraftSave = ref(false);
const publishOptions = ref<ListingPublishOptions>(defaultListingPublishOptions); const publishOptions = ref<ListingPublishOptions>(defaultListingPublishOptions);
function isNavActive(path: string) { function isNavActive(path: string) {
@@ -25,53 +57,69 @@ function isNavActive(path: string) {
return route.path.startsWith(path); return route.path.startsWith(path);
} }
const form = reactive({ function defaultForm(): PublishForm {
server_region: "", return {
face_owner: "", server_region: "",
haf_coin_amount: "" as number | "", face_owner: "",
rank_level: "", haf_coin_amount: "",
secret_kd: "", rank_level: "",
fire_level: "" as number | "", secret_kd: "",
season_insurance: "", fire_level: "",
stamina_level: "", season_insurance: "",
load_level: "", stamina_level: "",
login_method: "", load_level: "",
online_start: "", login_method: "",
online_end: "", online_start: "",
ban_record: "", online_end: "",
common_regions: [] as string[], ban_record: "",
deposit_amount: "" as number | "", common_regions: [],
ratio: "" as number | "", deposit_amount: "",
final_price: 0, ratio: "",
rent_days: 0, final_price: 0,
remark: "", rent_days: 0,
}); remark: "",
};
}
const quantityValues = reactive<Record<QuantityKey, number>>({ function defaultQuantityValues(): Record<QuantityKey, number> {
awmAmmo: 0, return {
helmet6: 0, awmAmmo: 0,
armor6: 0, helmet6: 0,
kit5: 0, armor6: 0,
level6Ammo: 0, kit5: 0,
gridCard9: 0, level6Ammo: 0,
}); gridCard9: 0,
};
}
const quantityModes = reactive<Record<QuantityKey, ChargeMode>>({ function defaultQuantityModes(): Record<QuantityKey, ChargeMode> {
awmAmmo: "收费", return {
helmet6: "收费", awmAmmo: "收费",
armor6: "收费", helmet6: "收费",
kit5: "收费", armor6: "收费",
level6Ammo: "收费", kit5: "收费",
gridCard9: "收费", level6Ammo: "收费",
}); gridCard9: "收费",
};
}
const screenshotFiles = reactive<Record<ScreenshotKey, string>>({ function defaultScreenshotFiles(): Record<ScreenshotKey, string> {
coin: "", return {
gameId: "", coin: "",
totalAsset: "", gameId: "",
tencentSecurity: "", totalAsset: "",
skin: "", tencentSecurity: "",
}); skin: "",
};
}
const form = reactive<PublishForm>(defaultForm());
const quantityValues = reactive<Record<QuantityKey, number>>(defaultQuantityValues());
const quantityModes = reactive<Record<QuantityKey, ChargeMode>>(defaultQuantityModes());
const screenshotFiles = reactive<Record<ScreenshotKey, string>>(defaultScreenshotFiles());
const selectedSkins = ref<string[]>([]); const selectedSkins = ref<string[]>([]);
const fileInput = ref<HTMLInputElement | null>(null); const fileInput = ref<HTMLInputElement | null>(null);
@@ -96,7 +144,18 @@ const dailyLossText = computed(() =>
form.rent_days ? calculateDailyLoss(Number(form.haf_coin_amount || 0)) : "" form.rent_days ? calculateDailyLoss(Number(form.haf_coin_amount || 0)) : ""
); );
onMounted(loadPublishOptions); onMounted(() => {
restoreDraft();
loadPublishOptions();
});
watch(
[form, quantityValues, quantityModes, screenshotFiles, selectedSkins],
() => {
saveDraft();
},
{ deep: true }
);
async function loadPublishOptions() { async function loadPublishOptions() {
try { try {
@@ -106,6 +165,74 @@ async function loadPublishOptions() {
} }
} }
function buildDraft(): PublishDraft {
return {
form: {
...form,
common_regions: [...form.common_regions],
},
quantityValues: { ...quantityValues },
quantityModes: { ...quantityModes },
screenshotFiles: { ...screenshotFiles },
selectedSkins: [...selectedSkins.value],
};
}
function saveDraft() {
if (suppressDraftSave.value) return;
localStorage.setItem(draftKey, JSON.stringify(buildDraft()));
}
function restoreDraft() {
const raw = localStorage.getItem(draftKey);
if (!raw) return;
try {
const draft = JSON.parse(raw) as Partial<PublishDraft>;
Object.assign(form, defaultForm(), draft.form || {});
form.common_regions = Array.isArray(draft.form?.common_regions)
? [...draft.form.common_regions]
: [];
Object.assign(quantityValues, defaultQuantityValues(), draft.quantityValues || {});
Object.assign(quantityModes, defaultQuantityModes(), draft.quantityModes || {});
Object.assign(screenshotFiles, defaultScreenshotFiles(), draft.screenshotFiles || {});
selectedSkins.value = Array.isArray(draft.selectedSkins)
? draft.selectedSkins.filter((skin): skin is string => typeof skin === "string")
: [];
} catch {
localStorage.removeItem(draftKey);
}
}
function resetDraftState() {
Object.assign(form, defaultForm());
Object.assign(quantityValues, defaultQuantityValues());
Object.assign(quantityModes, defaultQuantityModes());
Object.assign(screenshotFiles, defaultScreenshotFiles());
selectedSkins.value = [];
activeUploadKey.value = "coin";
}
async function handleResetDraft() {
try {
await showDialog({
title: "重置发布内容",
message: "将清空当前填写内容和本地草稿。",
confirmButtonText: "重置",
cancelButtonText: "取消",
showCancelButton: true,
});
} catch {
return;
}
suppressDraftSave.value = true;
resetDraftState();
localStorage.removeItem(draftKey);
showToast({ message: "已重置", icon: "passed" });
window.setTimeout(() => {
suppressDraftSave.value = false;
});
}
function selectRadio<T extends string>(value: T, setter: (value: T) => void) { function selectRadio<T extends string>(value: T, setter: (value: T) => void) {
setter(value); setter(value);
} }
@@ -158,7 +285,7 @@ function handleFireLevelInput(value: string | number) {
form.fire_level = ""; form.fire_level = "";
return; return;
} }
form.fire_level = Math.max(38, Math.trunc(level)); form.fire_level = Math.trunc(level);
} }
function calculatePrice() { function calculatePrice() {
@@ -207,6 +334,7 @@ async function handleSubmit() {
min_rent_hours: 24, min_rent_hours: 24,
max_rent_hours: rentDays * 24, max_rent_hours: rentDays * 24,
}); });
localStorage.removeItem(draftKey);
showToast({ message: "发布成功", icon: "passed" }); showToast({ message: "发布成功", icon: "passed" });
await router.push("/m/profile"); await router.push("/m/profile");
} catch (error) { } catch (error) {
@@ -671,17 +799,26 @@ function readError(error: unknown, fallback: string) {
/> />
</div> </div>
<van-button <div class="publish-actions">
type="primary" <van-button
block round
round class="reset-btn"
class="submit-btn" :disabled="loading"
:loading="loading" @click="handleResetDraft"
loading-text="发布中..." >
@click="handleSubmit" 重置
> </van-button>
保存发布 <van-button
</van-button> type="primary"
round
class="submit-btn"
:loading="loading"
loading-text="发布中..."
@click="handleSubmit"
>
保存发布
</van-button>
</div>
</section> </section>
<nav class="bottom-nav"> <nav class="bottom-nav">