发布功能可以 存草靠
This commit is contained in:
@@ -431,15 +431,32 @@
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.publish-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 92px minmax(0, 1fr);
|
||||
gap: 10px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.reset-btn,
|
||||
.submit-btn {
|
||||
height: 44px;
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
border: none !important;
|
||||
background: #ff6a00 !important;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.reset-btn {
|
||||
border: 1px solid #d7dce3 !important;
|
||||
background: #fff !important;
|
||||
color: #4b5563 !important;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
background: #ff6a00 !important;
|
||||
}
|
||||
|
||||
.bottom-nav {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<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 { showToast } from "vant";
|
||||
import { showDialog, showToast } from "vant";
|
||||
|
||||
import { uploadFile } from "@/api/files";
|
||||
import {
|
||||
@@ -14,10 +14,42 @@ import {
|
||||
} from "@/api/listingOptions";
|
||||
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 route = useRoute();
|
||||
const loading = ref(false);
|
||||
const uploading = ref(false);
|
||||
const suppressDraftSave = ref(false);
|
||||
const publishOptions = ref<ListingPublishOptions>(defaultListingPublishOptions);
|
||||
|
||||
function isNavActive(path: string) {
|
||||
@@ -25,53 +57,69 @@ function isNavActive(path: string) {
|
||||
return route.path.startsWith(path);
|
||||
}
|
||||
|
||||
const form = reactive({
|
||||
server_region: "",
|
||||
face_owner: "",
|
||||
haf_coin_amount: "" as number | "",
|
||||
rank_level: "",
|
||||
secret_kd: "",
|
||||
fire_level: "" as number | "",
|
||||
season_insurance: "",
|
||||
stamina_level: "",
|
||||
load_level: "",
|
||||
login_method: "",
|
||||
online_start: "",
|
||||
online_end: "",
|
||||
ban_record: "",
|
||||
common_regions: [] as string[],
|
||||
deposit_amount: "" as number | "",
|
||||
ratio: "" as number | "",
|
||||
final_price: 0,
|
||||
rent_days: 0,
|
||||
remark: "",
|
||||
});
|
||||
function defaultForm(): PublishForm {
|
||||
return {
|
||||
server_region: "",
|
||||
face_owner: "",
|
||||
haf_coin_amount: "",
|
||||
rank_level: "",
|
||||
secret_kd: "",
|
||||
fire_level: "",
|
||||
season_insurance: "",
|
||||
stamina_level: "",
|
||||
load_level: "",
|
||||
login_method: "",
|
||||
online_start: "",
|
||||
online_end: "",
|
||||
ban_record: "",
|
||||
common_regions: [],
|
||||
deposit_amount: "",
|
||||
ratio: "",
|
||||
final_price: 0,
|
||||
rent_days: 0,
|
||||
remark: "",
|
||||
};
|
||||
}
|
||||
|
||||
const quantityValues = reactive<Record<QuantityKey, number>>({
|
||||
awmAmmo: 0,
|
||||
helmet6: 0,
|
||||
armor6: 0,
|
||||
kit5: 0,
|
||||
level6Ammo: 0,
|
||||
gridCard9: 0,
|
||||
});
|
||||
function defaultQuantityValues(): Record<QuantityKey, number> {
|
||||
return {
|
||||
awmAmmo: 0,
|
||||
helmet6: 0,
|
||||
armor6: 0,
|
||||
kit5: 0,
|
||||
level6Ammo: 0,
|
||||
gridCard9: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const quantityModes = reactive<Record<QuantityKey, ChargeMode>>({
|
||||
awmAmmo: "收费",
|
||||
helmet6: "收费",
|
||||
armor6: "收费",
|
||||
kit5: "收费",
|
||||
level6Ammo: "收费",
|
||||
gridCard9: "收费",
|
||||
});
|
||||
function defaultQuantityModes(): Record<QuantityKey, ChargeMode> {
|
||||
return {
|
||||
awmAmmo: "收费",
|
||||
helmet6: "收费",
|
||||
armor6: "收费",
|
||||
kit5: "收费",
|
||||
level6Ammo: "收费",
|
||||
gridCard9: "收费",
|
||||
};
|
||||
}
|
||||
|
||||
const screenshotFiles = reactive<Record<ScreenshotKey, string>>({
|
||||
coin: "",
|
||||
gameId: "",
|
||||
totalAsset: "",
|
||||
tencentSecurity: "",
|
||||
skin: "",
|
||||
});
|
||||
function defaultScreenshotFiles(): Record<ScreenshotKey, string> {
|
||||
return {
|
||||
coin: "",
|
||||
gameId: "",
|
||||
totalAsset: "",
|
||||
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 fileInput = ref<HTMLInputElement | null>(null);
|
||||
@@ -96,7 +144,18 @@ const dailyLossText = computed(() =>
|
||||
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() {
|
||||
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) {
|
||||
setter(value);
|
||||
}
|
||||
@@ -158,7 +285,7 @@ function handleFireLevelInput(value: string | number) {
|
||||
form.fire_level = "";
|
||||
return;
|
||||
}
|
||||
form.fire_level = Math.max(38, Math.trunc(level));
|
||||
form.fire_level = Math.trunc(level);
|
||||
}
|
||||
|
||||
function calculatePrice() {
|
||||
@@ -207,6 +334,7 @@ async function handleSubmit() {
|
||||
min_rent_hours: 24,
|
||||
max_rent_hours: rentDays * 24,
|
||||
});
|
||||
localStorage.removeItem(draftKey);
|
||||
showToast({ message: "发布成功", icon: "passed" });
|
||||
await router.push("/m/profile");
|
||||
} catch (error) {
|
||||
@@ -671,17 +799,26 @@ function readError(error: unknown, fallback: string) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<van-button
|
||||
type="primary"
|
||||
block
|
||||
round
|
||||
class="submit-btn"
|
||||
:loading="loading"
|
||||
loading-text="发布中..."
|
||||
@click="handleSubmit"
|
||||
>
|
||||
保存发布
|
||||
</van-button>
|
||||
<div class="publish-actions">
|
||||
<van-button
|
||||
round
|
||||
class="reset-btn"
|
||||
:disabled="loading"
|
||||
@click="handleResetDraft"
|
||||
>
|
||||
重置
|
||||
</van-button>
|
||||
<van-button
|
||||
type="primary"
|
||||
round
|
||||
class="submit-btn"
|
||||
:loading="loading"
|
||||
loading-text="发布中..."
|
||||
@click="handleSubmit"
|
||||
>
|
||||
保存发布
|
||||
</van-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<nav class="bottom-nav">
|
||||
|
||||
Reference in New Issue
Block a user